题目:

本程序是针对超级玛丽小游戏的 JAVA 程序,进入游戏后首先用鼠标点击 GUI 窗口,然后开始游 戏,利用方向键来控制的马里奥的移动,同时检测马里奥与场景中的障碍物和敌人的碰撞,并判断马里 奥的可移动性和马里奥的生命。踩死蘑菇敌人与撞击金币砖块可获得积分与金币。记录马里奥获得的分 数与金币个数。当马里奥通过最后一个场景后游戏结束。利用多线程技术,给游戏分别添加背景音乐、 跳跃音乐、死亡音乐、顶金币音乐、游戏胜利音乐。

运行窗口

代码解析:

窗体类–MyF rame.java

该类主要用于存放游戏的场景以及其他各类,并且实现 KeyListener 接口,用于从键盘的按键中 读取信息。该类中的一些属性主要包括了用于存放所有场景的 list 集合 allBG,马里奥类 mario,当前 的场景 nowBG 以及其他一些游戏中需要的标记等。而且在该类中,运用双缓存的技术使得游戏的流畅 度更高,解决了游戏中出现的闪屏问题。 将该类的名字定义为 MyF rame,并且要在该类中实现 KeyListener 接口和 Runnable 接口。然 后首先要在该类中定义一个 List 集合,集合的泛型为背景类 BackGround,集合的名字定义为 allBG, 用于存放所有的背景。接着定义一个 M ario 类属性,名字为 mario,这个就是游戏运行时候的所需要 的 mario。接下来还要在类中定义一个 BackGround 属性,nowBG,默认值应当为空,会在构造方法 中赋予该属性初值,这个属性主要是用来存放当前游戏运行时马里奥所处的游戏场景。另外该类中还应 该有一个 T hread 类属性 thread,这个属性主要是为了在游戏运行的时候控制游戏的线程。然后就可以 在类中定义 main() 方法,将该类实现就可以了。 在该类的构造方法中,应当首先绘制窗体类的标题,以及窗体类的大小,并且要对窗体类在初始化 的时候的位置,也就是在屏幕中显示的位置,最好是显示的时候居中,这样的话在游戏运行时会比较美 观一些。考虑到玩家随意改变游戏的窗口大小可能会对游戏的体验造成影响,所以在这里应该设置游戏 的窗体默认不可以被拉伸。

当这些都设置好以后,接下来就应当在构造方法中绘制了,当然最先应当将游戏的场景绘制到窗体 类中,然后在窗体类中还应当绘制马里奥类,这是游戏中必不可少的。当然在绘制场景类的时候因为不 知一个场景,所以可以使用循环,将所有的场景全部绘制。然后在将所需要的所有监视设置好以后就可 以开启该类的线程了, 然后创建一个 Music 类播放背景主题音乐

在这些最基本的东西设置完以后,还需要一个方法来解决游戏中经常会出现的闪屏问题。这个方法 就是双缓存方法,现在类中定义一个 BufferedImage 的图片,然后从该图片中获取到图片的 Graphics graphics,然后利用画笔 graphics 将所要绘制的东西绘制到这个空的图片中,然后在利用窗体类中的 paint 方法中的画笔 graphics 将这个已经绘制好的图片绘制到窗体类中,这样利用空白图片作为程序运 行中的中转,就可以很好的解决游戏运行过程中出现的闪屏问题。

对于玩家对游戏中的马里奥的控制。在该类中实现 KeyListener 接口,这个接口的作用就是使该类 中实现一些方法,以便于达到玩家在游戏进行时可以对游戏中的马里奥进行控制。我们这里拟定对于马 里奥的控制可以使用我们常见的四个方向键,即我们说的上下左右。并且通过控制台打印,可以知道上 对应的是 38,右对应的是 39,左对应的是 37。并且游戏的设定是开始后游戏不会直接运行,而是要使 用鼠标点击以后游戏才会真正开始,所以还要加入鼠标监听

对于按键,那么相对应的就是当抬起按键的时候。因为你向右移动的时候,如果这时候突然停止, 那么很可能马里奥会保持一个运动的状态停下来,那么就必须在玛丽奥停止的时候给他一个指令,让他 的移动图片变为静止。相对于运动的时候是类似的,。

