寒假前写了一个贪吃蛇游戏,想用面向对象的思想来写。用的java语言。。。实际上,我只是知道C++和java的语法和一些基本的类(如文件IO,图形界面,线程等),对真正的面向对象思想和设计模式那些不太懂吧。。。去年也看过点设计模式的书,里面有些文字虽然深深记在心里(比如对象是拥有特定行为的实体,对象最主要的就是为自己的行为负责,这些行为就是对象提供的共有接口;又如封装不仅仅是数据隐藏,应该包括任何形式的隐藏;发现并封装变化点;优先使用对象组合而不是继承等等),但是应用时,在设计程序中的对象时,或者程序结构时,用得不好,其实是没有经验,也不是真正意义上的懂,只是知道字面意思。
说说这个贪吃蛇游戏吧。。
我最初的时候,这样思考的。设计一个蛇对象,一个苹果对象,一个游戏对象。蛇的行为就是产生自己,移动,吃苹果;苹果对象的行为就是产生自己;游戏对象就是绘制界面,画蛇,提供程序运行的时间线程。
但是在编写程序的时候,不知道怎么的,最后就做成了,只有一个body类。然后苹果是一个body类,蛇是一个body类组成的链表。然后body类的行为只有一个,就是移动自己。然后游戏类GreedySnake就包含一个蛇头的对象,和一个苹果的对象,当然还包括一个内部类线程,界面绘制,键盘鼠标事件等等。。。。然后蛇的移动,吃苹果的判断,是否撞到墙的判断,都是GreedySnake类的成员函数。 程序见最后的附录,能够运行和玩耍的。
虽然我知道程序肯定某些地方设计和实现地不太好,但是我也不知道问题究竟出在哪里,怎么才能改好或者怎么改进它,当然也许是这个程序本身太小了不用考虑那么多或者非要用上什么模式或者非要把某些理论在这个程序中去找到应用点,也许是自己想多了,也许是自己没有一个别人的需求,在自己随便想。。。在家的时候,看着儿时小霸王上的那些小游戏(比如马戏团,冒险岛,超级玛丽,坦克大战),有时候也在想,怎么样在我的程序基础上,去做类似的那些游戏。。自己也试过想做一下坦克大战,只做一个坦克发送子弹的过程,没有继续做下去,有时候我在想,小霸王这类游戏,在java上怎么样有一种较好的框架,然后不同的游戏,分别设计不同的类,往这个框架上面填写类。
然后今天,又在看设计模式的一点东西。看了bridge模式,而后在看到观察者模式的时候,和windows的消息循环机制。。然后想到,我写的贪吃蛇是否是这样的一种结构。
就是Game类,设计成为类似于操作系统的一种东西。。。然后蛇,和苹果,都是来注册的,注册之后,这些对象就在界面上,然后蛇移动之类,也都是类给他发消息,也就是调用提供的接口。。。。然后仔细想了一下,似乎真的是这样的。但是以前没做好的地方时,把蛇的很多行为,如 吃苹果,判断是否撞墙或者迟到尾巴等,都写成了Game类的成员方法。。。这个设计感觉是不太好的。。
然后刚才突然想法是,在屏幕上创建两条蛇,然后两条蛇同时动。。。可以两个人玩去抢同一个苹果或者一个人玩(左手wasd键控制一条蛇,右手ijkl键控制一条蛇)。。于是试着在游戏类中增加一条蛇。可是当我开始增加新的一条蛇时,才发现修改程序特别的麻烦。。。原因是刚才说的,我之前把蛇的很多行为,如 吃苹果,判断是否撞墙或者迟到尾巴等,都写成了Game类的成员方法,现在新增加了一条蛇,这些成员方法都需要改,还需要判断是哪条蛇调用的,可能还需要增加一个这些成员方法的参数,参数为蛇类的对象。判断逻辑等很多问题也要改。。。。假如说以前我把这些函数都做成蛇类的成员方法,那问题应该会简单些。(蛇对象应该为自己的行为负责)
这也印证了一句话,我们在设计对象时,并不一定设计的很好。。。
也许我现在看设计模式这些东西还是有点早,应该一步一个脚印,先做好一般程序员的基础事情,不要想太多系统设计师或者架构师的事情。当然了解下还是应该有用。
附:贪吃蛇程序
import java.awt.Graphics;
import java.io.IOException;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class GreedySnake extends JFrame implements KeyListener, ActionListener
{
/**
* @param args
*/
Body SnakeHead;//蛇头
char wasd;
Body Apple;//苹果
Body tail;//暂存蛇尾的变量;用于吃苹果的时候使用
boolean gameover;
public GreedySnake()
{
super("贪吃蛇小游戏;made by ljz");
reset();
delayThread();//蛇移动线程
}
public void reset()
{
tail=new Body(300,300);
generateApple();
SnakeHead=new Body(100,80);
Body t1=new Body(100,60);
Body t2=new Body(100,40);
SnakeHead.pre=null;
SnakeHead.next=t1;
t1.pre=SnakeHead;
t1.next=t2;
t2.pre=t1;
t2.next=null;
wasd='s';
gameover=false;
}
public void restart()
{
reset();
GreedySnake gs=new GreedySnake();
gs.showInterface();
gs.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gs.setSize(1030,730);
gs.setVisible(true);
}
public void Move(char direction)
{
Body t=SnakeHead;
while(t.next!=null)
{
t=t.next;
}
tail.x=t.x;
tail.y=t.y;
//System.out.println(t.y);
while(t.pre!=null)
{
t.x=t.pre.x;
t.y=t.pre.y;
t=t.pre;
}
SnakeHead.Move(direction);
}
public void generateApple()
{
Apple =new Body(200,200);
Apple.x=((int)(Math.random()*47)+2)*20;
Apple.y=((int)(Math.random()*27)+2)*20;
//System.out.println(Apple.x);
}
public boolean isOver()
{
Body t=new Body(100,100);
Body p=new Body(100,100);
t=SnakeHead;
p=SnakeHead.next;
if(t.x<40||t.x>980||t.y<40||t.y>580)//判断是否撞墙
return true;
while(p!=null)//判断是否吃到自己的尾巴
{
if(p.x==t.x&&p.y==t.y)
return true;
p=p.next;
}
return false;
}
public boolean isEated()
{
if(SnakeHead.x==Apple.x && SnakeHead.y==Apple.y)
return true;
else
return false;
}
public void EatApple()
{
Body t=SnakeHead;
while(t.next!=null)
{
t=t.next;
}
Apple.x=tail.x;
Apple.y=tail.x;
t.next=Apple;
Apple.next=null;
Apple.pre=t;
generateApple();
}
public void paint(Graphics g)
{
g.clearRect(0,0,getWidth(),getHeight());
if(gameover==true)
{
g.drawString("游戏结束", 300, 300);
return ;
}
g.setColor(Color.green);
g.drawLine(40, 40, 40, 600);
g.drawLine(40, 40, 1000, 40);
g.drawLine(40,600,1000,600);
g.drawLine(1000, 40, 1000, 600);
g.setColor(Color.red);
Body t;
t=SnakeHead;
while(t!=null)
{
g.drawOval(t.x,t.y,20,20);
t=t.next;
}
g.setColor(Color.blue);
g.drawOval(Apple.x, Apple.y, 20, 20);
}
public void delayThread()
{
new Thread()//蛇移动线程
{
public void run()
{
// TODO Auto-generated method stub
while(true)
{
try
{
Thread.sleep(150);
Move(wasd);
if(isEated()==true)
EatApple();
if(isOver()==true)
{
wasd='r';
gameover=true;
repaint();
break;//线程退出
}
repaint();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
}
public void showInterface()
{
Container c=getContentPane();
JButton b=new JButton("重新玩");
JTextArea t=new JTextArea();
c.add(t,BorderLayout.EAST);
c.add(b,BorderLayout.EAST);
b.addActionListener(this);
t.addKeyListener(this);
t.setLineWrap(true);
// t.setRows(3);
//t.setColumns(15);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("start greedysnake program");
GreedySnake gs=new GreedySnake();
gs.showInterface();
gs.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gs.setSize(1030,730);
gs.setVisible(true);
}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
//System.out.println("有键按下");
}
@Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
char c=e.getKeyChar();
//System.out.println(c);
if(c=='w')
wasd='w';
else if(c=='a')
wasd='a';
else if(c=='s')
wasd='s';
else if(c=='d')
wasd='d';
else
;
}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
//System.out.println("有键释放");
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
restart();
}
}
class Body
{
public int x;
public int y;
public Body next;
public Body pre;
public Body(int tx,int ty)
{
x=tx;
y=ty;
}
public int Move(char direction)
{
if(direction=='s')
y=y+20;
else if(direction=='w')
y=y-20;
else if(direction=='a')
x=x-20;
else if(direction=='d')
x=x+20;
else
;
return 0;
}
public boolean isEqual(Body tB)
{
return false;
}
}