FPS游戏

FPS游戏,第1张

文章目录
  • 1.创建新角色
    • FPSCharacter.h
    • FPSCharacter.cpp
  • 补充:轴映射与动作( *** 作)映射
  • 2.设置轴映射
  • 3.实现角色移动函数
    • FPSCharacter.h
    • FPSCharacter.cpp
  • 4.实现鼠标摄像机控制
    • FPSCharacter.cpp
    • Pitch Yaw Roll
  • 5.实现角色跳跃
    • FPSCharacter.h
    • FPSCharacter.cpp
  • 6.将网格体添加到角色
  • 7.更改摄像机视角
    • FPSCharacter.h
    • FPSCharacter.cpp
  • 8.将第一人称网格体添加到角色(添加手臂)
    • FPSCharater.h
    • FPSCharater.cpp
  • 收尾
  • 与本章内容关系不大的话
    • 参数解读

本文所有内容皆参考自 实现你的角色

1.创建新角色 FPSCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "FPSCharacter.generated.h"

UCLASS()
class FPSPROJECT_API AFPSCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	AFPSCharacter();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

};
FPSCharacter.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "FPSCharacter.h"

// Sets default values
AFPSCharacter::AFPSCharacter()
{
 	// 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 AFPSCharacter::BeginPlay()
{
	Super::BeginPlay();
	
	//输出日志方便我们确认BeginPlay()已经运行
	check(GEngine != nullptr);
	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using FPSCharacter."));
}

// Called every frame
void AFPSCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

// Called to bind functionality to input
void AFPSCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

}

至此,新角色创建完毕,我们设置为默认角色,并且创建对应蓝图类。


顺便提一嘴,Charater类继承自Pawn类,而Pawn类又继承自Actor类。


补充:轴映射与动作( *** 作)映射

Action *** 作映射:适用于“是/否”输入,当鼠标被按下、松开、双击、按下时间长或者短,都会进行报告,这种一般与射击、跳跃以及开门等物体互动有关。


Axis轴映射:是连续的。


例如手柄上的摇杆、鼠标上的光标等,即使不进行 *** 作,也会逐帧报告,这与我们的视角移动等有关。


会影响到我们行走、四处查看等。


2.设置轴映射

在项目设置中按此设置,完成坐标轴和键盘 *** 作的捆绑。


轴映射会不断被轮询,从而实现无缝的移动过渡和流畅的游戏行为。



3.实现角色移动函数

下面是官方的说法:

在典型的FPS控制模式中,角色的移动轴是相对于摄像机的。


“向前"移动是指"摄像机指向的方向”,“向右"是指"摄像机指向方向的右侧”。


你将使用 PlayerController 获取角色的控制旋转输入值。


另外, MoveForward 函数将忽略控制旋转输入值的俯仰(Pitch)分量,将输入限制在XY平面上,以确保在你向上或向下看时,你的角色将沿着地面移动。


完成此部分以后,我们的代码应该是这样的:

FPSCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "FPSCharacter.generated.h"

UCLASS()
class FPSPROJECT_API AFPSCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// 为此角色的属性设置默认值
	AFPSCharacter();

protected:
	// 当游戏开始或者重生(Spawn)时被调用
	virtual void BeginPlay() override;

public:	
	// 每一帧都会调用的函数,如果在构造函数中启用的话
	virtual void Tick(float DeltaTime) override;

	// 被调用,将功能与输入绑定
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	//UFUNCTION 宏(位于每个函数上方)让引擎可以发觉这些函数,以便将它们纳入序列化和其他引擎功能中。


//UFUNCTION是处理变量的,而UPRORYTY是处理变量的,功能类似。


//处理前后移动的输入 UFUNCTION() void MoveForward(float Value); //处理左右移动的输入 UFUNCTION() void MoveRight(float Value); };

FPSCharacter.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "FPSCharacter.h"

// 为此角色的属性设置默认值
AFPSCharacter::AFPSCharacter()
{
 	// 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;

}

// 当游戏开始或者重生(Spawn)时被调用
void AFPSCharacter::BeginPlay()
{
	Super::BeginPlay();
	
	//输出日志方便我们确认BeginPlay()已经运行
	check(GEngine != nullptr);
	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using FPSCharacter."));
}

