******************贪吃蛇***************
1 图形界面基础
如何显示和控制窗口,swing API的使用
2 图形界面的绘图技术,用于绘制舞台,蛇和食物
如何进行自定义绘制,swing API 的使用
3 图形界面的事件响应,用于控制蛇的运行方向
如何处理键盘事件,swing API 的使用
(1 2 3 是舞台的功能)
4 蛇的运行算法,是蛇的功能
爬行
创建两个方法:
1,爬行creep()
2,创建新头节点createHead()
吃食物
1,创建新头节点
2,检查是否吃到食物
3,如果吃到食物,就复制数组(扩容,丢弃原数组)
4,向后移动点
5,插入到新节点[0]
碰撞检测(边界,自身碰撞)
5 在舞台上定时驱动蛇的运行
**************单元测试工具****************
测试要新建个类,在这个新的类中测试
如果是测试主函数中的代码,则要在主函数外建一个test()方法,把主函数中的代码移到test()中,如果是在类中的方法可以直接调用,如果类中没有new的话,则需要在测试这个类中new下.
在项目点击右键选择properties---->左边框内选择Java Build Path选项--->右边选择Libraries--->Add Library--->Junit 4
导入包:import org.junit.Test; 单元测试工具注释
在方法前@Test
在一个测试类中有多个测试,所以会输出多个测试结果,如果只想看一个测试结果,可以在你想看到结果之外的其他测试上//@Test
**************爬行**********
创建两个方法:
1,爬行creep()
2,创建新头节点createHead()
/** 按照默认方向爬行一步 */
public void creep(){
for(int i=this.cells.length-1; i>=1;i-- ){
cells[i] = cells[i-1];
}
cells[0] = createHead(currentDirection);
}
/** 根据方向,和当前(this)的头节点,创建新的头节点 */
private Cell createHead(int direction){
Cell head = this.cells[0];//临时创建头节点
int x = head.getX();
int y = head.getY();
switch(direction){
case DOWN:
y++;
break;
case UP:
y--;
break;
case RIGHT:
x++;
break;
case LEFT:
x--;
break;
}
return new Cell(x,y);//返回新的节点
}
在测试类中调用着两个方法并测试
@Test//爬行测试 creep 爬行
public void creepTest(){
Worm worm = new Worm();
System.out.println(worm);
//[[0,0], [1,0], [2,0], [3,0], [4,0], [5,0],
//[6,0], [7,0], [8,0], [9,0], [10,0], [11,0]]
worm.creep();
System.out.println(worm);
//[[0,1], [0,0], [1,0], [2,0], [3,0], [4,0],
// [5,0], [6,0], [7,0], [8,0], [9,0], [10,0]]
/** 第二次输出的结果与第一次的比较表示蛇的爬行 */
}
**************吃食物****************
爬行的时候检查是否能够吃到food,food是参数
如吃到食物,蛇的长度会增加
1,创建新头节点
2,检查是否吃到食物
3,如果吃到食物,就复制数组(扩容,丢弃原数组)
4,向后移动点
5,插入到新节点[0]
public boolean creep(Cell food){
Cell head = createHead(currentDirection);
boolean eat = head.getX()==food.getX()//检查是否吃到食物
&& head.getY()==food.getY();
if(eat){//如果吃到食物就复制数组
Cell[] ary = Arrays.copyOf(cells, cells.length+1);
cells = ary;//丢弃源数组
}//向后移动节点
for(int i=cells.length-1;i>=1;i--){
cells[i] = cells[i-1];
}
cells[0] = head;//插入头节点
return eat;
}
在测试类中调用方法并测试
@Test
public void creepWithFoodTest(){
Worm worm = new Worm();
Cell food = new Cell(0,2);
System.out.println(worm);
//蛇的默认长度,
//[[0,0], [1,0], [2,0], [3,0], [4,0], [5,0],
//[6,0], [7,0], [8,0], [9,0], [10,0], [11,0]]
System.out.println(worm.creep(food));
//false,表示蛇爬行一步,但没有吃到食物
System.out.println(worm);
//蛇爬行后的长度,
//[[0,1], [0,0], [1,0], [2,0], [3,0], [4,0],
//[5,0], [6,0], [7,0], [8,0], [9,0], [10,0]]
System.out.println(worm.creep(food));
//true,表示蛇再次爬行后吃到了食物
System.out.println(worm);
//蛇吃到食物后的长度,蛇吃到食物后蛇的长度增加(数组的扩容)
//[[0,2], [0,1], [0,0], [1,0], [2,0], [3,0],
//[4,0], [5,0], [6,0], [7,0], [8,0], [9,0], [10,0]]
}
***********更换方向************
public boolean creep(int direction,Cell food){
if(currentDirection+direction==0){//反向
return false;//如果反向,就不进行任何动作
}
this.currentDirection = direction;//换方向
Cell head = createHead(currentDirection);
boolean eat = head.getX()==food.getX()//检查是否吃到食物
&& head.getY()==food.getY();
if(eat){//如果吃到食物就复制数组
Cell[] ary = Arrays.copyOf(cells, cells.length+1);
cells = ary;//丢弃源数组
}//向后移动节点
for(int i=cells.length-1;i>=1;i--){
cells[i] = cells[i-1];
}
cells[0] = head;
return eat;
}
在测试类中调用方法并测试
@Test//更换方向爬行测试
public void creepWithDirFoodTest(){
System.out.println("更换方向爬行测试");
Worm worm = new Worm();
Cell food = new Cell(1,2);
System.out.println(worm);
//[[0,0], [1,0], [2,0], [3,0], [4,0],
//[5,0], [6,0], [7,0], [8,0], [9,0], [10,0], [11,0]]
System.out.println(worm.creep(food));//false
System.out.println(worm);
//[[0,1], [0,0], [1,0], [2,0], [3,0], [4,0],
//[5,0], [6,0], [7,0], [8,0], [9,0], [10,0]]
System.out.println(worm.creep(food));//flase
System.out.println(worm);
//[[0,2], [0,1], [0,0], [1,0], [2,0], [3,0],
//[4,0], [5,0], [6,0], [7,0], [8,0], [9,0]]
System.out.println(worm.creep(Worm.UP,food));
//false,调用方向向上
System.out.println(worm);
//反向无动作.蛇的默认方向是向下,而调用的方向向上,与默认方向相反, 所以没有任何动作
//[[0,2], [0,1], [0,0], [1,0], [2,0], [3,0],
//[4,0], [5,0], [6,0], [7,0], [8,0], [9,0]]
System.out.println(worm.creep(Worm.RIGHT,food));
//true,调用的方向向右,吃到食物
System.out.println(worm);//吃到食物
//[[1,2], [0,2], [0,1], [0,0], [1,0], [2,0], [3,0],
//[4,0], [5,0], [6,0], [7,0], [8,0], [9,0]]
}
**************碰撞检测(边界,自身碰撞)**********
检查在新的运行方向上是否能够碰撞到边界和自己this
0 反向不检查返回false
1 生成下个新头节点位置
2 如果新头节点出界,返回true,表示碰撞边界
3 如果新头节点包含在蛇的前n-1节点范围,表示吃到自己返回true
4 否则返回false
public boolean hit(int direction){
if(currentDirection + direction==0){//反向
return false;//如果反向,就不进行任何动作
}
Cell head = createHead(direction);//创建新的头节点
//判断新的头节点是否出界
if(head.getX()<0 || head.getX()>=WormStage.COLS ||
head.getY()<0 ||head.getY()>=WormStage.ROWS){
return true;
}//判断新头节点是否包含在自己体内,表示吃到自己
for(int i=0;i<cells.length-1;i++){
if(cells[i].getX()==head.getX() &&
cells[i].getY()==head.getY()){
return true;
}
}
return false;
}
在测试类中调用方法并测试
@Test
public void hitTest(){
System.out.println("碰撞方法的检测");
Worm worm = new Worm();
System.out.println(worm);
worm.creep();
System.out.println(worm);
System.out.println(worm.hit(Worm.LEFT));
//true,因为蛇的默认方向是向下的,调用向左的方向,则会碰撞边界,返回true
System.out.println(worm.hit(Worm.RIGHT));
//false,因为蛇的默认方向是向下的,调用向右的方向,则不会碰撞边界,返回false
Cell food = new Cell(4,5);
worm.creep(Worm.RIGHT,food);
//改变默认方向,使蛇的方向由默认的向下改为向右
System.out.println(worm);
System.out.println(worm.hit(Worm.UP));
//true,因为蛇的默认方向是向右的,调用向上的方向,则会碰撞边界,返回true
System.out.println(worm.hit(Worm.DOWN));
//false,因为蛇的默认方向是向右的,调用向下的方向,则不会碰撞边界,返回false
}
*************显示和控制窗口***************
显示窗口需要导入包:
import javax.swing.JFrame;
导入包后就可以调用此包中的方法
1.frame.setSize(width,height);
调整组件的大小,使其宽度为 width,高度为 height。
2.frame.setLocation(x,y);
将组件移到新位置。通过此组件父级坐标空间中的 x 和 y 参数来指定新位置的左上角
3.frame.setLocationRelativeTo(null);
则此窗口将置于屏幕的中央。
4.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
设置默认的关闭操作为在关闭时候离开软件,单窗口软件这样设置最简单,多窗口软件另有其他设法
5.frame.setLayout(null)
关闭默认布局管理,避免面板充满窗口
6.frame.setVisible(true)
设置可见的窗口
************自定义绘制***************
/**重写JPanal绘制方法*/
先导入包:import java.awt.Graphics;
允许应用程序在组件以及在闭屏图像上进行绘制
public void paint(Graphics g){
//添加自定义绘制
//先绘制背景
g.setColor(Color.darkGray);//把背景涂成深灰色
g.fillRect(0, 0, getWidth(), getHeight());//填充指定矩形
g.setColor(Color.RED);
//draw 绘制 Rect矩形,绘制指定矩形的边框
g.drawRect(0, 0, this.getWidth()-1, this.getHeight()-1);
//绘制食物
g.setColor(food.getColor());
//fill 填充 3D 3维 Rect矩形 突起的立体按钮形状
g.fill3DRect(food.getX()*CELL_SIZE,
food.getY()*CELL_SIZE, CELL_SIZE, CELL_SIZE, true);
//绘制蛇
worm.paint(g);//让蛇自己利用画笔绘制//调用worm类中paint()方法
}
在Worm类中的paint()方法,表示为蛇添加绘制方法,也就是利用来自舞台面板的画笔(Graphics)绘制蛇到舞台上
public void paint(Graphics g){
g.setColor(this.color);//设置当前蛇的颜色
for(int i= 0;i<cells.length;i++){
Cell cell = cells[i];//临时创建一个格子
//绘制一个填充当前颜色的矩形
g.fill3DRect(cell.getX()*WormStage.CELL_SIZE,
cell.getY()*WormStage.CELL_SIZE,
WormStage.CELL_SIZE, WormStage.CELL_SIZE, true);
}
}
***********在舞台上定时驱动蛇的运行************
启动定时器驱动蛇的运行
1 检查碰撞是否将要发生
2 如果发生碰撞:创建新的蛇和食物,重写开始
3 如果没有碰撞就爬行,并检查是否能够吃到食物
4 如果吃到食物:重新创建新的食物
5 启动重新绘制界面功能 repaint() 更新界面显示效果!
repaint() 方法会尽快调用 paint() 更新界面!
private Timer timer;
private void go(){
if(timer==null){
timer = new Timer();
timer.schedule(new TimerTask(){
public void run(){
if(worm.hit()){//如果蛇碰到边界或自己
worm = new Worm();//创建新蛇
food = createFood();//创建新食物
}else{//如果没有碰到自己
boolean eat = worm.creep(food);
//蛇向前(当前方向)爬行,返回结果表示是否吃到食物
if(eat){//如果吃到食物,就生成新食物
food = createFood();
//不能new一个,要把蛇躲过去
}
}
repaint();
//不加repaint()会造成只有在拉画框是才会动
}
}, 0, 1000/10);
}
}
*********paint方法************
package com.tarena.worm;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
/**
* 如何绘图
*/
public class PaintDemo {
public static void main(String[] args) {
JFrame frame = new JFrame("绘图演示");
frame.setSize(300, 200);
frame.setLayout(null);//取消默认布局管理
//窗口中的组件,需要自定义位置和大小
MyPane panel = new MyPane();
panel.setSize(280, 150);
panel.setLocation(20, 20);//设置面板的位置
panel.setBorder(new LineBorder(Color.BLACK));
frame.add(panel);
frame.setVisible(true);
panel.go();
}
}
class MyPane extends JPanel{
String[] s = {".o0o....","..o0o...","...o0o..",
"....o0o.",".....o0o","o.....0o","0o.....o"};
int i = 0;
//方法重写:子类修改父类的功能
//paint是swing 默认调用绘制面板的方法
public void paint(Graphics g){
super.paint(g);//调用父类的paint方法,包含绘制边框功能
g.drawString(s[i++%s.length], 20, 20);//自定义绘制
}
public void go(){
Timer timer = new Timer();
timer.schedule(new TimerTask(){
public void run(){
repaint();//在JPanel中定义,尽快的启动界面绘制功能,
//界面绘制功能会调用paint方法
}
}, 0,1000/10);
}
}
转载于:https://blog.51cto.com/7190705/1209425