package ClassPackage;import javazoom.jl.decoder.JavaLayerException;import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;// KeyListener接口 键盘事件类
public class MyFrame extends JFrame implements KeyListener, Runnable {// 用于存储所有背景private List<BackGround> allBg = new ArrayList<>();//用于存储当前的背景private BackGround nowBg = new BackGround();//用于双缓存private Image offScreenImage = null;//马里奥对象private Mario mario = new Mario();//定义一个线程对象用于实现马里奥的运动private Thread thread = new Thread(this);public static final int START = 0;public static final int RUNNING = 1;private int state = START;public MyFrame() {//设置窗口的大小this.setSize(800, 600);// 设置窗口居中显示this.setLocationRelativeTo(null);//设置窗口可见this.setVisible(true);//设置点击窗口关闭见关闭程序this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置窗口大小不可变this.setResizable(false);//向键盘添加键盘监听器  KeyListener接口this.addKeyListener(this);//设置窗口名称this.setTitle("超级玛丽");// 初始化图片StaticValue.init();//初始化马里奥对象mario = new Mario(10, 355);//创建全部场景for (int i = 1; i <= 3; i++) {allBg.add(new BackGround(i, i == 3 ? true : false));}// 将第一个场景设置为当前场景nowBg = allBg.get(0);mario.setBackGround(nowBg);//调用鼠标点击方法action();//若游戏未开始 无限 停止线程while (state == START) {Thread.yield();}//绘制图像repaint();thread.start();//加入音乐try {Music m1 = new Music(1);} catch (FileNotFoundException | JavaLayerException | InterruptedException e) {e.printStackTrace();}}private void action() {//鼠标侦听器MouseAdapter m = new MouseAdapter() {@Overridepublic void mouseClicked(MouseEvent e) {//鼠标点击时若为游戏未开始状态则开始游戏if (state == START) {state = RUNNING;}}};this.addMouseListener(m);this.addMouseMotionListener(m);}public void paint(Graphics g) {//窗口if (offScreenImage == null) {offScreenImage = createImage(800, 600);}//画笔Graphics graphics = offScreenImage.getGraphics();//画窗口graphics.fillRect(0, 0, 800, 600);//画背景graphics.drawImage(nowBg.getBgImage(), 0, 0, this);//绘制敌人for (Enemy o : nowBg.getEnemyList()) {graphics.drawImage(o.getShow(), o.getX(), o.getY(), this);}//绘制障碍物//画障碍物for (Obstacle ob : nowBg.getObstacleList()) {graphics.drawImage(ob.getShow(), ob.getX(), ob.getY(), this);}//画城堡 旗杆graphics.drawImage(nowBg.getTower(), 620, 270, this);graphics.drawImage(nowBg.getGan(), 500, 220, this);//绘制马里奥graphics.drawImage(mario.getShow(), mario.getX(), mario.getY(), this);// 添加分数Color c = graphics.getColor();graphics.setColor(Color.GRAY);graphics.setFont(new Font("楷体", Font.BOLD, 18));graphics.drawString("当前分数:" + mario.getScore(), 16, 100);graphics.drawString("当前金币:" + mario.getGold(), 16, 123);graphics.setColor(c);//如果游戏未开始 画封面覆盖游戏画面if (state == START) {graphics.drawImage(BackGround.getStart(), 0, 0, this);}// 将图像绘制到窗口中g.drawImage(offScreenImage, 0, 0, this);}public static void main(String[] args) {MyFrame myFrame = new MyFrame();}@Overridepublic void keyTyped(KeyEvent e) {}//当键盘按下时调用@Overridepublic void keyPressed(KeyEvent e) {//向右移动if (e.getKeyCode() == 39) {mario.rightMove();}//向左移动if (e.getKeyCode() == 37) {mario.leftMove();}//跳跃if (e.getKeyCode() == 38) {mario.jump();}}//当键盘松开时@Overridepublic void keyReleased(KeyEvent e) {//向左停止if (e.getKeyCode() == 37) {mario.leftStop();}//向右停止if (e.getKeyCode() == 39) {mario.rightStop();}}@Overridepublic void run() {System.out.println(this.getName());while (true) {//调用鼠标点击方法action();//若游戏未开始 无限 停止线程while (state == START) {Thread.yield();}//如果马里奥达到了屏幕的最右  那么切换场景repaint();try {Thread.sleep(50);if (mario.getX() >= 775) {nowBg = allBg.get(nowBg.getSort());mario.setBackGround(nowBg);mario.setX(10);mario.setY(355);}//判断马里奥是否死亡if (mario.isDeath()) {//弹窗JOptionPane.showMessageDialog(this, "马里奥死亡!!!");System.exit(0);}// 判断游戏是否结束if (mario.isOK()) {JOptionPane.showMessageDialog(this, "恭喜你!成功通关了");System.exit(0);}} catch (InterruptedException e) {e.printStackTrace();}}}
}

常量类–StaticV alue.java

用于存放游戏所需要的所有图片文件,在游戏开始的时候将所有图片导入,提高游戏的运行速度。 并且在该类中将所有需要用到的图片进行分类,分为障碍物类,马里奥类,敌人类以及背景图片。当游 戏运行时可以直接调用这些集合中的图片进行遍历,在调用的时候更加方便,而且可以使马里奥或者敌 人在移动的时候产生动态效果。 首先在类中应当几个定义 BufferedImage 类型,属性名字为 jump_L,jump_R,stand_L,stand_R, 列表 run_L 和 run_R,这些属性的作用在于存放所有的马里奥图片,里面包括了马里奥的移动图片, 站立图片以及马里奥跳跃的图片。这样在程序运行的时候就可以从该类中的这个属性里面将所需要的马 里奥图片直接调用出来,并且还可以在马里奥移动时不断遍历里面的图片,这样就可以使马里奥产生移 动的动态效果。接下来要在该类中定义开始图片,结束图片以及背景图片,默认的初始值都为 null。注 意这些所有的属性都是静态的,包括下面要提到的所有的属性,这样做的目的是为了在程序运行时先加 载这些图片。然后应当定义存放食人花的 List 集合 flower,这个集合将食人花的不同形态,张嘴、闭嘴 图片存放进去,这样在运行的时候进行遍历就可以打到动态效果。同理存放蘑菇怪的集合 mogu,以及 存放所有障碍物的集合 obstacle。

