From 4c0fc8f608f3856dcce603ccb57552321f9e0e49 Mon Sep 17 00:00:00 2001 From: fredboy Date: Wed, 17 Apr 2024 01:21:42 +0700 Subject: [PATCH] Refactor world generator Fix freeze when drop appears --- android/build.gradle | 4 +- build.gradle | 2 +- core/build.gradle | 4 +- .../cavedroid/game/GamePhysics.java | 17 +- .../cavedroid/game/model/world/Biome.kt | 6 + .../world/generator/WorldGeneratorConfig.kt | 37 ++++ .../cavedroid/game/objects/Drop.java | 28 +-- .../cavedroid/game/world/GameWorld.java | 5 +- .../game/world/GameWorldGenerator.kt | 170 ++++++++++-------- desktop/build.gradle | 2 +- 10 files changed, 171 insertions(+), 104 deletions(-) create mode 100644 core/src/ru/deadsoftware/cavedroid/game/model/world/Biome.kt create mode 100644 core/src/ru/deadsoftware/cavedroid/game/model/world/generator/WorldGeneratorConfig.kt diff --git a/android/build.gradle b/android/build.gradle index 06d3005..5ad0a31 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -20,8 +20,8 @@ android { } } compileOptions { - sourceCompatibility 11 - targetCompatibility 11 + sourceCompatibility 17 + targetCompatibility 17 } packagingOptions { exclude 'META-INF/robovm/ios/robovm.xml' diff --git a/build.gradle b/build.gradle index 8250a6a..a67d071 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:8.2.2' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.23" } } diff --git a/core/build.gradle b/core/build.gradle index 2c25846..d968841 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -4,8 +4,8 @@ plugins { id "idea" } -sourceCompatibility = 11 +sourceCompatibility = 17 sourceSets.main.java.srcDirs = [ "src/" ] -java.targetCompatibility = JavaVersion.VERSION_11 \ No newline at end of file +java.targetCompatibility = JavaVersion.VERSION_17 \ No newline at end of file diff --git a/core/src/ru/deadsoftware/cavedroid/game/GamePhysics.java b/core/src/ru/deadsoftware/cavedroid/game/GamePhysics.java index 6b69faa..e3ceb53 100644 --- a/core/src/ru/deadsoftware/cavedroid/game/GamePhysics.java +++ b/core/src/ru/deadsoftware/cavedroid/game/GamePhysics.java @@ -96,18 +96,19 @@ public class GamePhysics { private void dropPhy(Drop drop, float delta) { int dropToPlayer = drop.closeToPlayer(mGameWorld, mMobsController.getPlayer()); + if (dropToPlayer > 0) { - drop.moveToPlayer(mGameWorld, mMobsController.getPlayer(), dropToPlayer); + drop.moveToPlayer(mGameWorld, mMobsController.getPlayer(), dropToPlayer, delta); } else { - if (drop.getVelocity().x >= .5f) { - drop.getVelocity().x -= .5f; - } else if (drop.getVelocity().x <= -.5f) { - drop.getVelocity().x += .5f; + if (drop.getVelocity().x >= 300f) { + drop.getVelocity().x = 300f; + } else if (drop.getVelocity().x <= -300f) { + drop.getVelocity().x = -300f; } else { drop.getVelocity().x = 0; } - if (drop.getVelocity().y < 9) { - drop.getVelocity().y += gravity.y / 4; + if (drop.getVelocity().y < PL_TERMINAL_VELOCITY) { + drop.getVelocity().y += gravity.y * delta; } } drop.move(delta); @@ -116,7 +117,7 @@ public class GamePhysics { if (checkColl(drop)) { drop.getVelocity().set(0, -1); do { - drop.move(delta); + drop.move(1); } while (checkColl(drop)); drop.getVelocity().setZero(); } diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/world/Biome.kt b/core/src/ru/deadsoftware/cavedroid/game/model/world/Biome.kt new file mode 100644 index 0000000..e2d3d7d --- /dev/null +++ b/core/src/ru/deadsoftware/cavedroid/game/model/world/Biome.kt @@ -0,0 +1,6 @@ +package ru.deadsoftware.cavedroid.game.model.world + +enum class Biome { + PLAINS, + DESERT +} \ No newline at end of file diff --git a/core/src/ru/deadsoftware/cavedroid/game/model/world/generator/WorldGeneratorConfig.kt b/core/src/ru/deadsoftware/cavedroid/game/model/world/generator/WorldGeneratorConfig.kt new file mode 100644 index 0000000..251b901 --- /dev/null +++ b/core/src/ru/deadsoftware/cavedroid/game/model/world/generator/WorldGeneratorConfig.kt @@ -0,0 +1,37 @@ +package ru.deadsoftware.cavedroid.game.model.world.generator + +import com.badlogic.gdx.utils.TimeUtils +import ru.deadsoftware.cavedroid.game.model.world.Biome + +data class WorldGeneratorConfig( + val width: Int, + val height: Int, + val seed: Long, + val minSurfaceHeight: Int, + val maxSurfaceHeight: Int, + val biomes: List, + val minBiomeSize: Int, + val seaLevel: Int, +) { + + companion object { + private const val DEFAULT_WIDTH = 1024 + private const val DEFAULT_HEIGHT = 256 + private const val DEFAULT_MIN_BIOME_SIZE = 64 + + fun getDefaultWithSeed(seed: Long): WorldGeneratorConfig { + return WorldGeneratorConfig( + width = DEFAULT_WIDTH, + height = DEFAULT_HEIGHT, + seed = TimeUtils.millis(), + minSurfaceHeight = DEFAULT_HEIGHT / 4, + maxSurfaceHeight = DEFAULT_HEIGHT * 3 / 4, + biomes = Biome.entries.toList(), + minBiomeSize = DEFAULT_MIN_BIOME_SIZE, + seaLevel = DEFAULT_HEIGHT / 2, + ) + } + + } + +} diff --git a/core/src/ru/deadsoftware/cavedroid/game/objects/Drop.java b/core/src/ru/deadsoftware/cavedroid/game/objects/Drop.java index 995c79e..9e4e570 100644 --- a/core/src/ru/deadsoftware/cavedroid/game/objects/Drop.java +++ b/core/src/ru/deadsoftware/cavedroid/game/objects/Drop.java @@ -44,7 +44,7 @@ public class Drop extends Rectangle implements Serializable { return 0; } - public void moveToPlayer(GameWorld gameWorld, Player player, int ctp) { + public void moveToPlayer(GameWorld gameWorld, Player player, int ctp, float delta) { if (ctp > 0) { float px = player.getX(); float py = player.getY(); @@ -61,29 +61,29 @@ public class Drop extends Rectangle implements Serializable { float dx = 0, dy = 0; if (px + player.getWidth() < x + 4) { - dx = -.5f; + dx = -300f; } else if (px > x + 4) { - dx = .5f; + dx = 300f; } if (py + player.getHeight() < y + 4) { - dy = -.5f; + dy = -300f; } else if (py > y + 4) { - dy = .5f; + dy = .300f; } - velocity.add(dx, dy); + velocity.add(dx * delta, dy * delta); - if (velocity.x > 2) { - velocity.x = 1; - } else if (velocity.x < -2) { - velocity.x = -1; + if (velocity.x > 300f) { + velocity.x = 300f; + } else if (velocity.x < -300f) { + velocity.x = -300f; } - if (velocity.y > 2) { - velocity.y = 1; - } else if (velocity.y < -2) { - velocity.y = -1; + if (velocity.y > 300f) { + velocity.y = 300f; + } else if (velocity.y < -300f) { + velocity.y = -300f; } } } diff --git a/core/src/ru/deadsoftware/cavedroid/game/world/GameWorld.java b/core/src/ru/deadsoftware/cavedroid/game/world/GameWorld.java index 60a34e1..b5ac7ee 100644 --- a/core/src/ru/deadsoftware/cavedroid/game/world/GameWorld.java +++ b/core/src/ru/deadsoftware/cavedroid/game/world/GameWorld.java @@ -8,6 +8,7 @@ import ru.deadsoftware.cavedroid.game.GameScope; import ru.deadsoftware.cavedroid.game.mobs.FallingGravel; import ru.deadsoftware.cavedroid.game.mobs.FallingSand; import ru.deadsoftware.cavedroid.game.mobs.MobsController; +import ru.deadsoftware.cavedroid.game.model.world.generator.WorldGeneratorConfig; import ru.deadsoftware.cavedroid.game.objects.Block; import ru.deadsoftware.cavedroid.game.objects.DropController; @@ -51,7 +52,7 @@ public class GameWorld implements Disposable { if (isNewGame) { mWidth = DEFAULT_WIDTH; mHeight = DEFAULT_HEIGHT; - Pair maps = GameWorldGenerator.INSTANCE.generate(mWidth, mHeight, TimeUtils.millis()); + Pair maps = new GameWorldGenerator(WorldGeneratorConfig.Companion.getDefaultWithSeed(TimeUtils.millis())).generate(); mForeMap = maps.getFirst(); mBackMap = maps.getSecond(); mMobsController.getPlayer().respawn(this); @@ -217,7 +218,7 @@ public class GameWorld implements Disposable { setForeMap(x, y, 0); mMobsController.addMob(FallingSand.class, x * 16, y * 16); updateBlock(x, y - 1); - } + } } if (getForeMap(x, y) == 11) { diff --git a/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldGenerator.kt b/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldGenerator.kt index bf37f45..8776254 100644 --- a/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldGenerator.kt +++ b/core/src/ru/deadsoftware/cavedroid/game/world/GameWorldGenerator.kt @@ -1,135 +1,157 @@ package ru.deadsoftware.cavedroid.game.world -import com.badlogic.gdx.utils.TimeUtils import ru.deadsoftware.cavedroid.game.GameItems +import ru.deadsoftware.cavedroid.game.model.world.Biome +import ru.deadsoftware.cavedroid.game.model.world.generator.WorldGeneratorConfig import kotlin.math.abs +import kotlin.math.max import kotlin.random.Random -object GameWorldGenerator { +class GameWorldGenerator( + private val config: WorldGeneratorConfig, +) { - private const val BIOME_MIN_SIZE = 64 + private val random = Random(config.seed) - private enum class Biome { - PLAINS, - DESERT - } + private val foreMap by lazy { Array(config.width) { IntArray(config.height) } } + private val backMap by lazy { Array(config.width) { IntArray(config.height) } } + + private val heights by lazy { generateHeights() } + private val biomesMap by lazy { generateBiomes() } + + private fun generateHeights(): IntArray { + val surfaceHeightRange = config.minSurfaceHeight .. config.maxSurfaceHeight + val result = IntArray(config.width) - private fun generateHeights(width: Int, min: Int, max: Int, random: Random) = IntArray(width).apply { - set(0, (min + max) / 2) - for (x in 1 until width) { - val previous = get(x - 1) + result[0] = (config.minSurfaceHeight + config.maxSurfaceHeight) / 2 + + for (x in 1 ..< config.width) { + val previous = result[x - 1] var d = random.nextInt(-5, 6).let { if (it !in -4..4) it / abs(it) else 0 } - if (previous + d !in min..max) { d = -d } - if (lastIndex - x < abs(get(0) - previous) * 3) { - d = get(0).compareTo(previous).let { if (it != 0) it / abs(it) else 0 } + if (previous + d !in surfaceHeightRange) { d = -d } + + if (result.lastIndex - x < abs(result[0] - previous) * 3) { + d = result[0].compareTo(previous).let { if (it != 0) it / abs(it) else 0 } } - set(x, get(x - 1) + d) + result[x] = result[x - 1] + d } + + return result } - private fun generateBiomes(width: Int, random: Random) = buildMap { + private fun generateBiomes(): Map { val xSequence = sequence { var lastX = 0 var count = 0 - while (lastX < width - BIOME_MIN_SIZE - 1) { + while (lastX < config.width - config.minBiomeSize - 1) { yield(lastX) - lastX = random.nextInt(lastX + BIOME_MIN_SIZE, width) + lastX = random.nextInt(lastX + config.minBiomeSize, config.width) count++ } } - return xSequence.associateWith { Biome.values()[random.nextInt(Biome.values().size)] } + return xSequence.associateWith { config.biomes.random(random) } } - private fun plainsBiome( - foreMap: Array, - backMap: Array, - width: Int, - height: Int, - x: Int, - xHeight: Int, - random: Random, - ) { - foreMap[x][xHeight] = GameItems.getBlockId("grass") - foreMap[x][height - 1] = GameItems.getBlockId("bedrock") - backMap[x][xHeight] = GameItems.getBlockId("grass") - backMap[x][height - 1] = GameItems.getBlockId("bedrock") - - for (y in xHeight + 1 until height - 1) { + private fun plainsBiome(x: Int) { + assert(x in 0 ..< config.width) { "x not in range of world width" } + + val surfaceHeight = heights[x] + + val grassId = GameItems.getBlockId("grass") + val bedrockId = GameItems.getBlockId("bedrock") + val dirtId = GameItems.getBlockId("dirt") + val stoneId = GameItems.getBlockId("stone") + + foreMap[x][surfaceHeight] = grassId + foreMap[x][config.height - 1] = bedrockId + backMap[x][surfaceHeight] = grassId + backMap[x][config.height - 1] = bedrockId + + for (y in surfaceHeight + 1 ..< config.height - 1) { foreMap[x][y] = when { - y < xHeight + random.nextInt(5, 8) -> GameItems.getBlockId("dirt") - else -> GameItems.getBlockId("stone") + y < surfaceHeight + random.nextInt(5, 8) -> dirtId + else -> stoneId } backMap[x][y] = foreMap[x][y] } } - private fun desertBiome( - foreMap: Array, - backMap: Array, - width: Int, - height: Int, - x: Int, - xHeight: Int, - random: Random, - ) { - foreMap[x][xHeight] = GameItems.getBlockId("sand") - foreMap[x][height - 1] = GameItems.getBlockId("bedrock") - backMap[x][xHeight] = GameItems.getBlockId("sand") - backMap[x][height - 1] = GameItems.getBlockId("bedrock") - - for (y in xHeight + 1 until height - 1) { + private fun desertBiome(x: Int) { + assert(x in 0 ..< config.width) { "x not in range of world width" } + + val surfaceHeight = heights[x] + + val sandId = GameItems.getBlockId("sand") + val bedrockId = GameItems.getBlockId("bedrock") + val sandstoneId = GameItems.getBlockId("sandstone") + val stoneId = GameItems.getBlockId("stone") + + + foreMap[x][surfaceHeight] = sandId + foreMap[x][config.height - 1] = bedrockId + backMap[x][surfaceHeight] = sandId + backMap[x][config.height - 1] = bedrockId + + for (y in surfaceHeight + 1 ..< config.height - 1) { foreMap[x][y] = when { - y < xHeight + random.nextInt(5, 8) -> GameItems.getBlockId("sand") - else -> GameItems.getBlockId("stone") + y < surfaceHeight + random.nextInt(5, 8) -> sandId + y < surfaceHeight + random.nextInt(0, 2) -> sandstoneId + else -> stoneId } backMap[x][y] = foreMap[x][y] } + + if (surfaceHeight < config.seaLevel && random.nextInt(100) < 5) { + generateCactus(x) + } } - private fun fillWater(foreMap: Array, width: Int, height: Int, waterLevel: Int) { - for (x in 0 until width) { - for (y in waterLevel until height) { + private fun fillWater() { + val waterId = GameItems.getBlockId("water") + + for (x in 0 ..< config.width) { + for (y in config.seaLevel ..< config.height) { if (foreMap[x][y] != 0) { break } - foreMap[x][y] = GameItems.getBlockId("water") + foreMap[x][y] = waterId } } } + private fun generateCactus(x: Int) { + val cactusId = GameItems.getBlockId("cactus") + val cactusHeight = random.nextInt(5) + val h = heights[x] - 1 + + for (y in h downTo max(0, h - cactusHeight)) { + foreMap[x][y] = cactusId + } + } + /** - * Generates world of given width and height with given seed - * @param width world width - * @param height world height - * @param seed seed for random number generator - * @return pair of foreground and background layers + * Generate world */ - fun generate(width: Int, height: Int, seed: Long = TimeUtils.millis()): Pair, Array> { - val random = Random(seed) - val foreMap = Array(width) { IntArray(height) } - val backMap = Array(width) { IntArray(width) } - val heightsMap = generateHeights(width, height / 4, height * 3 / 4, random) - val biomesMap = generateBiomes(width, random) - + fun generate(): Pair, Array> { var biome = Biome.PLAINS - for (x in 0 until width) { - val xHeight = heightsMap[x] + for (x in 0 until config.width) { + val xHeight = heights[x] biome = biomesMap[x] ?: biome when (biome) { - Biome.PLAINS -> plainsBiome(foreMap, backMap, width, height, x, xHeight, random) - Biome.DESERT -> desertBiome(foreMap, backMap, width, height, x, xHeight, random) + Biome.PLAINS -> plainsBiome(x) + Biome.DESERT -> desertBiome(x) } } - fillWater(foreMap, width, height, height / 2) + fillWater() return Pair(foreMap, backMap) } diff --git a/desktop/build.gradle b/desktop/build.gradle index 88ef144..57fae41 100644 --- a/desktop/build.gradle +++ b/desktop/build.gradle @@ -1,6 +1,6 @@ apply plugin: "kotlin" -sourceCompatibility = 11 +sourceCompatibility = 17 sourceSets.main.java.srcDirs = [ "src/" ] sourceSets.main.resources.srcDirs = ["../android/assets"] -- 2.29.2