虚幻5---第13部分---
碰撞盒(Collision Box)
------为武器添加盒体碰撞组件
重点与流程:
1.用C++ 在我的剑上添加盒子组件。
(声明类---写指针---添加对应头文件---构造函数)
-
调节碰撞盒的范围。
-
使用组件开始重叠事件来测试碰撞盒。
示例图




碰撞追踪(Tracing)
------使用盒体形状进行扫掠检测
重点和流程:
- 武器与物品无法触发重叠事件的原因?勾选生成重叠事件,查看物品的碰撞设置。
- 了解追踪的类型和定义和原理。
- 创建场景组件来作为起点和终点来做盒体追踪。在蓝图做一下看看逻辑。
- 修改武器的碰撞预设,使它的命中不被胶囊体干扰
示例图:



C++ 中的盒体碰撞追踪(Box Trace in C++)
------通过代码执行碰撞追踪
重点与流程:
- 在C++里设置碰撞预设,使用一些方便的函数。
- 在C++里新建场景组件变量。然后写盒式追踪函数。
示例图:




动态数组(Dynamic Arrays)
------虚幻引擎的 TArray 容器
重点与流程:
- 了解T-array,了解它的不同。(比如:自动扩容,自动缩小容量),崩溃的可能原因。
禁用盒体碰撞(Disabling Box Collision)
------仅在攻击时触发重叠事件
重点与流程:
- 进入攻击的蒙太奇动画,在里面设置通知。
- 然后再动画蓝图编辑器里面设置,连接。
- 在C++里面设置启动与禁用盒子碰撞。(一些可以访问私有变量的技巧)
- 在进入蓝图添加连接。
示例图





示例代码(来自SlashCharacter.h)
cpp
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include"CharacterType.h"
#include "SlashCharacter.generated.h"
class USpringArmComponent;//提前声明弹簧臂组件
class UCameraComponent;//提前声明摄像机组件
class UGroomComponent;//提前声明毛发组件
class AItem;
class UAnimMontage;
class AWeapon;
UCLASS()
class SLASH_API ASlashCharacter : public ACharacter
{
GENERATED_BODY()
public:
ASlashCharacter();
virtual void Tick(float DeltaTime) override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
//设置碰撞开关。
UFUNCTION(BlueprintCallable)
void SetWeaponCollisionEnabled(ECollisionEnabled::Type CollisionEnabled);
protected:
virtual void BeginPlay() override;
//输入的回调函数
void MoveForward(float Value); //前进函数。
void MoveRight(float Value);
void Turn(float Value);
void LookUp(float Value);
void EKeyPressed(); //这是一个动作映射(装备武器),因为是一次性,所以没有参数。
void Attack(); //攻击的回调函数,用来绑定攻击键。
//播放蒙太奇动画的函数//重构函数
void PlayAttackMontage();
UFUNCTION(BlueprintCallable)
void AttackEnd(); //攻击结束通知
bool CanAttack();// 是否可以攻击
void PlayEquipMontage(const FName& SectionName);//装备动画的函数
bool CanDisarm();//检查卸下播放装备武器的蒙太奇动画的条件
bool CanArm();//检查装备武器的蒙太奇动画的条件
UFUNCTION(BlueprintCallable)
void Disarm();//武器附着到脊椎上
UFUNCTION(BlueprintCallable)
void Arm();//武器从背上拿出的动画,也就是这次的附着点在右手
UFUNCTION(BlueprintCallable)
void FinishEquipping();//装备动画结束时
private:
ECharacterState CharacterState = ECharacterState::ECS_Unequipped; //使用枚举常量来控制游戏逻辑
UPROPERTY(BlueprintReadWrite,meta=(AllowPrivateAccess="true"))
EActionState ActionState = EActionState::EAS_Unoccupied; //在攻击过程中阻止继续攻击
UPROPERTY(VisibleAnywhere)
USpringArmComponent* CameraBoom;//添加弹簧臂组件
UPROPERTY(VisibleAnywhere)
UCameraComponent* ViewCamera;//添加摄像机组件
UPROPERTY(VisibleAnywhere, Category = Hair)
UGroomComponent* Hair;//添加毛发组件(头发)
UPROPERTY(VisibleAnywhere, Category = Hair)
UGroomComponent* Eyebrows;////添加毛发组件(眉毛)
UPROPERTY(VisibleInstanceOnly)
AItem* OverlappingItem; //创建一个重叠的游戏物品,需要在物品重叠时,才能从物品类里设置这个属性
UPROPERTY(VisibleAnywhere,Category=Weapon)
AWeapon* EquippedWeapon;//表示当前持有的武器
//动画蒙太奇,这里保留蒙太奇变量,未来可能添加更多
UPROPERTY(EditDefaultsOnly,Category=Montages)
UAnimMontage* AttackMontage;
//装备武器的蒙太奇动画
UPROPERTY(EditDefaultsOnly, Category = Montages)
UAnimMontage* EquipMontage;
public:
FORCEINLINE void SetOverlappingItem(AItem* Item) { OverlappingItem = Item; } //有了OverlappingItem的公开设置器。小型的的函数设置成内联更高效
FORCEINLINE ECharacterState GetCharacterState() const { return CharacterState; } //角色状态的公开访问器
};
虚幻引擎接口(Unreal Interfaces)
------使用接口处理受击事件
重点与流程:
- 了解接口,比如击中接口。让武器可以在击中的任何对象上调用get hit函数,不需要判断击中的是什么。
- 创建接口。
示例图:

