目录

1. 创建准星UI

2. 调整发射代码


1. 创建准星UI

结合之前文章关于UMG的内容,我们可以十分快速地创建一个之子准星的UI,这一部分视频对应课程P20开始。

首先,我们需要调整一下摄像机的位置。如果我们现在运行关卡,会发现游戏角色位于镜头的正中间,这无疑会在操作中遮挡玩家的实现。回忆一下各种第三人称视角的游戏,人物通常位于画面的偏左或偏右的位置。

进入Player的蓝图编辑器,选择弹簧臂组件(SpringArmComp),调整其中的“摄像机”属性。通过设置“长度”可以变化摄像机与角色的距离,设置“插槽偏移”从而在改变相机位置时保持弹簧地碰撞检测的功能。这里大家可以根据自己的喜好自行调节视角,我在这个地方的设置如图18-1:

图18-1 弹簧臂设置

接下来,在UI文件夹下创建Crosshair_Widget控件蓝图,并添加一个图像控件。通常,十字准星固定在屏幕的正中央。因此,我们设置图像的锚点为屏幕中间点,设置位置X、Y为0,并把X与Y的对齐均设置为0.5。适当调节尺寸X、Y属性,使准星的大小合适。

图18-2 准星设置

回到Player蓝图中,在之前添加血量条的位置再把准星加上,同样添加到视口。然后运行关卡,就可以看到屏幕正中间有一个简单的准星了。

图18-3 添加准星

图18-4 运行测试

2. 调整发射代码

在此前第5节的文章中,我在SurCharacter中的PrimaryAttack使用了GetActorRotation函数来获取角色的旋转,从而使粒子以角色的正前方发射,如图18-5:

图18-5 发射粒子

在添加十字准星后,我们肯定需要粒子沿着准星发射,即沿着玩家的视角发射。所以,我们只用把PrimaryAttack_TimeElapsed函数中的GetActorRotation()换成GetControlRotation()即可。此外,在当前项目下,我使用UE_LOG发现GetControlRotation()和GetViewRotation()的值相等,所以后面就统一用前者。

图18-6 运行测试

但这样的更改可能会带来两个问题:一是反方向(正脸面向摄像头)发射时可能会打中角色自己,这取决于魔法粒子蓝图的设置;二是击中的位置与准星还是存在偏差,尤其在角色朝向左边的时候(角色右手发射):

图18-7 反向发射

图18-8 位置偏差

第一个问题很容易理解,反方向发射的魔法粒子检测到了自己角色的actor,就触发了OnActorOverlap事件。在此前第11节的内容中,我们已经使用了Instigator判断是不是玩家自己,所以第一个问题我并没有遇到。

但此处还要注意,粒子虽然可以穿过角色,但仍会对角色自己造成伤害。我们控制粒子销毁的功能是在蓝图中实现的,而控制伤害的功能是在代码中实现的(有点混乱,但这是课程出于教学目的的设计),所以我们还需要再C++中添加忽略对玩家造成伤害的代码。代码如下所示,其实就是在之前的基础上在第一个if处添加了对Instigator的判断,和蓝图中的逻辑是一样的。

void ASurMagicProjectile::OnActorOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{//避免攻击者被自己的粒子伤害if (OtherActor && OtherActor != GetInstigator()) {//获得AttributeCompUSurAttributeComponent* AttributeComp = Cast<USurAttributeComponent>(OtherActor->GetComponentByClass(USurAttributeComponent::StaticClass()));// 再次判空,可能碰到的是墙壁、箱子等没有血量的物体if (AttributeComp) {// 魔法粒子造成20血量伤害AttributeComp->ApplyHealthChange(-20.0f);// 一旦造成伤害就销毁,避免穿过角色继续计算Destroy();}}
}

第二个问题就稍微复杂一些,我个人是这样理解的:粒子发射的方向是角色的相机方向,而粒子发射的位置是角色右手。也就是说,只要发射位置不在屏幕正中心(也就是相机的位置),最后粒子的落点一定存在偏移,且距离屏幕中间越远偏移越大。

一种最简单的解决办法是,直接设置魔法粒子的发射点在屏幕正中间,也就是相机位置。这个方法只修改需要一行代码,也就是把相机的Rotation和Location都赋给SpawnTM:

FTransform SpawnTM = FTransform(GetControlRotation(), CameraComp->GetComponentLocation());

图18-9 运行测试

这种方法的效果如图18-9,毫无疑问实现了指哪打哪。如果要使用这个方法,也许在每次攻击时把角色旋转到朝前,然后要精调角色在镜头中的位置,以及魔法粒子沿着GetControlRotation这个方向的具体生成位置,使其攻击时手部刚好和发射粒子的位置重合,感觉第一人称射击游戏可以更方便地使用这种方法(以上都是个人猜想)。