// 每一帧都会调用的函数,如果在构造函数中启用的话
void AFPSCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

// 被调用,将功能与输入绑定
void AFPSCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	//处理输入数据
	//将Axis轴映射的MoveForward与MoveForward()函数绑定
	PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
	//将Axis轴映射的MoveRight与MoveRight()函数绑定
	PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);
}

void AFPSCharacter::MoveForward(float Value) {
	//找出前进方向,并记录玩家想向该方向移动
	//将此 *** 作和旋转的X轴捆绑
	FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
	AddMovementInput(Direction, Value);
}

void AFPSCharacter::MoveRight(float Value) {
	//找出右侧的方向并且记录
	//此 *** 作与旋转的Y轴进行绑定
	FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
	AddMovementInput(Direction, Value);
}

保存、编译以后,可以直接在地图中试用了。


4.实现鼠标摄像机控制

我们之前说了,移动的坐标轴与玩家的摄像机有关,那么这里我们就设置摄像机并且通过鼠标来控制视角,以此来影响我们的移动。


在项目设置中进行的设置我是直接参考官方文档的,而这里处理鼠标移动的输入,虚幻引擎在Pawn类中,就已经为我们封装好了函数,不需要像"MoveForward"和"MoveRight"那样需要我们自己声明、定义。


在此,我们仅需要修改FPSCharacter.cpp

此时代码应该如下。


FPSCharacter.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "FPSCharacter.h"

// 为此角色的属性设置默认值
AFPSCharacter::AFPSCharacter()
{
 	// 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;

}

// 当游戏开始或者重生(Spawn)时被调用
void AFPSCharacter::BeginPlay()
{
	Super::BeginPlay();
	
	//输出日志方便我们确认BeginPlay()已经运行
	check(GEngine != nullptr);
	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using FPSCharacter."));
}

// 每一帧都会调用的函数,如果在构造函数中启用的话
void AFPSCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

// 被调用,将功能与输入绑定
void AFPSCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	//处理输入数据
	
	// 移动的绑定
	//将Axis轴映射的MoveForward与MoveForward()函数绑定
	PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
	//将Axis轴映射的MoveRight与MoveRight()函数绑定
	PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);
	
	// 观看、视角的绑定
	//Yaw对应Z轴
	PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::AddControllerYawInput);
	//Pitch对应Y轴
	PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::AddControllerPitchInput);
}

void AFPSCharacter::MoveForward(float Value) {
	//找出前进方向,并记录玩家想向该方向移动
	//将此 *** 作和旋转的X轴捆绑
	FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
	AddMovementInput(Direction, Value);
}

void AFPSCharacter::MoveRight(float Value) {
	//找出右侧的方向并且记录
	//此 *** 作与旋转的Y轴进行绑定
	FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
	AddMovementInput(Direction, Value);
}
Pitch Yaw Roll

不使用XYZ轴,对应数学的右手坐标系,应该是Pitch、Yaw、Roll,分别和XYZ轴对应。


当时更特别的是,UE4使用的是左手坐标系,跟XYZ轴对应的分别是X——Roll,Y——Pitch,Z——Yaw

5.实现角色跳跃

之前我们提到了 *** 作映射和轴映射,实现跳跃这种,按下、松开的时候,才会有相应 *** 作,并不是连续的,我们在 *** 作映射中,按照文档设置出一个Jump参数。


而ACharacter基类中有一个bPressedJump变量与角色跳跃捆绑了,我们只需要通过玩家按键,来修改bPressedJump变量的值,就可以实现跳跃了。


进行相应增加后,对应头文件和源代码如下。


FPSCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "FPSCharacter.generated.h"

UCLASS()
class FPSPROJECT_API AFPSCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// 为此角色的属性设置默认值
	AFPSCharacter();

protected:
	// 当游戏开始或者重生(Spawn)时被调用
	virtual void BeginPlay() override;

