ue5创建一个移动的actor学习笔记

ue5创建一个移动的actor学习笔记

包含如何创建一个actor,camera,controller,碰撞

使用cpp创建一个自己的actor

首先我们通过的是cpp文件来进行创建actor。
首先需要做的是生成一个自己的cpp函数,那么这个actor就是自己定制的一个东西,我们给他使用这个类来实例化UStaticMeshComponent

    VisualMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
    VisualMesh->SetupAttachment(RootComponent);

    static ConstructorHelpers::FObjectFinaUStaticMesh> CubeVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Cube.Shape_Cube"));

    if (CubeVisualAsset.Succeeded()) {
        VisualMesh->SetStaticMesh(CubeVisualAsset.Object);
        VisualMesh->SetRelativeLocation(FVector(0.0f, 0.0f, 0.0f));
    }

首先创建一个staticmesh的这个东西,挂在到rootcomp上然后加上一个shape,判断是否成功,如果成功,那么设置相对位置和是否可见。

所以他就是一个静态的mesh。我们在.h中定义这个之后,需要处理的是在构造函数中,创建他的各种属性。然后在tick中可以进行展示来控制他的移动。

    FVector NewLocation = GetActorLocation();
    FRotator NewRotation = GetActorRotation();
    float RunningTime = GetGameTimeSinceCreation();
    float DeltaHeight = (FMath::Sin(RunningTime + DeltaTime) - FMath::Sin(RunningTime));
    NewLocation.Z += DeltaHeight * 20.0f;       //Scale our height by a factor of 20
    float DeltaRotation = DeltaTime * 20.0f;    //Rotate by 20 degrees per second
    NewRotation.Yaw += DeltaRotation;
    SetActorLocationAndRotation(NewLocation, NewRotation);

在这个里面,首先获取到他的位置和角度还有运行的事件,规定这个高度是一个函数sin(runtime+deltatime) - sin(runtime)
总之给出来了一个新的高度然后让我们的newlocation的高度有一个变化的量来更着时间做变化。角度也是一个根据时间来进行变化的。
最后再通过SetActionLocationRotation新的location和newRotation.

这样编译之后,我们就可以得到一个能够旋转的物体了。之后再在我们的editor中转化成一个蓝图类,直接就可以拖入到我们的场景中使用。

plus

我们可以在做一点优化,可以在.h中将我们最后的loc,rota的*20.0f变成一个变量,这样我们定义成属性在蓝图中可以看到那我们就可以在蓝图中修改旋转的速度了。

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FloatingActor");
    float FloatSpeed = 20.0f;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FloatingActor");
    float RotationSpeed = 20.0f;
    NewLocation.Z += DeltaHeight * FloatSpeed;       //Scale our height by a factor of 20
    float DeltaRotation = DeltaTime * RotationSpeed;    //Rotate by 20 degrees per second

如果我们想要更多的可以自己旋转的不同形状的物体,我们在引擎中创建当前的蓝图类的子类之后对形状进行修改即可。
至此,我们学会了可以自己动手cpp文件来创建一个属于自己的actor,具有一定的特点。

给场景放置摄像头

我们按照tutorial,安装两个camera,一个是actor,还有一个是安装在一个新的cube上的组件。
然后新建一个CameraDirector的cpp类来帮助我们来控制这两个Camera.

在.h文件中创建两个camera的属性,可以编辑(在ue5)

    ACameraDirector();
    UPROPERTY(EditAnywhere)
        AActor* CameraOne;
    UPROPERTY(EditAnywhere);
        AActor* CameraTwo;
    
        float TimeToNextCameraChange;

加入gameplaystatic的头文件到.cpp可以来访问一些有用的函数。
#include “Kismet/GameplayStatics.h”
使用的是查看当前玩家的controller,找到玩家当前的controller,如果不在a那么就将controller的视角转化到a然后如果不到b那么就将他转化到b。

下面的代码,作用是算出来一个停留在当前的camera的时间,然后不断的切换两个视角。使用的是controller的setviewtargetwithblend

    const float TimeBetweenCameraChanges = 2.0f;
    const float SmoothBlendTime = 0.75f;
    TimeToNextCameraChange -= DeltaTime;
    if (TimeToNextCameraChange <= 0.0f)
    {
        TimeToNextCameraChange += TimeBetweenCameraChanges;

        // 查找处理本地玩家控制的actor。
        APlayerController* OurPlayerController = UGameplayStatics::GetPlayerController(this, 0);
        if (OurPlayerController)
        {
            if ((OurPlayerController->GetViewTarget() != CameraOne) && (CameraOne != nullptr))
            {
                // 立即切换到摄像机1。
                OurPlayerController->SetViewTarget(CameraOne);
            }
            else if ((OurPlayerController->GetViewTarget() != CameraTwo) && (CameraTwo != nullptr))
            {
                // 平滑地混合到摄像机2。
                OurPlayerController->SetViewTargetWithBlend(CameraTwo, SmoothBlendTime);
            }
        }
    }

编译运行之后,我们的这个类是一个控制camera的类,我们还是需要放到场景中。
放进去之后,点击细节进行调整。将我们设置的两个camera选中之前我们设置好的camera即可进行使用。

extra

我们可以将摄像机附加到移动的actor上创建摇臂来或者移动车镜头,这个是我们之后可以去实现的。但是首先我们需要学会如何创建和控制一个移动的actor

使用一个数组来存储camera,而不是一个或者两个。

camera详解

CameraActor

有关摄像机所有的属性和行为都在CameraComponent中设置,CameraActor类的作用是做CameraComponent的包装器,让这个组件可以直接放在这个关卡里面。也就是说,camera本来是一个组件,但是我们有一个类来让他能够单独的被放到界面里面。
可以在details里面来切换视野。可以在show里面的高级,选择显示camera frustum(视锥)来显示出显示的范围。

CameraComponent可以设施视角和设置。投射类型,视野和后期处理覆盖。如果viewTarget是一个CameraActor或者包含CameraComponent(就是有camera的actor)并且他的bFindCameraComponentWhenViewTarget 设置为true的Actor.那么Actor的calcCamera函数返回Actor的首个视图。
否则,它获取Actor 的位置和旋转方向。

controllers

在playercontroller类中,calccamera函数的行为方式和第二种情况类似,如果存在占有pawn,则会返回其位置以及playerController控制器旋转。

PlayerCameraManager

是一个特定的玩家管理摄像机。类似于一个模拟的眼球。可以在其他物体或者有camera的actor之间混合。
主要的外部职责是要可靠的对get函数进行回应。例如GetCameraViewPoint。
先停一下这个的学习,viewtarget …
感觉会是一个很多的知识和应用。

组件与碰撞

学习利用组件,来将pawn与物理交互,使用例子效果。
我理解是否是自定义一些组件进行使用。

创建和附加组件

新建一个pawn的cpp文件。
创建一个类型是class UParticleSystemComponent* OurParticleSystem;
加入一些引用:

#include "UObject/ConstructorHelpers.h"
#include "Particles/ParticleSystemComponent.h"
#include "Components/SphereComponent.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"

ConstructorHelpers的作用是进行静态加载,动态加载是在runtime的时候进行加载.
ConstructorHelpers::FObjectFinder是这个例子中使用的,作用就是静态的加载资源。忽然发现之前的一个里面并没有include,但是也是可以使用 。

后面的比较明显就是粒子效果,圆,相机,弹簧臂的comp。
构造函数写的比较全面:

// 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;
    // 在哪里设置了物理反应的球体,是最后一个profilename吗?创建了一个spherecomp成为一个rootcomp
    USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponet"));
    RootComponent = SphereComponent;
    SphereComponent->InitSphereRadius(40.0f);
    SphereComponent->SetCollisionProfileName(TEXT("Pawn"));
    
    // 创建一个meshcomp( 静态网格体)创建并绑定到root上。加载球的asset。
    /*
    其与刚才创建的半径40的球体组件无法完全吻合,因此需将把其缩小80%。还需将其向下移动40单位,使其中心与球体组件的中心对齐。
    mesh的球心是0,0,-40和创建的球体组件的中心对齐,然后缩小80%。
    */
    UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));
    SphereVisual->SetupAttachment(RootComponent);
    static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));
    if (SphereVisualAsset.Succeeded()) {
        SphereVisual->SetStaticMesh(SphereVisualAsset.Object);
        SphereVisual->SetWorldLocation(FVector(0.0f, 0.0f, -40.0f));
        SphereVisual->SetWorldScale3D(FVector(0.8f));
    }
    // 给我们可调属性
    OurParticleSystem = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MovementParticles"));
    OurParticleSystem->SetupAttachment(SphereVisual); // 挂在到网格体上
    OurParticleSystem->bAutoActivate = false;
    OurParticleSystem->SetRelativeLocation(FVector(-20.0f, 0.0f, 20.0f)); // 位置?
    static ConstructorHelpers::FObjectFinder<UParticleSystem> ParticleAsset(TEXT("/Game/StarterContent/Particles/P_Fire.P_Fire"));
    if (ParticleAsset.Succeeded())
    {
        OurParticleSystem->SetTemplate(ParticleAsset.Object);
    }
    // 挂在弹簧臂到root
    /*
    可将摄像机以低于其追踪Pawn的速度加速和减速,从而获得更平滑的摄像机附加点。其同时拥有内置功能,可防止摄像机穿过固体对象,
    用于处理如玩家在第三人称游戏里退到角落时等情况。虽然弹簧臂组件并非必要的组件,但它能够轻松让摄像机在游戏中移动时变得更加平滑。
    */
    USpringArmComponent* SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraAttachmentArm"));
    SpringArm->SetupAttachment(RootComponent);
    SpringArm->SetRelativeRotation(FRotator(-45.f, 0.f, 0.f));
    SpringArm->TargetArmLength = 400.0f;
    SpringArm->bEnableCameraLag = true;
    SpringArm->CameraLagSpeed = 3.0f;
    
    // 创建camera组件。只需要挂在到springarm上就行。
    UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("ActualCamera"));
    Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);
    //CameraComp->AttachToComponent(SpringArmComp, FAttachmentTransformRules::KeepRelativeTransform); 这个挂在的是?
    // 现在的player是默认的玩家。
    AutoPossessPlayer = EAutoReceiveInput::Player0;

