首先要说的是为什么要仿《文明》而不是其他什么么的仿雷电、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*1025
public 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);elseline[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;elseheightMap[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: xfor (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: yfor (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引擎制作仿《文明》游戏相关推荐

  1. 【游戏开发实战】使用Unity 2019制作仿微信小游戏飞机大战(七):主角飞机碰撞与爆炸

    文章目录 零.教程目录 一.前言 二.本篇目标 三.飞机机碰撞组件:BoxCollider2D.Rigidbody2D 四.添加Tag:Enemy 五.主角飞机碰撞处理:OnTriggerEnter2 ...

  2. 访日本Marza团队:Unity引擎制作VR动画实践

    在UNITE 2016 Shanghai上,日本世嘉飒美集团旗下的动画制作公司Marza引得了满堂喝彩,其使用Unity引擎制作动画的经验吸引了许多开发者膜拜学习.在演讲之后,我们采访了Marza团队 ...

  3. Love2D游戏引擎制作贪吃蛇游戏

    预览游戏 love2d游戏引擎重要函数 详情: love.load:当游戏开始时被调用且仅调用一次 love.draw:回调函数,每帧更新一次游戏画面 love.update:回调函数,每帧更新一次游 ...

  4. unity 如何制作成网页游戏版本

    unity 如何制作成网页游戏版本:游戏在unity中制作好后,如何发布成网页形式的游戏? 菜单File-->Build Setting...然后选择发布成网页如图: 当然也可以自行在网页中添加 ...

  5. 如何在unity中制作塔防游戏

       塔防游戏非常流行,毫无疑问--没有什么比看着自己的防御消灭讨厌的侵略者更让人满足!在这两部分教程中,用unity来制作一个塔防游戏! 将会学习怎样...... 创建一波敌人 让它们跟随线路点 ...

  6. 微信塔防小游戏开发教程,唤境引擎制作塔防游戏分享

    今天带来的是塔防游戏制作攻略! 点击这里来下载工程文件, 点击这里可以下载工程中所用的素材哦~ 预览状态时敌人会从四个生成点随机生成,并且会自动寻路绕过黑色墙体走向红色终点.点击黑色墙体可以创建炮塔, ...

  7. [Unity][游戏实现]使用Unity引擎制作的游戏列表以及优点特色

    不定时更新. 网络游戏: 1.炉石传说 2. 3. 4. 独立游戏: 1.守墓人(Graveyard Keeper) 墓地,养成,像素风格, 2. 3. 4. 5. 国产独立游戏: 1.波米亚时光 分 ...

  8. unity轻松制作塔防游戏

    课程介绍:玩儿过塔防游戏吗?听说过<保卫萝卜>和<塔防战争>吗?本系列课程使用简单易懂的逻辑带你一起剖析这类游戏制作的过程.本案例完美展示了该类项目从搭建到完成的完整架构体系, ...

  9. HTML5小游戏动手做(二):使用PIXI引擎制作坦克大战游戏

    这里写自定义目录标题 1. 简介 1.1 PIXI 简介 1.2 坦克大战游戏简介 2. PIXI 引擎入门 2.1 基本概念 2.1.1 舞台 Stage 2.1.2 容器 Container 2. ...

最新文章

  1. [译] ASP.NET 生命周期 – ASP.NET 上下文对象(六)
  2. Swift中依赖注入的解耦策略
  3. 真香!Vision Transformer 快速实现 Mnist 识别
  4. IO流 带行号的缓冲区
  5. Android网络优化之HttpClient
  6. 模拟spring - 简单实现spring IOC
  7. NABC框架的创意之校园导航。
  8. 16.4 配置Tomcat监听80端口 16.5/16.6/16.7 配置Tomcat虚拟主机16.8 Tomcat日志
  9. java vnc_linux下配置vnc的方法
  10. 12.4日团队工作总结
  11. android制作相册浏览器_一分钟简单制作一个专属于自己的卡通头像
  12. MTK 驱动开发(30)---Memory 移植
  13. 双十一清醒指南,送3本Python书
  14. 陷阱:使用==来比较原始的包装器对象,如Integer
  15. 给新服务器装linux系统,新服务器安装linux系统安装教程
  16. C盘快满了,处理一下pagefile.sys文件和hiberfil.sys文件
  17. 讲解HTML和CSS(超详细)
  18. Java返回机动车的功率_上季度平均功率BI
  19. Vagrant安装和使用
  20. 匿名科创--ANO_OPENMV视觉开发板介绍

热门文章

  1. zcmu之水题来一波~
  2. 服务器内网可以打开外网打不开怎么办?网站搭建后打不开怎么办?
  3. 今日收获 18/4/2
  4. 容联携手火星时代教育 促进线上线下一体化
  5. 电视盒子显示ntp服务器异常,ntp服务器连接异常咋办
  6. 告别脚本小子系列丨JAVA安全(6)——反序列化利用链(上)
  7. 针对手机连WIFI微信公众号等图片加载缓慢问题——解决方案
  8. symmetric tree java_Symmetric Tree对称树
  9. ubuntu 触摸板失灵解决
  10. (十)学生课程表查询