Entitas 十 Hello World 一个小例子

“Hello World”

“Hello World” 是一个典型的示例,用于说明所涉及技术的关键优势。对于编程语言,人们使用一个小程序,在控制台输出 Hello World。在 ECS 的大多数实现中,通常会从一个 MoveSystem 开始。

移动系统

移动系统是一个用于移动实体的系统。为了移动某些东西,这些东西需要具有位置和速度:

using Entitas;

[Game]
public sealed class PositionComponent : IComponent {
    public int x;
    public int y;
}

[Game]
public sealed class VelocityComponent : IComponent {
    public int x;
    public int y;
}

如果某物具有位置和速度,我们可以通过可移动组找到它。然后,我们可以遍历所有可移动物体,并将可移动物体的位置替换为其先前位置加上速度。

using Entitas;

public sealed class MoveSystem : IExecuteSystem {

    Group movableGroup;

    public MoveSystem(Contexts contexts) {
        var matcher = Matcher.AllOf(GameMatcher.Velocity, GameMatcher.Position);
        movableGroup = contexts.game.GetGroup(matcher);
    }

    public void Execute() {
        foreach(var e in movableGroup) {
            e.ReplacePosition(e.position.x + e.velocity.x, e.position.y + e.velocity.y);
        }
    }
}

这就是我们如何使用 Entitas 移动物体。这是一个非常简单的示例,但它说明了 ECS 的优势。这个系统可以找到每个相关的实体并移动它。我们不关心此实体附加了哪种其他组件,它可以是汽车、人、狗、房子,我们不关心。我们只是定义了,如果某物具有位置和速度,它将被移动。

减速系统

使用给定的移动系统,我们将保持以恒定速度移动事物,直到停止游戏/应用程序。然而,在大多数情况下,我们希望事物减速。让我们在一个单独的系统中实现它。

using Entitas;

public sealed class DecelerateSystem : IExecuteSystem {

    Group velocityGroup;

    public DecelerateSystem(Contexts contexts) {
        velocityGroup = contexts.game.GetGroup(GameMatcher.Velocity);
    }

    public void Execute() {
        foreach(var e in velocityGroup) {
            var velocityX = e.velocity.x / 2;
            var velocityY = e.velocity.y / 2;
            if (velocityX == 0 && velocityY == 0) {
                e.RemoveVelocity();
            } else {
                e.ReplaceVelocity(velocityX, velocityY);
            }
        }
    }
}

上述实现非常简单,它只是将速度减半,因此速度会很快变为零向量,当它变为零向量时,我们会将其整体移除。我不希望您在实际代码中编写此类内容,但它足以展示 ECS 的一个关键概念。让系统遵循单一责任原则是一个很好的主意。移动系统仅涉及_可移动_实体。减速系统涉及减少速度。我们甚至可以说,目前的减速系统实现具有两个职责。它减小速度,还将其删除。我们甚至可以进一步引入第三个 - 停止系统。这可以是一个响应式系统,在速度变为零向量时删除速度。

using Entitas;

public sealed class StopSystem : ReactiveSystem<GameEntity> {

    protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context) {
        return context.CreateCollector(GameMatcher.Velocity);
    }

    protected override bool Filter(GameEntity entity) {
        return entity.hasVelocity;
    }

    protected override void Execute(List<GameEntity> entities) {
        foreach (var e in entities) {
            if (e.velocity.x == 0 && e.velocity.y == 0) {
                e.RemoveVelocity();
            }
        }
    }
}

现在,我们可以简化减速系统,通过删除速度检查并仅用减小的速度替换当前速度。

using Entitas;

public sealed class DecelerateSystem : IExecuteSystem {

    Group velocityGroup;

    public DecelerateSystem(Contexts contexts) {
        velocityGroup = contexts.game.GetGroup(GameMatcher.Velocity);
    }

    public void Execute() {
        foreach(var e in velocityGroup) {
            var velocityX = e.velocity.x / 2;
            var velocityY = e.velocity.y / 2;
            e.ReplaceVelocity(velocityX, velocityY);
        }
    }
}

领悟

这一切都很有趣,但我仍然没有解释我的主要观点。我的__主要观点__是系统不直接彼此通信。在面向对象编程、函数式编程甚至过程式编程中,对象、函数或过程会直接并同步地“交谈”。在 ECS 中,系统仅查询状态并更改状态。它们彼此解耦。或者至少它们不知道彼此的存在。您可能会争辩说通过组件类型,事物会耦合在一起,但组件类型是数据驱动的。我们定义它们是为了反映我们要模拟的_“世界”_,因此__数据__成为我们的API

这是一种处理事物的独特方法,结果就是我所谓的 ECS 的第一法则 - __将状态与行为

分离__。这就是为什么我认为这值得成为_“Hello World”_的主要原因。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值