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()方法,在对应的键盘监听事件中调用对应的方法即可。如下:
@Override public 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(); } } //当键盘松开按键时调用 @Override public 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 * 600
this.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();
}
@Override
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 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();
}
@Override
public void keyTyped(KeyEvent e) {
}
//当键盘按下按键时调用
@Override
public 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();
}
}
//当键盘松开按键时调用
@Override
public void keyReleased(KeyEvent e) {
//想左停止
if (e.getKeyCode() == 37) {
mario.leftStop();
}
//向右停止
if (e.getKeyCode() == 39) {
mario.rightStop();
}
}
@Override
public 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=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 = 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=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));
}
}
//绘制砖块
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=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));
}
}
//绘制第三个背景的砖块
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);
}
@Override
public 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;
}
@Override
public 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);
}
@Override
public 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();
}
}
@Override
public 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);
}
@Override
public 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);
}
}
}
}