休日の暇なときにVue.jsでテトリスを作ったお話です。
テトリスを作るのは今回が初めてで、基本的なロジック等々知らないまま思うがまま作っています。
コーディング等々適当なところもありますが参考にしていただき、またプレイしていただけると幸いです。
完成品
See the Pen
VwwZExv by b1san (@b1san1)
on CodePen.
コードペンのページはこちら
仕様について
正直な話、細かいテトリス仕様はよくわからずに作っています。なので細かいところは目をつむっていただければと思います。
スコアやレベルはかなり適当に設定しています。
基本ルール
10✕20のフィールドとなります。
フィールド上部からランダムな形状のテトリスミノが1つずつ落下してきます。
テトリスミノを配置していき、すべてテトリスミスで埋まったライン(横)は消え、消したラインの数によりスコアが加算されます。
フィールドの上部が埋まり、次のテトリスミノが配置できなくなったらゲーム終了です。
テトリスミノ
テトリスミノは次の7種類です。回転は、左(反時計回り)に90℃回転します。
See the Pen
PoooQoo by b1san (@b1san1)
on CodePen.
操作
次の操作が可能で、キー入力によって操作します。
操作 | キー | 説明 |
左移動 | ← | テトリスミノを左に移動させます。フィールド左端または移動先に配置済みテトリスミノがある場合は移動できません。 |
右移動 | → | テトリスミノを右に移動させます。フィールド右端または移動先に配置済みテトリスミノがある場合は移動できません。 |
ソフトドロップ | ↓ | テトリスミノを1つ下に移動させます。フィールド下端または移動先に配置済みテトリスミノがある場合は、テトリスミノをフィールドに設置する。 |
ハードドロップ | ↑ | テトリスミノを可能な限り真下に移動させ、テトリスミノをフィールドに配置します。 |
回転 | Space | テトリスミノを左回転させます。回転後のテトリスミノがフィールド外となるまたは配置済みのテトリスミノと重なる場合は回転できません。 |
ストック | Shift | 現在のテトリスミノをストックし、次のテトリスミノを落下させます。既にストックがある場合は、現在のテトリスミノとストックしているテトリスミノを入れ替えて落下させます。入れ替えた場合は再度フィールド上部から落下させます。 |
スコアとレベル
一度に消したラインの数によって次のスコアが加算されます。
ライン数 | スコア |
1 | 10 |
2 | 80 |
3 | 270 |
4 | 640 |
スコアが上がるにつれ、レベルが上昇していきます。レベルが上がるとテトリスミノの落下速度が上がります。レベルの上限は10です。
レベル | スコア | 落下速度 |
1 | 0 | 1s |
2 | 800 | 0.9s |
3 | 2700 | 0.8s |
4 | 6400 | 0.7s |
5 | 12500 | 0.6s |
6 | 21600 | 0.5s |
7 | 34300 | 0.4s |
8 | 51200 | 0.3s |
9 | 72900 | 0.2s |
10 | 10000 | 0.1s |
実装について
実装の解説については、個人的に重要だと思った点だけ行います。また細かい実装についてではなく、あくまで考え方の解説になります。
汚いコードかもしれませんが、コードペンでコードを見ることができますので、細かい点はそちらを参考にしてください。
あと解説で使用するコードと実際のコードは異なります。(許してください)
データの定義
次のようにデータを用意しています。
データ | 型 | 説明 |
テトリスミノ | Array | 2次元配列(縦✕横 = 5✕5)で、テトリスミノの形状をデータとして持ちます。 データはテトリスミノの種類によって次のようにします。
|
フィールド | Array | 2次元配列(縦✕横 = 20✕10)で、フィールドの状態をデータとして持ちます。 フィールドの座標(x, y)は、左上を(0, 0)とし、右下を(9, 19)とします。
|
現在のテトリスミノ(形状) | Array | 2次元配列(縦✕横 = 5✕5)で、現在落下しているテトリスミノの形状を持ちます。 |
現在のテトリスミノ(座標) | (Number, Number) | 現在落下しているテトリスミノの左上(0, 0)が、フィールドのどの位置(x, y)にあるかをデータとして持ちます。 |
次のテトリスミノ | Number | 次に落下させるテトリスミノの種類をデータとして持ちます。 |
ストック | Number | ストックしているテトリスミノの種類をデータとして持ちます。 |
フィールドの描画について
フィールドは2次元配列のデータなので、v-forディレクティブによって描画します。
<table> <tr v-for="(line, i) in field" :key="i"> <td v-for="(cell, j) in line" :key="j" /> </tr> </table>
ただし、画面に表示するフィールドは、データで定義しているフィールドではありません。
フィールドと現在のテトリスミノの形状と座標から、算出プロパティによって作り出したものを表示します。
これにより、テトリスミノの形状や座標が変更された場合に再計算され、あたかも動いているように見せることができます。
テトリスミノの色は、フィルターを使ったクラスバインディングによって行います。
<td v-for="(cell, j) in line" :key="j" class="mino" :class="cell | minoClass" />
filters: { minoClass(val) { switch(val) { case 1: return 'mino-i'; case 2: return 'mino-o'; case 3: return 'mino-t'; case 4: return 'mino-j'; case 5: return 'mino-l'; case 6: return 'mino-s'; case 7: return 'mino-z'; default: return ''; } } }
テトリスミノの回転
テトリスミノを左回転させる場合は、次のように要素の入れ替えを行います。ただし、O-テトリスミノは回転させません。
移動・回転の可否判定
移動後・回転後のテトリスミノが次の条件を満たす場合は、移動・回転可とします。
- テトリスミノの部分がフィールド外に出ない
- テトリスミノがフィールドに配置されているテトリスミノと重ならない
移動・回転不可の場合は、テトリスミノはそのままの状態となります。
ただし、下移動の場合はフィールドにテトリスミノを設置します。
ラインの消し方
ラインは次の手順で消します。
1.上から順に消すラインを探索します。
2.対象のラインが見つかったら、そのラインを上のラインに更新します。これをフィールドの最上部まで繰り返します。
3.フィールド最上部のラインをクリアします。
4.1~3をフィールドの最下部まで行います。
キー操作
Vue.jsのイベントハンドリング(v-on)は、対象の要素がアクティブである必要があるため、今回の実装には向いていません。
今回は次のようにイベントリスナーを設定します。
const app = new Vue({ //… methods: { handleKeydown(event) { //キー操作 } }, mounted() { window.addEventListenr("keydown", this.handleKeydown); }, beforeDestroy() { window.removeEventListener("keydown", this.handkeKeydown); } })
落下の設定
テトリスミノの落下は、setInterval()によって一定時間で落下するように設定します。落下を止めるために、setInterval()が実行される度にIntervalIdを保持するようにします。
const app = new Vue({ data: { intervalId: undefined }, methods: { //1マス下に移動 down() { //省略 }, //落下を開始 startDrop() { //1秒間に1マス下に移動 this.intervalId = setInterval(this.down, 1000); }, //落下を停止 stopDrop() { clearInterval(this.intervalId); } } })
おわりに
雑ですが大体8時間くらいかけて実装しました。
Vue.jsで作らないといけないことはないですが、個人的にはフィールドの描画は算出プロパティを使うことで楽に実装できたと思っています。
是非遊んでみてください。バグ等あれば報告いただければ幸いです。
- Vue.jsのおすすめ書籍はコチラ -
コメント