I’m putting together a trivial little test project to experiment with Amethyst. I was advised to try out the legion_v2 branch, which apparently will become the “official” Amethyst “real soon now”, so that’s what I’ve been doing.
I’ve hit a wall with my very first System, and it’s trivial enough that I’m sure someone knows the answer, but I’ve now spent 2-3 hours banging my head against it.
Background: Basically, what’s going on is that I’m trying to build the world’s stupidest Tetris implementation ever. Because I want to experiment with Amethyst, I have totally over-engineered it. Specifically, the problem I have right now is:
- There is a Board entity, which represents the play area. It contains a sprite holding the outline of the play area, and a component with game state (such as which squares are filled). This is used when an overall picture of the board is needed (for instance, to find filled rows, or to check collisions with a falling piece).
- There is a Block entity, which represents a filled square. It contains a sprite (so I can draw the square) and an Entity reference to the Board. The Block’s component stores its location in “board coordinates” (where (0, 0) is the lowest-left square on the board grid). This is the source of truth for the block’s location; its Transform and Sprite are derived data that are used to place it on the screen.
I’m sure there are better ways to do Tetris. I chose this approach because it has some parallels with other projects I’m interested in trying out.
My very first System is called UpdateBlockTransform
. It is responsible for … updating the block transform. Specifically, the logic should be (pseudocode):
- For each Block
b
:- Set
b.Transform
tob.Board.Transform + (BlockDimensions.width * b.x, BlockDimensions.height * b.y)
.
- Set
That’s all well and good, but my efforts to implement this in code have been unsuccessful, because I can’t figure out how to get access to the Transform
component of b.Board
. Here is my first attempt:
SystemBuilder::new("UpdateBlockTransformSystem")
.with_query(<(&components::Block, &mut Transform)>::query()
.filter(maybe_changed::<components::Block>()))
.read_component::<Transform>()
.build(move |_commands, world, (), query| {
query.for_each_mut(world, |(block, local_transform)| {
let board_ref = world.entry_ref(block.board).unwrap();
let board_transform: &Transform = board_ref.get_component().unwrap();
*local_transform = board_transform.clone();
local_transform.prepend_translation_x(block.x as f32 * components::BLOCK_WIDTH);
local_transform.prepend_translation_y(block.y as f32 * components::BLOCK_HEIGHT);
})
})
This fails because world
is borrowed mutably by for_each_mut
, but immutably by entry_ref.
I also tried using a “get” API on queries, which is used by the unit tests of the new “system” API.
SystemBuilder::new("UpdateBlockTransformSystem")
.with_query(<(&components::Block, &mut Transform)>::query()
.filter(maybe_changed::<components::Block>()))
.read_component::<Transform>()
.build(move |_commands, world, (), query| {
query.for_each_mut(world, |(block, local_transform)| {
let mut transform_query = <&Transform>::query();
let board_transform : &Transform = transform_query.get(world, block.board).unwrap();
*local_transform = board_transform.clone();
local_transform.prepend_translation_x(block.x as f32 * components::BLOCK_WIDTH);
local_transform.prepend_translation_y(block.y as f32 * components::BLOCK_HEIGHT);
})
})
This ran into the same problem: world
is borrowed as both mutable and immutable.
I tried a few weirder options out of desperation, and spent a lot of time poring over the docs and code trying to figure out what was going on. None of them worked.
Combining data of two entities seems like a pretty basic functionality – without it, there are a bunch of things I just can’t do (without bypassing the ECS library entirely, of course). As far as I understand, it was available in specs
by just invoking get
or get_mut
on an appropriate *Storage
implementation. Has this been removed in legion
? Or am I misunderstanding how one of the libraries works?
Thanks!