package ClassPackage;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 StaticValue {//背景public static BufferedImage bg = null;public static BufferedImage bg2 = null;//跳跃public static BufferedImage jump_L = null;public static BufferedImage jump_R = null;//站立public static BufferedImage stand_L = null;public static BufferedImage stand_R = null;//城堡public static BufferedImage tower = null;//旗杆public static BufferedImage gan = null;//旗杆public static BufferedImage gold1 = null;//障碍物public static List<BufferedImage> obstacle = new ArrayList<>();//跑public static List<BufferedImage> run_L = new ArrayList<>();public static List<BufferedImage> run_R = new ArrayList<>();//蘑菇public static List<BufferedImage> mogu = new ArrayList<>();//食人花public static List<BufferedImage> flower = new ArrayList<>();//乌龟public static List<BufferedImage> turtle = new ArrayList<>();//路径public static String path = System.getProperty("user.dir") + "/src/imgs/";//未开始界面public static BufferedImage start = null;//初始化方法public static void init() {try {//加载未开始图片start = ImageIO.read(new File(path + "start.jpg"));//加载背景图片bg = ImageIO.read(new File(path + "bg.png"));bg2 = ImageIO.read(new File(path + "bg2.png"));//加载马里奥向左跳跃jump_L = ImageIO.read(new File(path + "s_mario_jump1_L.png"));//加载马里奥向右跳跃jump_R = ImageIO.read(new File(path + "s_mario_jump1_R.png"));//加载马里奥向左站立stand_L = ImageIO.read(new File(path + "s_mario_stand_L.png"));//加载马里奥向右站立stand_R = ImageIO.read(new File(path + "s_mario_stand_R.png"));//加载城堡tower = ImageIO.read(new File(path + "tower.png"));//加载旗杆gan = ImageIO.read(new File(path + "gan.png"));//加载金币gold1 = ImageIO.read(new File(path + "gold1.png"));} catch (IOException e) {e.printStackTrace();}//加载马里奥向左跑for (int i = 1; i <= 2; i++) {try {run_L.add(ImageIO.read(new File(path + "s_mario_run" + i + "_L.png")));} catch (IOException e) {e.printStackTrace();}}//加载马里奥向右跑for (int i = 1; i <= 2; i++) {try {run_R.add(ImageIO.read(new File(path + "s_mario_run" + i + "_R.png")));} catch (IOException e) {e.printStackTrace();}}//加载障碍物try {obstacle.add(ImageIO.read(new File(path + "questionmark1.png")));obstacle.add(ImageIO.read(new File(path + "soil_up.png")));obstacle.add(ImageIO.read(new File(path + "soil_base.png")));} catch (IOException e) {e.printStackTrace();}//加载水管for (int i = 1; i <= 4; i++) {try {obstacle.add(ImageIO.read(new File(path + "pipe" + i + ".png")));} catch (IOException e) {e.printStackTrace();}}//加载不可破坏的砖块和旗子try {obstacle.add(ImageIO.read(new File(path + "brick2.png")));obstacle.add(ImageIO.read(new File(path + "flag.png")));// 加载金币obstacle.add(ImageIO.read(new File(path + "gold1.png")));// 加载无金币砖块obstacle.add(ImageIO.read(new File(path + "questionmark2.png")));} catch (IOException e) {e.printStackTrace();}//加载蘑菇敌人for (int i = 1; i <= 3; i++) {try {mogu.add(ImageIO.read(new File(path + "fungus" + i + ".png")));} catch (IOException e) {e.printStackTrace();}}//加载食人花敌人for (int i = 1; i <= 2; i++) {try {flower.add(ImageIO.read(new File(path + "flower1." + i + ".png")));} catch (IOException e) {e.printStackTrace();}}//加载w乌龟敌人for (int i = 1; i <= 6; i++) {try {turtle.add(ImageIO.read(new File(path + "tortoise" + i + ".png")));} catch (IOException e) {e.printStackTrace();}}}
}

背景类–BackGround.java

该类表示马里奥及障碍物和敌人所处的场景,并且将障碍物和敌人绘制到场景中。在该类中包括用 于存放敌人和障碍物的 list 集合,在敌人或者马里奥死亡时,将对应图像移出列表,以达到重新绘制图 像效果。 首先背景类中肯定要有一个标记来表示现在是第几个场景,因为不同的背景中所绘制的场景,障碍 物等也不同,所以该类中要有一个 int 类型的场景顺序 sort。并且在游戏的设定中,如果玩家玩到最后 一关的时候马里奥碰到棋子会失去玩家的控制,自己走向城堡。那么这里就要这几一个标记,是否为最 后的棋子与城堡,类型为 boolean 类型。。 接下来就应当定义背景类的构造方法了,通过获取场景的顺序,即场景的 sort,来判断是哪一个场 景,同时将场景绘制好。下面为绘制第一关的图像

