专栏首页我有一个梦想游戏开发之在UE4中编写C++代码控制角色

游戏开发之在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轴旋转身体的(这看起来也不自然),我们后面有其它用途。

最后的效果是这样的:

未完待续

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 2018新年贺卡 “人脸融合”玩出新高度!

    腾讯AI开放平台(AI.QQ.COM)上线人脸融合技术接口后广受开发者们热捧,从人民日报的刷屏H5「快看呐!这是我的军装照」,到腾讯翻译君的「四六级考前开运」、...

    企鹅号小编
  • 微信跳一跳可以用 Python 刷分;macOS 再曝重大安全漏洞,或许已存在 15 年

    转自:开源中国、solidot、cnBeta、腾讯科技等 ? 如果你每次都能挑到各自的正中间的话,可以 + 2 分,如果连着跳到中间会 + 4、+6、+8、+1...

    企鹅号小编
  • 高通副总裁直击AI利弊,前景堪忧?

    近日,高通Qualcomm执行副总裁Matt Grob在Gigaom 的独家播客专访中,针对人工智能的现状与未来发展趋势表达了自己的看法。 Matt Grob通...

    企鹅号小编
  • 32位汇编第六讲,OllyDbg逆向植物大战僵尸,快速定位阳光基址

          32位汇编第六讲,OllyDbg逆向植物大战僵尸,快速定位阳光基址 一丶基址,随机基址的理解 首先,全局变量的地址,我们都知道是固定的,是在PE文件...

    IBinary
  • DeepCut及DeeperCut:基于Tensorflow的人体姿态估计

      人体姿态估计是机器视觉的一个重要分支,在行为识别、人机交互、游戏、动画等领域有着很广阔的应用前景,是计算机视觉领域中一个既具有研究价值、同时又极具挑战性的热...

    sparkexpert
  • 跳一跳已经过时,微信小程序玩了一招让90后痛哭的回忆杀

    TASTER有趣 你有没有想过,他们会回来 微信更新了,小程序可以玩跳一跳,这个想必大家都知道了: ? 虽然大家都玩的很嗨,而且各种套路都推出来了、但是仔细想想...

    企鹅号小编
  • 碰壁反弹小游戏

    本文作者:IMWeb 一大碗油茶 原文出处:IMWeb社区 未经同意,禁止转载 利用碰壁反弹写的小游戏,可以积分,用左右键控制移动 由于小球是用bor...

    IMWeb前端团队
  • 如何开发一款游戏:游戏开发流程及所需工具

    「文末高能」 编辑 | 哈比 游戏作为娱乐生活的一个方面,参与其中的人越来越多,而大部分参与其中的人都是以玩家的身份。 他们热爱一款游戏,或是被游戏的故事情节、...

    企鹅号小编
  • 腾讯这一波,又会带火哪些域名?

    目前,只需要将微信客户端升级到最新版本,就能体验微信的这个新功能了。相信不少人也已经被下面这个图给刷屏了: ? 许多人都反映,“跳一跳”实在是太好玩了,一不小心...

    企鹅号小编
  • 2017关于人工智能与内容生产最全面的总结

    今年双11,阿里的AI鲁班系统完成了4亿张海报的制作,人工智能内容生产的高效震惊了很多人。 除了阿里的鲁班,实际上近来在文章、图片、影视、游戏等内容领域,已经陆...

    企鹅号小编

扫码关注云+社区

领取腾讯云代金券