昨晚实现了蛇身的增长,思路如下:
蛇身的增长:写一个类,这个类中有一个SnakeHead类型的数组(SnakeHead类型的数组是为了方便传
递坐标),在方法snakeRun()中实现蛇身的增长:在蛇头的坐标变化前,获取各节身体的坐标。下标为0的元素
获取蛇头在坐标位变化前的坐标,下标为1的元素获取下标为0的元素坐标变化前的坐标......(以此类推)。然后
在paint()方法中,在画完蛇头后,开始画蛇身(当然SnakeJPanel1中需要添加一个蛇身类的成员变量)。蛇每吃
掉一个食物,蛇身类的对象中的数组便增加一个元素。
蛇身类代码如下:
package doodleSnake;
import java.util.ArrayList;
public class SnakeBody {
ArrayList<SnakeHead> body; //蛇的身体的数组
public SnakeBody() {
this.body = new ArrayList<SnakeHead>();
}
public int getIndexX(int index) { //获取下标为index的元素的x坐标
SnakeHead h = body.get(index);
return h.getX();
}
public int getIndexY(int index) {//获取下标为index的元素的y坐标
SnakeHead h = body.get(index);
return h.getY();
}
public void setIndexCoordination(int index, int x, int y) { //设置下标为index的元素的坐标
this.body.get(index).setX(x);
this.body.get(index).setY(y);
}
public void setCoordinate() {//设置除第一节外其他身体部分的坐标
if(this.body.size() >= 2) {
for(int i=this.body.size()-1; i>=1; i--) {
this.setIndexCoordination(i, this.getIndexX(i-1), this.getIndexY(i-1));
//System.out.println(i+": ("+ this.getIndexX(i-1)+","+this.getIndexY(i-1)+")");
}
}
}
public void addBody() { //蛇身的增长
this.body.add(new SnakeHead());
if(this.body.size() >= 2) {
this.setIndexCoordination(this.body.size()-1, this.getIndexX(this.body.size()-2), this.getIndexY(this.body.size()-2));
}
}
}
新的JPanel1代码如下:
package doodleSnake;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class SnakePanel1 extends JPanel implements KeyListener{
JPanel jps[]; //定义面板数组,充当背景。
SnakeHead head; //定义蛇头
Food food; //定义食物
SnakeBody snaekBody; //定义蛇的身体
public SnakePanel1() { //构造方法
this.head = new SnakeHead();
this.food = new Food();
this.snaekBody = new SnakeBody();
this.setSize(619,619); //设置大面板尺寸
this.setLayout(new GridLayout(20,20,1,1)); //设置大面板布局方式:分成20*20的区域,间距为1个像素
jps = new JPanel[400]; //创建一个容量为400的JPanels数组,存放小面板
for(int i=0; i<jps.length; i++) {
jps[i] = new JPanel();
jps[i].setBackground(Color.green); //设置小面板的颜色为绿色
this.add(jps[i]); //在大面板中添加小面板
}
this.setFocusable(true); //获取焦点
addKeyListener(this); //监听自己
}
public void setColor(Color c) {
for(int i=0; i<jps.length; i++) {
jps[i].setBackground(c); //设置小面板的颜色为绿色
}
}
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.cyan);
g.fillRect(this.food.getX(), this.food.getY(), 30, 30);//画食物
g.setColor(Color.PINK);
for(int i=0; i<this.snaekBody.body.size(); i++) { //画蛇身
g.fillRect(this.snaekBody.getIndexX(i), this.snaekBody.getIndexY(i), 30, 30);
System.out.println("i: "+i+"size: "+this.snaekBody.body.size());
}
g.setColor(Color.RED);
g.fillRect(this.head.getX(), this.head.getY(), 30, 30);// 画蛇头,PS:先画食物,再画蛇头,这样蛇头才会覆盖食物
}
public boolean foodBeEaten() {
if(this.head.getX() == this.food.getX() && this.head.getY() == this.food.getY()) {
return true;
}
return false;
}
public void snakeRun() {
while(true) {
try {
Thread.sleep(head.getSpeed()); //设置蛇的移动速度
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
if(this.snaekBody.body.size() != 0) {
this.snaekBody.setCoordinate(); //设置其他身体的坐标,PS:必须先设置非第一节的蛇身的坐标,否则第二节蛇身的坐标永远会与第一节相同,即画出来的蛇身会少一节
this.snaekBody.setIndexCoordination(0, this.head.getX(), this.head.getY()); //设置第一节身体的坐标
}
if(head.getDirection() == 1) {
head.setY(head.getY()-31);
}
else if(head.getDirection() == 2) {
head.setX(head.getX()+31);
}
else if(head.getDirection() == 3) {
head.setY(head.getY()+31);
}
else if(head.getDirection() == 4) {
head.setX(head.getX()-31);
}
if(this.foodBeEaten()) {
food = new Food();
this.snaekBody.addBody(); //蛇身增长
System.out.println("蛇身的长度:"+this.snaekBody.body.size());
if(this.snaekBody.body.size() == 1) { //赋予地址,避免在(0,0)闪烁
this.snaekBody.setIndexCoordination(0, this.head.getX(), this.head.getY());
}
}
repaint();
}
}
@Override
public void keyPressed(KeyEvent e) {
// TODO 自动生成的方法存根
int n = e.getKeyCode();
if(n == KeyEvent.VK_UP && head.getDirection() != 3) { //当按下向上的键且蛇的移动方向不是向下时....
head.setDirection(1);
}
else if(n == KeyEvent.VK_DOWN && head.getDirection() != 1) {
head.setDirection(3);
}
else if(n == KeyEvent.VK_LEFT && head.getDirection() != 2) {
head.setDirection(4);
}
else if(n == KeyEvent.VK_RIGHT && head.getDirection() != 4) {
head.setDirection(2);
}
//repaint();
}
@Override
public void keyReleased(KeyEvent arg0) {
// TODO 自动生成的方法存根
}
@Override
public void keyTyped(KeyEvent arg0) {
// TODO 自动生成的方法存根
}
}
实现蛇身增长的过程中遇到的问题:
- 蛇每吃到一个食物,总是会在(0,0)位置画一节蛇身(一闪而过),其原因是: 刚开始SnakeBody类的addBody方法中,没有对新增蛇身的坐标进行初始化,所以系统自动将新增蛇身的坐标设为(0,0)。
- 蛇有了第一节身体后,再次吃到食物却没有长出第二节身体,但是当蛇第三次吃到食物后,蛇身增长正常化。其原因为: 在JPanel1类中SnakeRun()方法中,在改变蛇的坐标时,错误的做法是先改变蛇头的坐标,再改变蛇身的坐标。这样会导致蛇的第一节身体的坐标永远与蛇头的坐标相通,故而再paint方法中蛇头会将第一节蛇身覆盖,即蛇永远少画了一节身体。所以在改变蛇的坐标时,应当先改变蛇身体的坐标,再改变蛇头的坐标。
接下来要实现的功能:
1、按键&信息 面板的“开始”、“暂停”、“结束”、“得分”、“时间”。”设置“功能最后实现。
2、游戏结束的判断:蛇撞墙。