多视角系统,视角之间的切换,输入操作。无人机Pawn视角的实现

发布于:2025-05-12 ⋅ 阅读:(15) ⋅ 点赞:(0)

一.创建自己的PlayerController。它相当于是灵魂,穿梭在不同Pawn之间。也即是切换视角。不同输入的响应也写在这里。这样即使,都有鼠标操作,也能区分。避免了代码的重复耦合。也可以叫做视角系统。

class LZJGAMEMODE_API ALZJPlayerController : public APlayerController
{
	GENERATED_BODY()
	virtual void SetupInputComponent();

	void LeftButtonFunction();
	void DeactiveLeftButtonFunction();
	void RightButtonFunction();
	void DeactiveRightButtonFunction();
	void WheelUpFunction();

	void WheelDownFunction();


	void PanX(float value);
	void PanY(float value);
	
	void RotateX(float value);
	void RotateY(float value);
};

二.开始需要绑定,每一个,按键事件。对应回调那个函数。在函数里,会通过GetPawn,判断当前肉体是哪个Pawn,在对于调用此时这个Pawn的函数功能。

void ALZJPlayerController::SetupInputComponent()
{
	Super::SetupInputComponent();
	InputComponent->BindAction("LeftButtonFunction", IE_Pressed, this, &ALZJPlayerController::LeftButtonFunction);
	InputComponent->BindAction("LeftButtonFunction", IE_Released, this, &ALZJPlayerController::DeactiveLeftButtonFunction);
	InputComponent->BindAction("RightButtonFunction", IE_Pressed, this, &ALZJPlayerController::RightButtonFunction);
	InputComponent->BindAction("RightButtonFunction", IE_Released, this, &ALZJPlayerController::DeactiveRightButtonFunction);
	InputComponent->BindAction("WheelUpFunction", IE_Pressed, this, &ALZJPlayerController::WheelUpFunction);
	InputComponent->BindAction("WheelDownFunction", IE_Pressed, this, &ALZJPlayerController::WheelDownFunction);
	InputComponent->BindAxis("PanX", this, &ALZJPlayerController::PanX);
	InputComponent->BindAxis("PanY", this, &ALZJPlayerController::PanY);
	InputComponent->BindAxis("RotateX", this, &ALZJPlayerController::RotateX);
	InputComponent->BindAxis("RotateY", this, &ALZJPlayerController::RotateY);
	bShowMouseCursor = true;
}

void ALZJPlayerController::LeftButtonFunction()
{
	if (GetPawn()) {
		ALZJPawn* TmpCamPawn = Cast<ALZJPawn>(GetPawn());
		if (TmpCamPawn) {
			TmpCamPawn->ActivePin();
		}
	}
}

void ALZJPlayerController::DeactiveLeftButtonFunction()
{
	if (GetPawn()) {
		ALZJPawn* TmpCamPawn = Cast<ALZJPawn>(GetPawn());
		if (TmpCamPawn) {
			TmpCamPawn->DeActivePin();
		}
	}
}

void ALZJPlayerController::RightButtonFunction()
{
	if (GetPawn()) {
		ALZJPawn* TmpCamPawn = Cast<ALZJPawn>(GetPawn());
		if (TmpCamPawn) {
			TmpCamPawn->ActiveRotate();
		}
	}
	if (GetPawn()) {
		ALZJTravelPawn* TmpCamPawn = Cast<ALZJTravelPawn>(GetPawn());
		if (TmpCamPawn) {
			TmpCamPawn->ActiveRotate();
		}
	}
}

void ALZJPlayerController::DeactiveRightButtonFunction()
{
	if (GetPawn()) {
		ALZJPawn* TmpCamPawn = Cast<ALZJPawn>(GetPawn());
		if (TmpCamPawn) {
			TmpCamPawn->DeActiveRotate();
		}
	}
	if (GetPawn()) {
		ALZJTravelPawn* TmpCamPawn = Cast<ALZJTravelPawn>(GetPawn());
		if (TmpCamPawn) {
			TmpCamPawn->DeActiveRotate();
		}
	}
}

