Libgdx游戏开发(3)——通过柏林噪音算法地图随机地形
原文: Libgdx游戏开发(3)——通过柏林噪音算法地图随机地形-Stars-One的杂货小窝
在B站刷到了随机地图生成的视频,随手学习下并做下记录
注: 本篇使用javafx应用作演示,算是了解这个算法的使用,后续会再出篇libgdx生成地图的示例
说明
抛开算法实现,首先认知柏林噪音算法
一般我们想要随机数,会指定个范围,如0.0-1.0之间任意小数,而柏林算法的结果范围就是[-1,1]
柏林算法已经实现了对应一维到四维的算法,本文以二维来进行讲解,实现我自己的一个游戏随机地图效果,我以五种颜色区分不同地形,通过javafx应用展示出一个600*600的游戏地图地形,如下图所示
代码实现
需求如下:
使用一个二维数组来进行存储当前地图,每个数值里存放一个int类型,用来标明地图的地形,类型有0-4(即int数值可取[0,4]范围)
我们如果使用单纯的随机算法,地形可能就会很乱,所以我们采用柏林噪音算法来得到我们的对应地形,使其有个平滑的过渡效果,方法如下:
//这里我使用的是java实现版本,FastNoiseLite这个类可见下文列出的参考链接
val noise = FastNoiseLite()
noise.SetNoiseType(FastNoiseLite.NoiseType.OpenSimplex2)
noise.SetSeed(1000)
val result = noise.GetNoise(1f, 2f)
柏林算法最初是由Ken Perlin
,算法的命名由此得来,然后后续发展又继续了优化,所以有以下几种类型,对应着 FastNoiseLite.NoiseType
这个枚举类型,具体介绍如下:
OpenSimplex2
:OpenSimplex2 是关于 OpenSimplex 噪声的一种改进版本,提供更好的性能和质量。它通常用于生成连续、无缝的噪声,适用于图形学和自然模拟等领域。OpenSimplex2S
:OpenSimplex2S 是 OpenSimplex2 的另一个改进版本,其主要目标是在保持质量的同时提高性能。它通常比 OpenSimplex2 更快速、效率更高,适合需要大量计算的场景。Cellular
:Cellular Noise 是一种基于 Voronoi 图的噪声算法,产生类似于细胞结构的噪声效果。它常用于生成有规律排列的点、线或区域,以及模拟生物组织和自然纹理。Perlin
:Perlin Noise 是由 Ken Perlin 发明的一种经典噪声算法,用于生成自然风格的连续噪声图案。它被广泛应用于图形学、游戏开发和计算机模拟等领域。ValueCubic
:ValueCubic Noise 是一种基于立方插值的噪声算法,通常用于生成平滑的、无缝的噪声效果。它提供了更细致的控制和调整选项,适合需要更多定制化的噪声生成需求。Value
:Value Noise 是一种简单的噪声算法,通过对噪声值进行插值来生成连续的、均匀分布的噪声效果。虽然相对简单,但它在一些场景中也有其应用,如生成地形、纹理等。
柏林算法最终生成的结果数值与输入的x,y,还有seed(随机种子)有关,如果三者固定相同,则得到的数值是相同的;
一般来说我们拿一个随机数来作为随机种子(如使用Random.nextInt(1000)
方法,获取1000以内的一个随机数)
但这样还不是最优的,我们还可以使用正弦叠加的方法继续优化得到的数值,使其可以更加平滑
数学函数: result =noise(x,y) + (1/2) * noise(2x,2y) + (1/4) * noise(4x,4y) + (1/8) * noise(8x,8y)
这里我只叠加到8,当然还可以继续叠加下去,平滑过渡的效果就越好
转为代码:
val noise = FastNoiseLite()
noise.SetNoiseType(FastNoiseLite.NoiseType.OpenSimplex2)
noise.SetSeed(1000)
val result = noise.GetNoise(1f , 2f ) +(1/2 )* noise.GetNoise(1f *2, 2f * 2)+ (1/4 )* noise.GetNoise(1f *4, 2f * 4)+(1/8 )* noise.GetNoise(1f *8, 2f * 8)
对上面方法封装下,得到下面代码:
object MyUtil{
val noise by lazy {
val noise = FastNoiseLite()
noise.SetNoiseType(FastNoiseLite.NoiseType.OpenSimplex2)
//1000以内随机,可以自行更改
noise.SetSeed(Random.nextInt(1000))
noise
}
//size默认为4,表明进行3次叠加,如上面示例
fun noiseData(x: Float, y: Float, size: Int = 4): Double {
val result = (0 until size).sumOf {
//位与操作,相当于的2的n次方
// 1 shr 0 = 1
// 1 shr 1 = 2
val num = 1 shl it
1.0 * (1 / num) * noise.GetNoise(x * num, y * num)
}
return result
}
}
这里需要注意的是,我们封装得保证我们的每次调用函数的时候FastNoiseLite
用的是同个对象,否则就会出现其他效果,和我们预期不符,所以上面封装我使用了单例模式
使用:
fun main(){
//4*4的二维数据
val size = 4
val re = Array(size) { IntArray(size) }
(0 until size).forEach {x->
var buffer = StringBuffer()
(0 until size).forEach {y->
val data = noiseData(x.toFloat(),y.toFloat())
//todo 这里还需要将data转为0-4的int数值并存放在二维数组中
buffer.append(data.toString()+",")
}
println(buffer.toString())
buffer=StringBuffer()
}
}
上面只是得到的对应的噪音数据,而我们按照范围,将其转为对应的0-4数值,代码如下:
fun getType(result: Double): Int {
return when {
result >= -1 && result < -0.6 -> 0
result >= -0.6 && result < -0.2 -> 1
else -> 3
}
}
result的数据范围在[-1,1]之间,所以可以根据自己的需求,符合对应范围,自行给数值即可,上面代码只是一个简单的示例,你可以随意划分;
于是按照我的需求,我自行封装成下面的方法(可能有些难以理解,大概解释就是划分为n等分,每个区间有个下标,然后数值符合对应区间的,则返回对应区间下标)
fun getType(result: Double): Int {
//划分为5等分
val size = 5
val temp = 2.0 / size
for (i in (0 until size)) {
val it = i
val start = -1.0 + (it * temp)
val end = -1.0 + (it + 1) * temp
if (result >= start && result < end) {
return it
}
}
return size-1
}
最终这里我是使用一个简单的javafx应用来展示地图数据(每个地形则是不同颜色)
import javafx.application.Application
import javafx.scene.Scene
import javafx.scene.layout.HBox
import javafx.scene.layout.VBox
import javafx.stage.Stage
import site.starsone.demo.MyUtil.noiseData
import kotlin.random.Random
class MyApp : Application() {
override fun start(primaryStage: Stage?) {
val root = VBox().apply {
prefWidth = 600.0
prefHeight = 600.0
}
val scene = Scene(root, 600.0, 600.0)
val array = getData()
array.forEach {
val hbox = HBox()
it.forEach { resultType ->
val node = VBox().apply {
prefWidth = 1.0
prefHeight = 1.0
style = "-fx-background-color: ${getColor(resultType)};"
}
hbox.children.add(node)
}
root.children.add(hbox)
}
primaryStage!!.title = "Hello World Example"
primaryStage!!.scene = scene
primaryStage!!.show()
}
val list = listOf(
"#FF0000", // Red
"#00FF00", // Green
"#0000FF", // Blue
"#FFA500", // Orange
"#800080" // Purple
)
fun getColor(type: Int): String {
return list[type]
}
fun getData(): Array<IntArray> {
val size = 600
val re = Array(size) { IntArray(size) }
(0 until size).forEach { x ->
(0 until size).forEach { y ->
val data = noiseData(x.toFloat(), y.toFloat())
re[x][y] = getType(data)
}
}
return re
}
fun getType(result: Double): Int {
val size = 5
val temp = 2.0 / size
for (i in (0 until size)) {
val it = i
val start = -1.0 + (it * temp)
val end = -1.0 + (it + 1) * temp
if (result >= start && result < end) {
return it
}
}
return size-1
}
}
fun main() {
Application.launch(MyApp::class.java, "")
}
生成的截图如下所示:
参考
- 《地图随机生成》如何实现属于自己的开放世界_哔哩哔哩_bilibili
- 柏林噪声:程序化生成算法 - 掘金
- 算法工具类代码实现 - Auburn/FastNoiseLite: Fast Portable Noise Library - C# C++ C Java HLSL GLSL JavaScript Rust Go
Libgdx游戏开发(3)——通过柏林噪音算法地图随机地形的更多相关文章
- Libgdx游戏开发(2)——接水滴游戏实现
原文:Libgdx游戏开发(2)--接水滴游戏实现 - Stars-One的杂货小窝 本文使用Kotlin语言开发 通过本文的学习可以初步了解以下基础知识的使用: Basic file access ...
- [libGDX游戏开发教程]使用libGDX进行游戏开发(12)-Action动画
前文章节列表: 使用libGDX进行游戏开发(11)-高级编程技巧 使用libGDX进行游戏开发(10)-音乐音效不求人,程序员也可以DIY 使用libGDX进行游戏开发(9)-场景过渡 ...
- [libGDX游戏开发教程]使用libGDX进行游戏开发(1)-游戏设计
声明:<使用Libgdx进行游戏开发>是一个系列,文章的原文是<Learning Libgdx Game Development>,大家请周知.后续的文章连接在这里 使用Lib ...
- Android游戏开发之主角的移动与地图的平滑滚动
人物移动地图的平滑滚动处理 玩过rpg游戏的朋友应该都知道RPG的游戏地图一般都比较大 今天我和大家分享一下在RPG游戏中如何来处理超出手机屏幕大小的游戏地图. 如图所示为程序效果动画图 地图滚动的原 ...
- 精通libGDX游戏开发-RPG实战-开发游戏的基本前提
说起RPG,大概国人是不会陌生的. 这不得不从中国单机游戏市场说起,由于早期软件市场被盗版杀死,顺带的,单机游戏软件作为软件市场的分支,也没赚什么钱,养不活公司纷纷倒闭,只到RPG游戏<仙剑奇侠 ...
- [libgdx游戏开发教程]使用Libgdx进行游戏开发(11)-高级编程技巧 Box2d和Shader
高级编程技巧只是相对的,其实主要是讲物理模拟和着色器程序的使用. 本章主要讲解利用Box2D并用它来实现萝卜雨,然后是使用单色着色器shader让画面呈现单色状态:http://files.cnblo ...
- [libgdx游戏开发教程]使用Libgdx进行游戏开发(10)-音乐和音效
本章音效文件都来自于公共许可: http://files.cnblogs.com/mignet/sounds.zip 在游戏中,播放背景音乐和音效是基本的功能. Libgdx提供了跨平台的声音播放功能 ...
- [libgdx游戏开发教程]使用Libgdx进行游戏开发(9)-场景过渡
本章主要讲解场景过渡效果的使用.这里将用到Render to Texture(RTT)技术. Libgdx提供了一个类,实现了各种常见的插值算法,不仅适合过渡效果,也适合任意特定行为. 在本游戏里面, ...
- [libgdx游戏开发教程]使用Libgdx进行游戏开发(2)-游戏框架搭建
让我们抛开理论开始code吧. 入口类CanyonBunnyMain的代码: package com.packtpub.libgdx.canyonbunny; import com.badlogic. ...
- 精通libGDX游戏开发-RPG实战-欢迎来到RPG的世界
欢迎来到RPG的世界 本章我会快速的使用tiled这样的瓷砖地图工具,来带领大家创造所设想的世界. 创建并编辑瓷砖地图 瓷砖地图(tile-based map)是广泛应用于各种游戏类型的地图格式,li ...
随机推荐
- OpenTelemetry 简析
简介: OpenTelemetry 是 CNCF 的一个可观测性项目,旨在提供可观测性领域的标准化方案,解决观测数据的数据模型.采集.处理.导出等的标准化问题,提供与三方 vendor 无关的服务. ...
- 10个Bug环环相扣,你能解开几个?
简介:由阿里云云效主办的2021年第3届83行代码挑战赛已经收官.超2万人围观,近4000人参赛,85个团队组团来战.大赛采用游戏闯关玩儿法,融合元宇宙科幻和剧本杀元素,让一众开发者玩得不亦乐乎. ...
- [TP5] 动态绑定指定默认模块, 解决: 控制器不存在:app\index\controller\Api
当在 TP5 入口中简单使用 define('BIND_MODULE','index') 绑定默认模块后,访问 api 模块会提示: 控制器不存在:app\index\controller\Api 这 ...
- dotnet 如何将 Microsoft.Maui.Graphics 对接到 UNO 框架
本文将和大家介绍如何将 Microsoft.Maui.Graphics 对接到 UNO 框架里面.一旦完成 Microsoft.Maui.Graphics 对接,即可让 UNO 框架复用现有的许多绘制 ...
- dotnet DirectX 通过 Vortice 控制台使用 ID2D1DeviceContext 绘制画面
在上一篇博客里面告诉大家,如何使用 Vortice 从零开始控制台创建 Direct2D1 窗口.上一篇博客采用的是 CreateDxgiSurfaceRenderTarget 的方式拿到了 ID2D ...
- WPF 已知问题 InputEventArgs 的 Timestamp 属性是静态的导致事件之间相互影响
本文记录一个 WPF 已知的设计问题,当前此问题已经被大佬修复,这个设计问题刚好属于比较边缘的模块,我写了这么多年的代码还没有踩到这个坑一次,也没有听到有谁提到这个坑 远古时候,不知道大佬是故意还是失 ...
- WPF 已知问题 资源字典树引用与资源寻找的坑
大家都知道,在 WPF 里面,可以让资源字典合并其他资源字典,从而定义出资源字典引用树.然而在资源字典引用树里面,如果没有理清关系,将可以作出一个超级复杂的引用关系网.如果在性能优化中,将网断开部分, ...
- 005_Orcad里创建Homogeneous分裂元件
005_Orcad里创建Homogeneous分裂元件 两种类型Homogeneous和Hetergeneous的区别,都是用来把一个复杂的元件分成多个部分来画,不同的是homogeneous画的每部 ...
- 【Oracle故障处理】ORA-00845: MEMORY_TARGET not supported on this system
场景:由于需要用RMAN恢复数据库,提取以前的数据表中的数据.虚拟机为节省资源调小了内存,启动数据库报了 如下错误: ORA-00845: MEMORY_TARGET not supported on ...
- 虚拟服务器VirtualBox不要太好用
在工作和学习前端的路上遇到过太多的坑,就是跳进坑里了,还要勇敢的爬起来. 本章真的想真心实意的推荐一下,超好用的虚拟服务器.你还在纠结window环境和Mac本的区别吗?是不是上班用的window电脑 ...