一、游戏基本功能
1、能够左右移动和上下跳动(键盘监听)
2、能够踩死蘑菇
3、能够被蘑菇或食人花杀死
4、顶方块或杀敌敌人时有积分记录
二、游戏运行界面
第一关
第二关
第三关
三、代码中各种类的作用
MyTrame类:渲染页面,把其他类总和起来,记录关卡,绘制马里奥、敌人、地面等,控制马里奥的走动,记录马里奥的死亡,控制整个游戏的运行。
StaticValue类:把页面需要的图片,划分整理
BackGround类:绘制每一关的场景,其中包括水管、敌人、格子
Mariol类:记录马里奥的状态,实现马里奥的左右移动和上下跳跃,速度大小等
Enemy类:创建敌人,记录敌人初始化的坐标
Obastacle类:控制最后一关的旗子随马里奥移动
四、原码
MyFrame类:
package com.sxt; import javax.swing.*; import java.awt.*; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.ArrayList; import java.util.List; public class MyFrame extends JFrame implements KeyListener,Runnable{ //用于存储所有背景 private List<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 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(); //初始化马里奥 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); //绘制图像 repaint(); thread.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); //绘制马里奥 graphics.drawImage(mario.getShow(),mario.getX(),mario.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(); } } //当键盘松开始调用 @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(); } } } }
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 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)); } } //绘制砖块A for (int i = 120; i <=150 ; i+=30) { obstacleList.add(new Obstacle(i,300,7,this)); } //绘制砖块B-F 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)); } } //绘制砖块G for (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,1,true,this)); //绘制第一关的食人花敌人 enemyList.add(new Enemy(635,420,2,true,this,328,428)); } //判断是否是第二关 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)); } } //砖块C obstacleList.add(new Obstacle(300,330,0,this)); //绘制砖块B,E,G 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)); } } //绘制住砖块A、D、F、H、I 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)); } } //妨碍1砖块 obstacleList.add(new Obstacle(240,300,0,this)); //空1~4砖块 for (int i = 360; i <=540 ; i+=60) { obstacleList.add(new Obstacle(i,270,7,this)); } //绘制第二关的第一个食人花敌人 enemyList.add(new Enemy(75,420,2,true,this,328,418)); //绘制第二关的第二个食人花敌人 enemyList.add(new Enemy(635,420,2,true,this,298,388)); //绘制第二关的第一个蘑菇敌人 enemyList.add(new Enemy(200,385,1,true,this)); //绘制第二关的第二个蘑菇敌人 enemyList.add(new Enemy(500,385,1,true,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)); } } //绘制第三个背景的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,1,true,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; } }
Obstacle类:
package com.sxt; 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; } @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(); } } } }
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.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 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 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")); } 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(); } //加载蘑菇敌人 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(); } } } }
Mario类:
package com.sxt; import java.awt.image.BufferedImage; 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 int score = 0; public Mario(){ } public Mario(int x, int y) { this.x = x; this.y = y; show = StaticValue.stand_R; this.status = "stand--right"; 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"; } } //马里奥向右移动 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 void jump(){ if(status.indexOf("jump") == -1){ 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; } @Override public 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); score++; } 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(); } } //判断马里奥跳跃的操作 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; } }
Enemy类:
package com.sxt; import java.awt.image.BufferedImage; public class Enemy implements Runnable{ //存储当前坐标 private int x,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, int type, boolean face_to, BackGround bg) { this.x = x; this.y = y; this.type = type; this.face_to = face_to; this.bg = bg; show = StaticValue.mogu.get(0); thread.start(); } //食人花敌人的构造函数 public Enemy(int x, int y, int type, boolean face_to, BackGround bg, int max_up, int max_down) { this.x = x; this.y = y; this.type = type; this.face_to = face_to; this.bg = bg; this.max_up = max_up; this.max_down = max_down; show = StaticValue.flower.get(0); thread.start(); } //死亡方法 public void death(){ show = StaticValue.mogu.get(2); this.bg.getEnemyList().remove(this); } @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(50); } catch (InterruptedException e) { e.printStackTrace(); } } } public int getX() { return x; } public int getY() { return y; } public BufferedImage getShow() { return show; } public int getType() { return type; } }