生成基础游戏页面

游戏面板

用于标记游戏的尺寸,应该是全局唯一的,所以加上[Unique]

[Game,Unique]
public class BoardComponent : IComponent
{
    public int size;
}

关于游戏Item,对应三消游戏, 我们只要应该有以下组件

// 单纯用于标记Item
[Game]
public class GameItemComponent : IComponent
{
    
}

// Item关联的视图
[Game]
public class ViewComponent : IComponent
{
    public GameObject view;
}

// Item的坐标,为了 方便起见,我就以1 2 3为坐标
[Game]
public class PositonComponent : IComponent
{
    public Vector2Int pos;
}

// Item对应的Prefab名称,初始化时加载
[Game]
public class AssetComponent : IComponent
{
    public string assetName;
}

然后有一个对应的BoardSystem进行初始化

public class BoardSystem : ReactiveSystem<GameEntity>,IInitializeSystem
{
    private Contexts contexts;
    private Random random;
    private int boardSize = 10;
    
    public BoardSystem(Contexts contexts) : base(contexts.game)
    {
        this.contexts = contexts;
        random = new Random();
    }

    // 我们只关心所有拥有Position组件的GameItem
    protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context)
    {
        return context.CreateCollector(GameMatcher.AllOf(GameMatcher.GameItem, GameMatcher.Positon));
    }

    protected override bool Filter(GameEntity entity)
    {
        return entity.isGameItem && entity.hasPositon;
    }

    protected override void Execute(List<GameEntity> entities)
    {
       // 待完成
    }

    // 初始化,根据面板大小,初始化Item
    public void Initialize()
    {
        contexts.game.ReplaceBoard(boardSize);

        for (int i = 0; i < boardSize; i++)
        {
            for (int j = 0; j < boardSize; j++)
            {
                var entity = contexts.game.CreateEntity();
                entity.isGameItem = true;
                entity.AddAsset($"Item{random.Next(6)}");// 因为一共有6种Item
                entity.AddPositon(new Vector2Int(i, j));
            }
        }
    }
}

然后添加另外两个System

ViewSystem

public class ViewSystem : ReactiveSystem<GameEntity>
{
    private Transform parent;
    
    public ViewSystem(Contexts contexts) : base(contexts.game)
    {
        parent = new GameObject("Views").transform;
        parent.position=Vector3.zero;
    }
    
    protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context)
    {
        return context.CreateCollector(GameMatcher.Asset);
    }

    protected override bool Filter(GameEntity entity)
    {
        return entity.hasAsset && !entity.hasView;
    }

    protected override void Execute(List<GameEntity> entities)
    {
        foreach (var entity in entities)
        {
            var prefab = Resources.Load<GameObject>(entity.asset.assetName);
            var view = Object.Instantiate(prefab, parent);
            entity.AddView(view);
            view.Link(entity);
        }
    }
}

PositionSystem

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

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

    protected override bool Filter(GameEntity entity)
    {
        return entity.hasPositon && entity.hasView;
    }

    protected override void Execute(List<GameEntity> entities)
    {
        foreach (var entity in entities)
        {
            entity.view.view.transform.position = new Vector3(entity.positon.pos.x, entity.positon.pos.y, 0);
        }
    }
}

入口

此时添加一个游戏入口就能正常运行并生成

public class Enter : MonoBehaviour
{
   private Systems systems;
   private void Start()
   {
      var contexts = Contexts.sharedInstance;
      systems = new Feature("Systems")
         .Add(new BoardSystem(contexts))
         .Add(new ViewSystem(contexts))
         .Add(new PositionSystem(contexts));
      systems.Initialize();
   }

   private void Update()
   {
      systems.Execute();
      systems.Cleanup();
   }
}

image-20220427183915125

输入控制

此时就需要一个新的组件来存储点击位置,他也应该是全局唯一的

[Input,Unique]
public class InputComponent : IComponent
{
    public Vector2Int pos;
}

对应的我们写两个系统来控制输入

InputSystem

单纯只是获取当前点击到的位置

public class InputSystem : IExecuteSystem
{
    readonly Contexts contexts;

    public InputSystem(Contexts contexts)
    {
        this.contexts = contexts;
    }

    public void Execute()
    {
        if (Input.GetMouseButtonDown(0))
        {
            var mouseWorldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            var pos = new Vector2Int((int)Math.Round(mouseWorldPos.x), (int)Math.Round(mouseWorldPos.y));
            if (pos.x >= contexts.game.board.size || pos.x > contexts.game.board.size) 
                return;
            contexts.input.ReplaceInput(pos);
        }
    }
}

ProgressInputSystem

对我们的输入进行具体的逻辑操作

public class ProcessInputSystem : ReactiveSystem<InputEntity>
{
    readonly Contexts contexts;

    public ProcessInputSystem(Contexts contexts) : base(contexts.input)
    {
        this.contexts = contexts;
    }

    protected override ICollector<InputEntity> GetTrigger(IContext<InputEntity> context) => context.CreateCollector(InputMatcher.Input);

    protected override bool Filter(InputEntity entity) => entity.hasInput;

    protected override void Execute(List<InputEntity> entities)
    {
        var inputEntity = entities.SingleEntity();
        var input = inputEntity.input;
		// ...
    }
}

获取点击对象

当然现在还没啥用

我们需要获取我们点击到的那个Item,怎么做呢,当然可以通过射线检测来判断,但那就太麻烦了,因为现在所有物体的坐标都是规则且唯一的,所以我们完全可以直接根据坐标来查找

修改一下PositionComponent

[Game]
public class PositonComponent : IComponent
{
    [PrimaryEntityIndex]
    public Vector2Int pos;
}

此时就可以根据pos来查询entity了,不过要注意,通过这个标签标记的属性,不能够重复,类似的还有一个标签是[EntityIndex],是可以重复的

修改一下系统

public class ProcessInputSystem : ReactiveSystem<InputEntity>
{
    readonly Contexts contexts;

    public ProcessInputSystem(Contexts contexts) : base(contexts.input)
    {
        this.contexts = contexts;
    }

    protected override ICollector<InputEntity> GetTrigger(IContext<InputEntity> context) => context.CreateCollector(InputMatcher.Input);

    protected override bool Filter(InputEntity entity) => entity.hasInput;

   	protected override void Execute(List<InputEntity> entities)
    {
        var inputEntity = entities.SingleEntity();
        var input = inputEntity.input;
        var entity = contexts.game.GetEntityWithPositon(input.pos);
        Object.Destroy(entity.view.view);
    }
}

1