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 eneryList = new ArrayList();
//子弹容器
public ArrayList boomList = new ArrayList();
//子弹的速度
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 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 =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 list = new ArrayList();
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的资源中,下面是下载链接;不需要积分;
希望可以帮到想要学习这个游戏的朋友;
山寨版超级玛丽源代码下载