TomLooman_ActionRoguelike_第三章玩法与物理碰撞
2023-08-09 18:40:23 来源:哔哩哔哩
该专栏用于保存对TomLooman的ActionRoguelike项目的学习笔记,学习过程中的思考与记录不一定准确。
(相关资料图)
教程参考:/tomlooman/ActionRoguelike
基于的项目实现:/CarolBaggins2023/TomLooman_ActionRoguelike_Tutorial
2023_07_25
输入和旋转,魔法攻击
旋转的部分内容在2023_07_24中提及。
如上设置,在编辑器内启动游戏后不需要先点击一下鼠标才能操作。
如上设置,每次打开编辑器后不再是默认的level,而是上次关闭前的level。
创建SMagicProjectile类
头文件与Scharacter类类似,但SMagic类继承自Actor而不是Character,这也意味着我们不能控制它(只有Pawn以及Pawn的子类例如Character才能控制)。
且注意#include ""要最后引入。
与ASMagic类相似,类名前有ACTIONROGUELIKE_API宏,将这个Sprojectile类从ACTIONROGUELIKE中暴露出去,能被其他模块调用。
因为是游戏性类,且能在世界中生成,所以类名有前缀A。
同样有GENERATED_BODY()生成代码模板,减少我们的工作量。
与ASCharacter一样有BeginPlay和Tick,但没有SetupPlayerInputComponent,因为ASMagicProjectile只是Actor,而不是Pawn,所以无法被控制,只能按照某种规则运行。
ASMagicProjectile头文件中新增的三个Component。
USphereComponent和SCharacter里使用的CapsuleComponent类似,都是处理运动与碰撞的,但是形状不同。
但是在ASCHaracter中看不到显式的碰撞组件成员,可能是在GENERATED_BODY()中生成的。
在源文件中我们对碰撞组件进行了默认实例化(CreateDefaultSubobject<组件所属的类名>("组件在UE编辑器中显示的名字")),并设置了我们自定义的碰撞(后续有相关内容),还将其设置为该Actor的RootComponent。
UProjectileMovementComponent(抛体组件)能够实现抛体运动,反弹效果,抛物曲线等功能,它继承自UMovementComponent。
它会在每个tick更新另一个组件的位置(这里可能就是碰撞组件)。如果被更新的组件开启了模拟物理,则只有非零的初始速度(方向向量和大小)对后续轨迹有影响,因为在初始时刻后,后续运动由物理模拟接管。
在源文件中我们对其进行默认实例化。
InitialSpeed设置初始速度。
bRotationFollowsVelocity为true,则抛体组件的rotation在每个frame随着速度方向更新。(bool变量前缀为b)
bInitialVelocityInLocalSpace为true,则抛体组件的初始速度向量是相对于局部空间的,而不是相对于世界空间的。若该bool变量为false,则Projectile始终向坐标系内的一个方向运动,不考虑我们的Character和Controller。
UParticleSystemComponent负责CPU端的粒子数据和更新逻辑,是一个SceneComponent,带有位置信息,这意味着这个Component可以被挂到任意Actor身上。
这里我们只简单地进行默认实例化,并attach到碰撞组件上。
对UParticleSystemComponent类型的对象,我们可以在蓝图子类中设置其粒子效果。
在中的void ASCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)内添加
,并在ProjectSetting里添加。
实现将一个外部输入绑定到一个动作,再将一个动作绑定到一个成员函数。
BindAction()和BindAxis()的区别总结如下:
执行时机:BindAction() 绑定的函数会在输入事件发生时执行,如按键按下或抬起时执行一次;BindAxis() 绑定的函数每帧都会执行;
作用原理:BindAction() 用来监听外设是否到达某个状态,如某个按键被按下或者抬起;BindAxis() 监听的是外设状态的变化量;
函数参数:BindAction() 绑定的函数无参数;BindAxis() 绑定的函数有一个参数,该参数就是外设状态的变化量;
对按键的处理:当按键被按下或者抬起时,BindAction() 绑定的函数就会被触发;BindAxis() 绑定的函数会每帧都执行,但在按键被按下时收到的参数是 Scale(Axis Mapping 时设定的系数),在抬起时收到的参数是0。
ActionBind的函数接口和我们的调用如下
template<class UserClass> FInputActionBinding& BindAction( const FName ActionName, const EInputEvent KeyEvent, UserClass* Object, typename FInputActionHandlerSignature::TMethodPtr< UserClass > Func )
调用为
PlayerInputComponent->BindAction("PrimaryAttack", IE_Pressed, this, &ASCharacter::PrimaryAttack);
ActionName是我们在ProjectSetting设置的动作名称("PrimaryAttack"),KeyEvent是与该动作绑定的事件(例如按下、松开、双击)(IE_Pressed是枚举类型变量EInputEvent中的一个枚举成员的key,如下图),Object是函数作用的对象?(这里涉及到UE的委托机制),Func是与事件绑定的函数的指针(我们自定义的成员函数ASCharacter::PrimaryAttack)。
对ASCharacter::PrimaryAttack我们声明与实现如下,
public:
从后往前解释。我们实际上要生成一个Projectile然后才能把它发射出去,在世界中生成Actor使用GetWorld()->SpawnActor<类的类型>。
SpawnActor的函数接口如下,
template< class T > T* SpawnActor(UClass* Class, FTransform const& Transform,const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters())
SpawnActor用于从给定的参数Transform和SpawnParameters生成Class类的Actor。
在调用SpawnActor时我们要传入要spawn的对象的类名,这里我们先在头文件中声明一个变量,然后在蓝图类中将该变量绑定到我们的ASMagicProjectile类。
UPROPERTY(EditAnywhere)这个说明符使该属性公开给编辑器并且可编辑。
TSubclassOf<类的类名>是提供UClass类型安全性的模板类。模板类告知编辑器的属性窗口,只显示派生自<>内类的类(在这里就是指显式派生自AActor的类)。同时,这个参数在代码中也只接受派生自<>内类的类。
这样一来,上面SpawnActor函数在世界中Spawn的都是AAactor的子类。
FVector,FTransform,FActorSpawnParameters等F开头的类名说明它们是纯C++类,可以用new来产生新对象。
另外前缀为U的可以用NewObject函数来产生对象,前缀为A可以用SpawnActor函数来产生对象(就是上面的GetWorld()->SpawnActor<AActor>)。
FTransform是个结构体,包含一个 Translation 矢量,一个 Rotation 四元数,和一个Scale3D 矢量(分别对应位置translation,旋转rotation和缩放scale),可以用以下代码创建一个FTransform对象。
FRotator是上面提过的旋转类,是个三维向量结构体,封装roll,yaw,pitch。FVector存储三个T泛型的变量。
我们的代码中只用了Rotation和Location初始化FTransform。
GetControlRotation()之前提过,返回Pawn的Controller的Rotation。GetActorLocation()返回Actor(这里是我们的Character)的Location。
FActorSpawnParameters是传递给SpawnActor的可选参数类的类型。
其中,FactorSpawnParameters::SpawnCollisionHandlingOverride是控制生成点(Spawn的位置)冲突的变量。
ESpawnActorCollisionHandlingMethod中定义了解决Spawn生成点冲突的可用策略,其中的AlwaysSpawn表示Actor总会Spawn在我们想要的位置,忽视Collision。
按照以下代码,我们的ASMagicProjectile类的Actor会Spawn在Character的碰撞体的中心,但我们实际上想要让Actor生成在Character的手上,因此做出修改。
唯一的不同是我们把GetActorLocation()换成RightHandLocation(),两者都是Fvector。
GetMesh()返回指向MeshComponent的指针,GetSocketLocation(Socket名称)得到指定名称的Socket的位置。
添加Socket
首先,我们的Character有一个Skeleton(骨架),这个Skeleton是由许多bones(骨头)组成的,我们可以在某块bones上添加Socket。
双击Mesh组件的SkeletalMesh后,选择Skeleton
左侧显式Character的bones和sockets,可以看到两者的标志不同。这里角色的右手上已经有了一个名为“Muzzle_01”的socket,所以我们可以在C++中直接调用。因此,我们获得RightHandLocation时传入GetSocketLocation的参数是"Muzzle_01"(编辑器中的目标socket的名字)。
现在我们能正确地从ASCharacter实例的手部Spawn出ASMagicProjectile实例,但是Spawn出的Projectile会穿过我们放置在世界中的StaticMesh物体,不会发生碰撞,所以我们需要对ASMagicProjectile类的collision做出修改。
我们有三种方法修改Collision。
1、可以在蓝图子类中选择碰撞组件然后修改Collision。
2、可以在ASMagicProjectile的构造函数中修改碰撞组件的属性
this->SphereComp->SetCollisionObjectType(ECC_WorldDynamic); this->SphereComp->SetCollisionResponseToAllChannels(ECR_Ignore); this->SphereComp->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
SetCollisionObjectType设置该组件自身的碰撞属性(这里设置为ECC_WorldDynamic),SetCollisionResponseToAllChannels
设置该组件对所有通道的碰撞属性(这里设置为全部忽略ECR_Ignore),SetCollisionResponseToChannel
设置该组件对某个通道的碰撞属性(这里将该组件对ECC_Pawn的碰撞属性设置为ECR_Overlap)。
3、可以直接在ProjectSetting里创建一个Collision的Profile,然后在具体组件的Collision中应用(该方法可在多个组件中复用)
要应用上面创建的Profile,在C++中和在蓝图子类中都可以。但要注意的,C++中的设置类似于默认项,所以如果又在蓝图子类进行了修改,那么C++和蓝图子类中的Collision属性可能不同。
this->SphereComp->SetCollisionProfileName("Projectile");
需要注意的是,只有发生碰撞的双方都认为对方是Block的对象,才会发生Block(需要双向奔赴)。例如,在A的Collision属性中B是要被Block的,但在B的Collision属性中A是被Ignore的,那么A和B实际不会发生碰撞,只有在B的Collision属性中A也是被Block的,A与B之间的碰撞才会发生。
且最终结果会是双方Collision属性中更轻的那个,例如block和overlap则是overlap,overlap和ignore则是ignore。如下图,player会被wall阻挡,因为二者对对方的预设都是block。但不会被shrub阻挡:虽然player对worldStatic的预设是block,但shrub对pawn的预设是overlap,所以取了较轻的overlap判定。
UE中的Collision属性包括ignore、overlap和block。Block下会发生阻挡(两人见面时停下),ignore和overlap下都不会阻挡(两人擦肩而过)。Ignore和overlap的区别在于,overlap会出发overlap事件(两人都知道与对方擦肩而过),ignore则不会触发时间(两人根本没看见对方)。
实现ASMagicProjectile与WorldStatic碰撞效果后(否则魔法飞弹会直接穿过白色方块)
SimulatePhysics
若为true,则该对象会模拟物理行为,比如被推动、从高处落下、抛物运动等,同时对象Transform的Mobility属性应设置为movable。若为false,则该对象会固定在原地或按指示移动。
要实现物理模拟,对SkeletalMeshComponent,需要设置物理资产。对StaticMeshComponent,需要设置碰撞。
Actor的Mobility属性主要应用于StaticMesh和Light,包括三种状态Static、Stationary和Movable。
Mobility为Static的Actor无法进行任何的移动或改变。
Mobility为Stationary的Actor可以改变,但不能移动。
Mobility为Movable的Actor可以进行任何的添加、移动和改变。
2023_07_28
Assignment 1:爆炸桶,跳跃
首先创建C++类。因为爆炸桶不需要被Control,所以不需要继承Pawn及其子类,继承Actor就足够。以下是头文件中声明的爆炸桶组件的成员变量,以及源文件构造函数中对成员变量的实例化和设置。
这里声明组件的方式与之前有所不同,比较在Scharacter中声明摄像机组件,与这里声明网格组件,
两者都是指针,但声明CameraComp时用的是C++的原始指针,声明MeshComp时用的是TObjectPtr类。TObjectPtr类模板可用于替换原始指针,用法与其它类模板相同,为TObjectPtr<类名>。
在实例化MeshComp时,除了默认实例化和将其作为RootComponent,我们还开启了MeshComp的物理模拟。
需要注意的是,我们在蓝图中对物理模拟打钩后,Component的CollisionPresets会自动变成PhysicsActor。但是当我们在C++中执行SetSimulatePhysics(true)后,Component的CollisionPresets的变化需要显式地进行,否则不会变化。在这里会保持WorldDynamic,因为我们的SMagicProjectile对WorldDynamic的Collision是Overlap的,所以两者无法发生正确的碰撞。
ForceComp所属的URadialForceComponent类(径向力组件)会向径向方向对Physics和Destructible类别的Object施加力。
在实例化ForceComp时,我们设置了该径向力的范围和强度。
其中有一个bImpulseVelChangeBool类型变量,若为true,则该组件发出的径向力忽视Object的重量,否则Object重量越重,径向力对Object的影响越小。
ForceComp->AddCollisionChannelToAffect(ECC_WorldDynamic)和SMagicProjectile中使用过的SphereComp->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap)类似,都是对某个通道设置某种碰撞属性。
可通过ForceComp->SetAutoActivate()函数设置该组件的径向力是否自动开启,若为true,则径向力从该组件所依附的对象被创造时就存在(但是试验中没有效果)。因为我们需要用子弹激活这个径向力来模拟爆炸效果,所以设置为false。
在头文件中我们重载了Actor类的PostInitializeComponents函数,该函数是实例化Actor时的一环,
我们在源文件中对该函数的定义如下
Super::PostInitializeComponents();执行父类(Actor类)的同名函数。
OnComponentHit组件将会在击中或被击中时触发。
这里的AddDynamic涉及UE的委托机制,作用大致是将SexplosiveBarrel对象与函数ASExplosiveBarrel::OnActorHit绑定,当触发一些事件时激活(运行)函数ASExplosiveBarrel::OnActorHit。
BindAction进行两个绑定:将外部输入绑定到事件(ProjectSetting中完成),将事件绑定到函数(C++,SetupPlayerInputComponent成员函数中完成)。
因为ACharacter自带跳跃函数,所以实现较为简单。
如果把IE_Pressed换成IE_Release,角色就会在松开空格后起跳,而不是在按下空格时起跳(可以实现类似蓄力跳的效果)。
关键词:
为你推荐
-
TomLooman_ActionRoguelike_第三章玩法与物理碰撞
-
深圳大学2024年硕士研究生招生专业目录_专业+人数
-
涿州市快递企业积极复工复产助力灾后重建
-
台湾青年走进福建福鼎:希望携家人再次共赴山海
-
麻生太郎主动要求会面 郭台铭送其“金门和平宣言”
-
如何调节电脑字体大小 如何调节
-
贵州改造人防工程向社会提供停车位8.41万个
-
车评头条:当情怀遇上“法规” 试驾斯巴鲁XV智擎
-
《命运方舟》技能栏不够用怎么办
-
雷军宣布将于8月14日举办年度演讲 主题是“成长”
-
江西11岁男孩留遗书跳楼案一审宣判:涉事教师被认定无罪
-
帝国守卫战最好用的三个英雄排名 帝国守卫战英雄排名情况分享
-
港股午评:恒生指数跌0.06% 恒生科技指数跌0.66%
-
中国重汽跌超4% 7月重卡市场销量环比下滑25%
-
国家防总:当前汛情险情灾情总体趋向平稳
-
广州泛美实验股份怎么样?
-
教育部:“银龄讲学计划”2023年计划招募6000名讲学教师
-
翡翠花园2.6紫罗兰水晶
-
原油需求在八月有边际走弱可能
-
战洪峰 防洪灾 保安全 保稳定|市场监管总局发布公告 保障洪涝地区价格基本稳定
推荐内容
- TomLooman_ActionRoguelike_第三章玩法与物理碰撞
- 深圳大学2024年硕士研究生招生专业目录_专业+人数
- 涿州市快递企业积极复工复产助力灾后重建
- 台湾青年走进福建福鼎:希望携家人再次共赴山海
- 麻生太郎主动要求会面 郭台铭送其“金门和平宣言”
- 如何调节电脑字体大小 如何调节
- 贵州改造人防工程向社会提供停车位8.41万个
- 车评头条:当情怀遇上“法规” 试驾斯巴鲁XV智擎
- 《命运方舟》技能栏不够用怎么办
- 雷军宣布将于8月14日举办年度演讲 主题是“成长”
- 江西11岁男孩留遗书跳楼案一审宣判:涉事教师被认
- 帝国守卫战最好用的三个英雄排名 帝国守卫战英
- 港股午评:恒生指数跌0.06% 恒生科技指数跌0.66%
- 中国重汽跌超4% 7月重卡市场销量环比下滑25%
- 国家防总:当前汛情险情灾情总体趋向平稳
- 广州泛美实验股份怎么样?
- 教育部:“银龄讲学计划”2023年计划招募6000名讲
- 翡翠花园2.6紫罗兰水晶
- 原油需求在八月有边际走弱可能
- 战洪峰 防洪灾 保安全 保稳定|市场监管总局发
- 【金融】半导体和集成电路企业A股IPO审核要点
- 三星6812c(三星8262手机报价及图片)
- 威胜信息(688100):8月8日北向资金减持11.86万股
- “江源玉树·天上曲麻莱”第四届黄河源生态文化旅
- 年轻人越变越“抠”,原价购物的都是大冤种?
- 从夏开到冬,广州常见的“小黄虾”花开正盛
- 《孤注一掷》怎么看?你把它当一个“类型片拍反诈
- 苦菊怎么做好吃?
- 北辰区公交调整(北辰区邮编)
- 华勤技术登陆沪主板,持续加码东莞智能制造产业布
- 看好中国创新潜力,这家公司加速在华投资发展
- 奔驰怎么设置你好奔驰语音系统吗(你好奔驰语音系
- 理想汽车第二季度营收 286.5 亿元,同比增长 228.1%
- 国际米兰租借意乙门将,作为蓝黑军团2号门将,年
- 开学季首选华为智慧 PC,华为学习全家桶帮你提高
- 【完美世界】月婵石昊新婚大吉,结婚照出炉
- 杭州亚运会英文官方网站更新英雄联盟项目赛程具体
- 福特旗下有哪些汽车品牌
- 励志说说抖音2020 励志说说抖音文案
- 广东省将迎来2023年养老金重新核算及补发,35年缴
油气
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
经济
-
中新网通辽10月18日电 (记者 张林虎)18日,记者从内蒙古自治区通辽市奈曼旗公安局获悉,国家一级保护动物--梅花鹿误入当地村民羊群,
-
中新网杭州10月18日电 (王题题 胡燕婕)云天收夏色,浅秋正渐浓。10月18日,浙江杭州市西湖游船有限公司推出的惠民多站点“西湖环湖游
-
中新网福州10月18日电 (记者 龙敏 王东明)福州市晋安区官方18日晚间通报,18日14时47分,晋安区岳峰镇化工路爱摩轮商业广场项目摩天
-
中新网兰州10月18日电 (闫姣 艾庆龙 吉翔)“红山白土头,黄河向西流。”不少人疑问,天下黄河向东流,为何甘肃永靖县这段黄河却向西
-
中新网北京10月18日电 《清华城市健康设施指数》18日在北京发布。报告成果显示,城市健康设施指数领先城市以中心城市和东部沿海城市