1.实现了哪些功能?

1.按键←、→、↑实现玛丽奥的基本移动和跳跃。

2.获取特殊道具后可以发射子弹、可以巨大化以及提升跳跃能力。

3.玛丽奥头顶特殊方块后会生成可以旋转的金币,吃到金币可以增加分数。

4.敌人会不断移动,接触到障碍物后会自动改变移动方向。

5.玛丽奥可以踩敌人或者用枪击杀敌人,同时增加分数。

6.有背景音乐,以及玛丽奥跳跃、射击等音效。

2.需要哪些基础?

线程、IO流、面向对象、集合、Swing等

3.设计思路。

①加载图片

  • 加载图片的方式有五种,详细可见此博客:https://blog.csdn.net/qq827245563/article/details/79302217?spm=1001.2014.3001.5506

我这里是通过ImageIO读取图片文件的方式来获取加载图片。

例如这样:

public static BufferedImage bg = null;
bg = ImageIO.read(new File(path + "bg.png"));
  • 设置为静态变量可以在使用时更便利的调用,同时也能最先加载图片。

②游戏窗口的创建与绘图

  • 游戏窗口类继承了JFrame类,自然也担任其绘图的任务。

对于窗体的基本设置,在这里就不多赘述。

对于绘图,我们可以重写JFrame类中的paint()方法,通过g.drawImage(Image,int x,int y,Object observer)来绘制玛丽奥、敌人、障碍物等等。

  • 这里需要利用图形界面编程的双缓冲技术。

先提出问题,为什么需要使用双缓冲技术?

在单缓冲绘图中,会出先屏幕闪烁的问题。而出现屏幕闪烁的原因通常是由于快速地多次绘制同一个区域所导致的,因为我们需要不断重绘界面来更新游戏进程(玛丽奥的移动、道具的i显示等等)。这种情况下,由于绘制的速度过快,屏幕上的图像可能会出现闪烁或者部分未被绘制的情况。这是因为屏幕的刷新率是有限的,如果在两次屏幕刷新之间绘制了新的图像,那么屏幕上原来的图像就会消失,从而导致闪烁的效果。

什么是双缓冲技术?它是如何解决屏幕闪烁的问题的?

使用双缓冲技术,可以先将要绘制的图像绘制到一个缓冲区中,待所有绘制完成后再一次性地将缓冲区中的图像绘制到屏幕上,这样就可以避免图像闪烁的问题。

具体实现的代码案例:

