虚幻引擎5 GAS开发俯视角RPG游戏 P07-08 点击移动

目标:当鼠标左键一直按住时,角色会跟随鼠标移动

1.添加一些变量

Source/CC_Aura/Public/Player/CC_PlayerController.h

cpp 复制代码
/*鼠标点击移动*/
#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;		//样条曲线

#pragma endregion	

构造函数添加样条曲线组件:

cpp 复制代码
ACC_PlayerController::ACC_PlayerController()
{
	bReplicates = true;		//确保可复制
	
	Spline = CreateDefaultSubobject<USplineComponent>("Spline");
	
}

2.当输入刚刚按住时,判断:

(1)是否是鼠标左键?

(2)鼠标左键按住的是不是敌人类?

(3)需不需要自动追踪?

cpp 复制代码
/*开始按下*/
void ACC_PlayerController::AbilityInputTagPressed(FGameplayTag InputTag)
{
	/*如果是鼠标左键*/
	if (InputTag.MatchesTagExact(CC_GameplayTags::Input_LMB))
	{
		bTargetting = ThisActor ? true : false;		//是否有敌人?
		bAutoRunning = false;		//短按,不需要自动追踪
	}
}

3.在AbilityInputTagHold(FGameplayTag InputTag)函数中,先判断是不是鼠标左键,要是不是的,就正常触发技能:

cpp 复制代码
/*保持*/
void ACC_PlayerController::AbilityInputTagHold(FGameplayTag InputTag)
{
	/*如果不是鼠标左键*/
	if (!InputTag.MatchesTagExact(CC_GameplayTags::Input_LMB))
	{
		if (GetCC_AbilitySystemComponent() == nullptr) return;
		GetCC_AbilitySystemComponent()->AbilityInputTagHeld(InputTag);
		return;
	}

要是鼠标左键,则判断是不是敌人类:

cpp 复制代码
/*如果是鼠标左键*/
	if (bTargetting)	//是敌人类
	{
		if (GetCC_AbilitySystemComponent() == nullptr) return;
		GetCC_AbilitySystemComponent()->AbilityInputTagHeld(InputTag);
		return;
	}

不是敌人类,就跟随鼠标移动:

cpp 复制代码
					//非敌人类
	FollowTime += GetWorld()->GetDeltaSeconds();
	FHitResult Hit;
	if (GetHitResultUnderCursor(ECC_Visibility, false, Hit))
	{
		CachedDestination = Hit.ImpactPoint;
	}
	if (APawn* ControlledPawn = GetPawn<APawn>())
	{
		const FVector WorldDirection = (CachedDestination - ControlledPawn->GetActorLocation()).GetSafeNormal();
		ControlledPawn->AddMovementInput(WorldDirection);
	}

其中:

cpp 复制代码
if (GetHitResultUnderCursor(ECC_Visibility, false, Hit))
	{
		CachedDestination = Hit.ImpactPoint;
	}

这段代码用于在Unreal Engine中实现鼠标点击位置检测功能。通过射线检测获取鼠标光标下的碰撞点坐标,并将该坐标存储为缓存目标位置。

cpp 复制代码
UPROPERTY()
FVector_NetQuantize ImpactPoint;

用于存储碰撞检测中的实际接触点坐标

cpp 复制代码
GetSafeNormal()

向量安全归一化方法实现,用于获取向量的单位向量同时避免除零错误

源码:

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;	//当前帧拾取的接口指针

	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;		//样条曲线

#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 "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();

}


//鼠标位置追踪
void ACC_PlayerController::CurserTrace()
{
	FHitResult CursorHit;
	GetHitResultUnderCursor(ECC_Visibility, false, CursorHit);	//获取可视的鼠标命中结果
	if(!CursorHit.bBlockingHit) return; //如果未命中直接返回

	LastActor = ThisActor;
	ThisActor = CursorHit.GetActor();
	/**
	 * 射线拾取后,会出现的几种情况
	 * 1. LastActor is null   ThisActor is null 不需要任何操作
	 * 2. LastActor is null   ThisActor is valid 高亮ThisActor
	 * 3. LastActor is valid   ThisActor is null 取消高亮LastActor
	 * 4. LastActor is valid   ThisActor is valid LastActor != ThisActor 取消高亮LastActor 高亮ThisActor
	 * 5. LastActor is valid   ThisActor is valid LastActor == ThisActor 不需要任何操作
	 */
	
	if(LastActor == nullptr)
	{
		if(ThisActor != nullptr)
		{
			//case 2
			ThisActor->HighlightActor();
		} // else case 1
	}
	else
	{
		if(ThisActor == nullptr)
		{
			//case 3
			LastActor->UnHighlightActor();
		}
		else
		{
			if(LastActor != ThisActor)
			{
				//case 4
				LastActor->UnHighlightActor();
				ThisActor->HighlightActor();
			} //else case 5
		}
	}	
}


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 (GetCC_AbilitySystemComponent() == nullptr) return;
	GetCC_AbilitySystemComponent()->AbilityInputTagReleased(InputTag);
}

/*保持*/
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();
	FHitResult Hit;
	if (GetHitResultUnderCursor(ECC_Visibility, false, Hit))
	{
		CachedDestination = Hit.ImpactPoint;
	}
	if (APawn* ControlledPawn = GetPawn<APawn>())
	{
		const FVector WorldDirection = (CachedDestination - ControlledPawn->GetActorLocation()).GetSafeNormal();
		ControlledPawn->AddMovementInput(WorldDirection);
	}
}
相关推荐
·白小白2 小时前
力扣(LeetCode) ——209. 长度最小的子数组(C++)
c++·算法·leetcode
ohnoooo92 小时前
251106 算法
数据结构·c++·算法
卡提西亚3 小时前
C++笔记-24-文件读写操作
开发语言·c++·笔记
m0_748248023 小时前
C++ 异常处理全解析:从语法到设计哲学
java·c++·word
csd7983 小时前
从原理到实操:ddraw.dll是什么?为何游戏启动时频繁提示“找不到ddraw.dll”?解决思路全解析
游戏·dll修复工具·dll修复·dll丢失·dll缺失
程序猿追3 小时前
轻量级云原生体验:在OpenEuler 25.09上快速部署单节点K3s
人工智能·科技·机器学习·unity·游戏引擎
B0URNE4 小时前
【Unity基础详解】(7)Unity核心:动画系统
unity·游戏引擎
星释4 小时前
Rust 练习册 :Pig Latin与语言游戏
游戏·rust·c#
我的golang之路果然有问题4 小时前
mac M系列芯片 unity 安装会遇到的错误以及解决
经验分享·学习·macos·unity·游戏引擎