游戏开发之在UE4中编写C++代码控制角色
当你运行我们上次做完的项目,你可能会意识到我们移动的摄像机还是默认的那个摄像机,这个默认的摄像机可以自由飞翔。这一节,我们要使得开始的角色是我们的一个Avatar类的实例对象,并且使用键盘控制我们的角色。
一 创建游戏模式类
首先我们要明白什么是GameMode?GameMode包含了各种各样的游戏规则和让游戏引擎描述这个游戏是怎么玩的。
1. 创建游戏模式的步骤如下:
1) 点击文件 --> 新建C++类。

2) 选择Game Mode(游戏模式)。

3) 将其命名为“MyGameMode1”。点击创建类。

二 创建游戏模式的蓝图
UE4会自动启动VS开发环境,然后我们来创建MyGameMode1蓝图:
1) 如图所示操作:

2) 填写蓝图名称,我这里是“BP_GameMode1”,然后点好。

3) 从右侧的细节面板中的Default Pawn Class的下拉选项中选择上次我们创建好的角色蓝图BP_Avatar。

什么是Default Pawn Class?Default Pawn Class就是被角色使用的那一类物体,也就是可以被玩家控制的Actor角色。
4) 点击工具栏的保存,然后退出。
现在运行游戏的话,你可以看到我们使用的摄像头已经是BP_Avatar角色所包含的摄像头了。但是现在还是控制不了角色,因为我们还没设置控制器输入。
三 设置检测键盘输入
1) 点击工具栏的设置,然后点击项目设置。

2) 接下来,点击左侧面板的输入,然后在Axis Mappings(按键映射)后面点击加号,再点击前面的小三角形展开。输入一个名为Forward(前进)的按键映射,然后下面选择W键。接着再添加一个名为Back(后退)的按键映射,然后下面选择D键。Left(左移)对应A键,Right(右移)对于D键。

3) 直接关闭该窗口以保存设置。
四 通过C++代码控制角色行走
1) 现在打开你的VS里面的Avatar.h构造器,添加五个成员函数的声明:
//添加如下三个成员函数,用于角色控制: 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;
2) 然后在Avatar.cpp完成函数体定义:
//游戏开始时被调用以绑定设备输入功能
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相乘后得出的结果:
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的意思是绕横向轴旋转。见下图:

通过C++代码控制角色镜头
在Avatar.h你需要添加两个函数声明:
void Yaw(float amount); void Pitch(float amount);
然后在Avatar.cpp中实现它们:
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)函数体的下面添加这两条语句将输入信息和函数绑定:
InputComponent->BindAxis("Yaw", this, &AAvatar::Yaw);
InputComponent->BindAxis("Pitch", this, &AAvatar::Pitch);
完整代码贴出
Avatar.h完整代码如下:
// 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完整代码如下:
// 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());
}
}
六 最后工作
1. 删除多余角色
我们发现此时场景中有之前为了示例展示出来的多余的一个角色,我们选中该角色,按Delete键将其在场景中删除。
2. 删除多余碰撞体
1) 如下图所示打开BP_Avatar蓝图类编辑器

2) 因为我们已经有胶囊碰撞体了,所以不需要原来模型的碰撞体。在编辑器界面中进行如下操作,将碰撞预设值改为“NoCollision”即可。

经过本节的学习,现在我们的角色已经可以通过键盘前后左右移动和通过鼠标左右移动来绕yaw轴旋转身体了,而鼠标上下移动是不能绕pitch轴旋转身体的(这看起来也不自然),我们后面有其它用途。
最后的效果是这样的:

未完待续
游戏开发之在UE4中编写C++代码控制角色的更多相关文章
- 3D游戏开发之在UE4中创建非玩家角色(NPC)
接着上节我们继续学习,现在我们来创建一些NPC(non-playable characters,非玩家角色).在这个游戏中,当我们靠近NPC时,它们会做出相应的反应. 一 创建C++类 1) 在UE编 ...
- Unity3D游戏开发之C#编程中常见数据结构的比较
一.前言 Unity3D是如今最火爆的游戏开发引擎,它可以让我们能轻松创建诸如三维视频游戏.建筑可视化.实时三维动画等类型的互动内容.它支持2D/3D游戏开发,据不完全统计,目前国内80%的手机游戏都 ...
- Unity3D游戏开发之在Unity3D中视频播放功能的实现
版权声明:欢迎订阅公众号[5厘米的理想],愿生命里的每个小理想,都能成为生命里的小确幸.本文地址为: https://blog.csdn.net/qinyuanpei/article/details/ ...
- Liferay7 BPM门户开发之16: Liferay中用户\站点\组织架构\角色\用户组以及关联关系
用户 . 站点 . 组织架构 . 角色 . 用户组分别是:Users. Site. Organization. Role. UserGroups Users 用户是Liferay portal中的关键 ...
- Cocos2d-x 3.x游戏开发之旅
Cocos2d-x 3.x游戏开发之旅 钟迪龙 著 ISBN 978-7-121-24276-2 2014年10月出版 定价:79.00元 516页 16开 内容提要 <Cocos2d-x ...
- [整理]Unity3D游戏开发之Lua
原文1:[Unity3D]Unity3D游戏开发之Lua与游戏的不解之缘(上) 各位朋友,大家好,我是秦元培,欢迎大家关注我的博客,我地博客地址是blog.csdn.net/qinyuanpei.如果 ...
- [Unity3D]Unity3D游戏开发之从Unity3D到Eclipse
---------------------------------------------------------------------------------------------------- ...
- [Unity3D]Unity3D游戏开发之Lua与游戏的不解之缘(下)
---------------------------------------------------------------------------------------------------- ...
- 【转载】浅谈游戏开发之2D手游工具
浅谈游戏开发之2D手游工具 来源:http://www.gameres.com/459713.html 游戏程序 平台类型: iOS Android 程序设计: 其它 编程语言: 引擎/SDK ...
随机推荐
- (中等) CF 585C Alice, Bob, Oranges and Apples,矩阵+辗转相除。
Alice and Bob decided to eat some fruit. In the kitchen they found a large bag of oranges and apples ...
- UWSGI配置文件---ini和xml示例
一 conf.ini文件: [uwsgi] http = $(HOSTNAME):9033 http-keepalive = 1 pythonpath = ../ module = service ...
- php 访问对象的属性(当属性的键名称为数字时)的方法 {}
这篇文章下面写了一个 将 数组转化成对象的方法, 这个数组的键为数字(即数组为索引数组), 访问对象的时候, $obj->1 显然错位,那到底该怎么样访问对象的数字属性呢? $obj->{ ...
- Python+Selenuim测试网站,只能打开Firefox浏览器却不能打开网页的解决方法
最开始我使用的Selenium版本为2.48,Firefox版本为37,自动化打开网站的时候,可以正常打开. 后来由于Firefox的自检测更新,版本更新为47,导致版本不兼容,自动化打开网站浏览器时 ...
- Extjs4中的布局
布局用于定义容器如何组织内部子元素和控制子元素的大小.在一个应用程序中,作为定义容器的组织形式,布局是一个十分重要的组件.是显示单个子元素?还是垂直或水平显示多个子元素?这些均由布局来定义.并且布局将 ...
- HUST 1606 Naive
预处理一下,然后o(1)询问. #include<cstdio> #include<cstring> #include<cmath> #include<str ...
- mongodb学习(二)分级查询数组中的值
(PS: 标题有点不妥当...) 大概是这样...数据结构如下: 需要模糊查询title的值... mongodb中操作语句: 主要是注意这里urlElements不需要加[0]...我开始的时候写成 ...
- vim中多行注释 和 删除多行注释
1.多行注释: a. 按下ctrl + v,进入列模式; b. 在行首选择需要注释的行; c. 按下"I",进入插入模式: d. 然后输入注释符("//&q ...
- Win10還原成最乾淨的狀態 不必重灌
系統不穩定時我們想到的第一個選擇就是重灌,如果你的作業系統是win10將會有另外一個新選擇,就是透過程式進行還原,讓你的電腦回到剛安裝時的清爽. 工具資訊 [軟體名稱]微軟 Refresh Windo ...
- Objective-C中的Hello World
Objective-C在C语言的基础之上,加入了自己的一些独特的特性,而且在Objective-C中兼容C语言原有的用法.在Objective-C中,实现Hello World和C语言有一定的区别,如 ...