设为首页收藏本站喵玉殿官方微博

喵玉殿论坛 · 喵玉汉化组

 找回密码
 少女注册中
搜索
查看: 608|回复: 2

[编程算法] 尝试让萌新也能听懂的随机地图算法

[复制链接]
发表于 2019-2-2 14:01:20 | 显示全部楼层 |阅读模式
其实很早我就打算发出来分享给大家了_(:з」∠)_
但一直苦于高三学习的紧张,没法码这篇长篇大论
现在放寒假了,也有一点点空闲时间了
好了废话不多说,我们进入正题:

在网上找了很多相关例子,用得最多的还属柏林噪声了(柏林是个人的名字,不是地名
你知道吗?我的世界用的就是这个算法
关于这个噪声的具体描述我就不贴了_(:з」∠)_我估计一下午是解释不完了
本人还在念高三,没学高数,所以具体的一些术语我也不解释了,免得误导大家
大概就是用正玄波和噪声波相结合的产物
可以理解为一个普通的sin函数曲线在每个对应的f(x)上随机加或减一个数,得到一个随机函数曲线
(sin图像大家能理解吧,要是不理解那我也没办法了
和普通随机数相比,优势很明显:
不会产生悬崖式的断带,看起来也不是那么的突兀

概念解释完了,我们结合例子看一下。由于我用的是unity学习这个算法,所以今天用unity来举例子
打字大家估计看的烦,我直接上代码吧结合代码学习应该快一点

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

public class PerlinNoiseGenerator : MonoBehaviour {
        /*
        为了让大家看得更直观,我们用盒子来生成地图,就像我的世界一样
        */
        public int width = 100;//地图的长
        public int depth = 100;//地图的宽
        public float maxHeight = 10;//地图的高
        public float relief = 15;//生成地图的坡度

        private float _seedX, _seedZ;//长和宽的种子,当然这两个可以为同一个

        void Awake () {
                float _seedX = Random.value * 100f;//我发现不乘100根本看不出来有变化_(:з」∠)_
                float _seedZ = Random.value * 100f;

                for(int x = 0;x<width;x++){
                        for (int z = 0; z < depth; z++) {
                                GameObject cube = GameObject.CreatePrimitive (PrimitiveType.Cube);//生成一个盒子,就是右键生成那种
                                cube.transform.localPosition = new Vector3 (x, 0, z);
                                cube.transform.SetParent (transform);
                                SetY (cube);
                        }
                }
        }

        void Start () {
               
        }
       
        // Update is called once per frame
        void Update () {
               
        }

        void SetY (GameObject Cube){
                float Y = 0;

                float xSample = (Cube.transform.localPosition.x + _seedX) / relief;
                float zSample = (Cube.transform.localPosition.z + _seedZ) / relief;
                float noise = Mathf.PerlinNoise (xSample, zSample);//这个就是核心代码了,unity自带的一个算法。当然网上也是有这个随机库的,下一个就好啦
                Y = maxHeight * noise;//给盒子设置高度

                Cube.transform.localPosition = new Vector3 (Cube.transform.localPosition.x, Y,Cube.transform.localPosition.z);//设置盒子的新的高度
        }
               
}

到这里应该差不多了,我们看看效果_

QQ截图20190202133927.png

看起来还行,确实蛮舒服的
简单吧?大家快去试试吧!


(以下为拓展)
既然核心算法大家都明白了,那我们还可以往更高级的地方考虑
比如这种:
3e3550b9c9942e23b954a2bf56d24aab56b2026a.png
或者这种:
6e4e0dde8fd8eca97613662a298f4144c4d32356.png
不过我想重点介绍这种(高度图结合柏林噪声并根据相关坡度生成相应贴图):
QQ截图20190202135305.png
这个一共需要两个脚本,一个存储变量,一个提供算法
还是老规矩,直接上代码:
这是提供算法的
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MapGenerator : MonoBehaviour {

        private TerrainData Terr_data;
        public Terrain Terr;
        private GameController Game;
        private float _seedX, _seedZ;
        private float relief = 15;
        private float Maxheight = 15;
        private float Height = 15;
        void Awake () {
                Game = GameObject.FindGameObjectWithTag (Tags.Gamesetting).GetComponent<GameController>();//这里很重要!请适当修改
                relief = Game.relief;
                Maxheight = Game.Maxheight;
                Height = Game.Height;
                float _seedX = Random.value * 100;
                float _seedZ = Random.value * 100;
        }

        // Use this for initialization
        void Start () {
                Terr_data = new TerrainData ();
                Terr_data.heightmapResolution = Game.heightmapResolution;
                SetHeighMap ();
        }
       
        // Update is called once per frame
        void Update () {
               
        }



        void SetHeighMap () {
                var heightmap = GetHeightMap ();
                Terr_data.SetHeights (0, 0, heightmap);
                Terr_data.alphamapResolution = 32;
                ApplyTextrue (Terr_data);
                Terr_data.size = new Vector3 (Game.Depth, Game.Maxheight, Game.Width);

                Terr = Terrain.CreateTerrainGameObject (Terr_data).GetComponent<Terrain> ();
                Terr.Flush ();
        }

        void ApplyTextrue (TerrainData data) {
                var FlatSplat = new SplatPrototype ();
                var SteepSplat = new SplatPrototype ();

                FlatSplat.texture = Game.FlatTexture;
                SteepSplat.texture = Game.SteepTexture;

                data.splatPrototypes = new SplatPrototype[] {
                        FlatSplat,
                        SteepSplat
                };
                data.RefreshPrototypes ();

                var splatMap = new float[data.alphamapResolution, data.alphamapResolution, 2];

                for (var z = 0; z < data.alphamapHeight; z++) {
                        for (var x = 0; x < data.alphamapWidth; x++) {
                                var normalizedX = (float)x / (data.alphamapWidth - 1);
                                var normalizedZ = (float)z / (data.alphamapHeight - 1);

                                var steepness = data.GetSteepness(normalizedX, normalizedZ);
                                var steepnessNormalized = Mathf.Clamp(steepness / 5.5f, 0, 1f);

                                splatMap[z, x, 0] = 1f - steepnessNormalized;
                                splatMap[z, x, 1] = steepnessNormalized;
                        }
                }
                data.SetAlphamaps(0, 0, splatMap);
        }

        float[,] GetHeightMap () {
                var Heightmap = new float[Terr_data.heightmapResolution, Terr_data.heightmapResolution];
                for (var x = 0; x < Terr_data.heightmapResolution; x++) {
                        for (var z = 0; z < Terr_data.heightmapResolution; z++) {
                                var XC = ((float)x + _seedX) / relief;
                                var ZC = ((float)z + _seedZ) / relief;

                                float Y = Mathf.PerlinNoise (XC, ZC);
//                                GameObject go = GameObject.CreatePrimitive (PrimitiveType.Cube)as GameObject;
//                                go.transform.position = new Vector3 (x, Y, z);
                                Heightmap[x,z] = Y;
                        }
                }
                return Heightmap;
        }
}

这是提供变量的:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameController : MonoBehaviour {
        [Header("地图属性")]
        public float Width = 100;
        public float Depth = 100;
        public float Maxheight = 50;
        public float Height = 10;
        public float WaterHigh = 10;
        public float relief = 15;
        public int heightmapResolution = 16;
        public Texture2D FlatTexture;
        public Texture2D SteepTexture;

        // Use this for initialization
        void Start () {
               
        }
       
        // Update is called once per frame
        void Update () {
               
        }
}

这一块就有点上难度了大家如果有不懂的可以评论一下,我一定会鼎力相助的
当然,这篇文章只是起到抛转引玉的效果。如果大家有什么好的想法的话,请务必在评论里面分享一下你的建议

(题外话)
有没有人对东方的辐射类型的RPG游戏感兴趣啊迫切希望找个同好一起完成,我的QQ:370683893
谢谢大家

发表于 2019-2-2 17:54:59 | 显示全部楼层
tql            

点评

_(:з」∠)_一般般啦2333  发表于 2019-2-2 20:38
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 少女注册中

本版积分规则

合作与事务联系|手机版|小黑屋|无图版|喵玉殿

GMT+8, 2019-4-25 05:47

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表