Unity引擎制作仿《文明》游戏

来源:互联网 发布:wap手机图文列表源码 编辑:程序博客网 时间:2024/05/22 10:23

     首先要说的是为什么要仿《文明》而不是其他什么么的仿雷电、RPG此类。

     第一点,游戏制作室自发性质,所以要选热爱的游戏类型,这样才有动力做下去。我所热爱的游戏,而且不能这么宏伟,可以想到的是小时候FC上玩的热血篮球,初高中玩的CS、大富翁、富甲天下,近期玩的一下卡牌类游戏,还有就是战略游戏:文明5,纪元系列;第二点,选《文明》是因为相比其他游戏而言,它占用的美术资源少,大多是算法的实现。

     当然我也没什么游戏制作经验,猜想了一下《文明5》的内部需要实现的关键技术,最先想到的是生成一个随机地形。这篇文章就是关于生成一个初步的地形地图。


     查资料。

     网上查了一下资料,下面两篇文章对我帮助甚大,对原作者表示感谢:随机分形地形算法,   地形算法小结,两篇文章是同一个作者,一篇是翻译一篇是原创。这两重要资料让我了解到地形可以用高度图表示,如何确定地形纹理。

     Unity接口。Unity提供了用高度图设置地形以及设置地形纹理的接口,这些接口在TerrainData这个类里面。

     实现。

     资料所提供的算法生成的地形非常随机,而游戏要求的地形却是大部分是平原,小部分是高地,偶尔有几座山,显然算法不适合我们。考虑到文明系列的地图实际上是多边形组合而成(正方形、正六边形)的,我也可以将地图分成N块,每块应用地形算法,调整算法的系数,生成指定地形还是可以做到的。

     问题。

     用这种方法实现的话,也有不少问题。如果日后能成游戏的话,整个地图就行一个棋盘,有正方形组合而成,非常不美观,不如《文明5》的六边形组合美观,这点看以后能不能改进吧。

     另一个较为严重问题是块与块之间的边界不好处理,边界感觉很分明:



      这段时间都在处理这个问题,想了很多复杂的办法。实在没办法了,采用平均的办法。我目前采用的办法:对于高度图每一点heightMap[i,j] = sum([i-range,j], [i-range+1,j], ..., [i+range-1,j],[io+range,j]) / range / 2。就是取x轴周围range范围内的点的高度的平均值,Y轴也做同样的工作。

      令我意外的是,如果反复进行设置地形步骤和平滑地形步骤,得到的效果还不错。


当然这效果我还不太满意,以后会在这个方向多努力。


这就是我目前的工作了,先把代码贴上吧。关于代码,有很多可以讲的细节,实在无力在此说明,有兴趣的朋友可以发我的常用邮箱共同探讨:clevenmfang2010@qq.com

用法:新建地形,HeightMapCreater脚本附于其上,设置一下参数:

参数填4,反复按"Set"和“Smooth”按钮,大概循环四五次,把参数填小一点,2、1各Set、Smooth一次。

//HeightMapSection.cs 负责生产每块的地形

