贪吃蛇 V1.0
项目需求
- 贪吃蛇的窗体游戏
- 可以通过键盘来控制蛇的移动方向
- 可以吃东西,实现蛇身增长
源代码
//Mainfrm.java
package com.snake.ui;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.LinkedList;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
import com.snake.Block;
public class Mainfrm extends JFrame{//游戏窗口
//泛型:集合里所放的数据被限制为Block
private LinkedList<Block> snake = new LinkedList<>();
Timer time;//定时器
int direction=1;
Block food = null;//食物
public Mainfrm(){
initSnakeBody();
controler();
this.setTitle("我的超级蛇V1.0");
this.setSize(600,500);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setVisible(true);
time=new Timer();
time.schedule( new TimerTask() {
@Override
public void run() {
move(direction);
repaint();
}
}, 0, 500);//每隔1秒执行一次
}
public void paint(Graphics g){//绘图功能
super.paint(g);
for(int i=0;i<snake.size();i++){
Block b = snake.get(i);
Random r = new Random();
g.setColor(b.getColor());//设置画笔的颜色
g.fillRect(b.getX(), b.getY(), 20, 20);
drawFood(g);//画食物
eat();
}
}
public void initSnakeBody(){
for(int i=0;i<3;i++){
Block block = new Block();
block.setX(50+21*i);
block.setY(50);
Random r = new Random();
block.setColor(new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255)));
snake.add(block);
}
}
public void move(int direc){
Block end = snake.removeLast();
Block head = snake.getFirst();
switch(direc){
case 1:
end.setX(head.getX() + 21);
end.setY(head.getY());
break;
case 2:
end.setX(head.getX());
end.setY(head.getY() + 21);
break;
case 3:
end.setX(head.getX() - 21);
end.setY(head.getY());
break;
case 4:
end.setX(head.getX());
end.setY(head.getY() - 21);
break;
}
snake.addFirst(end);
}
//键盘控制
public void controler(){
this.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e){//按下会执行
switch(e.getKeyCode()){
case KeyEvent.VK_W:
direction = 4;
break;
case KeyEvent.VK_A:
direction = 3;
break;
case KeyEvent.VK_S:
direction = 2;
break;
case KeyEvent.VK_D:
direction = 1;
break;
}
}
});
}
//画食物
boolean isNew = true;
public void drawFood(Graphics g){
if(isNew){
isNew = false ;
food = new Block();
Random r = new Random();
food.setX(r.nextInt(500));
food.setY(r.nextInt(400));
food.setColor(new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255)));
}
g.fillRect(food.getX(), food.getY(), 20, 20);
}
//吃食物
public void eat(){
Block head = snake.getFirst();
if(Math.abs(head.getX()-food.getX())<20 && Math.abs(head.getY()-food.getY())<20){
snake.add(food);
isNew = true;
}
}
}
每一个方块的定义类
//Block.java
package com.snake;
import java.awt.Color;
public class Block {//蛇块
private int x;
private int y;
private Color color;
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
main方法入口
//Test.java
package com.snake;
import com.snake.ui.Mainfrm;
public class Test {
public static void main(String[] args) {
new Mainfrm();
}
}
效果展示
主要知识点
Graphics
绘图类的设计- 蛇身
Block
类的定义 JFrame
界面的设计- 泛型集合
LinkedList
的使用,定义受限制的Block
类 - 多线程
Timer
类的运用 Random
随机数生成随机颜色块
实现思路
- 此设计的重点在于对蛇身的处理上,将蛇身视为一个一个小方块组成是一个不错的选择。而对于蛇的移动,可以视为蛇尾的最后一个小方块移动到蛇头的位置(这样做的好处在于,未改变蛇的长度的情况下巧妙的实现了蛇的动态移动)。而对于蛇吃食物,也就是通过算法来判断蛇头是否和食物方块触及,若是则将食物方块添加到蛇身上并将此方块设为新的头部,而该食物方块应remove,并增添下一个随机位置的食物块。
- 另外就是通过键盘控制蛇移动的方向,这里有四个方向需要考虑,每一个不同的方向的改变其实就是通过设置新头部相对于原蛇头的位置(这里需清楚的是,Swing界面的左上角为坐标原点,水平方向为X轴坐标,垂直方向为Y轴坐标)
仍需改进的地方
- 该游戏未考虑游戏时长,可以加入时间计时器计时,以此来结束游戏。
- 可以为游戏设置等级难度,其中一种方式就是通过加速蛇移动的速度。实现方式:多线程Timer().schedule的刷新时间减少,线程的执行效率增加。
- 蛇身可以无障碍的穿越界面边界,并到从相对的边界出来。