Java 小游戏《超级马里奥》

news/2024/10/19 22:52:33/

文章目录

  • 一、效果展示
  • 二、代码编写
    • 1. 素材准备
    • 2. 创建窗口类
    • 3. 创建常量类
    • 4. 创建动作类
    • 5. 创建关卡类
    • 6. 创建障碍物类
    • 7. 创建马里奥类
    • 8. 编写程序入口

一、效果展示

在这里插入图片描述

二、代码编写

1. 素材准备

首先创建一个基本的 java 项目,并将本游戏需要用到的图片素材 image 导入。

图片素材如下:
https://pan.baidu.com/s/1db_IcPvPKWKbVPtodPWO5Q?pwd=03kv
提取码:03kv

在这里插入图片描述

2. 创建窗口类

在这里插入图片描述

① Java 内部已经给我们封装了窗口的各种方法,我们只需创建一个窗口类并重写父类的方法,即可使用;
② Alt + Enter 键 → implement methods 可一键补全所有的重写方法;
③ 实现多线程有两种方法,分别是继承 Thread 类和实现 Runnable 接口,这里我们用 Runnable 方法,因为 Java 不支持多继承。

重写 paint 方法,实现场景、物体的绘制,使用多线程无限绘制窗口。

完整代码如下:

java">package com.zxe.beans;import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.List;/*** 窗口类*/
public class MyFrame extends JFrame implements KeyListener, Runnable {//定义一个集合用于所有的关卡private List<LevelMap> levelMaps = new ArrayList<>();//定义一个变量,存放当前背景private LevelMap levelMap = new LevelMap();//定义变量,记录马里奥private Mario mario;//重写paint方法,实现场景、物体的绘制@Overridepublic void paint(Graphics g) {//创建一张图片Image image = createImage(1045, 500);//设置图片Graphics graphics = image.getGraphics();graphics.drawImage(levelMap.getBg(), 0, 0, 1045, 500, this);//绘制障碍物for (Obstacle obstacle : levelMap.getObstacles()){graphics.drawImage(obstacle.getObstacleImage(), obstacle.getX(), obstacle.getY(), this);}//绘制马里奥graphics.drawImage(mario.getMarioImage(), mario.getX(), mario.getY(), this);//将图片描绘到当前窗口中g.drawImage(image, 0, 0, this);}@Overridepublic void keyTyped(KeyEvent e) {}@Overridepublic void keyPressed(KeyEvent e) {if (e.getKeyCode() == 37) {mario.runLeft();} else if (e.getKeyCode() == 39) {mario.runRight();} else if (e.getKeyCode() == 38) {mario.jump();}}public Mario getMario() {return mario;}public void setMario(Mario mario) {this.mario = mario;}@Overridepublic void keyReleased(KeyEvent e) {if (e.getKeyCode() == 37) {mario.runLeftStop();} else if (e.getKeyCode() == 39) {mario.runRightStop();} else if (e.getKeyCode() == 38) {mario.jumpDown();}}@Overridepublic void run() {//无限次绘制马里奥while (true) {repaint();try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}//判断一下马里奥是否通关if (mario.getX() > 1040) {levelMap = levelMaps.get(levelMap.getLevel());mario.setLevelMap(levelMap);mario.setX(50);mario.setY(420);}}}public List<LevelMap> getLevelMaps() {return levelMaps;}public void setLevelMaps(List<LevelMap> levelMaps) {this.levelMaps = levelMaps;}public LevelMap getLevelMap() {return levelMap;}public void setLevelMap(LevelMap levelMap) {this.levelMap = levelMap;}
}

3. 创建常量类

小游戏中的各种元素画面其实都是一张张的图片,而这些图片路径的定义都将放在常量类中完成。