package ClassPackage;import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;public class BackGround {//显示当前场景的图片private BufferedImage bgImage = null;//记录当前第几个场景private int sort;//判断是否为最后一个场景private boolean flag;//用于显示旗杆private BufferedImage gan = null;//用于显示城堡private BufferedImage tower = null;//判断马里奥是否到达旗杆位置private boolean isReach = false;//判断棋子是否落地private boolean isBase = false;// 存放金币private static BufferedImage start = null;//用于存放所有的敌人private List<Enemy> enemyList = new ArrayList<>();//用于存放我们所有的障碍物private List<Obstacle> obstacleList = new ArrayList<>();// 空参构造方法public BackGround() {}// 两个参数构造方法public BackGround(int sort, boolean flag) {this.sort = sort;this.flag = flag;start = StaticValue.start;if (flag) {// 最后一个场景bgImage = StaticValue.bg2;} else {bgImage = StaticValue.bg;}if (sort == 1) {//地面//type是StaticValue类的障碍物//绘制第一关的地面  上地面type=1  下地面type=2for (int i = 0; i < 27; i++) {obstacleList.add(new Obstacle(i * 30, 420, 1, this));}for (int j = 0; j <= 120; j += 30) {for (int i = 0; i < 27; i++) {obstacleList.add(new Obstacle(i * 30, 570 - j, 2, this));}}//绘制砖块Afor (int i = 120; i <= 150; i += 30) {obstacleList.add(new Obstacle(i, 300, 7, this));}//绘制砖块B-Ffor (int i = 300; i <= 570; i += 30) {if (i == 360 || i == 390 || i == 480 || i == 510 || i == 540) {obstacleList.add(new Obstacle(i, 300, 7, this));} else {// 普通砖块obstacleList.add(new Obstacle(i, 300, 0, this));}}//砖块Gfor (int i = 420; i <= 450; i += 30) {obstacleList.add(new Obstacle(i, 240, 7, this));}//水管for (int i = 360; i <= 600; i += 25) {if (i == 360) {obstacleList.add(new Obstacle(620, i, 3, this));obstacleList.add(new Obstacle(645, i, 4, this));} else {obstacleList.add(new Obstacle(620, i, 5, this));obstacleList.add(new Obstacle(645, i, 6, this));}}// 绘制第一关的蘑菇敌人enemyList.add(new Enemy(580, 385, true, 1, this));//食人花enemyList.add(new Enemy(635, 420, true, 2, 328, 428, this));//乌龟//enemyList.add(new Enemy(570, 390,  3,  this));}if (sort == 2) {for (int i = 0; i < 27; i++) {obstacleList.add(new Obstacle(i * 30, 420, 1, this));}for (int j = 0; j <= 120; j += 30) {for (int i = 0; i < 27; i++) {obstacleList.add(new Obstacle(i * 30, 570 - j, 2, this));}}//第一个水管for (int i = 360; i <= 600; i += 25) {if (i == 360) {obstacleList.add(new Obstacle(60, i, 3, this));obstacleList.add(new Obstacle(85, i, 4, this));} else {obstacleList.add(new Obstacle(60, i, 5, this));obstacleList.add(new Obstacle(85, i, 6, this));}}//第二个水管for (int i = 330; i <= 600; i += 25) {if (i == 330) {obstacleList.add(new Obstacle(620, i, 3, this));obstacleList.add(new Obstacle(645, i, 4, this));} else {obstacleList.add(new Obstacle(620, i, 5, this));obstacleList.add(new Obstacle(645, i, 6, this));}}//砖块CobstacleList.add(new Obstacle(300, 330, 0, this));//砖块B,E,Gfor (int i = 270; i <= 330; i += 30) {if (i == 270 || i == 330) {// 普通砖块obstacleList.add(new Obstacle(i, 360, 0, this));} else {obstacleList.add(new Obstacle(i, 360, 7, this));}}//砖块A,D,F,H,Ifor (int i = 240; i <= 360; i += 30) {if (i == 240 || i == 360) {obstacleList.add(new Obstacle(i, 390, 0, this));} else {obstacleList.add(new Obstacle(i, 390, 7, this));}}//妨碍砖块obstacleList.add(new Obstacle(240, 300, 0, this));for (int i = 360; i <= 540; i += 60) {obstacleList.add(new Obstacle(i, 270, 7, this));}//食人花敌人enemyList.add(new Enemy(75, 420, true, 2, 328, 418, this));enemyList.add(new Enemy(635, 420, true, 2, 298, 388, this));//蘑菇敌人enemyList.add(new Enemy(200, 385, true, 1, this));enemyList.add(new Enemy(500, 385, true, 1, this));}if (sort == 3) {for (int i = 0; i < 27; i++) {obstacleList.add(new Obstacle(i * 30, 420, 1, this));}for (int j = 0; j <= 120; j += 30) {for (int i = 0; i < 27; i++) {obstacleList.add(new Obstacle(i * 30, 570 - j, 2, this));}}//A-O砖块int temp = 290;for (int i = 390; i >= 270; i -= 30) {for (int j = temp; j <= 410; j += 30) {obstacleList.add(new Obstacle(j, i, 7, this));}temp += 30;}//P-R砖块temp = 60;for (int i = 390; i >= 360; i -= 30) {for (int j = temp; j <= 90; j += 30) {obstacleList.add(new Obstacle(j, i, 7, this));}temp += 30;}//绘制旗杆gan = StaticValue.gan;//绘制城堡tower = StaticValue.tower;//将棋子绘制到旗杆上obstacleList.add(new Obstacle(515, 220, 8, this));//蘑菇enemyList.add(new Enemy(150, 385, true, 1, this));}}// 顶金币public void createMoney(int x, int y, int type) {obstacleList.add(new Obstacle(x, y, type, this));}public BufferedImage getBgImage() {return bgImage;}public int getSort() {return sort;}public boolean isFlag() {return flag;}public List<Obstacle> getObstacleList() {return obstacleList;}public BufferedImage getGan() {return gan;}public BufferedImage getTower() {return tower;}public boolean isReach() {return isReach;}public void setReach(boolean reach) {isReach = reach;}public boolean isBase() {return isBase;}public void setBase(boolean base) {isBase = base;}public List<Enemy> getEnemyList() {return enemyList;}//获取未开始图片public static BufferedImage getStart() {return start;}}

马里奥类–M ario.java

用来控制马里奥的行动,并且在该类中加入碰撞检测,判断马里奥是否与障碍物或者敌人发生碰 撞。该类中的属性主要定义了马里奥所在的场景,马里奥的移动和跳跃的速度,以及马里奥在移动过程 中需要显示的图片。另外该类中还定义了玩家的生命值和所获得的分数。并且在 run()方法中还定义 了当马里奥到达最后一关的旗子时,玩家将失去对马里奥的控制,剩下的由程序控制走到城堡,完整全 部游戏。在游戏中,玛丽奥要在玩家的控制下完成移动、跳跃等动作,那么这些动作首先肯定要涉及到 坐标,那么我们在该类中首先要定义两个属性,这两个属性即为马里奥的坐标 x 和 y。并且该类还要实 现 Runnable 接口,在 run() 方法中写马里奥的移动规则。 为了玩家在游戏过程中的良好体验,那么对于马里奥的移动速度和跳跃速度就必须要定义好。所以 该类里面还应当定义马里奥的移动速度和跳跃速度,其本质就是马里奥在移动过程中坐标加减的值。当 然初始值为零,必须等到马里奥构造的时候,再将这些属性赋予相对应的值。在本类中还要定义游戏的 分数以及马里奥的生命数,这些都是必不可少的。 在马里奥这个类中,还要定义马里奥的移动和跳跃方法,以便玩家在按下方向键后调用这些方法, 来达到控制马里奥的移动。。在定义马里奥的跳跃方法的时候,不单单定义一个方法就行,而且还要判 断马里奥的状态。如果马里奥是在地面或者是在障碍物的上方,那么马里奥可以进行跳跃,如果马里奥 处于空中,那么马里奥就不可以继续跳跃. 下面是马里奥向左移动的方法,其他方法同理

然后对当前马里奥所处的场景中的所有障碍物进行遍历,获取到所有障碍物的坐标,通过障碍物的 坐标和马里奥的坐标的之间的关系的判断,来决定马里奥是否与障碍物发生了碰撞,并且通过判断的结 果来对马里奥和障碍物的状态进行相应的变化,若撞击金块则产生金币,原金块就会变成空金块。还有 对碰撞怪物以及怪物和马里奥是存活状态进行判断。部分代码如下,其他功能可查看代码

package ClassPackage;import javazoom.jl.decoder.JavaLayerException;import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;public class Mario implements Runnable {//用于表示当前马里奥的横纵坐标private int x;private int y;//用于表示当前的状态private String status;//用于显示当前状态对应的图像private BufferedImage show = null;//定义一个BackGround对象  用于获取障碍物信息private BackGround backGround = new BackGround();//用来实现马里奥的动作private Thread thread = null;//定义一个索引private int index;//马里奥的移动速度private int xSpeed;//马里奥的跳跃速度private int ySpeed;//表示马里奥的上升的时间private int upTime = 0;//判断马里奥是否走到了 城堡的门口private boolean isOK;//用于判断马里奥是否死亡private boolean isDeath = false;// 积分,金币private int score = 0;private int gold = 0;public int getScore() {return score;}public int getGold() {return gold;}//马里奥死亡方法public void death() {try {new Music(4);} catch (FileNotFoundException | JavaLayerException | InterruptedException e) {e.printStackTrace();}isDeath = true;}public boolean isDeath() {return isDeath;}public boolean isOK() {return isOK;}//马里奥跳跃public void jump() {// 判断马里奥是否为跳跃状态if (status.indexOf("jump") == -1) {try {new Music(3);} catch (FileNotFoundException | JavaLayerException | InterruptedException e) {e.printStackTrace();}//判断马里奥的跳跃方向是否为左if (status.indexOf("left") != -1) {status = "jump--left";} else {//不为左  则为右status = "jump--right";}ySpeed = -10;upTime = 7;}//判断马里奥是否碰到了旗子if (backGround.isReach()) {ySpeed = 0;}}//马里奥下落public void fall() {//判单马里奥的跳跃方向是否为左if (status.indexOf("left") != -1) {status = "jump--left";} else {//不为左  则为   右status = "jump--right";}ySpeed = 10;}public Mario() {}public Mario(int x, int y) {this.x = x;this.y = y;//初始马里奥向右站立show = StaticValue.stand_R;//马里奥当前状态this.status = "status--right";thread = new Thread(this);thread.start();}//向左移动public void leftMove() {//改变速度xSpeed = -5;//判断马里奥是否碰到了旗子if (backGround.isReach()) {xSpeed = 0;}//判断马里奥是否处于空中if (status.indexOf("jump") != -1) {status = "jump--left";} else {status = "move--left";}}//向右移动public void rightMove() {xSpeed = 5;//判断马里奥是否碰到了旗子if (backGround.isReach()) {xSpeed = 0;}//判断马里奥是否处于空中if (status.indexOf("jump") != -1) {status = "jump--right";} else {status = "move--right";}}//向左停止public void leftStop() {xSpeed = 0;//判断马里奥是否处于空中if (status.indexOf("jump") != -1) {status = "jump--left";} else {status = "stop--left";}}//向右停止public void rightStop() {xSpeed = 0;//判断马里奥是否处于空中if (status.indexOf("jump") != -1) {status = "jump--right";} else {status = "stop--right";}}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 getShow() {return show;}public void setShow(BufferedImage show) {this.show = show;}public void setBackGround(BackGround backGround) {this.backGround = backGround;}public String getStatus() {return status;}public BackGround getBackGround() {return backGround;}public Thread getThread() {return thread;}@Overridepublic void run() {while (true) {//判断马里奥是否在障碍物上boolean onObstacle = false;//判断是否可以向右走boolean canRight = true;//判断是否可以向左走boolean canLeft = true;//判断是否到达旗杆位置if (backGround.isFlag() && this.x >= 500) {this.backGround.setReach(true);if (this.backGround.isBase()) {status = "move--right";if (x < 690) {x += 5;} else {//表示马里奥已经到了城堡处isOK = true;}//如果旗子没有下落完成} else {//判断马里奥是否在空中if (y < 395) {xSpeed = 0;this.y += 5;status = "jump--right";}//如果马里奥罗到了地上if (y > 395) {this.y = 395;status = "stop--right";}}} else {//遍历当前场景的所有障碍物for (int i = 0; i < backGround.getObstacleList().size(); i++) {Obstacle ob = backGround.getObstacleList().get(i);if (ob.getY() == this.y + 25 && (ob.getX() > this.x - 30 && ob.getX() < this.x + 25)) {onObstacle = true;}//判断跳起是否顶到了砖块if ((ob.getY() >= this.y - 30 && ob.getY() <= this.y - 20) && (ob.getX() > this.x - 30 && ob.getX() < this.x + 25)) {if (ob.getType() == 0) {backGround.getObstacleList().remove(ob);backGround.createMoney(ob.getX(), ob.getY() - 40, 9); //金币try {new Music(2);} catch (FileNotFoundException | JavaLayerException | InterruptedException e) {e.printStackTrace();}try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}score += 50;}upTime = 0;}if (ob.getType() == 9) {backGround.getObstacleList().remove(ob);backGround.createMoney(ob.getX(), ob.getY() + 40, 10);gold += 1;}//判断是否可以往右走if (ob.getX() == this.x + 25 && (ob.getY() > this.y - 30 && ob.getY() < this.y + 25)) {canRight = false;}//判断是否可以往左走if (ob.getX() == this.x - 30 && (ob.getY() > this.y - 30 && ob.getY() < this.y + 25)) {canLeft = false;}}//判断马里奥是否碰到敌人死亡  或者踩死敌人for (int i = 0; i < backGround.getEnemyList().size(); i++) {Enemy e = backGround.getEnemyList().get(i); //当前敌人if (e.getY() == this.y + 20 && (e.getX() - 25 <= this.x && e.getX() + 35 >= this.x)) {if (e.getType() == 1) {e.death();upTime = 3;ySpeed = -10;score += 100;} else if (e.getType() == 2) {//马里奥死亡death();}}if ((e.getX() + 35 > this.x && e.getX() - 25 < this.x) && (e.getY() + 35 > this.y && e.getY() - 20 < this.y)) {death();}}//进行马里奥跳跃的操作if (onObstacle && upTime == 0) {if (status.indexOf("left") != -1) {if (xSpeed != 0) {status = "move--left";} else {status = "stop--left";}} else {if (xSpeed != 0) {status = "move--right";} else {status = "stop--right";}}} else {if (upTime != 0) {upTime--;} else {fall();}y += ySpeed;}}//判断马里奥是否在运动if ((canLeft && xSpeed < 0) || (canRight && xSpeed > 0)) {//改变马里奥坐标x += xSpeed;//判断马里奥是否移动到了屏幕最左边if (x < 0) {x = 0;}}//判断当前是否在移动状态if (status.contains("move")) {index = index == 0 ? 1 : 0;}//判断是否是向左移动if ("move--left".equals(status)) {show = StaticValue.run_L.get(index);}//判断是否是向右移动if ("move--right".equals(status)) {show = StaticValue.run_R.get(index);}//判断是否向左停止if ("stop--left".equals(status)) {show = StaticValue.stand_L;}//判断是否向右停止if ("stop--right".equals(status)) {show = StaticValue.stand_R;}//判断马里奥是否向左跳跃if ("jump--left".equals(status)) {show = StaticValue.jump_L;}//判断马里奥是否向右跳跃if ("jump--right".equals(status)) {show = StaticValue.jump_R;}try {//让线程休眠50毫秒Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}}
}

障碍物类–Obstacle.java

绘制场景中所需要的障碍物,例如地面、砖块、水管等等。该类中的属性包括了障碍物的坐标,障 碍物所需要显示的图片等。游戏中的场景是由背景中的障碍物绘制而成的,不同的障碍物所在的位置肯 定也不相同,那么对于障碍物而言,就必须要有坐标属性来使绘制的时候将不同的障碍物绘制到不同的 位置,所以必须要有两个 int 属性 x 和 y 来表示障碍物的坐标。同时该类也必须要实现 Runnable 接口, 实现这个接口的作用主要是为了在最有一个场景中控制旗子的运动,当然同时还要为该类加入线程。 最后该类中的 run 方法主要是为了控制最后一个场景中的旗子的移动,并且在旗子移动完毕后要 设置一个标记,并且将该标记表示给马里奥类,这样马里奥就可以开始自主移动了。部分代码如下:

package ClassPackage;import java.awt.image.BufferedImage;public class Obstacle implements Runnable {// 坐标private int x;private int y;//障碍物类型private int type;// 用于显示图像private BufferedImage show = null;//当前场景对象private BackGround bg = null;//用于完成棋子下落的线程对象private Thread thread = new Thread(this);public Obstacle(int x, int y, int type, BackGround bg) {this.x = x;this.y = y;this.type = type;this.bg = bg;show = StaticValue.obstacle.get(type);//如果是棋子,则启动线程if (type == 8) {thread.start();}}public int getX() {return x;}public int getY() {return y;}public int getType() {return type;}public BufferedImage getShow() {return show;}@Overridepublic void run() {while (true) {if (this.bg.isReach()) {if (this.y < 375) {this.y += 5;} else {this.bg.setBase(true);}}try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}}
}

敌人类–Enemy.java

该类中主要设置了两种敌人,一种是蘑菇怪,可以被马里奥踩死,另一种是食人花,不能被踩死。 该类中的属性包括了敌人的坐标,需要显示的图片,以及敌人的移动方向和移动范围等。。 在该类中首先要实现 Runnable 接口,因为在游戏中的敌人是可以移动的,所以一定要通过重写 run() 方法来达到敌人可以移动的效果。当然还要在该类中定义一个 Thread 属性,用于控制线程。在 该类中就要定义两个 int 属性 x 和 y,用于控制敌人的位置以及敌人的移动。 这个类中应当有两个构造方法,对于不同的敌人,所需要的属性都是不同的。对于蘑菇则需要判断 是否移动到窗口边界,以及是否死亡。对于食人花则需要判断是否移动到上下限。

package ClassPackage;import java.awt.image.BufferedImage;public class Enemy implements Runnable {//存储当前的坐标private int x;private int y;//存储敌人的类型private int type;//判断敌人运动的方向private boolean face_tc = true;//用于显示当前敌人的图像private BufferedImage show;//定义一个背景图像private BackGround bg;//食人花运动的极限范围private int max_up = 0;private int max_down = 0;//定义线程对象private Thread thread = new Thread(this);//定义当前图片的状态private int image_type = 0;//蘑菇敌人的构造函数public Enemy(int x, int y, boolean face_tc, int type, BackGround bg) {this.x = x;this.y = y;this.type = type;this.face_tc = face_tc;this.bg = bg;this.show = StaticValue.mogu.get(0);//启动线程实现蘑菇的移动thread.start();}//食人花敌人的构造函数public Enemy(int x, int y, boolean face_tc, int type, int max_up, int max_down, BackGround bg) {this.x = x;this.y = y;this.type = type;this.face_tc = face_tc;this.show = StaticValue.flower.get(0);this.bg = bg;this.max_up = max_up;this.max_down = max_down;//启动线程实现食人花的移动thread.start();}//乌龟敌人的构造函数public Enemy(int x, int y, int type, BackGround bg) {this.x = x;this.y = y;this.type = type;this.show = StaticValue.flower.get(0);this.bg = bg;//启动线程实现乌龟的移动thread.start();}// 敌人的死亡方法public void death() {show = StaticValue.mogu.get(2);this.bg.getEnemyList().remove(this);}public int getType() {return type;}public BufferedImage getShow() {return show;}public int getX() {return x;}public int getY() {return y;}@Overridepublic void run() {while (true) {//判断是否是蘑菇敌人if (type == 1) {if (face_tc) {this.x -= 2;} else {this.x += 2;}image_type = image_type == 1 ? 0 : 1;show = StaticValue.mogu.get(image_type);}//定义两个布尔变量boolean canLeft = true;boolean canRight = true;//遍历每一个障碍物for (int i = 0; i < bg.getObstacleList().size(); i++) {Obstacle ob1 = bg.getObstacleList().get(i);//判断是否可以往右走if (ob1.getX() == this.x + 36 && (ob1.getY() + 65 > this.y && ob1.getY() - 35 < this.y)) {canRight = false;}//判断是否可以继续往左走if (ob1.getX() == this.x - 36 && (ob1.getY() + 65 > this.y && ob1.getY() - 35 < this.y)) {canLeft = false;}}if (face_tc && !canLeft || this.x == 0) {face_tc = false;} else if ((!face_tc) && (!canRight) || this.x == 764) {face_tc = true;}//判断是否是食人花敌人if (type == 2) {if (face_tc) {this.y -= 2;} else {this.y += 2;}image_type = image_type == 1 ? 0 : 1;//食人花是否到达了极限位置if (face_tc && (this.y == max_up)) {face_tc = false;}if ((!face_tc) && (this.y == max_down)) {face_tc = true;}show = StaticValue.flower.get(image_type);}if (type == 3) {this.x -= 1;try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}image_type = image_type == 0 ? 1 : 0;show = StaticValue.turtle.get(image_type);}try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}}
}

音乐类–Music.java

对于音乐类,则通过变量 type 来判断是播放哪种音乐,为了让播放音乐时人物不会卡住,采用单 独一个线程播放音乐,对于不同音乐则创建多个线程实现。然后只需要在其他类中,通过在不同场景中 定义 Music 对象 new Music(type),t 来实现触发该 type 对应的音乐播放。部分代码如下:

package ClassPackage;import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.Player;import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;public class Music {public Music(int type) throws FileNotFoundException, JavaLayerException, InterruptedException {if (type == 1) {Player player;String str = System.getProperty("user.dir") + "/src/music/music.wav";BufferedInputStream name = new BufferedInputStream(new FileInputStream(str));player = new Player(name);player.play();} else if (type == 2) {Player playerGold;String str = System.getProperty("user.dir") + "/src/Music/MarioTopGoldCoins.mp3";BufferedInputStream name = new BufferedInputStream(new FileInputStream(str));playerGold = new Player(name);new Thread(() -> {//调用播放方法进行播放try {playerGold.play();} catch (JavaLayerException e) {e.printStackTrace();}}).start();} else if (type == 3) {Player playerJump;String str = System.getProperty("user.dir") + "/src/Music/jump.mp3";BufferedInputStream name = new BufferedInputStream(new FileInputStream(str));playerJump = new Player(name);new Thread(() -> {//调用播放方法进行播放try {playerJump.play();} catch (JavaLayerException e) {e.printStackTrace();}}).start();} else if (type == 4) {Player playerDeath;String str = System.getProperty("user.dir") + "/src/Music/death.mp3";BufferedInputStream name = new BufferedInputStream(new FileInputStream(str));playerDeath = new Player(name);new Thread(() -> {//调用播放方法进行播放try {playerDeath.play();} catch (JavaLayerException e) {e.printStackTrace();}}).start();} else if (type == 5) {Player playerCity;String str = System.getProperty("user.dir") + "/src/Music/AccelerateToTheCastle.mp3";BufferedInputStream name = new BufferedInputStream(new FileInputStream(str));playerCity = new Player(name);new Thread(() -> {//调用播放方法进行播放try {playerCity.play();} catch (JavaLayerException e) {e.printStackTrace();}}).start();}}}

项目结构:

报告截图

java 课设-超级玛丽游戏相关推荐

  1. 【源码+教程】Java课设项目_12款最热最新Java游戏项目_Java游戏开发_Java小游戏_飞翔的小鸟_王者荣耀_超级玛丽_推箱子_黄金矿工_贪吃蛇

    马上就要期末了,同学们课设做的如何了呢?本篇为大家带来了12款热门Java小游戏项目的源码和教程,助力大家顺利迎接暑假![源码+教程]Java课设项目_12款最热最新Java游戏项目_Java游戏开发 ...

  2. JAVA课设单人版五子棋小游戏

    内容介绍:该程序为Java课设的单人版五子棋小游戏,通过eclipse编辑,实现了动作事件的监听与处理,以及JavaSwing的界面编程.  编辑排行榜,包含局数,结果,步数,以及"关于我们 ...

  3. 【java毕业设计】基于java+GUL的超级玛丽游戏GUL设计与实现(毕业论文+程序源码)——超级玛丽游戏

    基于java+GUL的超级玛丽游戏GUL设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于java+GUL的超级玛丽游戏GUL设计与实现,文章末尾附有本毕业设计的论文和源码下载地址哦. 文章 ...

  4. 课设——石头剪刀布游戏

    存个代码 问题: 样式图: 代码: package Main; import java.awt.*; //导入抽象窗口工具类包: import java.awt.event.*; //导入事件处理工具 ...

  5. java 课设 商品库存管理系统

    比较辛苦的java课设!写了蛮久的,战斗了好多个通宵. 下载https://download.csdn.net/download/qq_37871063/10297290 入门:JAVAFX+MVC+ ...

  6. 华南农业大学课设——数据结构课设、Java课设、操作系统课设

    文章目录 缘起 大二上-数据结构课设(高校教学管理系统)-C++.Qt 视频演示 感想 大二下-Java课设(流程图绘制程序)-JavaFX 视频演示 感想 大三上-操作系统课设(模拟磁盘文件系统实现 ...

  7. 学生信息管理系统(成绩统计)Java课设

    下载地址:学生信息管理系统(成绩统计)Java课设-Web服务器文档类资源-CSDN下载 ├── StudentInfo │   ├── bin │   │   ├── com │   │   │   ...

  8. Java课设——ArxivHelper

    项目地址https://github.com/PKUCSS/arxiv-helper How to run运行方式:java -jar arxiv-helper.jar Tips:We use pyi ...

  9. 100套java课设源码参考/毕设源码代码参考

    引言:本人是一个Java 开发者,喜欢分享Java课设源码和代码,用于课程设计或者作业学习参考噢,开发一些有技术含量的Java web源码,主要的技术有JSP+Servlet,SSM/SpringBo ...

最新文章

  1. CSS3 美女动画相框
  2. 与vnpy相关的有用博客网址
  3. 【错误记录】GitHub 网站和仓库无法访问 ( 域名重定向 | 检查 C:\Windows\System32\drivers\etc\hosts 配置文件中的 GitHub 地址域名配置 )
  4. 如何写优雅的SQL原生语句?
  5. mysql存储word文档_使用MySQL存储和检索word文档
  6. Spring 配置多个数据源,并实现动态切换
  7. 繁体字_如何简单快速地批量认识繁体字?
  8. “ 鸡尾酒会问题”(cocktail party problem)
  9. android手机qq账号管理在哪里,qq安全中心手机版之功能详解
  10. 基于单片机的数字频率计设计
  11. 淘宝开放平台深入浅出
  12. Learn Git Branching学习笔记 Git常用命令
  13. 连锁企业——屈臣氏的经营模式
  14. 论文笔记1:Fast and Robust Multi-Person 3D Pose Estimation from Multiple Views
  15. php生成gif1009php生成gif,怎样将几张图片做成会动的GIF的动态图像?GIF动画制作软件,将图片制作成GIF动图...
  16. 最近在做一些改变,想听听你的意见
  17. Attention mask理解
  18. xx-xx-xx-xx转换成x年x月x日星期x
  19. python话费充值_【图片】话费充值解决方案(附代码)转载【开发吧】_百度贴吧...
  20. python爬虫爬取《斗破苍穹》小说全文

热门文章

  1. windows10下替换记事本中指定字符
  2. 315Mhz、433Mhz无线遥控信号的解码分析和模拟
  3. 计算机集中控制系统结构上和DCS基本一致,DCS工作原理及组成ppt课件
  4. 计算机系400分左右的学校,杭州2021年400分能上计算机学校吗
  5. 详解 Spark RDD 的转换操作与行动操作
  6. 坑:vuex中的mutations不能使用return获取数据
  7. linux下打包和解包、解压和压缩
  8. python end用法是什么?
  9. 基于MATLAB的频谱、能量谱、三分之一倍频程分析
  10. fliqlo翻页时钟屏保win7/win10免费下载