public:	
	// 每一帧都会调用的函数,如果在构造函数中启用的话
	virtual void Tick(float DeltaTime) override;

	// 被调用,将功能与输入绑定
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	//UFUNCTION 宏(位于每个函数上方)让引擎可以发觉这些函数,以便将它们纳入序列化和其他引擎功能中。


//UFUNCTION是处理变量的,而UPRORYTY是处理变量的,功能类似。


//处理前后移动的输入 UFUNCTION() void MoveForward(float Value); //处理左右移动的输入 UFUNCTION() void MoveRight(float Value); //Acharacter类内置了一个bPressedJump变量与角色跳跃绑定 //按下空格,置跳跃标志为true UFUNCTION() void StartJump(); //松开空格,置跳跃标志为false UFUNCTION() void StopJump(); };

FPSCharacter.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "FPSCharacter.h"

// 为此角色的属性设置默认值
AFPSCharacter::AFPSCharacter()
{
 	// 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;

}

// 当游戏开始或者重生(Spawn)时被调用
void AFPSCharacter::BeginPlay()
{
	Super::BeginPlay();
	
	//输出日志方便我们确认BeginPlay()已经运行
	check(GEngine != nullptr);
	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using FPSCharacter."));
}

// 每一帧都会调用的函数,如果在构造函数中启用的话
void AFPSCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

// 被调用,将功能与输入绑定
void AFPSCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	//处理输入数据
	
	// 移动的绑定
	//将Axis轴映射的MoveForward与MoveForward()函数绑定
	PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
	//将Axis轴映射的MoveRight与MoveRight()函数绑定
	PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);
	
	// 观看、视角的绑定
	//Yaw对应Z轴,左右是绕着Z轴旋转
	PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::AddControllerYawInput);
	//Pitch对应Y轴,上下是绕着Y轴旋转
	PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::AddControllerPitchInput);

	//设置跳跃 *** 作的绑定
	PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AFPSCharacter::StartJump);
	PlayerInputComponent->BindAction("Jump", IE_Released, this, &AFPSCharacter::StopJump);
}

void AFPSCharacter::MoveForward(float Value) {
	//找出前进方向,并记录玩家想向该方向移动
	//将此 *** 作和旋转的X轴捆绑
	FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
	AddMovementInput(Direction, Value);
}

void AFPSCharacter::MoveRight(float Value) {
	//找出右侧的方向并且记录
	//此 *** 作与旋转的Y轴进行绑定
	FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
	AddMovementInput(Direction, Value);
}

//按下空格,置跳跃标志为true
void AFPSCharacter::StartJump() {
	bPressedJump = true;
}

//松开空格,置跳跃标志为false
void AFPSCharacter::StopJump() {
	bPressedJump = false;
}

至此,保存、编译后,便可以 *** 作一个可以前后左右移动、转动视角、实现跳跃的角色了。


6.将网格体添加到角色

这部分没什么好说的,从文档中的链接去下载网格体的压缩包,然后找一个地方解压,再按照文档说明导入就行。


但是在测试的时候,你可以发现摄像机视角很怪,可以看到自己大肚子emmmm,这就需要我们在下一部分作出调整

7.更改摄像机视角

现在FPSCharater.h引入两个头文件
并在类中增加一个摄像机组件

#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"

//下面部分添加为类的成员变量
//添加一个摄像机组件给角色
	UPROPERTY(VisibleAnywhere)
		UCameraComponent* FPSCameraComponent;

最终。


FPSCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "FPSCharacter.generated.h"

UCLASS()
class FPSPROJECT_API AFPSCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// 为此角色的属性设置默认值
	AFPSCharacter();

protected:
	// 当游戏开始或者重生(Spawn)时被调用
	virtual void BeginPlay() override;

public:	
	// 每一帧都会调用的函数,如果在构造函数中启用的话
	virtual void Tick(float DeltaTime) override;

	// 被调用,将功能与输入绑定
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	//UFUNCTION 宏(位于每个函数上方)让引擎可以发觉这些函数,以便将它们纳入序列化和其他引擎功能中。


//UFUNCTION是处理变量的,而UPRORYTY是处理变量的,功能类似。


