ue4学习笔记

Unreal Engine 4 Scripting with C++ Cookbook 简单笔记

  1. 主动删除Object 内存 调用 object->ConditionalBeginDestroy() 间隔一段时间后会被垃圾回收系统清除 也可以手动调用GetWorld()->ForceGarbageCollection(true) 来立即清空
    时间间隔的设置默认是60s 位置在Epic Games\4.11\Engine\Config \BaseEngine.ini gc.TimeBetweenPurgingPendingKillObjects=60
    2.创建UStruct
    创建。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.创建Enum
UENUM()
enum Status
{
Stopped UMETA(DisplayName = "display Stopped"),
Moving,
Attacking
};
使用Enum
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Status")
TEnumAsByte<Status> status;
4.内存管理
继承自UObject的类型通过声明UProperty 保存引用 会自动添加到GC系统中 可以自动删除 或者通过AddToRoot 防止被自动删除 需要删除时 调用RemoveFromRoot自动被gc系统删除
Actor 与 ActorComponent例外
非UObject类可以通过TSharedRef 或者 TWeakRef 智能指针来保存引用 智能指针智能不能保存UObject类型
TWeakPtr 不会保证引用会在内存中保存 所有可以通过ptr.isValid() 来判断指针指向的对象是否存在
TSharePtr 是线程安全的 不需要的画可以使用TAutoPtr
TScopedPointer 作用域智能指针 确保指针会在作用域结束后才能删除
如果使用TArray之类的容器保存UObject指针 需要将TArray声明UPorperty()

5.控制Actor的生存时间 一种方式是通过调用延迟函数 在延迟回调里面通过actor->destroy() 破坏掉
GetWorldTimerManager().SetTimer(Timer, this,
&AUE4CookbookGameMode::DestroyActorFunction, 10)
还有一种方式是通过 设置setLifeSpan(10) 在10s后actor会调用自身的Destroy()删除

  1. 没有任何Component的actor是没有任何意义的 既没有transform也不能附加到其他的actor上
    为Actor添加Component的话 通过在Actor的构造函数(必须要在构造函数中)调用CreateDefaultSubobject 创建Component 测试发现当将CreateDefaultSubobject创建的
    Component保存为UProperty是 名称参数不起作用 实际在Editor中显示的名称与变量名称一致
    加载StaticMesh资源 auto meshAsset = ConstructorHelpers::FObjectFinder<UStaticMesh>(TEXT("/Game/StarterContent/Props/SM_Bush"));
    其中路径部分的game对应的是Content文件夹 可以通过在资源右键 copyreference 获得该资源的路径

7.可以通过继承自ActorComponent 重写component类 可以通过GetOwner()获得Component绑定的Actor
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) 其中ClassGroup指定 在Editor中 component的分类 meta 指定该Component是否可以被蓝图actor使用

  1. 代理与事件的区别 代理的调用可以在任何可以访问到代理的类中进行访问 而事件 只可以在声明事件的类中进行访问

9.在代码中控制输出 继承Character类 在SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{中 调用PlayerInputComponent->BindAxis() 将在ProjectSetting、input中的动作名称 绑定到自定义的函数上
同理可调用BindAction() 绑定动作按键

  1. 在代码中动态的添加按键动作
    创建AxisKey FInputAxisKeyMapping forwardKey("Forward", EKeys::W, 1.0f)
    添加key GetWorld()->GetFirstPlayerController()->PlayerInput->AddAxisMapping(forwardKey)

  2. 创建接口类
    接口类指的是一组classs声明 其中U开头的类 继承自UInterface I开头的类是声明实际接口方法的类
    创建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();
};
创建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();
}

接口的使用 创建一个新的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;

};

检测是否实现接口 可通过
obj->GetClass()->ImplementsInterface(UInterface::StaticClass());

如果要创建继承的接口类 就要以UMyInterface为父类 IMyInterface为父类 按照上面的格式 重新添加接口类

