ue4-笔记

学习 ue4 的一些笔记记录, 记录各种踩坑, 也是一种不归路的写照. 持续更新中…
之前也有写过点,这里做个汇总,顺便补上漏记得东东,持续更新……
这里以 MyTest 项目名说明。


视频教程集合


不错的 GitHub 残酷


官方编码标准


包含头文件的一些规则

  • 包含引擎类。如果需要扩展或使用引擎类,都在项目模块的预编译头文件 MyTest.h 中包含进来。自己创建的头文件都会包含这个 MyTest.h 头文件。

    1
    2
    3
    4
    5
    6
    7
    #ifndef __MYTEST_H__
    #define __MYTEST_H__

    #include "EngineMinimal.h"
    #include "EngineGlobals.h"
    #include "Engine/Engine.h"
    #include "GameFramework/GameMode.h"
  • 包含自己创建的类。

    • 头文件 MyGameMode.h 中的自动生成的头文件 MyGameMode.generated.h 一定要放在最后包含
      1
      2
      3
      4
      5
      6
      #pragma once
      //#include "MyUtil.h"
      #include "MyGameMode.generated.h"

      UCLASS()
      class MYTEST_API AMyGameMode : public AGameMode
    • cpp文件 中一定要先包含预编译头文件 MyTest.h 和对应的头文件 MyGameMode.h,在包含其他头文件
      1
      2
      3
      4
      5
      #include "MyTest.h"
      #include "MyGameMode.h"

      //#include "MyUtil.h"
      AMyGameMode::AMyGameMode() : Super()

安装 vs2017

安装 企业版 vs_enterprise.exe , 激活秘钥 VS2017序列号|Visual Studio 2017 激活码 序列号

  • 企业版:NJVYC-BMHX2-G77MM-4XJMR-6Q8QF
  • 专业版:KBJFW-NXHK6-W4WJM-CRMQB-G3CDH

下载 企业版: https://visualstudio.microsoft.com/zh-hans/downloads/

参考官方文档要求: https://docs.unrealengine.com/en-us/Programming/Development/VisualStudioSetup 中的 Options for a New Visual Studio Installation, 如果缺少组件, 可以再次双击 vs_enterprise.exe 安装缺少的组件.
安装视频教程: Setting up Visual Studio with UE4 - https://www.youtube.com/watch?v=4yQTGwuFm1w

常用快捷键

快捷键 功能
Ctrl+K, F 格式化选中的代码

安装 Visual Assist X

vs中智能提示神器.

常用快捷键

快捷键 功能
Alt + G 在定义与声明之间互跳。
Alt + O 在.h与.cpp之间互跳。(O是字母O,不是数字零)
Alt + Shift + Q 鼠标定位到函数名上,若是在h文件中,按此快捷键会弹出右键菜单,里面有个选项–创建定义;若是在cpp文件中,则按此快捷键会弹出右键菜单,里面有一个选项–创建声明。
Alt + Shift + R 当想改掉一个类名或是其他东西的命名时,可能已经有很多地方引用这个名称了,这时按下此快捷键,可以很方便的辅助你重命名。
Alt + Shift + S 方便你寻找某个对象或变量等等。
Alt + Shift + O 定位文件。项目文件太多时,这个会帮上大忙,当然,你的文件名命名最好有个比较好的规范。
Alt + Shift + F 光标放到某个字符串上,按下此键,会找出所有引用了这个字符串的地方。

