如何用JPanel与Java Swing实现2D地形生成及场景切换?
解决方案
核心问题:要不要用线程持续检测坐标?
完全没必要!更高效的做法是在角色移动的逻辑里直接判断边界——毕竟只有角色动的时候才会碰到边界,一直轮询既浪费资源,还容易踩Swing的线程安全坑(所有UI操作必须在事件调度线程EDT里做)。如果是用线程驱动角色动画,那就在动画线程的每一步移动后加边界判断就行,但UI操作一定要通过SwingUtilities.invokeLater切到EDT执行。
1. 正确生成3×3地形面板数组
你第二段代码的问题是:只生成了一次随机地形类型,导致所有非中心面板全是同一种地形。得在每个非中心面板的循环里重新生成随机数,同时给中心的“house”面板做个标记。
import java.awt.Color; import java.util.Random; import javax.swing.JPanel; // 初始化3×3的世界面板数组 JPanel[][] world = new JPanel[3][3]; Random random = new Random(); for (int i = 0; i < world.length; i++) { for (int j = 0; j < world[i].length; j++) { if (i == 1 && j == 1) { // 中心面板:标记为house,比如设灰色背景+名称 world[i][j] = new JPanel(); world[i][j].setBackground(Color.GRAY); world[i][j].setName("house"); } else { // 随机生成森林(深绿)或平原(浅绿) int terrainTypeGen = random.nextInt(2) + 1; world[i][j] = new JPanel(); if (terrainTypeGen == 1) { // 森林 world[i][j].setBackground(new Color(34, 139, 34)); world[i][j].setName("forest"); } else { // 平原 world[i][j].setBackground(new Color(144, 238, 144)); world[i][j].setName("plain"); } } } } // 初始显示中心的house面板 frame.setContentPane(world[1][1]); frame.validate(); // 必须调用这个刷新布局 frame.repaint();
2. 边界检测与面板切换逻辑
你第一段代码的问题很多:循环条件写错了(j的循环用了i的判断)、逻辑混乱、还直接在线程里操作UI(违反Swing规则)。正确的做法是:
- 用变量记录当前显示面板的坐标(
currentRow、currentCol),不用每次遍历数组找 - 角色移动后立刻判断是否触边,再执行对应操作
- 所有改UI的操作必须在EDT里做
示例代码:
假设你已经有变量存当前面板坐标currentRow、currentCol,还有角色坐标guineaXPosition、guineaYPosition:
// 角色移动完就调用这个方法检查边界 public void checkBoundary() { // 左边界(X=0) if (guineaXPosition <= 0) { if (currentCol == 0) { // 已经在最左列,没法再往左了 System.out.println("Edge of world, nothing you can do!"); } else { // 切换到当前行、列数减1的面板 currentCol--; switchPanel(world[currentRow][currentCol]); } // 把角色X坐标重置到面板右侧,避免重复触发边界检测 guineaXPosition = 1200; // 假设面板宽1300,留100边距 } // 右边界(X=1300) else if (guineaXPosition >= 1300) { if (currentCol == 2) { System.out.println("Edge of world, nothing you can do!"); } else { currentCol++; switchPanel(world[currentRow][currentCol]); } guineaXPosition = 100; } // 上边界(Y=0) if (guineaYPosition <= 0) { if (currentRow == 0) { System.out.println("Edge of world, nothing you can do!"); } else { currentRow--; switchPanel(world[currentRow][currentCol]); } guineaYPosition = 800; // 假设面板高900 } // 下边界(Y=900) else if (guineaYPosition >= 900) { if (currentRow == 2) { System.out.println("Edge of world, nothing you can do!"); } else { currentRow++; switchPanel(world[currentRow][currentCol]); } guineaYPosition = 100; } } // 切换面板的工具方法,确保在EDT执行 private void switchPanel(JPanel newPanel) { SwingUtilities.invokeLater(() -> { frame.setContentPane(newPanel); frame.validate(); frame.repaint(); }); }
如果用线程驱动角色动画:
要是你用线程让角色持续移动,这么写(注意EDT安全):
new Thread(() -> { while (true) { // 角色移动逻辑,比如X坐标每次加5 guineaXPosition += 5; // 检查边界 checkBoundary(); // 更新角色位置,必须在EDT里做 SwingUtilities.invokeLater(() -> { character.setLocation(guineaXPosition, guineaYPosition); }); try { Thread.sleep(16); // 大概60帧每秒,动画更流畅 } catch (InterruptedException e) { e.printStackTrace(); break; } } }).start();
内容的提问来源于stack exchange,提问作者LeninYT
相关产品推荐
相关产品推荐