//处理前后移动的输入 UFUNCTION() void MoveForward(float Value); //处理左右移动的输入 UFUNCTION() void MoveRight(float Value); //Acharacter类内置了一个bPressedJump变量与角色跳跃绑定 //按下空格,置跳跃标志为true UFUNCTION() void StartJump(); //松开空格,置跳跃标志为false UFUNCTION() void StopJump(); //添加一个摄像机组件给角色 UPROPERTY(VisibleAnywhere) UCameraComponent* FPSCameraComponent; };

接着,我们在构造函数中对其摄像机组件进行初始化。


FPSCharacter.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "FPSCharacter.h"
#include "Components/InputComponent.h"
#include "GameFramework/PlayerController.h"
#include "Engine/Engine.h"

// 为此角色的属性设置默认值
AFPSCharacter::AFPSCharacter()
{
 	// 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;

	// 创建第一人称摄像机组件
	FPSCameraComponent = CreateDefauleSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));

	// 将摄像机组件附加到我们的"胶囊体"网格体上
	check(FPSCameraComponent != nullptr);
	// 此代码创建 UCameraComponent,并将其附加到角色的 CapsuleComponent
	FPSCameraComponent->SetupAttachment(CastChecked<USceneComponent, UCapsuleComponent>(GetCapsuleComponent()));
	
	// 将摄像机置于略高于眼睛上方的位置,SetRelativeLocation用于设置组件默认值
	FPSCameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f + BaseEyeHeight));

	//启用Pawn控制摄像机旋转
	FPSCameraComponent->bUsePawnControlRotation = true;
}

// 当游戏开始或者重生(Spawn)时被调用
void AFPSCharacter::BeginPlay()
{
	Super::BeginPlay();
	
	//输出日志方便我们确认BeginPlay()已经运行
	check(GEngine != nullptr);
	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using FPSCharacter."));
}

// 每一帧都会调用的函数,如果在构造函数中启用的话
void AFPSCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

// 被调用,将功能与输入绑定
void AFPSCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	//处理输入数据
	
	// 移动的绑定
	//将Axis轴映射的MoveForward与MoveForward()函数绑定
	PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
	//将Axis轴映射的MoveRight与MoveRight()函数绑定
	PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);
	
	// 观看、视角的绑定
	//Yaw对应Z轴,左右是绕着Z轴旋转
	PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::AddControllerYawInput);
	//Pitch对应Y轴,上下是绕着Y轴旋转
	PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::AddControllerPitchInput);

	//设置跳跃 *** 作的绑定
	PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AFPSCharacter::StartJump);
	PlayerInputComponent->BindAction("Jump", IE_Released, this, &AFPSCharacter::StopJump);
}

void AFPSCharacter::MoveForward(float Value) {
	//找出前进方向,并记录玩家想向该方向移动
	//将此 *** 作和旋转的X轴捆绑
	FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
	AddMovementInput(Direction, Value);
}

void AFPSCharacter::MoveRight(float Value) {
	//找出右侧的方向并且记录
	//此 *** 作与旋转的Y轴进行绑定
	FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
	AddMovementInput(Direction, Value);
}

//按下空格,置跳跃标志为true
void AFPSCharacter::StartJump() {
	bPressedJump = true;
}

//松开空格,置跳跃标志为false
void AFPSCharacter::StopJump() {
	bPressedJump = false;
}
8.将第一人称网格体添加到角色(添加手臂)

这里呢,其实和之前导入网格体的流程差不多,同样需要通过链接下载一个网格体,并且给角色添加一个组件。


由于 *** 作和之前基本一致,在此就只放代码了。


FPSCharater.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "FPSCharacter.generated.h"

class UCameraComponent;
class USkeletalMeshComponent;

UCLASS()
class FPSPROJECT_API AFPSCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// 为此角色的属性设置默认值
	AFPSCharacter();

protected:
	// 当游戏开始或者重生(Spawn)时被调用
	virtual void BeginPlay() override;

public:	
	// 每一帧都会调用的函数,如果在构造函数中启用的话
	virtual void Tick(float DeltaTime) override;

	// 被调用,将功能与输入绑定
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	//UFUNCTION 宏(位于每个函数上方)让引擎可以发觉这些函数,以便将它们纳入序列化和其他引擎功能中。


