Java 小游戏《超级马里奥》

embedded/2024/10/18 22:33:14/

文章目录

  • 一、效果展示
  • 二、代码编写
    • 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/embedded/128569.html

相关文章

SpringBoot框架在购物推荐网站设计中的创新应用

3系统分析 3.1可行性分析 通过对本东大每日推购物推荐网站实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本东大每日推购物推荐网站采用JAVA作为开发语言&…

农合生活平台用户量已突破5万人大关。

回顾走来的这一路&#xff0c;农合生活一直在成长的路上&#xff0c;从未停歇。 2024年1月&#xff0c;农合生活小程序1.0推出&#xff0c;上线1个月GMV破百万&#xff1b; 2024年4月&#xff0c;农合生活APP上线&#xff0c;注册用户破万&#xff1b; 2024年4月&#xff0c;…

Red Hat Enterprise Linux 9 的软件包列表

摘要 软件包清单文档提供了 Red Hat Enterprise Linux 9 中可用的软件包列表。 使开源包含更多 红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。我们从这四个术语开始&#xff1a;master、slave、黑名单和白名单。由于此项工作十分艰巨&#xff0c;这些更改将…

JavaWeb合集05-SpringBoot基础知识

五、SpringBoot基础知识 0、实用方法 0.1 动态获取某个文件路径 //getResource( name:" emp.txt") 更具名称获取资源链接&#xff1b;getFile() 获取文件对象 String filePaththis.getClass().getClassLoader().getResource( name:" emp.txt").getFile(…

OPENSSL-2023/11/10学习记录-C/C++对称分组加密DES

对称分组加密常用算法&#xff1a; DES 3DES AES 国密SM4 对称分组加密应用场景&#xff1a; 文件或者视频加密 加密比特币私钥 消息或者配置项加密 SSL通信加密 对称分组加密 使用异或实现一个简易的对称加密算法 A明文 B秘钥 AB密文AB (AB)B A 密码补全和初始化 数…

猫鼠游戏: KaijiK病毒入侵溯源分析

1. 事件背景 近期&#xff0c;网宿平台某客户在使用云主机工作的时候突然出现主机卡顿&#xff0c;连接不稳定&#xff0c;网络断开的情况&#xff0c;并且收到了网宿主机入侵检测产品的告警信息。由于客户没有专职的安全人员&#xff0c;由运维人员兼任安全运营工作&#xff…

WebGoat SQL Injection (intro) 源码分析

首先了解 java 中 mysql 的连接&#xff1a;java连接Mysql WebGoat SQL Injection (intro) 10 根据提示&#xff1a;下面两个输入框只有一个受到 sql 注入攻击。题目要求是检索到所有数据 发现请求路径为 SqlInjection/assignment5b 定位到所在文件如下&#xff0c;根据代码…

【CTF Web】Pikachu getimagesize() Writeup(文件上传+PHP+文件包含漏洞+JPEG图片马+getimagesize绕过)

不安全的文件上传漏洞概述 文件上传功能在web应用系统很常见&#xff0c;比如很多网站注册的时候需要上传头像、上传附件等等。当用户点击上传按钮后&#xff0c;后台会对上传的文件进行判断 比如是否是指定的类型、后缀名、大小等等&#xff0c;然后将其按照设计的格式进行重…