按计划本来是昨天就应该写好发上来的了,可是突然遭遇感冒,头疼疼的就耽搁了,不好意思啊 ,今天大步赶上,^_^;
游戏规则是这样的:
狐狸和兔子都有年龄,到了一定年龄死掉;
狐狸可以随机决定吃周围的一只兔子,吃一次年龄上限就会提高一次;
狐狸和兔子会随机生个baby;
狐狸和兔子会向周围随机移动;
这个游戏可以看成是细胞自动机游戏的进阶版。游戏窗口是分割成一个个的网格,这些网格里的对象都统一交由一个数据管理类(Field)来管理实现互动,这些互动包括随机移动(move),捕食(feed),繁殖(breed)。然后这些数据更新好后将会由一个表现类(View)将其画(paint )出来。狐狸(fox)和(rabbit)对象都会放进网格当中去,因此创建一个接口(ICell),让狐狸和兔子实现这个接口。这样就可以把狐狸和兔子放进网格里去,并交由Field对象来管理。同时狐狸和兔子都存在共同的特性,例如年龄,都存在年龄上限,都会移动,繁殖等,因此可以创建一个Animal类,并让狐狸和兔子继承(extends)它。
随手画了个结构简图(好粗糙):
出来一个效果是这样的(红色的是狐狸,黑色的是兔子(红狐黑兔)):
package field;
/**
* 为什么创建Location呢?
* 因为根据游戏规则,狐狸和兔子会向周围空的位置移动,因此需要一个类来记录周围位置的坐标;
* @author ItigerU
*
*/
public class Location {
private int row;
private int col;
public Location(int row, int col) {
super();
this.row = row;
this.col = col;
}
public int getRow() {
return row;
}
public int getCol() {
return col;
}
}
创建接口,狐狸和兔子通过这个接口放到Field里面去,Field对象通过管理ICell来管理狐狸和兔子:
package ICell;
/**
* 这个接口只定义了一个方法,就是把让实现了这个接口的子类自己把自己给画出来;
*/
import java.awt.Graphics;
public interface ICell {
void draw(Graphics g, int x, int y, int size);
}
创建好接口后创建Animal类:
package animal;
import java.util.List;
import field.Location;
/**1. 为什么Animal是抽象类;
* Animal是狐狸和兔子的父类,实现了共同的属性和方法;例如年龄,年龄上限,移动,捕食,繁殖;
* 繁殖需要每个子类重写(overridde),因为狐狸和兔子的繁殖概率不一样。所以将它定义成抽象方法;
* 所以Animal是抽象类;
* @author ItigerU
*
*/
public abstract class Animal {
private int age;
private int ageLimit;
private int breedableAge;
private boolean isAlive = true;
//给子类设定年龄上限和生育年龄
public Animal(int ageLimit, int breedableAge) {
this.ageLimit = ageLimit;
this.breedableAge = breedableAge;
}
public int getAge() {
return age;
}
public boolean isAlive() {
return isAlive;
}
//该方法用来实现颜色的渐变;
public double getAgePercent() {
return (double)age / ageLimit;
}
public boolean isbreedable() {
return age >= breedableAge;
}
//生长
public void grow() {
age ++;
if(age > ageLimit) {
this.die();
}
}
//死亡
public void die() {
this.isAlive = false;
}
//随机繁殖;返回的一定是一个Animal啊...
public abstract Animal breed();
//随机移动,返回的是一个位置(确定移动到那个点的位置)。传入某点坐标和周围空的位置;
public Location move(int row, int col, Location[] freeAdj) {
Location ret = null;
if(freeAdj.length > 0 && Math.random() < 0.12) {
ret = freeAdj[(int)(Math.random() * freeAdj.length)];
}
return ret;
}
/*
* 随机捕食;返回的是一个Animal对象,传入的是某点坐标和周围所有(意味着这是容器)的对象;
* 需要狐狸重写,因为只有狐狸才捕食;
*/
public Animal feed(int row, int col, List<Animal> fed) {
return null;
}
//根据规则:狐狸每捕食一次年龄上限加一次;
public void longerLife(int inc) {
ageLimit += inc;
}
@Override
public String toString() {
return "" + age + " : " + (this.isAlive ? "isAlive" : "dead");
}
}
创建好Animal类后再把狐狸和兔子两个子类给创建好:
package animal;
import java.awt.Color;
import java.awt.Graphics;
import java.util.List;
import ICell.ICell;
public class Fox extends Animal implements ICell {
public Fox() {
super(20, 4);
}
@Override
public Animal breed() {
Animal ret = null;
if(Math.random() < 0.05) {
ret = new Fox();
}
return ret;
}
@Override
public void draw(Graphics g, int x, int y, int size) {
int alpha = (int)((1 - this.getAgePercent()) * 255);
g.setColor(new Color(0, 0, 0, alpha));
g.fillRect(x, y, size, size);
}
//捕食,返回一个Animal.传入该点坐标,周围可捕食的对象;年龄上限加2;
public Animal feed(int row, int col, List<Animal> feed){
Animal ret = null;
if(Math.random() < 0.05) {
ret = feed.get((int)(Math.random() * feed.size()));
this.longerLife(2);
}
return ret;
}
@Override
public String toString() {
return "Fox " + super.toString();
}
}
package animal;
import java.awt.Color;
import java.awt.Graphics;
import ICell.ICell;
public class Rabbit extends Animal implements ICell {
public Rabbit() {
super(10, 2);
}
@Override
public void draw(Graphics g, int x, int y, int size) {
int alpha = (int)((1 - this.getAgePercent()) * 255);
g.setColor(new Color(255, 0, 0, alpha));
g.fillRect(x, y, size, size);
}
@Override
public Animal breed() {
Animal ret = null;
if(Math.random() < 0.12) {
ret = new Rabbit();
}
return ret;
}
@Override
public String toString() {
return "Rabbit: " + super.toString();
}
}
第一个可以看到是Fox,第二个是Rabbit,它们之间差不多,但是Fox重写feed()方法,Rabbit没有。因为按游戏规则,Rabbit没有吃的说法,因此当调用Rabbit去feed的时候,是调用了父类的feed()方法,此时返回一个null;
接下来把Field给码上:
package field;
import java.util.ArrayList;
import java.util.List;
import ICell.ICell;
public class Field {
private int height;
private int width;
private ICell[][] cells;
/*
* 1. adjacent为什么要将它设置为静态常量呢?
*/
private static final Location[] adjacent = {
new Location(-1, 1),new Location(0, 1),new Location(1, 1),
new Location(-1, 0),new Location(0, 0),new Location(0, 1),
new Location(-1, -1),new Location(0, -1),new Location(-1, -1)
};
public Field(int height, int width) {
this.height = height;
this.width = width;
cells = new ICell[height][width];
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public ICell get(int row, int col) {
return cells[row][col];
}
public ICell place(int row, int col, ICell cell) {
ICell ret = cells[row][col];
cells[row][col] = cell;
return ret;
}
// public ICell[] getNeighbour(int row, int col) {
// List<ICell> list = new ArrayList<ICell>();
// for(int i = -1; i < 2; i ++) {
// for(int j = -1; j < 2; j ++) {
// int r = row + i;
// int c = col + j;
// if(r > -1 && r < height && c > -1 && c < width && !(r == row && c == col)) {
// list.add(cells[r][c]);
// }
// }
// }
// return list.toArray(new ICell[list.size()]);
// }
public ICell[] getNeighbour(int row, int col) {
List<ICell> list = new ArrayList<ICell>();
for(Location loc : adjacent) {
int r = row + loc.getRow();
int c = col + loc.getCol();
if(r > -1 && r < height && c > -1 && c < width && !(r == row && c == col)) {
list.add(cells[r][c]);
}
}
return list.toArray(new ICell[list.size()]);
}
public Location[] getFreeAdj(int row, int col) {
List<Location> list = new ArrayList<Location>();
for(Location loc : adjacent) {
int r = row + loc.getRow();
int c = col + loc.getCol();
if(r > -1 && r < height && c > -1 && c < width && (cells[r][c] == null)) {
list.add(new Location(r, c));
}
}
return list.toArray(new Location[list.size()]);
}
//给指定的对象在指定的位置附近随机放置;需要返回什么吗?不需要返回什么;
public void placeRandomAdj(int row, int col, ICell cell) {
Location[] loc = this.getFreeAdj(row, col);
if(loc.length > 0) {
int index = (int)(Math.random() * loc.length);
cells[loc[index].getRow()][loc[index].getCol()] = cell;
}
}
public ICell remove(int row, int col) {
ICell ret = cells[row][col];
cells[row][col] = null;
return ret;
}
public void remove(ICell cell) {
for(int row = 0; row < height; row ++) {
for(int col = 0; col < width; col ++) {
if(cells[row][col] == cell) {
cells[row][col] = null;
break;
}
}
}
}
//将指定的对象移动到指定的地方
public void move(int row, int col, Location loc) {
cells[loc.getRow()][loc.getCol()] = cells[row][col];
remove(row, col);
}
}
现在是表现类View:
package field;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
import ICell.ICell;
public class View extends JPanel {
private static final int GIRD_SIZE = 16;
private Field field;
public View(Field field){
this.field = field;
}
@Override
public void paint(Graphics g) {
super.paint(g);
for(int row = 0; row < field.getHeight(); row ++) {
g.drawLine(0, row * GIRD_SIZE, field.getWidth() * GIRD_SIZE, row * GIRD_SIZE);
}
for(int col = 0; col < field.getHeight(); col ++) {
g.drawLine( col * GIRD_SIZE, 0, col * GIRD_SIZE, field.getHeight() * GIRD_SIZE);
}
for(int row = 0; row < field.getHeight(); row ++) {
for(int col = 0; col < field.getWidth(); col ++) {
ICell cell = field.get(row, col);
if(cell != null) {
cell.draw(g, col, row, GIRD_SIZE);
}
}
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(field.getWidth() * GIRD_SIZE + 1, field.getHeight() * GIRD_SIZE + 1);
}
}
现在我们已经把整个程序中的功能类已经完成了,现在通过引入更高层次的类去实现它们之间的一个通信,这个,我们就将在下一篇当中去详细展开~