Unity3D to Unreal Engine 4

文章先作为笔记记录,待整理
文章主要参考:官方文档http://api.unrealengine.com/CHN/GettingStarted/FromUnity/index.html

推荐几个不错的UE入门教程:
《Inside UE4》
《Unreal新手笔记(二)——编程架构》

类图

视频教程:
《Unreal Engine 4 喵喵教程 (中文)》
《虚幻4游戏引擎官方入门自学到提高全218集(中文字幕)上部》
《虚幻4入门到精通教程【全集】》


什么是蓝图?

蓝图通常包含2部分:

  • 蓝图资源:类似于Unity的Prefab
  • 蓝图脚本:可视化的游戏脚本系统
    一般我们所指的蓝图是狭义的蓝图,即可视化脚本系统。

       对于开发者来说,我们可以把它理解为一种可视化的高级语言(C#等),它有基本的变量、函数、类型转换,支持继承、多态等。通过使用连线把节点、事件、函数及变量连接到一起,这样就可以创建复杂的游戏性元素。蓝图通过各种用途的节点构成图来进行工作,这些节点包括针对蓝图每个实例的对象构建、独立的函数、一般的游戏性事件,从而实现各种行为及其它功能。

中文 英文
蓝图其实是可视化的类
Blueprint is visual script

蓝图核心类:

  • UObject:
  • Actor:物件;类似于Unity的GameObject
  • Pawn:棋子(由玩家或AI控制的游戏对象),士兵(由于UE是从FPS游戏进化来的缘故)
  • Character:角色
  • Controller:控制器,有些类似于Unity的MonoBehavior
  • Component:组件


    组件

蓝图:
蓝图,BluePrint,简称BP,是游戏的可视化脚本系统,Visual Scripting.
使用蓝图所定义的对象通常被直接称为“蓝图”。

问题:

  1. 蓝图类似于Unity的Prefab.
  2. 蓝图的逻辑节点其实跟Unity行为树的节点比较类似?
  3. 如何定义蓝图的一个节点?
  4. 蓝图的变量与行为树的黑板有什么区别吗?
  5. 如何将Prefab批量改为蓝图?

在虚幻 4 中编写游戏逻辑代码
好了,现在开始稍微深入一些。我们将谈论一下创建游戏所需要的编程话题。因为您了解 Unity,我们来面向 C# 的用户解释 C++ 的功能,当然您也可以使用蓝图来完成几乎所有的事情!我们尽可能的为范例提供 C++ 的同时也提供蓝图。

先说一下一些通用的游戏逻辑编程模式,以及如何在虚幻中实现。许多在 Unity 中的函数在虚幻中都有类似的函数。我们先从最常见的开始。

Instantiating GameObject / Spawning Actor
在 Unity 中,我们使用 Instantiate 函数来新建物体的实例。

该函数使用任意的 UnityEngine.Object 类型(GameObject,MonoBehaviour 等),并创建它的拷贝。

public GameObject EnemyPrefab;
public Vector3 SpawnPosition;
public Quaternion SpawnRotation;

void Start()
{
    GameObject NewGO = (GameObject)Instantiate(EnemyPrefab, SpawnPosition, SpawnRotation);
    NewGO.name = "MyNewGameObject";
}

在虚幻 4 中,根据不同的需要,有一些不同的函数用于创建物体。NewObject 用于创建新的 UObject 类型实例,而 SpawnActor 用于创建新的 AActor 类型实例。

首先我们总体说一下 UObject 和 NewObject。在虚幻中 UObject 的子类很像 Unity 中 ScriptableObject 的子类。对于游戏过程中,它们是那些不需要在游戏世界中创建并看见的存在。

在 Unity 中,如果要创建自己的 ScriptableObject 子类,可能会像下面这样的初始化:

MyScriptableObject NewSO = ScriptableObject.CreateInstance<MyScriptableObject>();

在虚幻中,如果要创建 UObject 的继承类,是像下面这样的初始化:

UMyObject* NewObj = NewObject<UMyObject>();

那么 Actor 呢?Actor 的在世界(C++ 中的 UWorld)中生成是通过 SpawnActor 方法。如何获取 World 对象?有些 UObject 会提供一个 GetWorld 的方法,所有的 Actor 则都具有这个方法。

您可能已经注意到,并没有传递一个 Actor,我们传递了一个 Actor 的 “class” 来作为生成 Actor 的参数。在我们的范例中,是一个 AMyEnemy 类的任意子类。

但如果想要创建某个东西的“拷贝”,就像 Unity 的 Instantiate 函数那样,该怎么做呢?

NewObject 和 SpawnActor 函数也能通过给一个 “模板” 对象来工作。虚幻引擎会创建该一个对象的拷贝,而不是从零创建一个新的对象。这将会拷贝该对象的所有属性(UPROPERTY)和组件。

AMyActor* CreateCloneOfMyActor(AMyActor* ExistingActor, FVector SpawnLocation, FRotator SpawnRotation)
{
    UWorld* World = ExistingActor->GetWorld();
    FActorSpawnParameters SpawnParams;
    SpawnParams.Template = ExistingActor;
    World->SpawnActor<AMyActor>(ExistingActor->GetClass(), SpawnLocation, SpawnRotation, SpawnParams);
}

您也许想知道“从零开始创建”在这里具体是什么意思。每个对象类在创建时都有一个默认模板,包含了它的默认属性和组件。在创建时如果您并不像修改这些默认属性,并没有提供你自己的模板,虚幻将使用这些默认值来创建该对象。为了更好的说明这个,我们先来看一下 MonoBehaviour 的例子:

public class MyComponent : MonoBehaviour
{
    public int MyIntProp = 42;
    public SphereCollider MyCollisionComp = null;

    void Start()
    {
        // Create the collision component if we don't already have one
        if (MyCollisionComp == null)
        {
            MyCollisionComp = gameObject.AddComponent<SphereCollider>();
            MyCollisionComp.center = Vector3.zero;
            MyCollisionComp.radius = 20.0f;
        }
    }
}

在上面这个例子中,有一个 int 属性,默认是 42,并有一个 SphereCollider 组件默认半径是 20。

在虚幻 4 中,利用对象的构造函数也能达到同样的效果。

UCLASS()
class AMyActor : public AActor
{
    GENERATED_BODY()

    UPROPERTY()
    int32 MyIntProp;

    UPROPERTY()
    USphereComponent* MyCollisionComp;

    AMyActor()
    {
        MyIntProp = 42;

        MyCollisionComp = CreateDefaultSubobject<USphereComponent>(FName(TEXT("CollisionComponent"));
        MyCollisionComp->RelativeLocation = FVector::ZeroVector;
        MyCollisionComp->SphereRadius = 20.0f;
    }
};

在 AMyActor 的构造函数中,我们为这个类设置了属性的默认值。请注意 CreateDefaultSubobject 函数。我们可以用它来创建组件并赋予组件默认值。所有子对象都将使用这个函数作为默认模板来创建,也可以在子类或者蓝图中对它进行修改。


通过 GameObject / Actor 访问组件
Unity

MyComponent MyComp = gameObject.GetComponent<MyComponent>();

C++

UMyComponent* MyComp = MyActor->FindComponentByClass<UMyComponent>();

类型转换
在这个例子中,获取了一个已知的组件,并将它转换为一个特定类型并有条件的做一些事情。

Unity C#:

Collider collider = gameObject.GetComponent<Collider>;
SphereCollider sphereCollider = collider as SphereCollider;
if (sphereCollider != null)
{
        // ...
}

虚幻 4 C++:

UPrimitiveComponent* Primitive = MyActor->GetComponentByClass(UPrimitiveComponent::StaticClass());
USphereComponent* SphereCollider = Cast<USphereComponent>(Primitive);
if (SphereCollider != nullptr)
{
        // ...
}

RayCast vs RayTrace
Unity C#:

GameObject FindGOCameraIsLookingAt()
{
    Vector3 Start = Camera.main.transform.position;
    Vector3 Direction = Camera.main.transform.forward;
    float Distance = 100.0f;
    int LayerBitMask = 1 << LayerMask.NameToLayer("Pawn");

    RaycastHit Hit;
    bool bHit = Physics.Raycast(Start, Direction, out Hit, Distance, LayerBitMask);

    if (bHit)
    {
        return Hit.collider.gameObject;
    }

    return null;
}

虚幻 4 C++:

APawn* AMyPlayerController::FindPawnCameraIsLookingAt()
{
    // You can use this to customize various properties about the trace
    FCollisionQueryParams Params;
    // Ignore the player's pawn
    Params.AddIgnoredActor(GetPawn());

    // The hit result gets populated by the line trace
    FHitResult Hit;

    // Raycast out from the camera, only collide with pawns (they are on the ECC_Pawn collision channel)
    FVector Start = PlayerCameraManager->GetCameraLocation();
    FVector End = Start + (PlayerCameraManager->GetCameraRotation().Vector() * 1000.0f);
    bool bHit = GetWorld()->LineTraceSingle(Hit, Start, End, ECC_Pawn, Params);

    if (bHit)
    {
        // Hit.Actor contains a weak pointer to the Actor that the trace hit
        return Cast<APawn>(Hit.Actor.Get());
    }

    return nullptr;
}

  1. Prefab vs 蓝图
    Unity 的Prefab,在虚幻中的蓝图有着类似的功能。
    但蓝图具有可扩展性,或者称为可继承性。(因为蓝图的本质其实是一个可视化的C++类?)
    --
    比如,在虚幻 4 中,可以创建一个蓝图类叫做 Monster,实现基本的怪物功能,比如追击人类。然后可以创建一个叫做 Dragon 的蓝图类来扩展它(某种特定的怪物,添加了火焰吐息的功能),再有一个 Grue(一种当它变黑是就有可能吃人的怪物),以及其他 8 种类型。这样一些 Monster 的子类都继承了基础的 Monster 类的功能,并在此基础上添加新的能力。
    在 Unity 中,则需要创建很多不同的 GameObject 的 prefabs:为 Dragon 创建一个,为 Grue 创建一个,等等。假设这时希望为所有的怪物添加某个功能,比如使用一个 Speak 组件来说话,在 Unity 中则需要更新所有的 10 个 prefabs,拷贝粘贴到每个中。
    在虚幻 4 中,只需简单的修改 Monster 的蓝图类,并为它添加新的 Speak 的能力,便做完了!Dragon,Grue 以及其他 8 种 Monster 的子类都会自动的继承这个说话的新功能,并不需要去修改这些子类。
  • 旋转示意图
  1. 如何为蓝图绑定脚本/添加脚本/AddComponent?
  • 新建C++类:Monster,父类选择Character
  • 新建蓝图类:BPMonster,分类选择Actor
  • 双击蓝图类BPMonster,打开蓝图编辑器
  • BPMonster(自身)相当于Unity的transform:选择BPMonster(自身),然后视口面板选择类设置,细节窗口选择父类为Monster完成脚本绑定。


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