第一個Java遊戲程式 - 推箱子
本人是寫面向過程程式長大的,對遊戲程式一竅不通。最初的想法是做一個超級瑪麗來自己玩玩,結果完全沒思路,遂找CC老師指引人生方向。最終的結論是,推箱子最簡單,寫出了推箱子在試試寫貪食蛇,完成了這兩個再考慮超級瑪麗。 >_< |
這個推箱子程式功能比較簡單,只有一個關卡,還是很弱智的。可以通過檔案讀取的方式來擴充關卡,暫時就不寫了。
下面說說第一次做一個遊戲程式的思路:
首先,做介面真是一件煩人的事情。若只是考慮核心代碼,二三十行就能搞定。做了圖形介面,加上幾個功能按鈕,這麼弱智一個程式就快到三百行了……
我把介面分為三塊,上、中、下。頂部一個Reset按鈕,中部是遊戲面板,底部留著暫時沒用。中部是一個獨立的類,取名GamePanel,繼承了JPanel類並實現了KeyListener介面。遊戲的準系統和鍵盤事件監聽都由GamePanel類獨立完成。
遊戲用兩個二維數組表示地圖,一個map[][]數組記錄基本地形,空格、牆壁或是目標地點;另一個數組box[][]記錄箱子所在位置。遊戲者的座標單獨用一個Point變數player來記錄。
製作過程中遇到一個鬱悶的問題,就是中部的GamePanel總是捕捉不到到鍵盤事件。折騰了好久,發現當我把頂部按鈕注釋掉以後就能正常捕獲,一旦把按鈕顯示出來就不行了。後來得知問題在於中部的組件沒有獲得輸入焦點。加上this.requestFocusInWindow();強制使中部組件獲得輸入焦點以後,問題解決。
以下是遊戲中用到的圖片:
Box_box1.gif Box_box2.gif Box_floor.gif Box_wall.gif Box_trg.gif Box_player.gif
(注意更改程式中圖片的路徑)
下面給出原始碼:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.imageio.*;
//Class GamePanel
//The main class of the game
class GamePanel extends JPanel implements KeyListener
{
private static final long serialVersionUID = 1L;
private char[][] map; //Map state
private char[][] box;
private int sX,sY; //Map size
private Point player,p; //Point of player
private int Num=0; //Number of boxs
private Point[] boxs; //Points of boxs;
//private Point[] trg; //Points of target
//private Point[] path;
//private int steps;
private Point[] dir;
private int size;
private Image imgfloor;
private Image imgwall;
private Image imgplayer;
private Image imgbox1;
private Image imgbox2;
private Image imgtrg;
//method
public GamePanel(char b[][],int w,int h,int size)throws Exception
{
sX = b.length;
sY = b[0].length;
Num = 0;
map = new char[sX][sY];
box = new char[sX][sY];
player= new Point();
p = new Point();
boxs= new Point[sX*sY];
dir = new Point[4];
dir[0]= new Point(0,-1); //left
dir[1]= new Point(-1,0); //up
dir[2]= new Point(0, 1); //right
dir[3]= new Point(1, 0); //down
File parent = new File("E:/My Document/我的大學/JAVA/工作區/Program/src/images/");
imgfloor = ImageIO.read(new File(parent,"Box_floor.gif"));
imgwall = ImageIO.read(new File(parent,"Box_wall.gif"));
imgplayer= ImageIO.read(new File(parent,"Box_player.gif"));
imgtrg = ImageIO.read(new File(parent,"Box_trg.gif"));
imgbox1 = ImageIO.read(new File(parent,"Box_box1.gif"));
imgbox2 = ImageIO.read(new File(parent,"Box_box2.gif"));
this.size=size;
//set Map
for(int i=0;i<sX;i++){
for(int j=0;j<sY;j++){
switch(b[i][j]){
case 'P': //Player
map[i][j]='.';
p.setLocation(i, j);
break;
case 'X': //Target
map[i][j]='X';
break;
case 'B': //Box
map[i][j]='.';
boxs[Num++]=new Point(i, j);
break;
case '#': //Wall
map[i][j]='#';
break;
case '.': //floor
map[i][j]='.';
break;
default :
map[i][j]='.';
break;
}
}
}//end set Map
reset(); //reset Game
}
public void reset(){
player.setLocation(p);
for(int i=0;i<sX;i++)
for(int j=0;j<sY;j++)
box[i][j]=' ';
for(int i=0;i<Num;i++)
box[boxs[i].x][boxs[i].y]='B';
//steps=0;
repaint();
}
public void undo(){
}
public boolean judge(){
for(int i=0;i<sX;i++)
for(int j=0;j<sY;j++)
if(box[i][j]=='B'&&map[i][j]!='X')
return false;
return true;
}
public void paint(Graphics g){
//draw map
for(int i=0;i<sX;i++){
for(int j=0;j<sY;j++){
switch(map[i][j]){
case '.':
g.drawImage(imgfloor, j*size, i*size, null);
break;
case '#':
g.drawImage(imgwall, j*size, i*size, null);
break;
case 'X':
g.drawImage(imgfloor, j*size, i*size, null);
break;
}
if(box[i][j]=='B'&&map[i][j]=='X') g.drawImage(imgbox2, j*size, i*size, null);
else if(box[i][j]=='B') g.drawImage(imgbox1, j*size, i*size, null);
else if(map[i][j]=='X') g.drawImage(imgtrg, j*size, i*size, null);
}
}
//draw player
g.drawImage(imgplayer, player.y*size, player.x*size, null);
g.drawRect(0, 0, sY*size, sX*size);
//get Focus
this.requestFocusInWindow(); /* ********* */
}
/* methods of KeyListener */
public void keyPressed(KeyEvent e){
int x=player.x;
int y=player.y;
int k,x0,y0;
k=e.getKeyCode()-37;
if(k<0||k>=4) return;
x+=dir[k].x;
y+=dir[k].y;
if(x<0||x>=sX||y<0||y>=sY) return;
if(map[x][y]=='#') return;
if(box[x][y]!='B'&&(map[x][y]=='.'||map[x][y]=='X')){
player.x=x;
player.y=y;
}
if(box[x][y]=='B'){
x0=x+dir[k].x;
y0=y+dir[k].y;
if(x0<0||y0<0||x0>=sX||y0>=sY) return;
if(map[x0][y0]!='#'&&box[x0][y0]!='B'){
box[x][y]=' ';
box[x0][y0]='B';
player.x=x;
player.y=y;
}
}
repaint();
if(judge()){
JOptionPane.showMessageDialog(null,"You win!");
reset();
}
}
public void keyReleased(KeyEvent e){}
public void keyTyped(KeyEvent e){}
}
//Class PushBox
//Creat JFrame to Play the game
public class PushBox extends JFrame
{
private static final long serialVersionUID = 1L;
private JPanel top;
private GamePanel center;
private JPanel bottom;
private JButton breset; //Button of reset
private JButton bundo; //Button of undo
private int width,height,size;
private int sX,sY;
//method of constitution
public PushBox(char[][] b)throws Exception{
super("PushBox Game");
//set Window
size=35;
sX=b.length;
sY=b[0].length;
width=size*sY+10;
height=size*sX+100;
top = new JPanel();
center = new GamePanel(b,width,height-100,size); //Panel of Game
bottom = new JPanel();
breset = new JButton("Reset");
bundo = new JButton("←Undo");
//add Listener
center.addKeyListener(center);
breset.addActionListener(new ResetListener());
bundo.addActionListener(new UndoListener());
top.add(breset);
//top.add(bundo);
add(top,"North");
add(center,"Center");
add(bottom,"South");
setSize(width,height);
setVisible(true);
}
//classes of Listener
class ResetListener implements ActionListener{
public void actionPerformed(ActionEvent e){
center.reset();
}
}
class UndoListener implements ActionListener{
public void actionPerformed(ActionEvent e){
center.undo();
}
}
//method main
public static void main(String[] args)throws Exception{
char[][] b=new char[5][6];
b[0]="##X##P".toCharArray();
b[1]="##.##.".toCharArray();
b[2]=".B.#..".toCharArray();
b[3]="X..B..".toCharArray();
b[4]="##....".toCharArray();
PushBox p=new PushBox(b);
p.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}