*

[Unity]Unity2Dチュートリアル 同じ色で繋がったパズルを消去する ソース解説

公開日: : C#, Unity, ソース, プログラミング

◆全文

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class GameSystem : MonoBehaviour {
	private const int TileLineNum = 5;
	private const int TileColNum = 6;
	private const float TileWidth = 0.5f;
	private const float TileHeight = 0.5f;
	private const float FirstTilePosX = -1.5f;
	private const float FirstTilePosY = -2;
	private int[,] tileTable = new int[TileLineNum,TileColNum];

	public GameObject holdObj;
	public float holdPositionX;
	public float holdPositionY;
	private float z = 10f;
	private GameObject[,] tileSet;

	// Use this for initialization
	void Start () {
		SetTileSet();

	}

	// Update is called once per frame
	void Update () {
		if(Input.GetMouseButtonDown(0)){
			LeftClick();
		}
		if(Input.GetMouseButton(0)){
			LeftDrag();
		}
		if(Input.GetMouseButtonUp(0)){
			LeftUp();
			DeleteMatchTile();
		}
	}

	private void LeftClick(){
		Vector3 tapPoint = new Vector3(Input.mousePosition.x, Input.mousePosition.y, z);
		if(holdObj == null){
			Collider2D col = Physics2D.OverlapPoint(Camera.main.ScreenToWorldPoint(tapPoint));
			if(col != null){
				this.holdObj = col.gameObject;
				holdPositionX = this.holdObj.transform.position.x;
				holdPositionY = this.holdObj.transform.position.y;
				holdObj.transform.position = Camera.main.ScreenToWorldPoint(tapPoint);
			}
		}

	}
	private void LeftDrag(){
		Vector3 tapPoint = Input.mousePosition;
		if(holdObj != null){
			this.holdObj.transform.position = Camera.main.ScreenToWorldPoint(new Vector3(tapPoint.x, tapPoint.y, z));
			Collider2D[] colSet = Physics2D.OverlapPointAll(Camera.main.ScreenToWorldPoint(new Vector3(tapPoint.x, tapPoint.y, z)));
			if(colSet.Length > 1){
				foreach(Collider2D col in colSet){
					if(!col.gameObject.Equals(this.holdObj)){
						float tmpPositionX = holdPositionX;
						float tmpPositionY = holdPositionY;
						holdPositionX = col.gameObject.transform.position.x;
						holdPositionY = col.gameObject.transform.position.y;
						col.gameObject.transform.position = new Vector3(tmpPositionX, tmpPositionY, z);
					}
				}
			}
		}
	}
	private void LeftUp(){
		if(holdObj != null){
			holdObj.transform.position = new Vector3(holdPositionX, holdPositionY, z);
			holdObj = null;
		}
	}
	private void SetTileSet(){
		GameObject[,] tileSet = new GameObject[TileLineNum,TileColNum];
		for(int i = 0; i < TileLineNum; i++){
			for(int j = 0; j < TileColNum; j++){
				Collider2D col = Physics2D.OverlapPoint(new Vector2(FirstTilePosX + TileWidth * j, FirstTilePosY + TileHeight * i));
				if("tile".Equals(col.tag)){
					tileSet[i,j] = col.gameObject;
				}
			}
		}

		this.tileSet = tileSet;
	}

	private void DeleteMatchTile(){
		int cnt = 0;
		for(int i = 0; i < TileLineNum; i++){
			for(int j = 0; j < TileColNum; j++){
				cnt = CompareHorizontal(i, j, cnt);
				cnt = CompareVertical(i, j, cnt);
			}
		}

		DeleteTile();
	}

	private int CompareHorizontal(int i, int j, int cnt){
		if(j + 1 < TileColNum){
			Sprite nowSprite = GetSprite(this.tileSet[i,j]);
			Sprite nextSprite = GetSprite(this.tileSet[i,j+1]);
			if(nowSprite.Equals(nextSprite)){
				if(tileTable[i, j] > 0){
					tileTable[i,j+1] = tileTable[i,j]; 
				}else{
					int cntInTile = ReCompareHorizontal(i, j, nowSprite);
					if(cntInTile > 0){
						tileTable[i,j] = cntInTile;
						tileTable[i,j+1] = cntInTile; 
					}else{
						cnt++;
						tileTable[i,j] = cnt;
						tileTable[i,j+1] = cnt; 
					}
				}
			}
		}
		return cnt;
	}

	private int ReCompareHorizontal(int i, int j, Sprite baseSprite){
		int result = 0; 
		if(j + 1 < TileColNum){
			Sprite nextSprite = GetSprite (this.tileSet[i, j+1]);
			if(baseSprite.Equals(nextSprite)){
				if(tileTable[i,j+1] > 0){
					result = tileTable[i, j+1];
				}else{
					result = ReCompareHorizontal(i, j+1, baseSprite);
				}
			}else{
				result = 0;
			}
		}else{
			result = 0;
		}
		return result;
	}

	private int CompareVertical(int i, int j, int cnt){
		for(int k = 1; i + k < TileLineNum; k++){
			Sprite nowSprite = GetSprite (this.tileSet[i,j]);
			Sprite nextSprite = GetSprite(this.tileSet[i+k,j]);
			if(nowSprite.Equals(nextSprite)){
				if(tileTable[i,j] > 0){
					tileTable[i+k, j] = tileTable[i, j];
				}else{
					cnt++;
					tileTable[i,j] = cnt;
					tileTable[i+k,j] = cnt;
				}
			}else{
				break;
			}
		}

		return cnt;
	}

	private Sprite GetSprite(GameObject obj){
		SpriteRenderer renderer = obj.GetComponent<SpriteRenderer>();
		return renderer.sprite;
	}

	private void DeleteTile(){
		for(int i = 0; i < TileLineNum; i++){
			for(int j = 0; j < TileColNum; j++){
				if(tileTable[i,j] > 0){
					Destroy(this.tileSet[i,j]);
				}
			}
		}
	}
}

