移动与相机跟随
1. 创建玩家类
在创建自定义的玩家类前,先简要了解一下UE中的常用class:
Object | 所有UE对象的基类 |
---|---|
Actor | 可以在世界中放置或生成的object |
Pawn | 可以被玩家或AI控制的Actor |
Character | 实现了双脚行走的Pawn |
PlayerController | 玩家与对应Pawn之间的接口 |
GameModeBase | 定义游戏的基础规则 |
GameStateBase | 记录游戏的状态 |
创建完成后,UE会在相应目录自动创建相应的.h和.cpp文件
打开.h文件后查看文件内容,在开头会看见类似下面的语句:
class ACTIONROGUELIKE_API ASCharacter : public ACharacter
可以发现UE在创建代码时,自动在原本的SurCharacter和Character类前添加了前缀A。这是因为UE遵守一系列代码编写规范,规定通过基类的种类或类的类型来确定前缀,进而可以区分类和实例。
基类 | 前缀 | 类型 | 前缀 |
---|---|---|---|
UObject | U | 界面 Interfaces | I |
AAcor | A | 枚举 Enums | E |
SWidget | S | 模板 Templates | T |
2. 相机跟随
在创建相机跟随玩家前,首先需要建立一个关卡。
然后需要创建玩家。在内容浏览器中右键 -> 蓝图类
创建完毕后,将角色从内容浏览器拖拽进世界中。此时的角色是个没有任何模型,由数根线条围成的胶囊体。最后在角色的细节面板中搜索Pawn,将“自动控制玩家”设置为玩家0,这样在运行关卡时就可以控制该角色。当然,目前还没有实现控制方法,所以在此处运行时,角色是不会对键盘输入有任何反应的。
要实现相机跟随,可以通过代码直接创建UE中的弹簧臂对象和相机对象,并将弹簧臂attach(attach可理解为父子关系,你的确可以在角色的完整蓝图左上角的“组件”看到各个组件的层级关系)到自定义角色上、再把相机attach到弹簧臂上。
这个场景可以想象成角色后面连着一根直线(弹簧臂),直线的另一端是相机。这样做的好处有两个:1. 相机与角色通过弹簧臂相连,实现了相机跟随; 2. 弹簧臂是碰撞体,因此我们的角色在靠近墙壁时,相机不会穿模到墙壁的另一边,从而保证玩家视野中不会因为墙壁遮挡自己的角色。同时,在代码中声明的对象可以利用UPROPERTY宏定义,使得该对象可以在UE中直接修改。
//SCharacter.h
class USpringArmComponent;
class UCameraComponent;
UCLASS()
class ACTIONROGUELIKE_API ASCharacter : public ACharacter
{
GENERATED_BODY()
protected:
UPROPERTY(VisibleAnywhere)
UCameraComponent* CameraComp;
UPROPERTY(VisibleAnywhere)
USpringArmComponent* SpringArmComp;
}
//SCharacter.cpp
ASCharacter::ASCharacter()
{
SpringArmComp = CreateDefaultSubobject<USpringArmComponent>("SpringarmComp");
SpringArmComp->SetupAttachment(RootComponent);
SpringArmComp->bUsePawnControlRotation = true;
CameraComp = CreateDefaultSubobject<UCameraComponent>("CameraComp");
CameraComp->SetupAttachment(SpringArmComp);
}
此时运行关卡(注意是保存在Maps中的关卡,而不是打开UE默认显示的关卡),可以发现角色和相机已经通过弹簧臂连接。
通过在角色和相机的中间插入移除正方体进行阻挡相机的实验,可以更好地理解弹簧臂在其中的作用。如果不添加弹簧臂,正方体就可能从角色和相机中间穿过,从而阻挡玩家视线。
3. 人物移动与转向
人物移动的相关代码需要编写在自动生成的SetupPlayerInputComponent函数中。这个函数中已经传入了PlayerInputComponent组件,所以不需要像上面自己创建新的弹簧臂和相机控件。人物移动对应UE中的轴(Axis)绑定,因为移动的幅度是个连续值,如使用游戏手柄时,不同程度推动摇杆可能对应不同的移动速度。
//SCharacter.cpp
// 实现角色向前移动
void ASCharacter::MoveForward(float value)
{
// 朝某个方向前进value,GetActorForwardVector指向角色正前方
AddMovementInput(GetActorForwardVector(), value);
}
void ASCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
// “UE中调用的名称”,this指针表示移动这个角色,&自定义移动方法
PlayerInputComponent->BindAxis("MoveForward", this, &ASCharacter::MoveForward);
}
随后,需要在UE中设置MoveForward的具体输入。在编辑 -> 项目设置 -> 输入 -> 绑定中添加轴映射 -> 输入MoveForward,即与BindAxis函数中第一个参数一致 -> 添加键盘输入W和S,两者的value分别为1.0和-1.0。这样设置后,在按下S的时候会调用MoveForward函数,传入-1.0,即角色前进负值,等同于后退。
此时运行关卡,就可以发现角色能够进行前进后退,且相机始终保持跟随。同理,利用UE中提供的GetActorRightVector()方法,可以实现角色的左右移动。
//SCharacter.cpp
void ASurCharacter::MoveRight(float value)
{
AddMovementInput(GetActorRightVector(), value);
}
void ASCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
// “UE中调用的名称”,this指针表示移动这个角色,&自定义移动方法
PlayerInputComponent->BindAxis("MoveRight", this, &ASurCharacter::MoveRight);
}
最后,要实现角色通过鼠标转向则更简单。类似平移可以用XYZ轴度量,旋转也可以分解成这三个方向,UE中的Yaw方向表示绕Z轴旋转、即水平旋转。利用UE提供的AddControllerYawInput()函数可以得到Yaw方向的偏转;AddControllerPitchInput()函数可以得到Pitch方向的旋转。
//SCharacter.cpp
PlayerInputComponent->BindAxis("Turn",this,&APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis("LookUp",this,&APawn::AddControllerPitchInput);
然后,同样需要在UE设置输入如下,鼠标X表示鼠标的横向(horizontal)移动,鼠标Y表示纵向(vertical)移动:
此外,在设置LookUp输入后,还需要在弹簧臂的“摄像机设置”属性中 勾选 “使用Pawn控制旋转”。
或者添加代码
ASCharacter::ASCharacter()
{
SpringArmComp->bUsePawnControlRotation = true;
}
添加模型和动画
1. 为角色添加网格体
网格体可以理解为由一组多边形组成的中空的三维模型,UE中的网格体分为两种:骨骼网格体和静态网格体。前者包括构成一组多边形组成的模型,和使多边形顶点产生动画的的骨骼;后者只包含一组多边形组成的模型。两者的区别在于是否有骨骼可以绑定动画
在UE中打开自定义角色Player的完整蓝图 -> 左上角“组件”中点击网格体 -> 右侧“细节”面板 -> 网格体 -> 选择Gideon模型。
但此时进入世界会发现,角色悬空,且没有朝向胶囊体的正前方(蓝色箭头)。
因此需要在Player的网格体组件的变换属性中,对位置和旋转两个参数进行相应调整。
2. 为角色添加动画
在网格体的动画属性中指定对应的Gideon角色动画
控制旋转
控制角色旋转
到目前为止,我们已经实现了初步的角色运动和旋转。但细看就会发现,角色的朝向和摄像机的旋转是一起的,即我们移动鼠标,角色也跟着旋转。但参考游戏经验,摄像机旋转和角色旋转应该是两个独立的事件。
因此,我们需要分离控制相机的鼠标控制旋转(ControlRotation)和角色旋转(Pawn’s Rotation)。只需要在Player(自身)的Pawn属性里取消勾选Use Controller Rotation Yaw;并把弹簧臂中的“摄像机设置”的“使用Pawn控制旋转”开启。
这在区分摄像机与角色旋转的同时,我们也不能再控制角色旋转。此外,还需要把Player的“角色移动”中的“将旋转朝向运动”勾选,这样在我们按下A/D进行左右移动时,角色会转向相应的方向,而不是侧着身子漂移。
或者在代码中添加 一劳永逸
//SCharacter.cpp
ASCharacter::ASCharacter()
{
SpringArmComp->bUsePawnControlRotation = true;
GetCharacterMovement()->bOrientRotationToMovement = true;
bUseControllerRotationYaw = false;
}
此时运行关卡,按下左移不松手,会发现角色一直转圈圈。因为每当向左移动,角色也朝向左边,后续的左移会在这个基础之上,所以结果就是这样的圆周运动。
我们目前需要实现的操作方式,是在按下前进后,角色转向相机的方向,然后直线移动;在按下左移时,角色在初始朝向的基础上,转向左方,然后一直朝固定的方向直线移动。也就是在移动时需要一个用来表示空间方向的方向向量vector,且这个vector在按下键盘的时间段内是一个定值。因此,前后移动的实现如下:
//SCharacter.cpp
void ASCharacter::MoveForward(float value)
{
FRotator ControlRot = GetControlRotation();
// 转向只关注水平Yaw方向,因此置0防止影响
ControlRot.Pitch = 0.0f;
ControlRot.Roll = 0.0f;
// 获取相机(鼠标控制器)的朝向,并朝这个方向移动
AddMovementInput(ControlRot.Vector(),value);
}
同理,左右移动只需要在相机朝向的基础上向左/右转90°就可以实现。需要额外说明,在UE中XYZ轴的正向分别对应前方、右方和上方,显示的箭头颜色分别为红色、绿色和蓝色(三基色的习惯顺序)。
void ASCharacter::MoveRight(float value)
{
FRotator ControlRot = GetControlRotation();
ControlRot.Pitch = 0.0f;
ControlRot.Roll = 0.0f;
// 获取相机(鼠标控制器)的朝向,转向右侧,并朝这个方向移动;传入的Y表示右侧
FVector RightVector = FRotationMatrix(ControlRot).GetScaledAxis(EAxis::Y);
AddMovementInput(RightVector,value);
}
再次运行关卡,发现角色可以正确的移动和转向了。
不是,哥们?
旭哥,快和我玩守望先锋!!!😫