Unity Tutorial Hello World - OneYoungMean/Entitas-CSharp-OYM GitHub Wiki

介绍

本教程将教授如何创建一个简单的“Hello World!” Unity与Entitas中的程序。目的是让您熟悉构成Entitas程序的不同部分,它们如何与彼此进行交互以及如何最佳地构建代码。

对于这样一个简单的程序来说,这似乎是一项非常繁重的工作,但我的目的是向您展示“Entitas Way”。以这种方式做事的好处一开始可能并不明显,但是在Etitas在处理他们的行为以后应该会具有一定的前瞻性。

为此,我已经准备了一些额外步骤,可以稍微扩展功能。一旦我们完成了设置项目的初步工作,您应该能够看到将它们插入现有游戏是多么容易。

项目文件 完成的统一项目可以在这里从github下载

准备工作

在我们开始之前,我们需要安装一个新的空Unity项目,并安装最新版本的Entitas。如果您需要帮助,请查看 step-by-step installation guide]

第1步 - 创建您的第一个组件

对于这个例子,我们只需要一个组件。它将把我们想要打印的消息存储到控制台。让我们在Sources中创建一个名为“Components”的新文件夹,在这个新文件夹中创建一个名为“DebugMessageComponent”的新C#脚本。

DebugMessageComponent.cs

using Entitas;

[Game]
public class DebugMessageComponent : IComponent 
{    
    public string message;
}

保存文件,返回Unity,等待编译器完成,然后再次单击Generate。您现在应该在生成的文件夹中有一个名为GameDebugMessageComponent.cs的文件(在Generated - > Game - > Components中)

第2步 - 创建您的第一个系统

我们需要一个系统来监听添加了这个组件的实体。我们不需要它来更新每一帧,我们只关心添加一个实体 - 之后我们可以忘记它。ReactiveSystems非常适合这种情况。

然后,我们的第一个系统将成为一个在Game的上下文中运行的ReactiveSystem。当我们注意到实体已经添加了DebugMessageComponent时,我们希望将该消息打印到日志中。

DebugMessageSystem.cs

using System.Collections.Generic;
using Entitas;
using UnityEngine;

public class DebugMessageSystem : ReactiveSystem<GameEntity>
{
    public DebugMessageSystem(Contexts contexts) : base(contexts.game)
    {
    }

    protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context)
    {
        // we only care about entities with DebugMessageComponent 
        return context.CreateCollector(GameMatcher.DebugMessage);
    }

    protected override bool Filter(GameEntity entity)
    {
        // good practice to perform a final check in case 
        // the entity has been altered in a different system.
        return entity.hasDebugMessage;
    }

    protected override void Execute(List<GameEntity> entities)
    {
        // this is the list of entities that meet our conditions
        foreach (var e in entities)
        { 
            // we can safely access their DebugMessage component
            // then grab the string data and print it
            Debug.Log(e.debugMessage.message);
        }
    }
}

Step 3 - 创建“Hello World!” 系统

我们现在有一个用于保存消息数据的组件和一个系统,可以在将该组件添加到实体时打印消息。我们现在需要创建一个系统来生成“Hello World!”的信息。我们将使用Initialize System来在程序开始时创建它。

HelloWorldSystem.cs

using Entitas;

public class HelloWorldSystem : IInitializeSystem
{ 
    // always handy to keep a reference to the context 
    // we're going to be interacting with it
    readonly GameContext _context;

    public HelloWorldSystem(Contexts contexts)
    { 
        // get the context from the constructor
        _context = contexts.game;
    }

    public void Initialize()
    {
        // create an entity and give it a DebugMessageComponent with
        // the text "Hello World!" as its data
        _context.CreateEntity().AddDebugMessage("Hello World!");
    }
}

第4步 - 将您的系统整合到一个功能中

整合到一个功能中可以保持您的系统井井有条。它们还为您的系统提供整洁的可视化调试工具,并将它们视觉分离,以便在Unity层次结构中进行检查。现在让我们将两个系统组合成一个功能。我们添加它们的顺序将定义程序运行时它们执行的顺序。功能要求您实现构造函数,您可以使用该Add()方法添加系统。

TutorialSystems.cs

using Entitas;

public class TutorialSystems : Feature
{
    public TutorialSystems(Contexts contexts) : base ("Tutorial Systems")
    {
        Add(new HelloWorldSystem(contexts));
        Add(new DebugMessageSystem(contexts));
    }
}

第5步 - 将所有内容放在一起

为了使所有这些代码实际执行,我们需要创建一个MonoBehaviour可以添加到Unity层次结构中的对象的代码。在Sources文件夹中,创建一个新的C#脚本并为其命名GameController.cs。这是我们的入口点。它负责创建,初始化和执行系统。

GameController.cs

using Entitas;
using UnityEngine;

public class GameController : MonoBehaviour
{
    Systems _systems;

    void Start()
    {
        // get a reference to the contexts
        var contexts = Contexts.sharedInstance;
        
        // create the systems by creating individual features
        _systems = new Feature("Systems")
            .Add(new TutorialSystems(contexts));

        // call Initialize() on all of the IInitializeSystems
        _systems.Initialize();
    }

    void Update()
    {
        // call Execute() on all the IExecuteSystems and 
        // ReactiveSystems that were triggered last frame
        _systems.Execute();
        // call cleanup() on all the ICleanupSystems
        _systems.Cleanup();
    }
}

保存脚本后,在层次结构中创建一个新的空GameObject并添加GameController.cs到其中。保存场景并按下播放。你应该看到“Hello World!” 在你的控制台中。

Success!

Duang~

额外的步骤

关于反应系统的额外内容

在游戏运行的情况下,打开层次结构中的DontDestroyOnLoad对象。您应该能够快速导航到刚刚创建的实体。同样你也应该能够看到它的DebugMessageComponent与“Hello World!” 消息字符串。猜一下当你输入那个字段时会发生什么(笑)。

我们已经设置了消息记录系统来响应DebugMesage组件中的更改,每次键入字段时,组件都会被替换,并且触发响应系统。 不妨尝试一下: 1)删除组件并再次添加。 2)尝试单击父对象并创建新实体并将组件添加到该对象。 请注意我们的日志系统如何轻松处理您正在做的所有事情。

Inspecting Components

inspector中的组件

清理系统

你到目前为止,我们在这个例子中添加了几个额外的系统怎么样?我们已经知道,在我们使用它们之后,我们不需要使用这些组件。让我们实现一个在其他系统运行完毕后摆脱它们的系统。

在这里,我们将创建一个ICleanupSystem,我们将使用a Group来跟踪添加了DebugMessages的实体。在我们的GameController中,删除这些实体不会干扰并不会干扰Execute和Reactive系统,所以我们可以在 Execute()之后调用 Cleanup()来减少系统的负担。 通过这种方式,我们可以在将来添加更多系统,以不同的方式处理我们的消息(例如打印到日志文件或将其作为电子邮件发送)。因此,我们不希望我们的第一个消息系统负责销毁这些实体,因为这会干扰我们未来计划的系统,所以我们需要把它独立开来。

CleanupDebugMessageSystem.cs

using Entitas;

public class CleanupDebugMessageSystem : ICleanupSystem
{
    readonly GameContext _context;
    readonly IGroup<GameEntity> _debugMessages;

    public CleanupDebugMessageSystem(Contexts contexts)
    {
        _context = contexts.game;
        _debugMessages = _context.GetGroup(GameMatcher.DebugMessage);
    }

    public void Cleanup()
    {
        // group.GetEntities() always gives us an up to date list
        foreach (var e in _debugMessages.GetEntities())
        {
           e.Destroy();
        }
    }
}

记录鼠标单击

让我们扩展我们的日志记录功能,以记录用户的鼠标点击。在这里,我们将使用IExecuteSystem来监听用户点击并创建新的DebugMessage实体。我们可以利用Unity的Input类来获取用户输入并在收到输入时创建新实体。

LogMouseClickSystem.cs

using Entitas;
using UnityEngine;

public class LogMouseClickSystem : IExecuteSystem
{
    readonly GameContext _context;

    public LogMouseClickSystem(Contexts contexts)
    {
        _context = contexts.game;
    }

    public void Execute()
    {
        if (Input.GetMouseButtonDown(0))
        {
            _context.CreateEntity().AddDebugMessage("Left Mouse Button Clicked");
        }

        if (Input.GetMouseButtonDown(1))
        {
            _context.CreateEntity().AddDebugMessage("Right Mouse Button Clicked");
        }
    }
}

结合新功能

你现在已经完成了大部分繁重的工作,因此很容易融入新系统。在较大的项目中,您可以将系统分离为逻辑连接的功能,并在它们之间强制执行命令。由于我们的项目非常简单,我们将新系统添加到现有功能中TutorialSystems。

using Entitas;

public class TutorialSystems : Feature
{
    public TutorialSystems(Contexts contexts) : base ("Tutorial Systems")
    {
        Add(new HelloWorldSystem(contexts));
        Add(new LogMouseClickSystem(contexts)); // new system
        Add(new DebugMessageSystem(contexts));
        Add(new CleanupDebugMessageSystem(contexts)); // new system (we want this to run last)
    }
}

现在,当您运行场景时,即使您的消息显示在控制台中,您也会注意到您的hello world实体不再存在。它已被我们的清理系统成功删除。您还会看到鼠标单击被记录到控制台。

More Success

Duaaaaaaang~

收集你的行为

您可能还注意到,层次结构中的游戏对象上现在列出了1个“可重用”实体。这是为了使您最大限度地减少垃圾收集和内存分配的实体。现在尝试单击鼠标。请注意,您的鼠标点击正在记录,并且他们的实体也正在被清理,并且仍然只列出了1个可重复使用的实体。这是因为您的鼠标点击使用的是可重复使用的鼠标,而不是每次都创建一个新的。如果您尝试同时单击两个按钮,将在同一帧中创建两个实体。现在你将拥有两个可重用的实体。

Pooling

收集行为

下一步

现在可能是回到MatchOne示例项目的好时机。您应该更容易检查项目中的代码,以确定您在屏幕上看到的效果是如何实现的。

⚠️ **GitHub.com Fallback** ⚠️