五一放假呆着没事儿,于是乎打算把battlecity用java写出来,实践一下<java游戏编程 >。
看了看第一章线程,第二章二维图形与动画和第三章交互性与用户界面,差不多了,开始做了!!!
一.大体程序框架先弄出来
主程序-啥也没写-只是初始化游戏类-估计以后会吧什么配置文件相关的东西写进来。
/** */
/**
* @author WS
* @created at 2006-5-3 20:49:39
*/


public
class
Tank
...
{


/** *//**
* @param args
*/

public static void main(String[] args) throws Exception ...{
// System.out.println("Fuck!");
Tank tank = new Tank();
tank.start();
}


public void start() throws Exception ...{
MainFrame mf = new MainFrame("Tank");
mf.Start();
}
}
主函数生成主程序类,由主类生成游戏类,够罗嗦的~~
二.凭想像写出主游戏类,以后随着做还会改,没有设计模式,全在一个类里写了。这是一个主线程。
import
java.awt.Color;
import
java.awt.Graphics;
import
java.awt.Graphics2D;
import
java.awt.Image;
import
java.awt.Toolkit;
import
java.awt.event.KeyEvent;
import
java.awt.event.KeyListener;
import
java.awt.event.WindowAdapter;
import
java.awt.event.WindowEvent;
import
java.awt.image.BufferStrategy;

import
javax.swing.ImageIcon;
import
javax.swing.JFrame;


/** */
/**
* @author WS
* @created at 2006-5-3 21:03:20
*/


public
class
MainFrame
extends
JFrame
implements
KeyListener
...
{


/**//* 开始从下网上番的大图片 */
private Image startImage;


/**//* 游戏坦克状态1 */
private Image tankImage1;


/**//* 游戏坦克状态2 */
private Image tankImage2;


/**//* 0-1 */
public int tankStatus = 0;


/**//* 无敌状态的白框1 */
private Image super1;


/**//* 无敌状态的白框2 */
private Image super2;

private Image tankBoom1;

private Image tankBoom2;

private Image tankBoom3;

private Image tankBoom4;

private Image tankBoom5;

private Image tankBoom6;

private Image enemyOne1;

private Image enemyOne2;

private Image enemyBig1;

private Image enemyBig2;

private Image wall;

private int tankX = 140;

private int tankY = 265;


/**//* Frames状态机 0-开始 1-选择 2-开始显示level 3-游戏中 4-Win 5-地图编辑 …… */
public int frames = 0;

public boolean gameRunning = true;

public int startY = 466;


/**//* 0-单人游戏 1-双人游戏 3-地图编辑 */
public int selectGame = 0;


public MainFrame(String title) ...{
super(title);
loadImages();
initFrame();
Thread engine = new OperatorThread(this);
engine.start();

this.addWindowListener(new WindowAdapter() ...{

public void windowClosing(WindowEvent e) ...{
System.exit(0);
}
});
}


private void initFrame() ...{
setSize(512, 466);
setVisible(true);
setBackground(Color.BLACK);
createBufferStrategy(2);
addKeyListener(this);
}


private void loadImages() ...{
startImage = loadImage("D:/eclipse/workspace/Tank/bin/start.gif");
tankImage1 = loadImage("D:/eclipse/workspace/Tank/bin/tank1.gif");
tankImage2 = loadImage("D:/eclipse/workspace/Tank/bin/tank2.gif");

if (startImage == null) ...{
System.out.println("Null");
}
}


private Image loadImage(String fileName) ...{
return new ImageIcon(fileName).getImage();
}


public Graphics2D getDoubleGraphics() ...{
BufferStrategy strategy = this.getBufferStrategy();
return (Graphics2D) strategy.getDrawGraphics();
}


private void update() ...{
BufferStrategy strategy = this.getBufferStrategy();

if (!strategy.contentsLost()) ...{
strategy.show();
}
Toolkit.getDefaultToolkit().sync();
}

private long fresh = 100;

// 0-开始 1-选择 2-开始显示level 3-游戏中 4-Win 5-地图编辑

public void Start() throws Exception ...{
long startTime;
long tmp = 0;

while (gameRunning) ...{
startTime = System.currentTimeMillis();
repaint();
tmp = System.currentTimeMillis() - startTime;
// System.out.println(fresh - tmp);
Thread.sleep((fresh - tmp) < 0 ? 0 : tmp);
}
}

private void drawTank(Graphics2D g2, int direct, int posX, int posY,

int tankStatus) throws Exception ...{
g2.drawImage(startImage, 0, startY, null);

if (tankStatus == 0) ...{
g2.drawImage(tankImage1, posX, posY, null);

} else ...{
g2.drawImage(tankImage2, posX, posY, null);
}
}


public void paint(Graphics g) ...{

try ...{

switch (frames) ...{
case 0:
getDoubleGraphics().drawImage(startImage, 0, startY, null);
update();
break;
case 1:
drawTank(getDoubleGraphics(), 4, tankX, tankY, tankStatus);
update();
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
}

} catch (Exception e) ...{

}
}


public void keyPressed(KeyEvent e) ...{
System.out.println("Key:" + e.getKeyCode());

switch (e.getKeyCode()) ...{
case KeyEvent.VK_G:
selectGame++;

if (selectGame == 3) ...{
selectGame = 0;
}

if (selectGame == 0) ...{
tankY = 265;

} else if (selectGame == 1) ...{
tankY = 297;

} else if (selectGame == 2) ...{
tankY = 330;
}

}
}


public void keyReleased(KeyEvent e) ...{

}


public void keyTyped(KeyEvent e) ...{

}
}
类继承JFrame类,实现KeyListener接口。直接在Frame的Content里画。
createBufferStrategy(2);为Frame建立双缓冲及分页控制类BufferStrategy,一共2个缓冲页面。
三个图片分别是程序启动,从下网上移动的游戏名称等,直接从FC模拟器上截图下来,存为Gif,由于省事,就不一块一块分割了,分割的话最后运行jar文件会小很多。(图1)

(图1)
坦克共分2种外形,链条不一样,两种交替产生运动的感觉。(图2)
(图2)
三.数据处理类,数据和数据的处理分开写了,不知道合不合理,写写再说。这是一个单独的线程,不断判断状态,进行数据调整和AI的计算,另主游戏类的线程主要用来不断刷新屏幕,根据处理类处理的数据来画屏幕。
public
class
OperatorThread
extends
Thread
...
{
private MainFrame mainFrame = null;


public OperatorThread(MainFrame mainFrame) ...{
this.mainFrame = mainFrame;
}


public void run() ...{

try ...{

while (mainFrame.gameRunning) ...{

switch (mainFrame.frames) ...{
case 0:

for (int i = 0; i < 446; i++) ...{
mainFrame.startY--;
Thread.sleep(10);
}
mainFrame.frames = 1;
break;
case 1:

while (mainFrame.frames == 1) ...{

if (mainFrame.tankStatus == 0) ...{
mainFrame.tankStatus = 1;

} else ...{
mainFrame.tankStatus = 0;
}
Thread.sleep(100);
}
break;
}
}

} catch (Exception e) ...{
System.out.println(e.getMessage());
}
}
}
这样添加了键盘事件后,游戏初始画面从下网上移动,然后现实坦克,按G健进行选择,选择单人游戏,多人游戏,地图编辑。
就这些了,写写再说…………
需要完成的难点:键盘控制配置,敌人坦克AI,地图文件txt,战场初始化及处理,音频(不知道去哪录那些音效,FC中还不能导出,郁闷)。