//UFUNCTION是处理变量的,而UPRORYTY是处理变量的,功能类似。


//处理前后移动的输入 UFUNCTION() void MoveForward(float Value); //处理左右移动的输入 UFUNCTION() void MoveRight(float Value); //Acharacter类内置了一个bPressedJump变量与角色跳跃绑定 //按下空格,置跳跃标志为true UFUNCTION() void StartJump(); //松开空格,置跳跃标志为false UFUNCTION() void StopJump(); //添加一个摄像机组件给角色 UPROPERTY(VisibleAnywhere) UCameraComponent* FPSCameraComponent; //第一人称网格体(手臂),仅对所属玩家可见 //添加一个网格体组件 UPROPERTY(VisibleDefaultsOnly, Category = Mesh) USkeletalMeshComponent* FPSMesh; };

FPSCharater.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "FPSCharacter.h"
#include "Components/InputComponent.h"
#include "GameFramework/PlayerController.h"
#include "Engine/Engine.h"

// 为此角色的属性设置默认值
AFPSCharacter::AFPSCharacter()
{
 	// 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;

	//************************************************
	// 创建第一人称摄像机组件
	FPSCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));

	// 将摄像机组件附加到我们的"胶囊体"网格体上
	check(FPSCameraComponent != nullptr);

	// 此代码创建 UCameraComponent,并将其附加到角色的 CapsuleComponent
	FPSCameraComponent->SetupAttachment(CastChecked<USceneComponent, UCapsuleComponent>(GetCapsuleComponent()));//GetCapsuleComponent()返回一个胶囊体指针,需要强转
	
	// 将摄像机置于略高于眼睛上方的位置,SetRelativeLocation用于设置组件默认值
	FPSCameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f + BaseEyeHeight));

	//启用Pawn控制摄像机旋转
	FPSCameraComponent->bUsePawnControlRotation = true;
	//************************************************

	//************************************************
	//为所属玩家创建第一人称网格体组件
	FPSMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FirstPersonMesh"));
	check(FPSMesh != nullptr);

	//仅手臂所属玩家可以看见此网格体
	FPSMesh->SetOnlyOwnerSee(true);

	//将FPS网格体附加到FPS摄像机
	FPSMesh->SetupAttachment(FPSCameraComponent);

	//禁用手臂阴影,不然看着会有点奇怪
	FPSMesh->bCastDynamicShadow = false;
	FPSMesh->CastShadow = false;

	// 所属玩家看不到常规(第三人称)全身网格体,营造第一人称的感觉
	GetMesh()->SetOwnerNoSee(true);
	
	//SetOnlyOwnerSee 表示此网格体仅对拥有此角色的 PlayerController 可见。


// 此代码还将网格体附加到摄像机,并禁用某些环境阴影。


让手臂投射阴影会破坏第一人称角色应该只有单个网格体的感觉。


//************************************************ } // 当游戏开始或者重生(Spawn)时被调用 void AFPSCharacter::BeginPlay() { Super::BeginPlay(); //输出日志方便我们确认BeginPlay()已经运行 check(GEngine != nullptr); GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using FPSCharacter.")); } // 每一帧都会调用的函数,如果在构造函数中启用的话 void AFPSCharacter::Tick(float DeltaTime) { Super::Tick(DeltaTime); } // 被调用,将功能与输入绑定 void AFPSCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) { Super::SetupPlayerInputComponent(PlayerInputComponent); //处理输入数据 // 移动的绑定 //将Axis轴映射的MoveForward与MoveForward()函数绑定 PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward); //将Axis轴映射的MoveRight与MoveRight()函数绑定 PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight); // 观看、视角的绑定 //Yaw对应Z轴,左右是绕着Z轴旋转 PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::AddControllerYawInput); //Pitch对应Y轴,上下是绕着Y轴旋转 PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::AddControllerPitchInput); //设置跳跃 *** 作的绑定 PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AFPSCharacter::StartJump); PlayerInputComponent->BindAction("Jump", IE_Released, this, &AFPSCharacter::StopJump); } void AFPSCharacter::MoveForward(float Value) { //找出前进方向,并记录玩家想向该方向移动 //将此 *** 作和旋转的X轴捆绑 FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X); AddMovementInput(Direction, Value); } void AFPSCharacter::MoveRight(float Value) { //找出右侧的方向并且记录 //此 *** 作与旋转的Y轴进行绑定 FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y); AddMovementInput(Direction, Value); } //按下空格,置跳跃标志为true void AFPSCharacter::StartJump() { bPressedJump = true; } //松开空格,置跳跃标志为false void AFPSCharacter::StopJump() { bPressedJump = false; }

