UE4 RTS 拉选提示框
前言
- 本文引擎版本:
4.15
- 项目源码地址
RTS
游戏中,当选取多个单位时会有一个操作,按住鼠标左键后,拖动鼠标,这时候屏幕上会出现一个矩形的范围提示框,当释放鼠标左键后,这个范围内的单位就会被选中,如图所示,本文在前篇 UE4 RTS
风格摄像机
的基础上,实现这个多选提示框
SelectInputInterface
接口
首先定义一个 SelectInputInterface
的接口类,包含鼠标按下和鼠标释放两个接口
SelectInputInterface.h
接口类不能直接在编辑器中直接添加,所以需要自己新建 .h/.cpp
文件,新建一个 SelectInputInterface.h
,内容如下
#pragma once
#include "SelectInputInterface.generated.h"
/**
*
*/
UINTERFACE()
class RTS_API USelectInputInterface : public UInterface
{
GENERATED_BODY()
};
/**
*
*/
class RTS_API ISelectInputInterface
{
GENERATED_BODY()
public:
virtual void OnSelectStart();
virtual void OnSelectEnd();
};
SelectInputInterface.cpp
新建一个 SelectInputInterface.cpp
,默认实现两个空的接口函数,内容如下
#include "RTS.h"
#include "SelectInputInterface.h"
void ISelectInputInterface::OnSelectStart()
{
}
void ISelectInputInterface::OnSelectEnd()
{
}
修改 RTSPlayerController
类
绑定鼠标左键事件
打开 Edit -> Project Settings... -> Engine -> Input
,增加一个事件,如图
在 PlayerController
中响应鼠标事件
在 RTSPlayerController.h
中重载 SetupInputComponent
并且增加两个鼠标事件响应函数
UCLASS()
class RTS_API ARTSPlayerController : public APlayerController
{
// ...
protected:
virtual void SetupInputComponent() override;
// 响应鼠标左键按下
void OnSelectStart();
// 响应鼠标左键释放
void OnSelectEnd();
};
在 RTSPlayerController.cpp
中实现新增的三个函数,目前 OnSelectStart
和 OnSelectEnd
都是空的,之后会逐渐实现
void ARTSPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
check(InputComponent);
InputComponent->BindAction(TEXT("Select"), IE_Pressed, this, &ARTSPlayerController::OnSelectStart);
InputComponent->BindAction(TEXT("Select"), IE_Released, this, &ARTSPlayerController::OnSelectEnd);
}
void ARTSPlayerController::OnSelectStart()
{
}
void ARTSPlayerController::OnSelectEnd()
{
}
修改 RTSCamera
类
当鼠标开始拖动选择单位时,即使鼠标移动到窗口边缘,也不喜欢触发窗口卷动,不然就会让单位选择更加困难
继承 ISelectInputInterface
接口
修改 RTSCamera
类,增加一个继承 ISelectInputInterface
,实现两个接口,并增加一个成员函数 bSelecting
UCLASS()
class RTS_API ARTSCamera : public ASpectatorPawn, public ISelectInputInterface
{
GENERATED_BODY()
public:
ARTSCamera();
// 实现 ISelectInputInterface 接口
virtual void OnSelectStart() override;
virtual void OnSelectEnd() override;
// 其他成员函数...
private:
// 其他成员变量...
bool bSelecting;
};
在 RTSCamera.cpp
中实现新增的两个函数
void ARTSCamera::OnSelectStart()
{
bSelecting = true;
}
void ARTSCamera::OnSelectEnd()
{
bSelecting = false;
}
修改 UpdateCameraScroll
函数
在 UpdateCameraScroll
函数中判断新增的 bSelecting
标志,只有为 false
的时候才继续判断其他卷动条件
bool ARTSCamera::UpdateCameraScroll(float DeltaSeconds)
{
if (bSelecting)
{
return false;
}
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;
}
修改 RTSPlayerController
类
RTSCamera
中已经实现了 ISelectInputInterface
接口,还需要在鼠标按下和释放时候触发接口类的调用,这时候就要回到之前的 RTSPlayerController
中的两个鼠标响应函数中,在这里进行事件触发
void ARTSPlayerController::OnSelectStart()
{
// Notify Camera
ISelectInputInterface* SelectInterfaceInstance = Cast<ISelectInputInterface>(GetPawn());
if (SelectInterfaceInstance)
{
SelectInterfaceInstance->OnSelectStart();
}
}
void ARTSPlayerController::OnSelectEnd()
{
// Notify Camera
ISelectInputInterface* SelectInterfaceInstance = Cast<ISelectInputInterface>(GetPawn());
if (SelectInterfaceInstance)
{
SelectInterfaceInstance->OnSelectEnd();
}
}
最终效果
修改完成后,编译代码,运行游戏,移动鼠标到窗口边缘,这时,按下鼠标左键,就会发现窗口停止卷动,当释放鼠标左键时,又会恢复卷动
在屏幕上绘制选择矩形提示框
在编辑器中新建一个 RTSHUD
的类,继承自 HUD
继承 ISelectInputInterface
接口
修改 RTSHUD.h
,继承 ISelectInputInterface
接口
/**
*
*/
UCLASS()
class RTS_API ARTSHUD : public AHUD, public ISelectInputInterface
{
GENERATED_BODY()
public:
ARTSHUD();
// 矩形提示框填充颜色
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FLinearColor SelectionBoxFillColor;
// 矩形提示框边框颜色
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FLinearColor SelectionBoxBorderColor;
// 矩形提示框边框粗细
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float SelectionBoxBorderThickness;
// 实现 ISelectInputInterface 接口
virtual void OnSelectStart() override;
virtual void OnSelectEnd() override;
protected:
// 重载绘制接口
virtual void DrawHUD() override;
// 矩形提示框更新函数
void UpdateSelectionBox();
private:
// 鼠标左键按下时的窗口坐标
FVector2D StartSelectPosition;
// 当前鼠标窗口坐标
FVector2D CurrentSelectPosition;
// 是否正在绘制矩形提示框
bool bDrawingSelectionBox;
};
修改 RTSHUD.cpp
,实现各个接口
ARTSHUD::ARTSHUD()
{
// 初始化成员变量
SelectionBoxFillColor = FLinearColor(0.2f, 0.8f, 0.2f, 0.1f);
SelectionBoxBorderColor = FLinearColor(0.08f, 0.55f, 0.06f, 1.f);
SelectionBoxBorderThickness = 1.f;
}
void ARTSHUD::OnSelectStart()
{
// 在鼠标左键按下时,保存下鼠标位置,并置位bDrawingSelectionBox
if (!PlayerOwner->GetMousePosition(StartSelectPosition.X, StartSelectPosition.Y))
{
return;
}
CurrentSelectPosition = StartSelectPosition;
bDrawingSelectionBox = true;
}
void ARTSHUD::OnSelectEnd()
{
// 在鼠标左键释放时,清除鼠标位置,并复位bDrawingSelectionBox
CurrentSelectPosition = StartSelectPosition = FVector2D(0.f, 0.f);
bDrawingSelectionBox = false;
}
void ARTSHUD::DrawHUD()
{
// 在 HUD 绘制回调中绘制矩形提示框
UpdateSelectionBox();
Super::DrawHUD();
}
void ARTSHUD::UpdateSelectionBox()
{
// 先判断当前处在绘制状态,并且当前鼠标位置和起始位置不同
if (bDrawingSelectionBox &&
PlayerOwner->GetMousePosition(CurrentSelectPosition.X, CurrentSelectPosition.Y) &&
StartSelectPosition != CurrentSelectPosition)
{
// 绘制填充矩形
DrawRect(
SelectionBoxFillColor,
StartSelectPosition.X,
StartSelectPosition.Y,
CurrentSelectPosition.X - StartSelectPosition.X,
CurrentSelectPosition.Y - StartSelectPosition.Y);
// 绘制四条边框
DrawLine(StartSelectPosition.X, StartSelectPosition.Y, CurrentSelectPosition.X, StartSelectPosition.Y,
SelectionBoxBorderColor, SelectionBoxBorderThickness);
DrawLine(StartSelectPosition.X, StartSelectPosition.Y, StartSelectPosition.X, CurrentSelectPosition.Y,
SelectionBoxBorderColor, SelectionBoxBorderThickness);
DrawLine(StartSelectPosition.X, CurrentSelectPosition.Y, CurrentSelectPosition.X, CurrentSelectPosition.Y,
SelectionBoxBorderColor, SelectionBoxBorderThickness);
DrawLine(CurrentSelectPosition.X, StartSelectPosition.Y, CurrentSelectPosition.X, CurrentSelectPosition.Y,
SelectionBoxBorderColor, SelectionBoxBorderThickness);
}
}
修改 RTSPlayerController
类
同样需要再次修改 RTSPlayerController
类的鼠标响应事件,在其中调用 RTSHUD
类的 ISelectInputInterface
接口
void ARTSPlayerController::OnSelectStart()
{
// Notify Camera
ISelectInputInterface* SelectInterfaceInstance = Cast<ISelectInputInterface>(GetPawn());
if (SelectInterfaceInstance)
{
SelectInterfaceInstance->OnSelectStart();
}
// Notify HUD
SelectInterfaceInstance = Cast<ISelectInputInterface>(GetHUD());
if (SelectInterfaceInstance)
{
SelectInterfaceInstance->OnSelectStart();
}
}
void ARTSPlayerController::OnSelectEnd()
{
// Notify Camera
ISelectInputInterface* SelectInterfaceInstance = Cast<ISelectInputInterface>(GetPawn());
if (SelectInterfaceInstance)
{
SelectInterfaceInstance->OnSelectEnd();
}
// Notify HUD
SelectInterfaceInstance = Cast<ISelectInputInterface>(GetHUD());
if (SelectInterfaceInstance)
{
SelectInterfaceInstance->OnSelectEnd();
}
}
新建 RTSHUD
蓝图类
编译完成后,在编辑器中新建一个蓝图对象 BP_RTSHUD
,继承自 RTSHUD
,可以在其中自定义提示框填充颜色,边框颜色,和边框粗细,然后打开 BP_RTSGameMode
蓝图,修改 HUD
对象类为 BP_RTSHUD
,如图
最终效果
运行游戏,在屏幕上按住鼠标左键并拖动,就会出现一个浅绿色的矩形提示框