◆解説
リファクタリングを行っていない、かつ考え方を実装するためのコードなのでかなり汚いです。

前回に比べての要点は以下です。

private int[,] tileTable = new int[TileLineNum,TileColNum];

消去するための情報を保持しておくフィールドを用意。
このテーブルに0以外が入っていたら消去します。

private int CompareHorizontal(int i, int j, int cnt){
	if(j + 1 < TileColNum){
		Sprite nowSprite = GetSprite(this.tileSet[i,j]);
		Sprite nextSprite = GetSprite(this.tileSet[i,j+1]);
		if(nowSprite.Equals(nextSprite)){
			if(tileTable[i, j] > 0){
				tileTable[i,j+1] = tileTable[i,j]; 
			}else{
				int cntInTile = ReCompareHorizontal(i, j, nowSprite);
				if(cntInTile > 0){
					tileTable[i,j] = cntInTile;
					tileTable[i,j+1] = cntInTile; 
				}else{
					cnt++;
					tileTable[i,j] = cnt;
					tileTable[i,j+1] = cnt; 
				}
			}
		}
	}
	return cnt;
}

横軸での比較です。隣のタイルをと比較して、同じスプライトだったら、数字を入れて、カウントアップしていきます。
また、「int cntInTile = ReCompareHorizontal(i, j, nowSprite)」で同じ色がある限り、どんどんテーブルの横を見ていって、既に数字が入っていないかを確認しています。
状況的には↓のようなときです。
Unity_
このようなときに、右を評価して、そのまま数字を入れるとカウントアップされた3が入ってしまいます。
ですので更に横を評価して同じであればその数字を取得しています。
ちなみに、数字が入っていなければカウントアップします。

private int ReCompareHorizontal(int i, int j, Sprite baseSprite){
	int result = 0; 
	if(j + 1 < TileColNum){
		Sprite nextSprite = GetSprite (this.tileSet[i, j+1]);
		if(baseSprite.Equals(nextSprite)){
			if(tileTable[i,j+1] > 0){
				result = tileTable[i, j+1];
			}else{
				result = ReCompareHorizontal(i, j+1, baseSprite);
			}
		}else{
			result = 0;
		}
	}else{
		result = 0;
	}
	return result;
}

