Entitas|02 三消游戏
生成基础游戏页面
游戏面板
用于标记游戏的尺寸,应该是全局唯一的,所以加上[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();
}
}
输入控制
此时就需要一个新的组件来存储点击位置,他也应该是全局唯一的
[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);
}
}