其实很早我就打算发出来分享给大家了_(:з」∠)_
但一直苦于高三学习的紧张,没法码这篇长篇大论.JPG)
现在放寒假了,也有一点点空闲时间了.gif)
好了废话不多说,我们进入正题:
在网上找了很多相关例子,用得最多的还属柏林噪声了(柏林是个人的名字,不是地名 )
你知道吗?我的世界用的就是这个算法
关于这个噪声的具体描述我就不贴了_(:з」∠)_我估计一下午是解释不完了.JPG)
本人还在念高三,没学高数,所以具体的一些术语我也不解释了,免得误导大家.JPG)
大概就是用正玄波和噪声波相结合的产物
可以理解为一个普通的sin函数曲线在每个对应的f(x)上随机加或减一个数,得到一个随机函数曲线
(sin图像大家能理解吧,要是不理解那我也没办法了 )
和普通随机数相比,优势很明显:
不会产生悬崖式的断带,看起来也不是那么的突兀
概念解释完了,我们结合例子看一下。由于我用的是unity学习这个算法,所以今天用unity来举例子.jpg)
打字大家估计看的烦,我直接上代码吧 结合代码学习应该快一点
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);//设置盒子的新的高度
}
}
到这里应该差不多了,我们看看效果_.gif)
看起来还行,确实蛮舒服的
简单吧?大家快去试试吧!.gif)
(以下为拓展)
既然核心算法大家都明白了,那我们还可以往更高级的地方考虑.jpg)
比如这种:
或者这种:
不过我想重点介绍这种(高度图结合柏林噪声并根据相关坡度生成相应贴图):
这个一共需要两个脚本,一个存储变量,一个提供算法
还是老规矩,直接上代码:
这是提供算法的
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 () {
}
}
这一块就有点上难度了 大家如果有不懂的可以评论一下,我一定会鼎力相助的
当然,这篇文章只是起到抛转引玉的效果。如果大家有什么好的想法的话,请务必在评论里面分享一下你的建议.gif)
(题外话)
有没有人对东方的辐射类型的RPG游戏感兴趣啊 迫切希望找个同好一起完成,我的QQ:370683893
谢谢大家.JPG)
|