|
6 | 6 | import com.almasb.fxgl.entity.SpawnData; |
7 | 7 | import com.almasb.fxgl.profile.DataFile; |
8 | 8 | import com.almasb.fxgl.profile.SaveLoadHandler; |
| 9 | +import com.github.codestorm.bounceverse.AssetsPath; |
9 | 10 | import com.github.codestorm.bounceverse.factory.entities.BallFactory; |
10 | 11 | import com.github.codestorm.bounceverse.factory.entities.BrickFactory; |
11 | 12 | import com.github.codestorm.bounceverse.factory.entities.BulletFactory; |
|
17 | 18 | import com.github.codestorm.bounceverse.ui.ingame.Hearts; |
18 | 19 |
|
19 | 20 | import javafx.geometry.Point2D; |
| 21 | +import javafx.scene.paint.Color; |
| 22 | + |
| 23 | +import libs.FastNoiseLite; |
20 | 24 |
|
21 | 25 | import org.jetbrains.annotations.NotNull; |
22 | 26 |
|
| 27 | +import java.util.ArrayList; |
| 28 | +import java.util.Comparator; |
23 | 29 | import java.util.Map; |
24 | 30 |
|
25 | 31 | public final class GameSystem extends InitialSystem { |
@@ -100,37 +106,98 @@ public static void walls() { |
100 | 106 | FXGL.spawn("wallRight"); |
101 | 107 | } |
102 | 108 |
|
103 | | - public static void brick() { |
104 | | - var rows = 6; |
105 | | - var cols = 10; |
106 | | - double startX = 85; |
107 | | - double startY = 50; |
108 | | - double brickWidth = 80; |
109 | | - double brickHeight = 30; |
110 | | - double spacingX = 5; |
111 | | - double spacingY = 5; |
112 | | - |
113 | | - for (var y = 0; y < rows; y++) { |
114 | | - for (var x = 0; x < cols; x++) { |
115 | | - var posX = startX + x * (brickWidth + spacingX); |
116 | | - var posY = startY + y * (brickHeight + spacingY); |
117 | | - |
118 | | - // Xác định loại brick theo hàng (bạn có thể chỉnh lại tuỳ ý) |
119 | | - String type; |
120 | | - switch (y) { |
121 | | - case 0 -> type = "shieldBrick"; // Hàng đầu có khiên |
122 | | - // case 1 -> type = "explodingBrick"; // Hàng thứ 2 |
123 | | - // nổ |
124 | | - // case 2 -> type = "specialBrick"; // Hàng thứ 3 rơi |
125 | | - // power-up |
126 | | - case 3 -> type = "strongBrick"; // Hàng thứ 4 trâu |
127 | | - default -> type = "normalBrick"; // Còn lại là thường |
128 | | - } |
| 109 | + public static void brick(int seed) { |
| 110 | + var noise = new FastNoiseLite(seed); |
| 111 | + var colors = AssetsPath.Textures.Bricks.COLORS.keySet().toArray(new Color[0]); |
| 112 | + var color = colors[Math.floorMod(seed, colors.length)]; |
| 113 | + |
| 114 | + noise.SetNoiseType(FastNoiseLite.NoiseType.OpenSimplex2); |
| 115 | + noise.SetFrequency(0.2f); |
| 116 | + noise.SetFractalType(FastNoiseLite.FractalType.FBm); |
| 117 | + noise.SetFractalOctaves(4); |
| 118 | + noise.SetFractalLacunarity(2.1f); |
| 119 | + noise.SetFractalGain(0.6f); |
| 120 | + noise.SetDomainWarpType(FastNoiseLite.DomainWarpType.OpenSimplex2); |
| 121 | + noise.SetDomainWarpAmp(6.0f); |
| 122 | + |
| 123 | + final var rows = 5; |
| 124 | + final var cols = 10; |
| 125 | + final var amount = 36; |
| 126 | + |
| 127 | + final var appWidth = FXGL.getAppWidth(); |
| 128 | + final var appHeight = FXGL.getAppHeight(); |
| 129 | + final double marginX = 40; |
| 130 | + final double startY = 60; |
| 131 | + final var verticalCoverageRatio = 0.30; |
| 132 | + final double spacingX = 5; |
| 133 | + final double spacingY = 5; |
| 134 | + final var aspect = 30.0 / 80.0; |
| 135 | + |
| 136 | + var availableWidth = appWidth - 2 * marginX; |
| 137 | + var wFromWidth = (availableWidth - (cols - 1) * spacingX) / cols; |
| 138 | + var hFromWidth = wFromWidth * aspect; |
| 139 | + |
| 140 | + var targetBrickAreaHeight = appHeight * verticalCoverageRatio; |
| 141 | + var hMaxByHeight = (targetBrickAreaHeight - (rows - 1) * spacingY) / rows; |
| 142 | + |
| 143 | + var brickW = wFromWidth; |
| 144 | + var brickH = hFromWidth; |
| 145 | + |
| 146 | + if (brickH > hMaxByHeight) { |
| 147 | + brickH = hMaxByHeight; |
| 148 | + brickW = brickH / aspect; |
| 149 | + } |
129 | 150 |
|
130 | | - var data = new SpawnData(); |
131 | | - data.put("pos", new Point2D(posX, posY)); |
132 | | - FXGL.spawn(type, data); |
| 151 | + double minW = 40, maxW = 160; |
| 152 | + if (brickW < minW) brickW = minW; |
| 153 | + if (brickW > maxW) brickW = maxW; |
| 154 | + brickH = brickW * aspect; |
| 155 | + |
| 156 | + var totalH = rows * brickH + (rows - 1) * spacingY; |
| 157 | + if (totalH > targetBrickAreaHeight) { |
| 158 | + brickH = hMaxByHeight; |
| 159 | + brickW = brickH / aspect; |
| 160 | + } |
| 161 | + |
| 162 | + var brickWidth = (int) Math.round(brickW); |
| 163 | + var brickHeight = (int) Math.round(brickH); |
| 164 | + |
| 165 | + var totalW = cols * brickWidth + (cols - 1) * spacingX; |
| 166 | + var startX = Math.max(marginX, (appWidth - totalW) / 2.0); |
| 167 | + |
| 168 | + // Lấy ra amount phần tử có độ cao cao nhất |
| 169 | + record Cell(int gx, int gy, double x, double y, float n) {} |
| 170 | + var cells = new ArrayList<Cell>(rows * cols); |
| 171 | + for (var gy = 0; gy < rows; gy++) { |
| 172 | + for (var gx = 0; gx < cols; gx++) { |
| 173 | + var posX = startX + gx * (brickWidth + spacingX); |
| 174 | + var posY = startY + gy * (brickHeight + spacingY); |
| 175 | + var n = noise.GetNoise(gx, gy); |
| 176 | + cells.add(new Cell(gx, gy, posX, posY, n)); |
| 177 | + } |
| 178 | + } |
| 179 | + cells.sort(Comparator.comparing(Cell::n).reversed()); |
| 180 | + var target = Math.min(amount, cells.size()); |
| 181 | + |
| 182 | + for (var i = 0; i < target; i++) { |
| 183 | + var c = cells.get(i); |
| 184 | + var n = c.n(); |
| 185 | + |
| 186 | + final String type; |
| 187 | + if (n > 0.5f) { |
| 188 | + type = "shieldBrick"; |
| 189 | + } else if (n > 0.3f) { |
| 190 | + type = "strongBrick"; |
| 191 | + } else { |
| 192 | + type = "normalBrick"; |
133 | 193 | } |
| 194 | + |
| 195 | + var data = new SpawnData(); |
| 196 | + data.put("pos", new Point2D(c.x(), c.y())); |
| 197 | + data.put("width", brickWidth); |
| 198 | + data.put("height", brickHeight); |
| 199 | + data.put("color", color); |
| 200 | + FXGL.spawn(type, data); |
134 | 201 | } |
135 | 202 | } |
136 | 203 |
|
@@ -202,6 +269,7 @@ public void addAll() { |
202 | 269 | if (isInitialized) { |
203 | 270 | return; |
204 | 271 | } |
| 272 | + // TODO: issue sau khi reset màn chơi thì không thêm lại UI |
205 | 273 |
|
206 | 274 | addScoreDisplay(); |
207 | 275 | addHeartsDisplay(); |
@@ -247,7 +315,8 @@ private static final class Holder { |
247 | 315 | public void apply() { |
248 | 316 | EntitySpawn.addFactory(); |
249 | 317 | EntitySpawn.walls(); |
250 | | - EntitySpawn.brick(); |
| 318 | + EntitySpawn.brick( |
| 319 | + (int) (System.currentTimeMillis() & 0x7FFFFFFF)); // TODO: Custom chọn seed |
251 | 320 | EntitySpawn.paddle(); |
252 | 321 | EntitySpawn.ball(); |
253 | 322 |
|
|
0 commit comments