Unityで作るテトリス
こんにちは!ジェイです。パズルゲームの王道といえばテトリス!!ということで今回はテトリスの作り方を説明します。
セルを判定するクラス
1つのセルについて、判定や結合するためのクラスを用意します。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCell
{
const int MAX_X = 16;
const int MAX_Y = 14;
char[] Cell;
bool[] CellFlags;
int XSize, YSize;
public CCell()
{
XSize = MAX_X;
YSize = MAX_Y;
Cell = new char[XSize * YSize];
CellFlags = new bool[XSize * YSize];
}
public CCell(int xsize, int ysize)
{
XSize = xsize;
YSize = ysize;
Cell = new char[XSize * YSize];
CellFlags = new bool[XSize * YSize];
}
public CCell(string cell, int size)
{
int x = 0;
int y = 0;
for (int i = 0; i < size; i++)
{
switch (cell[i])
{
case '\n':
y++;
x++;
if (x > XSize) XSize = x;
if (y > YSize) YSize = y;
x = 0;
break;
default:
x++;
break;
}
}
Cell = new char[XSize * YSize];
CellFlags = new bool[XSize * YSize];
for (int i = 0; i < XSize * YSize; i++)
{
Cell[i] = cell[i];
CellFlags[i] = false;
}
}
//~CCell() { delete[] Cell; }
public void Init(string cell)
{
Cell = new char[XSize * YSize];
CellFlags = new bool[XSize * YSize];
for (int i = 0; i < XSize * YSize; i++)
{
Cell[i] = cell[i];
CellFlags[i] = false;
}
}
public char Get(int x, int y)
{
if (0 <= x && x < XSize && 0 <= y && y < YSize)
{
return Cell[x + y * XSize];
}
else
{
return ' ';
}
}
public bool GetFlag(int x, int y)
{
if (0 <= x && x < XSize && 0 <= y && y < YSize)
{
return CellFlags[x + y * XSize];
}
else
{
return false;
}
}
public void Set(int x, int y, char value)
{
if (0 <= x && x < XSize && 0 <= y && y < YSize)
{
Cell[x + y * XSize] = value;
}
}
public void Set(int x, int y, char value, bool flag)
{
if (0 <= x && x < XSize && 0 <= y && y < YSize)
{
Cell[x + y * XSize] = value;
CellFlags[x + y * XSize] = flag;
}
}
public void Set(int x, int y, CCell cell)
{
for (int i = 0; i < cell.GetYSize(); i++)
{
for (int j = 0, jn = cell.GetXSize(); j < jn; j++)
{
Set(x + j, y + i, cell.Get(j, i));
}
}
}
private void Set(int x, int y, int v)
{
throw new NotImplementedException();
}
public void And(int value)
{
int xs = GetXSize(), ys = GetYSize();
for (int y = 0; y < ys; y++)
{
for (int x = 0; x < xs; x++)
{
Set(x, y, Get(x, y) & value);
}
}
}
public void Or(char value)
{
int xs = GetXSize(), ys = GetYSize();
for (int y = 0; y < ys; y++)
{
for (int x = 0; x < xs; x++)
{
Set(x, y, Get(x, y) | value);
}
}
}
public void Shift(int vx, int vy)
{
int xs = GetXSize(), ys = GetYSize();
int fx, tx, dx, fy, ty, dy;
if (vx < 0)
{
fx = 0;
tx = xs - 1;
dx = -1;
}
else
{
fx = xs - 1;
tx = 0;
dx = 1;
}
if (vy < 0)
{
fy = 0;
ty = ys - 1;
dy = -1;
}
else
{
fy = ys - 1;
ty = 0;
dy = 1;
}
for (int y = fx; y != ty; y += dy)
{
for (int x = fx; x != tx; x += dx)
{
Set(x, y, Get(x - vx, y - vx));
}
}
}
public void Replace(char c0, char c1)
{
int xs = GetXSize(), ys = GetYSize();
for (int y = 0; y < ys; y++)
{
for (int x = 0; x < xs; x++)
{
if (Get(x, y) == c0) Set(x, y, c1);
}
}
}
public void Swap(int xa, int ya, int xb, int yb)
{
char c = Get(xa, ya);
char d = Get(xb, yb);
Set(xa, ya, d);
Set(xb, yb, c);
}
public void Merge(int x, int y, CCell cell)
{
for (int i = 0; i < cell.GetYSize(); i++ )
{
for (int j = 0; j < cell.GetXSize(); j++)
{
if (cell.Get(j, i) != ' ')
{
Set(x + j, y + i, cell.Get(j, i));
}
}
}
}
public bool Hit(int x, int y, CCell cell)
{
for (int i = 0; i < cell.GetYSize(); i++ )
{
for (int j = 0; j < cell.GetXSize(); j++)
{
if (cell.Get(j, i) != ' ' && Get(x + j, y + i) != ' ')
{
return true;
}
}
}
return false;
}
public void Clear(char value)
{
for (int i = 0; i < GetYSize(); i++ )
{
for (int j = 0, jn = GetXSize(); j < jn; j++)
{
Set(j, i, value);
}
}
}
public int GetXSize() { return XSize; }
public int GetYSize() { return YSize; }
}
テトリスの本処理
上記のクラスはセル1つ分なので、それらの機能を使って実装します。以下に必要な処理をまとめてみました。
- テトリスブロックの形を保存するセル
- 地形を保存するセル
- レバー入力か、落下タイマーが一定に達したら、ブロックを下げる
- 移動処理
- 落下判定処理
- 横一列揃ったらセルを消す
- 消した後にそのラインより上のブロックを下に落とす
- ブロックを再出現させる
ブロックの種類
・結合したあとのブロックは#
・消す予定のブロックは+
・最初からある地形のブロックは=
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
public static class CharExt
{
public static int ToInt(this char self)
{
return self - '0';
}
}
public class CTetorisu : MonoBehaviour
{
CCell StageCell = new CCell();
CCell[] BlockCell = new CCell[DROPPING_BLOCK_PATTERN_COUNT];
CCell[] CurrentBlock = new CCell[DROPPING_BLOCK_CELL_SIZE];
CCell[] NextBlock = new CCell[DROPPING_BLOCK_PATTERN_COUNT];
public float X, Y;
int CX, CY, VX, VY;
public int State;
int Time, DropTime, Turn;
bool PrevDown;
const int DROPPING_BLOCK_CELL_SIZE = 4;
const int DROPPING_BLOCK_PATTERN_COUNT = 28;
const int DROPPING_BLOCK_TURN_COUNT = 4;
const int DROPPING_BLOCK_FIELD_LEFT = 1;
const int DROPPING_BLOCK_FIELD_RIGHT = 11;
const int DROPPING_BLOCK_FIELD_TOP = 1;
const int DROPPING_BLOCK_FIELD_BOTTOM = 12;
const int DROPPING_BLOCK_NEXT_LEFT = 12;
// ブロックのパターン
string[,] DroppingBlockPattern = new string[DROPPING_BLOCK_PATTERN_COUNT,DROPPING_BLOCK_TURN_COUNT]
{
{
" ",
" ## ",
" ## ",
" ",
},
{
" ",
" ## ",
" ## ",
" ",
},
{
" ",
" ## ",
" ## ",
" ",
},
{
" ",
" ## ",
" ## ",
" ",
},
{
" ",
"####",
" ",
" ",
},
{
" # ",
" # ",
" # ",
" # ",
},
{
" ",
"####",
" ",
" ",
},
{
" # ",
" # ",
" # ",
" # ",
},
{
" ",
"## ",
" ## ",
" ",
},
{
" # ",
" ## ",
" # ",
" ",
},
{
" ",
"## ",
" ## ",
" ",
},
{
" # ",
" ## ",
" # ",
" ",
},
{
" # ",
" ## ",
" # ",
" ",
},
{
" ",
" ## ",
"## ",
" ",
},
{
" # ",
" ## ",
" # ",
" ",
},
{
" ",
" ## ",
"## ",
" ",
},
{
" # ",
" # ",
" ## ",
" ",
},
{
" ",
"### ",
"# ",
" ",
},
{
"## ",
" # ",
" # ",
" ",
},
{
"# ",
"### ",
" ",
" ",
},
{
" ## ",
" # ",
" # ",
" ",
},
{
" ",
"### ",
" # ",
" ",
},
{
" # ",
" # ",
"## ",
" ",
},
{
" ",
"### ",
"# ",
" ",
},
{
" # ",
"### ",
" ",
" ",
},
{
" # ",
" ## ",
" # ",
" ",
},
{
" ",
"### ",
" # ",
" ",
},
{
" # ",
"## ",
" # ",
" ",
}
};
GameObject emptyGameObject;
void Init()
{
int rand = UnityEngine.Random.Range(0, 7);
for(int i = 0; i < DROPPING_BLOCK_TURN_COUNT; ++i)
{
Debug.Log(rand*4 + i);
CurrentBlock[i] = NextBlock[rand*4+i];
}
NextBlock = BlockCell;//.OrderBy(i => Guid.NewGuid()).ToArray();
CX = (DROPPING_BLOCK_FIELD_LEFT + DROPPING_BLOCK_FIELD_RIGHT - DROPPING_BLOCK_CELL_SIZE) / 2;
CY = DROPPING_BLOCK_FIELD_TOP;
X = CX;
Y = CY;
State = 0;
DropTime = 0;
Turn = 0;
if (StageCell.Hit(CX, CY, CurrentBlock[Turn]))
{
State = 99999;
}
PrevDown = true;
emptyGameObject = new GameObject("Empty Game Object");
for (int y = 0; y < CurrentBlock[Turn].GetYSize(); y++)
{
for (int x = 0; x < CurrentBlock[Turn].GetXSize(); x++)
{
if (CurrentBlock[Turn].Get(x, y) == '#')
{
GameObject go = Instantiate(Cube2, new Vector3(x, CurrentBlock[Turn].GetYSize() - y, 0), Quaternion.identity);
go.transform.parent = emptyGameObject.transform;
}
}
}
emptyGameObject.transform.position = new Vector3(X, (DROPPING_BLOCK_FIELD_BOTTOM - Y-1), 0);
}
public GameObject Cube,Cube2;
public Material CubeMat;
private void Start()
{
string temp = string.Empty;
for (int i = 0; i < DROPPING_BLOCK_PATTERN_COUNT; i++)
{
temp = string.Empty;
BlockCell[i] = new CCell(DROPPING_BLOCK_CELL_SIZE, DROPPING_BLOCK_CELL_SIZE);
for (int j = 0; j < DROPPING_BLOCK_TURN_COUNT; j++)
{
temp += DroppingBlockPattern[i, j];
}
BlockCell[i].Init(temp);
}
NextBlock = BlockCell;
string[] str =
{
" \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"================\n",
};
temp = string.Empty;
for (int i = 0; i < str.Length; ++i)
{
temp += str[i];
}
StageCell = new CCell(temp, temp.Length);
for (int y = 0; y < StageCell.GetYSize(); y++)
{
for (int x = 0; x < StageCell.GetXSize(); x++)
{
if(StageCell.Get(x,y) == '=')
{
Instantiate(Cube, new Vector3(x, StageCell.GetYSize()-y, 0), Quaternion.identity);
}
}
}
Init();
}
private void Update()
{
if (State == 0)
{
DropTime++;
// レバーを下に入力するか、落下タイマーが一定値に達したら、ブロックを下げる
if ((Input.GetKey(KeyCode.DownArrow) && !PrevDown) || DropTime == 60)
{
// セル座標を更新したときに、ブロックがステージの壁や他のブロックに
// 当たっているか調べる
if (StageCell.Hit(CX, CY + 1, CurrentBlock[Turn]))
{
GameObject[] objs = GameObject.FindGameObjectsWithTag("MoveCube");
for(int i = 0; i < objs.Length; ++i)
{
objs[i].tag = "Finish";
objs[i].GetComponent<MeshRenderer>().material = CubeMat;
}
// 接触する場合には、ブロックのセルをステージのセルに合流する
StageCell.Merge(CX, CY, CurrentBlock[Turn]);
// 消去判定状態へ移行する
// 着地後のブロックを落としたいならStateを2の落下判定状態にする
State = 4;
}
else
{
// 接触しない場合には、落下タイマーとセル座標を更新する
DropTime = 0;
CY++;
VX = 0;
VY = 1;
Time = 0;
State = 1;
}
}// ブロックを左へ移動させる
else if (Input.GetKey(KeyCode.LeftArrow)
&& !StageCell.Hit(CX - 1, CY, CurrentBlock[Turn]))
{
CX--;
VX = -1;
VY = 0;
Time = 0;
State = 1;
} //ブロックを右へ移動させる
else if (Input.GetKey(KeyCode.RightArrow) && !StageCell.Hit(CX + 1, CY, CurrentBlock[Turn]))
{
CX++;
VX = 1;
VY = 0;
Time = 0;
State = 1;
}// ブロックを回転させる
else if (Input.GetKeyDown(KeyCode.Z))
{
if (!StageCell.Hit(CX, CY, CurrentBlock[(Turn + 1) % DROPPING_BLOCK_TURN_COUNT]))
{
Turn = (Turn + 1) % DROPPING_BLOCK_TURN_COUNT;
GameObject[] cubes = GameObject.FindGameObjectsWithTag("MoveCube");
int cnt = 0;
for (int y = 0; y < CurrentBlock[Turn].GetYSize(); y++)
{
for (int x = 0; x < CurrentBlock[Turn].GetXSize(); x++)
{
if (CurrentBlock[Turn].Get(x, y) == '#')
{
cubes[cnt++].transform.localPosition = new Vector3(x, CurrentBlock[Turn].GetYSize() - y, 0);
//GameObject go = Instantiate(Cube2, new Vector3(x, CurrentBlock[Turn].GetYSize() - y, 0), Quaternion.identity);
}
}
}
}
}
if (Input.GetKeyDown(KeyCode.DownArrow)) PrevDown = false;
}
// 移動状態の処理
if (State == 1)
{
Time++;
X = CX - VX * (1 - Time * 0.1f);
Y = CY - VY * (1 - Time * 0.1f);
emptyGameObject.transform.position= new Vector3( X, DROPPING_BLOCK_FIELD_BOTTOM - Y-1, 0);
// 移動し終わったら入力状態へ
if (Time == 10)
{
State = 0;
}
}
// 落下判定状態
if (State == 2)
{
// 落ちるブロックがまったくない場合には、消去判定状態へ移行する
State = 4;
// ステージ全体のセルを下から上に調べる
for (int x = DROPPING_BLOCK_FIELD_LEFT; x < DROPPING_BLOCK_FIELD_RIGHT; x++)
{
for (int y = StageCell.GetYSize() - 1; y >= DROPPING_BLOCK_FIELD_TOP; y--)
{
// 下に何もないブロックを捜す
if (StageCell.Get(x, y) == '+')//' ' && StageCell->Get( x, y-1 ) != ' ' )
{
// 下に何もないブロックがあったら、
// そのブロックと、それより上にある全てのブロックを下に移動する
for (; y >= DROPPING_BLOCK_FIELD_TOP; y--)
{
// セルがブロックかどうかを調べ、
// ブロックの場合には落ちるブロックとしてマークし
// 一段ずつ下に移動させる
char c = StageCell.Get(x, y - 1);
if (c != ' ')
{
StageCell.Set(x, y, c, true);//Get(c | 0x80));
}
else
{
StageCell.Set(x, y, ' ');
}
}
// タイマーを設定し落下状態に移行する
Time = 0;
State = 3;
}
}
}
}
// 落下状態
if (State == 3)
{
Time++;
// タイマーが一定値になったら、落下判定状態に移行し、
// 落ちるボールのマークを解除する
if (Time == 10)
{
for (int y = DROPPING_BLOCK_FIELD_TOP; y < StageCell.GetYSize(); y++)
{
for (int x = DROPPING_BLOCK_FIELD_LEFT; x < DROPPING_BLOCK_FIELD_RIGHT; x++)
{
// 落ちるボールのマークを解除する
StageCell.Set(x, y, StageCell.Get(x, y), false);// StageCell.Get(x, y) & 0x7f);
}
}
GameObject[] objs2 = GameObject.FindGameObjectsWithTag("Finish");
for (int i = 0; i < objs2.Length; ++i)
{
Destroy(objs2[i]);
}
for (int y = 0; y < StageCell.GetYSize(); y++)
{
for (int x = 0; x < StageCell.GetXSize(); x++)
{
if (StageCell.Get(x, y) == '#')
{
Instantiate(Cube, new Vector3(x, StageCell.GetYSize() - y, 0), Quaternion.identity);
}
}
}
State = 2;
}
}
// 消去判定状態
if (State == 4)
{
// ブロックがまったく消えなかった場合には、
// 再出現状態に移行する
State = 6;
// ブロックが隙間なく揃った段を消す
for (int y = DROPPING_BLOCK_FIELD_TOP; y < StageCell.GetYSize() - 1; y++)
{
int x;
for (x = DROPPING_BLOCK_FIELD_LEFT; x < DROPPING_BLOCK_FIELD_RIGHT; x++)
{
// 空のセルがある場合はループを抜け出す
if (StageCell.Get(x, y) == ' ') break;
}
// ブロックが揃った段がある場合の処理
if (x >= DROPPING_BLOCK_FIELD_RIGHT)
{
for (x = DROPPING_BLOCK_FIELD_LEFT; x < DROPPING_BLOCK_FIELD_RIGHT; x++)
{
StageCell.Set(x, y, '+');
}
// タイマーを設定し、消去状態へ移行する
Time = 0;
State = 5;
}
}
GameObject[] objs = GameObject.FindGameObjectsWithTag("Finish");
for (int y = 0; y < StageCell.GetYSize(); y++)
{
for (int x = 0; x < StageCell.GetXSize(); x++)
{
if (StageCell.Get(x, y) == '+')
{
for(int i = 0; i < objs.Length; ++i)
{
float y2 = objs[i].transform.position.y;
if(y2 == StageCell.GetYSize() - y)
{
objs[i].tag = "EraseCube";
}
}
//Instantiate(Cube, new Vector3(x, StageCell.GetYSize() - y, 0), Quaternion.identity);
}
}
}
}
// 消去状態の処理
if (State == 5)
{
Time++;
// 一定時間になったらブロックを消す
if (Time == 20)
{
GameObject[] objs = GameObject.FindGameObjectsWithTag("EraseCube");
for(int i =0; i < objs.Length; ++i)
{
Destroy(objs[i]);
}
// 落下判定状態に移行する
State = 2;
}
}
// 再出現状態
if (State == 6)
{
Init(); // 新しいブロックを出現させる
}
}
}
Unityエディタでの作業
Resourcesを作り、その中にCube1(地形と結合前)とCube2(地形と結合後)の二つをつくります。マテリアルを2種類作って、結合前と結合後の色をわかるようにしておきましょう。空のゲームオブジェクトを作ってCTetorisu.csを張り付ければ完成です!

実行結果
テクスチャを使ったテトリス
Cubeを生成しないでテクスチャをOnGUIで描画したバージョンのテトリスのコードも紹介します。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
public class CTetorisu : MonoBehaviour
{
CCell StageCell = new CCell();
CCell[] BlockCell = new CCell[DROPPING_BLOCK_PATTERN_COUNT];
CCell[] CurrentBlock = new CCell[DROPPING_BLOCK_CELL_SIZE];
CCell[] NextBlock = new CCell[DROPPING_BLOCK_PATTERN_COUNT];
public float X, Y;
int CX, CY, VX, VY;
public int State;
int Time, DropTime, Turn;
bool PrevDown;
const int DROPPING_BLOCK_CELL_SIZE = 4;
const int DROPPING_BLOCK_PATTERN_COUNT = 28;
const int DROPPING_BLOCK_TURN_COUNT = 4;
const int DROPPING_BLOCK_FIELD_LEFT = 1;
const int DROPPING_BLOCK_FIELD_RIGHT = 11;
const int DROPPING_BLOCK_FIELD_TOP = 1;
const int DROPPING_BLOCK_FIELD_BOTTOM = 12;
const int DROPPING_BLOCK_NEXT_LEFT = 12;
// ブロックのパターン
string[,] DroppingBlockPattern = new string[DROPPING_BLOCK_PATTERN_COUNT,DROPPING_BLOCK_TURN_COUNT]
{
{
" ",
" ## ",
" ## ",
" ",
},
{
" ",
" ## ",
" ## ",
" ",
},
{
" ",
" ## ",
" ## ",
" ",
},
{
" ",
" ## ",
" ## ",
" ",
},
{
" ",
"####",
" ",
" ",
},
{
" # ",
" # ",
" # ",
" # ",
},
{
" ",
"####",
" ",
" ",
},
{
" # ",
" # ",
" # ",
" # ",
},
{
" ",
"## ",
" ## ",
" ",
},
{
" # ",
" ## ",
" # ",
" ",
},
{
" ",
"## ",
" ## ",
" ",
},
{
" # ",
" ## ",
" # ",
" ",
},
{
" # ",
" ## ",
" # ",
" ",
},
{
" ",
" ## ",
"## ",
" ",
},
{
" # ",
" ## ",
" # ",
" ",
},
{
" ",
" ## ",
"## ",
" ",
},
{
" # ",
" # ",
" ## ",
" ",
},
{
" ",
"### ",
"# ",
" ",
},
{
"## ",
" # ",
" # ",
" ",
},
{
"# ",
"### ",
" ",
" ",
},
{
" ## ",
" # ",
" # ",
" ",
},
{
" ",
"### ",
" # ",
" ",
},
{
" # ",
" # ",
"## ",
" ",
},
{
" ",
"### ",
"# ",
" ",
},
{
" # ",
"### ",
" ",
" ",
},
{
" # ",
" ## ",
" # ",
" ",
},
{
" ",
"### ",
" # ",
" ",
},
{
" # ",
"## ",
" # ",
" ",
}
};
void Init()
{
int rand = UnityEngine.Random.Range(0, 7);
for(int i = 0; i < DROPPING_BLOCK_TURN_COUNT; ++i)
{
CurrentBlock[i] = NextBlock[rand*4+i];
}
NextBlock = BlockCell;//.OrderBy(i => Guid.NewGuid()).ToArray();
CX = (DROPPING_BLOCK_FIELD_LEFT + DROPPING_BLOCK_FIELD_RIGHT - DROPPING_BLOCK_CELL_SIZE) / 2;
CY = DROPPING_BLOCK_FIELD_TOP;
X = CX;
Y = CY;
State = 0;
DropTime = 0;
Turn = 0;
if (StageCell.Hit(CX, CY, CurrentBlock[Turn]))
{
State = 99999;
}
PrevDown = true;
}
//public GameObject Cube,Cube2;
public Material CubeMat;
private void Start()
{
string temp = string.Empty;
for (int i = 0; i < DROPPING_BLOCK_PATTERN_COUNT; i++)
{
temp = string.Empty;
BlockCell[i] = new CCell(DROPPING_BLOCK_CELL_SIZE, DROPPING_BLOCK_CELL_SIZE);
for (int j = 0; j < DROPPING_BLOCK_TURN_COUNT; j++)
{
temp += DroppingBlockPattern[i, j];
}
BlockCell[i].Init(temp);
}
NextBlock = BlockCell;
string[] str =
{
" \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"= = \n",
"================\n",
};
temp = string.Empty;
for (int i = 0; i < str.Length; ++i)
{
temp += str[i];
}
StageCell = new CCell(temp, temp.Length);
Init();
}
private void Update()
{
if (State == 0)
{
DropTime++;
// レバーを下に入力するか、落下タイマーが一定値に達したら、ブロックを下げる
if ((Input.GetKey(KeyCode.DownArrow) && !PrevDown) || DropTime == 60)
{
// セル座標を更新したときに、ブロックがステージの壁や他のブロックに
// 当たっているか調べる
if (StageCell.Hit(CX, CY + 1, CurrentBlock[Turn]))
{
GameObject[] objs = GameObject.FindGameObjectsWithTag("MoveCube");
for(int i = 0; i < objs.Length; ++i)
{
objs[i].tag = "Finish";
objs[i].GetComponent<MeshRenderer>().material = CubeMat;
}
// 接触する場合には、ブロックのセルをステージのセルに合流する
StageCell.Merge(CX, CY, CurrentBlock[Turn]);
// 消去判定状態へ移行する
// 着地後のブロックを落としたいならStateを2の落下判定状態にする
State = 4;
}
else
{
// 接触しない場合には、落下タイマーとセル座標を更新する
DropTime = 0;
CY++;
VX = 0;
VY = 1;
Time = 0;
State = 1;
}
}// ブロックを左へ移動させる
else if (Input.GetKey(KeyCode.LeftArrow)
&& !StageCell.Hit(CX - 1, CY, CurrentBlock[Turn]))
{
CX--;
VX = -1;
VY = 0;
Time = 0;
State = 1;
} //ブロックを右へ移動させる
else if (Input.GetKey(KeyCode.RightArrow) && !StageCell.Hit(CX + 1, CY, CurrentBlock[Turn]))
{
CX++;
VX = 1;
VY = 0;
Time = 0;
State = 1;
}// ブロックを回転させる
else if (Input.GetKeyDown(KeyCode.Z))
{
if (!StageCell.Hit(CX, CY, CurrentBlock[(Turn + 1) % DROPPING_BLOCK_TURN_COUNT]))
{
Turn = (Turn + 1) % DROPPING_BLOCK_TURN_COUNT;
}
}
if (Input.GetKeyDown(KeyCode.DownArrow)) PrevDown = false;
}
// 移動状態の処理
if (State == 1)
{
Time++;
X = CX - VX * (1 - Time * 0.1f);
Y = CY - VY * (1 - Time * 0.1f);
//emptyGameObject.transform.position= new Vector3( X, DROPPING_BLOCK_FIELD_BOTTOM - Y-1, 0);
// 移動し終わったら入力状態へ
if (Time == 10)
{
State = 0;
}
}
// 落下判定状態
if (State == 2)
{
// 落ちるブロックがまったくない場合には、消去判定状態へ移行する
State = 4;
// ステージ全体のセルを下から上に調べる
for (int x = DROPPING_BLOCK_FIELD_LEFT; x < DROPPING_BLOCK_FIELD_RIGHT; x++)
{
for (int y = StageCell.GetYSize() - 1; y >= DROPPING_BLOCK_FIELD_TOP; y--)
{
// 下に何もないブロックを捜す
if (StageCell.Get(x, y) == '+')//' ' && StageCell->Get( x, y-1 ) != ' ' )
{
// 下に何もないブロックがあったら、
// そのブロックと、それより上にある全てのブロックを下に移動する
for (; y >= DROPPING_BLOCK_FIELD_TOP; y--)
{
// セルがブロックかどうかを調べ、
// ブロックの場合には落ちるブロックとしてマークし
// 一段ずつ下に移動させる
char c = StageCell.Get(x, y - 1);
if (c != ' ')
{
StageCell.Set(x, y, c, true);//Get(c | 0x80));
}
else
{
StageCell.Set(x, y, ' ');
}
}
// タイマーを設定し落下状態に移行する
Time = 0;
State = 3;
}
}
}
}
// 落下状態
if (State == 3)
{
Time++;
// タイマーが一定値になったら、落下判定状態に移行し、
// 落ちるボールのマークを解除する
if (Time == 10)
{
for (int y = DROPPING_BLOCK_FIELD_TOP; y < StageCell.GetYSize(); y++)
{
for (int x = DROPPING_BLOCK_FIELD_LEFT; x < DROPPING_BLOCK_FIELD_RIGHT; x++)
{
// 落ちるボールのマークを解除する
StageCell.Set(x, y, StageCell.Get(x, y), false);// StageCell.Get(x, y) & 0x7f);
}
}
State = 2;
}
}
// 消去判定状態
if (State == 4)
{
// ブロックがまったく消えなかった場合には、
// 再出現状態に移行する
State = 6;
// ブロックが隙間なく揃った段を消す
for (int y = DROPPING_BLOCK_FIELD_TOP; y < StageCell.GetYSize() - 1; y++)
{
int x;
for (x = DROPPING_BLOCK_FIELD_LEFT; x < DROPPING_BLOCK_FIELD_RIGHT; x++)
{
// 空のセルがある場合はループを抜け出す
if (StageCell.Get(x, y) == ' ') break;
}
// ブロックが揃った段がある場合の処理
if (x >= DROPPING_BLOCK_FIELD_RIGHT)
{
for (x = DROPPING_BLOCK_FIELD_LEFT; x < DROPPING_BLOCK_FIELD_RIGHT; x++)
{
StageCell.Set(x, y, '+');
}
// タイマーを設定し、消去状態へ移行する
Time = 0;
State = 5;
}
}
}
// 消去状態の処理
if (State == 5)
{
Time++;
// 一定時間になったらブロックを消す
if (Time == 20)
{
// 落下判定状態に移行する
State = 2;
}
}
// 再出現状態
if (State == 6)
{
Init(); // 新しいブロックを出現させる
}
}
public Texture aTexture;
private void OnGUI()
{
int ey;
for (ey = CY; ey < StageCell.GetYSize(); ey++)
{
if (StageCell.Hit(CX, ey + 1, CurrentBlock[Turn])) break;
}
if (Event.current.type == EventType.Repaint)
{
Graphics.DrawTexture(new Rect(0, 0, 32, 32), aTexture);
// 周りのブロックを描画
for (int y = 0; y < StageCell.GetYSize(); y++)
{
for (int x = 0; x < StageCell.GetXSize(); x++)
{
char c = StageCell.Get(x, y);
switch (c)
{
// 結合したあとのブロックを描画
case '#':
Graphics.DrawTexture(new Rect(x*32, y*32, 32, 32), aTexture);
//DrawBox(x * 32, y * 32, x * 32 + 32, y * 32 + 32, GetColor(255, 0, 0), TRUE);
break;
// 消えるブロックの描画
case '+':
Graphics.DrawTexture(new Rect(x * 32, y * 32, 32, 32), aTexture);
//DrawBox(x * 32, y * 32, x * 32 + 32, y * 32 + 32, GetColor(0, 255, 0), TRUE);
break;
// 最初からある地形ブロックを描画
case '=':
Graphics.DrawTexture(new Rect(x * 32, y * 32, 32, 32), aTexture);
//DrawBox(x * 32, y * 32, x * 32 + 32, y * 32 + 32, GetColor(0, 0, 255), TRUE);
break;
default:
// 消える瞬間のブロック
if (StageCell.GetFlag(x, y))
{
Graphics.DrawTexture(new Rect(x * 32, y * 32, 32, 32), aTexture);
//DrawBox(x * 32, (y - 1 + Time * 0.1f) * 32,
// (x + 1) * 32, (y - 1 + Time * 0.1f + 1) * 32,
// Game->Color[0], TRUE);
}
break;
}
}
}
for (int y = 0; y < DROPPING_BLOCK_CELL_SIZE; y++)
{
for (int x = 0; x < DROPPING_BLOCK_CELL_SIZE; x++)
{
// 落ちているブロックを描画
if (State < 2 && CurrentBlock[Turn].Get(x, y) == '#')
{
// 予想地点の描画
//SetDrawBlendMode(DX_BLENDMODE_ALPHA, 85);
//DrawBox(CX * 32 + (32 * x), ey * 32 + (32 * y), CX * 32 + (32 * x) + 32, ey * 32 + (32 * y) + 32, GetColor(255, 255, 255), TRUE);
//SetDrawBlendMode(DX_BLENDMODE_NOBLEND, 255);
// 実際に落ちてるブロックの描画
Graphics.DrawTexture(new Rect((X+x) * 32, (Y+y) * 32, 32, 32), aTexture);
//DrawBox((X + x) * 32, (Y + y) * 32, (X + x + 1) * 32, (Y + y + 1) * 32, GetColor(255, 255, 255), TRUE);
}
// 次のブロックを描画
//if (NextBlock[0].Get(x, y) == '#')
//{
// DrawBox(
// (DROPPING_BLOCK_NEXT_LEFT + x) * 32 + 32, (1 + y) * 32,
// (DROPPING_BLOCK_NEXT_LEFT + x) * 32 + 32 + 32, (1 + y) * 32 + 32, GetColor(255, 255, 255), TRUE);
//}
}
}
}
}
}