CoreMinimal头文件包含一套来自UE4的核心编程环境的普遍存在类型(包含FString,FName,TArray等)
CoreMinimal头文件(头文件位于UE4根目录\Engine\Source\Runtime\Core\Public\CoreMinimal.h下)首先被多数引擎头文件包含。
因为是基于Actor生成的源代码,所以默认包含Actor的头文件。其他同理
项目名称大写+下划线API 命名(如QUICKSTART_API)
类命名只包含字母数字字符,不包含空格。域将通知是否输入了无效命名。
任何一个创建的UE C++类文件都会包含一个”MyClass.generated.h”。虚幻将生成所有反射数据并放入该文件中。您必须将该文件作为声明类型的标头文件中的最后一个包含语句,将其包含进去,他必须在最下方,切记!
反射是指在运行状态下,任意一个实体类都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意方法和属性。
这种动态获取信息以及动态调用对象方法的功能成为语言的反射。
比如加一个UPROPERTY(EditAnywhere),可以实时的更改一变量的值。 不必在C++中写死。
和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) 来完成。
UE4 将这个标记替换为该类型生成的所有必要的样板代码。引擎在背后为我们做了很多工作,生成了很多代码。
调用父类有参数的构造方法,必须放在子类的构造方法(成员方法不可以)里面,并且只能放在构造方法的首句。其中括号中的参数是指与父类此有参构造方法中参数数据类型相对应子类中的参数。
本身是一个宏,括号内设置相关修饰描述符
将变量公开到主编辑器或蓝图(Defaults)
(1)VisibleAnywhere:修饰符设置该属性为在任何地方都可见,在蓝图和主编辑器中只显示但不可以编辑。
(2)VisibleDefaultsOnly:在主编辑器中不显示,但是在蓝图编辑器中显示(不可编辑)
(3)EditDefaultsOnly:在蓝图中可以编辑,但是在主编辑器中不显示所以不可编辑
(4)EditAnywhere:在主编辑器和蓝图编辑器中都显示并且都可以编辑
(5)EditInstanceOnly:在蓝图编辑器中不可修改但是可见,只有当蓝图实例化到场景中点击该组件才会出现相应的设置,实例化之后可以修改但只能在原型上进行。
用于显示此物体.
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->SetStaticMesh(CubeVisualAsset.Object);
VisualMesh->SetRelativeLocation(FVector(0.0f, 0.0f, 0.0f));
{
}
其中Asset的路径可以在UE Editor中Ctrl C得到。
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);
}
在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);
}
}
}
}
Actor 生命周期 | 虚幻引擎文档 (unrealengine.com)
重点:以A开头的都是可以放置在场景中的,以U开头的都是组件只能依附于其他组件,不能单独放置到场景中。
Actor和组件的关系:组件可以附加到Actor身上,但是不能独立出现在场景中。
Actor好比是人,组件好比是衣服,人可以出去逛街,但衣服不行。
Actor本身不存储相关信息,交给RootComponent来处理
RootComponent:每个Actor都有一个默认的RootComponent,它用来存储物体的基础信息。它可以改变。这是一个AActor的一个成员,用于保存AActor组件树中的顶级组件。
在某种意义上,Actor可被视为包含特殊类型对象(称作组件)的容器。不同类型的组件可用于控制Actor移动方式或被渲染的方式等等等等。Actor的其他主要功能是在游戏进程中在网络上进行属性赋值和函数调用。
组件被创建时与其包含的Actor相关联。
组件的主要类型有:
这是基础组件。
其可作为Actor的一部分被包含。但不存在于场景中的任意特定位置。
它们常用于概念上的功能,如AI或解释玩家输入。
Actor Componet(UA才通人Component类)有自己的行为,通常负责在许多类型Actor之间共享功能,例如,提供视觉网格体,粒子效果,摄像机视角和物理交互。Actor通常提供与其游戏总体角色有关的高级目标,而Actor Component通常执行用于支持这些更高级的目标的单独任务。
组件也可以与其他组件相连接,或者可以成为Actor的根组件,一个组件只能连接到一个父组件或Actor,但可以连接多个子Actor。
可以想象成一颗组件树。
子组件的位置、旋转和缩放相对于其父组件或Actor。
Actor和组件有很多用法,一种方法是将Actor-组件关系视为:Actor可能会回答问题”这是什么?”,而组件可能回答“这个东西是用什么东西做成的?”
Ticking - 在所属的Actor的Tick()过程中执行Tick函数。(在编写自己的Tick函数时,必须确保调用Super::Tick)
SceneComponents时拥有变换的ActorComponents。变换时场景中的位置,由位置、旋转和缩放定义。SceneComponents能以层级的方式相互附加。Actor的Location、Rotation和Scale取自位于层级根部的SceneComponent.
PrimitiveComponent是拥有一类图像表达(如网格体或粒子系统)的SceneComponent。诸多有趣的物理和碰撞设置均在此处。
Actor支持拥有一个SceneComponent的层级。每个Actor也拥有一个RootComponent属性,将指定作为Actor根的组件。Actor自身不含变换,因此不带有Location、Rotation和Scale。他们依赖于其组件的变换,具体来说是根组件的变换。
如果此组件是一个SceneComponent,其将提供Actor的变换信息,否则Actor将不带变换。其他附加的组件拥有相对于其附加到的组件的变换。
适用于“是/否”输入,例如鼠标或手柄上的按钮,被按下、松开、双击或短按长按时,其将进行报告。跳跃、射击或与物体互动等离散操作是这类映射的理想对象。
函数指针
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AFPSCharacter::StartJump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &AFPSCharacter::StopJump);
是连续的,可将其视为“程度”输入,例如手柄上的摇杆,或者鼠标光标的位置。其会逐帧报告自身的值,即使为一种也进行报告。通常使用此类方法 处理如行走、四处查看和操纵车辆等有量级或方向的对象。
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);
}
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);
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;
}
值得注意的:
①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:
组件和碰撞 | 虚幻引擎文档 (unrealengine.com)
// 根组件将成为对物理反应的球体
USphereComponent* SphereComponent = CreateDefaultSubobject(TEXT("RootComponent"));
RootComponent = SphereComponent;
SphereComponent->InitSphereRadius(40.0f);
SphereComponent->SetCollisionProfileName(TEXT("Pawn"));
现可将非活跃粒子系统组件附加到层级。可在代码中操纵此组件,然后设置输入进行开关。注意:粒子系统组件直接附加到静态网格体组件,而非附加到根。其同时略微偏离网格体底部中心,以便在运行时清晰显示。
// 创建可激活或停止的粒子系统
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);
}
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;
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);
使用UMG的用户界面 | 虚幻引擎文档 (unrealengine.com)