锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

UE4 C++笔记(一):杂记

时间:2023-02-11 09:00:00 弹簧目标连接器

1.默认生成代码(代码相关)

1.1 CoreMinimal是什么?

CoreMinimal第一个文件包含一套来源UE核心编程环境的常见类型(包括FString,FName,TArray等)

CoreMinimal头文件(头文件位于UE4根目录\Engine\Source\Runtime\Core\Public\CoreMinimal.h下)首先包含在大多数引擎头文件中。

1.2 #include “GameFramework/Actor.h”是什么?

因为是基于Actor因此,默认包含生成的源代码Actor头文件。其他同理

1.3 类命名规则

项目名称大写 下划线API 命名(如QUICKSTART_API)

类命名只包含字母数字符,不包含空格。域将通知是否输入无效命名。

1.4 generated.h

任何创造UE C 包含一个类文件MyClass.generated.h虚幻会产生一切反射数据并将其放入文件中。您必须将该文件作为声明类型标头文件中的最后一个包含语句,并将其包含在其中,他必须在底部,切记!

1.5 UE4 反射机制:

反射是指任何实体类在运行状态下都能知道该类的所有属性和方法;

它的任何方法和属性都可以调用。

这种动态获取信息和动态调用对象方法的功能成为语言的反射。

比如加一个UPROPERTY(EditAnywhere),变量值可以实时更改。不必在C 中写死。

1.5.1 公开函数

和IUPROPERTY宏同样,需要提供使用其操作的相关信息,使非程序员开发人员能够使用更多的功能和访问权,有三种选择:

BlueprintCallable 函数以C 形式编写可以从蓝图图表中调用,但只能编辑C 代码被修改或重写以这种方式标记的函数通常具有非程序员编写的功能,但不应修改,否则修改将毫无意义。数学函数鞭尸等函数的经典例子。

②在C header (.h)文件中设置 BlueprintImplementableEvent 函数,但是函数的主体是在蓝图中编写的,而不是C 中。创建此类产品通常是为了使非程序员能够对无预期默认动作或标准行为的特殊情况做出定制反应。在宇宙飞船游戏中,玩家飞船接触能量升级时发生的事件就是这个例子。

这是我们允许本地函数调用蓝图的主要方式。它们就像你在蓝图中实现的虚拟函数。如果没有,函数调用将被忽略。必须注意的是,如果BlueprintImplementableEvent如果没有返回值或输出参数,则将显示为一个可以使用的时间**右键单击**并在蓝图事件图标中选择使用。如果他有返回值或任何输出参数,他将在 我的蓝图(My Blueprints)列出选项卡,然后通过 右键单击 并选择实施函数进行覆盖,请注意,BlueprintImplementableEvent 本地实现没有函数。

BlueprintNativeEvent函数与BlueprintCallable 和 BlueprintImplementableEvent 函数组合相似。C 默认编程行为,但这种行为可以通过蓝图图表的覆盖来补充或替换。编程此类代码时,C 添加代码固定使用命名结尾_Implementation虚拟函数。

(其实是父子关系,子函数会覆盖父子)

变量、定时器和事件 | 虚幻引擎文档 (unrealengine.com)

但在本教程中,倒数被设置为结束时显示GO!,而非0。已使用蓝图可视化脚本完全取代了可视化脚本C 这种情况不会发生在功能上。这个结果并不理想,所以需要添加函数C 调用版本,此操作可以右键单击Countdown Has Finished在快捷菜单中选择节点添加对父函数的调用(Add call to parent function)来完成。

1.6 GENERATED_BODY()

UE4 用这种类型生成的所有必要的样板代码代替此标记。发动机为我们做了很多工作,生成了很多代码。

1.7 Super的作用?

调用父类参数的结构方法必须放在子类结构方法(成员方法不能)中,只能放在结构方法的第一句话中。括号中的参数是指与父类参数结构方法中的参数数据类型相对应的子类中的参数。

1.8 UPROPERTY相关

本身是一个宏,括号内设置相关修饰描述符

将变量公开到主编辑器或蓝图(Defaults)

(1)VisibleAnywhere:该属性在任何地方都可以看到,只显示在蓝图和主编辑器中,但不能编辑。

(2)VisibleDefaultsOnly:不显示在主编辑器中,但显示在蓝图编辑器中(不可编辑)

(3)EditDefaultsOnly:可以在蓝图中编辑,但不显示在主编辑器中,因此不能编辑

(4)EditAnywhere:显示在主编辑器和蓝图编辑器中,可以编辑

