---------------------- android培训、java培训、期待与您交流! ----------------------
在8月份的时候,在看完java初级视频后,我写了一个俄罗斯方块小游戏。写程序的过程相当痛苦,遇到问题只能靠自己解决。遇到的最大问题是让界面的刷新频率与方块的下降速度分离。我在开始写这个程序的时候,是通过方块的下降速度来控制界面的刷新频率,在一个线程中实现(原谅我的无知吧,毕竟是第一次写程序)。结果出现了让人非常生气的效果,完全不能用。这个版本的程序我现在还留着。能让人发疯。后来通过将图形的显示与方块的键盘监听放入不同的线程中才得到解决。改动后的版本与改动前的代码相差并不多(区别绝对不超过30行),效果却完全不同。
这份游戏代码结构是,一个Els类,一个Fangkuai类(小方块),一个抽象方块类,以及抽象方块类的各个子类(大方块由4个小方块构成)。子类大方块由4个小方块组合而成,子类实现了抽象类中移动等方法。
运行类Els中代码过多,有些代码能抽出组成新类。产生子类实例的代码程序应该应用工厂模式增加一个类,FangkuaiFactory.类用来产生方块。
这份代码中,面向对象的思想体现的较少,各个方法之间相互先后联系,但又不知道具体该如何改。
游戏由数组完成,没有使用集合类。按Thinking in java里说法,集合类的底层也是由数组实现,数组比集合类的效率高,但推荐使用集合类。集合类灵活且可读性高。但印象中有种说法java在效率上比c和c++要低些,游戏程序能用数组来代替集合类吗?
在看视频的时候看到有人说JAVA不适合做游戏,因为JAVA的垃圾回收机制可能会暂停游戏的运行,就像Eclipse运行在进行垃圾回收时暂停了编程。可是垃圾回收是可控的啊,如果在代码中经常进行垃圾回收,不让垃圾积累,每次垃圾回收的时间都变短,短到让人无法察觉不就可以解决这个问题吗?《深入理解JAVA虚拟机》中说,垃圾回收的次数和时间是可以通过对虚拟机的优化来减少的。为什么没见过用java开发出较大的游戏呢?
在实践中应用多线程,让人头疼。而且这还是在没有使用线程同步的情况下。我改好程序后,一直有一个疑问。为什么我使用了两个没有同步的线程却一直没发现运行错误?多个线程对同一份数组进行使用不是很容易出现意想不到的效果吗?一直到9月份后,才突然想起来,这两个线程对同一个数组的读写是分离的。显示线程不停的读取,另一个线程不停的改变方块在数组中的位置。换句话说一个读,一个写,二者是分离的,功能分开后,最可能出现的问题是显示的图形与数组中的方块位置有毫秒级别上的延迟,这个延迟是被视觉可以接受的。
其实这个延迟是很有意思的。这个延迟恰恰是和小时候游戏机上的延迟是吻合的。一般来说,当方块的下方已经存在方块时,这个方块就不能动了。可事实上当这个方块下方存在方块时,它还有几到几十毫秒的可移动时间,原因是判定它不能移动的方法和显示它的方法位于不同的线程中。如果在这段时间里控制它移动,它就出现短暂的水平移动。这个可移动的时间是存在且不确定的。存在但不能确定,应该算是多线程中最麻烦的特色。
在写这个程序时,还在“消行”(即消除满行的方块)上出现了较大的阻力。产生阻力的原因是:我无法确定是用循环还是递归来实现这个方法(因为我至今不知道循环和递归到底有什么明确的区别)。最初我选择了递归,因为平时很少写递归,就想借这个机会练习递归。最后没写出来完善的代码,总是有问题。是个非常小的问题。一般人不仔细的玩是发现不了的。在某种条件下,本该消去的一行却留下了最左边的一个方块。最后我放弃了递归,使用了循环。循环也是很麻烦的,单靠脑子去记有时候根本记不住该在什么条件下跳转到哪里。最终,我使用了传说中的必杀技,拿笔画出了流程图,很顺利就写出了方法(哈哈,以后我遇到难写的方法就画流程图)。画流程图真的非常方便,推荐给那些和我一样记忆力不强的菜鸟朋友。
附上运行代码以及消行的方法代码。(当初的重点是运行处游戏,没有在命名上下功夫,多多包涵)
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Random;
class Els extends Frame implements Runnable {
Fangkuai[][] fksz = new Fangkuai[10][20];//方块数组容纳方块
AbFangkuai fk = new Fangkuai2(fksz);//产生一个方块,面向抽象类AbFangkuai编程
private static Random r = new Random();
Image offScreenImage = null;
public void paint(Graphics g) {
for (int i = 0; i < 10; i++) {
for (int j = 19; j > 1; j--) {
if (fksz[i][j] != null) {
fksz[i][j].draw(g);//读出方块容器内的方块,并画出方块
}
}
}
if(fk.move){
fk.draw(g);
}
}
public void update(Graphics g) {//对图形进行双缓冲,改善显示效果(如果用swing包,则可免除这段代码,swing好像默认双缓冲)
if(offScreenImage == null) {
offScreenImage = this.createImage(300, 600);
}
Graphics gOffScreen = offScreenImage.getGraphics();
Color c = gOffScreen.getColor();
gOffScreen.setColor(Color.white);
gOffScreen.fillRect(0, 0, 300,600);
gOffScreen.setColor(c);
paint(gOffScreen);
g.drawImage(offScreenImage, 0, 0, null);
}
public void deleteline(int y) {//消行。4层循环嵌套,不用笔画图实在是写不出。
while(y>=0){
while((fksz[0][y]!= null)&& (fksz[1][y]!= null)&& (fksz[2][y]!= null)&& (fksz[3][y]!= null)&& (fksz[4][y]!= null)&& (fksz[5][y]!= null)&& (fksz[6][y]!= null)&& (fksz[7][y]!= null)&& (fksz[8][y]!= null)&& (fksz[9][y]!= null)){
for(int j = y;j>0;j--){
for(int i=0;i<10;i++){
fksz[i][j] = fksz[i][j-1];
if(fksz[i][j-1] != null){
fksz[i][j].setY(j) ;
}
}
}
}
y--;
}
}
public void launchFrame() {
this.setBounds(600, 100, 300, 600);
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent arg0) {
System.exit(0);
}
});
this.addKeyListener(new MyAdapter());
this.setVisible(true);
}
class MyAdapter extends KeyAdapter {
public void keyReleased(KeyEvent arg0) {
fk.keyReleased(arg0);
}
public void keyPressed(KeyEvent arg0) {
fk.keyPressed(arg0);
}
}
public static void main(String[] args) {
Els e = new Els();
Thread t1 = new Thread(e);
e.launchFrame();
t1.start();//此线程用来显示方块数组内的方块
while(true){//主线程用来操作方块数组内的方块。
e.fk.move();
if(!e.fk.move){
e.deleteline(e.fk.getMaxY());
int rn = r.nextInt(4);
if (rn == 0){//这段代码应该可以提取成一个工厂模式的类来产生方块。
e.fk = new Fangkuai1(e.fksz);
}
if(rn == 1){
e.fk = new Fangkuai2(e.fksz);
}
if(rn == 2){
e.fk = new Fangkuai3(e.fksz);
}
if(rn == 3){
e.fk = new Fangkuai4(e.fksz);
}
}
try {
Thread.sleep(500);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
public void run() {
while (true) {
repaint();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
---------------------- android培训、java培训、期待与您交流! ----------------------详细请查看:http://edu.csdn.net/heima