[17] C++ 虚幻引擎网络系统开发
Day1
加载UserWidget
//HUD.cpp
Super::BeginPlay();
//显示UMG
//1.加载类
//参数outer:职责用来管理关系链,用来方便做内存管理
//outer如果填nullptr,则表明加载的数据为临时数据,使用声明周期不是持久的,将被放置在临时数据空间,随着内存回收会被第一批回收
//destroy:用来标记actor消亡(挂载在actor身上的组件呢?)
//TEXT("")和""有什么区别?TEXT宏创建的是与平台无关(不强制以来任何平台特性,方便跨平台)的数据字符串,直接双引号创建的是c++的字符串
//string:是不是就是字符串?他是字符串对象
//_c:用来告知加载器,加载的是蓝图类
TSubclassOf<UMainMenuUserWidget> MyClass = LoadClass<UMainMenuUserWidget>(
nullptr, TEXT("/Script/UMGEditor.WidgetBlueprint'/Game/Lego/UMG/MainMenu/UMG_MainMenu.UMG_MainMenu_C'"));
//TSubclassOf<>:虚幻中添加的类的类型模板,用来方便我们拾取操作类
//2.创建实例
if (MyClass)
{
//第一个参数填玩家控制器
UMainMenuUserWidget* MyMainMenuUI = CreateWidget<UMainMenuUserWidget>(GetOwningPlayerController(), MyClass);
//3.添加到视口
MyMainMenuUI->AddToViewport();
//显示鼠标光标
GetOwningPlayerController()->SetShowMouseCursor(true);
}
C++ 调用 蓝图的办法
使用GetWidgetFromName获取蓝图对象
NativeOnInitialized中函数调用时机
思维导图
Day2
程序的状态
类型转换 : dynamic_cast和static_cast区别是 , dynamic会查虚函数表
蓝图绑定控件 :
//强制让子类蓝图绑定一个控件
UPROPERTY(meta=(BindWidgetAnim))
C++ 绑定蓝图的单击事件 多播代理
注意点 :
1、绑定函数的绑定原型是多播代理声明时候的原型
2、绑定的函数要加UFUNCTION
3、最大最用是可以在蓝图中事件绑定
说明 : 从而实现在C++ 父类强制子类绑定按钮指针、
命名 : 全屏UMG***** 子控件Widget_****
项目制作 :
材质是像素 运算的过程
C++调用蓝图动画
umg里面 : Get Owning Player 和其他蓝图获取playercontroller一样
C++控制蓝图显示UI
Widget Switcher 选项卡控件
Spacer控件
FString、FText、FName
国际化语言
选中文本路径
添加新语言 -> 收集文本 -> 编辑此语言的翻译 -> 计算字数 -> 编译文本
蓝图设置
控制台运行C++函数
void AMainMenuGameMode::CallFunc()
{
FText t1 = NSLOCTEXT("s1", "k1", "二狗");
GEngine->AddOnScreenDebugMessage(0, 10, FColor::Green, t1.ToString());
}
Day3
分辨率更改和全屏
播放声音
创建 :
加载文件什么情况需要加_C
按键拾取 :
拾取用户输入的按键用作自定义按键
增强输入
TMap同质容器
TMap<int32, FString> Map;
Map.Add(1, "123");
Map.Add(2, "234");
Map.Add(3, "345");
Map.Add(4, "567");
if (Map.Contains(1))
// {
UE_LOG(LogTemp, Log, TEXT("ok"))
}
else
{
UE_LOG(LogTemp, Log, TEXT("no"))
}
UE_LOG(LogTemp, Log, TEXT("读取 %s"), *Map[1]);
for (auto Kv : Map)
{
UE_LOG(LogTemp, Log, TEXT("%d"),Kv.Key)
UE_LOG(LogTemp, Log, TEXT("%s"),*Kv.Value)
}
UE_LOG(LogTemp, Log, TEXT("检查容器大小 = %d"), Map.Num())
Map.Remove(1);
Map.Reset();
自定义结构体生成数据报表
报错查找 : 无法解析外部符号 , 有声明没定义
CastTo : Cast只能在U类使用
自定义数据类型不需要加UPROPERTY()
Tips : 新代码运行前先保存
Day4
封装创建Widget代码块
宏
//定义
#define MAKEUSERWIDGETOBJECT(TClass, ObjectPtr, BPClassPath) if (!ObjectPtr)\
{\
TSubclassOf<TClass> UIClass = LoadClass<TClass>(nullptr, BPClassPath);\
ObjectPtr = CreateWidget<TClass>(GetOwningPlayerController(), UIClass);\
}
//使用
MAKEUSERWIDGETOBJECT(USettingUserWidget, SettingUserWidget, TEXT("/Script/UMGEditor.WidgetBlueprint'/Game/Lego/UMG/MainMenu/UMG_Setting.UMG_Setting_C'"));
//卸载
#undef MAKEUSERWIDGETOBJECT
泛型 or 模板
//定义
template <typename T>
void AMainMenuHUD::MakeUserWidgetObject(T*& ObjectPtr, const TCHAR* Path)//T*& ObjectPtr 对一个泛型指针进行引用
{
if (!ObjectPtr)
{
TSubclassOf<T> UIClass = LoadClass<T>(nullptr, Path);
if (!UIClass)
{
UE_LOG(LogTemp, Log, TEXT("LoadClass Path is invaild!"));
}
ObjectPtr = CreateWidget<T>(GetOwningPlayerController(), UIClass);
}
}
//使用
MakeUserWidgetObject<URegisterUserWidget>(RegisterUserWidget, TEXT("蓝图类文件路径"));
//MakeUserWidgetObject 函数名 <URegisterUserWidget> 编程泛型 T (Widget指针, TEXT("蓝图类文件路径"));
//泛型 : 做出功能但不考虑其类型 , 但会在编译阶段判断是否正确比如 ↓↓↓↓ 泛型编程处理的是逻辑
UMG_API
HUD : GetOwningPlayerController() //拿到HUD拥有者
GetOwningPlayer(); //拿到UMG拥有者
GetParent(); // 查找父节点
GetChildrenCount(); //查找子控件数量
GetChildAt(**); //获取子控件
UUserWidget的构造函数
UserWidget没有无参构造 : 如果父类只有带参构造函数没有无参构造 , 子类需要传递带参构造
Day5
正则表达式
//校验邮箱地址是否合规
FRegexPattern Pattern(TEXT("^(\\w|\\.){1,10}@[a-zA-Z0-9]{1,10}\\.[a-zA-Z]{2,6}$")); //*****@***.**
FRegexMatcher Matcher(Pattern, MainTextBox->GetText().ToString());
if (!Matcher.FindNext())
{
UE_LOG(LogTemp, Log, TEXT("邮箱地址非法!"));
return;
}
增强输入
新建输入
IA_Move
IM_Player
挂载增强输入组件
UPROPERTY(EditAnywhere)
UInputAction* IA_Jump;
UPROPERTY(EditAnywhere)
UInputMappingContext* InputMappingContext;
按键映射到对应的输入操作
操作映射
if (DTKeyMap.Contains(TEXT("Jump")))
{
FKeyInfoHeader* KeyInfoHeader = reinterpret_cast<FKeyInfoHeader*>(DTKeyMap[TEXT("Jump")]);
//装载按键和InputAction
FKey Key = GetUserCustomKey(TEXT("Jump"));
if (!Key.IsValid())//如果用户没有自定义过按键,则使用默认的
{
Key = KeyInfoHeader->DefaultKey;
}
MappingContext->MapKey(KeyInfoHeader->InputAction, Key); //按键映射到对应的输入操作
}
使用宏的方式
#define ADDMAPKEY_DIGITALACTION(KeyEventName) if (DTKeyMap.Contains(KeyEventName))\
{\
FKeyInfoHeader* KeyInfoHeader = reinterpret_cast<FKeyInfoHeader*>(DTKeyMap[KeyEventName]);\
FKey Key = GetUserCustomKey(KeyEventName);\
if (!Key.IsValid())\
{\
Key = KeyInfoHeader->DefaultKey;\
}\
MappingContext->MapKey(KeyInfoHeader->InputAction, Key);\
}
#undef ADDMAPKEY_DIGITALACTION
轴映射
if (DTKeyMap.Contains(TEXT("MoveForward")))
{
FKeyInfoHeader* KeyInfoHeader = reinterpret_cast<FKeyInfoHeader*>(DTKeyMap[TEXT("MoveForward")]);
//装载按键和InputAction
FKey Key = GetUserCustomKey(TEXT("MoveForward"));
if (!Key.IsValid()) //如果用户没有自定义过按键,则使用默认的
{
Key = KeyInfoHeader->DefaultKey;
}
MappingContext->MapKey(KeyInfoHeader->InputAction, Key);
}
if (DTKeyMap.Contains(TEXT("MoveBack")))
{
FKeyInfoHeader* KeyInfoHeader = reinterpret_cast<FKeyInfoHeader*>(DTKeyMap[TEXT("MoveBack")]);
//装载按键和InputAction
FKey Key = GetUserCustomKey(TEXT("MoveBack"));
if (!Key.IsValid()) //如果用户没有自定义过按键,则使用默认的
{
Key = KeyInfoHeader->DefaultKey;
}
FEnhancedActionKeyMapping& KeyMapping = MappingContext->MapKey(KeyInfoHeader->InputAction, Key);
//添加值反转
UInputModifierNegate* Negate = NewObject<UInputModifierNegate>(MappingContext); //翻转
KeyMapping.Modifiers.Add(Negate);
}
//获取是否有自定义按键
FKey ALGPlayerCharacter::GetUserCustomKey(FName KeyEventName)
{
FKey Key;
//检查有没有存档
if (!KeyMappingSaveGame && UGameplayStatics::DoesSaveGameExist(TEXT("userkey"), 0))
{
KeyMappingSaveGame = Cast<UKeyMappingSaveGame>(UGameplayStatics::LoadGameFromSlot(TEXT("userkey"), 0));
}
if (KeyMappingSaveGame && KeyMappingSaveGame->CustomKeys.Contains(KeyEventName))
{
Key = KeyMappingSaveGame->CustomKeys[KeyEventName];
}
return Key;
}
装载增强输入系统
//删掉读档内存
KeyMappingSaveGame = nullptr; //释放SaveGame内存 ; 会进入虚幻 内存回收机制
//获取增强输入系统
UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<
UEnhancedInputLocalPlayerSubsystem>(Pc->GetLocalPlayer());
//装载Mapping
Subsystem->AddMappingContext(MappingContext, 0);
绑定按键事件
// Called to bind functionality to input
void ALGPlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
//绑定按键的事件Action
if (UEnhancedInputComponent* MyInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent))
{
if (UInputAction* MyInputAction = GetInputAction(TEXT("Jump")))
{
MyInputComponent->BindAction(MyInputAction, ETriggerEvent::Started, this, &ALGPlayerCharacter::DoJump);
}
if (UInputAction* MyInputAction = GetInputAction(TEXT("Crouch")))
{ // 绑的事件 , 方式 , 对象 , 触发事件
MyInputComponent->BindAction(MyInputAction, ETriggerEvent::Started, this, &ALGPlayerCharacter::DoCrouch);
}
if (UInputAction* MyInputAction = GetInputAction(TEXT("MoveForward")))
{
MyInputComponent->BindAction(MyInputAction, ETriggerEvent::Triggered, this, &ALGPlayerCharacter::Move);
}
}
}
//数据表中检索并返回与特定事件相关联的输入动作对象
UInputAction* ALGPlayerCharacter::GetInputAction(FName KeyEventName)
{
if (!KeyDataTable)
{
KeyDataTable = LoadObject<UDataTable>(
nullptr, TEXT("/Script/Engine.DataTable'/Game/Lego/Data/UI/DT_KeyInfo.DT_KeyInfo'"));
}
if (KeyDataTable)
{
TMap<FName, uint8*> DTKeyMap = KeyDataTable->GetRowMap();
if (DTKeyMap.Contains(KeyEventName))
{
FKeyInfoHeader* KeyInfoHeader = reinterpret_cast<FKeyInfoHeader*>(DTKeyMap[KeyEventName]);
return KeyInfoHeader->InputAction;
}
}
return nullptr;
}