内部仕様


フレーム制御

オートフレームスキップ

基本は60fpsであるが、処理能力が低い端末では実測で24fps程度である。
速度低下の原因は、主にグラフィック処理能力の限界の低さによるものである。
そのため、グラフィックを描写する処理を飛ばせば、ゲーム内部は高速に作動できる。

このゲームでは、前のフレームからどれだけ時間が経ったかを計測し、
経過した時間の分だけフレームを進めることで、ゲーム速度を維持している。
また、フレームは小数点以下も計算されており、フレームレートが変動しやすい状況でも
正確な間隔でゲーム内部のフレームを進めることができる。

ここで注意してほしいのが、24fpsしか出せない端末で実際に表示されているのが24フレームでも
ゲーム内部は60フレームのため、表示されていないフレームも計算に入れないといけないことである。

詳しくはこちらのソースを参照。


ブロック

詳しくはこちらのソースを参照。

ブロックの耐久値

ブロックの耐久値の初期値は、標準的なもので100である。これが0になると壊れる。

メタルブロック

仕様上、ライン判定からカラー巻き込みの間にしかダメージの処理がされない。

クリスタルブロック

仕様上、どんなダメージを受けても絶対に壊れない。しかし、存在フラグを折ることで消すことは可能。


ブロックの落下

加速度

加速ボタンを押さない時の速度が初速で、ゲーム内部の1フレーム当たりドット1つ分だけ加速する。
ただし、最大化表示にしているとわかりにくいため、ブロックの大きさと比較すると以下の通りである。
  • 小ブロック 1/10個分
  • 中ブロック 1/16個分
  • 大ブロック 1/20個分

クイックドロップ時の速度の計算

物理の方程式を用い、落下距離から衝突時の到達速度を算出している。


ブロックのダメージ

落下時のダメージ

ぶつかった地点から真下に減衰しながら伝わる。
ただし、その下にブロックがない場合、そのブロックの左右方向に衝撃が逃げる。
横に伝わる方向の減退の方がより大きい。

このような処理なのは、一番下のブロックが直下の空いた空間に逃げようとする力を、
横にあるブロックを引っ張って相殺するという想定からである。

この時、連結しているかどうかは関係ないのは、隙間の殆ど無いほどの密着による、
真空に近い状態で左右のブロックが引き寄せられることを想定しているからである。

それによって左右のブロックがダメージを受けるのは単に摩擦と引っ張りによる衝撃である。
また、一番下になるブロックだけに適用されるのは、
上のブロックが下のブロックの反発力で上に押し出されていると想定されているからである。

詳しくはこちらのソースを参照。

回転時のダメージ

ゲームの内部フレーム毎に、どれだけブロックがめり込んでいるかでダメージ判定を行なっている。

ダメージ判定は、ひとつのブロック当たり25個の点による判定に簡略化されている。
簡単に言うと、一個のブロックを 5×5 のブロックの塊とみなして判定をしている。
その 5×5 の仮想的なブロックの一つ一つのブロックの座標が、既存のブロックの座標と被るとダメージ判定となる。

また、ひとつの当たり判定では10の固定ダメージである。そのため、1フレーム当たり最大で250のダメージになる。
ブロックが完全に重なりあうようなケースの場合、確実にどちらかのブロックが壊れる。

また、回転時は1フレーム当たり15度回転するため、90度回転の場合は4回のダメージ判定となり、
最大で1000まで減る。しかし、ワープのような処理のため通過したはずなのにダメージがないこともある。

詳しくはこちらのソースを参照。

連鎖時のダメージ

連鎖時のダメージ判定は、移動させるのと同時に加速とダメージの判定を行う。
操作ブロックのダメージ判定と違い簡略化されていて、左右へのダメージ判定はない。

これは、落下する際に下にある空気が隙間に入り込むと想定されるため、
ダメージの入り方は単体同士の単純な衝突扱いとしたことによる。
ただし、玉突きのような上下への連動ダメージはある。

  • 具体的な処理の流れは以下のとおり。
浮いているブロックの連結をモードに合わせて地図にする

地図を元に、ブロックの塊を他のブロックにぶち当たるまで移動させ、
表示上の位置を保持したまま内部の位置を変える。

ブロックの内部の位置と表示上の位置が一致するように、表示上の位置を移動させる。
内部の位置と表示上の位置が一致したら衝突処理を行う。

詳しくはこちらのソースを参照。
※移動と衝突処理しか載っていません。


ブロックの連結と落下

色連結モード

左右と上に連結判定がある。ダメージの度合いと同じ色かどうかで判定が出る。
ブロックの耐久値が30%以下になった時、または連結方向のブロックの耐久の平均が50%を下回った時に連結が外れる。
同じ色ではない場合、連結していないという処理になる。

左右連結モード

単純に左右だけに判定がある。色連結モードと解除条件は似ているが、色による判定はない。
ブロックの耐久が30%以下になった時、または連結方向のブロックの耐久の平均が50%を下回った時に外れる。

ブロックの連結の地図

このゲームが多種多様な連結の切り替えを可能にしているのは、
ブロックがどのように連結しているかを示す地図を中間処理で作成するからである。

