menon 2024/06/27 22:41

【新作進捗】ピクセルシミュレーション

こんばんは!
ギリギリ6月に間に合いました。
今回はゲームの目指している方向性というかグラフィック性というか、そんな感じのものをお伝えできる内容な気がします!

進捗

タイトルにもなってますが今回の内容はピクセルシミュレーションです!
便宜的にピクセルシミュレーションと言っていますが、要はピクセル単位で物体をシミュレーションしようということです。
昔からそういう感じのゲームであったり動画であったりは色々ありましたが最近のゲームでいうとnoitaなんかが記憶に新しいと思います。

ピクセルというかドット絵で描かれているのにリアルっぽい動作で1ドット1ドットが動いている世界ってなんか感動しませんか?
あ、でももちろん昔ながらのRPGみたいに1マス1マス動くものも好きですよ!
どっちもすごい!

今やっていること

今はまだゲームとして実装するような段階ではなく、ゲームに実装可能な処理速度でのシミュレーションが実現できるかを試作しているテスト段階です。
いつもなら最適化はある程度ゲームが形になってきてから行うのですが今回は重ための処理になるため、実装が現実的な範囲まで自分の技術力で持っていけるかを先に確認しているわけです。
そして現在の処理がコチラ!

512x288で動かしています。いい感じの処理速度が実現できるかを優先しているので物体の動作をいい感じにするのは後回し。
処理速度については、左上のFPSと書いてある行の数値を見るとどのくらい処理が重くなっているのかがわかります
うーん、なるほど。よーく見てみると物体の自由落下時にかなりfps_realの値が落ちてしまっています。
これでも試作当初と比べたら全体的にはかなり軽くなったんですけどね…。強敵…

最後に

前回は主人公のsprite候補だけだったのでどんなゲームを目指しているのかがイマイチわかりませんでしたが、今回はなんとなくお伝えできたかなと思います!

次回更新ではもう少し最適化を進めて、シミュレーション対象に気体を追加、物質同士の相互作用的なものまで実装できたらいいなーと考えています。

オマケ

一つ処理を変えるだけでこんなに動きが変わる!

処理方法をちょこっと変えるだけでこんなにピクセルの動きが変わるんだよーというお話です。まあ当たり前といえば当たり前かもしれない。

さて、上の動画にあるようなピクセルの一連の動作は簡単に説明するとこんな感じで処理しています

① 下に動けたら動く
② 左右斜め下に動けたら動く
③ 左右に動けたら動く

もう少し詳細に説明すると
1フレーム毎に
・①と②は同時に処理可能(下に移動後、さらに斜め下に動ける)
・③は①と②を試しても動けなかった場合に初めて処理

されます。

というわけでここで①と②を同時に処理不可能にしてみます

これは微妙な違い…
でも接地後の横への広がりを上の動画と比べると違いがわかると思います。
明らかに遅い!少しの間、接地前の四角い形が色濃く残ってしまっています。

次に変更した部分を元に戻して、今度はさっき説明した部分の2つ目を変更して①と②と③すべてを同時に処理可能にしてみます

もう一目瞭然ですね!弾き飛んでます。
さっきの動画を見た後だと尚更です。
水の動きとしてはこれをもとに調整したほうがいい感じになりそうな気がします。

おー、すごかったですね。
処理方法をすこーし変えるだけでこんなに変わっちゃうなんて。
このめちゃくちゃどうでもいい感じ…さすがオマケクオリティ。

処理方法の遷移

どんな感じに最適化していったのかというお話です。
GameMakerというゲームエンジンを使用しているのでGameMakerで使用されている言語であるGMLでの説明になります。
また、全部書くと大変なので掻い摘んで書きます。

参考にしたもののURL

Exploring the Tech and Design of Noita
How To Code a Falling Sand Simulation (like Noita) with Cellular Automata
Pixel Simulation - tech demo and breakdown

そのほか、ピクセル単位ではないですが2D上の水シミュレーションで良さげな記事があったのでこれも貼っておきます。
2D Liquid Simulator With Cellular Automaton in Unity

下に書いてある内容を読むよりこれらの素晴らしい動画や記事を参考にしたほうが圧倒的に良いです。引き返すなら今のうち!

最適化

まずそもそもこういうタイプのゲームを作るのにGameMakerは向いているのかっていう話なんですが多分向いてません。
マルチスレッドで処理高速化!的なことはできません。
まあそこはGameMakerで作っている以上は考えても仕方がないので気合で乗り切ります。

その1
全てのピクセルをシミュレートする方法として直感的に思いつくのは、1フレームごとに512x288全てのマスを調べて各ピクセルのシミュレーションを行うことかもしれませんが、全てのマスというのは今回の場合512x288=147456マスにも及びます。
つまり各マスを調べるだけで1秒(60フレーム)ごとに8847360回もの処理をすることになります。
まあもう数字を見るだけでなんとなくやばそうですよね。
実際に動作させるとこうです。

パラパラ漫画です。というより紙芝居です。
というわけでいかに調べるマスを減らすか、というのがとても重要になります。

その2
それに対するアプローチとして上で紹介した動画でなされていた一部に
・チャンクに分けてピクセルがある場所のみ処理。
・動いていないピクセルを無視する

という2つがあり、簡易的に実装してみました。


赤色になっているピクセルは無視しているピクセルです。
ピンクの四角形有効になっているチャンクです。
全てのマスを調べる方法と比べると大幅にFPSが改善されています!
しかもなんか有効になっているチャンクが表示されているとカッコいい!!

ただ、この動画時点の処理だと無視しているピクセルといっても「動かそうとする」という処理をスキップしているだけです。そのマスを調べてはいるわけですね。
また、有効になっているチャンク内はピクセルの有無関係なく全てのマスを調べてしまっているのでそれもムダな気がします。

その3
それらの問題を解消するぞ!といって紆余曲折ありながらも生まれたのが今現在の処理方法なのですが、まあ簡単な話、「動いているピクセルだけ調べればいいよね」ってことです。

そりゃそうだろって感じなんですがコード的には、配列に動いているピクセルの座標のみを保有させてそれをループして処理を行っています。
ここのやり方が一番処理速度に関係してくると思うので今後も最適化していく部分なのかなとは思ってます。

それに加えて描写面でも最適化を行い、静止しているピクセルを一度だけsurfaceに描画してその後はそのsurfaceを描画するだけにしています。
わかりやすく言えば動いていない部分のスクリーンショットを一度だけ撮って、動いているピクセル以外はその画像一枚をずっと表示させとけばいいって感じです。

こうやって比較していくとだいぶ処理速度マシになってますね!
また、コンパイラーもVMよりYYCの方が速いのでデバッグ以外ではそっちの方が快適です。

このほか現状思いつくものでやっていない細かい最適化はforループよりrepeatの方が速い!だったりとか外部スコープをできるだけ参照しないようにする!だとかがあるのでそのあたりも取り組んでみようかなーと思ってます。
あとは自由落下中の処理最適化もお試し中です。

というわけで長かったオマケもここまでです。
ここまで読んでくれた方ありがとうございました!
読まなかった方もありがとうございました!

月別アーカイブ

限定特典から探す

記事を検索