# UnityToUE4

此文为Unreal Engine 4 For Unity Developers (opens new window)的原创翻译,本文内容版权归原文所有,仅供学习,如需转载望注本文地址,翻译不易,谢谢理解。

本文主要讲如何从Unity3d快速迁移到UE4上。这篇文章以Unity3d用户的视角来快速浏览UE4,旨在帮你将你的Unity3d经验迁移到UE4上面。

# 编辑器

下面是Unity编辑器和Unreal编辑器的图片,同样颜色的区域意味着同样的功能,每块被UE4中和Unity3d相等概念的术语标记。Unreal Editor的布局是完全可通过拖拽来定制的。

EditorCompare

# 编辑资产

在Unity中,这个Inspector面板用来编辑在Project中选择的资产,在UE4中,Details面板暴露了选中objects的属性,更大的编辑任务需要单独的window或tab,这将会为每个要编辑的资产打开一个标签窗口,像网络浏览器那样。当然,你可以拖拽或像单独的窗口浮起来。

TabbedWindows

# 术语表

下面部分左边包含Unity的常见术语,右边是与之等价的UE4术语。

分类 Unity UE4
Gameplay Types Component Component
GameObject Actor,Pawn
Prefab Blueprint Class
Editor UI Hierarchy Panel World Outliner
Inspector Details Panel
Project Browser Content Browser
Scene View Viewport
Meshes Mesh Static Mesh
Skinned Mesh Skeletal Mesh
Materials Shader Material, Material Editor
Material Material Instance
Effects Particle Effect Effect, Particle, Cascade
Shuriken Cascade
Game UI UI UMG (Unreal Motion Graphics)
Animation Animation Skeletal Animation System
Mecanim Persona , Animation Blueprint
2D Sprite Editor Paper2D
Programming C# C++
Script Blueprint
Physics Raycast Line Trace, Shape Trace
Rigid Body Collision, Physics
Runtime Platforms iOS Player, Web Player Platforms

# 项目和文件

# 这些文件和目录都是啥

就像Unity项目一样,Unreal项目有自己的目录,在里面也有自己的项目文件,你可以双击.uproject文件将你的游戏加载进Unreal编辑器里,或者右键来进行其他操作。项目目录有各种子目录来包含你的游戏内容和源码,还有各种配置文件和可执行文件,最重要的是Content和Source子文件夹。

# 应该在哪存资产

在UE4里,每个项目都有一个Content文件夹,和Unity项目的Assets目录一样,这是你存放游戏资产的地方。为了将资产导入到你的游戏中,将文件放进项目Content文件夹里面,然后它们会自动被导入然后出现在Content Browser里,在编辑器中的资产当你使用外部程序更改它们时会自动更新。

ProjectOnDisk

# 都支持什么文件格式

Unity支持很广的文件格式,UE4支持常见的文件类型:

资产类型 支持格式
3D .fbx, .obj
Texture .png, .jpeg, .bmp ,.tga, .dds, .exr, .psd, .hdr
Sound .wav
Fonts .ttf, .otf
Videos .mov, .mp4, .wmv

# 场景应该存放在哪

在Unity中,你把GameObjects放置在场景中,然后将其保存为场景资产文件,UE4有和Unity场景相似的Map文件,Map文件存放关于Level的数据和对象,比如光照数据和某些和关卡有关的设置。

# 如何改变项目的设置

所有的项目设置都可以在Edit/Project Setting里面找到,不像Unity的项目设置,它允许你去指定关于你项目的一些信息,比如项目名字和图标,配置游戏输入绑定,在你的项目运行时定义engine的行为。在这里 (opens new window)你可以学习更多关于每个项目的设置。Unity有称为"player settings"的面板,在Unreal中他们是"platform settings",你可以在项目设置中的"Platforms"目录中找到。

# 源文件存在哪

在Unity中,你可能习惯将你的C#代码放在你的资产目录下,UE4不一样,对于拥有C++代码的项目,你会找到一个Source子目录,在这个目录下有各种的文件,包含C++的source(.cpp)和header(.h)文件,还有一些构建文件(.Build.cs,.Target.cs),但是只有蓝图的项目没有Source文件夹。

在UE4中开始使用C++的最简单方式就是使用File/Add Code to Project,或者只是从多个模板中创建一个C++项目,你可以在Content Browser里找到C++类然后通过双击用Visual Studio打开它。

# 从GameObjects到Actors

# GameObject去哪了

在Unity中,一个GameObject是一个可以被放置在世界中的"东西",在UE4中与之等价的概念是Actor,在Unreal编辑器中,你可以将一个空的Actor对象从放置面板拖拽到场景里面,你可以使用空的Actors来创建游戏,但UE4也包含了拥有某些内置特性的特殊Actors,比如Pawn(对于players或AI对象),或者Character(对于有动画的生物),就跟空的Actors一样,你可将这些特殊类型的Actors拖拽到场景中,然后添加并定制它们的属性和组件。你将稍后学习更多关于它的东西,但是现在谨记UE4有Gameplay Framework来让这些特殊Actors工作。

UE4中的Actors同Unity中的GameObjects有点不一样,在Unity中GameObject是你不能直接扩展的C#类,在UE4中,Actor是你可以通过继承扩展并定制的C++类。我们稍后再谈这点。

# Components去哪了

在Unity中,你可以给一个GameObject添加组件来增加功能。

在UE4中,你可以给Actors添加组件,在你将一个空Actor拖拽到关卡后,在Details面板点击Add Component按钮然后选择一个组件添加。在这里,我们通过拖拽空的Actor来创建一个火炬,然后添加mesh组件作为它的手柄,添加light组件和一个Particle组件来创建它的火焰。

在Unity中,一个GameObject包含一系列平级的组件,但是在UE4中一个Actor实际也包含组件依附到其他组件上的层级关系,你可在上面的例子看到,Light组件和Particle组件都依附到Mesh组件上,这稍后在Complex Actors and GameObjects中重点讨论。

# 从UnityPrefabs到UE4蓝图类

Unity的工作流是基于prefabs的。在Unity中你可以创建有一堆组件的GameObjects的集合,然后创建它的prefabs,接着你就可以将prefab的实例放到世界场景中,或者是在运行时实例化。

UE4对应的工作流是基于蓝图类的。在UE4场景中你可以创建拥有组件的Actor,选择它,然后在Details面板单击Edit Blueprint/Create Child Blueprint Class按钮创建蓝图,然后你的新蓝图类就可以在Content Browser中找到,然后双击直接编辑或者你将它们拖拽到关卡中。

# ScriptComponent和MonoBehaviour在哪

在Unity中你有脚本组件然后将它们拖拽到GameObjects上来添加C#脚本,你创建一个继承MonoBehavior的类来定义这个组件做什么。UE4中有类似的东西,你可以创建自己的全新组件类然后拖拽到任何Actor上,这些组件类可以通过蓝图或C++来创建。

那么如何在UE4中来创建自己的组件类?点击Details面板中的Add Component下拉菜单,如下图:

componentClass

在Unity中,在创建一个MonoBehavior时你将得到一个有Start()和Update()函数的模板类。

在UE4中,你将得到一个有InitializeComponent()和InitializeComponent()函数的模板类,它们类似上面的Start和Update。

如果你创建Blueprint Script Component你将得到像下面的两个可视结点。

BlueprintScriptComponent

# 脚本化的ActorBlueprint类

这里有一个UE4很酷的特性:你的新Actor蓝图类有它自己的蓝图可视化编程!这允许你给整个的对象添加逻辑而不只是一个单独的组件。同时结合继承,这会在你设计游戏时给你很大的灵活性。

除了蓝图类支持可视化编程,UE4也支持C++的实现,下面看下它们的不同:

# Unity

    using UnityEngine;
    using System.Collections;

    public class MyComponent : MonoBehaviour
    {
        int Count;

        // Use this for initialization.
        void Start ()
        {
            Count = 0;
        }

        // Update is called once per frame.
        void Update ()
        {

            Count = Count + 1;
            Debug.Log(Count);
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Unreal

    #pragma once
    #include "GameFramework/Actor.h"
    #include "MyActor.generated.h"

    UCLASS()
    class AMyActor : public AActor
    {
        GENERATED_BODY()
        int Count;

        // Sets default values for this actor's properties.
        AMyActor() 
        {
            // Allows Tick() to be called
            PrimaryActorTick.bCanEverTick = true;  
        }

        // Called when the game starts or when spawned.
        void BeginPlay()
        {
            Super::BeginPlay();
            Count = 0;
        }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# Blueprint

BPVisualScript

# UE4蓝图类可以被扩展

Unity预制件和UE4蓝图类在你的游戏中以相似的方式被实例化。但是Unity在一个预制件中嵌套其他预制件时会有复杂的关系,这会限制它们制作可扩展的构建块。

在UE4中,你可以创建一个蓝图类来扩展一个存在的蓝图类,并增加新的属性,组件和可视化编程的功能。

比如,在UE4中你可以创建一个名为Monster的蓝图类,它实现怪兽的基本功能,比如追逐人,你可以再进一步创建新的蓝图类来扩展它,比如Dragon,一种可以喷火的怪兽,Grue,在夜里想要吃掉你的怪兽,或者其他怪兽等等,这些怪兽的子类都继承了Monster的基本功能,然后又在此基础上添加了新的能力。

在Unity中,你可以通过创建很多不同的GameObject预制件来实现这点:一个给Dragon,一个给Grue等等。现在,假如你想给所有的monster增加新的功能,比如使用你的新Speak组件来说话的能力,在Unity中,你不得不给这些预制件单独地复制粘贴上去。

在UE4中,你只修改Monster Blueprint类来增加说话的能力就可以了!Dragon,Grue和其他怪兽都会自动地继承新的说话能力,而你不用修改这些子类。

还有,我们说的关于蓝图的每件事都适用于C++类,它们对于Actors和组件来说都是相同的!这些系统被设计为支持大规模地功能扩展开发,可以适用于10人或100人的项目!

# 我应该使用蓝图还是C++,还是都用

蓝图可视化脚本对于简单的游戏逻辑和按顺序执行的活动来说是理想的,对于设计者,艺术家和面向视觉编程的程序员来说是一个很棒的系统,因为它容易使用和可视化地控制游戏对象,你甚至可以只使用蓝图创建独立游戏,请查看Tappy Chicken示例。

C++编程是为了应对大规模人物,比如创建gameplay系统,复杂的AI和新的引擎特性,如果你已经有一些C++经验,请查看Introduction to C++ Programming in UE4 (opens new window)

大多数项目使用了蓝图和C++的混合,因为它易于使用并且充满了乐趣,很多开发者使用蓝图来创建游戏的原型,到后面为了性能和工程的要求可将它里面的逻辑完全移入C++。

# 蓝图类可扩展C++类

大部分UE4的游戏开发魔力源于程序员使用C++实现新特性,然后设计师和艺术家在蓝图中利用这些新特性,下面是一个团队,如何在一个基于UE4的射击游戏中实现捡东西,其中C++负责系统实现,而蓝图类负责行为和外表部分。

CSharpBP

# Transform组件

在Unity中每个GameObject有一个Transform组件,它定义了GameObject在世界场景中的位置,旋转和缩放。

相似地,在UE4中的Actors都有一个RootComponent,它可以是任何Scene Component的子类。一个Scene Component定义了一个Actor在世界中的位置,旋转和缩放,这也适用于它层级下的所有组件,很多你使用的组件都是SceneComponent的子类,因为拥有位置确实很有用!

即使你放置一个空的Actor,UE4也会给这个Actor创建一个"Default Scene Root",它只是一个Scene Component。如果你拖拽一个自己的Scene Component,它将替代这个默认的Scene Component。

# 合成对象

在Unity中,你通过构建GameObjects的层级来创建合成对象,将他们的transform以父子关系连接起来:

UnityCompoundObjects

在UE4中,你可以通过嵌套组件来创建合成对象:

UE4CompoundObjects

正如你从表中看到的,嵌套层级可以通过将Scene Components依附到其他Scene Components上创建,因为他们有transform,类似于Unity中的Transform。Actor组件(所有组件的基类)只能被依附到Actor自身。

# 能从组件中创造出所有吗

这取决于你,但通常你将结合定制的组件和使用这些组件的Actor类。我们上面提到了,但是UE4有很多特殊类型的Actors来保证一个关卡的功能实现,它们包含相应常用的一些组件。比如,一个Character常常包含一个Character Movement Component。

下面是一些你在引擎中快速遇到的一些Actor子类,它们常常在各种游戏类型中都很有用,下面列出了一些最常见的内置Actors类型:

  • Pawn - 一种代表能控制的游戏对象的Actor,具有代表性的是player的化身,Pawn常常被Players和AI通过自己的控制器移动。
  • Character - 一种更具体化版本的Pawn,它专门为有两条腿的化身设计,用来处理这类游戏对象的复杂逻辑。
  • Controller - 拥有和控制一个Pawn,通过将Pawn和Controller分离,你可以编写AI控制器来操作一个pawn,它使用的接口同player使用的接口一样。
  • Player Controller - 一个更具体化的控制器,它被设计成从玩家的手柄控制器,触摸板,鼠标键盘获取输入,然后用输入来驱动它们控制的Pawn或Character。

# 所有东西都是Actor吗

不是所有的东西都是,Actors在UE4的gameplay框架中是最常用的类,也是唯一一种在世界场景中可以生成的类型,所以你放在关卡中的每个东西都是一个Actor。

除此之外,最重要的是Object,它是所有Unreal类的基类,包含Actor和其他东西。它是比Actor的更基础底层的构建,但是拥有你期望从Unreal类得到的特性,比如反射和序列化。Object是一个非常基础的类,只有当一个新类型不符合Actor的模型时我们用它来定义该类型,比如Actor Component是所有组件的基类,它派生于Object而不是Actor。

# 在UE4中的GameplayFramework是什么

好了,这是一个有点疯狂的事,以一种敬畏的眼光来看,Unity给了你一个干净的石板来开始设计你的游戏,Unreal也做了同样的事,在Unity中你构建所有东西都是基于GameObjects和组件的,在Unreal中你构建的所有东西都基于Actors和Components。

但是,Unreal在上层有额外的一层被称为Gameplay Framework。在Unity中并没有相关概念,你不是必须得使用它,但是它相当的酷!基本上,如果你使用一些基本类然后遵循某些已有的约定,你的游戏将会自动取得极好的特性,这些特性可能会比较难实现和改进,比如全面的多人支持!

无数的好游戏已经基于Unreal顶层的Gameplay Framework构建,所以花一些时间来了解它是值得的。当然,你可以做一版你自己的Gameplay,如果你想这样做是极好的,但是很多明智的Unreal开发者充分地利用了UE4的框架,所以花一些时间学习吧!

为了使用Gameplay框架,你真的只需要去学习Unreal中的定制的内置Actor类,就像Pawn,Character和Player Controller,然后最终学习Unreal网络复制和通信的特性,现在,让我们回来看一些基础。

# 如何在UE4中编写代码

# 我曾习惯于在MonoDevelop中编程

对于蓝图编程,你只需要Unreal编辑器,所有的东西已经内置好了!为了用C++写代码,你需要在windows上下载免费版本的Visual Studio,在Mac上安装Xcode。当你第一次创建一个新的工程,或者向已有的工程添加代码,UE4自动的为你创建Visual Studio项目文件。你可以在Content Browser中双击一个C++类,或者在主菜单中单击Open Visual Studio来打开Visual Studio。

OpenVisualStudio

在UE4中有个重要的不同:你有时候不得不手动刷新Visual Studio项目文件,比如下载了一个新版本UE4时,或手动地改变磁盘上的源文件时,你可以通过单击主菜单中的Refresh Visual Studio Project,或在你的项目目录中右键.uproject文件然后选择Generate Visual Studio project files。

GenerateVisualStudioProjectFile

# 写事件函数(Start,Update等)

如果你曾习惯于MonoBehaviors,你肯定习惯于Start,Update和OnDestroy这样的方法。下面是一个Unity行为和它在UE4的Actors和组件中等价部分的比较。

在Unity中,我们可能会使用像下面一样的简单组件:


public class MyComponent : MonoBehaviour
{
    void Start() {}
    void OnDestroy() {}
    void Update() {}
}

1
2
3
4
5
6
7
8

但是记着,在UE4中你可以在Actor自身中写代码而不是编写新的组件,这是很通用和有用的。

类似于Unity的Start,OnDestroy和Update函数,我们在UE4中对Actors会有相似的方法集合:


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

    // Called at start of game.
    void BeginPlay();

    // Called when destroyed.
    void EndPlay(const EEndPlayReason::Type EndPlayReason);

    // Called every frame to update this actor.
    void Tick(float DeltaSeconds);
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

下面是蓝图的结点:

blueprintNode

在UE4中的组件包含不同的函数,下面是一个简单的例子:

C++的:

UCLASS()
class UMyComponent : public UActorComponent
{
    GENERATED_BODY()

    // Called after the owning Actor was created
    void InitializeComponent();

    // Called when the component or the owning Actor is being destroyed
    void UninitializeComponent();

    // Component version of Tick
    void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14

蓝图的:

blueprintComponentNode

请记住,在UE4中去调用父类版本的方法是很重要的。

举例来说,在Unity中C#会调用基类.Update(),但是在UE4的C++中我们这样使用Super::TickComponent():

void UMyComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
    // Custom tick stuff here
    Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}
1
2
3
4
5

你可能会注意C++中的一些东西是以"A"开头,其他则是以"U"开头,前缀"A"暗示着一个Actor的子类,而前缀"U"暗示着Object子类,还有一些其他前缀,比如"F"常用来表示普通数据结构或非UObject类。

# 在UE中写gameplay代码

现在,我们需要从这深入了解下,我们一直在讨论关于编程的主题,这对于创建游戏来说是很重要的。因为你已经了解Unity,我们在偏向C#用户解释如何学习Unreal C++,但是你可以使用蓝图编程来做所有你想做的事!我们添加的例子尽可能的都使用C++和蓝图。

让我们讨论一些最常见的gameplay编程模式,看下它们是如何在UE4中实现的。很多Unity中的函数在UE4中都有相似的函数,我们依次来看下这些最常见的函数。

# 实例化GameObject/生成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";
}

1
2
3
4
5
6
7
8
9
10
11

在UE4中,有很多不同的函数去实例化对象,这取决于你的需要,NewObject用来创建新的UObject类型,SpawnActor用来生成AActor类型。

首先我们简要的讨论下UObjects和NewObject。UObject的子类更像Unity中ScriptableObject中的子类。它们对于gameplay框架中的类很有用,这些类不用在世界场景中生成或拥有Actors所有的组件。

在Unity中,如果你创建你自己的ScriptableObject子类,你可能像下面这样实例化它:

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

在UE4中,如果你创建你自己的UObject派生类型,你需要像下面实例化它:

UMyObject* NewObj = NewObject<UMyObject>();
1

那关于Actors会怎样呢?使用SpawnActor方法在一个World对象(在C++里是UWorld)上生成Actors。你如何获得World对象呢?一些UObject提供了一个GetWorld方法给你,比如所有的Actors都有这个方法。

你注意到我们不是传入一个Actor对象,而是传入我们想生成的Actor的"class",在我们的例子中,这个class可能是任何AMyActor的子类。

但是如果你想复制一个对象,就像Instantiate那样,应该怎么办呢?

NewObject和SpawnActor函数可以被给一个模板object来工作,UE4将会复制这个object,而不是从零创建一个。这将复制所有它的UPROPERTYs属性和组件。

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);
}
1
2
3
4
5
6
7

