2024年6月29日土曜日

JavaScriptでゲームエンジン作ってみた

ゲームエンジン作ってみた
出てきた芽をつつくと昇天する

プログラムで、芽が突かれたらと言う判定をしていると言うよりは芽が自分が突かれた時の処理として昇天するアニメへ切り替えている
つまり、プログラムが制御すると言うよりは
オブジェクトが自分のシナリオデータに沿って動く
オブジェクト指向であり、データドリブンと言うのはこう言うことだと言う例にもなっている
マウスのクリック効果は録画ソフトがつけているので ゲームエンジンで出してるわけではない (やろうと思えばすぐにできる) まだフレームレートの計算でよくわからないところがあって、 適当に調整してしまったんだけど まぁ動いてるからとりあえずいいか・・・ 計算合わないのがすっごい気持ち悪いんだけど・・・ アニメーションはAsepriteで作った 上に飛ばすときにもう一度クリックすることもできてしまうので、判定処理を抜くルーチンを実装してやらないといけない まぁゲームエンジンは大体できてきた


どうもゲームエンジンというものがどういう物かわからないという声が出てくるので説明しておく 画面に絵を表示するとき 絵は矩形で描画する サイズは32x32,16x16など、都合のいいドット数であることが多い 描画するときにX,Y座標を指定するが、 X,Y位置に左上を合わせて描画するのが左上基点 X,Y位置に画像中心を合わせて表示するのが中央基点 X,Y位置に画像の左右中央下端を合わせて表示するのがベース中央基点 まぁ呼び方なんかどうでもいい ベース中央基点は、対戦格闘などでよく使う方法で、キャラクターの地面の立ち位置を基点にして表示する方法だ この方が画面の左右スクロール時に位置がどこなのか考えやすい だが、中央もよく使う 例えば、マウスクリックしたところから四方八方に光を散らせるパーティクル効果を表現するときは、雪の結晶のような画像をX,Y位置に画像中心を合わせて描画する このように都合に合わせて、どこを基点に表示するのか変わってくる これらをプログラムで一々調整するのではなく、Asepriteなどのアニメーションデータで雪がチラつくようなアニメーションを組み、それを再生させながら四方八方に散らせて、最後はアニメーションの透明度で消えていく という制御を、画像側主体で動かすのがゲームエンジンの基本的な考え方だ つまり、画像が自分で自分の次のフレームの画像を切り替え、自分の位置を切り替える プログラムはその切り替えるアニメーションパターンや、四方八方に飛び散る物理演算を設定するだけで、あとは画像側が勝手に動く わたしが先に示した草を引っこ抜くゲームエンジンの例では 「Mebae」「Idle」「Pickup」のアニメーションパターンをAsepriteで作っている


インターバルで草を生成するルーチンは以下2つの指定をしてスプライトを生み出している 1.MebaeからIdleまで再生し、Idleはループさせるという指定でX、Y位置をランダム生成してスプライトを生成している 2.その際、当たり判定を物理演算の一種として登録している。 マウスクリックに当たっていたら「Pickup」へ切り替え、「Pickup」が終わったらスプライトは自己消滅するように指定している 全体のスプライト数もわかるので、60個までで草を生み出すのを止めている

        function isPickUp(spr) {
            if (spr.isTouched()) {
                // Pickupが終わったら消えるように設定
                spr.setTagNames(["Pickup","DIE"]);
            }
        }

        function getRandomInt(max) {
            return Math.floor(Math.random() * max);
        }

        async function dispatch() {
            // 最大60個にしておく
            if (gPrim.primitives.length <= 60) {
                // 乱数で座標を生成
                var x = 32 + getRandomInt(640 - 64);
                var y = 32 + getRandomInt(480 - 64);
                // ["Mebae", "Idle","REPEAT"]のように指定すると、Mebae、Idleの順に実行した後、Idleをリピートする
                // ["Mebae", "Idle","DIE"]の場合はIdleを実行した後Spriteが消える
                var sp = await Sprite.build("Futaba", jsMebae, ["Mebae", "Idle","REPEAT"], x, y, 1, 0);
                sp.addPhysic(isPickUp);
                gPrim.append(sp);

                console.log("dispatched", x, y)
            }
            nextTime = 3 + getRandomInt(5) * 1000;
            setTimeout(dispatch, nextTime);
        }


さて、ゲームエンジンは画面の表示位置だけを担当するのではない 当たり判定、繰り返し、消滅、親子関係(親に追従するかしないかの指定も可)などのほかに、音声再生なども行う 例えば、何もないスプライトを親にして、その子供としてダンジョンマップを登録する 上下左右の移動で、何もない親スプライトの位置を変えれば子供の位置が全部変わる つまりマップがスクロールする 例えばぷよぷよのぷよが、自分の上下左右に自分と同じぷよがいるかどうかを調べ、自分の体を変形させる(その際、変形用アニメも用意されていればスムーズに変形する) こういった、例えばをプログラムだけでやることの愚かさがわかるだろうか? ゲームエンジンを使えば、スプライトが自分で考えて自分の姿形を変え、消滅したり、新たなスプライトを生む出したりもできる ゲームエンジンのシナリオデータだけで、自分がやられたら、子供をたくさん生み出すような敵も作れる 子供は子供の攻撃パターンで動くこともできる 物理エンジンの一種として登録させることができる ゲームエンジンがあれば、
これらを全部同時に動かすことができる
RPGもシューティングも格闘ゲームもパズルも同じ組方で実装できる
ゲームの仕事が来た時に
その都度似たようなプログラムが組みたいならやればいい
わたしはその機種ごとにゲームエンジンを作ってからゲームを作る

ブラウザでよいのなら、ブラウザに特化したゲームエンジンを作り、
スマホにも対応させればよい
G123など、すでにやっているではないか?
あれを作ればいいだけのことだ

 

0 件のコメント: