IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> UE4 C++笔记(持续更新) -> 正文阅读

[游戏开发]UE4 C++笔记(持续更新)

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

1.1 CoreMinimal是什么?

CoreMinimal头文件包含一套来自UE4的核心编程环境的普遍存在类型(包含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<UStaticMeshComponent>(TEXT("Mesh"));
    //把静态网格体组件附加到跟组件上
    VisualMesh->SetupAttachment(RootComponent);

    //ConstructorHelper 是 ObjectBase.h 定义的特殊命名空间,例如为资源或类寻找引用、以及创建并查找组件的助手模板。
    //静态类中包含一个生命模板结构体

    //去资源文件夹下查找一个资源并加载
    static ConstructorHelpers::FObjectFinder<UStaticMesh> 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));
    {
}

其中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<UStaticMeshComponent>("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<AFPSGameProjectile>(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<USphereComponent>(TEXT("RootComponent"));
    RootComponent = SphereComponent;
    SphereComponent->InitSphereRadius(40.0f);
    SphereComponent->SetCollisionProfileName(TEXT("Pawn"));

2.3 粒子系统

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

    // 创建可激活或停止的粒子系统
    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);
    }

2.4 弹簧臂

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

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

    // 使用弹簧臂给予摄像机平滑自然的运动感。
    USpringArmComponent* SpringArm = CreateDefaultSubobject<USpringArmComponent>(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<UCameraComponent>(TEXT("ActualCamera"));
    Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);

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

?

2.5 Lerp的用法

ZoomFactor = FMath::Clamp<float>(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<float>(90.0f, 60.0f, ZoomFactor);
OurCameraSpringArm->TargetArmLength = FMath::Lerp<float>(400.0f, 300.0f, ZoomFactor);

2.6 Widget

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

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-05-21 19:16:33  更:2022-05-21 19:16:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/20 15:13:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码