#マインスイーパーの作り方
詳しい作り方は動画で解説しているので、素材とコードだけを乗っけておきますw
https://youtu.be/LGvzNmZGq-Y
##素材はこちら
※動画で語ってないが、ゲームオーバーの時、ボムの後ろの赤い背景はテクスチャーを使ってないImageで作っていました。付け間違ったフラグに付けるバツはテキストの✕で作っていました。なのでこの二つに関しては素材がないです。
##カウントの文字に使う色のコードはこちら
1 → 0000F7 ■ 2 → 007C00 ■
3 → EC1F1F ■ 4 → 00007C ■
5 → 7C0000 ■ 6 → 007C7C ■
7 → 000000 ■ 8 → 7C7C7C ■
※大体ですw
##コードはこちら
###①右クリック対応ボタン
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
using UnityEngine.Events;
using System;
public class CCButton : MonoBehaviour, IPointerClickHandler
{
[Serializable]
public class LeftButtonClickedEvent : UnityEvent { }
[FormerlySerializedAs("onLeftClick"), SerializeField]
private LeftButtonClickedEvent m_OnLeftClick = new LeftButtonClickedEvent();
[Serializable]
public class RightButtonClickedEvent : UnityEvent { }
[FormerlySerializedAs("onRightClick"), SerializeField]
private RightButtonClickedEvent m_OnRightClick = new RightButtonClickedEvent();
public void OnPointerClick(PointerEventData eventData)
{
switch (eventData.button)
{
case PointerEventData.InputButton.Left:
m_OnLeftClick.Invoke();
break;
case PointerEventData.InputButton.Right:
m_OnRightClick.Invoke();
break;
case PointerEventData.InputButton.Middle:
//中クリックの時の処理、はないです
break;
default:
break;
}
}
}
###②タイル
using UnityEngine;
using UnityEngine.UI;
public enum TileType { EMPTY, BOOM, COUNT }
public class Tile : MonoBehaviour
{
enum MarkState { NO_MARK, FLAG, QUESTION }
public TileType TileType { get { return mTileType; } }
public int Index { get { return mIndex; } }
Manager mManager;
TileType mTileType = TileType.EMPTY;
MarkState mMarkState = MarkState.NO_MARK;
int mIndex;
bool mIsDigged = false;
[SerializeField] GameObject mCover;
[SerializeField] GameObject mFlag;
[SerializeField] GameObject mQuestion;
[SerializeField] GameObject mRedBG;
[SerializeField] GameObject mRedCross;
[SerializeField] Text mCount;
[SerializeField] GameObject mBoom;
[SerializeField] Color[] mCountCols = new Color[8];
public void Initialize(Manager manager, int index)
{
mManager = manager;
mIndex = index;
}
//タイルを掘る
public void OnDigged()
{
if (mIsDigged || mMarkState == MarkState.FLAG)
return;
if (mMarkState == MarkState.QUESTION)
mQuestion.SetActive(false);
mIsDigged = true;
mCover.SetActive(false);
switch (mTileType)
{
case TileType.EMPTY:
//マネージャーに隣接タイルを掘ってもらう
mManager.DigAround(mIndex);
mManager.CountDiggedTile();
break;
case TileType.BOOM:
//マネージャーにゲームオーバー処理をしてもらう
mRedBG.SetActive(true);
mManager.GameOver();
break;
case TileType.COUNT:
mManager.CountDiggedTile();
break;
}
}
//周りの地雷数を設定/表示
public void SetCount(int count)
{
mTileType = TileType.COUNT;
//カウントを設定して表示
mCount.gameObject.SetActive(true);
mCount.text = count.ToString();
//数字によって、文字の色を変える
mCount.color = mCountCols[count - 1];
}
public void SetBoom()
{
mTileType = TileType.BOOM;
mBoom.SetActive(true);
}
//マークを付ける
public void SetMark()
{
if (mIsDigged)
return;
switch (mMarkState)
{
case MarkState.NO_MARK:
mFlag.SetActive(true);
break;
case MarkState.FLAG:
mFlag.SetActive(false);
mQuestion.SetActive(true);
break;
case MarkState.QUESTION:
mQuestion.SetActive(false);
break;
default:
break;
}
mMarkState++;
int markStateLength = System.Enum.GetNames(typeof(MarkState)).Length;
if ((int)mMarkState == markStateLength)
mMarkState -= markStateLength;
}
public void GameOverCheck()
{
if (mMarkState == MarkState.FLAG && mTileType != TileType.BOOM)
mRedCross.SetActive(true);
else if (mMarkState != MarkState.FLAG && mTileType == TileType.BOOM)
mCover.SetActive(false);
}
}
###③タイル管理
using System.Collections.Generic;
using UnityEngine;
public class Manager : MonoBehaviour
{
[SerializeField] Tile mTilePrefab;
[SerializeField] Vector2Int mFieldSize = new Vector2Int(10, 10);
[SerializeField] int mTotalBoomCount = 20;
[SerializeField] GameObject mTilesRoot;
[SerializeField] GameObject mGameOverImage;
[SerializeField] GameObject mGameClearImage;
Tile[] mTiles;
Tile[] mBoomTiles;
int mDiggedTileCount;
private void Start()
{
GenerateTiles();
SetBooms();
SetCounts();
}
//タイルを設置
void GenerateTiles()
{
Vector2 canvasCenter = new Vector2(Screen.width / 2f, Screen.height / 2f);
float x = (mFieldSize.x - 1f) / 2f;
float y = (mFieldSize.y - 1f) / 2f;
Vector2 leftUnderTilePos = canvasCenter - new Vector2(x, y) * 100f;
mTiles = new Tile[mFieldSize.x * mFieldSize.y];
for (int i = 0; i < mTiles.Length; i++)
{
Vector2 thisTilePos = leftUnderTilePos + new Vector2(i % mFieldSize.x, i / mFieldSize.x) * 100f;
Tile tile = Instantiate(mTilePrefab, mTilesRoot.transform);
tile.GetComponent<RectTransform>().position = thisTilePos;
tile.Initialize(this, i);
mTiles[i] = tile;
}
}
//地雷を設置
void SetBooms()
{
List<Tile> safeTiles = new List<Tile>(mTiles.Length);
safeTiles.AddRange(mTiles);
mBoomTiles = new Tile[mTotalBoomCount];
for (int i = 0; i < mTotalBoomCount; i++)
{
int index = Random.Range(0, safeTiles.Count);
Tile boomTile = safeTiles[index];
mBoomTiles[i] = boomTile;
safeTiles.RemoveAt(index);
boomTile.SetBoom();
}
}
void SetCounts()
{
int[] boomCountEach = new int[mTiles.Length];
foreach (var tile in mBoomTiles)
{
int[] aroundTileIndices = GetAroundIndices(tile.Index);
foreach (var aroundIndex in aroundTileIndices)
boomCountEach[aroundIndex] = boomCountEach[aroundIndex] + 1;
}
for (int i = 0; i < boomCountEach.Length; i++)
{
int boomCount = boomCountEach[i];
if (boomCount != 0 && mTiles[i].TileType != TileType.BOOM)
mTiles[i].SetCount(boomCount);
}
}
//隣のタイルを開く
public void DigAround(int index)
{
foreach (var aroundTileIndex in GetAroundIndices(index))
mTiles[aroundTileIndex].OnDigged();
}
int[] GetAroundIndices(int index)
{
List<int> result = new List<int>(8);
int index0 = index - mFieldSize.x - 1;
if (index0 >= 0 && index0 % mFieldSize.x != mFieldSize.x - 1)
result.Add(index0);
int index1 = index - mFieldSize.x;
if (index1 >= 0)
result.Add(index1);
int index2 = index - mFieldSize.x + 1;
if (index2 >= 0 && index2 % mFieldSize.x != 0)
result.Add(index2);
int index3 = index - 1;
if (index3 >= 0 && index3 % mFieldSize.x != mFieldSize.x - 1)
result.Add(index3);
int index4 = index + 1;
if (index4 % mFieldSize.x != 0)
result.Add(index4);
int index5 = index + mFieldSize.x - 1;
if (index5 < mTiles.Length && index5 % mFieldSize.x != mFieldSize.x - 1)
result.Add(index5);
int index6 = index + mFieldSize.x;
if (index6 < mTiles.Length)
result.Add(index6);
int index7 = index + mFieldSize.x + 1;
if (index7 < mTiles.Length && index7 % mFieldSize.x != 0)
result.Add(index7);
return result.ToArray();
}
//ゲームオーバー
public void GameOver()
{
mGameOverImage.SetActive(true);
foreach (var tile in mTiles)
{
tile.GameOverCheck();
}
}
public void CountDiggedTile()
{
mDiggedTileCount++;
if (mDiggedTileCount == mFieldSize.x * mFieldSize.y - mTotalBoomCount)
GameClear();
}
//ゲームクリア
void GameClear()
{
mGameClearImage.SetActive(true);
}
}