Episode 09

发布时间 2023-03-22 21:08:46作者: Felix-Fu

Obstacle Placement——障碍物生成

Utility

using System.Collections;

public static class Utility
{
    //洗牌算法
    public static T[] ShuffleArray<T>(T[] array, int seed)
    {
        System.Random prng = new System.Random(seed);

        for (int i = 0; i < array.Length - 1; i++)
        {
            int randomIndex = prng.Next(i, array.Length);
            T tempItem = array[randomIndex];
            array[randomIndex] = array[i];
            array[i] = tempItem;
        }
        return array;
    }
}

T[] , shuffleArray():泛型(返回值为数组类型),泛型方法。

Random.Next():返回一个随机整数。

Next() 返回一个非负随机整数。
Next(Int32) 返回一个小于所指定最大值的非负随机整数。
Next(Int32, Int32) 返回在指定范围内的任意整数。

几种不同的洗牌算法:

//Fisher-Yates Shuffle算法
    public void FisherYatesShuffle<T>(List<T> list)
    {
        List<T> cache = new List<T>();
        int currentIndex;
        while (list.Count > 0)
        {
            currentIndex = Random.Range(0, list.Count);
            cache.Add(list[currentIndex]);
            list.RemoveAt(currentIndex);
        }
        for (int i = 0; i < cache.Count; i++)
        {
            list.Add(cache[i]);
        }       
    }
//Knuth-Durstenfeld Shuffle算法
 public void KnuthDurstenfeldShuffle<T>(List<T> list)
    {
        //随机交换
        int currentIndex;
        T tempValue;
        for (int i = 0; i < list.Count; i++)
        {
            currentIndex = Random.Range(0, list.Count - i);
            tempValue = list[currentIndex];
            list[currentIndex] = list[list.Count - 1 - i];
            list[list.Count - 1 - i] = tempValue;
        }
    }

//优化,但需要一开始知道长度
public void KnuthDurstenfeldShuffle<T>(List<T> list)
    {
        //随机交换
        int currentIndex;
        T tempValue;      
        for (int i = list.Count - 1; i >= 0; i--)
        {
            currentIndex = Random.Range(0, i+1);
            tempValue = list[currentIndex];
            list[currentIndex] = list[i];
            list[i] = tempValue;
        }
    }
//Inside-Out Algorithm算法
public List<T> InsideOutAlgorithm<T>(List<T> list)
    {
        List<T> cache = new List<T>();
        for (int i = 0; i < list.Count; i++)
        {
            cache.Add(list[i]);
        }
        //随机交换
        int currentIndex;
        T tempValue;
        for (int i = cache.Count - 1; i >= 0; i--)
        {
            currentIndex = Random.Range(0, i + 1);
            tempValue = cache[currentIndex];
            cache[currentIndex] = cache[i];
            cache[i] = tempValue;
        }
   
        return cache;
    }
//ReservoirSampling蓄水池抽样算法
public List<T> ReservoirSampling<T>(List<T> list, int m)
    {
        List<T> cache = new List<T>(m);
        for (int i = 0; i < m; i++)
        {
            cache.Add(list[i]);
        }
        int currentIndex;
        for (int i = m; i < list.Count; i++)
        {
            currentIndex = Random.Range(0, i + 1);
            if (currentIndex < m)
            {
                 cache[currentIndex] = list[i];
            }
        }
        return cache;
    }

MapGenerator

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

public class MapGenerator : MonoBehaviour
{
    public Transform tilePrefab;//瓦片预制体
    public Transform obstaclePrefab;//障碍物预制体
    public Vector2 mapSize;//地图尺寸

    [Range(0f, 1f)]
    public float outlinePercent;//瓦片间隙

    List<Coord> allTileCoords;//所有瓦片坐标
    Queue<Coord> shuffledTileCoords;//乱序后的瓦片坐标

    public int seed = 10;

    void Start()
    {
        GenerateMap();
    }

