原文: Libgdx游戏开发(3)——通过柏林噪音算法地图随机地形-Stars-One的杂货小窝

在B站刷到了随机地图生成的视频,随手学习下并做下记录

注: 本篇使用javafx应用作演示,算是了解这个算法的使用,后续会再出篇libgdx生成地图的示例

说明

抛开算法实现,首先认知柏林噪音算法

一般我们想要随机数,会指定个范围,如0.0-1.0之间任意小数,而柏林算法的结果范围就是[-1,1]

柏林算法已经实现了对应一维到四维的算法,本文以二维来进行讲解,实现我自己的一个游戏随机地图效果,我以五种颜色区分不同地形,通过javafx应用展示出一个600*600的游戏地图地形,如下图所示

代码实现

需求如下:

使用一个二维数组来进行存储当前地图,每个数值里存放一个int类型,用来标明地图的地形,类型有0-4(即int数值可取[0,4]范围)

我们如果使用单纯的随机算法,地形可能就会很乱,所以我们采用柏林噪音算法来得到我们的对应地形,使其有个平滑的过渡效果,方法如下:

  1. //这里我使用的是java实现版本,FastNoiseLite这个类可见下文列出的参考链接
  2. val noise = FastNoiseLite()
  3. noise.SetNoiseType(FastNoiseLite.NoiseType.OpenSimplex2)
  4. noise.SetSeed(1000)
  5. 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,当然还可以继续叠加下去,平滑过渡的效果就越好

转为代码:

  1. val noise = FastNoiseLite()
  2. noise.SetNoiseType(FastNoiseLite.NoiseType.OpenSimplex2)
  3. noise.SetSeed(1000)
  4. 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)

对上面方法封装下,得到下面代码:

  1. object MyUtil{
  2. val noise by lazy {
  3. val noise = FastNoiseLite()
  4. noise.SetNoiseType(FastNoiseLite.NoiseType.OpenSimplex2)
  5. //1000以内随机,可以自行更改
  6. noise.SetSeed(Random.nextInt(1000))
  7. noise
  8. }
  9. //size默认为4,表明进行3次叠加,如上面示例
  10. fun noiseData(x: Float, y: Float, size: Int = 4): Double {
  11. val result = (0 until size).sumOf {
  12. //位与操作,相当于的2的n次方
  13. // 1 shr 0 = 1
  14. // 1 shr 1 = 2
  15. val num = 1 shl it
  16. 1.0 * (1 / num) * noise.GetNoise(x * num, y * num)
  17. }
  18. return result
  19. }
  20. }

这里需要注意的是,我们封装得保证我们的每次调用函数的时候FastNoiseLite用的是同个对象,否则就会出现其他效果,和我们预期不符,所以上面封装我使用了单例模式

使用:

  1. fun main(){
  2. //4*4的二维数据
  3. val size = 4
  4. val re = Array(size) { IntArray(size) }
  5. (0 until size).forEach {x->
  6. var buffer = StringBuffer()
  7. (0 until size).forEach {y->
  8. val data = noiseData(x.toFloat(),y.toFloat())
  9. //todo 这里还需要将data转为0-4的int数值并存放在二维数组中
  10. buffer.append(data.toString()+",")
  11. }
  12. println(buffer.toString())
  13. buffer=StringBuffer()
  14. }
  15. }

上面只是得到的对应的噪音数据,而我们按照范围,将其转为对应的0-4数值,代码如下:

  1. fun getType(result: Double): Int {
  2. return when {
  3. result >= -1 && result < -0.6 -> 0
  4. result >= -0.6 && result < -0.2 -> 1
  5. else -> 3
  6. }
  7. }

result的数据范围在[-1,1]之间,所以可以根据自己的需求,符合对应范围,自行给数值即可,上面代码只是一个简单的示例,你可以随意划分;

于是按照我的需求,我自行封装成下面的方法(可能有些难以理解,大概解释就是划分为n等分,每个区间有个下标,然后数值符合对应区间的,则返回对应区间下标)

  1. fun getType(result: Double): Int {
  2. //划分为5等分
  3. val size = 5
  4. val temp = 2.0 / size
  5. for (i in (0 until size)) {
  6. val it = i
  7. val start = -1.0 + (it * temp)
  8. val end = -1.0 + (it + 1) * temp
  9. if (result >= start && result < end) {
  10. return it
  11. }
  12. }
  13. return size-1
  14. }

