游戲開發之在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軸旋轉身體的(這看起來也不自然),我們后面有其它用途。

最后的效果是這樣的:

未完待續

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM