Java小程序之山寨版超級瑪麗

夜聽春雨 7年前發布 | 21K 次閱讀 Java Java開發

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

 

 本文由用戶 夜聽春雨 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!