一.创建自己的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));
}
}