【Java:テトリス制作】テトリミノの定義と出現、落下の実装

2018年10月23日

前の記事 テトリス制作Top 次の記事

今回はテトリミノの定義(回転状態を含む)と出現処理、自由落下の実装をやっていきます。



テトリミノの定義

テトリミノの定義は数値の二次元配列で定義します。1であればブロック描画、0であれば何も描画しない。

これに、回転した後の状態を3次元目に追加します。

今回は1次元目に回転、2次元目に縦軸、3次元目に横軸となるように定義してみました。

下図はT型テトリミノを定義したときです。

一応3次元データを持つオブジェクトをMinoクラスとして定義して利用することにします。


//テトリミノ(ブロックの塊)
class Mino{

	//1次元目=回転、2次元目=縦、3次元目=横
	int[][][] minos;

	Mino(int[][][] minos){
		this.minos = minos;
	}
}

//T
minos.add(new Mino(new int[][][] {
{
	 {1,1,1}
	,{0,1,0}
	,{0,0,0}
}
,{
	 {0,1,0}
	,{1,1,0}
	,{0,1,0}
}
,{
	 {0,1,0}
	,{1,1,1}
	,{0,0,0}
}
,{
	 {0,1,0}
	,{0,1,1}
	,{0,1,0}
}
}));

テトリミノの出現処理

テトリミノオブジェクトはArrayListに出現予定オブジェクトが入るように入るようにしていくので、ここからデータを取り出し、出現対象とします。

出現対象の1次元目を0(無回転状態)、2次元目を縦、3次元目を横としてみていき、1である場合にブロックを描画するようにします。

表示場所に関しては、x,yでフィールド表示開始配列番号を表すものを定義し、その開始番号からテトリミノの2次元目、3次元目をループで回します。

[0][y+縦軸のインデックス][x+横軸のインデックス]で参照し、1ならブロックを描画します。

コードにするとこんな感じですかね。



int[][][] minos = currentMino.minos;
for ( int i = 0;i < minos[0].length;i++ ) {
	for ( int j = 0;j < minos[0][i].length;j++ ) {
		if ( minos[0][i][j] != 0 ) {
			g.fillRect((j+x)*25, (i+y)*25,25,25);
		}
	}
}

テトリミノの自由落下

この実装はフレーム数をカウントして実装することにします。規定フレーム経過したら、yを1加算すればよいだけなので簡単です。(まぁ普通にタイマー使ったほうが簡単そうですが、個人的にあまりタイマーで処理させる方法が好きではないので、この方法を採用しています。)

fallFrame++;
if ( fallFrame >= 60 ) {
	fallFrame = 0;
	y++;
}

テストコード

今回のテストコードはこんなかんじになりました。

import java.util.ArrayList;
import java.util.Collections;

import com.nompor.gtk.APIType;
import com.nompor.gtk.GTK;
import com.nompor.gtk.GTKColor;
import com.nompor.gtk.GTKView;
import com.nompor.gtk.draw.GTKGraphics;

public class Test2 {

