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; } } }}
暂时做到的工作就这么多了,想完成仿《文明》还有很多工作要做的。
- Unity引擎制作仿《文明》游戏
- 仿《文明》游戏制作日志2
- 仿《文明》游戏制作日志3
- Unity引擎制作的游戏接入腾讯WeGame快速入门
- Unity (游戏引擎)
- Unity制作扫雷游戏
- Unity制作SpaceShooter游戏
- Unity制作SurvivalShooter游戏
- 【Unity 3D 游戏引擎】使用 2DToolkit 插件 制作2D精灵动画
- Unity不再仅仅是游戏引擎
- unity游戏引擎相关知识
- 使用Unity制作俄罗斯方块游戏
- Unity制作游戏中的场景
- Unity头戴式游戏制作视频教程
- Unity 游戏2048:制作总结
- unity打靶游戏的制作
- Unity 2D游戏制作
- [读书笔记]<游戏引擎架构>|仿射矩阵
- mysql 操作总结 INSERT和REPLACE
- linux学习(23)vi,vim的使用
- JAVA内部类使用,什么时候该使用内部类及使用内部类的好处
- HashMap、TreeMap、Hashtable、LinkedHashMap区别
- UVa #12569 Planning mobile robot on Tree (EASY Version) (习题7-11)
- Unity引擎制作仿《文明》游戏
- ubuntu系统用户密码忘记了怎么办
- time_t
- linux select与poll实现机制与实例分析
- 子进程模块subprocess
- CF~Good Bye 2014 B. New Year Permutation
- 五子棋Pro-最好玩的五子棋游戏
- 「转」IEEE802.11数据帧在Linux上的抓取
- hdoj1012