最终这里我是使用一个简单的javafx应用来展示地图数据(每个地形则是不同颜色)

  1. import javafx.application.Application
  2. import javafx.scene.Scene
  3. import javafx.scene.layout.HBox
  4. import javafx.scene.layout.VBox
  5. import javafx.stage.Stage
  6. import site.starsone.demo.MyUtil.noiseData
  7. import kotlin.random.Random
  8. class MyApp : Application() {
  9. override fun start(primaryStage: Stage?) {
  10. val root = VBox().apply {
  11. prefWidth = 600.0
  12. prefHeight = 600.0
  13. }
  14. val scene = Scene(root, 600.0, 600.0)
  15. val array = getData()
  16. array.forEach {
  17. val hbox = HBox()
  18. it.forEach { resultType ->
  19. val node = VBox().apply {
  20. prefWidth = 1.0
  21. prefHeight = 1.0
  22. style = "-fx-background-color: ${getColor(resultType)};"
  23. }
  24. hbox.children.add(node)
  25. }
  26. root.children.add(hbox)
  27. }
  28. primaryStage!!.title = "Hello World Example"
  29. primaryStage!!.scene = scene
  30. primaryStage!!.show()
  31. }
  32. val list = listOf(
  33. "#FF0000", // Red
  34. "#00FF00", // Green
  35. "#0000FF", // Blue
  36. "#FFA500", // Orange
  37. "#800080" // Purple
  38. )
  39. fun getColor(type: Int): String {
  40. return list[type]
  41. }
  42. fun getData(): Array<IntArray> {
  43. val size = 600
  44. val re = Array(size) { IntArray(size) }
  45. (0 until size).forEach { x ->
  46. (0 until size).forEach { y ->
  47. val data = noiseData(x.toFloat(), y.toFloat())
  48. re[x][y] = getType(data)
  49. }
  50. }
  51. return re
  52. }
  53. fun getType(result: Double): Int {
  54. val size = 5
  55. val temp = 2.0 / size
  56. for (i in (0 until size)) {
  57. val it = i
  58. val start = -1.0 + (it * temp)
  59. val end = -1.0 + (it + 1) * temp
  60. if (result >= start && result < end) {
  61. return it
  62. }
  63. }
  64. return size-1
  65. }
  66. }
  67. fun main() {
  68. Application.launch(MyApp::class.java, "")
  69. }

生成的截图如下所示:

参考

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. 龙蜥正式开源 SysOM:百万级实战经验打造!一站式运维管理平台 | 龙蜥技术

    ​简介:SysOM集监控.告警.诊断.修复.安全能力于一体的操作系统运维平台. ​ 文/系统运维 SIG 如果你被突如其来的 OOPS 和满屏奇怪的函数弄得满头问号?机器内存明明很大,却申请不出来内存 ...

  2. 拥抱云原生,Fluid结合JindoFS :阿里云OSS加速利器

    简介: Fluid 是一个开源的 Kubernetes 原生的分布式数据集编排和加速引擎,主要服务于云原生场景下的数据密集型应用.在 Fluid 上使用和部署 JindoRuntime 实现数据集的可 ...

  3. Spring官方RSocket Broker 0.3.0发布: 快速构建你的RSocket架构

    ​简介:Spring官方的RSocket Broker其实开发已经非常久了,我以为会伴随着Spring Cloud 2021.0发布的,但是没有发生.不过Spring RSocket Broker还是 ...

  4. 技术干货 | 使用 mPaaS 配置 SM2 国密加密指南

    ​简介:随着移动智能终端的广泛应用,敏感信息极易被监控或盗取,给国家.企事业及个人带来极大政治.经济损失.金融和重要领域的各个企业正在逐步落实并完成国产密码改造工作.为解决客户侧因更换加密算法造成的种 ...

  5. 011_元件封装FootPrint处理

    011_元件封装FootPrint处理 原理图的引脚与PCB的引脚数量一一对应,IC的PCB Foot Print属性添加好属性,后面就不用一个个添加了.

  6. OLAP开源引擎对比之历史概述

    前言 OLAP概念诞生于1993年,工具则出现在更早以前,有史可查的第一款OLAP工具是1975年问世的Express,后来走进千家万户的Excel也可归为此类,所以虽然很多数据人可能没听过OLAP, ...

  7. SpringBoot3.1.5对应新版本SpringCloud开发(2)-Eureka的负载均衡

    Eureka的负载均衡 负载均衡原理 负载均衡流程 老版本流程介绍 当order-servic发起的请求进入Ribbon后会被LoadBalancerInterceptor负载均衡拦截器拦截,拦截器获 ...

  8. gorm 动态拼接查询条件

    结构体 type Mould struct { MouldId string `grom:"column:mouldID"` MouldInteriorID string `gro ...

  9. WEB服务与NGINX(16)-网站logo之favicon.ico文件

    目录 1. 网站logo之favicon.ico文件 1.1 favicon.ico文件的作用 1.2 favicon.ico文件带来的问题 1. 网站logo之favicon.ico文件 1.1 f ...

  10. Ubuntu更新源文件报错:E: 仓库 “http://ppa.launchpad.net/chris-lea/node.js/ubuntu bionic Release” 没有 Release 文件。

    E: 仓库 "http://ppa.launchpad.net/chris-lea/node.js/ubuntu bionic Release" 没有 Release 文件. 一条 ...