shipyard 0.11.2

Entity Component System
Documentation
# Spark


Let's infuse a bit of life into our friends.

```rust,noplaypen
use shipyard::{Component, IntoIter, Unique, UniqueView, UniqueViewMut, View, ViewMut, World};

const GROWTH_RATE: f32 = 0.15;
const MAX_SIZE: f32 = 25.0;

async fn main() {
    // -- SNIP --

        world.run(move_player);
        world.run(grow);
        world.run(render);

    // -- SNIP --
}


fn grow(mut vm_friend: ViewMut<Friend>) {
    for friend in (&mut vm_friend).iter() {
        let delta_size = (friend.0.size + GROWTH_RATE).min(MAX_SIZE) - friend.0.size;
        friend.0.size = friend.0.size + delta_size;
        friend.0.x = (friend.0.x - delta_size / 2.0).max(0.0);
        friend.0.y = (friend.0.y - delta_size / 2.0).max(0.0);
    }
}
```

`grow`'s code could be simpler but this version makes `Friends` grow from their center, which feels a lot more natural.

It appears our `Friends` want to come close to the `Player`, likely to give them a hug.

```rust,noplaypen
const SPEED: f32 = 1.5;

async fn main() {
    // -- SNIP --

        world.run(move_player);
        world.run(move_friends);
        world.run(grow);
        world.run(render);

    // -- SNIP --
}


impl Square {
    // -- SNIP --

    fn center(&self) -> Vec2 {
        vec2(self.x + self.size / 2.0, self.y + self.size / 2.0)
    }
}

fn move_friends(player: UniqueView<Player>, mut vm_friend: ViewMut<Friend>) {
    let mut dirs = vec![Vec2::ZERO; vm_friend.len()];

    for (friend, dir) in vm_friend.iter().zip(&mut dirs) {
        if friend.0.size <= player.square.size {
            continue;
        }

        let player_dir = player.square.center() - friend.0.center();

        *dir = player_dir.normalize();

        let mut neighbor_dir = Vec2::ZERO;

        for neighbor in vm_friend.iter() {
            if friend.0.center().distance_squared(neighbor.0.center())
                < friend.0.size * friend.0.size / 1.5
            {
                neighbor_dir +=
                    Vec2::new(friend.0.x - neighbor.0.x, friend.0.y - neighbor.0.y);
            }
        }

        *dir *= SPEED;

        *dir += neighbor_dir * 0.05;
    }

    let width = screen_width();
    let height = screen_height();
    for (friend, dir) in (&mut vm_friend).iter().zip(dirs) {
        if dir == Vec2::ZERO {
            continue;
        }

        friend.0.x = (friend.0.x + dir.x).clamp(0.0, width - friend.0.size);
        friend.0.y = (friend.0.y + dir.y).clamp(0.0, height - friend.0.size);
    }
}
```

As you can see, you can iterate views multiple times in the same system.\
We also prevent the `Friends` from overlapping by stirring them away from their neighbors.

But something doesn't feel right...

```rust,noplaypen
async fn main() {
    // -- SNIP --

        world.run(move_player);
        world.run(move_friends);
        world.run(grow);
        world.run(collision);
        world.run(render);

    // -- SNIP --
}

impl Square {
    // -- SNIP --

    fn collide(&self, other: &Square) -> bool {
        self.x + self.size >= other.x
            && self.x <= other.x + other.size
            && self.y + self.size >= other.y
            && self.y <= other.y + other.size
    }
}

fn collision(mut player: UniqueViewMut<Player>, v_friend: View<Friend>) {
    for friend in v_friend.iter() {
        if friend.0.size == MAX_SIZE && friend.0.collide(&player.square) {
            player.square.size -= 5.0 / 2.;

            if player.square.size < 5.0 {
                panic!("Murder");
            }
        }
    }
}
```

Oh my god! The "`Friends`" killed the `Player`!?