前回のUnityでブロック崩しをつくってみるで簡単なブロック崩しをつくってみましたが、立体のブロック崩しもあっていいのではないかと思いつくってみました。ボールも3次元空間を移動するタイプのものもつくってみましたが、うまくいきませんでした。ゲーム自体はできたのですが、実際にボールを跳ね返すのが難しすぎます。そこでボールは平面を移動して、立体構造になったブロックのかたまりが回転するタイプのものをつくってみました。

フィールドに表示するオブジェクトをつくる

まず周囲の枠をCubeからつくります。横幅は前回の半分にしました。またブロックが回転するため床はつくりません。回転は4つとも(0, 0, 0)です。

WallTop
位置 0,0,15.8
拡大率 16,1,0.5

WallBotom
位置 0,0,-15.8
拡大率 16,1,0.5

WallLeft
位置 -8,0,0
拡大率 0.5, 1, 32

WallRight
位置 8,0,0
拡大率 0.5, 1, 32

ボールはSphereから生成します。位置は移動するので適当、拡大率は(1, 1, 1)、回転は(0, 0, 0)です。マテリアルは適当な色を指定します。コンポーネントの追加から 物理 ⇒ リジットボディーを追加します。そして位置を固定のチェックボックスのY座標部分、回転を固定のチェックボックスのすべてにチェックをいれます。これをわすれるとプレイ中にボールが枠の外へ飛んでいってしまい、ゲーム続行不可になってしまいます。

それからAssetsのなかに物理マテリアルを追加し、これをBallにドラッグアンドドロップします。追加した物理マテリアルの項目のうち、DynamicFrictionとStaticFrictionを0に、Bouncinessを1に、BounceCombineをMaximにFrictionCombineをMinimumに設定します。

パドル(プレイヤー)は位置は(0, 0, -14)、回転は(0, 0, 0)、拡大率は(3, 1, 0.5)です。これもマテリアルは適当に設定します。

次にGameManagerオブジェクトを生成して、C#スクリプトでもGameManager.csを生成して両者を紐付けます。C#スクリプトが必要なのはBallとPlayerです。Ball.csとPlayer.csを生成して、それぞれのオブジェクトに紐付けます。

Playerを移動させるスクリプト

最初にPlayer.csを示します。方向キーが押されたらその方向に移動しますが、枠を突き破って移動しないように制限をかけています。

Ballを移動させるスクリプト

次にBall.csですが、インスペクターのタグにMiss、Block、Ballを追加しておいてください。そして一番手前の枠(WallBotom)のTagにMiss、パドルにPlayer、ボールにBallを設定しておいてください。

またBallのインスペクターのBallスクリプトのGameManagerの部分にGameManagerオブジェクトをドラッグアンドドロップしておきましょう。

ボールの速度に補正をいれる

ブロックが回転していることもあり、ボールの当たり方によっては変な跳ね方をしたり、速度が落ちてしまうことがあります。実際にやってみるとボールが最上段で止まってしまうことがあります。これではゲームにならないのでボールの速度に補正をいれます。それが自作メソッドのCorrectionBallです。

CorrectionBallメソッドではボールの速度を調べて遅くなりすぎている場合に速度調整をいれます。最初にフィールド変数 initVelocityを0にしておき、ボールに初速を与えるBallStart()メソッド内で速度を調べ、initVelocity == 0のときにこれを代入しておけば最初の速度を保存することができます。

またボールが跳ねたときにまっすぐ上に上がった場合、その先にブロックがないとボールは上下に往復するだけで、これまたゲーム続行不可になります。そこで速度のX成分とZ成分の割合を調べて、自作メソッド IsUnbalanceRatioVelocityXZメソッドであまりにもおかしな割合(これをアンバランスという言葉で表現していいのか?)のときは強制的に絶対値が1:1になるように設定しなおします。

ボールの進行方向によって適切に再設定しないといけないのですが、ここはそのときのX成分とZ成分を調べて三項演算子でリセットすべき速度に設定します。冒頭の動画ではこれをやらずになんでも rigidbody.velocity = new Vector3(1, 0, 1) * Speed;にしていました。そのためそのままブロックのなかにボールがめり込みやすくなり、右側のブロックのほうが壊れやすくなっています。

OnCollisionEnterメソッドではボールが何に当たったかで処理をわけています。GameManagerクラスの自作メソッドであるMiss、Hit、BlockBreakを呼び出していますが、これは前回作成したブロック崩しとたいした違いはありません。