敌方单位(Enemies)
------敌方角色类
重点与流程
创建C++类
- 创建一个character类,来作为游戏的敌人。
- 设置碰撞预设。把敌人设置成世界动态。(毕竟前面的武器忽略了pawn)也要忽略相机的碰撞响应。勾选或者在C++设置生成重叠事件。
新建对应的蓝图
- 新建敌人蓝图
- 去mixamo选择敌人的网络(Mesh)模型。导入项目,知道那些有用那些没用。
示例图:




根运动动画(Root Motion Animations)
------能实际移动角色的动画
重点与流程:
- 去mixamo找一些敌人的动画。(带皮肤)
- 了解根部运动动画,以及它的特点,比如使用条件等。(比如我们的角色要有跟骨头才可以使用。如果没有根骨骼,我们无法直接用动画来移动角色)
- 如果我们下载的角色没有怎么办?把这个网络体导入到Blender这样的网络体编辑软件里。简单说,就是:下载需要的动画,然后用Blender和Mixamo Converter插件来给它们加根骨头。
- 使用批量转换。
- 进入编辑器,删除原本的一些网络体,导入转化后的动画。进行导入动画的设置。
- 给敌人分配一个网络体。给他一个动画播放。
示例图:




注意:
- 可能出现重命名错误。这里有解决办法。
- 把敌人拖到场景测试劈砍,如果没反应,可能是敌人蓝图里面没有设置物理资产,记得勾选。
实现接口(Implementing Interfaces)
------使用我们的受击接口
重点与流程:
让角色被击中时做出反应。
- 在C++里面敌人的代码要继承接口。有个技巧,把敌人写成既是角色也是IHit接口,这样更灵活。
- 重写我之前声明的githit函数
- 在指定的文件里面测试githit函数。
示例图:





受击反应蒙太奇(Hit React Montage)
------为受击反应创建蒙太奇
重点与流程:
- 制作蒙太奇。
- 只有在动画蓝图使用的该蒙太奇的插槽时才能播放。所以,我们要为该角色建立一个动画蓝图。并在敌人蓝图里设置。
示例图



播放受击反应蒙太奇(Playing the Hit React Montage)
------使敌人产生踉跄效果
重点与流程:
- 回到C++项目,进入敌人类,添加一个HitReact蒙太奇变量。
- 编写蒙太奇函数。
- 在敌人蓝图里设置动画。测试。
- 给动画勾选根运动。
示例图:






注意:
- 击中敌人后敌人自动回到原来的位置。开启碰撞检测->胶囊体没动。解决办法:
我要进入该动画,确保它使用根运动。 - 动画不动,记得回到敌人的动画蓝图里勾选循环动画。
点积(Dot Product)
------两个向量之间的夹角
重点与流程
- 如何让敌人朝不同的方向倒?击中向量。了解击中的角度和对应的蒙太奇逻辑。
- 点积可以计算两个向量之间的角度。
原理图:
- 回到C++,创造击中向量。计算好角度与逻辑。
- 让我们的调试向量与地面平行,这样更好判断角度。
示例图:

