7. カベ際でも大丈夫!SRSとウォールキック入門
前回はミノの基本的な回転を学んだけど、「カベの近くや床の近くでうまく回せない!」って思った子もいるんじゃないかしら? そう、単純にその場でクルッと回るだけだと、どうしても限界があるの。そこで登場するのが、多くの現代テトリスで採用されている「SRS(スーパーローテーションシステム)」と、その一部である「ウォールキック」というテクニックよ!
これはね、ミノが回転した時にカベや床、他のブロックにぶつかっちゃいそうな場合、「ちょっとだけ位置をズラしてでも回転を成功させよう!」っていう、とっても賢い仕組みなの。まるでミノがカベを蹴って(キックして)回転するみたいだから、ウォールキックって呼ばれるのよ。
回転の状態とキックデータ
ウォールキックを実装するには、まずミノが「今どんな向きか」を覚えておく必要があるわ。これを「回転状態(rotation state)」と呼んで、0, 1, 2, 3 の数字で表すことにしましょう。
- 0: 初期状態 (0度)
- 1: 右に1回回転した状態 (90度)
- 2: 右に2回回転した状態 (180度)
- 3: 右に3回回転した状態 (270度)
そして、この回転状態から次の回転状態へ移る時に、どんな「ズラし方(オフセット)」を試すか、というデータが必要になるの。これが「キックデータ」あるいは「キックテーブル」よ。 SRSでは、ミノの種類(特にIミノと、それ以外のJ,L,S,T,Zミノ)と、現在の回転状態から次の回転状態への変化に応じて、試すべきオフセットのパターンが決まっているの。
例えば、J,L,S,T,Zミノ(Iミノ以外のミノたちね)が、状態0 (0度) から状態1 (90度) へ右回転する時には、最大5つの「テスト」を行うの。 各テストは (横方向のズレ, 縦方向のズレ) で表されるわ。
J,L,S,T,Z ミノのキックデータ例 (回転0→1 の場合):
テスト | オフセット (列, 行) | 説明 |
---|---|---|
1 | ( 0, 0) | まずはズラさずに回転できるか試す |
2 | (-1, 0) | ダメなら左に1マスずらしてみる |
3 | (-1,+1) | ダメなら左に1マス、下に1マスずらしてみる (Iミノは下向きが+なので注意) |
4 | ( 0,-2) | ダメなら上に2マスずらしてみる |
5 | (-1,-2) | ダメなら左に1マス、上に2マスずらしてみる |
※実際のSRSのオフセットは、(x, y) の符号や値が少し異なる場合があるわ。y軸が下向き正の場合、上にズレるのは y がマイナスになるわね。上の表はあくまでイメージよ!
Iミノは形が特殊なので、また別のキックデータを持っているの。 このテストを順番に試して、最初に回転が成功する(どこにもぶつからない)ズラし方を見つけたら、その位置で回転を確定させるのよ!
回転処理をパワーアップ!
前回作った tryRotateMino()
関数を、このウォールキックの考え方を使って大幅にパワーアップさせるわ。
- まず、現在のミノの形を単純に回転させる。
- 次に、現在のミノの種類(Iミノか、それ以外か)と、現在の回転状態から次の回転状態への変化に応じて、適切なキックデータのリストを取得する。
- キックデータのリストのオフセットを順番に試す。
- 回転後の形を、オフセット分だけズラした位置 (
currentMino.x + オフセットX
,currentMino.y + オフセットY
) に置けるか、canMinoMoveTo
でチェックする。 - もし置けるなら、ミノの位置をそのズレた位置に更新し、ミノの形も回転後のものに更新し、回転状態も新しい状態に更新して、回転成功!処理を終了する。
- 回転後の形を、オフセット分だけズラした位置 (
- どのオフセットを試してもダメだったら、回転は失敗。ミノの形も回転状態も元のまま。
// まずはミノに「種類」と「回転状態」を持たせるように spawnNewMino を変更するわ
// currentMino = { shape: ..., color: ..., x: ..., y: ..., type: 'I' or 'JLSTZ', rotationState: 0 };
// キックデータの定義 (SRSの標準的なものを参考に、右回転のみ)
// (dx, dy) : dxは右が+, dyは下が+
const KICK_DATA_JLSTZ = [ // 0: 初期状態, 1: R, 2: 2, 3: L
// 0 -> 1 (R)
[[0,0], [-1,0], [-1,+1], [0,-2], [-1,-2]],
// 1 -> 2 (R)
[[0,0], [+1,0], [+1,-1], [0,+2], [+1,+2]],
// 2 -> 3 (L)
[[0,0], [+1,0], [+1,+1], [0,-2], [+1,-2]],
// 3 -> 0 (L)
[[0,0], [-1,0], [-1,-1], [0,+2], [-1,+2]]
];
const KICK_DATA_I = [ // Iミノ用
// 0 -> 1 (R)
[[0,0], [-2,0], [+1,0], [-2,-1], [+1,+2]],
// 1 -> 2 (R)
[[0,0], [-1,0], [+2,0], [-1,+2], [+2,-1]],
// 2 -> 3 (L)
[[0,0], [+2,0], [-1,0], [+2,+1], [-1,-2]],
// 3 -> 0 (L)
[[0,0], [+1,0], [-2,0], [+1,-2], [-2,+1]]
];
function tryRotateMinoSRS() {
if (!currentMino) return;
const originalShape = currentMino.shape;
const originalRotationState = currentMino.rotationState;
const minoType = currentMino.type; // 'I' または 'JLSTZ'
// 1. まず形だけ回転
const rotatedShape = rotateMatrix(originalShape); // 前回作った関数
// 2. 次の回転状態を計算 (右回転なので +1 して 4で割った余り)
const nextRotationState = (originalRotationState + 1) % 4;
// 3. 適切なキックデータを選択
let kickTable;
if (minoType === 'I') {
kickTable = KICK_DATA_I[originalRotationState]; // 現在の状態から0->Rへ
} else {
kickTable = KICK_DATA_JLSTZ[originalRotationState];
}
// 注意: SRSのキックテーブルは「現在の回転状態」と「次の回転状態」のペアで定義されることが多いわ。
// 例えば 0->R, R->2, 2->L, L->0 など。今回は右回転のみなので、現在の状態をインデックスにしたわ。
// 4. キックテストを実行
for (let i = 0; i < kickTable.length; i++) {
const kick = kickTable[i];
const kickX = kick[0];
const kickY = kick[1]; // SRSではY軸が上向き正の場合があるので符号に注意。今回は下向き正。
// 試す位置
const testX = currentMino.x + kickX;
const testY = currentMino.y + kickY;
if (canMinoMoveTo(rotatedShape, testX, testY)) {
// 回転成功!
currentMino.shape = rotatedShape;
currentMino.x = testX;
currentMino.y = testY;
currentMino.rotationState = nextRotationState;
// playRotationSuccessSound(); // 回転成功音!
return; // ループを抜ける
}
}
// どのキックでもダメだったら回転失敗 (元のまま)
// console.log("ウォールキックでも回転できませんでした。");
playRotationFailSound(); // 前回作った失敗音
}
ふう、ちょっと複雑だったかしら?でも、このキックデータとテストのループが、あの滑らかな回転の秘密なのよ!
spawnNewMino
関数の中で、ミノが作られる時に type
(Iミノかそれ以外か) と rotationState
(最初は0) をセットしてあげる必要があるわね。
カベ際の攻防を体験!
さあ、いよいよ実践よ!下のCanvasで、ミノをカベ際や床の近くに持っていって回転させてみてちょうだい! 前回は「回転できない!」って言われた場面でも、今度はミノがちょっとズレてクルッと回ってくれることがあるはずよ。 これがウォールキックの力!Tスピンみたいな高度な技も、この仕組みが基本になっているの。
(ゲームの状態や操作方法が表示されるわ)
Summary of this page (Click to read in English)
This page introduces advanced rotation techniques: SRS (Super Rotation System) and Wall Kicks. These are used in modern Tetris games to allow Tetrominoes to rotate even when close to walls, the floor, or other blocks, by "kicking" or shifting them slightly.
We introduce a rotationState
(0-3) for the current Tetromino. Based on the Tetromino type ('I' or other 'JLSTZ' types) and the transition between rotation states (e.g., state 0 to state 1), a "kick table" provides a list of (x, y) offsets to try. The rotation process first rotates the shape, then tries each kick offset. If a kicked position is valid (no collision), the Tetromino's position, shape, and rotation state are updated. This allows for more flexible and intuitive rotations, forming the basis for techniques like T-spins.