【Unreal】为Unity开发者准备的指南-笔记四

目录

术语简表

文件夹作用

支持的文件格式

场景保存

项目设置

源文件位置

从GameObject到Actor

组件

从Prefab到蓝图类

从Script到C++Component

可编写脚本的Actor蓝图类

虚幻中的组件与Actor

Actor与Object与GamePlay框架

在虚幻中编写代码

实例化GameObject/生成Actor

UObject和NewObject

AActor和SpawnActor

类型转换

从组件访问GameObject/Actor

从GameObject/Actor访问组件

查找 GameObject/Actor

向GameObject/Actor添加标签

比较GameObject/Actor和MonoBehaviour/ActorComponent上的标签

物理:刚体与图元组件

RayCast与RayTrace

触发器体积

刚体运动

输入事件

如何抛异常?

虚幻的容器类和库

最后


本篇学习官方收录的《为Unity开发者准备的虚幻引擎指南》

术语简表

类别UnityUnreal Engine
GamePlay类型Component

组件

Component

GameObject

Actor

Actor

Prefab

蓝图类

Blueprint Class

编辑器界面

Editor UI

Hierarchy Panel

世界大纲视图

World Outliner

Inspector

细节面板

Details Panel

Project Browser

内容浏览器

Content Browser

Scene View

关卡视图

Level Viewport

网格体

Meshes

Mesh

静态网格体

Static Mesh

Skinned Mesh

骨骼网格体

Skeletal Mesh

材质

Materials

Shader

材质、材质编辑器

MaterialMaterial Editor

Material

材质实例

Material Instance

特效

Effects

Particle Effect

特效、粒子、Niagara

Effect, Particle, Niagara

游戏界面

Game UI

UI

UMG

UMG (Unreal Motion Graphics)

动画

Animation

Animation

骨骼网格体动画系统

Skeletal Mesh Animation System

Mecanim

动画蓝图

Animation Blueprint

SequencesSequencer
2DSprite EditorPaper2D

编程

Programming

C#C++
Script、Bolt

蓝图

Blueprint

物理

Physics

RaycastLine Trace, Shape Trace
Rigidbody

碰撞、物理

Collision, Physics

运行时平台

Runtime Platforms

iOS Player, Web Player

支持的平台

Platforms

文件夹作用

Content:保存项目的所有资产

Config:保存项目的配置 (.ini)文件

Source:保存项目中的C++文件

支持的文件格式

类型支持
3D.fbx、.obj
纹理.png、.jpeg、.bmp、.tga、.dds、.exr、.psd、.hdr
声音.wav
字体.ttf、.otf
视频.mov、.mp4、.wmv
Datasmith3ds Max、SketchUp、Revit等各种CAD应用程序中的3D场景和资产
LiDAR点云插件LiDAR点云。请参阅LiDAR点云插件概述,查看支持的文件类型列表。
USD导入器.usd(通用场景描述)
Alembic文件导入器.abc

场景保存

在Unity是保存Scene,虚幻使用 Map file 。它保存 Level 的数据。

项目设置

从虚幻编辑器的主菜单,转至 编辑(Edit)> 项目设置(Project Settings

Unity 中的 Player Settings = 虚幻中的 平台设置(Platform Settings),在 项目设置(Project Settings) 窗口的 平台(Platforms) 分段下配置

源文件位置

在Unity中,将C#源文件放在 Assets 文件夹中。在虚幻引擎中,源文件的位置会根据虚幻项目的类型不同而在不同的位置:

  • C++项目 在虚幻项目目录中有一个 Source 文件夹。Source 文件夹包含各种文件,包括C++源文件(.cpp)和头文件(.h),以及一些编译用的脚本(Build.csTarget.cs
  • 蓝图项目 没有 Source 文件夹。蓝图可以放在虚幻项目的 Content 文件夹中的任意位置
  • 如果想将C++代码添加到蓝图项目,请从虚幻编辑器的主菜单转至 工具(Tools)> 新C++类(New C++ Class)

从GameObject到Actor

与Unity的Game Object相对应的概念是Actor

虚幻还预制了特殊的Actor,例如 Pawn(用于可以由玩家或AI控制的Actor),或角色(用于需要复杂移动和交互的可控玩家角色)。就像空Actor那样。

虚幻引擎有一种Gameplay框架来处理这些特殊Actor。

虚幻中的Actor与Unity中的GameObject稍有不同。在Unity中GameObject是C#类,不能直接扩展。在虚幻引擎中,Actor是C++类,可以使用继承来扩展和自定义

组件

在Unity中使用组件向GameObject添加功能。在虚幻中,将组件添加到Actor。选中要添加组件的Actor点击 细节(Details) 面板中的 添加组件(Add Component) 按钮, 然后选择要添加的组件。

从Prefab到蓝图类

虚幻中的蓝图类可以做到类似Unity中预制件的工作流程

  1. 在Actor的 细节(Details) 面板中,点击 蓝图(Blueprint)  →添加脚本(Add Script),向其添加蓝图。
  2. 保存这个新的蓝图类。
  3. 点击 创建蓝图(Create Blueprint)

从Script到C++Component

与在Unity的GameObject添加C#脚本类似,在虚幻中从Actor的 细节(Detail) 面板或 编辑Actor(Editor Actor) 窗口,点击 添加组件(Add Component)

在Unity中创建新的MonoBehaviour时,会有一个骨架类文件,其中包含Start()Update()函数。虚幻中也有类似的函数

  • InitializeComponent(),其作用与Start()相同
  • TickComponent(),其作用与Update()相同

可编写脚本的Actor蓝图类

新的Actor蓝图类会有自己的蓝图可视化脚本。

除了支持可视化脚本的蓝图类之外,虚幻还支持使用代码来实现C++类。

虚幻中的组件与Actor

虚幻中有许多特殊类型的Actor,它们附带一定的功能,并始终包含特定组件。例如,角色(Character) 始终包含 角色移动组件(character movement component)

下面是一些常用的虚幻Actor:

  • Pawn:一种Actor,用于表示可控制的游戏对象,比如玩家操控的角色。玩家和AI都可以通过所属控制器移动Pawn
  • 角色(Character):一种版本更特殊的Pawn,用于双足类型的角色(角色有类似于人类的骨骼结构,用两腿行走)
  • 控制器(Controller):占有并控制Pawn。通过将Pawn与控制器分离,可以编写AI控制器,用于控制Pawn,并且和玩家控制Pawn采用相同的接口
  • 玩家控制器(Player Controller):一种更特殊的控制器,用于获取玩家的输入信息,并将这些信息映射到Pawn或角色的行为中

Actor与Object与GamePlay框架

Actor是虚幻中最常用于GamePlay的类,并且是唯一可以被生成(Spawned)到世界(World)中的类型。换句话说,放入关卡中的一切内容都将是Actor。

另一个重要的类型是Object。它实际上是所有虚幻的类的基类,包括Actor。它是比Actor低得多的类,但作为虚幻中的类,它仍拥有一些基本功能,例如 反射(Reflection) 和 序列化(Serialization) 。Object是一个非常基础的类,Actor组件是所有类型的基类,派生自Object。

虚幻有一个额外的层,称为Gameplay框架。如果使用特定图元类并遵循特定规范,游戏将自动获得原本实现起来困难又费时的额外功能(例如,完全多玩家支持)

要使用GamePlay框架,需要了解如何使用和自定义虚幻随附的内置Actor类,例如 Pawn、角色(Character) 玩家控制器(Player Controller)。例如,想实现多玩家,还需要熟悉虚幻中网络和复制的工作方式。

在虚幻中编写代码

有时必须手动刷新Visual Studio项目文件。

  • 从虚幻引擎的主菜单,转至 工具(Tools) > 刷新Visual Studio项目 (Refresh Visual Studio Project)
  • 右键单击 项目目录中的  .uproject 文件, 然后选择 Generate Visual Studio project files

在虚幻中,可以在Actor本身上编写代码,而不是仅仅对新组件类型编码。

虚幻中有一组类似Unity的Start(), Ondestroy(), Update()函数方法

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

        // 游戏开始时调用。
        void BeginPlay();

        // 销毁时调用。
        void EndPlay(const EEndPlayReason::Type EndPlayReason);

        // 每帧调用以更新此Actor。
        void Tick(float DeltaSeconds);
    };

在虚幻中必须调用父类版本的方法。

例如在Unity的C#中可能是调用 base.Update(), 但在虚幻C++中将使用Super::TickComponent(): 

    void UMyComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
    {
        // 此处添加自定义更新内容
        Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
    }

在C++中一些类使用以 开头,而另一些类以 U 开头。A 表示 Actor 子类,而 U 表示 Object 子类。另一个常用前缀是 F ,它用于大部分普通数据结构和非UObject类

在子类中使用 Super,是对父类成员函数、成员变量的调用

实例化GameObject/生成Actor

有两个不同的函数来实例化对象:

  • NewObject 用于创建新的 UObject
  • SpawnActor 用于生成 AActor 类型

UObject和NewObject

在虚幻中创建 UObject 的子类与在Unity中创建 ScriptableObject 的子类非常相似。对于不需要生成到世界中或像Actor那样绑定了组件的GamePlay类,这些很有用。

在Unity中,如果你创建自己的 ScriptableObject 子类,你会如下所示将其实例化

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

在虚幻引擎中,如果你创建自己的 UObject 派生类型,你可以如下所示将其实例化:

UMyObject* NewObj = NewObject<UMyObject>();

AActor和SpawnActor

Actor使用World(C++中的 UWorld)对象上的 SpawnActor 方法生成。一些UObject提供了 GetWorld 方法(例如,所有Actor都如此)。你可以采用此方法获取World对象

请注意,在下面的示例中,我们传入了我们想生成的Actor的类,而不是传入另一个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);
    }
  • GetWorld():获取world当中的东西 (要加头文件#include “Engine/World.h”)
  • GetWorld()→SpawnActor<类型名>(名字,位置,旋转):在世界中生成物体

类型转换

类似Unity中

    Collider collider = gameObject.GetComponent<Collider>;
    SphereCollider sphereCollider = collider as SphereCollider;

虚幻C++中

    UPrimitiveComponent* Primitive = MyActor->GetComponentByClass(UPrimitiveComponent::StaticClass());
    USphereComponent* SphereCollider = Cast<USphereComponent>(Primitive);

从组件访问GameObject/Actor

    // Unity中
    GameObject ParentGO = MyComponent.gameObject;

    // 虚幻C++中
    AActor* ParentActor = MyComponent->GetOwner();

从GameObject/Actor访问组件

    // Unity 中
    MyComponent MyComp = gameObject.GetComponent<MyComponent>();

    // 虚幻C++ 中
    UMyComponent* MyComp = MyActor->FindComponentByClass<UMyComponent>();

查找 GameObject/Actor

    // Unity 中
    GameObject MyGO = GameObject.Find("MyNamedGameObject");
    
 
    // 按类型查找Object
    MyComponent[] Components = Object.FindObjectsOfType(typeof(MyComponent)) as MyComponent[];

    // 按标签查找GameObject
    GameObject[] GameObjects = GameObject.FindGameObjectsWithTag("MyTag");

 

    // 虚幻 中
    // 按名称查找Actor(也适用于UObject)
    AActor* MyActor = FindObject<AActor>(nullptr, TEXT("MyNamedActor"));

    // 按类型查找Actor(需要UWorld对象)
    for (TActorIterator<AMyActor> It(GetWorld()); It; ++It)
    {
        AMyActor* MyActor = *It;
        // ...
    }

    // 按类型查找UObject
    for (TObjectIterator<UMyObject> It; It; ++it)
    {
        UMyObject* MyObject = *It;
        // ...
    }
    // 按标签查找Actor(也适用于ActorComponent,需要改用TObjectIterator)
    for (TActorIterator<AActor> It(GetWorld()); It; ++It)
    {
        AActor* Actor = *It;
        if (Actor->ActorHasTag(FName(TEXT("Mytag"))))
        {
            // ...
        }
    }

向GameObject/Actor添加标签

    // Unity 中
    MyGameObject.tag = "MyTag";


    // 虚幻 中
    // Actor可以有多个标签
    MyActor.Tags.AddUnique(TEXT("MyTag"));

比较GameObject/Actor和MonoBehaviour/ActorComponent上的标签

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

    // 检查它绑定到的GameObject上的标签
    if (MyComponent.CompareTag("MyTag"))
    {
        // ...
    }


    // 虚幻中
    // 检查某个Actor是否有此标签
    if (MyActor->ActorHasTag(FName(TEXT("MyTag"))))
    {
        // ...
    }

    // 检查某个ActorComponent是否有此标签
    if (MyComponent->ComponentHasTag(FName(TEXT("MyTag"))))
    {
        // ...
    }

物理:刚体与图元组件

在Unity中,假如要为GameObject赋予物理特征,首先必须为其提供刚体组件。

在虚幻引擎中,任何图元组件(C++中的 UPrimitiveComponent)都可以是物理对象。一些常见图元组件如下:

  • 形状组件(胶囊体、球体和盒体)
  • 静态网格体组件
  • 骨骼网格体组件

Unity将碰撞和可视性划分到不同的组件中,虚幻引擎则将"潜在的物理碰撞"和"潜在的可视效果"组合到了单个图元组件中。凡是在世界中具有形状的组件,只要能通过物理方式渲染或交互,都是 PrimitiveComponent 的子类

RayCast与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;
    }