另一种很巧妙的方法,是先检测再发射。在发射魔法粒子前,先用射线检测的方法,检测一个从相机位置沿着其朝向的较大的射程范围内,有没有命中对象。如果检测到命中,返回命中的位置,并利用向量加法得到这次攻击的方向向量,然后再发射魔法粒子;如果不命中,最后就会落到沿着相机,距离为射程的那个点上。这种方法每次攻击的方向向量是不相同的,是判定完位置后再计算的。

实现方法和之前第7节打开箱子的射线检测很类似,这里使用形状检测来增加检测的空间,核心代码如下:

// 获取模型右手位置
FVector RightHandLoc = GetMesh()->GetSocketLocation("Muzzle_01");// 检测距离为 5000 cm = 50 m
FVector TraceStart = CameraComp->GetComponentLocation();
FVector TraceEnd = TraceStart + ( GetControlRotation().Vector() * 5000 );// 检测半径
FCollisionShape Shape;
Shape.SetSphere(20.0f);// 不要检测自己角色
FCollisionQueryParams Params;
Params.AddIgnoredActor(this);// 碰撞设置
FCollisionObjectQueryParams ObjParams;
ObjParams.AddObjectTypesToQuery(ECC_WorldStatic);
ObjParams.AddObjectTypesToQuery(ECC_WorldDynamic);
ObjParams.AddObjectTypesToQuery(ECC_Pawn);FHitResult Hit;
if (GetWorld()->SweepSingleByObjectType(Hit, TraceStart, TraceEnd, FQuat::Identity, ObjParams, Shape, Params)) {TraceEnd = Hit.ImpactPoint;
}// 尾向量 - 头向量 = 方向向量 eg:起点(0,0) 终点(1,1),方向向量为(1,1)
FRotator ProjRotation = FRotationMatrix::MakeFromX(TraceEnd - RightHandLoc).Rotator();
// 朝向检测到的落点方向,在角色的右手位置生成
FTransform SpawnTM = FTransform(ProjRotation, RightHandLoc);// 此处设置碰撞检测规则为:即使碰撞也总是生成
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
SpawnParams.Instigator = this;GetWorld()->SpawnActor<AActor>(ProjectileClass, SpawnTM, SpawnParams);

原理已经十分清晰明了,但我使用如上代码在运行时并没有达到预期的效果。在我添加了DrawDebugLine和DrawDebugPoint进行调试后,终于发现问题的原因:预先检测时直接略过了预期对象,根据UE_LOG打印的内容显示,最后传回的Actor是地板Floor,如图18-10中黄球所示:

图18-10 Debug

于是我顺着这个问题,对碰撞和物理属性的各种设置折腾了许久,最后终于发现把对象的碰撞预设设置为“BlockAll”后,代码就可以正常运行了。但这样的缺点在于,这些物体即使设置了“模拟物理”也不会和场景内的火药桶产生交互,火药桶爆炸后它们还死死地钉在原地。

接下来我只能从函数上入手,我看到UE对SweepSingleByObjectType的解释是发射一个形状,会返回第一个碰撞的对象。一看到碰撞这个字眼,我就突然意识到ObjParams里面传入了需要检测的对象,兴许是我场景中的对象不属于代码中的三种。我在UE中检查这面墙的对象类型,发现是PhysicsBody,于是我尝试性地在代码中加入了ECC_PhysicsBody。最后,终于在物体能模拟物理的基础上,被SweepSingleByObjectType检测到。最终效果如图18-11所示,其中黄色是形状检测的点,紫色是粒子触发命中事件的点。

图18-11 运行测试

看来根本的问题还是在UE的碰撞设置上,目前有关各种碰撞预设设置、命中事件、重叠事件等一些列问题理解还十分粗浅。所以暂且留个坑,待日后对UE有一定概念后再回头来系统研究这个问题。