你可能想在这个地方从零创建意味着什么,你创建的每个对象类有一个默认的模板包含默认属性值和组件,如果你不想重写这些属性而且也不提供你自己的模板,UE4会使用这些默认值来构建你的对象,为了帮助说明这点,让我们首先看下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;
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

在上面的例子中,我们有一个int属性,它的默认值是42,有一个SphereCollider,它的半径默认设成了20。

我们可以在UE4中使用对象的构造函数来做到同样的事情:

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;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

在AMyActor的构造器中,我们给这个class设置了默认属性值。注意CreateDefaultSubobject函数的使用,我们可以使用它来创建组件然后给这些组件分配默认的属性值。所有我们创建的子类对象都会使用这个构造函数作为默认模板,我们在后面的子类或蓝图中可以改变这个构造函数。

# 转换类型

在这种情况里,我们试图得到一个我们都知道都存在的一个组件,然后将它转换成某种类型,然后再做一些事。

在Unity中:

Collider collider = gameObject.GetComponent<Collider>;
SphereCollider sphereCollider = collider as SphereCollider;
if (sphereCollider != null)
{
        // ...
}
1
2
3
4
5
6

在UE4中:

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

# 销毁GameObject/Actor

在Unity中:

    Destroy(MyGameObject);
1

在UE4中:

    MyActor->Destroy();
1

在UE4的蓝图中:

destroy

# 在1s内销毁GameObject/Actor

在Unity中:

    estroy(MyGameObject, 1);
1

在UE4中:

    MyActor->SetLifeSpan(1);
1

在UE4的蓝图中:

DestroyIn1s

# 禁用GameObjects/Actors

在Unity中:

    MyGameObject.SetActive(false);
1

在UE4中:

    // Hides visible components
    MyActor->SetActorHiddenInGame(true);

    // Disables collision components
    MyActor->SetActorEnableCollision(false);

    // Stops the Actor from ticking
    MyActor->SetActorTickEnabled(false);

1
2
3
4
5
6
7
8
9

在UE4的蓝图中:

DisableActor

# 从组件上获取GameObject/Actor

在Unity中:

    GameObject ParentGO = MyComponent.gameObject;
1

在UE4中:

     AActor* ParentActor = MyComponent->GetOwner();
1

在UE4的蓝图中:

GetOwner

# 从GameObject/Actor上获取一个组件

在Unity中:

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

在UE4中:

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

在UE4的蓝图中:

GetComponentByClass

# 寻找GameObjects/Actors

在Unity中:

// Find GameObject by name
GameObject MyGO = GameObject.Find("MyNamedGameObject");

// Find Objects by type
MyComponent[] Components = Object.FindObjectsOfType(typeof(MyComponent)) as MyComponent[];
foreach (MyComponent Component in Components)
{
        // ...
}

// Find GameObjects by tag
GameObject[] GameObjects = GameObject.FindGameObjectsWithTag("MyTag");
foreach (GameObject GO in GameObjects)
{
        // ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

在UE4中:

// Find Actor by name (also works on UObjects)
AActor* MyActor = FindObject<AActor>(nullptr, TEXT("MyNamedActor"));

// Find Actors by type (needs a UWorld object)
for (TActorIterator<AMyActor> It(GetWorld()); It; ++It)
{
        AMyActor* MyActor = *It;
        // ...
}

// Find UObjects by type
for (TObjectIterator<UMyObject> It; It; ++it)
{
    UMyObject* MyObject = *It;
    // ...
}

// Find Actors by tag (also works on ActorComponents, use TObjectIterator instead)
for (TActorIterator<AActor> It(GetWorld()); It; ++It)
{
    AActor* Actor = *It;
    if (Actor->ActorHasTag(FName(TEXT("Mytag"))))
    {
        // ...
    }
}
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

在UE4的蓝图中:

FindByClass

FindByTag

# 给GameObjects/Actors添加tags

在Unity中:

    MyGameObject.tag = "MyTag";
1

在UE4中:

// Actors can have multiple tags
MyActor.Tags.AddUnique(TEXT("MyTag"));
1
2

在UE4的蓝图中:

AddTag

# 给MonoBehaviors/ActorComponents添加tags

在Unity中:

    // This changes the tag on the GameObject it is attached to
    MyComponent.tag = "MyTag";
1
2

在UE4中:

    // Components have their own array of tags
    MyComponent.ComponentTags.AddUnique(TEXT("MyTag"));
1
2

# 比较GameObjects/Actors和MonoBehaviors/ActorComponents的tags

在Unity中:

if (MyGameObject.CompareTag("MyTag"))
{
    // ...
}

// Checks the tag on the GameObject it is attached to
if (MyComponent.CompareTag("MyTag"))
{
    // ...
}
1
2
3
4
5
6
7
8
9
10

在UE4中:

// Checks if an Actor has this tag
if (MyActor->ActorHasTag(FName(TEXT("MyTag"))))
{
    // ...
}

// Checks if an ActorComponent has this tag
if (MyComponent->ComponentHasTag(FName(TEXT("MyTag"))))
{
    // ...
}
1
2
3
4
5
6
7
8
9
10
11

在UE4的蓝图中:

ActorHasTag

ComponentsHasTag

# 刚体和Primitive组件

在Unity中要给任何物体一个物理特性,你首先需要给它一个RigidBody组件。在UE4中,任何Primitive组件(C++上是UPrimitiveComponent)都可以是一个物理对象。一些常见的Primitive组件是ShapeComponents(Capsule, Sphere, Box),StaticMeshComponent,and SkeletalMeshComponent。

不像Unity将碰撞和可视化的负责部分分开放到单独的组件里。UE4结合了潜在物理效果和可视化效果的概念到PrimitiveComponent中,在世界场景中有任何几何形状的任何组件,都可以被渲染或者在物理上同PrimitiveComponent的子类交互。

# Layers和Channels

在Unity中它们被称为"Layers",在UE4中使用Collision Channels,它们以相似的方式工作。

# RayCast和RayTrace

在Unity中:

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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

在UE4中:

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

在UE4的蓝图中:

RayTrace

# Triggers

在Unity中:

public class MyComponent : MonoBehaviour
{
    void Start()
    {
        collider.isTrigger = true;
    }
    void OnTriggerEnter(Collider Other)
    {
        // ...
    }
    void OnTriggerExit(Collider Other)
    {
        // ...
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

在UE4中:

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

    // My trigger component
    UPROPERTY()
    UPrimitiveComponent* Trigger;

    AMyActor()
    {
        Trigger = CreateDefaultSubobject<USphereComponent>(TEXT("TriggerCollider"));

        // Both colliders need to have this set to true for events to fire
        Trigger.bGenerateOverlapEvents = true;

        // Set the collision mode for the collider
        // This mode will only enable the collider for raycasts, sweeps, and overlaps
        Trigger.SetCollisionEnabled(ECollisionEnabled::QueryOnly);
    }

    virtual void NotifyActorBeginOverlap(AActor* Other) override;

    virtual void NotifyActorEndOverlap(AActor* Other) override;
};
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

在UE4的蓝图中:

trigger

你可以从这阅读更多关于碰撞的设置 (opens new window)

# KinematicRigidbodies

在Unity中:

public class MyComponent : MonoBehaviour
{
    void Start()
    {
        rigidbody.isKinimatic = true;
        rigidbody.velocity = transform.forward * 10.0f;
    }
}
1
2
3
4
5
6
7
8

在UE4中,碰撞组件和刚体组件是一个,它们的基类是UPrimitiveComponent,该类有很多子类,比如USphereComponent,UCapsuleComponent等。

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

    UPROPERTY()
    UPrimitiveComponent* PhysicalComp;

    AMyActor()
    {
        PhysicalComp = CreateDefaultSubobject<USphereComponent>(TEXT("CollisionAndPhysics"));
        PhysicalComp->SetSimulatePhysics(false);
        PhysicalComp->SetPhysicsLinearVelocity(GetActorRotation().Vector() * 100.0f);
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 输入事件

在Unity中:

public class MyPlayerController : MonoBehaviour
{
    void Update()
    {
        if (Input.GetButtonDown("Fire"))
        {
            // ...
        }
        float Horiz = Input.GetAxis("Horizontal");
        float Vert = Input.GetAxis("Vertical");
        // ...
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

在UE4中:

UCLASS()
class AMyPlayerController : public APlayerController
{
    GENERATED_BODY()

    void SetupInputComponent()
    {
        Super::SetupInputComponent();

        InputComponent->BindAction("Fire", IE_Pressed, this, &AMyPlayerController::HandleFireInputEvent);
        InputComponent->BindAxis("Horizontal", this, &AMyPlayerController::HandleHorizontalAxisInputEvent);
        InputComponent->BindAxis("Vertical", this, &AMyPlayerController::HandleVerticalAxisInputEvent);
    }

    void HandleFireInputEvent();
    void HandleHorizontalAxisInputEvent(float Value);
    void HandleVerticalAxisInputEvent(float Value);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

在UE4的蓝图中:

InputEvent

你项目设置里的输入属性看起来应该是这样的:

InputSetting

# FAQ

# 如何自动加载最近的项目

如果你曾习惯于Unity自动加载所工作的最近项目,在UE4里也是同样的。为了使用该功能,在打开工程的时候勾选"Always load last project on Startup"。

# 在哪为我的游戏设置输入绑定

在Unity中,你习惯于使用Input Manager设置来给项目设置默认键绑定,在UE4中也是以类似的方式设置,你可以打开Project Setting,在Input分类面板中设置,在这个地方你可以添加Action Mappings和Axis Mappings。给每个映射一个名字和默认绑定,然后,在输入事件触发的时候给游戏的Pawn设置回调函数。可以在Input documentation page (opens new window)来查看更多的细节。

# 我如何改变项目的启动场景

你可以在Project Settings->Maps & Modes改变项目的启动场景。

# 我如何运行游戏

最简单的方法就是在编辑器的工具栏处点击"Play"按钮,这会立刻在编辑器的进程内运行游戏,如果你想让它作为单独的应用运行,点击"Play"的下拉箭头按钮,然后选择"Standalone Game"。最后,如果你想运行在手机设备或者web浏览器中,使用旁边的"Launch"按钮,这需要你安装相应平台的sdk。

# 这些单位是什么

在Unity中,基本的度量单位是1米,在UE4中,基本的度量单位是1厘米。

所以如果在Unity中移动了一个单位(米),相当于你在UE4中移动了100个单位(厘米)。

如果你想在Unity中移动2英尺,那就是0.61个单位(米),在UE4中就是61个单位(厘米)。

# 坐标系统差别

Unity和UE4都是左手坐标,但是轴调换了,在UE4中,正向X是"前方",正向Y是"右方",正向Z是"上方";在Unity中,正向Z是"前方",正向X是"右方",正向Y是"上方"。

# 如何在我的游戏中看Log输出

在UE4编辑器中,你可以在 "Window -> Developer Tools" 打开 "Output Log"。或者在运行你的游戏时添加"-log"命令行参数来在运行游戏时打开一个专门的窗口,这确实很有用!

# 说到Log输出,我的Debug.Log在哪

UE4中的log输出是高度定制化的,你可以在这查看详细的说明 (opens new window)

# 如何抛出异常

在Unity中你习惯于在事情发生错误的时候抛出异常,但是UE4并不适用异常处理,它使用"check()"函数来判断是否触发了一个断言错误,你可以传入错误消息,如果你想报告一个错误但并不想中断程序,请使用"ensure()"。这将会以全调用栈输出一个错误,但是程序还是继续执行。如果你想使用一个调试程序,这两个函数都会进入调试模式。

# .Net框架在哪

不像Unity,UE4并不使用.Net framework。UE4有它自己的容器类和库,一些常见的比较是:

.Net Framework UE4
String FString,FText
List TArray
Dictionary TMap
HashSet TSet

# UE4在代码改变时会自动加载吗

是的,你可以打开着UE4编辑器时边写代码,在完成代码时在Visual Studio直接编译,然后UE4编辑器就会自动地热加载你写的新代码。你也可以在UE4的编辑器中点击编译,在你使用Visual Studio debugger时将会很有用。

# 接下来应该往哪去

感谢阅读本教程!该教程在UE4社区里各地的开发者帮助下完成,我们希望得到你的反馈和修正,在我们发现更有利向UE4过渡的东西后我们会尽力完善本文档。下面你可以阅读下官方文档来获取更多的帮助。