1. 程式人生 > >2D獵寶行動(類掃雷小遊戲)DAY 4

2D獵寶行動(類掃雷小遊戲)DAY 4

1.用陷阱和數字元素初始化地圖

    private void InitMap()
    {
        //可用索引值的列表
        List<int> availabaleIndex = new List<int>();
        for(int i = 0; i < w * h; i++)
        {
            availabaleIndex.Add(i);
        }
        GenerateTrap(availabaleIndex);
        GenerateNumber(availabaleIndex);
    }

    /// <summary>
    /// 生成陷阱
    /// </summary>
    /// <param name="availableIndex">尚未初始化的地圖元素的索引值</param>
    private void GenerateTrap(List<int> availableIndex)
    {
        float trapProbability = Random.Range(minTrapProbability, maxTrapProbability);
        int trapNum = (int)(availableIndex.Count * trapProbability);
        for(int i = 0; i < trapNum; i++)
        {
            int tempIndex = availableIndex[Random.Range(0, availableIndex.Count)];
            int x, y;
            GetPosition(tempIndex, out x, out y);
            availableIndex.Remove(tempIndex);
            SetElement(tempIndex, ElementContent.Trap);
        }
    }

    /// <summary>
    /// 生成數字
    /// </summary>
    /// <param name="availableIndex">尚未初始化的地圖元素的索引值</param>
    private void GenerateNumber(List<int> availableIndex)
    {
        foreach(int i in availableIndex)
        {
            SetElement(i, ElementContent.Number);
        }
        availableIndex.Clear();
    }

    /// <summary>
    /// 設定元素的型別
    /// </summary>
    /// <param name="index">元素所在位置的一維索引值</param>
    /// <param name="content">需要設定的型別</param>
    /// <returns>設定好的的新型別的元件</returns>
    private BaseElement SetElement(int index,ElementContent content)
    {
        int x, y;
        GetPosition(index, out x ,out y);
        GameObject tempGo = mapArray[x, y].gameObject;
        Destroy(tempGo.GetComponent<BaseElement>());
        switch (content)
        {
            case ElementContent.Number:
                mapArray[x, y] = tempGo.AddComponent<NumberElement>();
                break;
            case ElementContent.Trap:
                mapArray[x, y] = tempGo.AddComponent<TrapElement>();
                break;
            case ElementContent.Tool:
                break;
            case ElementContent.Gold:
                break;
            case ElementContent.Enemy:
                break;
            case ElementContent.Door:
                break;
            case ElementContent.BigWall:
                break;
            case ElementContent.SmallWall:
                break;
            case ElementContent.Exit:
                break;
            default:
                break;
        }
        return mapArray[x, y];
    }

2.計算並顯示數字元素八領域的陷阱數

在GameManager中新增計算八領域中陷阱個數的方法

    /// <summary>
    /// 計算指定位置元素的八領域中的陷阱個數
    /// </summary>
    /// <param name="x">元素所在的位置x</param>
    /// <param name="y">元素所在的位置y</param>
    /// <returns></returns>
    public int CountAdjacentTraps(int x, int y)
    {
        int count = 0;
        if (IsSameContent(x, y + 1,ElementContent.Trap)) count++;
        if (IsSameContent(x, y - 1,ElementContent.Trap)) count++;
        if (IsSameContent(x - 1, y,ElementContent.Trap)) count++;
        if (IsSameContent(x + 1, y, ElementContent.Trap)) count++;
        if (IsSameContent(x - 1, y + 1, ElementContent.Trap)) count++;
        if (IsSameContent(x + 1, y + 1, ElementContent.Trap)) count++;
        if (IsSameContent(x - 1, y - 1, ElementContent.Trap)) count++;
        if (IsSameContent(x + 1, y - 1, ElementContent.Trap)) count++;
        return count;
    }

    /// <summary>
    /// 判定指定位置的元素型別
    /// </summary>
    /// <param name="x">元素所在位置的x</param>
    /// <param name="y">元素所在位置的y</param>
    /// <param name="content">需要比較的型別</param>
    /// <returns>比較結果</returns>
    public bool IsSameContent(int x,int y,ElementContent content)
    {
        if (x >= 0 && x < w && y > 0 && y < h)
        {
            return mapArray[x, y].elementContent == content;
        }
        return false;
    }

然後新增數字的圖片

最後在NumberElement中呼叫顯示圖片的方法

//TODO 計算並顯示自身數字       

LoadSprite(GameManager._instance.numberSprites[GameManager._instance.CountAdjacentTraps(x, y)]);

結果如下:

3.泛洪演算法簡介

4.利用泛洪演算法翻開連續的控數字區域