void ALZJPlayerController::WheelUpFunction()
{
	if (GetPawn()) {
		ALZJPawn* TmpCamPawn = Cast<ALZJPawn>(GetPawn());
		if (TmpCamPawn) {
			TmpCamPawn->Zoom(1, 10);
		}
	}
}

void ALZJPlayerController::WheelDownFunction()
{
	if (GetPawn()) {
		ALZJPawn* TmpCamPawn = Cast<ALZJPawn>(GetPawn());
		if (TmpCamPawn) {
			TmpCamPawn->Zoom(0, 10);
		}
	}
}



void ALZJPlayerController::PanX(float value)
{
	if (GetPawn()) {
		ALZJPawn* TmpCamPawn = Cast<ALZJPawn>(GetPawn());
		if (TmpCamPawn) {
			TmpCamPawn->PanX(value);
		}
	}

}

void ALZJPlayerController::PanY(float value)
{
	if (GetPawn()) {
		ALZJPawn* TmpCamPawn = Cast<ALZJPawn>(GetPawn());
		if (TmpCamPawn) {
			TmpCamPawn->PanY(value);
		}
	}
}

void ALZJPlayerController::RotateX(float value)
{
	if (GetPawn()) {
		ALZJPawn* TmpCamPawn = Cast<ALZJPawn>(GetPawn());
		if (TmpCamPawn) {
			TmpCamPawn->RotateX(value * 0.8f);
		}
	}
	if (GetPawn()) {
		ALZJTravelPawn* TmpCamPawn = Cast<ALZJTravelPawn>(GetPawn());
		if (TmpCamPawn) {
			TmpCamPawn->RotateX(value * 0.2f);
		}
	}
}

void ALZJPlayerController::RotateY(float value)
{
	if (GetPawn()) {
		ALZJPawn* TmpCamPawn = Cast<ALZJPawn>(GetPawn());
		if (TmpCamPawn) {
			TmpCamPawn->RotateY(value * 0.8f);
		}
	}
	if (GetPawn()) {
		ALZJTravelPawn* TmpCamPawn = Cast<ALZJTravelPawn>(GetPawn());
		if (TmpCamPawn) {
			TmpCamPawn->RotateY(value * 0.2f);
		}
	}
}

三.无人机视角

1.这里说一下,其中之一的无人机视角。如果肉体是无人机。这个无人机是如何实现的。

安装碰撞盒,用与碰撞。模型不必多说。还有两个物理喷射器的指针,因为我们要从物理上模仿,这个无人机。下面分别是物理银子的参数,模型数组,以及旋转螺旋桨动画函数的声明。

UCLASS()
class WXDRONE_API ADrone : public APawn
{
	GENERATED_BODY()

public:
	// Sets default values for this pawn's properties
	ADrone();

	UPROPERTY(EditAnywhere)
	class UBoxComponent* OutCollision;//碰撞包围盒
	//如果是指针 VisibleAnywhere 和 EditAnywhere 差不多
	UPROPERTY(VisibleAnywhere)
	class UStaticMeshComponent* Mesh;//使用前置声明避免头文件增加复杂度

	UPROPERTY(VisibleAnywhere)
	class UStaticMeshComponent* Paddle1;
	UPROPERTY(VisibleAnywhere)
	class UStaticMeshComponent* Paddle2;
	UPROPERTY(VisibleAnywhere)
	class UStaticMeshComponent* Paddle3;
	UPROPERTY(VisibleAnywhere)
	class UStaticMeshComponent* Paddle4;

	UPROPERTY(VisibleAnywhere)
	class UPhysicsThrusterComponent* UpThruster;
	UPROPERTY(VisibleAnywhere)
	class UPhysicsThrusterComponent* ForwardThruster;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Init")
	float LiftAcc = 20000.0f;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Init")
	float LiftThrustMax = 2000.0f;


	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Init")
	float ForwardAcc = 5000.0f;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Init")
	float ForwardThrustMax = 2000.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Init")
	float TurnStrength = 500000.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Init")
	float PaddleRotateSpeed = 500.0f;