この中間生成は、最初は「ブロック1個を動かす度に連結判定を出すと重い」という現象の回避ためであった。
しかし、前後の互換性を考えなくても、連結状態の地図さえあれば柔軟に対応できることから、
複数の落下処理を一つのアルゴリズムで行えるようになった。

なお、下の図を見てもらえば分かる通り、連結状態の地図は
"連結状態のブロックを数字でレイヤー分け"しただけのモノである。

これ以上落ちないブロックは"レイヤー1"に指定され、落下処理ではスキップされる。
落下はレイヤーの数字ごとに行われ、最大でもレイヤーの数だけしか判定が起こらない。
もし、"レイヤー1"しかないなら、落下はスキップされる。

このようなレイヤーの最適化と落下の判定回数の減少で、落下処理が高速化した。

色連結時の空中停止バグ回避

さて、お気づきだろうか?
以下の図の状態の時に、ついている番号順に落下処理を行うと、
そのままでは 8 が 9 につっかかり、 9 は落ちるが 8 と 11 は空中で静止する。


これを回避するために、 8 と 9 を一緒に落下させる必要があるため、
8の番号のブロックは、下にあるブロックを巻き込んで一緒に落下する処理が入る。

また、上記の対策を付け加えても
「2層以上の連結解除状態の色ブロックを、C状の同じ色のブロックで挟むと落下しない。」
というバグが起こりえる。

このバグの回避のために、一番最後に
「浮いているブロックを、"すべてレイヤー2扱い"として落とす。」
という処理をしている。

ブロックの連鎖処理の流れ

ラインの判定

これ以上落ちないブロックの検索(レイヤー1の決定)

浮いているブロックの連結をモードに合わせて地図にする

レイヤー1までしかないなら、連鎖を終了する処理に移行 → ...

レイヤーごとにブロックの塊を他のブロックにぶち当たるまで移動させ、
表示上の位置を保持したまま内部の位置を変える。

ブロックの内部の位置と表示上の位置が一致するように、表示上の位置を移動させる。
内部の位置と表示上の位置が一致したら衝突処理を行う。

すべてのブロックの内部の位置と表示上の位置が一致したら連鎖カウンターを進める。

"ラインの判定"に戻る。


操作ブロック

操作ブロックの仕様

操作ブロックに使われるブロックは最大数は24個である。
また、8✕8の空間で表現できる用に作成されている。
※これを超えると、大ブロックオプションで使えなくなる形状が出るため。

操作ブロックの形状の生成

無数の形状の中から、約24万種類のある程度対称な形状をひな形として収録している。
たいていはそのひな形の形をそのまま使っているが、変わった形のオーダーがあった場合には、
その約24万種類から比較的扱いやすい形状を生成するようになっている。

操作ブロックの表現方法

  • システム上
システム上では、XとYの座標を収めた配列で管理する。
回転する際に、個数分の座標を操作するだけで済むため扱いやすい。
ただし、一つの座標セットでも最低1バイト必要であるため、容量が大きい。

もし、24万種類をこれで表現したなら、メモリを11MB使う事になる。


x_Ary → [ 0,-1,-1, 1]
y_Ary → [ 0, 0, 1, 0]

L字型だと内部では上のような表現になる。
この形は標準的なint型での表現の場合、32Byteのメモリを使う。

  • ライブラリ上
ブロックのライブラリ上では、64bit符号なし整数上にBool値の集合として表現する。
回転する際に固定で64フェーズが必要なため、高速作動には向かない。
8バイト固定のため、容量が少なくて済む利点がある。
約24万種類を納めても1.8MB程度しかメモリを使わない。


BlockHash → 0x00000107

上のように表現される。見慣れない者にとっては暗号でしかない。
このハッシュは、一番小さい値になるように格納される。
そのため、同じ形状が違うハッシュとして登場することはない。

また、ビット演算を用いれば既存の形と比較するのが容易であることと、
Hash値=形状であるため、膨大な個数の中でも出現した形状を追うことができる利点がある。


表示関係

ブロックの表示

このゲームのブロックの数はネット対戦時の最大で3500個を超える。
これをFlashPlayerに任せてそのまま並べただけでは、オブジェクトが多すぎて速度低下を招く。
(多分、そのまま3500個を並べた状態では、Core i7の4GHzでも10fps出るかどうか。)

そもそも、FlashPlayerの描写での速度低下の原因は以下のとおり。
  1. フレーム毎の描写更新が重い。
  2. オブジェクトの更新通知の検索が重い。
  3. オブジェクトのマウスやタッチへの当たり判定が重い。

そのため、ブロック専用の描写クラスを作り、1つのオブジェクトに集約して解決している。
このクラスのおおまかな処理は以下のとおり。
  1. すべてのパターンのブロックのBitMapデータをキャッシュを作成する。
  2. ブロックを並べる、表示用のBitMapを定義する。
  3. 要求があったら、表示用のBitMapにキャッシュからBitMapデータのコピーを取って並べる。

ぱっと見ると手間が増えているように見えるが、
ブロックは表示用のBitMapデータに変換されることによって表示されるようになるため、
表示用のBitMapデータをすべて作りきることによって、後はそれをコピーするだけになる。
これ以外にも、オブジェクトを集約した効果で反応が格段(200倍程度)に向上する。

  • 操作ブロックの表示
128×128pixelの大きさのBitmapを持つ。
操作ブロックの数だけ実態があり、一人用の場合は6つの実態が存在する。

  • ブロック溜まりの表示
160×320pixel+上下の40pixelの大きさのBitMapを持つ。
ブロック溜まりの数だけ実態があり、一人用の場合は1つの実態が存在する。


ブロック以外の表示

FlashPlayerのデフォルトの表示方法では、沢山の実態を作るため、速度が低下する。
そのため、ひとつの実態を共有して表示するクラスを作って最適化している。
7人対戦時でも、ブロックの表示以外の実態やコピーは増えない。

背景の表示

このゲームの背景は、背景リストによって管理されている。
"背景リスト"と言う名前ながら、実際にはスライドショークラスである。
これ自体は、大抵のOSに付属のスライドショーソフトと同等の機能を持つ。

エフェクトの表示

FlashPlayerのデフォルトの表示方法では、今表示している場面のキャッシュしか持てない。
エフェクトのような、何回も表示されるものを毎回1から描写するのはあまりにも無駄である。
無駄を省くために、すべてのフレームをキャッシュできるクラスを作って高速表示している。
また、エフェクトの中身のデータの実態を、1つに集約することでメモリを節約している。

また、内容が変わらないままで色が変わるタイプのエフェクトの時も、
キャッシュを操作せずに、無駄なキャッシュの再描写が起こることが確認されている。
これを回避するために、キャッシュを操作して色を変えるクラスを作成している。

  • エフェクト待機
連鎖数やボーナスなどをゆっくり見る時間を設けるため、
一部のエフェクトが発生している間はゲームのメイン処理そのものが止まる。
この間に何らかのキーを入力すると、次の処理開始時にその入力が反映されるため注意して欲しい。


通信対戦

対戦相手のゲーム内容の表示

このゲームの対戦相手のゲーム内容の表示は、プレイヤー同士をP2Pでつないでいる。
これは、ロビーサーバーの回線が混むのを避けるためである。

不正防止

このゲームでは、ゲームの途中経過をロビーサーバーで監視している。
定期的に送られてくるデータが途絶えたり、エラーの場合は部屋から追い出される。

ゲームの勝敗

ゲームの勝敗の決定はロビーサーバーで行う。
そのため、なるべくネットワークの遅延の少ない環境のほうが有利である。

おじゃまブロック

おじゃまブロックは、ロビーサーバー経由で通知される。
そのため、対戦相手と直接繋がらない状態でもおじゃまブロックは交換される。
だが、そのため表示とは少しラグがある。

ネットワーク

ロビーサーバーとの通信

ロビーサーバーとの接続には、TCPベースの独自プロトコルを使用する。
Zlib・RC4・AES・RSAなどをサポートし劣化SSLといえるような仕様。

4096bitのRSAを用いてAESの鍵を交換し、重要なデータはAESでやり取りする。
また、それぞれの暗号と圧縮は同一データに重ねても使用できる仕様となっている。
なお、一つのメッセージで16KBを超えるデータを受け取った場合は切断される。

なお、パケットの中身は以下のように構成される。
  • メッセージの長さ
  • イベントの名前
  • イベント用のデータ
比較的簡単な仕様なのでデバッグしやすく、基礎がTCPでなくとも問題ない。

対戦相手の表示

完全な同期データと、キー入力データを使って対戦相手を再現している。
完全な同期データは1230~2500Byteと重く、キー操作データは8~32Byteと軽い。
そのため、完全な同期データは、要求がない限りは伝達されることはない。

また、完全な同期データによる負荷を避けるため、最低でも3秒間隔でしか伝達しない。


操作関係

操作リスト

このゲームでは、操作を登録するリストを作り、ユーザーが操作を設定できるようにしている。
ただし、キーボードにないボタンは認識できないため、ゲームパッドは外部ツール必須。

なお、操作別にイベントのリストがあり、枠を増築して同じイベントを増やすこともできる。
しかし、無尽蔵に追加させる訳にはいかないので、残念ながら枠の数は固定されている。


BGM/SE関係

BGMリスト

BGMリストとは言っているものの、実際にはMP3プレイヤーそのものである。
FlashPlayerで再生できる対応形式がもっと多ければ、ありがたいのだが。

SEリスト

実は、内部では入れ替え可能な効果音の管理リストがある。
余裕があれば効果音をプレイヤーが選択できるように作られている。
しかし、次のような理由でこの機能はロックされている。

  • 効果音を自分で用意しているプレイヤーが居ない
  • リストで選択できるようにするには容量がいくらあっても足りない。
残念である。

他に内部仕様で知りたいことなど

※100文字まで入力できます。
  • なんかブロックの連結に違和感があると思ったら、
    >ブロックの耐久の平均が50%を下回った時に連結が外れる。
    これのせいか。 -- 名無しさん (2012-12-25 14:38:54)
名前:
コメント:
最終更新:2013年04月15日 01:07