	public static void main(String[] args) {
		GTKView gv = new GTKView() {

			//テトリミノ(ブロックの塊)
			class Mino{

				//1次元目=回転、2次元目=縦、3次元目=横
				int[][][] minos;

				Mino(int[][][] minos){
					this.minos = minos;
				}
			}

			static final int BLOCK_SIZE=25;
			GTKColor black;
			Mino currentMino = null;
			ArrayList<Mino> nextMinos = new ArrayList<>();
			ArrayList<Mino> minos = new ArrayList<>();
			int[][] fields = new int[20][10];
			int fallFrame;
			int y;

			public void start() {
				black = GTK.createIntColor(0, 0, 0);


				//定義したミノの種類をリストに登録していく

				//I
				minos.add(new Mino(new int[][][] {
					{
						{0,0,0,0}
						,{1,1,1,1}
						,{0,0,0,0}
						,{0,0,0,0}
					}
					,{
						{0,1,0,0}
						,{0,1,0,0}
						,{0,1,0,0}
						,{0,1,0,0}
					}
				}));

				//L
				minos.add(new Mino(new int[][][] {
					{
						 {0,0,0,0}
						,{0,1,1,1}
						,{0,1,0,0}
						,{0,0,0,0}
					}
					,{
						 {0,0,0,0}
						,{0,1,1,0}
						,{0,0,1,0}
						,{0,0,1,0}
					}
					,{
						 {0,0,0,0}
						,{0,0,1,0}
						,{1,1,1,0}
						,{0,0,0,0}
					}
					,{
						 {0,1,0,0}
						,{0,1,0,0}
						,{0,1,1,0}
						,{0,0,0,0}
					}
				}));

				//J
				minos.add(new Mino(new int[][][] {
					{
						 {0,0,0,0}
						,{0,1,0,0}
						,{0,1,1,1}
						,{0,0,0,0}
					}
					,{
						 {0,0,0,0}
						,{0,1,1,0}
						,{0,1,0,0}
						,{0,1,0,0}
					}
					,{
						 {0,0,0,0}
						,{1,1,1,0}
						,{0,0,1,0}
						,{0,0,0,0}
					}
					,{
						 {0,0,1,0}
						,{0,0,1,0}
						,{0,1,1,0}
						,{0,0,0,0}
					}
				}));

				//T
				minos.add(new Mino(new int[][][] {
					{
						 {1,1,1}
						,{0,1,0}
						,{0,0,0}
					}
					,{
						 {0,1,0}
						,{1,1,0}
						,{0,1,0}
					}
					,{
						 {0,1,0}
						,{1,1,1}
						,{0,0,0}
					}
					,{
						 {0,1,0}
						,{0,1,1}
						,{0,1,0}
					}
				}));

				//O
				minos.add(new Mino(new int[][][] {
					{
						 {1,1}
						,{1,1}
					}
				}));

				//S
				minos.add(new Mino(new int[][][] {
					{
						 {0,1,1}
						,{1,1,0}
						,{0,0,0}
					}
					,{
						 {1,0,0}
						,{1,1,0}
						,{0,1,0}
					}
				}));

				//Z
				minos.add(new Mino(new int[][][] {
					{
						 {1,1,0}
						,{0,1,1}
						,{0,0,0}
					}
					,{
						 {0,1,0}
						,{1,1,0}
						,{1,0,0}
					}
				}));

				//テトリミノ全種をシャッフルし、二巡分だけnextに挿入しておく
				Collections.shuffle(minos);
				minos.stream().forEach(nextMinos::add);
				Collections.shuffle(minos);
				minos.stream().forEach(nextMinos::add);

				//現在のテトリミノを取得
				currentMino = nextMinos.remove(0);
			}

			public void draw(GTKGraphics g) {

				//自由落下の処理
				fallFrame++;
				if ( fallFrame >= 60 ) {
					fallFrame = 0;
					if ( y+currentMino.minos.length >= fields.length ) {
						y = 0;
						currentMino = nextMinos.remove(0);
						//出現予定テトリミノが減ってきたらシャッフルして次の一巡分を挿入
						if ( nextMinos.size() < 7 ) {
							Collections.shuffle(this.minos);
							this.minos.stream().forEach(nextMinos::add);
						}

					} else {
						y++;
					}
				}

				g.setColor(black);
				g.fillRect(0, 0, GTK.getWidth(), GTK.getHeight());
				for ( int i = 0;i < fields.length;i++ ) {
					for ( int j = 0;j < fields[i].length;j++ ) {
						//ブロックの描画
						if ( fields[i][j] != 0 ) {
							g.drawImage(Resource.b_green, j*BLOCK_SIZE, i*BLOCK_SIZE);
						}
					}
				}

				//現在落下中のミノの描画
				if ( currentMino != null ) {
					int[][][] minos = currentMino.minos;
					for ( int i = 0;i < minos[0].length;i++ ) {
						for ( int j = 0;j < minos[0][i].length;j++ ) {
							if ( minos[0][i][j] != 0 ) {
								g.drawImage(Resource.b_green,j*BLOCK_SIZE, i*BLOCK_SIZE+y*BLOCK_SIZE);
							}
						}
					}
				}
			}

		};

		GTK.start("TETRIS", 250, 500, new GTKView() {
			public void start() {
				Resource.load();
				GTK.runLater(()->{
					GTK.changeView(gv);
				});
			}
		},APIType.SWING);
	}
}
実行結果

全ソースはこちら

Java

Posted by nompor