	//UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Init")
	//TSubclassOf<class AMissle> Bullet;
protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

	void Lift(float aixs);
	void Forward(float aixs);
	void Turn(float aixs);
	void Fire();

	//定义了一个数组存储所有螺旋桨
	TArray<UStaticMeshComponent*> Paddles;

	//旋转螺旋桨
	void RotatePaddle(float dt);
public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

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

无人机功能的实现,首先包含需要的头文件,和构造时初始化组件,设置位置参数等。

#include "Drone.h"
#include "Components/StaticMeshComponent.h"
#include "Components/BoxComponent.h"
#include "PhysicsEngine/PhysicsThrusterComponent.h"
#include "Kismet/KismetMathLibrary.h"
#include <Kismet/GameplayStatics.h>


ADrone::ADrone()
{
	// Set this pawn to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	OutCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("OutCollision"));
	RootComponent = OutCollision;

	OutCollision->SetBoxExtent(FVector(90.0f, 90.0f, 40.0f));
	OutCollision->SetSimulatePhysics(true);

	OutCollision->BodyInstance.bLockXRotation = true;
	OutCollision->BodyInstance.bLockYRotation = true;

	//UE的C++中,UE自己的类是由UE自己来管理生命周期的,如果要创建UE的对象需要使用专门的对应的函数来创建
	Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
	Mesh->SetupAttachment(OutCollision);

	Paddle1 = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Paddle1"));
	Paddle2 = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Paddle2"));
	Paddle3 = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Paddle3"));
	Paddle4 = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Paddle4"));

	Paddle1->SetupAttachment(Mesh, TEXT("Paddle1"));
	Paddle2->SetupAttachment(Mesh, TEXT("Paddle2"));
	Paddle3->SetupAttachment(Mesh, TEXT("Paddle3"));
	Paddle4->SetupAttachment(Mesh, TEXT("Paddle4"));

	//将元素加入到数组
	Paddles.Add(Paddle1);
	Paddles.Add(Paddle2);
	Paddles.Add(Paddle3);
	Paddles.Add(Paddle4);

	UpThruster = CreateDefaultSubobject<UPhysicsThrusterComponent>(TEXT("UpThruster"));
	UpThruster->SetupAttachment(RootComponent);
	UpThruster->ThrustStrength = 980.0f;
	UpThruster->SetAutoActivate(true);
	UpThruster->SetWorldRotation(UKismetMathLibrary::MakeRotFromX(-this->GetActorUpVector()));

	ForwardThruster = CreateDefaultSubobject<UPhysicsThrusterComponent>(TEXT("ForwardThruster"));
	ForwardThruster->SetupAttachment(RootComponent);
	ForwardThruster->ThrustStrength = 0.0f;
	ForwardThruster->SetAutoActivate(true);
	ForwardThruster->SetWorldRotation(UKismetMathLibrary::MakeRotFromX(-this->GetActorForwardVector()));

}

然后,在Tick函数里,做动画。螺旋桨动画,和前倾,旋转等。因为Tick是和帧数相关的循环。不会出现明显抖动。如果用定时器,就有可能卡帧抖动。

// Called every frame
void ADrone::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	APlayerController* tmpPC = UGameplayStatics::GetPlayerController(GetWorld(), 0);
	APawn* tmpPawn = tmpPC->GetPawn();
	if (tmpPawn) {
		ADrone* TmpCamPawn = Cast<ADrone>(tmpPawn);
		if (TmpCamPawn) {
			//松开的时候可以维持悬停
			if (InputComponent->IsValidLowLevel())
			{
				if (FMath::Abs(InputComponent->GetAxisValue(TEXT("Lift"))) < 0.000001f)
				{
					UpThruster->ThrustStrength = 980.0f;
				}
				//没有输入就取消前进推力
				if (FMath::Abs(InputComponent->GetAxisValue(TEXT("Forward"))) < 0.000001f)
				{
					ForwardThruster->ThrustStrength = 0.0f;
				}
			}

			//调用旋转螺旋桨
			RotatePaddle(DeltaTime);

			//复原倾斜度
			float pitch = Mesh->GetRelativeRotation().Pitch;
			if (FMath::Abs(pitch) > KINDA_SMALL_NUMBER)
			{
				Mesh->AddRelativeRotation(FRotator(-pitch * DeltaTime, 0.0f, 0.0f));
				if (FMath::Abs(Mesh->GetRelativeRotation().Pitch) <= KINDA_SMALL_NUMBER)
				{
					Mesh->SetRelativeRotation(FRotator(0.0f, 0.0f, 0.0f));
				}
			}
		}
	}
	
}

绑定输入,因为这个输入是特殊的,没有其它的Pawn和它相同。所以就写在Pawn肉体上,其实写在灵魂PlayerController上也可以。让后就是,通过物理喷射器,向前喷射多大的力,向上喷射多大的力,让无人机在移动上模仿物理效果,结合动画,就完成了无人机。这里要将碰撞的阻尼系数,勾上。不然没有摩擦力,无人机收到喷射,就会一直加速,越来越快。停不下来。

// Called to bind functionality to input
void ADrone::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	//轴事件函数不管有没有输入事件,都会每帧调用
	PlayerInputComponent->BindAxis(TEXT("Lift"), this, &ADrone::Lift);
	PlayerInputComponent->BindAxis(TEXT("Forward"), this, &ADrone::Forward);
	PlayerInputComponent->BindAxis(TEXT("Turn"), this, &ADrone::Turn);

	PlayerInputComponent->BindAction(TEXT("Fire"), IE_Pressed, this, &ADrone::Fire);

}

void ADrone::SetPossess()
{
	APlayerController* tmpPC = UGameplayStatics::GetPlayerController(GetWorld(), 0);
	if (tmpPC->IsValidLowLevel())
	{
		tmpPC->Possess(this);
	}
}


void ADrone::Lift(float aixs)
{
	if (aixs != 0)
	{
		UpThruster->ThrustStrength += aixs * LiftAcc * GetWorld()->DeltaTimeSeconds;
		UpThruster->ThrustStrength = FMath::Clamp(UpThruster->ThrustStrength, -LiftThrustMax, LiftThrustMax);
	}


}

void ADrone::Forward(float aixs)
{
	ForwardThruster->ThrustStrength += aixs * ForwardAcc * GetWorld()->DeltaTimeSeconds;
	ForwardThruster->ThrustStrength = FMath::Clamp(ForwardThruster->ThrustStrength, -ForwardThrustMax, ForwardThrustMax);

	float pitch = FMath::Abs(Mesh->GetRelativeRotation().Pitch);
	if (pitch < 30.0f)
	{
		Mesh->AddRelativeRotation(FRotator(-100.0f * aixs * GetWorld()->DeltaTimeSeconds, 0.0f, 0.0f));
	}
}

void ADrone::Turn(float aixs)
{
	OutCollision->AddTorqueInDegrees(-this->GetActorUpVector() * TurnStrength * aixs);
}

void ADrone::Fire()
{
	//FTransform firetransform = Mesh->GetSocketTransform(TEXT("FireSocket"));

	//UClass* bullet = StaticLoadClass(AMissle::StaticClass(), NULL, TEXT("Blueprint'/Game/MyMissle.MyMissle_C'"));


	//AActor* bullet = Cast<AActor>(StaticLoadObject(AMissle::StaticClass(), NULL, TEXT("/Game/MyMissle")));

	//bullet->SetActorTransform(firetransform);
	//GetWorld()->SpawnActor<AMissle>(bullet, firetransform);
}

void ADrone::RotatePaddle(float dt)
{
	for (auto paddle : Paddles)
	{
		paddle->AddRelativeRotation(FRotator(0.0f, dt * PaddleRotateSpeed, 0.0f));
	}
}