叉积(Cross Product)
------另一种高级向量运算
重点与流程:
- 反余弦总是返回正数,但是叉积可以根据叉积的方向来判断角度的正负。这对判断左右击中方向很重要。
- 数学书用的右手定则,但是虚幻引擎是左手定则。
原理图:
- 返回Cpp,写叉积的调试办法。
示例图:
方向受击反应(Directional Hit Reactions)
------基于 θ 角选择蒙太奇片段
重点与流程:
- 在C++里写各个动画的播放与角度之间的逻辑。(注意大于等于号不要重合)
示例图

注意:
- 可能出现一次攻击导致击中多次的情况,下一部分会解决这个问题。
单次挥击单次命中(One Hit Per Swing)
------忽略已命中的 Actor
重点与流程:
- 重构一下我们的GitHit函数里的部分内容。
- 在武器里编写忽略被击中的角色的函数。
示例图





即使有多个重叠反应,也只有一个击中点。

受击音效(Hit Sounds)
------在敌人受击时播放音效
重点与流程:
- 下载一些击中的音效。导入项目
- 利用这些音效搞一个MetaSounds。设置好MetaSound,排除掉不合适的声音。
- 回到C++,进入敌人类,添加一个变量,用来存放敌人被击中的音效资产。
- 写该声音的处理逻辑。ps:热重载快捷键可以方便不按鼠标。
- 回到编辑器,设置我们的敌人。
- 设置声音,让声音有根据距离衰减的效果。建立声音衰减的资产类型。
- 在元音效蓝图里面设置。
- 调试声音的一些办法。
示例图





注意
- 每一个敌人一开始都是默认的声音蓝图,所以我们要在编辑器选择敌人,一个个设置好。当然,我们也可以在敌人蓝图里一次性设置好。
受击粒子效果(Hit Particles)
------在敌人受击时生成血迹效果
重点与流程:
- 了解Cascade(旧)和Niagara(新)。知道一下怎么从这两种方法来生成粒子效果。
- 先学一下怎么生成Cascade粒子。先弄一些血液粒子(Cascade类型)。
- 学习把整个文件夹导入项目的方法。查看该粒子效果
- 让敌方角色有一个这个粒子效果的变量。这样每个敌人有不同的血溅效果。
- 在蓝图使用测试一下spawn emitter at location。
- 在C++里完成这个效果。新加一个变量给粒子系统。
- 回到敌人蓝图,查找我们的变量,设置好它。
- 在C++写具体的击中效果函数。测试看看效果是否符合预期,位置是否正确。
- 之后去掉调试线条看看效果。记得把武器的调试追踪也关闭,这样每次做盒式追踪时就不会画盒子了。再关闭调试箭头。
示例图




部分代码示例:来自Enemy.h
cpp
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Interfaces/HitInterface.h"//让敌人可以继承接口里的函数和类
#include "Enemy.generated.h"
class UAnimMontage;
UCLASS()
class SLASH_API AEnemy : public ACharacter,public IHitInterface //继承两个父类
{
GENERATED_BODY()
public:
AEnemy();
virtual void Tick(float DeltaTime) override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
virtual void GetHit(const FVector& ImpactPoint) override;
void DirectionalHitReact(const FVector& ImpactPoint);
private:
//动画蒙太奇,这里保留蒙太奇变量,未来可能添加更多
UPROPERTY(EditDefaultsOnly, Category = Montages)
UAnimMontage* HitReactMontage;
UPROPERTY(EditAnywhere, Category = Sounds)
USoundBase* HitSound; //该变量存储MetaSound资产类型的值
//击中的效果粒子,可以是血液,小石头等等
UPROPERTY(EditAnywhere, Category = VisualEffects)
UParticleSystem* HitParticles;
protected:
virtual void BeginPlay() override;
//播放蒙太奇函数
void PlayHitReactMontage(const FName& SectionName);
public:
};
注意:
- 有一些变量的类型不需要提前声明,因为已经内置了。可以通过热重载来判断。
ps:热重载快捷键:ctrl+Alt+F11。
武器拖尾(Weapon Trails)
------拖尾视觉效果
重点与流程:
- 我们需要一个轨道系统的粒子效果。在虚幻商城添加paragon:minions到项目,里面有我们需要的粒子系统。
- 进入主角的攻击蒙太奇。加速一下攻击,随便调整一下各个标签位置。
- 新建通知来添加尾部(trail)。
- Trail应该从哪里开始。选择它的插槽位置。
示例图:




挑战13
- 可以给脚加一些脚步声。
- 给游戏加一些音频。

