当你运行我们上次做完的项目,你可能会意识到我们移动的摄像机还是默认的那个摄像机,这个默认的摄像机可以自由飞翔。这一节,我们要使得开始的角色是我们的一个Avatar类的实例对象,并且使用键盘控制我们的角色。
首先我们要明白什么是GameMode?GameMode包含了各种各样的游戏规则和让游戏引擎描述这个游戏是怎么玩的。
UE4会自动启动VS开发环境,然后我们来创建MyGameMode1蓝图:
什么是Default Pawn Class?Default Pawn Class就是被角色使用的那一类物体,也就是可以被玩家控制的Actor角色。
现在运行游戏的话,你可以看到我们使用的摄像头已经是BP_Avatar角色所包含的摄像头了。但是现在还是控制不了角色,因为我们还没设置控制器输入。
2) 接下来,点击左侧面板的输入,然后在Axis Mappings(按键映射)后面点击加号,再点击前面的小三角形展开。输入一个名为Forward(前进)的按键映射,然后下面选择W键。接着再添加一个名为Back(后退)的按键映射,然后下面选择D键。Left(左移)对应A键,Right(右移)对于D键。
1
2
3
4
5
6
|
//添加如下三个成员函数,用于角色控制:
void
SetupPlayerInputComponent(
class
UInputComponent* InputComponent) override;
//覆写虚函数,当有输入的时候被调用以绑定功能
void
MoveForward(
float
amount);
void
MoveBack(
float
amount);
void
MoveLeft(
float
amount);
void
MoveRight(
float
amount);
|
并删除原有的这一行:virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;
1
2
3
4
5
6
7
8
9
|
//游戏开始时被调用以绑定设备输入功能
void
AAvatar::SetupPlayerInputComponent(
class
UInputComponent* InputComponent)
{
check(InputComponent);
//检查空指针
InputComponent->BindAxis(
"Forward"
,
this
, &AAvatar::MoveForward);
InputComponent->BindAxis(
"Back"
,
this
, &AAvatar::MoveForward);
InputComponent->BindAxis(
"Left"
,
this
, &AAvatar::MoveRight);
InputComponent->BindAxis(
"Right"
,
this
, &AAvatar::MoveRight);
}
|
上面的InputComponent::BindAxis(...)函数用于将按键信息于函数调用绑定。例如当玩家按下W键,引擎就会检测到有我们之前命名的"Forward"按键信息,然后自动去调用当前类的AAvatar::MoveForward(float amount)函数。其它三个按键也是如此运作。所以接下来定义好这四个函数就可以了,这里的参数amount是设备输入量经过与scale相乘后得出的结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
void
AAvatar::MoveForward(
float
amount)
{
// Controller控制器当前拥有该actor。例如如果当前角色死了,actor不存在了,此时Controller控制器没有拥有任何一个actor,那么如果我们还想去控制它,就会出错。
// 如果控制器没有拥有actor或者移动量是0,不能进入该函数。
if
(Controller && amount)
{
// GetActorForwardVector()取得角色向前的向量
FVector fwd = GetActorForwardVector();
// 我们调用AddMovementInput来在‘fwd’向量的方向上移动角色‘amount’个单位
AddMovementInput(fwd, amount);
}
}
void
AAvatar::MoveBack(
float
amount)
{
if
(Controller && amount)
{
// GetActorForwardVector()取得角色向前的向量,加上负号,该向量就向后,所以取得了角色向后的向量
FVector back = -GetActorForwardVector();
AddMovementInput(back, amount);
}
}
// 后面的函数类似前面,很容易懂
void
AAvatar::MoveLeft(
float
amount)
{
if
(Controller && amount)
{
FVector left = -GetActorRightVector();
AddMovementInput(left, amount);
}
}
void
AAvatar::MoveRight(
float
amount)
{
if
(Controller && amount)
{
FVector right = GetActorRightVector();
AddMovementInput(right, amount);
}
}
|
接下来我们用第二步同样的操作打开项目设置并添加Yaw和Pitch按键信息,分别对应的是鼠标的X坐标和Y坐标。
注意Yaw的意思是绕竖轴旋转,Pitch的意思是绕横向轴旋转。见下图:
在Avatar.h你需要添加两个函数声明:
1
2
|
void
Yaw(
float
amount);
void
Pitch(
float
amount);
|
然后在Avatar.cpp中实现它们:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
void
AAvatar::Yaw(
float
amount)
{
if
(Controller && amount)
{
// AddControllerYawInput()函数用于改变控制器的Yaw变量,即增加纵向轴旋转量。
// GetWorld()函数取得世界指针UWorld*,通过世界指针调用GetDeltaSeconds()取得每帧耗费的时间。
// 之所以要乘以每帧耗费的时间,是为了使得每一【秒】都增加200.0f * amount的改变量。
// 如果不乘以每帧耗费的时间,那么每一【帧】都会增加200.0f * amount的改变量。(注意由于每秒渲染量不同,所以每秒的帧数不一定是固定的。)
// 通过帧数来控制变量,那么游戏看起来就不那么流畅。试想,机子性能好的时候游戏角色动作就迅速,机子性能差的时候游戏角色动作就慢,这对于玩家公平吗?
AddControllerYawInput(200.f * amount * GetWorld()->GetDeltaSeconds());
}
}
// 下面的函数与上面雷同,不再赘述
void
AAvatar::Pitch(
float
amount)
{
if
(Controller && amount)
{
AddControllerPitchInput(200.f * amount * GetWorld()->GetDeltaSeconds());
}
}
|
在void AAvatar::SetupPlayerInputComponent(class UInputComponent* InputComponent)函数体的下面添加这两条语句将输入信息和函数绑定:
1
2
|
InputComponent->BindAxis(
"Yaw"
,
this
, &AAvatar::Yaw);
InputComponent->BindAxis(
"Pitch"
,
this
, &AAvatar::Pitch);
|
Avatar.h完整代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "GameFramework/Character.h"
#include "Avatar.generated.h"
UCLASS()
class
MYFIRSTCODE_API AAvatar :
public
ACharacter
{
GENERATED_BODY()
public
:
// Sets default values for this character's properties
AAvatar();
// Called when the game starts or when spawned
virtual
void
BeginPlay() override;
// Called every frame
virtual
void
Tick(
float
DeltaSeconds ) override;
// Called to bind functionality to input
//virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
//添加如下三个成员函数,用于角色控制:
void
SetupPlayerInputComponent(
class
UInputComponent* InputComponent) override;
//覆写虚函数,当有输入的时候被调用以绑定功能
void
MoveForward(
float
amount);
void
MoveBack(
float
amount);
void
MoveLeft(
float
amount);
void
MoveRight(
float
amount);
void
Yaw(
float
amount);
void
Pitch(
float
amount);
};
|
Avatar.cpp完整代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyFirstCode.h"
#include "Avatar.h"
// Sets default values
AAvatar::AAvatar()
{
// 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
;
}
// Called when the game starts or when spawned
void
AAvatar::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void
AAvatar::Tick(
float
DeltaTime )
{
Super::Tick( DeltaTime );
}
// Called to bind functionality to input
//void AAvatar::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
//{
// Super::SetupPlayerInputComponent(PlayerInputComponent);
//
//}
//游戏开始时被调用以绑定设备输入功能
void
AAvatar::SetupPlayerInputComponent(
class
UInputComponent* InputComponent)
{
check(InputComponent);
//检查空指针
InputComponent->BindAxis(
"Forward"
,
this
, &AAvatar::MoveForward);
InputComponent->BindAxis(
"Back"
,
this
, &AAvatar::MoveForward);
InputComponent->BindAxis(
"Left"
,
this
, &AAvatar::MoveRight);
InputComponent->BindAxis(
"Right"
,
this
, &AAvatar::MoveRight);
InputComponent->BindAxis(
"Yaw"
,
this
, &AAvatar::Yaw);
InputComponent->BindAxis(
"Pitch"
,
this
, &AAvatar::Pitch);
}
void
AAvatar::MoveForward(
float
amount)
{
// Controller控制器当前拥有该actor。例如如果当前角色死了,actor不存在了,此时Controller控制器没有拥有任何一个actor,那么如果我们还想去控制它,就会出错。
// 如果控制器没有拥有actor或者移动量是0,不能进入该函数。
if
(Controller && amount)
{
// GetActorForwardVector()取得角色向前的向量
FVector fwd = GetActorForwardVector();
// 我们调用AddMovementInput来在‘fwd’向量的方向上移动角色‘amount’个单位
AddMovementInput(fwd, amount);
}
}
void
AAvatar::MoveBack(
float
amount)
{
if
(Controller && amount)
{
// GetActorForwardVector()取得角色向前的向量,加上负号,该向量就向后,所以取得了角色向后的向量
FVector back = -GetActorForwardVector();
AddMovementInput(back, amount);
}
}
// 后面的函数类似前面,很容易懂
void
AAvatar::MoveLeft(
float
amount)
{
if
(Controller && amount)
{
FVector left = -GetActorRightVector();
AddMovementInput(left, amount);
}
}
void
AAvatar::MoveRight(
float
amount)
{
if
(Controller && amount)
{
FVector right = GetActorRightVector();
AddMovementInput(right, amount);
}
}
void
AAvatar::Yaw(
float
amount)
{
if
(Controller && amount)
{
// AddControllerYawInput()函数用于改变控制器的Yaw变量,即增加纵向轴旋转量。
// GetWorld()函数取得世界指针UWorld*,通过世界指针调用GetDeltaSeconds()取得每帧耗费的时间。
// 之所以要乘以每帧耗费的时间,是为了使得每一【秒】都增加200.0f * amount的改变量。
// 如果不乘以每帧耗费的时间,那么每一【帧】都会增加200.0f * amount的改变量。(注意由于每秒渲染量不同,所以每秒的帧数不一定是固定的。)
// 通过帧数来控制变量,那么游戏看起来就不那么流畅。试想,机子性能好的时候游戏角色动作就迅速,机子性能差的时候游戏角色动作就慢,这对于玩家公平吗?
AddControllerYawInput(200.f * amount * GetWorld()->GetDeltaSeconds());
}
}
// 下面的函数与上面雷同,不再赘述
void
AAvatar::Pitch(
float
amount)
{
if
(Controller && amount)
{
AddControllerPitchInput(200.f * amount * GetWorld()->GetDeltaSeconds());
}
}
|
我们发现此时场景中有之前为了示例展示出来的多余的一个角色,我们选中该角色,按Delete键将其在场景中删除。
1) 如下图所示打开BP_Avatar蓝图类编辑器
2) 因为我们已经有胶囊碰撞体了,所以不需要原来模型的碰撞体。在编辑器界面中进行如下操作,将碰撞预设值改为“NoCollision”即可。
经过本节的学习,现在我们的角色已经可以通过键盘前后左右移动和通过鼠标左右移动来绕yaw轴旋转身体了,而鼠标上下移动是不能绕pitch轴旋转身体的(这看起来也不自然),我们后面有其它用途。
最后的效果是这样的: