UE_CPP学习

虚幻编辑器设置

1767186940261

1767186893108

案例1——控制物体的移动

创建一个蓝图

1767189226779

创建一个C++ Class

1767189252400

把脚本挂到蓝图中,并在脚本下面挂上一个静态的Mesh

1767189313118

进IDE编写代码

NewMovement.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Components/SceneComponent.h"
#include "NewMovement.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class MYPROJECT_API UNewMovement : public USceneComponent
{
	GENERATED_BODY()

public:
	// Sets default values for this component's properties
	UNewMovement();

protected:
	// Called when the game starts
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
	// 成员变量
	UPROPERTY(EditAnywhere, Category = "Movement")
	FVector MoveOffset;  // 移动偏移

	UPROPERTY(EditAnywhere, Category = "Movement")
	float Speed = 100.0f;  // 移动速度

	FVector StartLocation;  // 初始位置
	FVector MoveOffsetNormal;   // 归一化后的移动偏移
	float EndDistance;      // 总移动距离
	float CurrentDistance = 0.0f;  // 当前移动的距离
	int32 DirectionMove = 1; 

};

NewMovement.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "NewMovement.h"

// Sets default values for this component's properties
UNewMovement::UNewMovement()
{
// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
    // off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;

// ...
}


// Called when the game starts
void UNewMovement::BeginPlay()
{
Super::BeginPlay();
StartLocation = this->GetRelativeLocation(); 
EndDistance = MoveOffset.Length();
//归一化
MoveOffset.Normalize();
MoveOffsetNormal = MoveOffset;

}


// Called every frame
void UNewMovement::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
//设置物体的相对位置
SetRelativeLocation(StartLocation + CurrentDistance * MoveOffsetNormal);
//更新当前的移动距离
CurrentDistance += DeltaTime * Speed * DirectionMove;
//方向反转
if (CurrentDistance >= EndDistance || CurrentDistance <= 0.0f)
    {
DirectionMove *= -1;
    }
}

效果:

1767192134578

Unreal C++基础

01创建和设置Class

案例2——StackOBot

初始化

新建C++类,继承于Character

1768199550286

1768199582337

打开IDE

1768199800911

AstroBot.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "GameFramework/SpringArmComponent.h"
#include "AstroBot.generated.h"

UCLASS()
class ASTROBOT_WITH_CPP_API AAstroBot : public ACharacter
{
	GENERATED_BODY()

public:
	void Init();
	// Sets default values for this character's properties
	AAstroBot();

	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;


private:
	UPROPERTY(VisibleAnywhere)
	USpringArmComponent* SpringArm;
};

AstroBot.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "AstroBot.h"

void AAstroBot::Init()
{
	SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraSpringArm"));
	SpringArm->SetupAttachment(GetRootComponent());
}
// Sets default values
AAstroBot::AAstroBot()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	Init();
}

// Called when the game starts or when spawned
void AAstroBot::BeginPlay()
{
	Super::BeginPlay();

}

// Called every frame
void AAstroBot::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

// Called to bind functionality to input
void AAstroBot::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

}


新建蓝图继承于上面的BP_AstroBot,在这个蓝图中不需要编辑任何东西,只是用于可视化CPP中的操作

1768200260074

可以看到SpringArm已经正常初始化

括号内是TEXT命名

1768200386857

新建BP_GameMode

1768200354336

Pawn设置为BP_AstroBot

1768200330614

继续初始化相机

AstroBot.h

UPROPERTY(VisibleAnywhere)
UCameraComponent* Camera;

AstroBot.cpp

Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("MyCamera"));
Camera->SetupAttachment(SpringArm);

1768201103433

另一种附加组件到父级的方法:动态创建、自定义规则

Camera->AttachToComponent(SpringArm,FAttachmentTransformRules::KeepRelativeTransform);

剩余初始化

AstroBot.cpp

void AAstroBot::Init()
{
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("MySpringArm"));
SpringArm->SetupAttachment(GetRootComponent());
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("MyCamera"));
Camera->AttachToComponent(SpringArm,FAttachmentTransformRules::KeepRelativeTransform);

SpringArm->TargetArmLength = 300;
SpringArm->SocketOffset = FVector(100, 100, 50);
SpringArm->bUsePawnControlRotation = true;

bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;

GetCharacterMovement()->MaxWalkSpeed = 500;
GetCharacterMovement()->bOrientRotationToMovement = true;
}

增强输入

蓝图参考:

1768206460729

C++:

AstroBot.cpp

void AAstroBot::BeginPlay()
{
	Super::BeginPlay();
	APlayerController* AstroBotController = Cast<APlayerController>(GetController());
	if (AstroBotController)
	{
		UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(AstroBotController->GetLocalPlayer());
		if (Subsystem)
		{
			Subsystem->ClearAllMappings();
			Subsystem->AddMappingContext(IMC_AstroBot,0);
		}
	}

}

AstroBot.h

protected:

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = MyInput)
	TObjectPtr<UInputMappingContext> IMC_AstroBot;

.h中长期存在的指针,用安全指针TObjectPtr<>声明

.cpp中的函数临时变量直接用裸指针

回到蓝图BP_AstroBot

1768209364107

Move

蓝图:

1768213601735

C++:

AstroBot.h

protected:

UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = MyInput)
TObjectPtr<UInputAction> IA_Move;

void Move(const FInputActionValue& ActionValue);

AstroBot.cpp

// Called to bind functionality to input
void AAstroBot::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	TObjectPtr<UEnhancedInputComponent> EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent);
	if (EnhancedInputComponent)
	{
		EnhancedInputComponent->BindAction(IA_Move,ETriggerEvent::Triggered,this,&AAstroBot::Move);
	}
}
void AAstroBot::Move(const FInputActionValue& InputValue)
{
const FVector2D MoveAxis2D = InputValue.Get<FVector2D>();
FRotator ControlRotation = GetControlRotation();
//前
FVector ForwardVector = FRotationMatrix(FRotator(0,ControlRotation.Yaw,0)).GetUnitAxis(EAxis::X);
AddMovementInput(ForwardVector,MoveAxis2D.Y);
//右
FVector RightVector = FRotationMatrix(FRotator(0,ControlRotation.Yaw,ControlRotation.Roll)).GetUnitAxis(EAxis::Y);
AddMovementInput(RightVector,MoveAxis2D.X);
}

Level中的Forward是x轴,Right是y轴

因此蓝图中

GetForwardVector对应FRotationMatrix().GetUnitAxis(EAxis::X)

GetRightVector对应FRotationMatrix().GetUnitAxis(EAxis::Y)

另外要注意FRotator()的参数顺序,在IDE中会提示:

1768214009742

Look

蓝图:

1768271766688

C++:

AstroBot.h

UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = MyInput)
TObjectPtr<UInputAction> IA_Look;

void Look(const FInputActionValue& ActionValue);

AstroBot.cpp

EnhancedInputComponent->BindAction(IA_Look,ETriggerEvent::Triggered,this,&AAstroBot::Look);
void AAstroBot::Look(const FInputActionValue& InputValue)
{
	const FVector2D InputVector2D = InputValue.Get<FVector2D>();
	AddControllerYawInput(InputVector2D.X);
	AddControllerPitchInput(InputVector2D.Y);
}

Jump

蓝图:

1768272675708

C++

AstroBot.h

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = MyInput)
	TObjectPtr<UInputAction> IA_Jump;

AstroBot.cpp

		EnhancedInputComponent->BindAction(IA_Jump,ETriggerEvent::Started,this,&ACharacter::Jump);
		EnhancedInputComponent->BindAction(IA_Jump,ETriggerEvent::Completed,this,&ACharacter::StopJumping);

动画状态机

蓝图:

1768276800159

其他ABP就是Lyra那套分层管理

案例3——蓝图到C++

对象类型对应

1768879702209

1768879793836

UPROPERTY标识符

  • 作用 :修饰成员变量,控制它们如何在编辑器显示、如何参与序列化、GC管理、以及蓝图访问权。
  • 常用标识符
    • EditAnywhere:变量在编辑器中可编辑(关卡、蓝图均可)。
    • BlueprintReadWrite:蓝图中可读写访问。
    • BlueprintReadOnly:蓝图中只读。
    • VisibleAnywhere:仅可见,不可编辑。
    • Transient:不参与保存与序列化。
    • Replicated:变量用于网络复制。
    • Config:变量值可保存在.config文件中。
  • 元数据(Meta)标识符
    • DisplayName:编辑器显示名。
    • EditCondition:定义此属性是否可编辑的条件。
    • ClampMin/ClampMax:限定数值范围。
    • 复杂条件控制编辑状态也支持逻辑表达式(如 EditCondition = "bIsEnabled && Health > 0")。

示例:

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Character Stats")
float Health;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Weapon")
int32 AmmoCount;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings", meta = (ClampMin="0", ClampMax="100"))
int32 Volume;

