【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)检测子弹如果超出了窗体边界,则需要把当前子弹从容器当中移除掉

四、程序源代码:

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


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);
			}
		}
	}

}

角色类:

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();
	
	}
}
监听类:

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;
			
		}
	}

}
背景类:

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();

}

子弹类:

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;
	}

}

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

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

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;
	}
}


金币类:

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);
	}

}

砖头类:

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);
	}
}

水管类:

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);
	}
}


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


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


好吧,就先写到这吧!共勉之 微笑


  • 246
    点赞
  • 740
    收藏
    觉得还不错? 一键收藏
  • 515
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 515
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值