上記の横を見て、そのまた横を見て、を実装したメソッド。
result = ReCompareHorizontal(i, j+1, baseSprite);で再帰してます。
再帰というのは関数の中でその関数を呼び出すもの。
この場合ですとこんな感じ。
Unity_
重要なのは赤でアンダーラインをつけた、jに1足していること。
慣れれば簡単でシンプルに書けるのでお勧めです。

private int CompareVertical(int i, int j, int cnt){
	for(int k = 1; i + k < TileLineNum; k++){
		Sprite nowSprite = GetSprite (this.tileSet[i,j]);
		Sprite nextSprite = GetSprite(this.tileSet[i+k,j]);
		if(nowSprite.Equals(nextSprite)){
			if(tileTable[i,j] > 0){
				tileTable[i+k, j] = tileTable[i, j];
			}else{
				cnt++;
				tileTable[i,j] = cnt;
				tileTable[i+k,j] = cnt;
			}
		}else{
			break;
		}
	}
	return cnt;
}

今度は縦方向の比較。
縦方向の場合、すぐ上よりも上に数字があることはありえないので楽なコードですね。

private Sprite GetSprite(GameObject obj){
	SpriteRenderer renderer = obj.GetComponent<SpriteRenderer>();
	return renderer.sprite;
}

一応追加したので、ここにも書いておきます。
スプライトを取得する処理を何度も書くのが嫌だったのでメソッド化。
処理速度とはトレードオフだと思うのでご注意。

元の記事に戻る

ad

関連記事

Unity_

[Unity]Unity 2Dチュートリアル スプライトをクリック(タップ)して削除する

前回、スプライトを置くことが出来たのですが、動かせるようにしたいところです。 ドラッグして動

記事を読む

Unity_

[Unity]Unity2Dチュートリアル スプライトを利用する その2

前回で、タイルを置いて、全部消えちまうじゃねぇか! 色一個一個つけるのめんどくせぇ!! というところ

記事を読む

unity

[Unity]コードリファレンス GameObjectを親子関係にする。

親子関係にして、親が動いたら子も動くような動きをさせます。 transform.parentプ

記事を読む

logo

プログラミング概論 導入

プログラミングは基礎から覚えた私ですので、やはり基礎から書いていきます。 よくプログラミングのサイ

記事を読む

Wannabenote

[Unity]Unity2Dチュートリアル パズルをランダム生成して配置する。(PrefabとInstantiate)

Unity関連記事まとめページはこちら 前回の続き。 パズルを消すことが出来たのでランダ

記事を読む

Unity_

[Unity]Unity2Dチュートリアル 画面上のGameObjectを左下から取得する ソース解説

◆全文 public class GameSystem : MonoBehaviour {

記事を読む

NuGet visual studio

[C#]C#でPDFファイルを画像(System.Drawing.Image)にする

PDFを画像に PDFファイルを読み込んで、画像にする必要があったんです。 インターネットで調べ

記事を読む

unity socket

[Unity]Socket通信でP2Pすっぞ 接続待ち受け

P2Pしましょ。 まずはサーバーとして待ちうけまで作ります。 UnityだとNAT越えどうす

記事を読む

unity rotate worldpoint

[Unity]コードリファレンス GameObjectを回転する。

くるくる回します。 rotationプロパティ Objectの向きをワールド座標で指定します

記事を読む

Excel Csharp

[C#]DataGridViewにプロパティを利用しながら配列をバインドする。

DataGridView C#のFormにデータを表示するのに便利なコントロールがあります。 デ

記事を読む

ad

Message

メールアドレスが公開されることはありません。

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

ad

  •  Auther;わなび

     「オープン系得意だよね? 俺のPCの調子悪いんだけど」という無茶振りから解き放たれゲームエンジニアに。
    C#とかUnityを扱います。
    Twitterフォロー大歓迎です。
    githubアカウント→wannabenote
  • follow us in feedly
PAGE TOP ↑