using UnityEngine;using System.Collections;//将地图分为256块,每块高度图分辨率是65*65//也就是总体高度图分比率是1025*1025public enum TerrainType{    HighHill,           //高山    Hill,               //一般的山    Highland,           //高地    Plain,              //平原    Lowland             //低地}public class HeightMapSection : MonoBehaviour {    private enum BorderLocation    {        left,        up,        right,        down    }    private enum CornerLocation    {        topLeft,        topRight,        lowerRight,        lowerLeft    }    public struct TotalParameter    {        public float rangeMax;        public float rangeMin;        public float factor;    }    public struct BorderParameter    {        public float rangeMax;        public float rangeMin;        public float factor;    }        public struct Location    {        public int x;        public int y;    }    public TerrainType terrainType;    public Location location;        private float[,] localHeightMap = new float[65, 65];    //各类地形的参数    private static Parameter highHill, hill, highland, plain, lowland;        public class Parameter    {        public TotalParameter total;        public BorderParameter border;        public float[] seed;        public Parameter(float max, float min, float factor,            float b_max, float b_min, float b_factor)        {            total.rangeMax = max;            total.rangeMin = min;            total.factor = factor;            border.rangeMax = b_max;            border.rangeMin = b_min;            border.factor = b_factor;        }    }    public static void ParameterInitialize()    {        highHill = new Parameter(0.6f, -0.3f, 0.5f, 0, -0.1f, 0.5f);        highHill.seed = new float[] { 0.9f, 0.7f, 0.7f, 0.7f, 0.7f };        hill = new Parameter(0.5f, -0.2f, 0.5f, 0, -0.1f, 0.5f);        hill.seed = new float[] { 0.7f, 0.5f, 0.5f, 0.5f, 0.5f };        highland = new Parameter(0.15f, 0, 0.4f, 0.1f, 0, 0.4f);        highland.seed = new float[] { 0.4f, 0.4f, 0.4f, 0.4f, 0.4f};        plain = new Parameter(0.02f, -0.02f, 0.5f, 0.03f, -0.03f, 0.5f);        plain.seed = new float[] { 0.35f, 0.35f, 0.35f, 0.35f, 0.35f };        lowland = new Parameter(0.02f, -0.05f, 0.5f, 0.03f, -0.03f, 0.5f);        lowland.seed = new float[] { 0.3f, 0.3f, 0.3f, 0.3f, 0.3f };    }    //使用菱形-正方形(Diamond-Square)算法    //height map的边长为二的幂加一    public void CreateLocalHeightMap(float[,] hm, int width, TerrainType[,] map)    {        for (int i = 0; i < 65; i++)            for (int j = 0; j < 65; j++)                localHeightMap[i, j] = -1;                int increament = 32;        int row, col;                            //迭代器        //确定地形类型        Parameter p = GetParameter();        float rangeMax = p.total.rangeMax;        float rangeMin = p.total.rangeMin;        //四角赋值        localHeightMap[0, 0] = 0.35f;        localHeightMap[0, 64] = 0.35f;        localHeightMap[64, 0] = 0.35f;        localHeightMap[64, 64] = 0.35f;        //生成边界        //取每条边的中点,与-1比较。等于-1,还是原始值,需要生产边界;不等于-1,已生产,用原来的值即可        bool[] isNeedCreate = new bool[4];        isNeedCreate[0] = hm[location.x * 64, location.y * 64 + 32] == -1;       //左边界        isNeedCreate[1] = hm[location.x * 64 + 32, location.y * 64] == -1;       //上边界        isNeedCreate[2] = hm[location.x * 64 + 64, location.y * 64 + 32] == -1;       //右边界        isNeedCreate[3] = hm[location.x * 64 + 32, location.y * 64 + 64] == -1;       //下边界        CreateBorder(hm, localHeightMap[0, 64], localHeightMap[0, 0], isNeedCreate[0], BorderLocation.left, p.border);                CreateBorder(hm, localHeightMap[0, 0], localHeightMap[64, 0], isNeedCreate[1], BorderLocation.up, p.border);        CreateBorder(hm, localHeightMap[64, 0], localHeightMap[64, 64], isNeedCreate[2], BorderLocation.right, p.border);        CreateBorder(hm, localHeightMap[0, 64], localHeightMap[64, 64], isNeedCreate[3], BorderLocation.down, p.border);        //第一轮种子        localHeightMap[32, 32] = p.seed[0];        localHeightMap[16, 16] = p.seed[1];        localHeightMap[48, 16] = p.seed[2];        localHeightMap[48, 48] = p.seed[3];        localHeightMap[16, 48] = p.seed[4];        while (increament >= 1)        {            //正方形阶段            for (row = increament; row < 65; row += increament * 2)            {                for (col = increament; col < 65; col += increament * 2)                {                    if (localHeightMap[row, col] == -1)                        localHeightMap[row, col] = GetHeight(row, col, true, increament, rangeMin, rangeMax);                }            }            //菱形阶段            bool isEven = true;            for (row = 0; row < 65; row += increament)            {                for (col = isEven ? increament : 0; col < 65; col += increament * 2)                {                    if (localHeightMap[row, col] == -1)                        localHeightMap[row, col] = GetHeight(row, col, false, increament, rangeMin, rangeMax);                }                isEven = !isEven;            }            rangeMin *= p.total.factor;            rangeMax *= p.total.factor;            increament /= 2;        }        //赋值给全局高度图        for (int i = 0; i < 65; i++)            for (int j = 0; j < 65; j++)                hm[location.x * 64 + i, location.y * 64 + j] = localHeightMap[i, j];    }    float GetHeight(int x, int y, bool isSquare, int increament, float minHeight, float maxHeight)    {        float corner01, corner02, corner03, corner04;        if (isSquare)        //正方形        {            corner01 = localHeightMap[x - increament, y - increament];   //左上角            corner02 = localHeightMap[x - increament, y + increament];   //左下角            corner03 = localHeightMap[x + increament, y - increament];   //右上角            corner04 = localHeightMap[x + increament, y + increament];   //右下角        }        else        {            //左角            if (x - increament < 0) corner01 = localHeightMap[x + increament, y];            else corner01 = localHeightMap[x - increament, y];            //下角            if (y + increament > 64) corner02 = localHeightMap[x, y - increament];            else corner02 = localHeightMap[x, y + increament];            //右角            if (x + increament > 64) corner03 = localHeightMap[x - increament, y];            else corner03 = localHeightMap[x + increament, y];            //上角            if (y - increament < 0) corner04 = localHeightMap[x, y + increament];            else corner04 = localHeightMap[x, y - increament];        }        return (corner01 + corner02 + corner03 + corner04) / 4 + Random.Range(minHeight, maxHeight);    }    Parameter GetParameter()    {        switch (terrainType)        {            case TerrainType.HighHill: return highHill;            case TerrainType.Highland: return highland;            case TerrainType.Hill: return hill;            case TerrainType.Lowland: return lowland;            default: return plain;        }    }    void CreateBorder(float[,] hm, float head, float tail, bool isNeedCreate, BorderLocation bl, BorderParameter bp)    {        float[] line = new float[65];        if (isNeedCreate)       //自己生成边界        {            int increament = 32;            line[0] = head; line[64] = tail;            Debug.Log(head + " " + tail);            while (increament >= 1)            {                bp.rangeMin *= bp.factor;                bp.rangeMax *= bp.factor;                for (int i = increament; i < 65; i += increament * 2)                {                    /*if (head > tail)                        line[i] = (tail - head) / Mathf.Atan(width) * Mathf.Atan((float)i / 2) + head +                            Random.Range(minH, maxH);                    else                        line[i] = (head - tail) / Mathf.Atan(-width) * Mathf.Atan((float)i / 2 - width) + tail +                            Random.Range(minH, maxH);*/                    line[i] = (line[i + increament] + line[i - increament]) / 2 + Random.Range(bp.rangeMin, bp.rangeMax);                }                increament /= 2;            }        }        else                    //用已有的        {            switch (bl)            {                case BorderLocation.down:                    for (int i = 0; i < 65; i++)                        line[i] = hm[location.x * 64 + i, location.y * 64 + 64];                    break;                case BorderLocation.left:                    for (int i = 0; i < 65; i++)                        line[i] = hm[location.x * 64, location.y * 64 + i];                    break;                case BorderLocation.right:                    for (int i = 0; i < 65; i++)                        line[i] = hm[location.x * 64 + 64, location.y * 64 + i];                    break;                case BorderLocation.up:                    for (int i = 0; i < 65; i++)                        line[i] = hm[location.x * 64 + i, location.y * 64];                    break;            }        }        //边界赋值给局部高度图        switch (bl)        {            case BorderLocation.down:                for (int i = 0; i < 65; i++) localHeightMap[i, 64] = line[i];                break;            case BorderLocation.left:                for (int i = 0; i < 65; i++) localHeightMap[0, i] = line[i];                break;            case BorderLocation.right:                for (int i = 0; i < 65; i++) localHeightMap[64, i] = line[i];                break;            case BorderLocation.up:                for (int i = 0; i < 65; i++) localHeightMap[i, 0] = line[i];                break;        }    }}