斯坦福UE4 + C++课程学习记录 18:十字准星相关推荐

  1. 斯坦福UE4 + C++课程学习记录 10:蓝图-优化宝箱动画

    目录 1. 优化宝箱打开动画 2. 查看蓝图运行过程 3. 添加金块 4. 关闭宝箱 5. 蓝图代码 在之前第7节的内容中,我们通过在SurItemChest中实现SurGameplayInterfa ...

  2. 斯坦福UE4 + C++课程学习记录 22:击败动画

    目录 1. 判断存活 2. 添加击败动画 3. 禁用输入 这一节内容将会为角色添加是否存活的判定,并为其添加击败后的倒地动画. 1. 判断存活 在之前第13节实现血量条时,我们创建了一个Attribu ...

  3. 斯坦福UE4 + C++课程学习记录 14:UMG-优化血量条

    目录 1. 优化执行效率 2. 简易脉冲动画 3. 完整代码 1. 优化执行效率 上一节虽然实现了UI的实时更新,但绑定函数会每帧刷新进而造成资源浪费,毕竟血量变化的速度和一帧比起来还是慢太多.类似于 ...

  4. 1 Robotics: Aerial Robotics 第3+4周 课程学习记录及课后习题解答

    Robotics: Aerial Robotics 第3+4周 WEEK - 3 Quiz Programming Assignment: 2-D Quadrotor Control WEEK - 4 ...

  5. cs224w(图机器学习)2021冬季课程学习笔记18 Colab 4:异质图

    诸神缄默不语-个人CSDN博文目录 cs224w(图机器学习)2021冬季课程学习笔记集合 文章目录 Question 1. DeepSNAP异质图简介 1.1 Question 1.1:分配Node ...

  6. UE4·究极·学习记录

    UE4官方文档 2020/2/20 周末需要整理一下,遗留的困惑点比较多,也可能在后续的学习中能够反映过来,之前的问题是可以解决的 目前的计划:先学完官方文档,再回头去解答这些疑问 实现相机的切换,相 ...

  7. 《Qt-OpenGL系列编程》课程学习记录(1):相关概念、VAO、VBO、绘制三角形、使用OpenGL原生方式编译链接着色器程序

    大家可以去B站看课程的视频支持一下作者哈: OpenGL,Qt实现:1入门篇(已更完)_哔哩哔哩_bilibili课程相关源码.PPT.安装包,完整课程合集(1:入门篇:2:基础光照:3:模型加载:4 ...

  8. 《圈外课程学习记录》1.1结构化的特征 1.2表达时主题先行

    初识结构化思维 1.1结构化的特征 1.2表达时主题先行 SCQA: 课后作业 [KANO模型] 问题总结: 延伸拓展: 一.认识思维导图 二.如何用思维导图做学习笔记? 三.创建Xmind思维导图 ...

  9. 《圈外课程学习记录》剧本杀:如何申请加薪

    背景 销售部王总: 某医疗设备公司Y的销售部门总管,为公司工作了十多年,从来不会轻易表露情绪,而且对人力成本的管理控制也很严格,从来不会主动给下属涨薪.即使是下属主动来提加薪,王总也会用各种各样的托词 ...

最新文章

  1. python中排序英文单词怎么写_Python实现对文件进行单词划分并去重排序操作示例...
  2. libcurl选项CURLOPT_WRITEDATA中的“坑”
  3. 一批美国名校被曝监控学生:14秒记录一次手机位置,想翘课更难了
  4. 手把手教你实现 Docker 部署 Redis 集群
  5. Oracle GoldenGate 下载地址
  6. 微信java版s40_塞班微信S40版下载
  7. Windows 开启护眼模式 | Windows护眼软件
  8. R语言——矩阵中删除缺省值可用的函数
  9. isdigit()函数如何判断负数
  10. SwiftUI 使用Apple Visionkit构建文档扫描仪
  11. 时间管理类入门书籍分享
  12. docker版mongodb数据同步到elasticsearch
  13. 新手小白怎样才能画好女生身体?画好女生身体有哪些技巧?
  14. c语言编程TLC2543AD采集,51单片机驱动12位AD转换TLC2543电路图+程序
  15. 【Android 教程系列第 27 篇】如何使用 Keytool 工具生成 keystore 签名文件
  16. vs 项目重新生成无反应,仍然执行之前原先代码
  17. 小米4 第三方re奇兔_小米Air 2 Pro -小米高端耳机的滑铁卢
  18. matlab狗抓兔子,怎样训成年犬捉兔子-怎样训狗抓兔子
  19. python爬虫爬取百度搜索结果,Bob blog
  20. 第5期丨一周电商零售News10条(2.8-2.14)B站被指“侮辱女性”被多家品牌方拉黑/TikTok计划开拓美国电商市场...

热门文章

  1. 以“有用”为圆心:重新认识智慧城市的“高手之路”
  2. Python的10种谋生方法,你们都知道了吗?
  3. 2018年数学建模国赛B题
  4. 【自用】simetrix/simplis使用体验及问题分析(2)
  5. Maven3.8.3下载
  6. 利用 CPU 流水线加快数据处理
  7. 从4方面解释,什么叫云原生应用?
  8. 「 LaTex 」写论文,插入Acknowledge
  9. z-blog php关键字和描述,zblogphp网站添加关键词、描述的方法
  10. 想成为UI设计师都需要学习哪些软件工具