2D游戏背景滚动教程(JavaSwing)
想象你正在制作一个2D游戏,角色可以在一个大世界里走动,但你的屏幕只能显示世界的一部分。你需要让背景随着角色移动,但当角色走到世界边缘时,背景不能再滚动,而且在屏幕中间区域走动时,背景也不该移动。
核心思路
世界比屏幕大:背景图片(世界)比游戏窗口大得多
视口概念:屏幕就像一扇窗户,只显示世界的一部分
角色带动视口:角色靠近屏幕边缘时,背景才开始移动
边界限制:背景不能滚出世界边界
图片示意
1.一开始
2.“死区”内移动时
3.背景中间区域,可显示屏幕的边缘
4.背景的边缘(实操中随着不同游戏有一定不同处理,如反向出现,死亡判定,掉落触发动画,根本不允许走到此位置等等)
代码片段
1. 游戏基本设置与视口初始化
// 游戏窗口尺寸
private static final int SCREEN_WIDTH = 800;
private static final int SCREEN_HEIGHT = 600;// 背景尺寸(比屏幕大)
private final int BG_WIDTH = 1600;
private final int BG_HEIGHT = 1200; // 角色世界坐标(从世界中心开始)
private int playerX = BG_WIDTH / 2;
private int playerY = BG_HEIGHT / 2; // 视口位置(决定显示世界的哪一部分)
private int viewportX;
private int viewportY; // 初始化视口位置(使角色在屏幕中央开始)
private void centerViewportOnPlayer() {viewportX = playerX - SCREEN_WIDTH / 2; // 计算视口X位置viewportY = playerY - SCREEN_HEIGHT / 2; // 计算视口Y位置// 确保视口不超出背景边界(核心保护机制)viewportX = Math.max(0, Math.min(viewportX, BG_WIDTH - SCREEN_WIDTH));viewportY = Math.max(0, Math.min(viewportY, BG_HEIGHT - SCREEN_HEIGHT));
}
2. 滚动触发区域设置
// 定义滚动触发区域(屏幕边缘区域)
private final int SCROLL_ZONE_LEFT = 150; // 左侧触发区宽度
private final int SCROLL_ZONE_RIGHT = 150; // 右侧触发区宽度
private final int SCROLL_ZONE_TOP = 100; // 顶部触发区高度
private final int SCROLL_ZONE_BOTTOM = 100; // 底部触发区高度// 滚动速度系数(控制滚动平滑度)
private final float SCROLL_SPEED = 0.5f; // 在绘制方法中可视化滚动区域(调试用)
g2d.setColor(new Color(255, 68, 0, 50)); // 半透明橙色
g2d.fillRect(0, 0, SCROLL_ZONE_LEFT, SCREEN_HEIGHT); // 左侧区域
g2d.fillRect(SCREEN_WIDTH - SCROLL_ZONE_RIGHT, 0, SCROLL_ZONE_RIGHT, SCREEN_HEIGHT); // 右侧区域
// ... 其他区域类似
3. 键盘控制角色移动
// 键盘事件监听器
addKeyListener(new KeyAdapter() {@Overridepublic void keyPressed(KeyEvent e) {int step = 5; // 每次按键移动距离// WASD控制角色在世界中的位置switch (e.getKeyCode()) {case KeyEvent.VK_W -> playerY -= step; // 上移case KeyEvent.VK_S -> playerY += step; // 下移case KeyEvent.VK_A -> playerX -= step; // 左移case KeyEvent.VK_D -> playerX += step; // 右移}// 角色移动后更新视口updateViewport();repaint(); // 请求重绘画面}
});
4. 视口更新核心逻辑(最关键部分)
private void updateViewport() {// 计算角色在屏幕上的位置(世界坐标 → 屏幕坐标)int playerScreenX = playerX - viewportX;int playerScreenY = playerY - viewportY;int scrollDX = 0; // 水平滚动量int scrollDY = 0; // 垂直滚动量// 水平滚动检测if (playerScreenX < SCROLL_ZONE_LEFT) {// 角色进入左侧区域:背景向右滚动(视口左移)scrollDX = (int)((playerScreenX - SCROLL_ZONE_LEFT) * SCROLL_SPEED);} else if (playerScreenX > SCREEN_WIDTH - SCROLL_ZONE_RIGHT) {// 角色进入右侧区域:背景向左滚动(视口右移)scrollDX = (int)((playerScreenX - (SCREEN_WIDTH - SCROLL_ZONE_RIGHT)) * SCROLL_SPEED);}// 垂直滚动检测(原理同上)if (playerScreenY < SCROLL_ZONE_TOP) {scrollDY = (int)((playerScreenY - SCROLL_ZONE_TOP) * SCROLL_SPEED);} else if (playerScreenY > SCREEN_HEIGHT - SCROLL_ZONE_BOTTOM) {scrollDY = (int)((playerScreenY - (SCREEN_HEIGHT - SCROLL_ZONE_BOTTOM)) * SCROLL_SPEED);}// 应用滚动量viewportX += scrollDX;viewportY += scrollDY;// 边界约束(防止视口移出背景)viewportX = Math.max(0, Math.min(viewportX, BG_WIDTH - SCREEN_WIDTH));viewportY = Math.max(0, Math.min(viewportY, BG_HEIGHT - SCREEN_HEIGHT));
}
5. 背景绘制优化
// 只绘制视口可见部分(性能关键!)
g2d.drawImage(background,0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, // 在屏幕上的绘制区域viewportX, viewportY, // 背景源图像起点(世界坐标)viewportX + SCREEN_WIDTH, // 背景源图像终点XviewportY + SCREEN_HEIGHT, // 背景源图像终点Ynull);
6. 角色绘制与坐标转换
// 将角色世界坐标转换为屏幕坐标
int playerScreenX = playerX - viewportX;
int playerScreenY = playerY - viewportY;// 绘制角色(红色方块)
g2d.setColor(PLAYER_COLOR);
g2d.fillRect(playerScreenX - PLAYER_SIZE/2, // 屏幕X位置(居中)playerScreenY - PLAYER_SIZE/2, // 屏幕Y位置(居中)PLAYER_SIZE, PLAYER_SIZE);// 调试信息(显示坐标)
g2d.drawString("角色世界坐标: (" + playerX + ", " + playerY + ")", 10, 20);
g2d.drawString("视口位置: (" + viewportX + ", " + viewportY + ")", 10, 40);
7. 游戏启动入口
public static void main(String[] args) {// 使用SwingUtilities确保线程安全SwingUtilities.invokeLater(() -> {JFrame frame = new JFrame("滚动背景游戏");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.add(new ScrollBackgroundGame()); // 添加游戏面板frame.pack(); // 自动调整窗口大小frame.setLocationRelativeTo(null); // 屏幕居中frame.setVisible(true); // 显示窗口});
}