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

论坛 期权论坛 脚本     
匿名网站用户   2020-12-20 17:45   18   0

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,比如,可以一直跳,在跳的同时还能再跳,还有就是下降可能不会落地面的同一高度,可能会高于地面的高度,可能会低于地面的高度;障碍物的添加,我们是通过配置地图的方式,配置地图有时通过读入文件的操作,大致的原理是将后面的那张的背景图片划分成很多的小格子,将这些小格子用代码转换为二位数组,二维数组可以存入不同数字,代表不同的障碍物,通过操作文件就可以轻易配置地图;首先通过文件输入流将文件数据读入,将字符转换为数字,并存储在二维数组中,后面,通过遍历二维数组的不同位置的值,在相应的格子填充相应的障碍物;地图就这样配置好了;
觉得配地图很炫酷,因为以前有位学长在演示的时候,觉得好难,现在自己掌握了觉得也不是很难了,当然了,这里离不开老师的指导;碰撞检测类似之前小球的碰撞检测,这里主要需要处理碰撞之后的一些逻辑,如何避免一直碰撞(人物贴着障碍物不能动)等;发射子弹的话,也比较简单了,利用容器装子弹,不过需要注意的是要及时将容器中子弹移除,不然内存会很快被吃完的;
写程序,一定要按照思路一步一步来,一点一点来实现!不要太过急于求成!以前老想着自己做一个小东西出来,可是自己总是很急,希望很快做出来,结果,思路没有理清,写起来一团糟!前车之鉴!
好了,就先写到这吧!共勉!


最近很多人问我要源代码:于是乎就把源代码上传到了CSDN的资源中,下面是下载链接;不需要积分;
希望可以帮到想要学习这个游戏的朋友;

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1136255
帖子:227251
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP