Tutorial ‐ Entitas‐lang(已停止维护) - OneYoungMean/Entitas-CSharp-OYM GitHub Wiki
本教程将简要介绍如何将Entitas-lang与Entitas一起使用。在本教程中,我将使用一个小例子来创建一个简单的战斗系统,玩家可以通过按空格键来攻击游戏中的敌人。
译者注:这玩意相当的奇怪,翻译起来很多地方谷歌翻译都败了,所以翻译不过去的我只能保留原文了(摊手)。
使用Entitas-lang的一个优点是,它允许您在Entitas中创建新组件和系统时,一遍又一遍地消除编写相同代码的许多冗余。这意味着您必须编写更少的代码来完成相同的工作量,并且每次添加新的组件和系统时它都会自动生成新文件。
另一个优点是,它允许您生成新文件,即使您遇到编译错误。如果没有Entitas-lang,Unity中的“Entitas”菜单将会消失,从而使您无法生成新文件(如果您不使用Entitas-lang,这是生成文件的唯一方法).
一旦习惯了Entitas-lang的语法,就可以很容易地创建新的组件和系统,从而为游戏添加新功能.
首先你得安装一个Entitas,这里我就不重复叙述了。 其次,你可以使用两种方法可以使用Entitas-lang;使用Visual Studio Code或Eclipse。请注意,Visual Studio Code有一些限制(例如不支持多个.entitas文件)。但是,它确实支持自动完成,这在开始学习Entitas-lang时可以提供很多帮助。在本教程中,我将使用Visual Studio Code。
- 下载 Visual Studio Code.(注意不是visual studio!)
- 确保您的计算机上安装了 Java 1.8
- 下载 Entitas Extension.(度盘炸掉发issue补档)
- 打开VS Code的命令提示符 (Ctrl+Shift+P)输入 "install from VSIX" 然后在弹出的对话框中选择vsix文件。
- File > Open Folder... > Go to your Unity project folder > Select the Assets folder // 文件 > 打开文件夹... > 你的 Unity project 文件夹> Assets 文件夹
Install the eclipse plugin from the update site by following this step-by-step guide:
- Open Eclipse > Go to Help > Install New Software...
- Paste "http://bomzhi.de/entitas_lang/site.xml" into "Work with" (top input bar).
- Tick the item "Uncategorized" in the box.
- Click Next and let it install.
- File > Open Projects from file system > Click on Directory > Select your Unity projects Assets folder
- If eclipse prompts you with a dialog about converting the project to an Xtext project; click yes.
Alternatively you can download the plugin as a zip file if you want to add it to eclipse manually.
在你安装完Entitas之后。你的项目应该看起来像是这个样子:
新建一个 "Sources"文件夹. 这是你创建操纵游戏实体的体统的地方,然后在Assets文件夹下创建一个叫做"compAndSys.entitas" (记住结尾一定要是.entitas,这很重要)(没看到格式选项就自己写一下),这个文件将会包含所有的component与系统那些Entitas-lang将会生成的。
最后项目结构看起来是这个样子的:
为了让Entitas-lang在C#中生成文件,请在"compAndSys.entitas"第一行写上:
target entitas_csharp
执行此操作后,您可以看到Entitas-lang创建了一个名为“src-gen”的文件夹。这是放置所有生成的文件的位置。
在这里我们将会只使用两个上下文————一个game和一个input,game上下文将会拥有所有的游戏逻辑,input上下文将会处理所有来自user的输入。(在这个例子中,我们使用的是空格键)
要生成game与input的上下文,只需要在 "compAndSys.entitas" 中写:
context Game(default), Input
另一个标记(default标记)已添加到上面的代码中。正如我们编写所需要的component格式那样,所有未指定其上下文的新component(即没有注明[Input]的component)都将被置于默认上下文中。
现在我们的"compAndSys.entitas"应该是这个样子:
target entitas_csharp
context Game(default), Input
游戏将由玩家和玩家可以攻击的敌人组成,这可以分为几个区间:生命值,伤害,玩家,敌人,与来自空格的输入。
要生成这些组件,我们需要引入一个别名:
alias int : "int"
每当您想在组件中使用C#中的类型时,最佳做法是指定一个新别名。请看Entitas-lang中的以下代码:
comp CompA
ListOfInts : "System.Collections.Generic.List"
comp CompB
ListOfInts : "System.Collections.Generic.List"
comp CompC
ListOfInts : "System.Collections.Generic.List"
因为Entitas-lang不了解C#中的类型,所以必须指定完整的命名空间。但是,使用别名这会变得更容易. 请区分下面代码与上面的区别 (两者所希望表达的含义是一样的):
alias IntList : "System.Collections.Generic.List"
comp CompA
ListOfInts : IntList
comp CompB
ListOfInts : IntList
comp CompC
ListOfInts : IntList
如上所示,生成组件;使用“comp”关键字。也就是说,要生成运行状况和损坏组件,请编写代码:
comp Health
Value : int
comp Damage
Value : int
这是Entitas-lang的力量;能够快速编写和生成组件。通常你会写下面的C#代码:
public class HealthComponent : IComponent{
public int Value { get; set; }
}
public class DamageComponent : IComponent{
public int Value { get; set; }
}
然后在Unity中按生成。 Entitas-lang会自动为您完成此操作.
为了区分玩家实体和敌人实体,我们将创建两个将用作标志的组件.
comp Player (unique)
comp Enemy
此外,还添加了"(unique)"来指定整个游戏中只有一个玩家。
(简体) 应该指出的是,到目前为止,我们尚未指定组件的上下文。那是因为我们之前设置的默认上下文。在输入组件中,我们必须使用“in”关键字显式编写上下文。以下代码演示了这一点:
comp SpacebarInput in Input
现在你的 "compAndSys.entitas"看起来应该是这样子的:
target entitas_csharp
context Game(default), Input
alias int : "int"
comp Health
Value : int
comp Damage
Value : int
comp Player (unique)
comp Enemy
comp SpacebarInput in Input
如果你顺手打开了"src-gen"文件夹,你会看到所有你所希望的组件此刻已经被Entitas-lang生成了:
在我们可以开始使用刚刚生成的component之前,我们需要生成一个系统,在这里我们需要四个系统来支持:
- SpawnSystem (initialize system)
- InputSystem (execute and cleanup system - to gather inputs from the keyboard)(键盘输入系统:execute and cleanup system)
- ProcessSpacebarInputSystem (reactive system - 处理所有有关于实体的伤害)
- PrintHealthSystem (reactive system - 把生命值传递给entitas
() 我们将使用初始化系统来创建游戏中的玩家和敌人。以下代码执行此操作:
sys SpawnSystem (init)
access:
_gameContext : Game
为了生成系统,我们需要使用关键词“sys”,当我们处理一个初始化系统,我们需要指定它才能给他命名。这在上面用“(init)”显示。另一个关键词“访问”被引入了。这允许我们访问我们将创建玩家和敌方实体的上下文。 “_gameContext”是将在C#中生成的字段的名称,“Game”是上下文。
上面的代码生成了一个名为“AbstractSpawnSystem”的类,我们将在创建spawn系统时继承它。以下C#代码段显示了这一点:
public class SpawnSystem : AbstractSpawnSystem
{
public SpawnSystem(Contexts contexts) : base(contexts)
{ }
public override void Execute()
{
// leave this as empty
}
public override void Initialize()
{
// access the game context and create a player
var player = _gameContext.CreateEntity();
player.isPlayer = true;
player.AddDamage(2);
player.AddHealth(10);
// create two enemies
var enemy1 = _gameContext.CreateEntity();
enemy1.isEnemy = true;
enemy1.AddDamage(2);
enemy1.AddHealth(10);
var enemy2 = _gameContext.CreateEntity();
enemy2.isEnemy = true;
enemy2.AddDamage(2);
enemy2.AddHealth(10);
}
}
在上面的代码中,我展示了如何使用我们创建的上下文访问器“_gameContext”。上下文字段用于创建玩家和敌方实体。
将Entitas与Unity一起使用时,通常将所有外部逻辑同步到Entitas是一种很好的做法。这意味着,如果您有来自Unity的输入,您应该制作可以操作该输入的组件和系统。例如,这可能是:来自Unity的碰撞系统的碰撞,在Unity中创建游戏对象和/或键盘和鼠标输入。
在这里,我将展示如何将键盘输入同步到Entitas,以便它可以被我们游戏中的不同系统流畅地使用。以下代码显示了如何创建输入系统:
sys InputSystem (cleanup)
access:
_inputContext : Input
请注意,我们现在使用访问器作为输入上下文。此外,还增加了"(cleanup)",使其成为一个cleanup system。
与SpawnSystem一样,Entitas-lang创建了一个名为“AbstractInputSystem”的类,我们将使用SpacebarInputComponent创建一个实体:
using UnityEngine;
public class InputSystem : AbstractInputSystem
{
public InputSystem(Contexts contexts) : base(contexts)
{ }
public override void Execute()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var spaceBarInput = _inputContext.CreateEntity();
spaceBarInput.isSpacebarInput = true;
}
}
public override void Cleanup()
{
var spacebarInputs = _inputContext.GetGroup(InputMatcher.SpacebarInput);
foreach (InputEntity inputEntity in spacebarInputs.GetEntities())
{
_inputContext.DestroyEntity(inputEntity);
}
}
}
如果我们不这么做的话,在这个系统中,我们每次玩家按空格键时都会创建一个新实体,实体永远不会被清理干净!这就是.entitas文件中添加“(cleanup)”的原因。每个帧我们清理所有输入实体,因为它们将不再被任何系统使用。
们在InputSystem中创建的实体将具有SpacebarInputComponent。当添加到实体时,我们希望在ProcessSpacebarInputSystem中对它做出反应。反过来,这会对游戏中的所有敌人造成伤害。
要创建一个被动系统,我们将在定义系统时使用“trigger”关键字。我们只想在玩家击中空格键时伤害我们的敌人。也就是说,将SpacebarInputComponent添加到实体时。因此我们使用关键字“添加”。我们还添加了“过滤allOf(SpacebarInput”以确保我们获得的所有实体实际上都有这个组件。参考下面的代码:
sys ProcessSpacebarInputSystem
trigger:
added(SpacebarInput)
filter allOf(SpacebarInput)
access:
_gameContext : Game
在C#中,我们希望获得游戏中的所有敌人,并根据玩家可以处理多少伤害来降低他们的健康状况。以下代码演示了这一点:
using System.Collections.Generic;
class ProcessSpacebarInputSystem : AbstractProcessSpacebarInputSystem
{
public ProcessSpacebarInputSystem(Contexts contexts) : base(contexts)
{
}
protected override void Execute(List<InputEntity> spacebarInputs)
{
// get all enemies
var enemies = _gameContext.GetGroup(GameMatcher.Enemy).GetEntities();
// get the player
var player = _gameContext.player;
foreach (var spacebarInput in spacebarInputs)
{
foreach (var enemy in enemies)
{
enemy.ReplaceHealth(enemy.health.Value - player.damage.Value);
}
}
}
}
PrintHealthSystem将在其健康状况发生变化时打印出实体的健康状况。以下代码创建此系统:
sys PrintHealthSystem
trigger:
added(Health)
filter allOf(Health)
And the C# code looks like this:
using System.Collections.Generic;
using UnityEngine;
class PrintHealthSystem : AbstractPrintHealthSystem
{
public PrintHealthSystem(Contexts contexts) : base(contexts)
{
}
protected override void Execute(List<GameEntity> entities)
{
// iterate over all entities where their health has changed
foreach (var entity in entities)
{
Debug.Log(entity + " has " + entity.health.Value + " health left");
}
}
}
The final code for the systems in "compAndSys.entitas" looks like this:
sys SpawnSystem (init)
access:
_gameContext : Game
sys InputSystem (cleanup)
access:
_inputContext : Input
sys ProcessSpacebarInputSystem
trigger:
added(SpacebarInput)
filter allOf(SpacebarInput)
access:
_gameContext : Game
sys PrintHealthSystem
trigger:
added(Health)
filter allOf(Health)
有一件事丢失了!这些代码都没有运行。为了使系统执行他们的代码,在Unity中创建一个名为“GameController”的空游戏对象,其脚本名为“GameController.cs”,如下所示:
In GameController.cs write the following code:
using UnityEngine;
using Entitas;
class GameController : MonoBehaviour
{
private Systems _systems;
private void Start()
{
var contexts = Contexts.sharedInstance;
contexts.SetAllContexts();
_systems = CreateSystems(contexts);
_systems.Initialize();
}
private void Update()
{
_systems.Execute();
_systems.Cleanup();
}
private void OnDestroy()
{
_systems.TearDown();
}
private Systems CreateSystems(Contexts contexts)
{
return new Feature("Systems")
.Add(new SpawnSystem(contexts))
.Add(new InputSystem(contexts))
.Add(new ProcessSpacebarInputSystem(contexts))
.Add(new PrintHealthSystem(contexts))
;
}
}
在运行游戏时,您将首先在控制台中看到三个打印件;一个为玩家,两个为敌人:
每一次你按下空格,都会对敌人造成两点的伤害 Every time you press the space bar, it will deal 2 damage to the enemies:
这就对了!这就是使用Entitas时如何使用Entitas-lang的优势。正如我所示,它允许您快速为游戏创建新的组件和系统。
在这个小例子中做了很多,但游戏仍然需要所有的视觉效果!尝试查看[Match-One示例](https://github.com/sschmid/Match-One)并查看是否可以在Unity中添加代表游戏中敌人和玩家的游戏对象。
我向您展示了Entitas-lang的一些功能。如果您想查看完整游戏,可以找到Match-One示例项目的.entitas文件here (converted by mazrks)
如果你知道如何阅读语言语法图( language grammars),你可能会对阅读语法感兴趣,可以在这里找到它