GameManagerのスクリプト

次にGameManager.csをどうするかですが、そのまえに・・・

ブロックを生成する

まず回転するブロックをつくるのですが、これの元になるブロックをCubeでつくります。大きさは1でいいのですが、隙間なくならべてしまうとブロック同士の境界線がみえないので、拡大率は(0.95, 0.95, 0.95)と1よりも少し小さな値にしています。回転は(0, 0, 0)、位置は後で変更するので適当に設定して問題ありません。マテリアルは適当に設定しておきましょう(動画では赤にしています)。

まず最初におこなわれる処理の部分 Startメソッドとこれに関連する部分(メソッドとフィールド変数)をしめします。フィールド変数でBallとoriginalBlockをつかっているのでインスペクターの該当する部分に該当するオブジェクトをドラッグアンドドロップしておきましょう。originalBlockは上のパラグラフで作成したオブジェクトです。

Startメソッドのなかで自作メソッドであるBlocksInitを呼び出して、ブロックを生成しています。ゲームでは5× 5のブロックが16列回転します。そこで 25× 16のブロックをoriginalBlockをもとに生成します。生成後は必要ないのでSetActive(false)として表示されないようにします。

ブロックを回転させる処理

次にUpdateメソッド内で、上記の方法で生成したブロックを回転させる処理をおこないます。

自作メソッド RotateBlocksのなかでフィールド変数 angleを1増加させ、各ブロックの中心位置を計算します。

まずX座標を計算します。ブロックのインデックスからブロックが何列目になるかがわかります。真ん中の列がフィールドの中央に表示されるように左側に7.5平行移動させた位置がブロックのX座標です。25で割った商の整数部分が列であり、その剰余を5で割った商と剰余からY座標とZ座標も計算できます。これでX軸を中心にあつまったブロック群の初期の位置がわかります。

次にX軸を中心とした回転処理を考えます。ここは回転行列を使います。初期の座標と回転行列の積から回転後のブロックの中心座標がわかります。あとはこれを向こう側に平行移動(Z方向に8移動)させます。これでフィールド上で回転しているブロックの座標がわかります。あとは移動させるだけです。ブロック自身も回転しているのでその処理も忘れずに・・・。

点数表示 ミス・ゲームオーバー判定

次に点数表示とミス時の処理、ゲームオーバー判定をおこないます。

ゲームオーバーの表示と点数の表示ですが、ヒエラルキーで右クリック、UI ⇒ テキストを選択します。するとCanvasというオブジェクトが生成され、そのなかにTextというオブジェクトが表示されるので、名称をCanvasをGameUIに、TextをGameOvetTextに変更します。さらにヒエラルキーでGameUIを右クリックしてUI ⇒ テキストを選択してもうひとつTextオブジェクトを追加して、名前をScoreTextに変更します。

さらにGameUIを右クリックしてUI ⇒ ボタンを生成して、この名前をRetryButtonに変更します。GameManagerクラスにgameOverText、retryButton、scoreTextというフィールド変数を生成して、GameManagerのインスペクターのGameManagerスクリプトの下に表示されているGameOverText、RetryButton、ScoreTextの部分にヒエラルキーで作成したそれぞれのオブジェクトをドラッグアンドドロップします。

GameOverText、RetryButton、ScoreTextの設定ですが、

GameOvetText
位置 (0,40, 0)
幅・高さ (160, 30)
文字は Game Over
フォントサイズは48
中央寄せ
水平・垂直オーバーフロー Overflow

ScoreText
位置 (0,200, 0)
幅・高さ (160, 30)
文字は あとで変更するので「New Text」のままでOK
フォントサイズは24
中央寄せ
水平・垂直オーバーフロー Overflow

RetryButton
位置 (0,-20, 0)
幅・高さ (160, 30)
ボタンの色は適当でかまいませんが、クリック時の処理はGameManager.ReStartを指定してください。
RetryButtonの子オブジェクトにTextがありますが、そのテキストにボタンに表示させたい文字と文字色を設定しておいてください。

ScoreプロパティとRestプロパティで値が変更されたら表示も同時に変更されるようにしています。IsGameOverプロパティではtrueになったらGame Overの文字列とリトライのボタンをボタンを表示して、ゲームが開始されたらこれらを非表示にさせています。