虚幻 C++

    APawn* AMyPlayerController::FindPawnCameraIsLookingAt()
    {
        // 你可以在这里自定义有关追踪的各种属性
        FCollisionQueryParams Params;
        // 忽略玩家的Pawn
        Params.AddIgnoredActor(GetPawn());

        // 击中结果由线路追踪填充
        FHitResult Hit;

        // 来自摄像机的Raycast,仅与Pawn碰撞(它们在ECC_Pawn碰撞通道上)
        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包含指向追踪所击中的Actor的弱指针
            return Cast<APawn>(Hit.Actor.Get());
        }

        return nullptr;
    }

触发器体积

Unity C#

    public class MyComponent : MonoBehaviour
    {
        void Start()
        {
            collider.isTrigger = true;
        }
        void OnTriggerEnter(Collider Other)
        {
            // ...
        }
        void OnTriggerExit(Collider Other)
        {
            // ...
        }
    }

虚幻 C++

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

        // 我的触发器组件
        UPROPERTY()
        UPrimitiveComponent* Trigger;

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

            // 两个碰撞物都需要将此项设置为true,才能触发事件
            Trigger.bGenerateOverlapEvents = true;

            // 设置碰撞物的碰撞模式
            // 此模式仅为光线投射、扫描和重叠启用碰撞物
            Trigger.SetCollisionEnabled(ECollisionEnabled::QueryOnly);
        }

        virtual void NotifyActorBeginOverlap(AActor* Other) override;

        virtual void NotifyActorEndOverlap(AActor* Other) override;
    };

刚体运动

Unity C#

    public class MyComponent : MonoBehaviour
    {
        void Start()
        {
            rigidbody.isKinematic = true;
            rigidbody.velocity = transform.forward * 10.0f;
        }
    }

在虚幻引擎4中,碰撞组件和刚体组件是同一个组件。其基类是 UPrimitiveComponent,它有许多子类(USphereComponentUCapsuleComponent 等)

    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);
        }
    };

输入事件

Unity C#

    public class MyPlayerController : MonoBehaviour
    {
        void Update()
        {
            if (Input.GetButtonDown("Fire"))
            {
                // ...
            }
            float Horiz = Input.GetAxis("Horizontal");
            float Vert = Input.GetAxis("Vertical");
            // ...
        }
    }

虚幻C++

    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);
    };

需要对项目设置中的输入属性进行配置

如何抛异常?

不同于Unity,虚幻引擎并不处理异常。请改用 check() 函数来触发严重的断言错误。你可以传入错误信息提示。如果你想报告错误,但不希望打断程序,请改用 ensure() 这将记录一个带有完整调用堆栈的错误信息,但程序会继续执行。如果你附加了调试器,那么这两个函数都会暂定并进入调试器。check()

虚幻的容器类和库

.NetFramework虚幻引擎
StringFStringFText
ListTArray
DictionaryTMap
HashSetcheck()

 还可以去文档中了解其他容器

最后

  • 在编写代码时可以将编辑器保持开启状态,完成编辑后,从VS启动编译,编辑器将自动“热重新加载”
  • 论坛

  • 社区资源

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值