UE_LOG
垃圾引擎,毁我青春,垃圾C++,毁我未来满腔的怒火使我开悟,写给自己看的博客,最后会附我的项目,基本纯C++实现,基本无蓝图内容,部分内容经AI整理,已经过个人校对,放心食用C++→UE核心类概览与生命周期→灵活使用反射系统→UC++基本语法→UC++基础数据结构→常用类方法→虚幻网络部分(浅尝辄止)→失业→恭喜可以随便搓游戏了。
UC++
垃圾引擎,毁我青春,垃圾C++,毁我未来
文章目录
前言
垃圾引擎,毁我青春,垃圾C++,毁我未来
满腔的怒火使我开悟,写给自己看的博客,最后会附我的项目,基本纯C++实现,基本无蓝图内容,部分内容经AI整理,已经过个人校对,放心食用
学习路径:
C++→UE核心类概览与生命周期→灵活使用反射系统→UC++基本语法→UC++基础数据结构→常用类方法→虚幻网络部分(浅尝辄止)→失业→恭喜可以随便搓游戏了
tip:使用前关闭虚幻自带热重载(实时编译)
引擎右下角的小魔方按钮的√给它关了,能免去莫名其妙的BUG调试时间
二、UE核心类概览-必看!!!
核心游戏类
🎯 Pawn/Character - 玩家/AI控制的实体(可移动的角色)
📊 PlayerState - 玩家数据(分数、姓名、等级)- 跨关卡保持
🎮 HUD/UMG - 用户界面显示
🤖 AIController - AI控制器
🤖PlayerController -玩家控制器
🌍 Level/Map - 关卡本身
📦 Actor - 所有对象的老祖宗(Pawn/HUD等都继承自它)
组件系统
🔧 SceneComponent - 有位置的组件(Transform)
🎨 PrimitiveComponent - 可视/可碰撞组件(Mesh、碰撞盒)
🎯 CameraComponent - 摄像机组件
🔫 SkeletalMeshComponent - 骨骼网格组件
⚡ ParticleSystemComponent - 粒子特效组件
数据和资源类
💾 SaveGame - 存档数据基类
📋 DataTable - 数据表格(Excel式数据)
🎨 Material - 材质
🔊 SoundBase - 音效基类
📁 DataAsset - 纯数据资产
网络和同步类
🌐 OnlineSession - 在线会话管理
👥 PlayerState - 网络同步的玩家数据
📡 Replication - 属性同步系统
🔐 Authority - 服务器权威控制
常用工具类
⏰ TimerManager - 定时器管理
📝 GameplayStatics - 静态工具函数大全
🔍 Cast - 类型转换
📢 Delegate/Broadcast - 事件系统
🗃️ TArray/TMap - 容器类
基本生命周期:
创建顺序:
游戏启动 → GameInstance → 关卡 → World → GameMode → 玩家加入 → PlayerController
销毁顺序:
玩家离开 → PlayerController → 卸载关卡 → GameMode → World → 游戏关闭 → GameInstance
重点:
GameInstance跟着游戏同生死;GameMode跟关卡,切换就重建;PlayerController 最短暂,随玩家进出创建与析构,后面还会因为虚幻网络部分的C/S架构 + 属性复制 + RPC,会继续聊到这个
游戏实例GameInstance的作用
🎮 游戏流程控制 - 管理整个游戏会话的状态机
💾 跨关卡数据持久化 - 唯一能在关卡间保持数据的对象 非常重要!!
🔧 全局系统管理 - 所有子系统的容器和协调者 子系统后面会涉及到
🌐 网络和社交 - 在线功能、多人游戏、好友系统
⚙️ 配置管理 - 图形、音频、输入等全局设置
📊 数据分析 - 性能监控、用户行为追踪
🏪 资源管控 - 全局资源加载、内存管理
📱 平台适配 - 不同平台的特定功能处理
🚨 错误处理 - 崩溃报告、用户反馈
🔄 关卡协调 - 虽然涉及关卡加载,但主要是协调作用
游戏实例怎么用-Gameinstance -2025/11/28更新
游戏实例示例代码
游戏实例中可以存一些经常要用的变量,比如分数,当前全局玩家数量之类的,然后由Gamemode,玩家控制器拿出来用
// SimpleGameInstance.h
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "SimpleGameInstance.generated.h"
UCLASS()
class YOURGAME_API USimpleGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
// 储存一个简单的分数
UPROPERTY(BlueprintReadWrite, Category = "Simple")
int32 Score = 0;
// 储存玩家名字
UPROPERTY(BlueprintReadWrite, Category = "Simple")
FString PlayerName = "Player";
// 增加分数的函数
UFUNCTION(BlueprintCallable, Category = "Simple")
void AddScore(int32 Points)
{
Score += Points;
UE_LOG(LogTemp, Warning, TEXT("分数增加 %d,现在是 %d"), Points, Score);
}
// 重置分数
UFUNCTION(BlueprintCallable, Category = "Simple")
void ResetScore()
{
Score = 0;
UE_LOG(LogTemp, Warning, TEXT("分数重置为 0"));
}
};
在角色类中获取游戏实例中的数据来使用,游戏实例中的数据只要游戏没结束一直都会保留!
PlayerController、PlayerState、Pawn等玩家相关类中,可以直接获取和使用GameInstance,这里仅仅做个简单示例
// SimpleCharacter.cpp
#include "SimpleGameInstance.h"
#include "Kismet/GameplayStatics.h"
void ASimpleCharacter::AddSomeScore()
{
// 获取GameInstance
UGameInstance* GameInstance = GetGameInstance();
if (GameInstance)
{
// 转换成我们的GameInstance
USimpleGameInstance* MyGameInstance = Cast<USimpleGameInstance>(GameInstance);
if (MyGameInstance)
{
MyGameInstance->AddScore(10);
MyGameInstance->PlayerName = "Hero";
UE_LOG(LogTemp, Warning, TEXT("玩家名字: %s"), *MyGameInstance->PlayerName);
}
}
}
void ASimpleCharacter::OnDeath()
{
// 死亡时重置分数
USimpleGameInstance* MyGameInstance = Cast<USimpleGameInstance>(GetGameInstance());
if (MyGameInstance)
{
MyGameInstance->ResetScore();
}
}
注意:虚幻引擎有着自己的规则,不要试图抵抗它,遵循官方命名规则和推荐写法能最大化效率开发,除非您已经掌握修改引擎底层的能力
虚幻反射系统 2025/11/30更新
反射系统是指在运行时或编译时,程序能够自省自身的结构——比如类有哪些属性、方法、父类是谁等信息(自己查看自己的信息),C++ 本身并不原生支持反射,但虚幻通过一套自定义机制实现了这个功能。
虚幻的反射系统基于宏标记代码 + 代码生成工具(Unreal Header Tool,简称 UHT)协同工作.
作为Gamepaly游戏程序,绝大多数人不需要掌握它的底层实现.专注与怎么用就好了.
反射系统的组成
宏标记
使用 UCLASS(), UPROPERTY(), UFUNCTION()等宏标注类、属性、函数等,使 UHT 能解析它们。
Unreal Header Tool(UHT)
构建过程中运行的工具,扫描头文件,根据宏生成对应的 .generated.h文件和反射代码。
Generated Code(反射代码)
所有标记为反射类型的对象,都会生成一个对应的 YourClass.generated.h文件,其中包含注册信息供运行时使用。
运行时反射支持
虚幻通过 UObject, FProperty, UFunction等类提供运行时的访问能力。
下面直接看示例,其类似于初学CPP时的helloworld
// MyActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h" // 必须包含这个
UCLASS()
class MYGAME_API AMyActor : public AActor
{
GENERATED_BODY()
public:
// 构造函数
AMyActor();
protected:
// 可被序列化和在编辑器中显示的属性
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyCategory")
float Health;
// 可在蓝图中调用的函数
UFUNCTION(BlueprintCallable, Category = "MyFunctions")
void TakeDamage(float DamageAmount);
};
我现在先粗浅的概况这些宏的使用方法
1:所有虚幻生成的类,上方必须被添加UCLASS()宏标记,这样编辑器才能将类正确反射到编辑器并开启资源回收
2.所有变量,上方必须添加 UPROPERTY()宏标记,这样编辑器才能将变量正确反射到编辑器
3.所有函数,上方必须添加UFUNCTION()宏标记,这样编辑器才能将变量正确反射到编辑器
4: 所有类内部必须添加GENERATED_BODY()宏标记,这样编辑器才能将类正确反射到编辑器
5:所有 UCLASS()、UPROPERTY()、UFUNCTION()都必须写在头文件中(别问为什么,UHT无法解析CPP文件,按EPIC的来)
6:每个反射类都需要一个对应的 .generated.h,名称就是你创键的类名+.generated.h,且必须被 #include在头文件的末尾
UPROPERTY()宏
UPROPERTY用于标记类的成员变量,使其支持以下功能:
编辑器显示和修改(EditAnywhere / VisibleAnywhere)
蓝图读写(BlueprintReadWrite / BlueprintReadOnly)
网络复制(Replicated)
序列化(SaveGame)
GC 引用追踪(防止被错误回收)
括号里的常见参数代表的含义:
EditAnywhere / VisibleAnywhere 在编辑器中是否可编辑/可见
BlueprintReadWrite / ReadOnly 蓝图是否可读写
Category 在编辑器中的分类标签(会在编辑器中给一块分类便于你管理变量或者资源)
Replicated 启用网络同步
SaveGame 可序列化到存档
Transient 不保存、不复制
//此变量编辑器任意地方可编辑,蓝图可读写,分类是Stats,网络同步开启
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Stats", Replicated)
int32 Score;
UFUNCTION()宏
UFUNCTION用于标记方法,使其能被蓝图调用、网络 RPC、委托绑定等。
至于其它宏参数,大家可去官方文档自行查阅用法
UFUNCTION(BlueprintCallable, Category="Combat")
void FireWeapon();
UFUNCTION(Server, Reliable)
void ServerFire(); // 网络RPC:服务器执行
UFUNCTION(Client, Unreliable)
void ClientShowHitEffect(); // 网络RPC:客户端执行
虚幻枚举、结构体也支持反射
枚举:
UENUM(BlueprintType)
enum class EWeaponState : uint8
{
Idle,
Firing,
Reloading
};
结构体:
USTRUCT(BlueprintType)
struct FMyStruct
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString Name;
UPROPERTY(EditAnywhere)
int32 Level;
};
运行时代码中使用反射
你可以通过反射接口在运行时动态访问类、属性或函数:
示例:查找并设置属性值
UMyActor* Actor = ...;
UClass* Class = Actor->GetClass();
// 查找属性
FProperty* Property = Class->FindPropertyByName(TEXT("Health"));
if (FFloatProperty* FloatProp = CastField<FFloatProperty>(Property))
{
float CurrentHealth = FloatProp->GetPropertyValue_InContainer(Actor);
FloatProp->SetPropertyValue_InContainer(Actor, CurrentHealth - 10.0f);
//代码意思是,从类中取出变量并重新设置变量的值
}
反射示例:调用函数
UFunction* Func = Actor->FindFunction(FName("TakeDamage"));//通过名称查找函数
if (Func)
{
struct { float DamageAmount; } Params;
Params.DamageAmount = 25.0f;
Actor->ProcessEvent(Func, &Params);//在 Actor对象上调用名为 Func的函数,并传入参数 Params。取决于你调用函数的参数,如果参数传错了会崩溃
}
警告!仅展示引擎内部反射机制,此写法类型不安全,性能差,可读性差,实际工程不要这么写,而是直接调用对象即可
好的,同时你现在已经学会虚幻枚举和结构体的用法了,那么现在还有一些其它基本数据结构,用法和CPP基本一致
虚幻的基本数据结构对比标准 C++,帮助你快速理解它们的异同和使用场景。
一、字符串类型
// C++标准类型
std::string Str = "Hello";
std::string Full = Str + " World";
// UE UE 使用 TEXT("...")宏包裹字符串,确保跨平台(Unicode)兼容。
//FText是唯一支持本地化的字符串类型,UI 必须用 FText。
FString Str = TEXT("Hello");
FString Full = Str + TEXT(" World");
FString Formatted = FString::Printf(TEXT("分数: %d"), 100);
----------------------------------------------------------------------------------------
//基本用法
#include "Misc/Paths.h"
#include "Internationalization/Text.h"
void StringExamples() {
// === FString: 可变字符串,最常用 ===
FString Str1 = TEXT("Hello Unreal"); // 字面量初始化
FString Str2 = FString::Printf(TEXT("Score: %d"), 100); // 格式化
FString Str3 = Str1 + TEXT(" World"); // 拼接
int32 Len = Str1.Len(); // 长度
bool IsEmpty = Str1.IsEmpty(); // 判空
FString ToUpper = Str1.ToUpper(); // 转大写
// === FName: 不可变标识符,用于资源引用 ===
FName Name1 = TEXT("PlayerController"); // 创建名称
FName Name2 = FName(TEXT("PlayerController")); // 另一种创建方式
// Name1 = TEXT("NewName"); // 错误!FName不可修改
bool SameName = (Name1 == TEXT("PlayerController")); // 比较
// === FText: 本地化文本,支持多语言 ===
FText Text1 = FText::FromString(TEXT("Hello")); // 从字符串创建
FText Text2 = NSLOCTEXT("Namespace", "Key", "Localized Text"); // 本地化
FText Text3 = FText::AsCultureInvariant(TEXT("Invariant")); // 文化无关
// === 字符串转换 ===
FString StringFromName = Name1.ToString(); // FName → FString
FString StringFromText = Text1.ToString(); // FText → FString
FName NameFromString = FName(*Str1); // FString → FName
// FText不能直接从FString转换,需要指定上下文
}
二、标识符
//FName是给编辑器查找用的。FString→ FName有转换开销,尽量缓存复用
FName playername = TEXT("name");
三、容器类
虚幻引擎中的容器使用和STL基本一致,右边的是虚幻容器类,bro,不要说你不知道STL里都是些啥
Vector 与TArray
#include <vector>
#include <iostream>
#include <algorithm>
void VectorExample() {
// === 创建和添加 ===
std::vector<int> Numbers;
Numbers.reserve(10); // 预分配内存
Numbers.push_back(1); // 尾部添加
Numbers.push_back(2);
Numbers.insert(Numbers.begin() + 1, 99); // 中间插入
// === 访问和查询 ===
int First = Numbers[0]; // 下标访问
int Second = Numbers.at(1); // 安全访问(会抛异常)
int Size = Numbers.size(); // 元素个数
bool Empty = Numbers.empty(); // 是否为空
// === 修改 ===
Numbers[0] = 100; // 直接修改
std::sort(Numbers.begin(), Numbers.end()); // 排序
// === 删除 ===
Numbers.pop_back(); // 删除尾部
Numbers.erase(Numbers.begin()); // 删除指定位置
Numbers.clear(); // 清空所有
// === 遍历 ===
for(int Num : Numbers) { // 范围for循环
std::cout << Num << " ";
}
}
-------------------------------------------
#include "Containers/Array.h"
void TArrayExample() {
// === 创建和添加 ===
TArray<int32> Numbers;
Numbers.Reserve(10); // 预分配内存
Numbers.Add(1); // 尾部添加
Numbers.Add(2);
Numbers.Insert(99, 1); // 在索引1处插入
// === 访问和查询 ===
int32 First = Numbers[0]; // 下标访问
int32 Second = Numbers[1]; // 直接访问(越界崩溃)
int32 Size = Numbers.Num(); // 元素个数
bool Empty = Numbers.IsEmpty(); // 是否为空
// === 修改 ===
Numbers[0] = 100; // 直接修改
Numbers.Sort(); // 排序(默认升序)
// === 删除 ===
Numbers.Pop(); // 删除尾部
Numbers.RemoveAt(0); // 删除指定位置
Numbers.Empty(); // 清空所有
// === 遍历 ===
for(int32 Num : Numbers) { // 范围for循环
UE_LOG(LogTemp, Log, TEXT("%d"), Num);
}
}
Map 与TMap
#include <map>
#include <string>
void MapExample() {
// === 创建和添加 ===
std::map<std::string, int> PlayerScores;
PlayerScores["Alice"] = 100; // 直接赋值添加
PlayerScores.insert({"Bob", 85}); // insert方法添加
PlayerScores.emplace("Charlie", 92); // 原地构造
// === 访问和查询 ===
int AliceScore = PlayerScores["Alice"]; // 下标访问(不存在会创建)
int BobScore = PlayerScores.at("Bob"); // 安全访问(不存在抛异常)
bool HasAlice = PlayerScores.count("Alice") > 0; // 是否存在
// === 修改 ===
PlayerScores["Alice"] = 150; // 修改值
// === 删除 ===
PlayerScores.erase("Bob"); // 按键删除
PlayerScores.clear(); // 清空所有
// === 遍历 ===
for(const auto& Pair : PlayerScores) {
std::cout << Pair.first << ": " << Pair.second << std::endl;
}
}
--------------------------------------------------------------------------
#include "Containers/Map.h"
void TMapExample() {
// === 创建和添加 ===
TMap<FString, int32> PlayerScores;
PlayerScores.Add("Alice", 100); // Add方法添加
PlayerScores.Add("Bob", 85);
PlayerScores.Emplace("Charlie", 92); // Emplace原地构造
// === 访问和查询 ===
int32 AliceScore = PlayerScores["Alice"]; // 下标访问(不存在会创建!)
int32 BobScore;
bool FoundBob = PlayerScores.TryGetValue("Bob", BobScore); // 安全获取
bool HasAlice = PlayerScores.Contains("Alice"); // 是否存在
int32 Index = PlayerScores.Find("Alice"); // 查找索引
// === 修改 ===
PlayerScores["Alice"] = 150; // 修改值
// === 删除 ===
PlayerScores.Remove("Bob"); // 按键删除
PlayerScores.Empty(); // 清空所有
// === 遍历 ===
for(const auto& Pair : PlayerScores) {
UE_LOG(LogTemp, Log, TEXT("%s: %d"), *Pair.Key, Pair.Value);
}
}
Set 与TSet
#include <set>
#include <string>
void SetExample() {
// === 创建和添加 ===
std::set<std::string> UniqueNames;
UniqueNames.insert("Alice"); // 插入元素
UniqueNames.insert("Bob");
UniqueNames.insert("Alice"); // 重复插入无效
// === 访问和查询 ===
bool HasAlice = UniqueNames.count("Alice") > 0; // 是否存在
auto It = UniqueNames.find("Bob"); // 查找迭代器
// === 删除 ===
UniqueNames.erase("Alice"); // 按键删除
UniqueNames.clear(); // 清空所有
// === 遍历 ===
for(const std::string& Name : UniqueNames) {
std::cout << Name << std::endl;
}
}
------------------------------------------------------------
#include "Containers/Set.h"
void TSetExample() {
// === 创建和添加 ===
TSet<FString> UniqueNames;
UniqueNames.Add("Alice"); // 添加元素
UniqueNames.Add("Bob");
bool Added = UniqueNames.Add("Alice"); // 重复添加返回false
// === 访问和查询 ===
bool HasAlice = UniqueNames.Contains("Alice"); // 是否存在
bool Found = UniqueNames.Contains("Alice");
// === 删除 ===
UniqueNames.Remove("Alice"); // 按键删除
UniqueNames.Empty(); // 清空所有
// === 遍历 ===
for(const FString& Name : UniqueNames) {
UE_LOG(LogTemp, Log, TEXT("%s"), *Name);
}
}
Queue 与 TQueue
#include <queue>
#include <string>
void QueueExample() {
// === 创建和入队 ===
std::queue<std::string> MessageQueue;
MessageQueue.push("First"); // 入队
MessageQueue.push("Second");
// === 访问和查询 ===
std::string FrontMsg = MessageQueue.front(); // 查看队首
std::string BackMsg = MessageQueue.back(); // 查看队尾
bool Empty = MessageQueue.empty(); // 是否为空
size_t Size = MessageQueue.size(); // 元素个数
// === 出队 ===
MessageQueue.pop(); // 出队(删除队首)
// === 注意:没有直接遍历方法,需要逐个pop ===
while(!MessageQueue.empty()) {
std::string Msg = MessageQueue.front();
MessageQueue.pop();
std::cout << Msg << std::endl;
}
}
-----------------------------------------------------------
#include "Containers/Queue.h"
void TQueueExample() {
// === 创建和入队 ===
TQueue<FString> MessageQueue;
MessageQueue.Enqueue("First"); // 入队
MessageQueue.Enqueue("Second");
// === 访问和查询 ===
FString FrontMsg;
MessageQueue.Peek(FrontMsg); // 查看队首(不删除)
bool Empty = MessageQueue.IsEmpty(); // 是否为空
// === 出队 ===
FString OutMsg;
MessageQueue.Dequeue(OutMsg); // 出队(获取并删除队首)
// === 批量处理 ===
while(MessageQueue.Dequeue(OutMsg)) {
UE_LOG(LogTemp, Log, TEXT("%s"), *OutMsg);
}
}
基础数值类型
// C++ 原生类型
void NativeTypes() {
int a = 10; // 32位有符号整数
float b = 3.14f; // 32位浮点数
double c = 3.14159; // 64位浮点数
bool d = true; // 布尔值
char e = 'A'; // 字符
}
// UE 封装类型
void UETypes() {
int32 a = 10; // 明确32位有符号整数
int64 b = 10000000000; // 64位有符号整数
float c = 3.14f; // 32位浮点数(与C++相同)
double d = 3.14159; // 64位浮点数(与C++相同)
bool e = true; // 布尔值(与C++相同)
uint32 f = 100; // 无符号32位整数
uint8 g = 255; // 无符号8位整数(0-255)
}
向量和数学类型(最常用的,一定得弄清楚)
#include "Math/Vector.h"
#include "Math/Rotator.h"
#include "Math/Transform.h"
void MathTypes() {
// === FVector: 三维向量(x,y,z) ===
FVector Pos1 = FVector(1.0f, 2.0f, 3.0f); // 构造函数
FVector Pos2 = FVector::ZeroVector; // 零向量
FVector Pos3 = FVector::ForwardVector; // 前向量(1,0,0)
FVector Pos4 = FVector(10.0f, 0.0f, 0.0f); // X轴方向
float Length = Pos1.Size(); // 向量长度
FVector Normalized = Pos1.GetSafeNormal(); // 单位向量
FVector Added = Pos1 + Pos2; // 向量加法
FVector Scaled = Pos1 * 2.0f; // 数乘
// === FRotator: 欧拉角旋转(pitch,yaw,roll) ===
FRotator Rot1 = FRotator(45.0f, 90.0f, 0.0f); // 俯仰、偏航、翻滚 这玩意不懂的可以百度或者问AI,是游戏开发基本知识
FRotator Rot2 = FRotator::ZeroRotator; // 零旋转
FRotator FromVector = FRotationMatrix::MakeFromZ(FVector::UpVector).Rotator();
// === FQuat: 四元数,避免万向节死锁 === //万向节死锁
FQuat Quat1 = FQuat(FRotator(45.0f, 90.0f, 0.0f)); // 从旋转创建
FQuat Quat2 = FQuat::Identity; // 单位四元数
// === FTransform: 变换矩阵(位置+旋转+缩放) ===
FTransform Transform1; // 默认构造
FTransform Transform2 = FTransform(Rot1, Pos1); // 位置和旋转
FTransform Transform3 = FTransform::Identity; // 单位变换
FVector NewPos = Transform1.GetLocation(); // 获取位置
FRotator NewRot = Transform1.Rotator(); // 获取旋转
}
颜色相关类型与常用方法
#include "Math/Color.h"
void ColorTypes() {
// === FColor: 8位RGBA颜色(0-255) ===
FColor Color1 = FColor(255, 128, 0, 255); // RGBA构造
FColor Color2 = FColor::Red; // 预定义颜色
FColor Color3 = FColor::White;
FColor Color4 = FColor(255, 255, 255); // RGB自动设Alpha=255
// === FLinearColor: 浮点RGBA颜色(0.0-1.0) ===
FLinearColor LinearColor1 = FLinearColor(1.0f, 0.5f, 0.0f, 1.0f);
FLinearColor LinearColor2 = FLinearColor::Blue;
FLinearColor LinearColor3 = FLinearColor(Color1); // FColor → FLinearColor
// === 颜色转换 ===
FColor ConvertedBack = LinearColor1.ToFColor(true); // 带伽马校正
FLinearColor GammaCorrected = Color1.ReinterpretAsLinear(); // 重新解释
}
时间和日期类型与常用方法
#include "HAL/PlatformTime.h"
#include "Misc/DateTime.h"
void TimeTypes() {
// === FDateTime: 日期时间 ===
FDateTime Now = FDateTime::Now(); // 当前时间
FDateTime Today = FDateTime::Today(); // 今天零点
FDateTime Specific = FDateTime(2024, 12, 25, 10, 30, 0); // 指定时间
int32 Year = Now.GetYear(); // 年
int32 Month = Now.GetMonth(); // 月
int32 Day = Now.GetDay(); // 日
int32 Hour = Now.GetHour(); // 时
FString IsoDate = Now.ToIso8601(); // ISO格式字符串
// === FTimespan: 时间间隔 ===
FTimespan Span1 = FTimespan(1, 30, 0); // 1小时30分钟
FTimespan Span2 = Now - Today; // 时间差
double TotalHours = Span2.GetTotalHours(); // 总小时数
// === 计时相关 ===
double StartTime = FPlatformTime::Seconds(); // 高精度计时开始
// ... 执行一些操作 ...
double EndTime = FPlatformTime::Seconds(); // 计时结束
double Elapsed = EndTime - StartTime; // 耗时(秒)
}
路径和文件类型与常用方法
#include "Misc/Paths.h"
#include "GenericPlatform/GenericPlatformFile.h"
void PathTypes() {
// === 文件路径操作 ===
FString TestPath = TEXT("C:\\Project\\Content\\..\\Content\\Texture.uasset");
FString CleanPath = FPaths::CleanPath(TestPath); // 清理路径
FString AbsolutePath = FPaths::ConvertRelativePathToFull(TestPath); // 转绝对路径
FString BaseName = FPaths::GetBaseFilename(TestPath); // 获取文件名(无扩展名)
FString Extension = FPaths::GetExtension(TestPath); // 获取扩展名
FString PathPart = FPaths::GetPath(TestPath); // 获取路径部分
// === 引擎特殊路径 ===
FString ProjectDir = FPaths::ProjectDir(); // 项目目录
FString ContentDir = FPaths::ProjectContentDir(); // Content目录
FString EngineDir = FPaths::EngineDir(); // 引擎目录
FString SaveDir = FPaths::ProjectSavedDir(); // 保存目录
FString ScreenShotDir = FPaths::ScreenShotDir(); // 截图目录
}
}
更多推荐



所有评论(0)