UE4 RTS 风格摄像机
前言
- 本文引擎版本:
4.15
- 项目源码地址
修改历史
2017-4-26
: 简化UpdateCameraDistance
和UpdateCameraPitchDegree
函数计算方式
C++
类
Camera
基类
在虚幻4中,要实现一个 RTS
风格的摄像机,比较好的方式是从 SpectatorPawn
继承一个类。
新建一个类 ARTSCamera
,继承自 ASpectatorPawn
,如图
1. 添加 SpringArm
组件
增加一个 USpringArmComponent
成员对象,作为摄像机的绑定对象
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = Camera)
USpringArmComponent* CameraBoom;
2. 添加 Camera
组件
增加一个 UCameraComponent
成员对象
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = Camera)
UCameraComponent* Camera;
3. 在构造函数中初始化组件成员
ARTSCamera::ARTSCamera()
{
CameraBoom = CreateDefaultSubobject<USpringArmComponent>("CameraBoom");
CameraBoom->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
// 默认角度
CameraBoom->SetRelativeRotation(FRotator(-60.f, 0, 0));
// 禁止碰撞
CameraBoom->bDoCollisionTest = false;
// 默认距离
CameraBoom->TargetArmLength = 1134.f;
Camera = CreateDefaultSubobject<UCameraComponent>("Camera");
Camera->AttachToComponent(CameraBoom, FAttachmentTransformRules::KeepRelativeTransform);
}
GameMode
基类
新建一个 ARTSGameMode
继承自 AGameModeBase
,用来修改游戏中各种对象的类类型参数,添加基础通用代码,目前不需要添加什么代码,如图
PlayerController
基类
新建一个 ARTSPlayerController
继承自 APlayerController
,用来修改游戏中控制逻辑,如图
在构造函数中默认打开鼠标的显示
ARTSPlayerController::ARTSPlayerController()
{
bShowMouseCursor = true;
}
蓝图类
Camera
蓝图类
新建一个蓝图类 BP_RTSCamera
,继承自基类 ARTSCamera
,可以在这里自定义摄像机参数,如角度,距离等
PlayerController
蓝图类
新建一个蓝图类 BP_RTSPlayerController
,继承自基类 ARTSPlayerController
,可以在这里自定义控制逻辑,比如隐藏鼠标指针
GameMode
蓝图类
新建一个蓝图类 BP_RTSGameMode
继承自 ARTSGameMode
,用来做自定义的修改
应用蓝图类
双击打开 BP_RTSGameMode
,将 Classes->Default Pawn Class
修改为 BP_RTSCamera
。 将 Classes -> Player Controller Class
改为 BP_RTSPlayerController
。这样在使用这个 GameMode
的地图运行时,就会用 BP_RTSCamera
来创建默认的 Pawn
对象。 并且将 BP_RTSPlayerController
对象作为玩家控制器。如图
在地图编辑器中,选择 Settings -> World Settings
,在界面右边的 World Settings
标签中,将 Game Mode -> GameMode Override
修改为 BP_RTSGameMode
。这样,当这个地图开始运行时,就会使用 BP_RTSGameMode
对象作为当前的游戏模式,如图
修改完成后,点击 Play
按钮,就会以第三人称视角观察游戏世界,如图
摄像机控制
屏幕卷动控制
窗口卷动方向枚举
在 RTSCamera.h
头文件中增加窗口卷动方向的枚举类型
UENUM()
enum class ECameraScrollDirection
{
None,
Top,
Bottom,
Left,
Right,
};
增加控制窗口是否卷动的摄像机和窗口边缘距离值变量
在 RTSCamera.h
中增加成员变量,当鼠标和窗口边缘的距离小于这个值的时候,窗口就开始卷动
// 触发窗口卷动的鼠标和窗口边缘距离值
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
int32 ScrollingSize;
在构造函数中初始化
ARTSCamera::ARTSCamera()
{
// 其他代码...
// 默认距离为20像素
ScrollingSize = 20.f;
}
增加获取窗口卷动方向的接口
在 RTSCamera.h
中增加成员函数
UFUNCTION(BlueprintCallable, Category = Camera)
ECameraScrollDirection GetCameraScrollDirection();
在 RTSCamera.cpp
中实现
ECameraScrollDirection ARTSCamera::GetCameraScrollDirection()
{
APlayerController* PlayerController = Cast<APlayerController>(Controller);
if (!PlayerController)
{
return ECameraScrollDirection::None;
}
// 获取当前鼠标位置
float MouseX = 0.f, MouseY = 0.f;
if (!PlayerController->GetMousePosition(MouseX, MouseY))
{
return ECameraScrollDirection::None;
}
// 获取当前窗口分辨率
int32 SizeX = 0, SizeY = 0;
PlayerController->GetViewportSize(SizeX, SizeY);
// 根据鼠标位置判断窗口的卷动方向
if (MouseY < ScrollingSize)
{
return ECameraScrollDirection::Top;
}
if (MouseY > SizeY - ScrollingSize)
{
return ECameraScrollDirection::Bottom;
}
if (MouseX < ScrollingSize)
{
return ECameraScrollDirection::Left;
}
if (MouseX > SizeX - ScrollingSize)
{
return ECameraScrollDirection::Right;
}
return ECameraScrollDirection::None;
}
增加更新窗口卷动的接口
在 RTSCamera.h
中增加成员函数来更新窗口卷动
protected:
bool UpdateCameraScroll(float DeltaSeconds);
在 RTSCamera.cpp
中实现
bool ARTSCamera::UpdateCameraScroll(float DeltaSeconds)
{
const ECameraScrollDirection ScrollDirection = GetCameraScrollDirection();
switch (ScrollDirection)
{
case ECameraScrollDirection::Top:
AddMovementInput(FVector::ForwardVector);
break;
case ECameraScrollDirection::Bottom:
AddMovementInput(-FVector::ForwardVector);
break;
case ECameraScrollDirection::Left:
AddMovementInput(-FVector::RightVector);
break;
case ECameraScrollDirection::Right:
AddMovementInput(FVector::RightVector);
break;
default:
break;
}
return ScrollDirection != ECameraScrollDirection::None;
}
重载帧更新接口
重载 ARTSCamera
的帧更新接口,在其中调用 UpdateCameraScroll
函数
protected:
virtual void Tick(float DeltaSeconds) override;
实现
void ARTSCamera::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
UpdateCameraScroll(DeltaSeconds);
}
运行效果
编译程序,运行游戏,当鼠标和窗口边缘间的距离小于预设值后,窗口就会朝着响应的方向卷动,这个预设值可以在 BP_RTSCamera
蓝图中修改 ScrollingSize
属性来调整
鼠标滚轮放大/缩小摄像机距离
增加成员变量
在 ARTSCamera.h
中增加以下成员变量,并重载 BeginPlay
public:
// 最小距离
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Distance")
float MinDistance;
// 最大距离
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Distance")
float MaxDistance;
// 距离变化速度
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Distance")
float DistanceChangeSpeed;
// 每次鼠标滚轮事件修改的距离值
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Distance")
float DistanceChangeDelta;
protected:
virtual void BeginPlay() override;
private:
// 当前距离
float CurrentDistance;
// 最终距离
float DesiredDistance;
在构造函数和 BeginPlay
中初始化成员变量
ARTSCamera::ARTSCamera()
{
// 默认最小距离为800
MinDistance = 800.f;
// 默认最大距离为1134
MaxDistance = 1134.f;
// 默认距离变化速度为每秒334
DistanceChangeSpeed = 334.f;
// 默认每次修改距离为167,167 / 334 = 0.5s,每滚动一下滚轮,从当前距离变化到最终距离要花0.5秒
DistanceChangeDelta = 167.f;
CurrentDistance = MaxDistance;
DesiredDistance = CurrentDistance;
// 其他代码...
CameraBoom->TargetArmLength = CurrentDistance;
}
void ARTSCamera::BeginPlay()
{
Super::BeginPlay();
CurrentDistance = MaxDistance;
DesiredDistance = CurrentDistance;
CameraBoom->TargetArmLength = CurrentDistance;
}
增加距离帧更新函数
protected:
void UpdateCameraDistance(float DeltaSeconds);
在 RTSCamera.cpp
中实现,并在帧更新函数中调用
void ARTSCamera::UpdateCameraDistance(float DeltaSeconds)
{
if (CurrentDistance != DesiredDistance)
{
CurrentDistance = FMath::FInterpConstantTo(CurrentDistance, DesiredDistance, DeltaSeconds, DistanceChangeSpeed);
// 设置摄像机距离
CameraBoom->TargetArmLength = CurrentDistance;
}
}
void ARTSCamera::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
UpdateCameraScroll(DeltaSeconds);
UpdateCameraDistance(DeltaSeconds);
}
鼠标滚轮放大/缩小摄像机角度
增加成员变量
在 ARTSCamera.h
中增加以下成员变量
public:
// 最小角度
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Degree")
float MinPitchDegree;
// 最大角度
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Degree")
float MaxPitchDegree;
// 角度变化速度
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Degree")
float PitchDegreeChangeSpeed;
// 每次鼠标滚轮事件角度变化量
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Degree")
float PitchDegreeChangeDelta;
private:
// 当前角度
float CurrentPitchDegree;
// 最终角度
float DesiredPitchDegree;
在构造函数和 BeginPlay
中初始化成员变量
ARTSCamera::ARTSCamera()
{
MinPitchDegree = -60.f;
MaxPitchDegree = -38.f;
PitchDegreeChangeSpeed = 22.f;
// 默认每次修改角度11度,11 / 22 = 0.5s,每滚动一下滚轮,从当前角度变化到最终角度要花0.5秒
PitchDegreeChangeDelta = 11.f;
CurrentPitchDegree = MinPitchDegree;
DesiredPitchDegree = CurrentPitchDegree;
// 其他代码...
CameraBoom->SetRelativeRotation(FRotator(CurrentPitchDegree, 0.f, 0.f));
}
void ARTSCamera::BeginPlay()
{
Super::BeginPlay();
CurrentDistance = MaxDistance;
DesiredDistance = CurrentDistance;
CurrentPitchDegree = MinPitchDegree;
DesiredPitchDegree = CurrentPitchDegree;
CameraBoom->SetRelativeRotation(FRotator(CurrentPitchDegree, 0.f, 0.f));
CameraBoom->TargetArmLength = CurrentDistance;
}
增加角度帧更新函数
protected:
void UpdateCameraPitchDegree(float DeltaSeconds);
在 RTSCamera.cpp
中实现,并在帧更新函数中调用
void ARTSCamera::UpdateCameraPitchDegree(float DeltaSeconds)
{
if (CurrentPitchDegree != DesiredPitchDegree)
{
CurrentPitchDegree = FMath::FInterpConstantTo(CurrentPitchDegree, DesiredPitchDegree, DeltaSeconds, PitchDegreeChangeSpeed);
// 设置摄像机角度
CameraBoom->SetRelativeRotation(FRotator(CurrentPitchDegree, 0.f, 0.f));
}
}
void ARTSCamera::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
UpdateCameraScroll(DeltaSeconds);
UpdateCameraDistance(DeltaSeconds);
UpdateCameraPitchDegree(DeltaSeconds);
}
滚轮输入事件响应
增加滚轮输入事件
打开 Edit -> Project Settings... -> Engine -> Input
,增加两个事件,如图
绑定事件
在 RTSCamera.h
中重载 SetupPlayerInputComponent
并增加两个事件响应函数
protected:
void OnZoomIn();
void OnZoomOut();
virtual void SetupPlayerInputComponent(UInputComponent* InInputComponent) override;
在 RTSCamera.cpp
中实现
void ARTSCamera::OnZoomIn()
{
DesiredDistance = FMath::Clamp(CurrentDistance - DistanceChangeDelta, MinDistance, MaxDistance);
DesiredPitchDegree = FMath::Clamp(CurrentPitchDegree + PitchDegreeChangeDelta, MinPitchDegree, MaxPitchDegree);
}
void ARTSCamera::OnZoomOut()
{
DesiredDistance = FMath::Clamp(CurrentDistance + DistanceChangeDelta, MinDistance, MaxDistance);
DesiredPitchDegree = FMath::Clamp(CurrentPitchDegree - PitchDegreeChangeDelta, MinPitchDegree, MaxPitchDegree);
}
void ARTSCamera::SetupPlayerInputComponent(UInputComponent* InInputComponent)
{
check(InInputComponent);
InInputComponent->BindAction(TEXT("ZoomIn"), IE_Pressed, this, &ARTSCamera::OnZoomIn);
InInputComponent->BindAction(TEXT("ZoomOut"), IE_Pressed, this, &ARTSCamera::OnZoomOut);
}
结束
最后运行游戏,会得到一个典型的 RTS
风格的第三人称视角的摄像机,可以放大缩小视野,可以在窗口边缘卷动窗口