目标:当鼠标左键短暂的按一下地面,角色可以自动的移动到点击位置.
1.在CC_PlayerController.cpp里的AbilityInputTagReleased()函数里:
(1)判断是不是鼠标左键,不是鼠标左键执行技能操作:
cpp
/*如果不是鼠标左键*/
if (!InputTag.MatchesTagExact(CC_GameplayTags::Input_LMB))
{
if (GetCC_AbilitySystemComponent() == nullptr) return;
GetCC_AbilitySystemComponent()->AbilityInputTagReleased(InputTag);
return;
}
(2)判断点击地点是不是敌人类,如果是敌人类,执行技能:
cpp
/*如果是鼠标左键*/
if (bTargetting) //是敌人类
{
if (GetCC_AbilitySystemComponent() == nullptr) return;
GetCC_AbilitySystemComponent()->AbilityInputTagReleased(InputTag);
return;
}
(3)判断是不是长按,如果是长按,就退出:
cpp
if(FollowTime > ShortPressThreshold) return; //长按,退出
(4)角色不存在,就退出:
cpp
APawn* ControlledPawn = GetPawn<APawn>();
if (ControlledPawn == nullptr) return;
(5)找到路径
a. 构建文件,添加导航系统模块:
Source/CC_Aura/CC_Aura.Build.cs:
cpp
PrivateDependencyModuleNames.AddRange(new string[] {
"GameplayAbilities",
"GameplayTags",
"GameplayTasks",
"NavigationSystem"
});
b. 同步查找路径位置
cpp
/*同步查找路径位置*/
UNavigationPath* NavPath = UNavigationSystemV1::FindPathToLocationSynchronously(this, ControlledPawn->GetActorLocation(), CachedDestination);
if (NavPath == nullptr) return;
(6)画样条线:
cpp
Spline->ClearSplinePoints(); //清除样条内现有的点
for(const FVector& PointLoc : NavPath->PathPoints)
{
Spline->AddSplinePoint(PointLoc, ESplineCoordinateSpace::World); //将新的位置添加到样条曲线中
DrawDebugSphere(GetWorld(), PointLoc, 8.f, 8, FColor::Orange, false, 5.f); //点击后debug调试
}
//自动寻路将最终目的地设置为导航的终点,方便停止导航
CachedDestination = NavPath->PathPoints[NavPath->PathPoints.Num() - 1];
bAutoRunning = true; //设置当前正常自动寻路状态,将在tick中更新位置
2.蓝图地图里,添加导航网格体边界体积:

全图覆盖:按P键查看范围

3.添加障碍物:

确保他有碰撞:


效果:


4.在帧循环实现自动移动:
(1)创建AutoRun()函数,在Tick()里调用:
cpp
void ACC_PlayerController::PlayerTick(float DeltaTime)
{
Super::PlayerTick(DeltaTime);
CurserTrace();
AutoRun();
}
(2)AutoRun()方法:
cpp
void ACC_PlayerController::AutoRun()
{
if(!bAutoRunning) return;
APawn* ControlledPawn = GetPawn();
if(ControlledPawn == nullptr) return;
//找到距离样条最近的位置
const FVector LocationOnSpline = Spline->FindLocationClosestToWorldLocation(ControlledPawn->GetActorLocation(), ESplineCoordinateSpace::World);
//获取这个位置在样条上的方向
const FVector Direction = Spline->FindDirectionClosestToWorldLocation(LocationOnSpline, ESplineCoordinateSpace::World);
ControlledPawn->AddMovementInput(Direction);
const float DistanceToDestination = (LocationOnSpline - CachedDestination).Length();
if(DistanceToDestination <= AutoRunAcceptanceRadius)
{
bAutoRunning = false;
}
}
(3)如果需要服务器,我们需要在导航系统设置运行客户端导航,这样,会在所有的客户端生成导航体积。

5.调整一下代码:
(1)为了提高性能,建立一个鼠标点击地点的缓存,避免总是调用GetHitResultUnderCursor方法
:CC_PlayerController.h:

