上一次我们尝试新建一个关卡。这一次我们尝试创建一个可以移动的人物。
首先需要做一些准备工作,针对vs不能自动补全头文件这一问题可以安装reshaper插件,可帮助补全。如果不安装插件且不知道头文件名称可复制相关类名查询API。
网络上相关教程很多,在此我只选取官方Demo给出的源码,尝试以这种方式让人物动起来。
首先我们在内容浏览器中创建一个新的C++类,命名为HeroDemo,继承自Character类。
我们在头文件中做一些声明:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Camera)
class USpringArmComponent* CameraBoom;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Camera)
class UCameraComponent* FollowCamera;
protected:
void MoveForward(float Axis);
//设置人物前进
void MoveRight(float Axis);
//设置人物向右移动
public:
FORCEINLINE class USpringArmComponent* GetCamera() const { return CameraBoom; }
FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
UPROPERTY通常用于说明一些状态,如是否在其他位置可以编辑、是否可以被蓝图读取等,其中Category通常表示在编辑器的名称。
USpringArmComponent 为一个组件类,它提供相机的弹簧臂,可以实现视角的移动等。
UCameraComponent 为相机组件类,在可以提供视野等。
FORCEINLINE 是c++关键字 用于内联函数。区别于inline,FORCEINLINE 根据程序员判断进行内联,而非根据编辑器性能和优化分析。
头文件已经编写完成,现在我们开始完成具体内容。
AHeroDemo::AHeroDemo()
{
//设置输入控制默认玩家
AutoPossessPlayer = EAutoReceiveInput::Player0;
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
//设置碰撞体积大小
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.f);
//保证控制器不会随视角反转
bUseControllerRotationPitch = false;
bUseControllerRotationRoll = false;
bUseControllerRotationYaw = false;
//获取人物移动,此为Character内置
GetCharacterMovement()->bOrientRotationToMovement = true;
GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f);
GetCharacterMovement()->JumpZVelocity = 600.0f;//跳跃速度
GetCharacterMovement()->AirControl = 0.2f;
//设置弹簧臂
CameraBoom = CreateDefaultSubobject(TEXT("CameraBoom"));//创建默认组件
CameraBoom->SetupAttachment(RootComponent);//附加到根组件
CameraBoom->TargetArmLength = 300.0f;//弹簧臂长度
CameraBoom->bUsePawnControlRotation = true;//设置是否根据输入控制旋转
//设置相机
FollowCamera = CreateDefaultSubobject(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
FollowCamera->bUsePawnControlRotation = false;
}
CreateDefaultSubobject 该函数是个模板函数,用于创建组件或子对象,然后返回指向新建组件内存区域的指针。
关于bOrientRotationToMovement
用于更新人物的旋转。
关于SocketName问题
指弹簧臂末端位置
下面开始绑定按键
void AHeroDemo::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
//用于绑定函数和按键,保证输入
Super::SetupPlayerInputComponent(PlayerInputComponent);
//check用于检查指针
check(PlayerInputComponent);
PlayerInputComponent->BindAction(("Jump"), IE_Pressed, this, &ACharacter::Jump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
PlayerInputComponent->BindAxis("MoveForward", this, &AHeroDemo::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AHeroDemo::MoveRight);
PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
}
BindAction用于绑定触发事件,如跳跃等。
第一个参数为名称,之后再编辑器中需要设置
第二个为触发条件,如IE_Pressed和IE_Released表示按下/松开触发。
第三个为执行主体,通常为this
第四个为调用函数。
这里调用了ACaharacter和Apawn的一些函数,这也是为何继承Character类的原因,因为UE4底层已经给我们编写好了基础的移动,我们只需要调用就可以。当然或许高层次的开发会需要重写底层代码,在此不做考虑。
然后需要在编辑器中设置绑定
点击编辑->项目设置->输入,并进行如下设置:
向后和向左移动只需要将值设为复数即可完成。
下面编写移动的函数
void AHeroDemo::MoveForward(float Axis)
{
if((Controller!=NULL)&(Axis!=0.0f))
{
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
AddMovementInput(Direction, Axis);
}
}
void AHeroDemo::MoveRight(float Axis)
{
if ((Controller != NULL) & (Axis != 0.0f))
{
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
AddMovementInput(Direction, Axis);
}
}
两段代码很相似。大体模式是首先获得控制器的旋转,之后选择性输入一个方向的旋转。FRotationMatrix为矩阵结构,可以让一个物体轻松地转向另一个物体。这里应该是让actor转向controller的方向,从而实现人物转动。GetUnitAxis()将获取的控制器左右旋转角度转化为旋转后x/y轴方向的单位向量存为Direction(要去的方向)。最后AddMoveMentInput添加移动,两个方向为方向和长度。在Pawn和Character类中,底层为我们写好按帧刷新读取,故不用额外操作。
到此代码已经完成,下面回到编辑器编译,运行。成功。
向量存为Direction(要去的方向)。最后AddMoveMentInput添加移动,两个方向为方向和长度。在Pawn和Character类中,底层为我们写好按帧刷新读取,故不用额外操作。
[外链图片转存中…(img-sdlH6uKn-1635072344978)]
到此代码已经完成,下面回到编辑器编译,运行。成功。
以上许多都是个人理解和感受,如有不正确、不到位的地方希望大家指正。