    public void GenerateMap()
    {
        //记录所有瓦片位置
        allTileCoords = new List<Coord>();
        for (int x = 0; x < mapSize.x; x++)
        {
            for (int y = 0; y < mapSize.y; y++)
            {
                allTileCoords.Add(new Coord(x, y));
            }
        }
        //乱序后的瓦片坐标存为队列
        shuffledTileCoords = new Queue<Coord> (Utility.ShuffleArray(allTileCoords.ToArray(), seed));

        string holderName = "Generated Map";
        if (transform.Find(holderName))
        {
            DestroyImmediate(transform.Find(holderName).gameObject);
        }

        Transform mapHolder = new GameObject(holderName).transform;//创建Generated Map集合瓦片
        mapHolder.parent = transform;//将Generated Map父对象设置为为MapGenerator

        //循环创建瓦片
        for (int x = 0; x < mapSize.x; x++)
        {
            for (int y = 0; y < mapSize.y; y++)
            {
                Vector3 tilePosition = CoordToPosition(x,y);//居中
                Transform newTile = Instantiate(tilePrefab, tilePosition, Quaternion.Euler(Vector3.right * 90)) as Transform;
                newTile.localScale = Vector3.one * (1 - outlinePercent);
                newTile.parent = mapHolder;//将瓦片父对象设置为Generated Map
            }
        }

        int obstacleCount = 10;//障碍物数量
        for (int i = 0; i < obstacleCount; i++)
        {
            Coord randomCoord = GetRandomCoord();//获得队列中第一个的坐标
            Vector3 obstaclePosition = CoordToPosition(randomCoord.x,randomCoord.y);//坐标到实际坐标转换
            Transform newobstacle = Instantiate(obstaclePrefab, obstaclePosition + Vector3.up * 0.5f, Quaternion.identity) as Transform;
            newobstacle.parent = mapHolder;//将障碍物父对象设置为Generated Map
        }
    }

    //坐标到实际坐标转换
    Vector3 CoordToPosition(int x, int y)
    {
        return new Vector3(-mapSize.x / 2 + 0.5f + x, 0, -mapSize.y / 2 + 0.5f + y);
    }

    //
    public Coord GetRandomCoord()
    {
        Coord randomCoord = shuffledTileCoords.Dequeue();//移除并返回在 Queue 的开头的对象。
        shuffledTileCoords.Enqueue(randomCoord);//向 Queue 的末尾添加一个对象。
        return randomCoord;
    }

    public struct Coord
    {
        //Coord结构体存储坐标
        public int x;
        public int y;

        public Coord(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    }
}

List:泛型List。

序号 方法名 & 描述
1 Add() 将东西加入到列表的最后。
2 Remove() 删掉项中第一个匹配你想删除的条件的项(删去第一个匹配此条件的项)。
3 Clear() 清空所有项。
4 Sort() 用系统默认的方式对项进行排序。
5 Contains() 查看某项是否存在于列表中。

Queue:代表了一个先进先出的对象集合。当您需要对各项进行先进先出的访问时,则使用队列。当您在列表中添加一项,称为入队,当您从列表中移除一项时,称为出队

序号 方法名 & 描述
1 public virtual void Clear(); 从 Queue 中移除所有的元素。
2 public virtual bool Contains( object obj ); 判断某个元素是否在 Queue 中。
3 public virtual object Dequeue(); 移除并返回在 Queue 的开头的对象。
4 public virtual void Enqueue( object obj ); 向 Queue 的末尾添加一个对象。
5 public virtual object[] ToArray(); 复制 Queue 到一个新的数组中。
6 public virtual void TrimToSize(); 设置容量为 Queue 中元素的实际个数。

struct:在 C# 中,结构体是值类型数据结构。它使得一个单一变量可以存储各种数据类型的相关数据。struct 关键字用于创建结构体。

结构体是用来代表一个记录。

拓展: