Java小程序之山寨版超级玛丽

夜听春雨 2年前

Java小程序之山寨版超级玛丽

一、游戏基本功能

1、能够向左向右行走(键盘监听)

2、能够跳跃

3、能够发射子弹

4、能够检测和障碍物之间的碰撞

5、背景图片的移动

二、游戏运行界面

三、游戏大致实现思路:

1.窗体

2.自己角色的添加

3.背景图片的添加

4.背景图片的移动

5.人物的移动和跳跃

6.砖头、水管等等障碍物的添加

7.任务和障碍物的碰撞

难点分析:

1.人物的多键控制

1)给人物设定方向boolean变量:向左、向右、向上、向下

2)通过键盘监听来修改方向的变量值

按下某个键的时候,我们把相应方向改为true,释放的时候改false

2.地图配置

自定义文件读取方式实现:文件流的使用和字符串String类的方法调用

3.碰撞检测

封装一个Rectangle类的对象

4.子弹添加

1)先定义一个容器,这个用于封装所有的子弹对象

2)按下某个键的时候,创建一个子弹对象(以角色的坐标为基准初始化)

3)把子弹对象添加到容器当中

4)在paint方法中,遍历容器,取出子弹对象并进行绘制

5)检测子弹如果超出了窗体边界,则需要把当前子弹从容器当中移除掉

四、程序源代码:

代码结构图:分了三个包、敌人类包、游戏界面类包、游戏地图配置包

com.huaxin.mario包:

  package com.huaxin.mario;    import java.awt.Color;  import java.awt.Graphics;  import java.awt.image.BufferedImage;  import java.util.ArrayList;    import javax.swing.ImageIcon;  import javax.swing.JFrame;    import com.huaxin.enery.Enery;  import com.huaxin.enery.Pipe;    import Util.Map;    public class GameFrame extends JFrame{      public Mario mario;   public Enery pipe,cion,brick;   //背景图片   public BackgroundImage bg ;      //容器装敌人   public ArrayList<Enery> eneryList = new ArrayList<Enery>();      //子弹容器   public ArrayList<Boom> boomList = new ArrayList<Boom>();      //子弹的速度   public int bspeed=0;      //画地图,制定规则,是1画砖头,是2画金币,是3画水管   public int [][] map =null;      //构造函数里面初始化背景图片和马里奥对象   public GameFrame() throws Exception {        mario = new Mario(this);    mario.start();    Map mp= new Map();    bg = new BackgroundImage();        //窗体重绘线程    new Thread(){     public void run(){      while(true){       //重绘窗体       repaint();       //检查子弹是否出界       checkBoom();       try {        Thread.sleep(10);       } catch (InterruptedException e) {        e.printStackTrace();       }      }     }    }.start();        map=mp.readMap();    //读取地图,并配置地图    for (int i = 0; i < map.length; i++) {     for (int j = 0; j < map[0].length; j++) {      //读取到的是1,画砖头      if(map[i][j]==1){       brick = new Pipe(j*30,i*30,30,30,new ImageIcon("image/brick.png").getImage());       eneryList.add(brick);      }      //读到2画金币      if(map[i][j]==2){       cion = new Pipe(j*30,i*30,30,30,new ImageIcon("image/coin_brick.png").getImage());       eneryList.add(cion);      }      //读到3画水管      if(map[i][j]==3){       pipe = new Pipe(j*30,i*30,60,120,new ImageIcon("image/pipe.png").getImage());       eneryList.add(pipe);      }           }    }    //设置背景音乐    com.huaxin.music.Util.startMusic("music/bg1.wav");   }      public void initFrame(){    //设置窗体相关属性    this.setSize(800,450);    this.setTitle("山寨版超级玛丽");    this.setResizable(false);    this.setLocationRelativeTo(null);    this.setDefaultCloseOperation(3);    this.setVisible(true);        //该窗体添加键盘监听    KeyListener kl = new KeyListener(this);    this.addKeyListener(kl);   }      public void paint(Graphics g) {    //利用双缓冲画背景图片和马里奥    BufferedImage bi =(BufferedImage)this.createImage(this.getSize().width,this.getSize().height);    Graphics big =bi.getGraphics();    big.drawImage(bg.img, bg.x, bg.y, null);        for (int i = 0; i <eneryList.size(); i++) {     Enery e =eneryList.get(i);     big.drawImage(e.img, e.x, e.y, e.width, e.height,null);    }        //画子弹    for (int i = 0; i < boomList.size(); i++) {     Boom b =boomList.get(i);     Color c =big.getColor();     big.setColor(Color.red);     big.fillOval(b.x+=b.speed, b.y, b.width, b.width);     big.setColor(c);    }        //画人物    big.drawImage(mario.img, mario.x, mario.y, mario.width, mario.height,null);    g.drawImage(bi,0,0,null);       }      //检查子弹是否出界,出界则从容器中移除,不移除的话,内存会泄漏   public void checkBoom(){    for (int i = 0; i < boomList.size(); i++) {     Boom b = boomList.get(i);     if(b.x<0 || b.x>800){      boomList.remove(i);     }    }   }    }
  package com.huaxin.mario;    import java.awt.Image;  import java.awt.Rectangle;    import javax.swing.ImageIcon;    import com.huaxin.enery.Enery;    //自己的角色类  public class Mario extends Thread{      public GameFrame gf;      public boolean jumpFlag=true;      //马里奥的坐标   public int x=0,y=358;   //马里奥的速度   public int xspeed=5,yspeed=1;   //马里奥的宽高   public int width=30,height=32;   //马里奥的图片   public Image img = new ImageIcon("image/mari1.png").getImage();      public boolean left=false,right=false,down=false,up=false;      public String Dir_Up="Up",Dir_Left="Left",Dir_Right="Right",Dir_Down="Down";         public Mario (GameFrame gf) {    this.gf=gf;    this.Gravity();   }      public void run(){    while(true){     //向左走     if(left){      //碰撞到了      if(hit(Dir_Left)){       this.xspeed=0;      }            if(this.x>=0){       this.x-=this.xspeed;       this.img=new ImageIcon("image/mari_left.gif").getImage();      }            this.xspeed=5;     }          //向右走     if(right){            if(hit(Dir_Right)){       this.xspeed=0;      }      //任人物向右移动      if(this.x<400){       this.x+=this.xspeed;       this.img=new ImageIcon("image/mari_right.gif").getImage();      }            if(this.x>=400){       //背景向左移动       gf.bg.x-=this.xspeed;       //障碍物项左移动       for (int i = 0; i <gf.eneryList.size(); i++) {        Enery enery = gf.eneryList.get(i);        enery.x-=this.xspeed;       }       this.img=new ImageIcon("image/mari_right.gif").getImage();      }      this.xspeed=5;     }          //向上跳     if(up){        if(jumpFlag && !isGravity){       jumpFlag=false;       new Thread(){        public void run(){         jump();         jumpFlag=true;        }       }.start();      }     }          try {      this.sleep(20);     } catch (InterruptedException e) {      e.printStackTrace();     }    }   }         //向上跳的函数   public void jump(){    int jumpHeigh=0;    for (int i = 0; i < 150; i++) {     gf.mario.y-=this.yspeed;     jumpHeigh++;     if(hit(Dir_Up)){      break;     }     try {      Thread.sleep(5);     } catch (InterruptedException e) {      e.printStackTrace();     }    }    for (int i = 0; i <jumpHeigh; i++) {     gf.mario.y+=this.yspeed;     if(hit(Dir_Down)){      this.yspeed=0;     }     try {      Thread.sleep(5);     } catch (InterruptedException e) {      e.printStackTrace();     }              }    this.yspeed=1;//还原速度   }      //检测碰撞   public boolean hit(String dir){    Rectangle myrect = new Rectangle(this.x,this.y,this.width,this.height);    Rectangle rect =null;        for (int i = 0; i < gf.eneryList.size(); i++) {     Enery enery = gf.eneryList.get(i);          if(dir.equals("Left")){      rect = new Rectangle(enery.x+2,enery.y,enery.width,enery.height);     }     else if(dir.equals("Right")){      rect = new Rectangle(enery.x-2,enery.y,enery.width,enery.height);     }          else if(dir.equals("Up")){      rect = new Rectangle(enery.x,enery.y+1,enery.width,enery.height);     }else if(dir.equals("Down")){      rect = new Rectangle(enery.x,enery.y-2,enery.width,enery.height);     }     //碰撞检测     if(myrect.intersects(rect)){      return true;     }    }        return false;   }      //检查是否贴地   public boolean isGravity=false;      public void Gravity(){     new Thread(){      public void run(){              while(true){        try {         sleep(10);        } catch (InterruptedException e) {         e.printStackTrace();        }                if(!jumpFlag){                 }                while(true){         if(!jumpFlag){          break;         }                  if(hit(Dir_Down)){          break;         }                  if(y>=358){          isGravity=false;         }         else{          isGravity=true;          y+=yspeed;         }                  try {          sleep(10);         } catch (InterruptedException e) {          e.printStackTrace();         }       }      }      }     }.start();      }  }
  package com.huaxin.mario;    import java.awt.event.KeyAdapter;  import java.awt.event.KeyEvent;    import javax.swing.ImageIcon;      //键盘按下监听类  public class KeyListener extends KeyAdapter{      public GameFrame gf;   public boolean jumpFlag=true;      public KeyListener(GameFrame gf) {    this.gf=gf;   }      //键盘监听   public void keyPressed(KeyEvent e) {    int code = e.getKeyCode();    switch(code){    //向右走    case 39:     gf.mario.right=true;     break;     //向左走    case 37:     gf.mario.left=true;    break;        case 66:     addBoom();    break;        //向上跳    case 74:     gf.mario.up=true;     break;   }   }      //添加子弹   public void addBoom() {     Boom b = new Boom(gf.mario.x,gf.mario.y+5,10);    if(gf.mario.left) b.speed=-2;    if(gf.mario.right) b.speed=2;    gf.boomList.add(b);   }     //键盘释放监听   public void keyReleased(KeyEvent e) {        int code=e.getKeyCode();    if(code==39){          gf.mario.right=false;          gf.mario.img=new ImageIcon("image/mari1.png").getImage();    }    if(code==37){     gf.mario.left=false;          gf.mario.img=new ImageIcon("image/mari_left1.png").getImage();    }        if(code==74){     gf.mario.up=false;         }   }    }
  package com.huaxin.mario;    import java.awt.Image;    import javax.swing.ImageIcon;    public class BackgroundImage {      public int x=0,y=0;   public int ox=0,oy=0;   public Image img=new ImageIcon("image/startBack.jpg").getImage();    }
  package com.huaxin.mario;    //子弹类  public class Boom {   //子弹的坐标,大小,速度   int x,y;   int width;   int speed=1;      public Boom(int x, int y, int width) {    super();    this.x = x;    this.y = y;    this.width = width;   }    }

主函数类,作为整个程序的入口

  package com.huaxin.mario;    public class Test {      //主函数,程序入口   public static void main(String[] args) throws Exception {    GameFrame gf = new GameFrame();    gf.initFrame();   }  }

com.huaxin.enery包:

  package com.huaxin.enery;    import java.awt.Image;    //障碍物的抽象父类  public abstract class Enery {      public int x,y;   public int width,height;   public Image img;   public Enery(int x, int y, int width, int height,Image img) {    this.x = x;    this.y = y;    this.width = width;    this.height = height;    this.img=img;   }  }  
  package com.huaxin.enery;    import java.awt.Image;    //金币类  public class Coin extends Enery{     public Coin(int x, int y, int width, int height, Image img) {    super(x, y, width, height, img);   }    }
  package com.huaxin.enery;    import java.awt.Image;    //砖头类  public class Brick extends Enery {     public Brick(int x, int y, int width, int height, Image img) {    super(x, y, width, height, img);   }  }
  package com.huaxin.enery;    import java.awt.Image;    //水管类  public class Pipe extends Enery {     public Pipe(int x, int y, int width, int height, Image img) {    super(x, y, width, height, img);   }  }

com.huaxin.util包:

  package Util;    import java.io.BufferedReader;  import java.io.FileInputStream;  import java.io.FileNotFoundException;  import java.io.InputStreamReader;  import java.util.ArrayList;    //地图配置类  public class Map {      //数据容器   public ArrayList<String> list = new ArrayList<String>();      public int [][] map=null;      public  int[][] readMap() throws Exception {    // 构造文件输入流    FileInputStream fis = new FileInputStream("map.txt");    InputStreamReader isr = new InputStreamReader(fis);    BufferedReader br = new BufferedReader(isr);        //直接读取一行数据    String value =br.readLine();           while(value!=null){        //将读取到的一行数据加入到容器中        list.add(value);        value =br.readLine();       }              br.close();              //得到多少行多少列       int row=list.size();       int cloum=0;          for (int i = 0; i < 1; i++) {     String str=list.get(i);     String [] values=str.split(",");     cloum=values.length;    }                     map = new int [row][cloum];              //将读到的字符创转换成整数,并赋值给二位数组map      for (int i = 0; i < list.size(); i++) {       String str=list.get(i);     String [] values=str.split(",");     for (int j = 0; j < values.length; j++) {      map[i][j]=Integer.parseInt(values[j]);     }   }     return map;     }    }

五、项目总结:

这个小项目看起来挺简单的,其实里面也有很多功能需要仔细去思考的,以前觉得游戏人物在窗体走的时候,觉得一直是人在走,其实,人物走到中间或者走到某一位置的时候,人物已经是在原地踏步了,取而代之的是背后那张长度很长的背景的图片在移动,任务的左右移动相对而言比较简答实现,跳跃功能就不是那么简单了,需要永达线程,跟后面障碍物碰撞检测又有关系,当没有碰到的时候,人物要往下掉,一不注意就会出现各种bug,比如,可以一直跳,在跳的同时还能再跳,还有就是下降可能不会落地面的同一高度,可能会高于地面的高度,可能会低于地面的高度;障碍物的添加,我们是通过配置地图的方式,配置地图有时通过读入文件的操作,大致的原理是将后面的那张的背景图片划分成很多的小格子,将这些小格子用代码转换为二位数组,二维数组可以存入不同数字,代表不同的障碍物,通过操作文件就可以轻易配置地图;首先通过文件输入流将文件数据读入,将字符转换为数字,并存储在二维数组中,后面,通过遍历二维数组的不同位置的值,在相应的格子填充相应的障碍物;地图就这样配置好了;觉得配地图很炫酷,因为以前有位学长在演示的时候,觉得好难,现在自己掌握了觉得也不是很难了,当然了,这里离不开老师的指导;碰撞检测类似之前小球的碰撞检测,这里主要需要处理碰撞之后的一些逻辑,如何避免一直碰撞(人物贴着障碍物不能动)等;发射子弹的话,也比较简单了,利用容器装子弹,不过需要注意的是要及时将容器中子弹移除,不然内存会很快被吃完的;

写程序,一定要按照思路一步一步来,一点一点来实现!不要太过急于求成!以前老想着自己做一个小东西出来,可是自己总是很急,希望很快做出来,结果,思路没有理清,写起来一团糟!前车之鉴!

好了,就先写到这吧!共勉!

 

来自:http://blog.csdn.net/bluesky_usc/article/details/54115406