收尾

最后,就是设置一下发射物的声明周期,做一些碰撞规则的定义,再就是添加一个十字准星都屏幕中央。


做完这些,这个FPS游戏的C++部分也就到此为止了,下一步添加动画,官方完全就是以蓝图的形式在制作了,那部分我就不打算深入下去了。


所有的代码在官网文档上面都有,我这个学习的过程也只是照着官网的敲,并且熟悉一下流程。


接下来我打算跟着另外一个实战开发,争取开发出一个像模像样的小游戏。


与本章内容关系不大的话

马上就要面试网易互娱的游戏研发岗了,在宣讲会上面面试官提到了找一个开源游戏引擎,在代码中找到与自己学过理论相关的地方,去进行深入的学习。


这相当于是学习虚幻引擎过程中的一个额外的任务吧。


在此我想提一下这个任务,我的完成情况。


首先,在实现角色移动函数里面,有那么两句代码。


PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);

从字面意义上BindAxis中去理解,我们可以大概知道它的作用就是"绑定轴映射"。


我再去浏览了对应函数的定义,定义如下。


template<class UserClass>
	FInputAxisBinding& BindAxis( const FName AxisName, UserClass* Object, typename FInputAxisHandlerSignature::TUObjectMethodDelegate< UserClass >::FMethodPtr Func )
	{
		FInputAxisBinding AB( AxisName );
		AB.AxisDelegate.BindDelegate(Object, Func);
		AxisBindings.Emplace(MoveTemp(AB));
		return AxisBindings.Last();
	}
参数解读
  1. 需要传入一个轴映射里面,我们设置好的数据名称。


    例如我们前面在轴映射设置的"MoveForward"、"MoveRight"等就是。


  2. 需要我们传入一个UserClass类的指针。


  3. 前面两个参数的数据类型都比较正常,第三个就有点奇怪了。


下面我们来细说一下这个参数。


首先,由开始的语句可知这是一个函数模板,而这个typename就是用来给第三个参数使用的。


template<class UserClass>

而这里则是结构体嵌套了一个模板结构体,再嵌套一个模板结构体里面的函数指针。


typename FInputAxisHandlerSignature::TUObjectMethodDelegate< UserClass >::FMethodPtr Func

我之前曾经了解过,怎么在C语言中实现多态,所使用到的方法就是函数指针。



具体可以参考如下链接:
C语言实现多态

因为在C语言里面,并不存在C++那样重写或者重载这样的多态实现方式。


结构体作为C语言的部分,当然也没办法通过C++的方式去实现。


那么想要调用一个结构体的函数,以类似C++调用类里函数,就需要使用函数指针。


在此,就利用函数指针实现了结构体与函数的多态绑定。


——简单一点的说法就是绑定函数。


提到绑定函数,我又想到C++里面使用到的绑定函数有两种方法,一个是std::function,另一个是std::bind。



然后我就试着去想了一下,这个地方能否可以用这两个来代替。


bind是将函数和一些变量或者常量 绑定,类似于 默认参数函数那样,而function也是类似的 *** 作,可以拿一些变量名和函数之间作映射。


但似乎都不适用于这个地方。


因为它这里只是负责传递参数给其他函数,而将要传递参数给到的函数,里面具体是怎么实现的,这个在编写模板类的时候是没法知道的,也就没法使用这两个方法,只能依靠函数指针。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/562523.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-01
下一篇 2022-04-01

发表评论

登录后才能评论

评论列表(0条)

保存