当前位置: 首页 > news >正文

2D游戏背景滚动教程(JavaSwing)

想象你正在制作一个2D游戏,角色可以在一个大世界里走动,但你的屏幕只能显示世界的一部分。你需要让背景随着角色移动,但当角色走到世界边缘时,背景不能再滚动,而且在屏幕中间区域走动时,背景也不该移动。

核心思路

  1. 世界比屏幕大:背景图片(世界)比游戏窗口大得多

  2. 视口概念:屏幕就像一扇窗户,只显示世界的一部分

  3. 角色带动视口:角色靠近屏幕边缘时,背景才开始移动

  4. 边界限制:背景不能滚出世界边界

图片示意

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); // 显示窗口});
}

http://www.xdnf.cn/news/1187983.html

相关文章:

  • 机器学习特征工程详解:特征选择与降维(PCA)
  • 数据赋能(336)——技术平台——智能化运营
  • 2.JVM跨平台原理(字节码机制)
  • Java研学-RabbitMQ(二)
  • C++中new和delete的多重面孔:operator new、new operator与placement new解析
  • 初识java
  • Python 程序设计讲义(20):选择结构程序设计——双分支结构的简化表示(三元运算符)
  • Model Control Protocol 三层架构设计,三种传输方式,完成MCP项目构建实现工具调试,多维度评价指标检测多工具多资源调用的鲁棒性和稳健性
  • java面试题(二)
  • 栈----1.有效的括号
  • 扒网站工具 HTTrack Website Copier
  • 3020雕刻机脱机自定义指令
  • 一些常见的网络攻击方式
  • 疯狂星期四第19天运营日记
  • Java并发编程第十篇(ThreadPoolExecutor线程池组件分析)
  • 锁相环技术简介(面向储能变流器应用)
  • 机器学习(一)KNN,K近邻算法(K-Nearest Neighbors)
  • [硬件电路-85]:一款高集成度热电制冷器(TEC)控制器芯片ADN8835ACPZ
  • 工程师实践出真知
  • 【Spring WebFlux】为什么 Spring 要拥抱响应式
  • java面试题(一)
  • 基于深度学习的图像分类:使用DenseNet实现高效分类
  • 解决 Delete ␍ prettier/prettier问题的方案
  • TwinCAT3编程入门1
  • 理解Spring中的IoC
  • 探索 MyBatis-Plus
  • [2025CVPR-图象分类方向]SPARC:用于视觉语言模型中零样本多标签识别的分数提示和自适应融合
  • TDengine 转化函数 TO_UNIXTIMESTAMP 用户手册
  • S7-1500 与 ET200MP 的组态控制通信(Configuration Control)功能实现详解(下)
  • 【vue3+vue-pdf-embed】实现PDF+图片预览