LoginSignup
6
3

More than 1 year has passed since last update.

マインスイーパーの作り方

Last updated at Posted at 2021-08-10

マインスイーパーの作り方

詳しい作り方は動画で解説しているので、素材とコードだけを乗っけておきますw
https://youtu.be/LGvzNmZGq-Y
0401サムネ.png

素材はこちら

bg.pngboom.pngcover.pngflag.pnghatena.png
※動画で語ってないが、ゲームオーバーの時、ボムの後ろの赤い背景はテクスチャーを使ってない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);
    }
}
6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3