vscode 环境配置

  1. vscode 中需要必须安装的两个个插件: c/c++c#. 辅助插件: unreal engine 4 snippets, vscode-epics

  2. [安装 vs2017](#安装 vs2017). (本来安装 vs2017 build tools 就可以, 但貌似找不到了, 只好安装 vs2017)

  3. 设置编辑器为 vscode. edit -> editor preferences -> source code

  4. 生成 vscode 项目. file -> generate visual studio code project. (因为我已经生成给过了, 所以才显示 refresh)

  5. vscode 打开项目. file -> open visual studio code 或者 双击 cpp 类


Android 打包

配置环境

edit->project settings->platforms->android

配置 sdk,ndk,java,ant 的目录

不生成 OBB 数据包

默认打包会分成 apk 安装包 和 obb 数据包, 如果不想要分包, 全都打在一个 apk 中, 可以在 edit->project settings->platforms->android 中勾选上 package game data inside .apk

简单的打包, 夜神模拟器跑


log 乱码

参考: Unreal Engine 4:编译时出现中文乱码问题 - https://blog.csdn.net/netyeaxi/article/details/81206896

实测, 只有把 visual studio 的语言换成 英语, 才能解决问题. 在vs中 工具 -> 选项 -> 环境 -> 区域设置 -> 语言 中选择 English. (如果没有该语言, 可以通过 vs_enterprise.exe 安装语言包)


关掉鼠标hover节点时的提示

参考: https://answers.unrealengine.com/questions/302345/is-there-any-way-to-disable-editor-tooltips.html

window -> develop tools -> session fronted,

  • 输入命令 Slate.AllowToolTips 0 , 回车关掉提示.
  • 输入命令 Slate.AllowToolTips 1 , 回车打开提示.


配置默认的GameMode、GameInstance、Map等


多人游戏的关卡切换

官网教程


暴露给蓝图的规则

  • class类,在类的头上加上标记 BlueprintableBlueprintType

    1
    2
    UCLASS(Blueprintable, BlueprintType)
    class MYTEST_API UCoolDownComp : public UActorComponent
    • Blueprintable: 可在新建一个蓝图类时继承c++类,显示在可继承列表中
    • BlueprintType: 这个c++类可作为一个类型选择,显示在类型选择中
  • enum枚举,加上 BlueprintType 即可,一般不用来继承使用

    1
    2
    UENUM(BlueprintType)
    enum class CharState : uint8 //动画状态

SceneComponent


启动游戏添加参数解析


创建 .uproject的 vs2013解决方案 xxx.sln文件

  • 创建工程的时候如果创建的是蓝图工程,可以通过两种方式创建vs工程
  1. 直接在编辑器中创建, 随便添加一个 c++类 即可
  2. 命令行创建
  • 其实就是使用ubt工具+参数


  • 先cd到这个目录中,再使用命令生成vs2013的解决方案,默认是2015,没有安装的话会报错

    1
    I:\UnrealEngine_4.10\Engine\Binaries\DotNET>UnrealBuildTool.exe -projectfiles -project="F:/workplace_ue4/ShooterGame_4.10/data/ShooterGame.uproject" -game -rocket -progress -2013

  • vs工程就蹦出了


C++动态加载即替换character的 skeletal mesh 和 anim blueprint

  • 两者必须在同一帧同时替换,因为一个模型对应一个动画

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    bool UMyBpFuncLib::TestChangeCharAnimInstance(AMyChar* _myChar, FString _pathMesh, FString _pathAnim)
    {
    FStreamableManager* stream = new FStreamableManager();
    FStringAssetReference ref1(*_pathMesh);
    USkeletalMesh* TmpMesh = Cast<USkeletalMesh>(stream->SynchronousLoad(ref1));
    _myChar->GetMesh()->SetSkeletalMesh(TmpMesh);

    FStringAssetReference ref2(*_pathAnim);
    UAnimBlueprint* TmpMeshAnim = Cast<UAnimBlueprint>(stream->SynchronousLoad(ref2));
    _myChar->GetMesh()->SetAnimInstanceClass((UClass*)TmpMeshAnim->GetAnimBlueprintGeneratedClass());
    delete stream;
    return true;
    }
  • 真相


C++中加载一个蓝图类

  • _path = /Game/TopDownCPP/Blueprints/MyChar/MyCharBP
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    UObject* AMyText::TestLoadBPObject(FString _path)
    {
    auto cls = StaticLoadObject(UObject::StaticClass(), nullptr, *_path);
    if (!cls)
    {
    //GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("Failed to load UClass "));
    return nullptr;
    }
    else
    {
    //GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("UClass LOADED!!!!"));
    UBlueprint* bp = Cast<UBlueprint>(cls);
    if (!bp)
    {
    //GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("Failed to load UClass 2 "));
    }
    else
    {
    //GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("UClass LOADED!!!! 2 " + bp->GetName()));
    TSubclassOf<class UObject> MyItemBlueprint;
    MyItemBlueprint = (UClass*)bp->GeneratedClass;
    UWorld* const World = GWorld->GetWorld();
    if (World){
    FActorSpawnParameters SpawnParams;
    //SpawnParams.Instigator = this;

    UObject* DroppedItem = World->SpawnActor<UObject>(MyItemBlueprint, GetActorLocation(), { 0, 0, 0 }, SpawnParams);
    }
    }
    return cls;
    }
    }

BlueprintFunctionLibrary中指定默认参数

1
2
3
4
5
6
7
8
9
10
11
12
13
//  declare
UFUNCTION(BlueprintCallable, Category = "MyGlobalMgr", meta = (WorldContext = "WorldContextObject"))
static bool Test_WordObj(class UObject* WorldContextObject, int32 WaveNumber);

// define
bool UMyBPFuncLib::Test_WordObj(class UObject* WorldContextObject, int32 WaveNumber) {
UObject* const MyWorld = GEngine->GetWorldFromContextObject(WorldContextObject);
if (MyWorld) {
FString str = FString::Printf(TEXT("--- Test_WordObj, WaveNumber:%d"), WaveNumber);
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Red, str);
}
return true;
}

