はじめに
面白いゲームを作りたい!と思ったら、試行錯誤をどうしても必要。
作ってみたけど、イマイチだったから、こう変えたい、なんて事は日常茶飯事です。
プログラマーは、降りてきた仕様をそのまま実装するのでなく、将来の仕様変更を見越しつつ、現実的な工数で、柔軟でメンテナンス性の高い、ソフトウェア設計をしなければなりません。
今まで私が経験してきた多くのプロジェクトは、オブジェクト指向による設計をしていました。
しかし、開発が進み、仕様変更を繰り返すうちに、いつしかスパゲッティコードになり、どのデータがどのデータに依存して変更されるのか、分からなくなっていく…
そのパターンがいつも繰り返されています。(読者の中にも、そんな方、いらっしゃるのではないでしょうか?)
Rust
今までC++やC#でのゲーム開発をしてきましたが、そもそもの言語設計が、オブジェクト指向をベースとしています。よってどうしても、考え方がオブジェクト指向から離れず、上に書いた様な問題に直面することは、避けられないように思います。
Rustは、関数型の考え方を取り入れた言語であり、強力なミュータビリティ(変数の変更可能・不可能)チェック機能により、プログラマーはデータの変更について、強制的に意識させられます。
(Rustの初学者は、このことが煩わしくて、返って毛嫌いしてしまう事も多いのですが、実はこれこそが、堅牢でバグの少ないコードを書くことに、大変有用なのです)
Rustでオブジェクト指向的な考え方で実装しようと思うと、ミュータビリティチェックにひっかかり、どうしてもうまく行かないことに気付かされます。
例えば、プレイヤーキャラを表すクラスを作ったとして、その中に、HPや攻撃力、バフ・デバフ、位置や、グラフィックの制御など、あらゆるデータを詰め込んでしまうと、ある機能はHPや攻撃力だけにフォーカスした処理なのに、クラスに他の情報が一緒になっているために、「あるオブジェクトを変更している間は、そのオブジェクトを他から参照してはならない」というRustのルールによって、コンパイルエラーになってしまう・・・といったことが起こります。
Rustでは、「モノ=オブジェクト」をベースとした設計では、うまく行きません。そうではなく「機能」をベースとして、それに必要なデータとは?と考えて、小さな機能を組み合わせることで、全体の仕組みを作っていく、という設計が求められます。
ECS
そこで、ECS(Entity-Component System) です。
ECSによる設計では、ある機能を実現するのに必要なデータを「コンポーネント」に分け、そのコンポーネントに「システム」側からアクセスし、データに変更を加えることで、機能を実現します。
例えば、「速度」コンポーネントと、「位置」コンポーネントがあったら、「移動」システムは、「速度」コンポーネントからデータを読み取り、「位置」コンポーネントのデータに読み取った速度を加算します。
これによって、移動の機能が実現するわけです。
ECSについて詳しくは、
https://ja.wikipedia.org/wiki/%E3%82%A8%E3%83%B3%E3%83%86%E3%82%A3%E3%83%86%E3%82%A3%E3%83%BB%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%83%BB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0
こちらなどを参考にしてください。
これからやりたいこと
ということで、今後何回かの投稿に分けて、Rust + ECS を用いて、ゲーム設計をするとどうなるのか、やってみたいと思います。
私もやりながら、勉強しながらなので、途中で、やっぱこうした方が良かった、なんてこともあるかと思いますが、お付き合いいただけると幸いです。
目次
その1 〜 序文
その2 〜 キャラの移動
その3−1 〜 コンポーネントの設計
その3−2 〜 システムの設計
その3−3 〜 メイン部分
その4−1 〜 剣を表示
その4−2 〜 アニメーションコンポーネント
その4-3 〜 アニメーションを動かす
その5-1~ あたり判定
その5-2~ やられアニメーション
その6 〜 これまでの振り返り
(2020.3.9 目次を追記しました)