原文: 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, "")
}

生成的截图如下所示:

参考

Libgdx游戏开发(3)——通过柏林噪音算法地图随机地形的更多相关文章

  1. Libgdx游戏开发(2)——接水滴游戏实现

    原文:Libgdx游戏开发(2)--接水滴游戏实现 - Stars-One的杂货小窝 本文使用Kotlin语言开发 通过本文的学习可以初步了解以下基础知识的使用: Basic file access ...

  2. [libGDX游戏开发教程]使用libGDX进行游戏开发(12)-Action动画

    前文章节列表:  使用libGDX进行游戏开发(11)-高级编程技巧   使用libGDX进行游戏开发(10)-音乐音效不求人,程序员也可以DIY   使用libGDX进行游戏开发(9)-场景过渡   ...

  3. [libGDX游戏开发教程]使用libGDX进行游戏开发(1)-游戏设计

    声明:<使用Libgdx进行游戏开发>是一个系列,文章的原文是<Learning Libgdx Game Development>,大家请周知.后续的文章连接在这里 使用Lib ...

  4. Android游戏开发之主角的移动与地图的平滑滚动

    人物移动地图的平滑滚动处理 玩过rpg游戏的朋友应该都知道RPG的游戏地图一般都比较大 今天我和大家分享一下在RPG游戏中如何来处理超出手机屏幕大小的游戏地图. 如图所示为程序效果动画图 地图滚动的原 ...

  5. 精通libGDX游戏开发-RPG实战-开发游戏的基本前提

    说起RPG,大概国人是不会陌生的. 这不得不从中国单机游戏市场说起,由于早期软件市场被盗版杀死,顺带的,单机游戏软件作为软件市场的分支,也没赚什么钱,养不活公司纷纷倒闭,只到RPG游戏<仙剑奇侠 ...

  6. [libgdx游戏开发教程]使用Libgdx进行游戏开发(11)-高级编程技巧 Box2d和Shader

    高级编程技巧只是相对的,其实主要是讲物理模拟和着色器程序的使用. 本章主要讲解利用Box2D并用它来实现萝卜雨,然后是使用单色着色器shader让画面呈现单色状态:http://files.cnblo ...

  7. [libgdx游戏开发教程]使用Libgdx进行游戏开发(10)-音乐和音效

    本章音效文件都来自于公共许可: http://files.cnblogs.com/mignet/sounds.zip 在游戏中,播放背景音乐和音效是基本的功能. Libgdx提供了跨平台的声音播放功能 ...

  8. [libgdx游戏开发教程]使用Libgdx进行游戏开发(9)-场景过渡

    本章主要讲解场景过渡效果的使用.这里将用到Render to Texture(RTT)技术. Libgdx提供了一个类,实现了各种常见的插值算法,不仅适合过渡效果,也适合任意特定行为. 在本游戏里面, ...

  9. [libgdx游戏开发教程]使用Libgdx进行游戏开发(2)-游戏框架搭建

    让我们抛开理论开始code吧. 入口类CanyonBunnyMain的代码: package com.packtpub.libgdx.canyonbunny; import com.badlogic. ...

  10. 精通libGDX游戏开发-RPG实战-欢迎来到RPG的世界

    欢迎来到RPG的世界 本章我会快速的使用tiled这样的瓷砖地图工具,来带领大家创造所设想的世界. 创建并编辑瓷砖地图 瓷砖地图(tile-based map)是广泛应用于各种游戏类型的地图格式,li ...

随机推荐

  1. OpenTelemetry 简析

    简介: OpenTelemetry 是 CNCF 的一个可观测性项目,旨在提供可观测性领域的标准化方案,解决观测数据的数据模型.采集.处理.导出等的标准化问题,提供与三方 vendor 无关的服务. ...

  2. 10个Bug环环相扣,你能解开几个?

    ​简介:由阿里云云效主办的2021年第3届83行代码挑战赛已经收官.超2万人围观,近4000人参赛,85个团队组团来战.大赛采用游戏闯关玩儿法,融合元宇宙科幻和剧本杀元素,让一众开发者玩得不亦乐乎. ...

  3. [TP5] 动态绑定指定默认模块, 解决: 控制器不存在:app\index\controller\Api

    当在 TP5 入口中简单使用 define('BIND_MODULE','index') 绑定默认模块后,访问 api 模块会提示: 控制器不存在:app\index\controller\Api 这 ...

  4. dotnet 如何将 Microsoft.Maui.Graphics 对接到 UNO 框架

    本文将和大家介绍如何将 Microsoft.Maui.Graphics 对接到 UNO 框架里面.一旦完成 Microsoft.Maui.Graphics 对接,即可让 UNO 框架复用现有的许多绘制 ...

  5. dotnet DirectX 通过 Vortice 控制台使用 ID2D1DeviceContext 绘制画面

    在上一篇博客里面告诉大家,如何使用 Vortice 从零开始控制台创建 Direct2D1 窗口.上一篇博客采用的是 CreateDxgiSurfaceRenderTarget 的方式拿到了 ID2D ...

  6. WPF 已知问题 InputEventArgs 的 Timestamp 属性是静态的导致事件之间相互影响

    本文记录一个 WPF 已知的设计问题,当前此问题已经被大佬修复,这个设计问题刚好属于比较边缘的模块,我写了这么多年的代码还没有踩到这个坑一次,也没有听到有谁提到这个坑 远古时候,不知道大佬是故意还是失 ...

  7. WPF 已知问题 资源字典树引用与资源寻找的坑

    大家都知道,在 WPF 里面,可以让资源字典合并其他资源字典,从而定义出资源字典引用树.然而在资源字典引用树里面,如果没有理清关系,将可以作出一个超级复杂的引用关系网.如果在性能优化中,将网断开部分, ...

  8. 005_Orcad里创建Homogeneous分裂元件

    005_Orcad里创建Homogeneous分裂元件 两种类型Homogeneous和Hetergeneous的区别,都是用来把一个复杂的元件分成多个部分来画,不同的是homogeneous画的每部 ...

  9. 【Oracle故障处理】ORA-00845: MEMORY_TARGET not supported on this system

    场景:由于需要用RMAN恢复数据库,提取以前的数据表中的数据.虚拟机为节省资源调小了内存,启动数据库报了 如下错误: ORA-00845: MEMORY_TARGET not supported on ...

  10. 虚拟服务器VirtualBox不要太好用

    在工作和学习前端的路上遇到过太多的坑,就是跳进坑里了,还要勇敢的爬起来. 本章真的想真心实意的推荐一下,超好用的虚拟服务器.你还在纠结window环境和Mac本的区别吗?是不是上班用的window电脑 ...