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]
对于这个例子,我们只需要一个组件。它将把我们想要打印的消息存储到控制台。让我们在Sources中创建一个名为“Components”的新文件夹,在这个新文件夹中创建一个名为“DebugMessageComponent”的新C#脚本。
DebugMessageComponent.cs
using Entitas;
[Game]
public class DebugMessageComponent : IComponent
{
public string message;
}
保存文件,返回Unity,等待编译器完成,然后再次单击Generate。您现在应该在生成的文件夹中有一个名为GameDebugMessageComponent.cs的文件(在Generated - > Game - > Components中)
我们需要一个系统来监听添加了这个组件的实体。我们不需要它来更新每一帧,我们只关心添加一个实体 - 之后我们可以忘记它。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);
}
}
}
我们现在有一个用于保存消息数据的组件和一个系统,可以在将该组件添加到实体时打印消息。我们现在需要创建一个系统来生成“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!");
}
}
整合到一个功能中可以保持您的系统井井有条。它们还为您的系统提供整洁的可视化调试工具,并将它们视觉分离,以便在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));
}
}
为了使所有这些代码实际执行,我们需要创建一个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!” 在你的控制台中。
Duang~
在游戏运行的情况下,打开层次结构中的DontDestroyOnLoad对象。您应该能够快速导航到刚刚创建的实体。同样你也应该能够看到它的DebugMessageComponent与“Hello World!” 消息字符串。猜一下当你输入那个字段时会发生什么(笑)。
我们已经设置了消息记录系统来响应DebugMesage组件中的更改,每次键入字段时,组件都会被替换,并且触发响应系统。 不妨尝试一下: 1)删除组件并再次添加。 2)尝试单击父对象并创建新实体并将组件添加到该对象。 请注意我们的日志系统如何轻松处理您正在做的所有事情。
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实体不再存在。它已被我们的清理系统成功删除。您还会看到鼠标单击被记录到控制台。
Duaaaaaaang~
您可能还注意到,层次结构中的游戏对象上现在列出了1个“可重用”实体。这是为了使您最大限度地减少垃圾收集和内存分配的实体。现在尝试单击鼠标。请注意,您的鼠标点击正在记录,并且他们的实体也正在被清理,并且仍然只列出了1个可重复使用的实体。这是因为您的鼠标点击使用的是可重复使用的鼠标,而不是每次都创建一个新的。如果您尝试同时单击两个按钮,将在同一帧中创建两个实体。现在你将拥有两个可重用的实体。
收集行为
现在可能是回到MatchOne示例项目的好时机。您应该更容易检查项目中的代码,以确定您在屏幕上看到的效果是如何实现的。