(5)EditInstanceOnly:蓝图编辑器不能修改,但可以看出,只有当蓝图实例在场景中点击组件时,才会出现相应的设置。实例可以修改,但只能在原型上进行。

1.9 UStaticMeshComponent* 静态网格变量

用于显示物体.

1.10 构造函数中相关CreateDefaultSubobject和ConstructorHelpers

AFloatingActor::AFloatingActot() {     //将此Actor逐帧调用Tick(),如果没有此功能,可关闭以提高性能     PrimaryActorTick.bCanEverTick = true;     ///创建指向组件的指针     ///创建组件并将地址给我们指针     VisualMesh = CreateDefualtSubobject(TEXT("Mesh"));     ///将静态网格体组件附加到跟踪组件上     VisualMesh->SetupAttachment(RootComponent);      //ConstructorHelper 是 ObjectBase.h 定义的特殊命名空间,例如,为资源或类别寻找引用,并创建搜索组件的助手模板。     ///静态类包含生命模板结构      ///去资源文件夹找到资源并加载     static ConstructorHelpers::FObjectFinder CubeVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Cube.Shape_Cube"));          if(CubeVisualAsset.Succeeded())     {         VisualMesh->SetStaticMes(CubeVisualAsset.Object);
        VisualMesh->SetRelativeLocation(FVector(0.0f, 0.0f, 0.0f));
    {
}

其中Asset的路径可以在UE Editor中Ctrl C得到。 

1.11 以Actor为例,获取Locatoin和Rotation并设置

void AFloatingActor::Tick(float DeltaTime)
{
    //调用父类有参数的构造方法,也必须放在子类的构造方法里面
    //并且只能放在构造方法的首句
    //
    Super::Tick(DeltaTime);

    //获取当前物体位置
    FVector NewLocation = GetActorLocation();
    
    //获取当前物体的旋转
    FRotator NewRotation = GetActorRotation();
    
    //获取当前物体的运行时间
    float RunningTime = GetGameTimeSinceCreation();


    float DeltaHeight = (FMath::Sin(RunningTime + DeltaTime) - FMath::Sin(RunningTime));
    
    NewLocation.Z += DeltaHeight * 20.0f;
    float DeltaRotation = DeltaTime * 20.0f;
    NewRotation.Yaw += DeltaRotation;

    //设置位置和旋转
    SetActorLocationAndRotation(NewLocation,NewRotation);
}

1.12 玩家控制器PlayerController

在C++代码中体现为,重点关注一下API的使用。

// 每一帧调用
// Deltatime = 1/60
// 一秒会执行60次循环
void ACameraDirector::Tick( float DeltaTime )
{
    Super::Tick( DeltaTime );

    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)
        {
            //GetViewTarget获取玩家身上的相机Actor
            if ((OurPlayerController->GetViewTarget() != CameraOne) && (CameraOne != nullptr))
            {
                //立即切换到摄像机1。
                OurPlayerController->SetViewTarget(CameraOne);
            }
            else if ((OurPlayerController->GetViewTarget() != CameraTwo) && (CameraTwo != nullptr))
            {
                //平滑地混合到摄像机2。
                OurPlayerController->SetViewTargetWithBlend(CameraTwo, SmoothBlendTime);
            }
        }
    }
}

1.13 UE4中组件的概念和基本继承关系

Actor 生命周期 | 虚幻引擎文档 (unrealengine.com)

重点:以A开头的都是可以放置在场景中的,以U开头的都是组件只能依附于其他组件,不能单独放置到场景中。

Actor和组件的关系:组件可以附加到Actor身上,但是不能独立出现在场景中。

Actor好比是人,组件好比是衣服,人可以出去逛街,但衣服不行。

Actor本身不存储相关信息,交给RootComponent来处理

1.13.1 RootComponent的作用

RootComponent:每个Actor都有一个默认的RootComponent,它用来存储物体的基础信息。它可以改变。这是一个AActor的一个成员,用于保存AActor组件树中的顶级组件。

1.13.2 Actor的作用

在某种意义上,Actor可被视为包含特殊类型对象(称作组件)的容器。不同类型的组件可用于控制Actor移动方式或被渲染的方式等等等等。Actor的其他主要功能是在游戏进程中在网络上进行属性赋值和函数调用。

组件被创建时与其包含的Actor相关联。

组件的主要类型有:

1.13.3 UActorComponent的作用:

这是基础组件。

其可作为Actor的一部分被包含。但不存在于场景中的任意特定位置。

它们常用于概念上的功能,如AI或解释玩家输入。