//HeightMapCreater.cs 设置整个地图地形,生产地图

using UnityEngine;using System.Collections;using UnityEditor;using System.Collections.Generic;public class HeightMapCreater : MonoBehaviour{    public TerrainType[,] middleMap = new TerrainType[16, 16];    private TerrainType[] CreateOrder;    private TerrainData terrainData;    private float[,] heightMap;    private int width;    private string range = "";    void Start()    {        terrainData = GetComponent<Terrain>().terrainData;        width = terrainData.heightmapResolution;        heightMap = new float[width, width];        for (int i = 0; i < width; i++)            for (int j = 0; j < width; j++)            {                if (i == 0 || j == 0 || i == width - 1 || j == width - 1)                    heightMap[i, j] = 0.35f;                else                    heightMap[i, j] = -1;            }        CreateOrder = new TerrainType[] { TerrainType.HighHill, TerrainType.Hill,             TerrainType.Highland, TerrainType.Plain, TerrainType.Lowland };        HeightMapSection.ParameterInitialize();        for(int i = 0; i< 16; i++)            for (int j = 0; j < 16; j++)            {                /*float randValue = Random.value;                if (randValue < 0.7f) middleMap[i, j] = TerrainType.Plain;                else if (randValue < 0.85f) middleMap[i, j] = TerrainType.Highland;                else if (randValue < 0.9f) middleMap[i, j] = TerrainType.Hill;                else if (randValue < 0.95f) middleMap[i, j] = TerrainType.HighHill;                else middleMap[i, j] = TerrainType.Lowland;*/                if (i == 0 || i == 15 || j == 0 || j == 15 )                    middleMap[i, j] = TerrainType.HighHill;                else if (i == 1 || i == 14 || j == 1 || j == 14)                    middleMap[i, j] = TerrainType.Hill;                else middleMap[i, j] = TerrainType.Plain;            }    }    void OnGUI()    {        if(GUILayout.Button("Set"))        {            BaseHeightMapCreate();            terrainData.SetHeights(0, 0, heightMap);        }        range = GUILayout.TextField(range,30);        if (GUILayout.Button("Smooth"))        {            SmoothTerrain(int.Parse(range));            terrainData.SetHeights(0, 0, heightMap);        }    }    void BaseHeightMapCreate()    {        HeightMapSection local = new HeightMapSection();        for (int turn = 0; turn < CreateOrder.Length; turn++)        {            for (int i = 0; i < 16; i++)            {                for (int j = 0; j < 16; j++)                {                    if (CreateOrder[turn] == middleMap[i, j])                    {                        Random.seed = (int)(i + j + turn + Time.time) * 1000;                        local.terrainType = middleMap[i, j];                        local.location.x = i;                        local.location.y = j;                        local.CreateLocalHeightMap(heightMap, width, middleMap);                    }                }            }        }            }    void SmoothTerrain(int range)    {        //axis: x        for (int i = range; i < width - range; i++)        {            for (int j = 1; j < width - 1; j++)            {                float sum = 0.0f;                for (int m = i - range; m < i + range; m++)                    sum += heightMap[m, j];                heightMap[i, j] = sum / range / 2;            }        }        //axis: y        for (int j = range; j < width - range; j++)        {            for (int i = 1; i < width - 1; i++)            {                float sum = 0.0f;                for (int m = j - range; m < j + range; m++)                    sum += heightMap[i, m];                heightMap[i, j] = sum / range / 2;            }        }    }}

暂时做到的工作就这么多了,想完成仿《文明》还有很多工作要做的。



0 0
原创粉丝点击