5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Rust + Entity Component System で仕様変更に強いゲーム設計 その3−3 〜 メイン部分

Last updated at Posted at 2020-01-10

目次

その1 〜 序文
その2 〜 キャラの移動
その3−1 〜 コンポーネントの設計
その3−2 〜 システムの設計
【イマココ】その3−3 〜 メイン部分
その4−1 〜 剣を表示
その4−2 〜 アニメーションコンポーネント
その4-3 〜 アニメーションを動かす
その5-1~ あたり判定
その5-2~ やられアニメーション
その6 〜 これまでの振り返り

その3−1、3−2で、コンポーネントとシステムが出来上がりました。
では、それらを駆動するメイン部分である Game 構造体を作ってみます。

Game 構造体

このような作りにしました。

struct Game {
    next_entity_id: EntityID,
    inputs: CContainer<Input>,
    teams: CContainer<Team>,
    move_targets: CContainer<MoveTarget>,
    positions: CContainer<Position>,
    velocities: CContainer<Velocity>,
    character_views: CContainer<CharacterView>,
}

next_entity_id は、次に生成するエンティティのエンティティIDです。
エンティティを作るたびに、インクリメントしていきます。

他は、各コンポーネントのコンテナです。

次に、プレイヤーキャラを生成するメソッドと、敵キャラを生成するメソッドです。

impl Game {
    fn create_hero(&mut self) {
        let entity_id = self.next_entity_id;

        self.inputs.push(entity_id, Input::default());
        self.teams.push(entity_id, Team::new(0));
        self.positions.push(
            entity_id,
            Position {
                x: 150f32,
                y: 150f32,
            },
        );
        self.velocities.push(entity_id, Velocity::default());
        self.character_views.push(
            entity_id,
            CharacterView {
                color: Color::GREEN,
                radius: 10f32,
                ..Default::default()
            },
        );

        self.next_entity_id = self.next_entity_id + 1;
    }

    fn create_enemy(&mut self) {
        let entity_id = self.next_entity_id;

        self.move_targets.push(entity_id, MoveTarget::default());
        self.teams.push(entity_id, Team::new(1));
        self.positions
            .push(entity_id, Position { x: 10f32, y: 10f32 });
        self.velocities.push(entity_id, Velocity::default());
        self.character_views.push(
            entity_id,
            CharacterView {
                color: Color::RED,
                radius: 15f32,
                ..Default::default()
            },
        );

        self.next_entity_id = self.next_entity_id + 1;
    }
}

それぞれ、必要なコンポーネントを生成して追加しています。

次に、quicksilverの trait State の実装です。

impl State for Game {
    fn new() -> Result<Game> {
        let mut game = Self::default();
        game.create_hero();
        game.create_enemy();
        Ok(game)
    }

    /// Will happen at a fixed rate of 60 ticks per second under ideal conditions. Under non-ideal conditions,
    /// the game loop will do its best to still call the update at about 60 TPS.
    ///
    /// By default it does nothing
    fn update(&mut self, _window: &mut Window) -> Result<()> {
        System::process(
            &mut self.move_targets,
            &(&self.teams, &self.positions),
        );
        System::process(&mut self.velocities, &self.inputs);
        System::process(&mut self.velocities, &(&self.positions, &self.move_targets));
        System::process(&mut self.positions, &self.velocities);
        System::process(&mut self.character_views, &self.positions );
        Ok(())
    }
    /// Process an incoming event
    ///
    /// By default it does nothing
    fn event(&mut self, event: &Event, _: &mut Window) -> Result<()> {
        match event {
            Event::Key(key, state) => {
                let mut pressed = false;
                if *state == ButtonState::Pressed {
                    pressed = true;
                } else if *state == ButtonState::Released {
                    pressed = false;
                }
                match key {
                    Key::A => {
                        self.inputs.iter_mut().for_each(|(_, i)| {
                            i.left = pressed;
                        });
                    }
                    Key::D => {
                        self.inputs.iter_mut().for_each(|(_, i)| {
                            i.right = pressed;
                        });
                    }
                    Key::W => {
                        self.inputs.iter_mut().for_each(|(_, i)| {
                            i.up = pressed;
                        });
                    }
                    Key::S => {
                        self.inputs.iter_mut().for_each(|(_, i)| {
                            i.down = pressed;
                        });
                    }
                    _ => {}
                }
            }
            _ => {}
        }
        Ok(())
    }

    fn draw(&mut self, window: &mut Window) -> Result<()> {
        window.clear(Color::WHITE)?;
        System::process(window, &self.character_views);
        Ok(())
    }
}

Game 生成時に、プレイヤーキャラと敵キャラを生成するようにしました。

update() では、System::process() を順に更新するコンポーネントを引数として渡しながら、呼んでいます。
ただし、Window の更新だけは、draw() で行います。

event() の処理では、キー入力情報に従って Input コンポーネントを更新しています。

完成!

ここまでの実装で、プレイヤーキャラに加えて、敵キャラも実装することができました。
そして、エンティティ、コンポーネント、システムの基本的な設計が完了しました。

めでたしめでたし。

次からは、いろんな機能を追加しながら、この設計が、「仕様変更に強い」かどうかを、検証していきます。

次回につづく・・・

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?