12.创建一个被editor 蓝图识别的UObject类型
需要在UClass中 添加BlueprintType 表示该类可以被蓝图认为是一种type类型 可以作为返回 和 输入的节点

  1. 添加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模式下运行 又在发布版运行
    "LoadingPhase": "PostEngineInit", //模块加载时机
    "AdditionalDependencies": [
    "Engine",
    "CoreUObject"
    ]
    }
    ]
    }
    添加配置文件
    在Source下 参照原有的文件接口 添加UE4CookTestEditor 文件夹 里面相应创建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相关的函数 可以不加

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中的一致性 模块创建失败多是由于名称不一致引起的

  1. 添加工具ui到Editor中

editor的ui是通过TCommand的方式进行声明
在。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, //配合提示信息的 参数
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()); //创建button 保存到myButton中

undef LOCTEXT_NAMESPACE

}

在Editor module类中 重写StartupModule ShutdownModule函数 在其中将之前定义的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; //拓展类 存储添加菜单的结果
//按钮点击事件
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);
    }
};
//添加到工具栏的按钮添加执行函数
void AddToolbarExtension(FToolBarBuilder& builder)
{
    //创建按钮图标
    FSlateIcon iconBrush = FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.ViewOptions", "LevelEditor.ViewOptions.Small");
    //添加按钮 第一个参数是
    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()); //创建commandlist 保存我们绑定的事件
//绑定按钮事件 第一个参数是我们创建的按钮 第二个参数是按钮的执行回调 第三个参数是执行前的判断回调 为true的画才可以执行按钮事件 这里使用预定义的回调 始终返回true
CommandList->MapAction(FCookbookCommands::Get().myButton, FExecuteAction::CreateRaw(this, &FUE4CookTestEditorModule::MyButton_Clicked), FCanExecuteAction());
ToolbarExtender = MakeShareable(new FExtender()); //创建菜单类 可以用来拓展右键菜单 和工具栏菜单
//添加按钮到工具栏 第一个参数是添加的位置 可以通过Editor Setting/ Generay / miscellaneous /display uiextension points 获得菜单的位置 第二个参数是具体的位置在前
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()
{
//模块关闭时 移除拓展
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函数中 调用builder.AddMenuEntry
2.在。cpp extender的extender->AddMenuExtension 变成AddMenuExtension
3.LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(ToolbarExtender);

添加窗口
可以通过windows/develop tools/widget reflector 点击PickLiveWidget 获得窗口层级
TSharedRef<SWindow> CookbookWindow = SNew(SWindow) //创建一个SWindow类型的Slate
.Title(FText::FromString(TEXT("Cookbook Window"))) //设置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 创建ue4可用的新的资源类型
创建继承自UObject 自定义类
创建继承自UFactory 的自定义类 重写其中的FactoryCreateNew方法 在方法中创建我们自定义的类型
自定义的类型 可以通过在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 创建自定义资源的右键菜单
创建菜单类 继承自FAssetTypeActions_Base
.h文件

pragma once

include "AssetTypeActions_Base.h"

class FMyCustomAssetActions :public FAssetTypeActions_Base
{
public:
virtual bool HasActions(const TArray<UObject>& InObjects)const override; //系统调用 确认该类型是否有action
virtual void GetActions(const TArray<UObject
>& InObjects, FMenuBuilder& menuBuilder) override; //如果上面返回true 那么调用下面 添加菜单项
virtual FText GetName() const override; //资源在缩略图模式下 鼠标的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;
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容

  • 23种设计模式 “对象性能”模式 面向对象很好的解决了“抽象”的问题,但是必不可免地要付出一定的代价。对于通常情况...
    孙浩_9bfd阅读 377评论 0 0
  • 23种设计模式 “对象性能”模式 面向对象很好的解决了“抽象”的问题,但是必不可免地要付出一定的代价。对于通常情况...
    孙浩_9bfd阅读 507评论 0 0
  • 序言:今天(2017年3月19日)开始记录自己学习虚幻引擎4的历程。一方面将来可以看一下自己的经历(汲取iOS开发...
    小虫1234阅读 435评论 0 1
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,497评论 18 139
  • 姚来来来阅读 177评论 0 0