我们可以看到加入到uproperty中,应该是可以在editor中使用。

配置输入和创建pawn移动的组件

首先对于映射写一个合理的东西
进入该菜单后,可在左侧面板中的 引擎 部分中选择 输入。同时需要设置粒子系统切换的 操作映射 、移动 Pawn 的两个 轴映射,和转动 Pawn 的一个 轴映射。
轴映射加入一个值,向前加一,向右+1。turn+1;

然后创建cpp文件来控制这个映射

我们创建的这个pawn移动组件默认需要不可见,在创建的时候继承一下pawnmovement的组件。
所以我们的这个代码表示的是给pawn的移动组件的重新编写。
我们在这个组件中只需要编写TickComponent函数类似Actor的Tick函数。
在.h文件中声明重写virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;

在.cpp文件中定义这个函数。

void UCollidingPawnMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) {
    // 
    Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

    if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime)) {
        return;
    }

    FVector DesiredMovementThisFrame = ConsumeInputVector().GetClampedToMaxSize(1.0f) * DeltaTime * 150.0f;
    if (!DesiredMovementThisFrame.IsNearlyZero()) {
        FHitResult Hit;
        SafeMoveUpdatedComponent(DesiredMovementThisFrame, UpdatedComponent->GetSocketRotation(), true, Hit);

        // 若发生碰撞,尝试滑过去
        if (Hit.IsValidBlockingHit())
        {
            SlideAlongSurface(DesiredMovementThisFrame, 1.f - Hit.Time, Hit.Normal, Hit);
        }
    }
}

此 TickComponent 函数使用 UPawnMovementComponent 类提供的几种强大功能。
ConsumeInputVector 报告并清空用于存储移动输入的内置变量值。
SafeMoveUpdatedComponent 利用虚幻引擎物理移动Pawn移动组件,同时考虑固体障碍。
移动导致碰撞时, SlideAlongSurface 会处理沿墙壁和斜坡等碰撞表面平滑滑动所涉及的计算和物理,而非直接停留原地,粘在墙壁或斜坡上。
Pawn移动组件中还包含众多值得探究的功能,但本教程范围中暂时无需使用。可查看如 浮动Pawn移动、旁观者Pawn移动 或 角色移动组件等其他类,了解额外使用范例和方法。

同时使用pawn和组件,要使用自定义的pawn的组件,收i按需要将变量添加到pawn类进行追踪。
在collidingpawn中定义的一个刚刚的组件。

UPROPERTY()
class UCollidingPawnMovementComponent* OurMovementComponent;

然后就是熟悉的加组件的操作,首先加入include,然后在构造函数中加入绑定(有些不同因为自带一个movement所以是updatecomp)到rootcomponent上面。并且这个不是场景组件不需要添加location啥的内容

    // 创建移动组件的实例,并要求其更新根。
    OurMovementComponent = CreateDefaultSubobject<UCollidingPawnMovementComponent>(TEXT("CustomMovementComponent"));
    OurMovementComponent->UpdatedComponent = RootComponent;

重写getmovementcomponent函数,因为本来我们的这个pawn自带这个函数现在需要重写。

.h
    virtual UPawnMovementComponent* GetMovementComponent() const override;
.cpp
// 返回我们的movementcomponent
UPawnMovementComponent* ACollidingPawn::GetMovementComponent() const
{
    return OurMovementComponent;
}

设置我们的移动函数,对应刚刚的映射,然后将这些函数绑定到输入事件,给setupcomponent中加入这个绑定。

.h
void MoveForward(float AxisValue);
void MoveRight(float AxisValue);
void Turn(float AxisValue);
void ParticleToggle();
.cpp
 void ACollidingPawn::MoveForward(float AxisValue)
    {
        if (OurMovementComponent && (OurMovementComponent->UpdatedComponent == RootComponent))
        {
            OurMovementComponent->AddInputVector(GetActorForwardVector() * AxisValue);
        }
    }

    void ACollidingPawn::MoveRight(float AxisValue)
    {
        if (OurMovementComponent && (OurMovementComponent->UpdatedComponent == RootComponent))
        {
            OurMovementComponent->AddInputVector(GetActorRightVector() * AxisValue);
        }
    }

    void ACollidingPawn::Turn(float AxisValue)
    {
        FRotator NewRotation = GetActorRotation();
        NewRotation.Yaw += AxisValue;
        SetActorRotation(NewRotation);
    }

    void ACollidingPawn::ParticleToggle()
    {
        if (OurParticleSystem && OurParticleSystem->Template)
        {
            OurParticleSystem->ToggleActive();
        }
    }

okok,最后编译运行之后我们就可以行动啦!!!!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值