在GameManager中寫泛洪演算法

    /// <summary>
    /// 泛洪演算法翻開連片的空白區域
    /// </summary>
    /// <param name="x">開始泛洪的元素的位置x</param>
    /// <param name="y">開始泛洪的元素的位置y</param>
    /// <param name="visited">訪問表</param>
    public void FloodFillElement(int x,int y,bool[,] visited)
    {
        //檢測x,y是否在範圍內
        //是否訪問過
        //翻不翻開,怎麼翻開
        //將自己標記為訪問過
        //讓鄰居一起做
        if (x >= 0 && x < w && y > 0 && y < h)
        {
            if (visited[x, y]) return;
            if (mapArray[x, y].elementType != ElementType.CantCovered)
            {
                ((SingleCoveredElement)mapArray[x, y]).UncoverElementSingle();
            }
            if (CountAdjacentTraps(x, y) > 0) return;
            if (mapArray[x, y].elementType == ElementType.CantCovered) return;
            visited[x, y] = true;
            FloodFillElement(x - 1, y, visited);
            FloodFillElement(x + 1, y, visited);
            FloodFillElement(x, y - 1, visited);
            FloodFillElement(x, y + 1, visited);
            FloodFillElement(x - 1, y - 1, visited);
            FloodFillElement(x + 1, y + 1, visited);
            FloodFillElement(x + 1, y - 1, visited);
            FloodFillElement(x - 1, y + 1, visited);
        }
    }

在NumberElement中進行呼叫

public override void OnUnCovered()
    {
        //TODO 泛洪演算法翻開周邊的元素
        GameManager._instance.FloodFillElement(x, y, new bool[GameManager._instance.w, GameManager._instance.h]);
    }

執行程式,結果如下:

5.快速翻開方法的製作

該功能是滑鼠中鍵執行。

    public void UncoveredAdjacentElements(int x,int y)
    {
        int marked = 0;
        for(int i = x - 1; i <= x + 1; i++)
        {
            for(int j = y - 1; j <= y + 1; j++)
            {
                if (i >= 0 && j < w && i > 0 && j < h)
                {
                    if (mapArray[i, j].elementState == ElementState.Marked) marked++;
                    if (mapArray[i, j].elementState == ElementState.Uncovered && mapArray[i, j].elementContent == ElementContent.Trap) marked++;
                }
            }
        }
        if(CountAdjacentTraps(x,y)== marked)
        {
            for (int i = x - 1; i <= x + 1; i++)
            {
                for (int j = y - 1; j <= y + 1; j++)
                {
                    if (i >= 0 && i < w && j > 0 && j < h)
                    {
                        if(mapArray[i,j].elementState != ElementState.Marked)
                        {
                            mapArray[i, j].OnPlayerStand();
                        }                        
                    }
                }
            }
        }
    }

6.失敗後翻開所有陷阱

    /// <summary>
    /// 翻開地圖內所有的陷阱
    /// </summary>
    public void DisplayAllTraps()
    {
        foreach (BaseElement element in mapArray)
        {
            if(element.elementContent == ElementContent.Trap)
            {
                ((TrapElement)element).UncoverElementSingle();
            }
            if(element.elementContent != ElementContent.Trap && element.elementState == ElementState.Marked)
            {
                Instantiate(errorElement, element.transform);
            }
        }
    }

執行程式,結果如下:

7.更改雙翻元素的設計

    public bool isHide = true;

    public override void Awake()
    {
        base.Awake();
        elementType = ElementType.DoubleCovered;
        if (Random.value < GameManager._instance.uncoveredProbability)
        {
            UncoverElementSingle();
        }
    }

8.完善雙翻元素類的設計

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

public class DoubleCoveredElement : SingleCoveredElement {

    public bool isHide = true;

    public override void Awake()
    {
        base.Awake();
        elementType = ElementType.DoubleCovered;
        if (Random.value < GameManager._instance.uncoveredProbability)
        {
            UncoverElementSingle();
        }
    }

    public override void OnPlayerStand()
    {
        switch (elementState)
        {
            case ElementState.Covered:
                if(isHide == true)
                {
                    UncoverElementSingle();
                }
                else
                {
                    UncoveredElement();
                }
                break;
            case ElementState.Uncovered:
                return;
            case ElementState.Marked:
                if (isHide == true)
                {
                    RemoveFlag();
                }      
                break;
        }
    }

    public override void OnMiddleMouseButton()
    {
        GameManager._instance.UncoveredAdjacentElements(x, y);
    }

    public override void OnRightMouseButton()
    {
        switch (elementState)
        {
            case ElementState.Covered:
                if (isHide == true)
                {
                    AddFlag();
                }               
                break;
            case ElementState.Uncovered:
                return;
            case ElementState.Marked:
                if (isHide == true)
                {
                    RemoveFlag();
                }
                break;
        }
    }

    public override void UncoverElementSingle()
    {
        if (elementState == ElementState.Uncovered) return;
        isHide = false;
        RemoveFlag();
        ClearShadow();
        ConfirmSprite();
    }

    public override void OnUnCovered()
    {
        elementState = ElementState.Uncovered;
        ToNumberElement();
    }

    public virtual void ConfirmSprite()
    {

    }
}

9.設計工具元素類

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

public class ToolElement : DoubleCoveredElement {

    public ToolType toolType;

    public override void Awake()
    {
        base.Awake();
        elementContent = ElementContent.Tool;
    }

    public override void OnUnCovered()
    {
        //TODO 獲得道具
        base.OnUnCovered();
    }

    public override void ConfirmSprite()
    {
        LoadSprite(GameManager._instance.toolSprites[(int)toolType]);
    }
}