import java.awt.*;
import java.awt.image.BufferedImage;public class DoubleBufferingExample extends Canvas {private BufferedImage buffer;public DoubleBufferingExample() {// 创建一个大小为 500x500 的缓冲区buffer = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);}public void paint(Graphics g) {// 获取缓冲区的 Graphics 对象Graphics bufferGraphics = buffer.getGraphics();// 在缓冲区中绘制图像bufferGraphics.setColor(Color.WHITE);bufferGraphics.fillRect(0, 0, buffer.getWidth(), buffer.getHeight());bufferGraphics.setColor(Color.RED);bufferGraphics.fillRect(100, 100, 200, 200);// 将缓冲区中的图像绘制到屏幕上g.drawImage(buffer, 0, 0, null);}public static void main(String[] args) {Frame f = new Frame("Double Buffering Example");DoubleBufferingExample example = new DoubleBufferingExample();f.add(example);f.setSize(500, 500);f.setVisible(true);}
}

在该示例中,创建了一个大小为 500x500 的缓冲区,并在缓冲区中绘制了一个红色的矩形。最后,将缓冲区中的图像绘制到屏幕上,从而避免了图像闪烁的问题。

  • 键盘监听器

让该类实现KeyListener接口,并重写keyPressed()和 keyReleased()方法,在对应的键盘监听事件中调用对应的方法即可。如下:

 @Overridepublic void keyPressed(KeyEvent e) {//向右移动if (e.getKeyCode() == 39) {mario.rightMove();}//向左移动if (e.getKeyCode() == 37) {mario.leftMove();}//跳跃if (e.getKeyCode() == 38) {mario.jump();}//发射子弹if(e.getKeyCode()==32){mario.fire();}}//当键盘松开按键时调用@Overridepublic void keyReleased(KeyEvent e) {//想左停止if (e.getKeyCode() == 37) {mario.leftStop();}//向右停止if (e.getKeyCode() == 39) {mario.rightStop();}}

③如何实现玛丽奥的移动?

我们可以定义一个xSpeed的int类型变量,来改变玛丽奥的坐标,控制玛丽奥的移动方向和速度。再定义一个status的String类型变量来表示玛丽奥的运动状态。通过move()方法来改变玛丽奥的运动状态和移动速度。

然后在一个线程中,不断判断玛丽奥的运动状态来改变玛丽奥的坐标,实现玛丽奥的移动。

④如何让玛丽奥发射子弹?

  • 子弹的本质是什么?

玛丽奥只有发射这个行为,发射之后,子弹就跟玛丽奥没有关系。所以子弹需要单独封装为一个类,拥有自己的属性和行为。

  • 如何实现子弹的正确发射和移动?

1.首先对于子弹的方向:我们需要获取玛丽奥的方向,然后将其作为参数传入子弹的构造方法,来控制子弹的移动方向。

2.对于子弹的出现位置:子弹的出现位置一定是在玛丽奥的位置,所以在发射子弹的时候,创建子弹的对象,并传入玛丽奥的坐标作为子弹的初始坐标,进行发射。

  • 如何消除子弹?

我们可以把创建好的子弹放在一个子弹集合中,当子弹被创建出来就存放在集合中,paint()方法对集合进行遍历绘制。如果子弹碰撞到障碍物或者敌人,就从子弹集合中删除该子弹对象。

⑤如何实现动图效果?

动图效果的实现其实是通过多个静态图片不断切换来实现的。例如玛丽奥的走路、金币的转动等等。我们只需要提前准备好几张图片,通过线程或定时器,轮番展示即可。如果是两张图片可以用三目运算符解决,如果是三张或三张以上的图片,可以通过switch-case语句来实现。示例如下:

//如果是金币if(this.type==2){switch (image_type) {case 0 -> image_type = 1;case 1 -> image_type = 2;case 2 -> image_type = 3;case 3 -> image_type = 4;case 4 -> image_type = 5;case 5 -> image_type = 0;}show=StaticValue.coins.get(image_type);}

⑥如何实现碰撞检测?

对于碰撞检测有三种方法,我在简书的游戏考核复盘上有写过,这里引入链接:https://www.jianshu.com/p/d1ccf963f76b

对于用Java实现一个小游戏,则推荐使用矩形碰撞检测,在玛丽奥对障碍物的阻碍,子弹碰撞到敌人或者障碍物的检测,都可以使用。原理就是,在障碍物的坐标周围形成一个与图片像素大小相应的矩形。通过intersects()来判断两个矩形是否接触。

⑦如何添加背景音乐?

我们可以在指定位置创建一个匿名内部类的线程来播放音乐。示例:

 new Thread("sheji"){public void run(){String filename="src//Music//attack.mp3";try {BufferedInputStream buffer = new BufferedInputStream(new FileInputStream(filename));Player player = new Player(buffer);player.play();} catch (Exception e) {System.out.println(e);}}}.start();

在此之前我们需要导入一个jre包。链接在这里:http://链接:https://pan.baidu.com/s/1iEeJ8pufKouQrBzzBPIhew?pwd=fncd 提取码:fncd

4.源码

1.MyFrame类(主方法所在)

package com.sxt;import javazoom.jl.player.Player;import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;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();public static boolean isStart;//定义一个线程对象,用于实现马里奥的运动private Thread thread = new Thread(this);public MyFrame() {//设置窗口的大小为800 * 600this.setSize(800, 600);//设置窗口居中显示this.setLocationRelativeTo(null);//设置窗口的可见性this.setVisible(true);//设置点击窗口上的关闭键,结束程序this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置窗口大小不可变this.setResizable(false);//向窗口对象添加键盘监听器this.addKeyListener(this);//设置窗口名称this.setTitle("超级玛丽");//初始化图片StaticValue.init();isStart = true;//初始化马里奥mario = new Mario(0, 355,this.getHeight(),this.getWidth());//创建全部的场景for (int i = 1; i <= 3; i++) {allBg.add(new BackGround(i, i == 3 ? true : false));}//将第一个场景设置为当前场景nowBg = allBg.get(0);mario.setBackGround(nowBg);//绘制图像repaint();thread.start();new Thread("backMusic"){public void run(){String filename="src//Music//music.wav";try {sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}try {BufferedInputStream buffer = new BufferedInputStream(new FileInputStream(filename));Player player = new Player(buffer);player.play();} catch (Exception e) {System.out.println(e);}}}.start();}@Overridepublic 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 e : nowBg.getEnemyList()) {graphics.drawImage(e.getShow(),e.getX(),e.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);//绘制马里奥if(!mario.isBig()) {graphics.drawImage(mario.getShow(), mario.getX(), mario.getY(), 40, 25, this);}else {graphics.drawImage(mario.getShow(), mario.getX(), mario.getY()-15, 50, 40, this);}//绘制子弹for(Bullet bullet:nowBg.getBulletList()){graphics.drawImage(bullet.getShow(),bullet.getX()+5,bullet.getY()+7,this);}//绘制道具for(DaoJv daoJv:nowBg.getDaoJvList()){graphics.drawImage(daoJv.getShow(), daoJv.getX(), daoJv.getY(), this);}//添加分数Color c = graphics.getColor();graphics.setColor(Color.BLACK);graphics.setFont(new Font("黑体",Font.BOLD,25));graphics.drawString("当前的分数为: " + mario.getScore(),300,100);graphics.setColor(c);//将图像绘制到窗口中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();}//发射子弹if(e.getKeyCode()==32){mario.fire();}}//当键盘松开按键时调用@Overridepublic void keyReleased(KeyEvent e) {//想左停止if (e.getKeyCode() == 37) {mario.leftStop();}//向右停止if (e.getKeyCode() == 39) {mario.rightStop();}}@Overridepublic void run() {while (true) {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();}}}
}

2.BackGround类

package com.sxt;import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;public class BackGround {//当前场景要显示的图像private BufferedImage bgImage = null;//记录当前是第几个场景private int sort;//判断是否是最后一个场景private boolean flag;//用于存放我们的所有障碍物private List<Obstacle> obstacleList = new ArrayList<>();//用于存放我们的所有敌人private List<Enemy> enemyList = new ArrayList<>();//用于存放我们的所有子弹private List<Bullet> bulletList = new ArrayList<>();//用于存放我们的所有道具private List<DaoJv> daoJvList = new ArrayList<>();//用于显示旗杆private BufferedImage gan = null;//用于显示城堡private BufferedImage tower = null;//判断马里奥是否到达旗杆位置private boolean isReach = false;//判断旗子是否落地private boolean isBase = false;public BackGround() {}public BackGround(int sort,boolean flag) {this.sort = sort;this.flag = flag;if (flag) {bgImage = StaticValue.bg2;}else {bgImage = StaticValue.bg;}//判断是否是第一关if (sort == 1) {//绘制第一关的地面,上地面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));}}//绘制砖块for (int i = 120;i <= 150;i += 30) {obstacleList.add(new Obstacle(i,300,7,this));}obstacleList.add(new Obstacle(210,300,7,this));//绘制砖块for (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));}}//绘制砖块for (int i = 420;i <= 450;i += 30) {obstacleList.add(new Obstacle(i,240,7,this));}//绘制特殊物品obstacleList.add(new Obstacle(445,150,9,this));//绘制道具方块obstacleList.add(new Obstacle(180,300,10,this));obstacleList.add(new Obstacle(480,300,10,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));}//判断是否是第二关if (sort == 2) {//绘制第二关的地面,上地面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));}}//绘制第一个水管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));}}//绘制砖块obstacleList.add(new Obstacle(300,330,0,this));//绘制道具方块obstacleList.add(new Obstacle(210,300,10,this));obstacleList.add(new Obstacle(285,270,10,this));for (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));}}for (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) {//绘制第三关的地面,上地面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));}}//绘制第三个背景的砖块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;}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));enemyList.add(new Enemy(500,385,true,1,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 List<Bullet> getBulletList() {return bulletList;}public boolean isBase() {return isBase;}public void setBase(boolean base) {isBase = base;}public List<Enemy> getEnemyList() {return enemyList;}public List<DaoJv> getDaoJvList() {return daoJvList;}
}

3.Bullet类

package com.sxt;import java.awt.*;
import java.awt.image.BufferedImage;public class Bullet implements Runnable{//子弹坐标private int x;private int y;//子弹的朝向private boolean face_to=true;//子弹的图像private BufferedImage show=null;//创建一个背景类private BackGround bg;//子弹图像的状态private int image_type=0;//子弹移动的线程private Thread thread=new Thread(this);public Bullet(int x,int y,boolean face_to,BackGround bg){this.x=x;this.y=y;this.face_to=face_to;this.bg=bg;show=StaticValue.bullet_R;thread.start();}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 boolean isFace_to() {return face_to;}public void setFace_to(boolean face_to) {this.face_to = face_to;}public BufferedImage getShow() {return show;}public void setShow(BufferedImage show) {this.show = show;}public BackGround getBg() {return bg;}public void setBg(BackGround bg) {this.bg = bg;}public Rectangle getRec(){return new Rectangle(this.x,this.y,15,15);}@Overridepublic void run() {while(true){if(face_to){this.x-=10;show=StaticValue.bullet_L;}else {this.x+=10;show=StaticValue.bullet_R;}//遍历所有障碍物for(int i=0;i<bg.getObstacleList().size();i++){Obstacle ob=bg.getObstacleList().get(i);if(this.getRec().intersects(ob.getRec())){//this.y=900;this.bg.getBulletList().remove(this);}}//遍历所有敌人for(int i=0;i<bg.getEnemyList().size();i++){Enemy e=bg.getEnemyList().get(i);if(this.getRec().intersects(e.getRec())){this.y=900;this.bg.getBulletList().remove(this);e.death();}}try {thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

4.DaoJv类

package com.sxt;import java.awt.image.BufferedImage;public class DaoJv implements Runnable{//定义道具坐标private int x,y;//定义道具类型private int type;//定义道具显示图片private BufferedImage show=null;//定义线程private Thread thread = new Thread(this);//定义一个背景类private BackGround bg;//显示图片的状态private int image_type=0;public DaoJv(){}public DaoJv(int x,int y,int type,BackGround bg){this.x=x;this.y=y;this.type=type;this.bg=bg;show=StaticValue.bigD;thread.start();}public void eat(){bg.getDaoJvList().remove(this);}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 int getType() {return type;}public void setType(int type) {this.type = type;}public BufferedImage getShow() {return show;}public void setShow(BufferedImage show) {this.show = show;}public BackGround getBg() {return bg;}public void setBg(BackGround bg) {this.bg = bg;}public int getImage_type() {return image_type;}public void setImage_type(int image_type) {this.image_type = image_type;}@Overridepublic void run() {while(true){//是否是变大道具if(this.type==0){show=StaticValue.bigD;}//如果是跳跃提升道具if(this.type==1){show=StaticValue.jumpD;}//如果是金币if(this.type==2){switch (image_type) {case 0 -> image_type = 1;case 1 -> image_type = 2;case 2 -> image_type = 3;case 3 -> image_type = 4;case 4 -> image_type = 5;case 5 -> image_type = 0;}show=StaticValue.coins.get(image_type);}try {thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

5.Enemy类

package com.sxt;import java.awt.*;
import java.awt.image.BufferedImage;public class Enemy implements Runnable {//存储当前坐标private int x;private int y;//存储敌人类型private int type;//判断敌人运动的方向private boolean face_to = 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_to, int type, BackGround bg) {this.x = x;this.y = y;this.face_to = face_to;this.type = type;this.bg = bg;show = StaticValue.mogu.get(0);thread.start();}//食人花敌人的构造函数public Enemy(int x, int y, boolean face_to, int type, int max_up, int max_down, BackGround bg) {this.x = x;this.y = y;this.face_to = face_to;this.type = type;this.max_up = max_up;this.max_down = max_down;this.bg = bg;show = StaticValue.flower.get(0);thread.start();}//死亡方法public void death() {show = StaticValue.mogu.get(2);this.bg.getEnemyList().remove(this);}public int getX() {return x;}public int getY() {return y;}public BufferedImage getShow() {return show;}public int getType() {return type;}public Rectangle getRec(){return new Rectangle(this.x,this.y,35,35);}@Overridepublic void run() {while (true) {//判断是否是蘑菇敌人if (type == 1) {if (face_to) {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_to && !canLeft || this.x == 0) {face_to = false;} else if ((!face_to) && (!canRight) || this.x == 764) {face_to = true;}//判断是否是食人花敌人if (type == 2) {if (face_to) {this.y -= 2;} else {this.y += 2;}image_type = image_type == 1 ? 0 : 1;//食人花是否到达极限位置if (face_to && (this.y == max_up)) {face_to = false;}if ((!face_to) && (this.y == max_down)) {face_to = true;}show = StaticValue.flower.get(image_type);}try {Thread.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}}}}

6.Mario类

package com.sxt;import javazoom.jl.player.Player;import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.FileInputStream;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 xSpeed;//马里奥的跳跃速度private int ySpeed;//定义一个索引来遍历玛丽奥的动作private int index;//表示马里奥上升的时间private int upTime = 0;//用于判断马里奥是否走到了城堡的门口private boolean isOK;//用于判断马里奥是否死亡private boolean isDeath = false;//定义玛丽奥的朝向private boolean face_to;//定义玛丽奥的枪里是否有子弹private boolean isEmpty=true;//表示分数private int score = 0;private int flag=0;private boolean isBig=false;private boolean isJump=false;private int height=25;private int width=25;public Mario() {}public Mario (int x,int y,int height,int width) {this.x = x;this.y = y;this.height=height;this.width=width;show = StaticValue.stand_R;this.status = "stand--right";this.face_to=true;thread = new Thread(this);thread.start();}//马里奥的死亡方法public void death() {isDeath = true;}//马里奥向左移动public void leftMove() {//改变速度xSpeed = -5;//判断马里奥是否碰到旗子if (backGround.isReach()) {xSpeed = 0;}//判断马里奥是否处于空中if (status.indexOf("jump") != -1) {status = "jump--left";}else {status = "move--left";}this.face_to=true;}//马里奥向右移动public void rightMove() {xSpeed = 5;//判断马里奥是否碰到旗子if (backGround.isReach()) {xSpeed = 0;}if (status.indexOf("jump") != -1) {status = "jump--right";}else {status = "move--right";}this.face_to=false;}//马里奥向左停止public void leftStop() {xSpeed = 0;if (status.indexOf("jump") != -1) {status = "jump--left";}else {status = "stop--left";}this.face_to=true;}//马里奥向右停止public void rightStop() {xSpeed = 0;if (status.indexOf("jump") != -1) {status = "jump--right";}else {status = "stop--right";}this.face_to=false;}//马里奥跳跃public void jump() {if (status.indexOf("jump") == -1) {if (status.indexOf("left") != -1) {status = "jump--left";this.face_to = true;} else {status = "jump--right";this.face_to = false;}if(!isJump) {ySpeed = -10;upTime = 7;}else{ySpeed=-20;upTime=7;}new Thread("jump"){public void run(){String filename="src//Music//jump.mp3";try {sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}try {BufferedInputStream buffer = new BufferedInputStream(new FileInputStream(filename));Player player = new Player(buffer);player.play();} catch (Exception e) {System.out.println(e);}}}.start();//判断马里奥是否碰到旗子if (backGround.isReach()) {ySpeed = 0;}}}//马里奥下落public void fall() {if(status.indexOf("left")!=-1){status="jump--left";this.face_to=true;}else{status="jump--right";this.face_to=false;}ySpeed=10;}//玛丽奥发射子弹public void fire(){if(!isEmpty) {backGround.getBulletList().add(new Bullet(this.x + 8, this.y + 7, this.face_to, backGround));new Thread("sheji"){public void run(){String filename="src//Music//attack.mp3";try {BufferedInputStream buffer = new BufferedInputStream(new FileInputStream(filename));Player player = new Player(buffer);player.play();} catch (Exception e) {System.out.println(e);}}}.start();}}@Overridepublic void run() {while (true) {flag=0;//判断是否处于障碍物上boolean onObstacle = false;//判断是否可以往右走boolean canRight = true;//判断是否可以往左走boolean canLeft = true;//System.out.println(x);//判断马里奥是否到达旗杆位置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||ob.getType()==9) {backGround.getObstacleList().remove(ob);score += 1;flag=1;if(ob.getType()==9){isEmpty=false;}}if (ob.getType()==10){if(backGround.getSort()!=1) {if (isBig) {backGround.getDaoJvList().add(new DaoJv(ob.getX(), ob.getY() - 35, 1, backGround));} else {backGround.getDaoJvList().add(new DaoJv(ob.getX(), ob.getY() - 35, 0, backGround));}}else{backGround.getDaoJvList().add(new DaoJv(ob.getX()+5, ob.getY() - 30, 2, backGround));}}upTime = 0;}//判断是否可以往右走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();score += 2;upTime = 3;ySpeed = -10;} 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();}}//判断马里奥是否碰到道具for(int i=0;i<backGround.getDaoJvList().size();i++){DaoJv d=backGround.getDaoJvList().get(i);if((d.getX()+35>this.x&&d.getX()-25<this.x)&&d.getY()+35>this.y&&d.getY()-25<this.y){//如果是变大道具if(d.getType()==0){d.eat();this.setBig(true);}//如果是跳跃提升道具if(d.getType()==1){d.eat();this.setJump(true);}//如果是金币道具if(d.getType()==2){d.eat();score+=5;}}}//进行马里奥跳跃的操作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 {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}}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 boolean isOK() {return isOK;}public boolean isDeath() {return isDeath;}public int getScore() {return score;}public boolean isBig() {return isBig;}public boolean isJump() {return isJump;}public void setBig(boolean big) {isBig = big;}public void setJump(boolean jump) {isJump = jump;}public int getHeight() {return height;}public void setHeight(int height) {this.height = height;}public int getWidth() {return width;}public void setWidth(int width) {this.width = width;}
}

7.Music类

package com.sxt;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() throws FileNotFoundException, JavaLayerException {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();}
}

8.Obstacle类

package com.sxt;import java.awt.*;
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;}//在障碍物周围生成一个矩形public Rectangle getRec(){return new Rectangle(this.x,this.y,30,30);}@Overridepublic void run() {while (true) {if (this.bg.isReach()) {if (this.y < 374) {this.y += 5;}else {this.bg.setBase(true);}}try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}}
}

9.StaticValue类

package com.sxt;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.security.PrivilegedExceptionAction;
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;//BufferedImage=null//旗杆public static BufferedImage gan = null;//障碍物public static List<BufferedImage> obstacle = new ArrayList<>();//金币public static List<BufferedImage> coins=new ArrayList<>();//变大道具public static BufferedImage bigD=null;//跳跃提升道具public static BufferedImage jumpD=null;//马里奥向左跑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 BufferedImage bullet_R=null;public static BufferedImage bullet_L=null;//路径的前缀,方便后续调用public static String path = System.getProperty("user.dir") + "/src/images/";//初始化方法public static void init() {try {//加载背景图片bg = ImageIO.read(new File(path + "bg.png"));bg2 = ImageIO.read(new File(path + "bg2.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"));//加载马里奥向左跳跃jump_L = ImageIO.read(new File(path + "s_mario_jump1_L.png"));//加载马里奥向右跳跃jump_R = ImageIO.read(new File(path + "s_mario_jump1_R.png"));//加载子弹bullet_L=ImageIO.read(new File(path+"bullet1_left.png"));bullet_R=ImageIO.read(new File(path+"bullet1_right.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 + "brick.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")));} catch (IOException e) {e.printStackTrace();}//加载特殊物品try {obstacle.add(ImageIO.read(new File(path+"danjia.png")));obstacle.add(ImageIO.read(new File(path+"wenhaofangkuai.jpg")));} catch (IOException e) {throw new RuntimeException(e);}//加载蘑菇敌人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();}}//加载变大道具try {bigD=ImageIO.read(new File(path+"big.png"));} catch (IOException e) {throw new RuntimeException(e);}//加载跳跃提升道具try {jumpD=ImageIO.read(new File(path+"jump.png"));} catch (IOException e) {throw new RuntimeException(e);}//记载金币道具for(int i=1;i<=6;i++){try {coins.add(ImageIO.read(new File(path+"coin"+i+".png")));} catch (IOException e) {throw new RuntimeException(e);}}}}

Java实现超级玛丽小游戏教程相关推荐

  1. Java小游戏教程,一小时学会贪吃蛇大作战,制作属于自己的游戏

    相信很多人大学时候都学过Java这门语言吧,这门课一般最后有一个大作业,就是用Java写一个小软件,比方说简单的聊天软件.贪吃蛇.计算器等等.作为以前诺基亚手机上的经典游戏,贪吃蛇和俄罗斯方块一样,都 ...

  2. 【教程1】Java制作国际象棋小游戏-01

    Java 制作国际象棋小游戏-01 菜鸟学了几天Java之后手痒痒了,所以开始谋划写个小游戏什么的练练手,刚好一门面向对象的课程布置了一个project,不限内容不限语言,所以菜鸟的小组决定做个国际象 ...

  3. JAVA实现2048小游戏

    2048小游戏也算是一款好玩的益智休闲小游戏,下面本博主用 java 语言将该游戏复现,感兴趣的小伙伴点击 关注 哦! 同时博主还用 python 语言复现了该游戏,可点击以下链接浏览博主的另一篇文章 ...

  4. Java Swing实现小游戏集合源码

    ​小编也不给大家废话, 直接给大家分享源码 Java 植物大战僵尸 Java Swing实现小游戏扫雷 Java Swing实现的拼图小游戏 Java swing实现的小游戏魔方 Java Swing ...

  5. 不愧是大厂牛人!用Java实现象棋小游戏(附超详细,超长究极无敌代码)

    本文实例为大家分享了java实现象棋小游戏的具体代码,供大家参考,具体内容如下 用Eclipse编写 java环境1.8jdk 代码如下 package xiangqi象棋; /***中国象棋Java ...

  6. Java练手小游戏---黄金矿工

    你玩过4399曾经最火的黄金矿工小游戏吗,黄金矿工小游戏是一款休闲娱乐的一个挖宝游戏,在游戏中地下会有许多的宝藏,你得想办法把它们都挖出来,作为矿工的你,对你来说是轻车熟路的,能不能挖到宝,看你抓取的 ...

  7. 个人主页增添超级玛丽小游戏与留言板功能

    项目地址(GitHub). 我不打算花时间一点点去磨这个网站,没那么多时间,所以都是修改.利用别人现成的模板. 超级玛丽小游戏来源.留言板来源.小游戏是直接照抄,加了两个标签:留言板原来是txt文档格 ...

  8. java文字类小游戏2.0版本

    java文字类小游戏 用javaFx面板显示文字类小游戏,目前正已完成基本打斗和打怪爆出武器的开发,后续会不断更新示例图如下: 运行这个类开始代码我已上传至码云,有需要的小伙伴自行拉取代码,git项目 ...

  9. Java 贪吃虫小游戏

    我参考了[Java]Java实现贪吃蛇小游戏(带详细注释)和 java贪吃蛇小游戏(详解) 先设置需要用到的常数 package Snake;public class ConstantNumber { ...

最新文章

  1. 最小生成树prim算法———模板
  2. java中override快捷键_【基础回溯1】面试又被 Java 基础难住了?推荐你看看这篇文章。...
  3. O(N) 求 1~N 逆元 模板及证明
  4. 华为鸿蒙os系统有哪些黑科技,华为首款搭载鸿蒙os智慧产品除了鸿蒙系统还有这黑科技...
  5. intellij无法输入中文
  6. 深圳当代艺术家的一次聚会
  7. python dict初始化大小_在Python中初始化/创建/填充Dict的Dict
  8. mysql是一个_Mysql
  9. HC大会,华为联合合作伙伴发布一站式物联网IoT开发工具小熊派BearPi
  10. table中怎么设置两行间距
  11. uva 11021 Tribles
  12. linux下hex文件到bin文件的格式转化,hex转换成bin文件小工具(HEX2BIN.EXE)下载_hex转换成bin文件小工具(HEX2BIN.EXE)官方下载-太平洋下载中心...
  13. textbox wpf 居中_C# + WPF: TextBox中的光标定位问题
  14. 模拟退火算法求解超市布局问题
  15. 记Dorado7学习(4)
  16. C# ASP.NET WebApi入门
  17. 沉没成本效应:每个人终其一生的断舍离
  18. 深入学习java的困惑:3年开发,下一步学习的方向以及要做什么?
  19. 华为服务器显示110,ar110s路由器设置内部服务器
  20. 为什么要配置Java环境变量以及Java环境变量的配置

热门文章

  1. “她力量”:创邻科技联合创始人吴菁获评创业邦“2022最值得关注的女性创业者”榜单
  2. AutoCAD利用VBA宏绘制多重平行线
  3. Web前端:适合初学者的最佳 JavaScript 项目
  4. AI Studio学习篇3_《乘风破浪的姐姐》百度百科嘉宾信息爬取与数据分析
  5. 2023年第47届(第二届)浙江技能大赛网络安全项目 (世赛省选拔赛)A模块解析
  6. Adaptec by PMC为高密度超大规模数据中心三倍提升存储连接能力
  7. GOT Hook的简单实现与原理
  8. 哪些自媒体运营平台比较靠谱
  9. STM32 ADC输入采集电压
  10. c语言随机数rand函数一探究竟