前回の記事ではブロックの落下処理及び落下後に一面ブロックがそろっていたら消える処理を作りました。
前回の記事:
今回の記事では落下中のブロックを回転するようにしていきます。
スクリプトの内容は難しいものになっていますが、やっていきましょう!
ブロックの回転処理の作成
まず、ブロックを回転できるようにしていきましょう!
「BlockInstance」クラスを次のように修正してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
// BlockInstance.cs 回転処理の作成 //次のものを修正(public (int x, int y, int z) ToRotateCoordination(int x, int y, int z){~}全体を修正) public (int x, int y, int z) ToRotateCoordination(int localX, int localY, int localZ) { var p = rotaMat.MultiplyPoint(new Vector3(localX, localY, localZ) - Origin); p.x += p.x > 0 ? 0.5f : -0.5f; p.y += p.y > 0 ? 0.5f : -0.5f; p.z += p.z > 0 ? 0.5f : -0.5f; return ((int)p.x, (int)p.y, (int)p.z); } //次のものを追加 public Matrix4x4 rotaMat = Matrix4x4.identity; public void RotateForward(bool isLeft) { switch(Forward) { case Direction.Forward: switch(Up) { case Direction.Left: Forward = isLeft ? Direction.Down : Direction.Up; break; case Direction.Right: Forward = isLeft ? Direction.Up : Direction.Down; break; case Direction.Up: Forward = isLeft ? Direction.Left : Direction.Right; break; case Direction.Down: Forward = isLeft ? Direction.Right : Direction.Left; break; } break; case Direction.Back: switch (Up) { case Direction.Left: Forward = isLeft ? Direction.Up : Direction.Down; break; case Direction.Right: Forward = isLeft ? Direction.Down : Direction.Up; break; case Direction.Up: Forward = isLeft ? Direction.Right : Direction.Left; break; case Direction.Down: Forward = isLeft ? Direction.Left : Direction.Right; break; } break; case Direction.Left: switch (Up) { case Direction.Up: Forward = isLeft ? Direction.Back : Direction.Forward; break; case Direction.Down: Forward = isLeft ? Direction.Forward : Direction.Back; break; case Direction.Forward: Forward = isLeft ? Direction.Up : Direction.Down; break; case Direction.Back: Forward = isLeft ? Direction.Down : Direction.Up; break; } break; case Direction.Right: switch (Up) { case Direction.Up: Forward = isLeft ? Direction.Forward : Direction.Back; break; case Direction.Down: Forward = isLeft ? Direction.Back : Direction.Forward; break; case Direction.Forward: Forward = isLeft ? Direction.Down : Direction.Up; break; case Direction.Back: Forward = isLeft ? Direction.Up : Direction.Down; break; } break; case Direction.Up: switch (Up) { case Direction.Left: Forward = isLeft ? Direction.Forward : Direction.Back; break; case Direction.Right: Forward = isLeft ? Direction.Back : Direction.Forward; break; case Direction.Forward: Forward = isLeft ? Direction.Right : Direction.Left; break; case Direction.Back: Forward = isLeft ? Direction.Left : Direction.Right; break; } break; case Direction.Down: switch (Up) { case Direction.Left: Forward = isLeft ? Direction.Back : Direction.Forward; break; case Direction.Right: Forward = isLeft ? Direction.Forward : Direction.Back; break; case Direction.Forward: Forward = isLeft ? Direction.Left : Direction.Right; break; case Direction.Back: Forward = isLeft ? Direction.Right : Direction.Left; break; } break; } var deg = isLeft ? -90 : 90; var minPos = DirectionUtils.Rotate(MinPos, deg, Up); var maxPos = DirectionUtils.Rotate(MaxPos - Vector3Int.one, deg, Up); MinPos = Vector3Int.Min(minPos, maxPos); MaxPos = Vector3Int.Max(minPos, maxPos) + Vector3Int.one; rotaMat = Matrix4x4.Rotate(Quaternion.AngleAxis(deg, DirectionUtils.GetDirVec(Up))) * rotaMat; } public void RotateUp(bool isLeft) { switch(Up) { case Direction.Left: switch(Forward) { case Direction.Up: Up = isLeft ? Direction.Forward : Direction.Back; break; case Direction.Down: Up = isLeft ? Direction.Back : Direction.Forward; break; case Direction.Forward: Up = isLeft ? Direction.Down : Direction.Up; break; case Direction.Back: Up = isLeft ? Direction.Up : Direction.Down; break; } break; case Direction.Right: switch (Forward) { case Direction.Up: Up = isLeft ? Direction.Back : Direction.Forward; break; case Direction.Down: Up = isLeft ? Direction.Forward : Direction.Back; break; case Direction.Forward: Up = isLeft ? Direction.Up : Direction.Down; break; case Direction.Back: Up = isLeft ? Direction.Down : Direction.Up; break; } break; case Direction.Up: switch (Forward) { case Direction.Left: Up = isLeft ? Direction.Back : Direction.Forward; break; case Direction.Right: Up = isLeft ? Direction.Forward : Direction.Back; break; case Direction.Forward: Up = isLeft ? Direction.Left : Direction.Right; break; case Direction.Back: Up = isLeft ? Direction.Right : Direction.Left; break; } break; case Direction.Down: switch (Forward) { case Direction.Left: Up = isLeft ? Direction.Forward : Direction.Back; break; case Direction.Right: Up = isLeft ? Direction.Back : Direction.Forward; break; case Direction.Forward: Up = isLeft ? Direction.Right : Direction.Left; break; case Direction.Back: Up = isLeft ? Direction.Left : Direction.Right; break; } break; case Direction.Forward: switch (Forward) { case Direction.Left: Up = isLeft ? Direction.Up : Direction.Down; break; case Direction.Right: Up = isLeft ? Direction.Down : Direction.Up; break; case Direction.Up: Up = isLeft ? Direction.Right : Direction.Left; break; case Direction.Down: Up = isLeft ? Direction.Left : Direction.Right; break; } break; case Direction.Back: switch (Forward) { case Direction.Left: Up = isLeft ? Direction.Down : Direction.Up; break; case Direction.Right: Up = isLeft ? Direction.Up : Direction.Down; break; case Direction.Up: Up = isLeft ? Direction.Left : Direction.Right; break; case Direction.Down: Up = isLeft ? Direction.Right : Direction.Left; break; } break; } var deg = isLeft ? -90 : 90; var minPos = DirectionUtils.Rotate(MinPos, deg, Forward); var maxPos = DirectionUtils.Rotate(MaxPos - Vector3Int.one, deg, Forward); MinPos = Vector3Int.Min(minPos, maxPos); MaxPos = Vector3Int.Max(minPos, maxPos) + Vector3Int.one; rotaMat = Matrix4x4.Rotate(Quaternion.AngleAxis(deg, DirectionUtils.GetDirVec(Forward))) * rotaMat; } |
回転は3Dなので、複数の方向の回転があります。
3Dでは姿勢を表す方向に横と縦と前の3種類ありますが、その内の2種類の回転を実装するとだいたい問題なくオブジェクトを回転させることができます。
記事では次の方向を実装しています。
- 前方向を回転させるもの
- 上方向を回転させるもの
また、回転させる際には行列やクォータニオンなど数学の知識を使用していますが、そのあたりの説明は割愛します。
このあたりのゲーム数学の知識に関しては一冊本を持っておくといいかもしれません
「Unityでわかるゲーム数学」がわかりやすくておススメです。
この講座では基本的にUnityが用意しているメソッドを組み合わせて使用しています。
グリッド内の状態を見て回転できるかどうか判定する
ブロックの回転処理の基本ができましたので、次はグリッド内の状態を見て回転できるかどうかを判定できるようにします。
「Block」コンポーネントを次のように修正してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
// Block.cs グリッドの状態に合わせた回転処理の作成 //次のものを追加 public void RotateForward(Grid grid, bool isLeft) { Instance.RotateForward(isLeft); if(IsTouch(grid, Vector3Int.zero)) { Instance.RotateForward(!isLeft); } else { _forward = Instance.Forward; } transform.localRotation = Instance.rotaMat.rotation; } public void RotateUp(Grid grid, bool isLeft) { Instance.RotateUp(isLeft); if (IsTouch(grid, Vector3Int.zero)) { Instance.RotateUp(!isLeft); } else { _up = Instance.Up; } transform.localRotation = Instance.rotaMat.rotation; } //次のものを修正 private void Awake() { Pos = Pos; transform.localRotation = Instance?.rotaMat.rotation ?? Quaternion.identity; } |
回転操作の実装
ここまでできたら、次は回転操作を組み込むだけになります。
「TetrisSceneManager」スクリプトを次のように修正してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
// TetrisSceneManager.cs 回転操作の組み込み //次のものを修正 IEnumerator MainLoop() { var rnd = new System.Random(); while(true) { yield return null; var template = DropBlocks[rnd.Next(DropBlocks.Length)]; var block = Object.Instantiate(BlockPrefab, transform); block.SetTemplate(template); block.Pos = DropStartPos; //block.FillMass(Grid, false); while (!block.IsTouch(Grid, Vector3Int.zero)) { float t = 0f; while (t < DropSpeed) { t += Time.deltaTime; var offset = Vector3Int.zero; if (Input.GetKeyDown(KeyCode.LeftArrow)) offset.x += 1; else if (Input.GetKeyDown(KeyCode.RightArrow)) offset.x -= 1; if (Input.GetKeyDown(KeyCode.DownArrow)) offset.z += 1; else if (Input.GetKeyDown(KeyCode.UpArrow)) offset.z -= 1; block.Move(Grid, offset); if (Input.GetKeyDown(KeyCode.Q)) block.RotateForward(Grid, true); else if (Input.GetKeyDown(KeyCode.E)) block.RotateForward(Grid, false); if (Input.GetKeyDown(KeyCode.A)) block.RotateUp(Grid, true); else if (Input.GetKeyDown(KeyCode.D)) block.RotateUp(Grid, false); yield return null; } block.DropMove(1); } { var pos = block.Pos; pos.y++; block.Pos = pos; } block.FillMass(Grid); Object.Destroy(block.gameObject); CheckGridDepth(); } } |
コンパイルできたら再生して動作を確認してみてください。
キー入力に合わせてブロックを回転できるようになっていればOKです。
キー入力は次のようになります。
- Q : 前方向を左に向ける
- E : 前方向を右に向ける
- A : 上方向を左に回す
- D : 上方向を右に回す
まとめ
今回の記事では落下中のブロックを回転するようにしました。
回転操作は数学の知識が必要になり難しい感じになってますが、ここの改造や理解に関しては今回は読者のチャレンジ課題とします。
け、決して解説を始めたら終わらなくなるなんて理由では・・・ないですよ()
また操作自体も難しい感じになってしまってるので、キーを押すと予め設定しておいたブロックの形に変更するなどの仕様変更もいいかもしれません。
まとめると以下のようになります。
- 回転処理をブロック関係のスクリプトに追加した。
- 回転する時にグリッド内の状態を見て回転できるかどうか判定するようにした
それでは次の記事に行ってみましょう!
次の記事:
コメント