java">package com.zxe.beans;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;/*** 常量类*/
public class Constant {//给窗口定义一张图片public static BufferedImage bg;//右跳图片public static BufferedImage jumpR;//左跳图片public static BufferedImage jumpL;//右边站立public static BufferedImage standR;//左边站立public static BufferedImage standL;//定义一个集合,存放右跑动作public static List<BufferedImage> runR = new ArrayList<>();//定义一个集合,存放左跑动作public static List<BufferedImage> runL = new ArrayList<>();//为障碍物定义一个集合public static List<BufferedImage> onstacles = new ArrayList<>();//定义一个变量,记录文件路径前缀public static String prefix = "C:\\Users\\Lenovo\\Desktop\\demo\\file\\image\\";//初始化图片到系统中public static void initImage() {try {//加载图片bg = ImageIO.read(new File(prefix + "bg2.jpeg"));jumpR = ImageIO.read(new File(prefix + "mario_jump_r.png"));jumpL = ImageIO.read(new File(prefix + "mario_jump_l.png"));standR = ImageIO.read(new File(prefix + "mario_stand_r.png"));standL = ImageIO.read(new File(prefix + "mario_stand_l.png"));runR.add(ImageIO.read(new File(prefix + "mario_run_r1.png")));runR.add(ImageIO.read(new File(prefix + "mario_run_r2.png")));runL.add(ImageIO.read(new File(prefix + "mario_run_l1.png")));runL.add(ImageIO.read(new File(prefix + "mario_run_l2.png")));for (int i = 1; i <= 6; i++) {onstacles.add(ImageIO.read(new File(prefix + "ob" + i + ".png")));}} catch (IOException e) {e.printStackTrace();throw new RuntimeException(e);}}}

常量用 static 修饰,外部可直接通过类名调用常量,而无需创建对象。

4. 创建动作类

记录玛丽的动作状态,具体的常量名与代码分离,可以降低代码的耦合度,更规范化。

在这里插入图片描述

5. 创建关卡类

每个关卡的障碍物是不一样的,这里需要外部传入关卡号,在关卡类中把不同的障碍物拼成自定义的关卡。

完整代码如下:

java">package com.zxe.beans;import com.zxe.utils.Constant;import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;/*** 关卡地图类*/
public class LevelMap {//记录当前场景需要的图片private BufferedImage bg;//记录当前关卡private int level;//创建一个集合,用于存放障碍物private List<Obstacle> obstacles = new ArrayList<>();public LevelMap() {}public LevelMap(int level) {this.level = level;bg = Constant.bg;if (level == 1) {//绘制方块obstacles.add(new Obstacle(100, 370, 0, this));obstacles.add(new Obstacle(130, 370, 1, this));obstacles.add(new Obstacle(160, 370, 0, this));obstacles.add(new Obstacle(190, 370, 1, this));obstacles.add(new Obstacle(300, 260, 0, this));obstacles.add(new Obstacle(330, 260, 1, this));obstacles.add(new Obstacle(360, 260, 1, this));obstacles.add(new Obstacle(800, 300, 0, this));obstacles.add(new Obstacle(830, 300, 0, this));obstacles.add(new Obstacle(860, 300, 1, this));obstacles.add(new Obstacle(890, 300, 1, this));//绘制水管obstacles.add(new Obstacle(420, 420, 4, this));obstacles.add(new Obstacle(450, 420, 5, this));obstacles.add(new Obstacle(415, 390, 2, this));obstacles.add(new Obstacle(435, 390, 2, this));obstacles.add(new Obstacle(455, 390, 3, this));obstacles.add(new Obstacle(600, 420, 4, this));obstacles.add(new Obstacle(630, 420, 5, this));obstacles.add(new Obstacle(600, 390, 4, this));obstacles.add(new Obstacle(630, 390, 5, this));obstacles.add(new Obstacle(595, 360, 2, this));obstacles.add(new Obstacle(615, 360, 2, this));obstacles.add(new Obstacle(635, 360, 3, this));} else if (level == 2) {int i = 0;for (int y = 420; y >= 300; y -= 30) {for (int x = 100; x <= 190 - 30 * i; x += 30) {obstacles.add(new Obstacle(x + 30 * i, y, 0, this));}for (int x = 300; x <= 390 - 30 * i; x += 30) {obstacles.add(new Obstacle(x, y, 0, this));}for (int x = 550; x <= 640 - 30 * i; x += 30) {obstacles.add(new Obstacle(x + 30 * i, y, 0, this));}for (int x = 670; x <= 790 - 30 * i; x += 30) {obstacles.add(new Obstacle(x, y, 0, this));}i++;}}}public BufferedImage getBg() {return bg;}public void setBg(BufferedImage bg) {this.bg = bg;}public int getLevel() {return level;}public void setLevel(int level) {this.level = level;}public List<Obstacle> getObstacles() {return obstacles;}public void setObstacles(List<Obstacle> obstacles) {this.obstacles = obstacles;}
}

6. 创建障碍物类

障碍物的属性包括图片以及横纵坐标。

完整代码如下:

java">package com.zxe.beans;import com.zxe.utils.Constant;import java.awt.image.BufferedImage;/*** 障碍物类*/
public class Obstacle {//记录障碍物的坐标private int x;private int y;//定义一个变量,记录当前障碍物的图片信息private BufferedImage obstacleImage;//定义障碍物类型private int type;//定义变量存放当前的背景private LevelMap bg;public Obstacle(int x, int y, int type, LevelMap bg) {this.x = x;this.y = y;this.type = type;this.bg = bg;//根据障碍物的编号,从常量中的障碍物集合中获取对应的图片this.obstacleImage = Constant.onstacles.get(type);}public int getX() {return x;}public void setX(int x) {this.x = x;}public int getY() {return y;}public void setY(int y) {this.y = y;}public BufferedImage getObstacleImage() {return obstacleImage;}public void setObstacleImage(BufferedImage obstacleImage) {this.obstacleImage = obstacleImage;}public int getType() {return type;}public void setType(int type) {this.type = type;}public LevelMap getBg() {return bg;}public void setBg(LevelMap bg) {this.bg = bg;}
}

7. 创建马里奥类

马里奥的无限行走动作由多线程实现,定义一个状态量status,用于标记马里奥当前的运动状态,以便进行不同动作的来回切换。

完整代码如下:

java">package com.zxe.beans;import com.zxe.utils.Action;
import com.zxe.utils.Constant;import java.awt.image.BufferedImage;/*** 马里奥类*/
public class Mario implements Runnable {//记录马里奥坐标信息private  int x;private int y;//记录马里奥状态private String status;//定义一个变量,记录马里奥当前动作所对应的图片信息private BufferedImage marioImage;//定义变量,记录当前的关卡地图,也可以获取障碍物的信息private LevelMap levelMap = new LevelMap();//创建线程执行马里奥的动作private Thread thread;//定义变量,记录马里奥的移动速度private int xSpeed;//定义变量,记录马里奥的跳跃速度private int ySpeed;//定义变量,记录马里奥的上升状态private int up;public Mario() {}public Mario(int x, int y) {this.x = x;this.y = y;//默认马里奥的动作是朝右站立status = Action.STAND_RIGHT;marioImage = Constant.standR;thread = new Thread(this);thread.start();}//马里奥向左移动的方法public void runLeft() {//判断当前是否为跳跃状态,如果不是,就改变状态if ( !status.contains("jump") ) {status = Action.RUN_LEFT;} else {status = Action.JUMP_LEFT;}xSpeed = -5;}//马里奥向右移动的方法public void runRight() {//判断当前是否为跳跃状态,如果不是,就改变状态if ( !status.contains("jump") ) {status = Action.RUN_RIGHT;} else {status = Action.JUMP_RIGHT;}xSpeed = 5;}public void jump() {if (status.contains("left")) {status = Action.JUMP_LEFT;} else {status = Action.JUMP_RIGHT;}ySpeed = -12;}public void jumpDown() {ySpeed = 12;}private void jumpStop() {if (status.contains("left")) {status = Action.STAND_LEFT;} else {status = Action.STAND_RIGHT;}ySpeed = 0;}//马里奥向左移动停止的方法public void runLeftStop() {if (!status.contains("jump")) {status = Action.STAND_LEFT;} else {status = Action.JUMP_LEFT;}xSpeed = 0;}//马里奥向右移动停止的方法public void runRightStop() {if (!status.contains("jump")) {status = Action.STAND_RIGHT;} else {status = Action.JUMP_RIGHT;}xSpeed = 0;}public int getX() {return x;}public void setX(int x) {this.x = x;}public int getY() {return y;}public void setY(int y) {this.y = y;}public String getStatus() {return status;}public void setStatus(String status) {this.status = status;}public BufferedImage getMarioImage() {return marioImage;}public void setMarioImage(BufferedImage marioImage) {this.marioImage = marioImage;}public LevelMap getLevelMap() {return levelMap;}public void setLevelMap(LevelMap levelMap) {this.levelMap = levelMap;}public Thread getThread() {return thread;}public void setThread(Thread thread) {this.thread = thread;}public int getxSpeed() {return xSpeed;}public void setxSpeed(int xSpeed) {this.xSpeed = xSpeed;}public int getySpeed() {return ySpeed;}public void setySpeed(int ySpeed) {this.ySpeed = ySpeed;}public int getUp() {return up;}public void setUp(int up) {this.up = up;}@Overridepublic void run() {int index = 0;//控制马里奥无限移动while (true) {//判断当前是否移动,xSpeed<0左移动,xSpeed>0右移动if (xSpeed < 0 || xSpeed > 0) {x += xSpeed;if (x < 0) {x = 0;}}if (ySpeed < 0 || ySpeed > 0) {y += ySpeed;if (y > 420) {y = 420;jumpStop();}if (y < 280) {y = 280;}}//判断移动状态,跑步状态图片切换if (status.contains("run")) {index = index == 0 ? 1 : 0;}//根据马里奥的状态切换不同的图片if (Action.RUN_LEFT.equals(status)) {marioImage = Constant.runL.get(index);}if (Action.RUN_RIGHT.equals(status)) {marioImage = Constant.runR.get(index);}if (Action.STAND_LEFT.equals(status)) {marioImage = Constant.standL;}if (Action.STAND_RIGHT.equals(status)) {marioImage = Constant.standR;}if (Action.JUMP_LEFT.equals(status)) {marioImage = Constant.jumpL;}if (Action.JUMP_RIGHT.equals(status)) {marioImage = Constant.jumpR;}// 控制线程的速度try {Thread.sleep(30);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}

8. 编写程序入口

创建游戏窗口,并对窗口的基本属性进行设置,创建三个关卡,并调用 repaint 方法绘制场景。

java">package com.zxe;import com.zxe.beans.LevelMap;
import com.zxe.beans.Mario;
import com.zxe.utils.Constant;
import com.zxe.beans.MyFrame;import javax.swing.*;public class Main {public static void main(String[] args) {//创建窗口对象MyFrame myFrame = new MyFrame();//设置窗口大小myFrame.setSize(1045,500);//设置窗口居中myFrame.setLocationRelativeTo(null);//设置窗口可见性myFrame.setVisible(true);//设置窗口关闭程序myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置键盘监听事件myFrame.addKeyListener(myFrame);//设置窗口的大小不可改变myFrame.setResizable(false);//设置窗口标题myFrame.setTitle("超级玛丽");//加载图片Constant.initImage();//创建三个关卡地图for (int i = 1; i <= 3; i++) {myFrame.getLevelMaps().add(new LevelMap(i));}//设置当前关卡地图myFrame.setLevelMap(myFrame.getLevelMaps().get(0));//创建马里奥Mario mario = new Mario(50, 420);myFrame.setMario(mario);mario.setLevelMap(myFrame.getLevelMap());//绘制场景myFrame.repaint();Thread thread = new Thread(myFrame);thread.start();}
}

http://www.ppmy.cn/news/1540355.html

相关文章

智能体网络时代即将来临,我们需要新的连接技术

备注&#xff1a;如果你也对这个话题感兴趣&#xff0c;欢迎联系我们&#xff1a; email: chgaoweigmail.com Discord: https://discord.gg/CDYdTPXXMB 官网: https://pi-unlimited.com 我们的方案代码已经开源&#xff0c;github&#xff1a;https://github.com/chgaowei/…

Ubuntu卸载Mysql【ubuntu 24.04/mysql 8.0.39】

一、准备工作 查看ubuntu版本号 查看mysql版本号(如果没有安装mysql,这一步省略) 二、Ubuntu上卸载mysql(如果没有安装mysql这一步省略) 在Ubuntu上卸载MySQL可以通过以下步骤进行&#xff0c;确保完全移除MySQL相关的包和数据&#xff1a; 1. 停止MySQL服务 在卸载之前…

【MySQL】InnoDB存储引擎中的锁

实现事务隔离级别的过程中用到了锁&#xff0c;所谓锁就是在事务A修改某些数据时&#xff0c;对这些数据加一把锁&#xff0c;防止其他事务同时对这些数据执行修改操作;当事务A完成修改操作后&#xff0c;释放当前持有的锁&#xff0c;以便其他事务再次上锁执行对应的操作。不同…

10月18日,每日信息差

第一、现代汽车集团在上海举办了中国前瞻技术研发中心的发布及启新庆典&#xff0c;宣布成立其全资法人公司 —— 现代前瞻汽车技术开发&#xff08;上海&#xff09;有限公司。该中心是集团在海外建立的首个前瞻技术研发中心&#xff0c;专注于自动驾驶、智能座舱、共享出行等…

23种设计模式具体实现方法

提示&#xff1a;文章 文章目录 前言一、背景二、设计模式1、代理模式2、适配器模式2.1 总结 三、3.1 总结 前言 前期疑问&#xff1a; 本文目标&#xff1a; 一、背景 最近 二、设计模式 1、代理模式 参考的这篇文章&#xff0c;代理模式(Proxy) 同时这篇文章还引用了另…

嵌入式数据结构中顺序栈用法

第一:嵌入式C语言中栈特点 栈是限制在一端进行插入操作和删除操作的线性表(俗称堆栈),允许进行操作的一端称为“栈顶”,另一固定端称为“栈底”,当栈中没有元素时称为“空栈”。特点 :后进先出(LIFO)。

机器学习:opencv--人脸检测以及微笑检测

目录 前言 一、人脸检测的原理 1.特征提取 2.分类器 二、代码实现 1.图片预处理 2.加载分类器 3.进行人脸识别 4.标注人脸及显示 三、微笑检测 前言 人脸检测是计算机视觉中的一个重要任务&#xff0c;旨在自动识别图像或视频中的人脸。它可以用于多种应用&#xff0…

基于Springboot+Vue的宠物管理系统(含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 这个系…