ブロックバスター制作資料
TRANSCRIPT
ブロックバスター 制作資料By Max Neet Games
注意事項 ( 言い訳 )
技術面では適当で汚い実装が多いので、参考にすべきかは微妙な感じです。( なら何故公開する!?)
このスライドの内容は過去作やブログで解説した事はあまり載せていませんので、良ければそちらも参照ください。 Farm Fury! 技術資料 幽霊の棲む家技術資料
スライド概要 Max Neet Games について ゲーム紹介 開発環境 技術解説 使用アセット一覧
チーム情報 Max Neet Games
( 訳 : Hatarakitakunai-De-Gozaruuuu!!)
活動概要 まったりとゲーム作ってます。きっと、多分、恐らく 作ったゲームの資料も公開していく予定
ホームページ http://maxneet.web.fc2.com/
メンバー 大熊猫 : プログラム、エゴサーチ、惰眠担当 まる : アート、雑用担当
ブロックバスター紹介 プラットフォーム: Windows
プレイ人数 : 2~4人 ジャンル : 対戦アクション 想定プレイ時間 : 1試合35~125秒 コントローラー必須
開発環境
開発環境 ゲームエンジン
Unity5
ファイル管理 Dropbox 上に svn のリポジトリ作成し、 Dropbox のフォルダを共有
技術解説
入力周り
入力周り コントローラー必須と割り切ったので、基本は XBOX コントローラー基準
基準にしているだけで他でも動きます 下記の物を改良しました
http://deciduousgames.blogspot.jp/2013/05/xbox-controller-input-wrapper-for-unity.html
改良点は主に XNA に近づける感じです ( 改悪の可能性有り )
コントローラーの各ボタンの enum を定義し、ゲームの入力状態を保持する構造体 (GamepadState) に各状態を保持する Dictionary<GamePad.Button, bool> で各ボタン(スティックの向きを含む ) 各スティックの状態も保持
入力周り2 前のスライドを元に、入力管理クラスを作成 コントローラー4つを基準とし、各 GamePadState の現在のフレームと、前のフレームの状態を保持する
毎フレーム更新します 上記の状態を元に入力確認を行っております。
押されているか、このフレームに押されたか、離されたか 入力管理を別に作成したのはスティック方向も入力したフレームだけを確認したかったり、実装してませんがリピート入力も出来る様にしたかったからです。 ついでにリプレイでも使える様にしたかったからです。
キャラの入力 キャラに各キーのアサインを設定するコンポーネントをアタッチしてます コントローラーのインデックスも設定 中で入力可能なボタンの種類も定義しています。
キャラ入力コードの一部 public enum Type { Kick, Grab, Special, }
public GamePad.Index Controller = GamePad.Index.One;
public GamePad.Axis Move = GamePad.Axis.LeftStick;
public GamePad.Button KickButton = GamePad.Button.A;
public GamePad.Button GrabButton = GamePad.Button.B;
public GamePad.Button SpecialButton = GamePad.Button.Y;
キャラ入力確認If ( InputManager.Instance.IsPressed(
InputSettings.Controller, InputSettings.KickButton))
{// … 処理
}
キーコンフィグ やる事としては、前のスライドで紹介した入力設定 (InputSetting) に入る値を設定する
キーコンフィグ 確認方法はコントローラーに入力が有るかを確認 有った場合は同じボタンが使われているか確認 使われていた場合、今設定しているボタンにアサインされているボタンと交換する
無かったらそのまま設定 キーコンフィグを抜けたらセーブしています。
セーブには通常の PlayerPref を使用 ゲーム開始時にセーブデータの設定を読み込んでます
キャラの当たり
キャラの当たり 基本的に Unityの BoxCollider2D と
Rigid Body2D を使っています。 キャラには自身と子オブジェクトにコライダーを付けてます。 自身はブロックや他のキャラ等の当たり、子オブジェクトにはステージの壁だけに対する当たりを付けています。 壁の当たりを別にしているのはキャラの当たりだと、体の上の方で移動が停止してしまうからです。 子オブジェクトは別のレイヤー設定にし、プロジェクト設定で当たりの確認を壁以外とはしない様にしています。
ブロック連鎖
ブロック連鎖 ブロックが他のブロックとぶつかった時に、爆発するかの確認を行っています。 処理としては、そのブロックの周りに接触しているブロックがあるか確認。 同じ確認処理を接触しているブロックにし、それを繰り返す。接触したブロックをリストに保存し、それが一定数(3個)を超えた場合、それらを爆発させます。
ブロック連鎖処理1接触ブロックの確認には
Physics2D.OverlapAreaAll() を使用。 ブロックを専用のレイヤー設定にし、他のオブジェクトは確認しない様にしてます。 その中で同じ種類のブロックのみ接触リストに追加
ブロック連鎖処理2 ( 接触確認 )
public void SearchCollidingBlocks(Block currentBolck, BlockType chainBlockType, ref List<GameObject> chainList) { Vector3 adjustments = new Vector3(0.1f, 0.1f, 0.0f); BoxCollider2D collider = currentBolck.GetComponent<BoxCollider2D>(); Collider2D[] colliders = Physics2D.OverlapAreaAll(collider.bounds.min - adjustments, collider.bounds.max + adjustments, m_layer); if (colliders == null || colliders.Length <= 0) { // 周りにブロックが無い return; }
ブロック連鎖処理3 ( リスト追加 )
List<Block> toAdd = new List<Block>(); int count = colliders.Length; for (int i = 0; i < count; ++i) { Block block = colliders[i].GetComponent<Block>(); if (block == null || chainList.Contains(block.gameObject) || toAdd.Contains(block) ) { continue; }
if ( block.Type != chainBlockType ) { // 連鎖出来ない continue; }
toAdd.Add(block); chainList.Add(block.gameObject); }
ブロック連鎖処理4接触リストを追加した後は確認処理を再帰的に繰り返す
int count = toAdd.Count; for (int i = 0; i < count; ++i) { SearchCollidingBlocks(toAdd[i], chainBlockType, ref chainList); } }
ブロック連鎖処理5検索が終わったら実際に爆発させます
(検索処理呼び出し元) public void CheckChain() { List<GameObject> chainList = new List<GameObject>(); chainList.Add(gameObject); SearchCollidingBlocks(this, Type, ref chainList);
m_chainCount = chainList.Count; if (IsChain(m_chainCount)) { for (int i = 0; i < chainList.Count; ++i) { chainList[i].GetComponent<Block>().StartExplode(); } chainList.Clear(); } }
リプレイ 主に展示用に実装しました
デジゲー博では不具合が出て使えませんでしたが・・・ なので、最適化とかはまったく考えてない作り
保存している情報 毎フレーム入力状態を保存 ブロックの生成時に位置情報を保存 試合設定と人数(キャラを含め )を保存
ファイルがそこそこ大きくなるので、保存は別にスレッドを作って行っています。 書き込みはC#のSystem.IO周りの物を使って実装
再生時の処理 キャラと試合設定を記録から指定 入力確認処理の戻り値を記録した物に変更する ブロック生成時の位置指定を記録した物に変更する
リプレイ2 入力処理の上書き
private void UpdateCurrentState(int index) { if (Utility.IsPlayBack) { // リプレイ時 m_currentPadState[index] = BattleRecorder.Instance.GetPadState(index); } else { m_currentPadState[index] = GamePad.GetState((GamePad.Index)index + 1); } }
UI(ゲージ類) uGui + UniRx で実装
UniRx は試しに使ってみただけだったりします・・・ キャラに ReactiveProperty<float> を保持させ、ゲージから Subscribe()を呼んでゲージ更新処理を登録しています。
ゲージにキャラの番号情報が有るので、それを元に登録 これでキャラの情報が変わったらゲージ更新処理が呼ばれます。
ゲージの表示にはスライダーを使用しています 上記で追加したゲージ更新処理で Slider.Value を設定しています Value は0~1なので、現在の値 /最大値の答えを割り当てています
UIアニメーション
UIアニメーション 基本スクリプトでごり押し!移動は iTween で実装、色は自前
色の変更をする方法が分からなかったので、自作のTween処理で行いました 毎回書くのは面倒なので、基本は似たパターンを使うようにし、ある程度パラメーター弄って調整出来る様に実装
文字だけ色が変わらない問題位置やスケールは自動で反映されますが、色は子オブジェクトに反映されない為、画像と不一致になります。 解決策としては、アニメーションするオブジェクトに描画する子オブジェクトへの参照を持たせ、それらもアニメーションさせる Image や Text は UnityEngine.UI.Graphic継承しているので、 Graphic を探し、保持します
子供の色を更新する処理protected Tweener<Color> m_colourTweener;protected UnityEngine.UI.Graphic[] m_childUIElements;private void FindAllChildUI(){ m_childUIElements = GetComponentsInChildren<UnityEngine.UI.Graphic>();}
void Update(){ if (m_colourTweener.IsActive) { m_colourTweener.Update(Time.deltaTime); UpdateColour(); }}
protected override void UpdateChildColour() { int length = m_childUIElements.Length; for (int i = 0; i < length; ++i) { m_childUIElements[i].color = m_colourTweener.CurrentValue; }}
使用アセット スクリプト
iTween UniRx - Reactive Extensions for Unity
エフェクト: Cartoon FX Pack Cartoon FX Pack2
BGM: Game Music Pack – SUITE Tadashii Kamen no Tsukaikata OST
ボイス: Action Voice Pack - Nami Vol.1 Action Voice Pack - Nadeshico Vol.1