C++动态调用蓝图方法CallFunctionByNameWithArguments,根据str调用到蓝图中对应得方法(没有Ufunction好用)

(只能传递string 和 int 这种基础类型参数)

1
2
3
4
5
6
7
8
9
10
11
// declare
UFUNCTION(BlueprintCallable, Category = "MyChar")
void runBpFunc(FString _funcName, FString _arg1, int32 _arg2);

// define
#include "Misc/OutputDeviceNull.h"
void AMyActor::runBpFunc(FString _funcName, FString _arg1, int32 _arg2) {
FOutputDeviceNull ar;
FString command = FString::Printf(TEXT("%s \"%s\" %d"), *_funcName, *_arg1, _arg2);
CallFunctionByNameWithArguments(*command, ar, nullptr, true);
}
  • 第一个是蓝图的方法名,
  • 第二个fstring类型的参数,必须用“”双引号包起来,不然会报找不到string value property的错
  • 第三个参数是int32类型的参数
  • 全部用空格隔开

CallFunctionByNameWithArgumentsUobject 的方法,所以只要是继承自Uobject的类都可以使用


继承FTickableGameObject,实现每帧Tick

  • 需要重写几个纯虚函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    UCLASS()
    class UCoolDownMgr : public UObject, public FTickableGameObject, public USingleton<UCoolDownMgr>
    {
    GENERATED_BODY()
    public:
    // Sets default values for this character's properties
    UCoolDownMgr();
    virtual ~UCoolDownMgr();

    // Begin FTickableGameObject Interface.
    virtual void Tick(float DeltaTime) override;
    virtual bool IsTickable() const override;
    virtual TStatId GetStatId() const override;
    // End FTickableGameObject Interface.
    };

国际化:任何要显示到屏幕上的文字都要用FText,因为它带有国际化功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void UMyBpFuncLib::TestNSLocatext(int32 _dayCount, int32 _hp)
{
FFormatNamedArguments Args; //命名式参数
Args.Add("DayCount", _dayCount);
Args.Add("Hp", _hp);
FText txt1 = FText::Format(NSLOCTEXT("Solus", "Day", "--- Day1 {DayCount}"), Args);
FText txt2 = FText::Format(NSLOCTEXT("Solus", "HP", "--- HP1 {Hp}"), Args);

FFormatOrderedArguments Args2; //索引式参数
Args2.Add(_dayCount);
Args2.Add(_hp);
FText txt3 = FText::Format(NSLOCTEXT("Solus", "Day", "--- Day2 {0}"), Args2);
FText txt4 = FText::Format(NSLOCTEXT("Solus", "HP", "--- HP2 {1}"), Args2);


FString str = txt1.ToString()
+ TEXT("\n")
+ txt2.ToString()
+ TEXT("\n")
+ txt3.ToString()
+ TEXT("\n")
+ txt4.ToString();
GEngine->AddOnScreenDebugMessage(0, 5.0f, FColor::Green, str);
}

整形暴露给蓝图只能使用 int32

  • 否则报错:error code: OtherCompilationError (5)

std::string 和 TCHAR* 之间的互转

1
2
3
#define TCHAR_TO_ANSIANSI_TO_TCHAR(str) (TCHAR*)StringCast<TCHAR>(static_cast<const ANSICHAR*>(str)).Get()
#define TCHAR_TO_UTF8(str) (ANSICHAR*)FTCHARToUTF8((const TCHAR*)str).Get()
#define UTF8_TO_TCHAR(str) (TCHAR*)FUTF8ToTCHAR((const ANSICHAR*)str).Get()

Blueprint中用代码spawn的actor使用自定义的AIController

  • 虽然可以在编辑器中默认设置中设置指定的自定义AIController,但这样只对拖进场景的Actor生效,对Blueprint调用spawn actor生产的actor无效,需要用代码生产自定的AIController,再指定哪个actor运行

C++声明方法但不实现,蓝图负责实现(C++调用Blueprint的另一种实现),已经形参引用或const传递

/// This function is designed to be overridden by a blueprint. Do not provide a body for this function in C++; 不要给BlueprintImplementableEvent声明的方法提供c++方法实现,由蓝图实现

  1. C++定义,并声明 BlueprintImplementableEvent

    1
    2
    UFUNCTION(BlueprintImplementableEvent)
    void CountDownHasFinished();
  2. 蓝图实现:


C++ 与 蓝图 交互的 参数传递 及 多返回值

  • 如果形参是fstring,就必须是引用类型,一般不需要蓝图修改的都加const,需要修改的就加引用声明

    • 不需要蓝图修改
      1
      2
      UFUNCTION(BlueprintImplementableEvent, Category = "MyBehavior")
      void OnDeath(const FString& _str, int32 _num);
    • 需要蓝图修改
      1
      2
      UFUNCTION(BlueprintImplementableEvent, Category = "MyBehavior")
      void OnDeath(UPARAM(ref) FString& _str, int32 _num);
  • c++ 返回 多个值 给蓝图

    • 如果需要返回多个值,就不需要再c++中 return,使用 形参引用 就行。

      1
      2
      UFUNCTION(BlueprintCallable, Category = "UMyGameInstance")
      void MyMerge(AActor* PlayerPawn, TArray<USkeletalMesh*> SrcMeshList, AActor*& retActor, USkeletalMeshComponent*& retSkeletalComp);


C++ 声明方法且实现,蓝图重写

  • /// This function is designed to be overridden by a blueprint, but also has a native implementation.
    /// Provide a body named [FunctionName]_Implementation instead of [FunctionName]; the autogenerated
    /// code will include a thunk that calls the implementation method when necessary.

    BlueprintNativeEvent,

  1. C++声明、实现

    1
    2
    3
    4
    5
    6
    7
    // declare
    UFUNCTION(BlueprintNativeEvent)
    void CountDownHasFinished();
    virtual void CountDownHasFinished_Implementation();

    // define
    void AMyText::CountDownHasFinished_Implementation(){ ... }
  2. 蓝图实现重写


添加一个时间轴


蓝图接口的使用

  1. 新建一接口蓝图
  2. 添加接口
  3. 使用接口

自定一个继承自ActorCompoment的组件,暴露在编辑器中

1
2
UCLASS(ClassGroup="MyCustom", meta=(BlueprintSpawnableComponent))
class UMyComp : public UActorComponent { ... };

只是用来做展示的Mesh,需要取消掉碰撞,不然会和其他Actor产生碰撞

->


子弹每帧修改朝向,速度需要保持不变,方向需要归一化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void AMyBullet::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);

//每帧修正到目标的飞行方向
if (mTargetActor != nullptr)
{
FVector targetLoc = mTargetActor->GetActorLocation();
if ( mLastTargetLoc != targetLoc)
{
FVector bulletLoc = GetActorLocation();
FRotator rota = UKismetMathLibrary::FindLookAtRotation(bulletLoc, targetLoc);
SetActorRotation(rota);
MovementComp->Velocity = MovementComp->InitialSpeed * (targetLoc - bulletLoc).GetSafeNormal(); //子弹移动方向归一化,然后再乘以速度就成了速度矢量
mLastTargetLoc = targetLoc;
}
}
}

行为树中的几种节点(Service、Decorator、Task)的初始化方法

  • 重写父类这两个方法

    1
    2
    virtual void OnInstanceCreated(UBehaviorTreeComponent& OwnerComp) override;
    virtual void OnInstanceDestroyed(UBehaviorTreeComponent& OwnerComp) override;
  • 因为有些数据不能再类的构造方法中初始化,涉及到引擎必须用引擎提供的 初始化方法,如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    void UBTDecorateMyBase::OnInstanceCreated(UBehaviorTreeComponent& OwnerComp)
    {
    //初始化成员都放在这个方法
    if (!mOwnerChar)
    mOwnerChar = GetMyChar();

    if (!mBTComp)
    mBTComp = &OwnerComp;
    }

行为树中的几种节点(Service、Decorator、Task)的每帧tick

  • 需要打开父类UBTAuxiliaryNode的开关,构造里默认设置为false

    1
    2
    /** if set, OnTick will be used */
    uint8 bNotifyTick : 1;
  • 需要在子类中的构造中把这个开关打开

    1
    2
    3
    4
    5
    6
    UBTDecorateMyBase::UBTDecorateMyBase()
    : Super()
    {
    //设置tick 开关
    bNotifyTick = true;
    }

模型导入

  1. 先导入骨骼,导入时可以选择是否导入 材质、贴图
  2. 在导入所有动画,导入时选择1中导入的骨骼

控制台执行 关卡事件

  1. 关卡中新建个自定义时间 moveCube

  2. 控制执行这个事件,输入命令:ce moveCube


设置 Windows 打包的分辨率,及相关设置