Actor Componet(UA才通人Component类)有自己的行为,通常负责在许多类型Actor之间共享功能,例如,提供视觉网格体,粒子效果,摄像机视角和物理交互。Actor通常提供与其游戏总体角色有关的高级目标,而Actor Component通常执行用于支持这些更高级的目标的单独任务。

组件也可以与其他组件相连接,或者可以成为Actor的根组件,一个组件只能连接到一个父组件或Actor,但可以连接多个子Actor。

可以想象成一颗组件树。

子组件的位置、旋转和缩放相对于其父组件或Actor。

Actor和组件有很多用法,一种方法是将Actor-组件关系视为:Actor可能会回答问题”这是什么?”,而组件可能回答“这个东西是用什么东西做成的?”

Ticking - 在所属的Actor的Tick()过程中执行Tick函数。(在编写自己的Tick函数时,必须确保调用Super::Tick)

1.13.4 USceneComponent

SceneComponents时拥有变换的ActorComponents。变换时场景中的位置,由位置、旋转和缩放定义。SceneComponents能以层级的方式相互附加。Actor的Location、Rotation和Scale取自位于层级根部的SceneComponent.

1.13.5 UPrimitiveComponent

PrimitiveComponent是拥有一类图像表达(如网格体或粒子系统)的SceneComponent。诸多有趣的物理和碰撞设置均在此处。

Actor支持拥有一个SceneComponent的层级。每个Actor也拥有一个RootComponent属性,将指定作为Actor根的组件。Actor自身不含变换,因此不带有Location、Rotation和Scale。他们依赖于其组件的变换,具体来说是根组件的变换。

如果此组件是一个SceneComponent,其将提供Actor的变换信息,否则Actor将不带变换。其他附加的组件拥有相对于其附加到的组件的变换。

1.14 轴映射和动作映射,绑定函数

1.14.1 Action 操作映射

适用于“是/否”输入,例如鼠标或手柄上的按钮,被按下、松开、双击或短按长按时,其将进行报告。跳跃、射击或与物体互动等离散操作是这类映射的理想对象。

1.14.2 Action绑定函数(相关代码)

函数指针

PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AFPSCharacter::StartJump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &AFPSCharacter::StopJump);

1.14.3 Axis轴映射

是连续的,可将其视为“程度”输入,例如手柄上的摇杆,或者鼠标光标的位置。其会逐帧报告自身的值,即使为一种也进行报告。通常使用此类方法 处理如行走、四处查看和操纵车辆等有量级或方向的对象。

1.14.4 Axix绑定函数(相关代码)

Player-Controlled Cameras | 虚幻引擎文档 (unrealengine.com)

主要关注SetupPlayerInputComponent(class UInputComponent* InputComponent)override;

这里先创建了一个FPSCharacter类,只贴出部分关键代码:

void AFPSCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);
    //设置移动绑定
    PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
    PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);
    
}

void AFPSCharacter::MoveForward(float Value)
{
    FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
    AddMovementInput(Direction, Value);
}

void AFPSCharacter::MoveRight(float Value)
{
    FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
    AddMovementInput(Direction, Value);
}

1.14.4 Roll Pitch Yaw(X Y Z).

Pitch是围绕X轴旋转,也叫做俯仰角

Yaw是围绕Y轴旋转,也叫做偏航角

Roll是围绕Z轴旋转,也叫做翻滚角

 

但是注意了:只是在右手坐标系内的规则

Unreal里面使用的是左手坐标系,Y轴指向右方向,X轴指向正前方,Z轴指向正上方

在UE4中的规则 为:Roll,Pitch,Yaw对应X,Y,Z

设置鼠标控制视角左右(Turn)和上下(LookUp)移动:

PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::AddControllerYawInput);
PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::AddControllerPitchInput);

1.15  关于Pawn的移动方式

void AMyPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);

    InputComponent->BindAxis("MoveX", this, &AMyPawn::Move_XAxis);
    InputComponent->BindAxis("MoveY", this, &AMyPawn::Move_YAxis);
}

void AMyPawn::Move_XAxis(float AxisValue)
{
    //以100 单位/秒的速度向前或向后移动
    //-1 到 1
    CurrentVelocity.X = FMath::Clamp(AxisValue, -1.0f, 1.0f) * 100.0f;
}

void AMyPawn::Move_YAxis(float AxisValue)
{
    //以100 单位/秒的速度向左或向右移动
    //-1 到 1
    CurrentVelocity.Y = FMath::Clamp(AxisValue, -1.0f, 1.0f) * 100.0f;
}

2.关于碰撞的随笔

2.1 在官网实例中的子弹销毁物体

值得注意的:

①OnComponentBeginOverlap.AddDynamic(this, &AFloatingActor::CheckActor)

②Cast

AFloatingActor::AFloatingActor()
{
    PrimaryActorTick.bCanEverTick = true;
   
    StaticComp = CreateDefaultSubobject("StaticComp");
    StaticComp->OnComponentBeginOverlap.AddDynamic(this, &AFloatingActor::CheckActor);
}

void AFloatingActor::CheckActor(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
    AFPSGameProjectile *projectile = Cast(OtherActor);
    
    if(projectile == nullptr){
        return;
    }
    else{
        this->Destory();
    }
}

③在主编辑器界面中设置碰撞的相关选项,

1.必须设置Overlap,即重叠

Collision Presets:由BlockAll 改为Custom,并在其中设置projectile为Overlap

2.必须设置重叠事件

勾选Generate Overlap Events

同理子弹也需要设置Overlap:

2.2 碰撞预设

组件和碰撞 | 虚幻引擎文档 (unrealengine.com)

    // 根组件将成为对物理反应的球体
    USphereComponent* SphereComponent = CreateDefaultSubobject(TEXT("RootComponent"));
    RootComponent = SphereComponent;
    SphereComponent->InitSphereRadius(40.0f);
    SphereComponent->SetCollisionProfileName(TEXT("Pawn"));

2.3 粒子系统

现可将非活跃粒子系统组件附加到层级。可在代码中操纵此组件,然后设置输入进行开关。注意:粒子系统组件直接附加到静态网格体组件,而非附加到根。其同时略微偏离网格体底部中心,以便在运行时清晰显示。

    // 创建可激活或停止的粒子系统
    OurParticleSystem = CreateDefaultSubobject(TEXT("MovementParticles"));
    OurParticleSystem->SetupAttachment(SphereVisual);
    //在创建时激活或必须显示激活(是否手动激活)
    OurParticleSystem->bAutoActivate = false;
    OurParticleSystem->SetRelativeLocation(FVector(-20.0f, 0.0f, 20.0f));
    static ConstructorHelpers::FObjectFinder ParticleAsset(TEXT("/Game/StarterContent/Particles/P_Fire.P_Fire"));
    if (ParticleAsset.Succeeded())
    {
        OurParticleSystem->SetTemplate(ParticleAsset.Object);
    }

2.4 弹簧臂

Up讲的非常生动,类似于人放风筝:物体类似于放风筝的人,弹簧臂类似于风筝线,摄像机类似于风筝。

利用弹簧臂组件,可将摄像机以低于其追踪Pawn的速度加速和减速,从而获得更平滑的摄像机附加点。其同时拥有内置功能,可防止摄像机穿过固体对象,用于处理如玩家在第三人称游戏里退到角落时等情况。虽然弹簧臂组件并非必要的组件,但它能够轻松让摄像机在游戏中移动时变得更加平滑。

    // 使用弹簧臂给予摄像机平滑自然的运动感。
    USpringArmComponent* SpringArm = CreateDefaultSubobject(TEXT("CameraAttachmentArm"));
    SpringArm->SetupAttachment(RootComponent);
    SpringArm->SetRelativeRotation(FRotator(-45.f, 0.f, 0.f));
    SpringArm->TargetArmLength = 400.0f;
    //开启摄像机延迟,若为true,则相机滞后于目标位置以平滑其运动
    SpringArm->bEnableCameraLag = true;
    //设置摄像机延迟速度
    SpringArm->CameraLagSpeed = 3.0f;

    // 创建摄像机并附加到弹簧臂
    UCameraComponent* Camera = CreateDefaultSubobject(TEXT("ActualCamera"));
    Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);

    // 控制默认玩家
    AutoPossessPlayer = EAutoReceiveInput::Player0;

 

2.5 Lerp的用法

ZoomFactor = FMath::Clamp(ZoomFactor, 0.0f, 1.0f);
//基于ZoomFactor混合相机的FOV和SpringArm长度
//两个向量之间的线性插值,按照数字t在from和to之间插值
//T Lerp(const T& A, const T& B, const U& alpha)
//t是夹在0到1之间,当t=0,返回from,当t=1,返回to,当t=0.5返回from和to之间的平均数
OurCamera->FieldOfView = FMath::Lerp(90.0f, 60.0f, ZoomFactor);
OurCameraSpringArm->TargetArmLength = FMath::Lerp(400.0f, 300.0f, ZoomFactor);

2.6 Widget

使用UMG的用户界面 | 虚幻引擎文档 (unrealengine.com)

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章