CC_PlayerController.cpp:
cpp
//鼠标位置追踪
void ACC_PlayerController::CurserTrace()
{
GetHitResultUnderCursor(ECC_Visibility, false, CursorHit); //获取可视的鼠标命中结果
if(!CursorHit.bBlockingHit) return; //如果未命中直接返回
LastActor = ThisActor;
ThisActor = CursorHit.GetActor();
if (LastActor != ThisActor)
{
if (LastActor) LastActor->UnHighlightActor();
if (ThisActor) ThisActor->HighlightActor();
}
}

(2)修改鼠标追踪函数:
cpp
//鼠标位置追踪
void ACC_PlayerController::CurserTrace()
{
GetHitResultUnderCursor(ECC_Visibility, false, CursorHit); //获取可视的鼠标命中结果
if(!CursorHit.bBlockingHit) return; //如果未命中直接返回
LastActor = ThisActor;
ThisActor = CursorHit.GetActor();
if (LastActor != ThisActor)
{
if (LastActor) LastActor->UnHighlightActor();
if (ThisActor) ThisActor->HighlightActor();
}
}
源代码:
Source/CC_Aura/Public/Player/CC_PlayerController.h:
cpp
// 版权归陈超所有
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "CC_PlayerController.generated.h"
class USplineComponent;
class UCC_AbilitySystemComponent;
struct FGameplayTag;
class UDataAsset_InputConfig;
class ICC_EnemyInterface;
class UInputMappingContext;
class UInputAction;
/**
*
*/
UCLASS()
class CC_AURA_API ACC_PlayerController : public APlayerController
{
GENERATED_BODY()
public:
ACC_PlayerController();
virtual void PlayerTick(float DeltaTime) override;
protected:
virtual void BeginPlay() override;
UPROPERTY()
TObjectPtr<UCC_AbilitySystemComponent> CC_AbilitySystemComponent; //能力系统组件
UCC_AbilitySystemComponent* GetCC_AbilitySystemComponent();
/*鼠标追踪
* 1.高亮敌人类
*/
#pragma region CurserTrace
private:
//鼠标追踪变量
TScriptInterface<ICC_EnemyInterface> LastActor; //上一帧拾取的接口指针
TScriptInterface<ICC_EnemyInterface> ThisActor; //当前帧拾取的接口指针
FHitResult CursorHit; //鼠标点击位置
void CurserTrace(); //鼠标追踪函数
#pragma endregion
/*输入设置
* 1.移动输入
* 2.能力输入
*/
#pragma region Inputs
protected:
virtual void SetupInputComponent() override;
private:
UPROPERTY(EditAnywhere, Category="CC|Input")
TObjectPtr<UDataAsset_InputConfig> DataAssetInputConfig;
void Move(const struct FInputActionValue& InputActionValue);
void AbilityInputTagPressed(FGameplayTag InputTag); //按下
void AbilityInputTagReleased(FGameplayTag InputTag); //结束
void AbilityInputTagHold(FGameplayTag InputTag); //持续
#pragma endregion
/*鼠标点击移动*/
#pragma region ClickToMove
private:
FVector CachedDestination = FVector::ZeroVector; //追击的目的地
float FollowTime = 0.f; //鼠标按住的时间
float ShortPressThreshold = 0.5f; //短按阈值
bool bAutoRunning =false; //是否自动追踪
bool bTargetting = false; //目标锁定
UPROPERTY(EditDefaultsOnly, Category="CC|ClickToMove")
float AutoRunAcceptanceRadius = 50.f; //自动追踪到达目的地的半径后停止
UPROPERTY(VisibleAnywhere, Category="CC|ClickToMove")
TObjectPtr<USplineComponent> Spline; //样条曲线
void AutoRun(); //自动寻路
#pragma endregion
};
Source/CC_Aura/Private/Player/CC_PlayerController.cpp
cpp
// 版权归陈超所有
#include "Player/CC_PlayerController.h"
#include "AbilitySystemBlueprintLibrary.h"
#include "AbilitySystem/CC_AbilitySystemComponent.h"
#include "CC_GameplayTags.h"
#include "Components/SplineComponent.h"
#include "EnhancedInputSubsystems.h"
#include "EnhancedInputComponent.h"
#include "NavigationPath.h"
#include "NavigationSystem.h"
#include "Input/CC_InputComponent.h"
#include "Interations/CC_EnemyInterface.h"
ACC_PlayerController::ACC_PlayerController()
{
bReplicates = true; //确保可复制
Spline = CreateDefaultSubobject<USplineComponent>("Spline");
}
void ACC_PlayerController::PlayerTick(float DeltaTime)
{
Super::PlayerTick(DeltaTime);
CurserTrace();
AutoRun();
}
void ACC_PlayerController::AutoRun()
{
if(!bAutoRunning) return;
APawn* ControlledPawn = GetPawn();
if(ControlledPawn == nullptr) return;
//找到距离样条最近的位置
const FVector LocationOnSpline = Spline->FindLocationClosestToWorldLocation(ControlledPawn->GetActorLocation(), ESplineCoordinateSpace::World);
//获取这个位置在样条上的方向
const FVector Direction = Spline->FindDirectionClosestToWorldLocation(LocationOnSpline, ESplineCoordinateSpace::World);
ControlledPawn->AddMovementInput(Direction);
const float DistanceToDestination = (LocationOnSpline - CachedDestination).Length();
if(DistanceToDestination <= AutoRunAcceptanceRadius)
{
bAutoRunning = false;
}
}
//鼠标位置追踪
void ACC_PlayerController::CurserTrace()
{
GetHitResultUnderCursor(ECC_Visibility, false, CursorHit); //获取可视的鼠标命中结果
if(!CursorHit.bBlockingHit) return; //如果未命中直接返回
LastActor = ThisActor;
ThisActor = CursorHit.GetActor();
if (LastActor != ThisActor)
{
if (LastActor) LastActor->UnHighlightActor();
if (ThisActor) ThisActor->HighlightActor();
}
}
void ACC_PlayerController::BeginPlay()
{
Super::BeginPlay();
//设置鼠标光标
bShowMouseCursor = true; //游戏中是否显示鼠标光标
DefaultMouseCursor = EMouseCursor::Default; //鼠标光标的样式
FInputModeGameAndUI InputModeData;
InputModeData.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock); //将鼠标锁定在视口内
InputModeData.SetHideCursorDuringCapture(false); //鼠标被捕获时是否隐藏
SetInputMode(InputModeData); //设置给控制器
}
UCC_AbilitySystemComponent* ACC_PlayerController::GetCC_AbilitySystemComponent()
{
if (CC_AbilitySystemComponent == nullptr)
{
UAbilitySystemComponent* AbilitySystemComponent = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(GetPawn<APawn>());
CC_AbilitySystemComponent = Cast<UCC_AbilitySystemComponent>(AbilitySystemComponent);
}
return CC_AbilitySystemComponent;
}
//设置输入组件函数(重载)
void ACC_PlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
checkf(DataAssetInputConfig,TEXT("DataAssetInputConfig 指针为空!"));
UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer());
if(Subsystem == nullptr) return;
//添加映射上下文
Subsystem->AddMappingContext(DataAssetInputConfig->DefaultMappingContext, 0);
UCC_InputComponent* CC_InputComponent = CastChecked<UCC_InputComponent>(InputComponent); //将InputComponent转换成增强输入组件
CC_InputComponent->BindInputAction(DataAssetInputConfig, CC_GameplayTags::Input_Move, ETriggerEvent::Triggered, this, &ThisClass::Move); //绑定移动事件
CC_InputComponent->BindAbilityAction(DataAssetInputConfig, this, &ThisClass::AbilityInputTagPressed, &ThisClass::AbilityInputTagReleased, &ThisClass::AbilityInputTagHold);
}
//移动事件的返回函数
void ACC_PlayerController::Move(const struct FInputActionValue& InputActionValue)
{
const FVector2D InputAxisVector = InputActionValue.Get<FVector2D>(); //获取输入操作的2维向量值
const FRotator Rotation = GetControlRotation(); //获取控制器旋转
const FRotator YawRotation(0.f, Rotation.Yaw, 0.f); //通过控制器的垂直朝向创建一个旋转值,忽略上下朝向和左右朝向
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X); //获取向前的值(旋转矩阵)
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y); //获取向右的值,-1到1
if(APawn* ControlledPawn = GetPawn<APawn>())
{
ControlledPawn->AddMovementInput(ForwardDirection, InputAxisVector.Y);
ControlledPawn->AddMovementInput(RightDirection, InputAxisVector.X);
}
}
/*开始按下*/
void ACC_PlayerController::AbilityInputTagPressed(FGameplayTag InputTag)
{
/*如果是鼠标左键*/
if (InputTag.MatchesTagExact(CC_GameplayTags::Input_LMB))
{
bTargetting = ThisActor ? true : false; //是否有敌人?
bAutoRunning = false; //不需要自动追踪
}
}
/*释放*/
void ACC_PlayerController::AbilityInputTagReleased(FGameplayTag InputTag)
{
/*如果不是鼠标左键*/
if (!InputTag.MatchesTagExact(CC_GameplayTags::Input_LMB))
{
if (GetCC_AbilitySystemComponent() == nullptr) return;
GetCC_AbilitySystemComponent()->AbilityInputTagReleased(InputTag);
return;
}
/*如果是鼠标左键*/
if (bTargetting) //是敌人类
{
if (GetCC_AbilitySystemComponent() == nullptr) return;
GetCC_AbilitySystemComponent()->AbilityInputTagReleased(InputTag);
return;
}
if(FollowTime > ShortPressThreshold) return; //长按,退出
/*短按*/
APawn* ControlledPawn = GetPawn();
if (ControlledPawn == nullptr) return;
/*同步查找路径位置*/
UNavigationPath* NavPath = UNavigationSystemV1::FindPathToLocationSynchronously(this, ControlledPawn->GetActorLocation(), CachedDestination);
if (NavPath == nullptr) return;
Spline->ClearSplinePoints(); //清除样条内现有的点
for(const FVector& PointLoc : NavPath->PathPoints)
{
Spline->AddSplinePoint(PointLoc, ESplineCoordinateSpace::World); //将新的位置添加到样条曲线中
DrawDebugSphere(GetWorld(), PointLoc, 8.f, 8, FColor::Cyan, false, 5.f); //点击后debug调试
}
//自动寻路将最终目的地设置为导航的终点,方便停止导航
if (NavPath->PathPoints.Num() > 0)
{
CachedDestination = NavPath->PathPoints[NavPath->PathPoints.Num() - 1];
}
bAutoRunning = true; //设置当前正常自动寻路状态,将在tick中更新位置
FollowTime = 0.f;
bTargetting = false;
}
/*保持*/
void ACC_PlayerController::AbilityInputTagHold(FGameplayTag InputTag)
{
/*如果不是鼠标左键*/
if (!InputTag.MatchesTagExact(CC_GameplayTags::Input_LMB))
{
if (GetCC_AbilitySystemComponent() == nullptr) return;
GetCC_AbilitySystemComponent()->AbilityInputTagHeld(InputTag);
return;
}
/*如果是鼠标左键*/
if (bTargetting) //是敌人类
{
if (GetCC_AbilitySystemComponent() == nullptr) return;
GetCC_AbilitySystemComponent()->AbilityInputTagHeld(InputTag);
return;
}
//非敌人类
FollowTime += GetWorld()->GetDeltaSeconds();
if (CursorHit.bBlockingHit) //阻挡性碰撞
{
CachedDestination = CursorHit.ImpactPoint; //缓存点击位置
}
if (APawn* ControlledPawn = GetPawn<APawn>())
{
const FVector WorldDirection = (CachedDestination - ControlledPawn->GetActorLocation()).GetSafeNormal();
ControlledPawn->AddMovementInput(WorldDirection);
}
if(!bAutoRunning) FollowTime = 0.f;
}