Unreal Engine 4 Scripting with C++ Cookbook 簡單筆記
- 主動刪除Object 內(nèi)存 調(diào)用 object->ConditionalBeginDestroy() 間隔一段時間后會被垃圾回收系統(tǒng)清除 也可以手動調(diào)用GetWorld()->ForceGarbageCollection(true) 來立即清空
時間間隔的設(shè)置默認(rèn)是60s 位置在Epic Games\4.11\Engine\Config \BaseEngine.ini gc.TimeBetweenPurgingPendingKillObjects=60
2.創(chuàng)建UStruct
創(chuàng)建。h文件
pragma once
include "ColoredTexture.generated.h"
USTRUCT()
struct FColoredTexture
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = HUD)
UTexture* Texture;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = HUD)
FLinearColor Color;
};
3.創(chuàng)建Enum
UENUM()
enum Status
{
Stopped UMETA(DisplayName = "display Stopped"),
Moving,
Attacking
};
使用Enum
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Status")
TEnumAsByte<Status> status;
4.內(nèi)存管理
繼承自UObject的類型通過聲明UProperty 保存引用 會自動添加到GC系統(tǒng)中 可以自動刪除 或者通過AddToRoot 防止被自動刪除 需要刪除時 調(diào)用RemoveFromRoot自動被gc系統(tǒng)刪除
Actor 與 ActorComponent例外
非UObject類可以通過TSharedRef 或者 TWeakRef 智能指針來保存引用 智能指針智能不能保存UObject類型
TWeakPtr 不會保證引用會在內(nèi)存中保存 所有可以通過ptr.isValid() 來判斷指針指向的對象是否存在
TSharePtr 是線程安全的 不需要的畫可以使用TAutoPtr
TScopedPointer 作用域智能指針 確保指針會在作用域結(jié)束后才能刪除
如果使用TArray之類的容器保存UObject指針 需要將TArray聲明UPorperty()
5.控制Actor的生存時間 一種方式是通過調(diào)用延遲函數(shù) 在延遲回調(diào)里面通過actor->destroy() 破壞掉
GetWorldTimerManager().SetTimer(Timer, this,
&AUE4CookbookGameMode::DestroyActorFunction, 10)
還有一種方式是通過 設(shè)置setLifeSpan(10) 在10s后actor會調(diào)用自身的Destroy()刪除
- 沒有任何Component的actor是沒有任何意義的 既沒有transform也不能附加到其他的actor上
為Actor添加Component的話 通過在Actor的構(gòu)造函數(shù)(必須要在構(gòu)造函數(shù)中)調(diào)用CreateDefaultSubobject 創(chuàng)建Component 測試發(fā)現(xiàn)當(dāng)將CreateDefaultSubobject創(chuàng)建的
Component保存為UProperty是 名稱參數(shù)不起作用 實際在Editor中顯示的名稱與變量名稱一致
加載StaticMesh資源 auto meshAsset = ConstructorHelpers::FObjectFinder<UStaticMesh>(TEXT("/Game/StarterContent/Props/SM_Bush"));
其中路徑部分的game對應(yīng)的是Content文件夾 可以通過在資源右鍵 copyreference 獲得該資源的路徑
7.可以通過繼承自ActorComponent 重寫component類 可以通過GetOwner()獲得Component綁定的Actor
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) 其中ClassGroup指定 在Editor中 component的分類 meta 指定該Component是否可以被藍(lán)圖actor使用
- 代理與事件的區(qū)別 代理的調(diào)用可以在任何可以訪問到代理的類中進(jìn)行訪問 而事件 只可以在聲明事件的類中進(jìn)行訪問
9.在代碼中控制輸出 繼承Character類 在SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{中 調(diào)用PlayerInputComponent->BindAxis() 將在ProjectSetting、input中的動作名稱 綁定到自定義的函數(shù)上
同理可調(diào)用BindAction() 綁定動作按鍵
在代碼中動態(tài)的添加按鍵動作
創(chuàng)建AxisKey FInputAxisKeyMapping forwardKey("Forward", EKeys::W, 1.0f)
添加key GetWorld()->GetFirstPlayerController()->PlayerInput->AddAxisMapping(forwardKey)創(chuàng)建接口類
接口類指的是一組classs聲明 其中U開頭的類 繼承自UInterface I開頭的類是聲明實際接口方法的類
創(chuàng)建MyInterface。h 寫出一下代碼
// Fill out your copyright notice in the Description page of Project Settings.
pragma once
include "CoreMinimal.h"
include "MyInterface.generated.h"
/**
*/
UINTERFACE()
class UE4COOK_API UMyInterface:public UInterface
{
GENERATED_BODY()
};
class UE4COOK_API IMyInterface
{
GENERATED_BODY()
public:
virtual FString GetTestName();
};
創(chuàng)建MyInterface.cpp
// Fill out your copyright notice in the Description page of Project Settings.
include "MyInterface.h"
include "UE4Cook.h"
FString IMyInterface::GetTestName()
{
unimplemented();
return FString();
}
接口的使用 創(chuàng)建一個新的actor類 繼承該接口 并重寫GetTestName類
class UE4COOK_API ASingleInterfaceActor : public AActor, public IMyInterface
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ASingleInterfaceActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
FString GetTestName() override;
};
檢測是否實現(xiàn)接口 可通過
obj->GetClass()->ImplementsInterface(UInterface::StaticClass());
如果要創(chuàng)建繼承的接口類 就要以UMyInterface為父類 IMyInterface為父類 按照上面的格式 重新添加接口類
12.創(chuàng)建一個被editor 藍(lán)圖識別的UObject類型
需要在UClass中 添加BlueprintType 表示該類可以被藍(lán)圖認(rèn)為是一種type類型 可以作為返回 和 輸入的節(jié)點
- 添加module的流程
右鍵.uproject文件 添加新的模塊
{
"FileVersion": 3,
"EngineAssociation": "4.16",
"Category": "",
"Description": "",
"Modules": [
{
"Name": "UE4Cook",
"Type": "Runtime",
"LoadingPhase": "Default",
"AdditionalDependencies": [
"Engine",
"CoreUObject"
]
},
{
"Name": "UE4CookTestEditor", //新模塊名稱
"Type": "Editor", //運行模式 Runtime 表示 既在editor模式下運行 又在發(fā)布版運行
"LoadingPhase": "PostEngineInit", //模塊加載時機(jī)
"AdditionalDependencies": [
"Engine",
"CoreUObject"
]
}
]
}
添加配置文件
在Source下 參照原有的文件接口 添加UE4CookTestEditor 文件夾 里面相應(yīng)創(chuàng)建xx.h xx.build.cs xx.cpp文件
.build.cs
using UnrealBuildTool;
public class UE4CookTestEditor : ModuleRules
{
public UE4CookTestEditor(ReadOnlyTargetRules Target) : base(Target)
{
PublicDependencyModuleNames.AddRange(new string[] {"Core", "CoreUObject", "Engine", "InputCore", "RHI","RenderCore", "ShaderCore" });
PublicDependencyModuleNames.Add("UE4Cook"); //主模塊
PrivateDependencyModuleNames.AddRange(new string[] {"UnrealEd" });
}
}
其中。h文件
#pragma once
include "CoreMinimal.h"
include "Engine.h"
include "ModuleManager.h"
include "UnrealEd.h" //為了使用Editor相關(guān)的函數(shù) 可以不加
class FUE4CookTestEditorModule: public IModuleInterface
{
};
.cpp文件
include "UE4CookTestEditor.h"
include "Modules/ModuleManager.h"
IMPLEMENT_PRIMARY_GAME_MODULE( FUE4CookTestEditorModule, UE4CookTestEditor); //此處的名稱要與。uproject中配置的名稱保持一致
添加完以上文件后 右鍵。uproject 重新生成vsstudio文件
編輯UE4CookTestEditor.Target.cs
using UnrealBuildTool;
using System.Collections.Generic;
public class UE4CookEditorTarget : TargetRules
{
public UE4CookEditorTarget(TargetInfo Target) : base(Target)
{
Type = TargetType.Editor;
ExtraModuleNames.AddRange( new string[] { "UE4CookTestEditor" } );
}
}
編譯程序并運行 點開/developTools/Modules 查看自定義的module是否正確添加
主要要點 一定要確保模塊名稱在文件名 。build.cs .h 。cpp中的一致性 模塊創(chuàng)建失敗多是由于名稱不一致引起的
- 添加工具ui到Editor中
editor的ui是通過TCommand的方式進(jìn)行聲明
在。build.cs中
publicDependencyModuleNames 添加Slate模塊
添加CookbookCommands.h
pragma once
include "Commands.h"
include "EditorStyleSet.h"
class FCookbookCommands : public TCommands<FCookbookCommands>
{
public:
FCookbookCommands() :TCommands<FCookbookCommands>(FName(TEXT("UE4_Cookbook")), //command 名稱
FText::FromString("Cookbook Commands"), // 提示信息
NAME_None, //配合提示信息的 參數(shù)
FEditorStyle::GetStyleSetName())
{};
virtual void RegisterCommands() override;
TSharedPtr<FUICommandInfo> myButton;
};
添加CookbookCommands.cpp
include "UE4CookTestEditor.h"
include "Commands.h"
include "CookbookCommands.h"
void FCookbookCommands::RegisterCommands()
{
define LOCTEXT_NAMESPACE ""
UI_COMMAND(myButton, "Cookbook", "Demo Cookbook Toolbar command", EUserInterfaceActionType::Button, FInputGesture()); //創(chuàng)建button 保存到myButton中
undef LOCTEXT_NAMESPACE
}
在Editor module類中 重寫StartupModule ShutdownModule函數(shù) 在其中將之前定義的command 綁定到具體的事件和ui中
editor.h文件
// Fill out your copyright notice in the Description page of Project Settings.
pragma once
include "CoreMinimal.h"
include "Engine.h"
include "ModuleManager.h"
include "UnrealEd.h"
include "MainFrame.h" //需要在。build.cs中添加Mainframe模塊
include "CookbookCommands.h"
include "MultiBoxExtender.h"
class FUE4CookTestEditorModule: public IModuleInterface
{
public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
TSharedPtr<FExtender> ToolbarExtender; //菜單類 可以用來拓展右鍵菜單 和工具欄菜單
TSharedPtr<const FExtensionBase> Extension; //拓展類 存儲添加菜單的結(jié)果
//按鈕點擊事件
void MyButton_Clicked()
{
TSharedPtr<SWindow> CookbookWindow = SNew(SWindow)
.Title(FText::FromString(TEXT("Co Window")))
.ClientSize(FVector2D(800, 400))
.SupportsMaximize(false)
.SupportsMinimize(false);
IMainFrameModule& mainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>(TEXT("MainFrame"));
if (mainFrameModule.GetParentWindow().IsValid())
{
FSlateApplication::Get().AddWindowAsNativeChild(CookbookWindow, mainFrameModule.GetParentWindow().ToSharedRef())
}
else
{
FSlateApplication::Get().AddWindow(CookbookWindow);
}
};
//添加到工具欄的按鈕添加執(zhí)行函數(shù)
void AddToolbarExtension(FToolBarBuilder& builder)
{
//創(chuàng)建按鈕圖標(biāo)
FSlateIcon iconBrush = FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.ViewOptions", "LevelEditor.ViewOptions.Small");
//添加按鈕 第一個參數(shù)是
builder.AddToolBarButton(FCookbookCommands::Get().myButton, NAME_None, FText::FromString("btn"), FText::FromString("Click to display", iconBrush, NAME_None));
};
};
editor.cpp文件
// Fill out your copyright notice in the Description page of Project Settings.
include "UE4CookTestEditor.h"
include "Modules/ModuleManager.h"
include "ILevelEditor.h"
include "SlateBasics.h"
IMPLEMENT_GAME_MODULE(FUE4CookTestEditorModule, UE4CookTestEditor);
void FUE4CookTestEditorModule::StartupModule()
{
FCookbookCommands::Register(); //注冊自定義command
TSharedPtr<FUICommandList> CommandList = MakeShareable(new FUICommandList()); //創(chuàng)建commandlist 保存我們綁定的事件
//綁定按鈕事件 第一個參數(shù)是我們創(chuàng)建的按鈕 第二個參數(shù)是按鈕的執(zhí)行回調(diào) 第三個參數(shù)是執(zhí)行前的判斷回調(diào) 為true的畫才可以執(zhí)行按鈕事件 這里使用預(yù)定義的回調(diào) 始終返回true
CommandList->MapAction(FCookbookCommands::Get().myButton, FExecuteAction::CreateRaw(this, &FUE4CookTestEditorModule::MyButton_Clicked), FCanExecuteAction());
ToolbarExtender = MakeShareable(new FExtender()); //創(chuàng)建菜單類 可以用來拓展右鍵菜單 和工具欄菜單
//添加按鈕到工具欄 第一個參數(shù)是添加的位置 可以通過Editor Setting/ Generay / miscellaneous /display uiextension points 獲得菜單的位置 第二個參數(shù)是具體的位置在前
Extension = ToolbarExtender->AddToolBarExtension("Compile", EExtensionHook::Before, CommandList, FToolBarExtensionDelegate::CreateRaw(this,
&FUE4CookTestEditorModule::AddToolbarExtension));
//將工具欄添加到editor中
FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender);
}
void FUE4CookTestEditorModule::ShutdownModelu()
{
//模塊關(guān)閉時 移除拓展
ToolbarExtender->RemoveExtension(Extension.ToSharedRef());
Extension.Reset();
ToolbarExtender.Reset();
}
.build.cs
// Fill out your copyright notice in the Description page of Project Settings.
using UnrealBuildTool;
public class UE4CookTestEditor : ModuleRules
{
public UE4CookTestEditor(ReadOnlyTargetRules Target) : base(Target)
{
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "RHI", "RenderCore", "ShaderCore", "Slate", "SlateCore", "MainFrame", "EditorStyle", });
PublicDependencyModuleNames.Add("UE4Cook");
PrivateDependencyModuleNames.AddRange(new string[] { "UnrealEd" });
}
}
添加菜單按鈕
與普通按鈕不一樣的地方
1.在。h的Add函數(shù)中 調(diào)用builder.AddMenuEntry
2.在。cpp extender的extender->AddMenuExtension 變成AddMenuExtension
3.LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(ToolbarExtender);
添加窗口
可以通過windows/develop tools/widget reflector 點擊PickLiveWidget 獲得窗口層級
TSharedRef<SWindow> CookbookWindow = SNew(SWindow) //創(chuàng)建一個SWindow類型的Slate
.Title(FText::FromString(TEXT("Cookbook Window"))) //設(shè)置window的屬性
.ClientSize(FVector2D(800, 400))
.SupportsMaximize(false)
.SupportsMinimize(false)
[ //在【】中添加要拜訪到該slot的ui window只有能添加一個widget
SNew(SVerticalBox)
+ SVerticalBox::Slot() //通過重載運算符 + 獲得一個slot
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(FText::FromString(TEXT("Hello from Slate")))
]
];
IMainFrameModule& mainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>(TEXT("MainFrame"));
if (mainFrameModule.GetParentWindow().IsValid())
{
FSlateApplication::Get().AddWindowAsNativeChild(CookbookWindow, mainFrameModule.GetParentWindow().ToSharedRef());
}
else
{
FSlateApplication::Get().AddWindow(CookbookWindow);
}
15 創(chuàng)建ue4可用的新的資源類型
創(chuàng)建繼承自UObject 自定義類
創(chuàng)建繼承自UFactory 的自定義類 重寫其中的FactoryCreateNew方法 在方法中創(chuàng)建我們自定義的類型
自定義的類型 可以通過在content中 右鍵 miscellaneous中查詢到
。cpp文件
// Fill out your copyright notice in the Description page of Project Settings.
include "CustomAssetFactory.h"
include "MyCustomAsset.h"
UCustomAssetFactory::UCustomAssetFactory():Super()
{
bCreateNew = true;
bEditAfterNew = true;
SupportedClass = UMyCustomAsset::StaticClass();
}
UObject* UCustomAssetFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn, FName CallingContext)
{
auto NewObjectAsset = NewObject<UMyCustomAsset>(InParent, InClass, InName, Flags);
return NewObjectAsset;
}
16 創(chuàng)建自定義資源的右鍵菜單
創(chuàng)建菜單類 繼承自FAssetTypeActions_Base
.h文件
pragma once
include "AssetTypeActions_Base.h"
class FMyCustomAssetActions :public FAssetTypeActions_Base
{
public:
virtual bool HasActions(const TArray<UObject>& InObjects)const override; //系統(tǒng)調(diào)用 確認(rèn)該類型是否有action
virtual void GetActions(const TArray<UObject>& InObjects, FMenuBuilder& menuBuilder) override; //如果上面返回true 那么調(diào)用下面 添加菜單項
virtual FText GetName() const override; //資源在縮略圖模式下 鼠標(biāo)的tip提示
virtual UClass* GetSupportedClass() const override; //資源縮略圖的顏色
virtual FColor GetTypeColor() const override;
virtual uint32 GetCategories() override;
void MyCustomAssetContent_Clicked();
};
.cpp文件
include "UE4CookTestEditor.h"
include "MyCustomAssetActions.h"
include "MyCustomAsset.h"
bool FMyCustomAssetActions::HasActions(const TArray<UObject*>& InObjects) const
{
return true;
}
void FMyCustomAssetActions::GetActions(const TArray<UObject*>& InObjects, FMenuBuilder& menuBuilder)
{
menuBuilder.AddMenuEntry(FText::FromString("CustomAssetAction"),
FText::FromString("Action from Cook"),
FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.ViewOptions"),
FUIAction(FExecuteAction::CreateRaw(this, &FMyCustomAssetActions::MyCustomAssetContent_Clicked),
FCanExecuteAction())
);
}
uint32 FMyCustomAssetActions::GetCategories()
{
return EAssetTypeCategories::Misc;
}
FText FMyCustomAssetActions::GetName() const
{
return FText::FromString(TEXT("My Custom Asset"));
}
UClass* FMyCustomAssetActions::GetSupportedClass() const
{
return UMyCustomAsset::StaticClass();
}
FColor FMyCustomAssetActions::GetTypeColor() const
{
return FColor::Emerald;
}
void FMyCustomAssetActions::MyCustomAssetContent_Clicked()
{
TSharedRef<SWindow> CookbookWindow = SNew(SWindow)
.Title(FText::FromString(TEXT("Cookbook Window")))
.ClientSize(FVector2D(800, 400))
.SupportsMaximize(false)
.SupportsMaximize(false);
IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>(TEXT("MainFrame"));
if (MainFrameModule.GetParentWindow().IsValid())
{
FSlateApplication::Get().AddWindowAsNativeChild(CookbookWindow, MainFrameModule.GetParentWindow().ToSharedRef());
}
else
{
FSlateApplication::Get().AddWindow(CookbookWindow);
}
}
在Editor Module startModule中
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
auto Actions = MakeShareable(new FMyCustomAssetActions);
AssetTools.RegisterAssetTypeActions(Actions);
刪除的時候 在shutdownModule中
//刪除command
if (DisplayTestCommand)
{
IConsoleManager::Get().UnregisterConsoleObject(DisplayTestCommand);
DisplayTestCommand = nullptr;
}