UFUNCTION标识符

  • 作用 :修饰C++函数,使其能被蓝图调用或重写,支持特定的调用约定、权限和扩展功能。
  • 常用标识符
    • BlueprintCallable:蓝图中可调用的普通函数。
    • BlueprintPure:无副作用函数,蓝图调用时无执行引脚。——对应蓝图中就是Pure函数
    • BlueprintImplementableEvent:声明一个函数接口,蓝图可实现。
    • BlueprintNativeEvent:C++有默认实现,蓝图可覆盖。
    • Exec:可以在控制台执行的函数。
    • ServerClientNetMulticast:网络RPC相关。
    • Reliable:保证RPC可靠传输。
  • 元数据
    • DisplayName:蓝图节点显示名。
    • Keywords:用于搜索蓝图节点的关键词。
    • AdvancedDisplay:隐藏参数,放在展开面板。
UFUNCTION(BlueprintCallable, Category="Weapon")
void Fire();

UFUNCTION(BlueprintPure, Category="Utilities")
float GetHealthPercent() const;

UFUNCTION(BlueprintImplementableEvent, Category="Events")
void OnEliminated();

UFUNCTION(Server, Reliable)
void ServerEliminate();

蓝图可实现节点——BlueprintCallable,BlueprintPure

怎么根据蓝图中的节点找到在C++中应该调用的函数API

1768880699945

可以看到时SceneComponent中的函数,因此可以在源码中找到SceneComponent.h,直接搜索即可找到蓝图封装的函数对应C++中的API

1768880819383

点进API,查看C++中应该调用的函数名

1768880896520

1768880962698

这个就是我们需要的函数名

如果找到的函数名是

1768881144634

说明可以直接点出

蓝图自定义函数用C++实现

1769151891315

对于有返回值函数

1769151409780

1769151504701

在头文件中Include

1769151530229

因为返回值是一个object,因此返回类型是指针,并且不需要改变他的值,用const

1769151566797

对于GetComponent(),返回类型是UActorComponent*,因此需要一个能够自动转换返回类型的函数——FindComponentByClass <T>()

1769151754031

最终的函数

1769151854661

蓝图可实现事件 ——BlueprintCallable,BlueprintImplementableEvent

只是在c++中声明,但是实现必须放在蓝图中

适用:需要被override的事件

把事件的声明放到C++中

1769152971890

实现部分仍然在蓝图中

1769152914684

1769152889685

蓝图本地事件——BlueprintCallable,BlueprintNativeEvent

声明和默认实现都在c++中,相当于是虚函数,蓝图中可以调用并Override他的默认实现

1769153158530

声明:

1769153477628

引用传递参数HitActorHitComponent 用了 *& 引用指针,意味着函数可以直接修改外部变量,将命中结果 “带出去”,无需额外的返回值。

相当于out参数

1769154034852

引用传递参数:让输入的参数能够被修改——UPARAM(ref) 参数类型&

1769965959405

默认实现需要在函数名后 _Implementation

1769154410249

在蓝图中就可以Override这个函数了

1769154453182

可以把大部分逻辑放在C++中默认实现

应当优先在 C++ 中定义的

  1. 结构体
  2. 枚举
  3. 需要在蓝图和C++中访问的变量

在C++声明结构体

1769154936194

新建C++类,继承自Object

1769155011104

然后改写为结构体,不需要继承自任何类

结构体需要在第一行调用 GENERATED_BODY()宏,并且成员变量用 UPROPERTY()声明

1769154954693

1769155213022

在C++中声明枚举类

建议使用枚举类而不是枚举,这会避免类型转换错误

1769765680606

结构体/枚举——与其他类定义在同一个.h文件中

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Item.generated.h"

UENUM(BlueprintType)
enum class EItemType : uint8
{
	Health,
	Shield,
	PistolClip,
	RifleClip
};

USTRUCT(BlueprintType)
struct FItemData : public FTableRowBase
{
	GENERATED_BODY()
 
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Item Data")
	FName Name;
 
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Item Data")
	EItemType ItemType;
 
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Item Data")
	int32 Amount;
};

UCLASS()
class LYRAALS_API AItem : public AActor
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite,Category = Items)
	TObjectPtr<UDataTable> DT_Items;


protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Sets default values for this actor's properties
	AItem();
	// Called every frame
	virtual void Tick(float DeltaTime) override;

};

在C++中访问数据表等资产

在头文件中声明数据表的引用,之后就可以在.cpp中访问这个数据表了

// .h 文件
#include "CoreMinimal.h"
#include "Engine/DataTable.h"
#include "工程名.h"
#include "该数据表的结构体头文件.h"

UCLASS()
class 工程名_API AYourActor : public AActor
{
    GENERATED_BODY()

public:
    // 在蓝图中指定数据表资产
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DataTable")
    TObjectPtr<UDataTable> DT_WeaponGates;
};
posted @ 2025-12-31 23:37  EanoJiang  阅读(32)  评论(0)    收藏  举报