一、绘制网格界面
public class SnakeFrame extends JFrame {
// 方格的宽度和长度
public static final int BLOCK_WIDTH = 15;
public static final int BLOCK_HEIGHT = 15;
// 界面的方格的行数和列数
public static final int ROW = 20;
public static final int COL = 20;
static SnakeFrame sf = new SnakeFrame();
JPanel jp = new JPanel();
public static void main(String[] args) {
sf.launch();
}
public void launch(){
setTitle("贪吃蛇");
setLocation(30, 40);
//边界判断很迷,暂时不知道怎么解决
setSize(BLOCK_WIDTH*(COL+1), BLOCK_HEIGHT*(ROW+2));
setVisible(true);
add(new SnakePanel());
this.setResizable(false);
}
//用JPanel面板来绘制,可以避免闪烁问题
public class SnakePanel extends JPanel{
@Override
public void paint(Graphics g){
//画横线
for(int i=0;i<=ROW;i++){
g.drawLine(0,i*BLOCK_HEIGHT,BLOCK_WIDTH*COL,i*BLOCK_HEIGHT);
}
//画竖线
for(int i=0;i<=COL;i++){
g.drawLine(i*BLOCK_WIDTH,0,i*BLOCK_WIDTH,BLOCK_HEIGHT*ROW);
}
}
}
}
界面如下:
二、画蛇,用链表实现
import java.awt.Graphics;
public class Snake {
/*
* 用链表实现蛇,蛇头的位置和方向就能确定蛇的位置
*/
// 一节蛇的大小,和网格一个格子一样大
private static final int BLOCK_WIDTH = SnakeFrame.BLOCK_WIDTH;
private static final int BLOCK_HEIGHT = SnakeFrame.BLOCK_HEIGHT;
private Node head = null;// 蛇头
private Node tail = null;// 蛇尾
public enum Direction {// 用枚举设置蛇的方向
R, L, D, U
}
public Snake(){//蛇的构造函数
Node node = new Node(3,4,Direction.D);
head = node;
tail = node;
}
public void draw(Graphics g){//绘制蛇
if(head == null) return;
for(Node node = head;node!=null;node=node.next){
node.draw(g);
}
}
public class Node {
// 结点位置(左上角)
private int row;
private int col;
// 蛇头方向
private Direction dir;
public Node pre;// 上一个节点
public Node next;// 下一个节点
public Node(int row, int col, Direction dir) {// 构造结点,参数为位置和方向
this.row = row;
this.col = col;
this.dir = dir;
this.pre = null;
this.next = null;
}
//绘制结点
public void draw(Graphics g) {
g.fillRect(row*BLOCK_WIDTH, col*BLOCK_HEIGHT, BLOCK_WIDTH, BLOCK_HEIGHT);
}
}
}
最后在SnakeFrame中new一个snake,在JPanel的paint()中添加snake.draw(),就在界面中画了一个静止的蛇头
三、让蛇动起来
1.在Snake中写一个move()函数
public void move() {// 通过删除结点增加结点来移动
addNodeInHead();// 从头部插入结点
deleteNodeInTail();// 从尾部删除结点
}
public void addNodeInHead() {
Node node = null;
// 根据头结点方向来确定增加结点的位置
switch (head.dir) {
case L:
node = new Node(head.row - 1, head.col, head.dir);// 横减
case R:
node = new Node(head.row + 1, head.col, head.dir);// 横加
case U:
node = new Node(head.row, head.col - 1, head.dir);// 纵减
case D:
node = new Node(head.row, head.col + 1, head.dir);// 纵加
}
head.pre = node;
node.next = head;
head = node;
}
public void deleteNodeInTail() {
Node node = tail;
tail = node.pre;
node = null;
tail.next = null;
}
在Snake的draw()函数中添加move(),每次绘制后移动
2.在SnakeFrame中写一个PaintThread类
public class PaintThread implements Runnable{
@Override
public void run() {
while(true){
repaint();//自动调用paint()
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
然后在lanuch()中添加new Thread(new PaintThread()).start();,在界面启动的时候即启动线程
3.用键盘控制蛇的移动
a.在SnakeFrame中写一个键盘监听类,继承键盘适配器
public class KeyMonitor extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
snake.keyPressed(e);
}
}
b.在Snake类中写好keyPressed()函数
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();// 获取key的值
switch (key) {
case KeyEvent.VK_LEFT:
if (head.dir != Direction.L) {
head.dir = Direction.L;
}
break;
case KeyEvent.VK_RIGHT:
if (head.dir != Direction.R) {
head.dir = Direction.R;
}
break;
case KeyEvent.VK_UP:
if (head.dir != Direction.U) {
head.dir = Direction.U;
}
break;
case KeyEvent.VK_DOWN:
if (head.dir != Direction.D) {
head.dir = Direction.D;
}
break;
}
}
c.launch()函数中添加键盘监听事件
sp.setFocusable(true);//获取屏幕焦点,在键盘监听无效时添加
sp.addKeyListener(new KeyMonitor());//添加键盘监听事件
this.add(sp);
这样就实现了用键盘控制蛇的移动
四、建立食物类,随机生成食物并显示
import java.awt.Graphics;
import java.util.Random;
public class Food {
//所在位置(左上角)
private int row;
private int col;
//大小(不变)
private static final int BLOCK_WIDTH = SnakeFrame.BLOCK_WIDTH;
private static final int BLOCK_HEIGHT = SnakeFrame.BLOCK_HEIGHT;
Random r = new Random();
public Food(){
this.row = r.nextInt(SnakeFrame.ROW);
this.col = r.nextInt(SnakeFrame.COL);
}
public void draw(Graphics g){
g.fillRect(row*BLOCK_WIDTH, col*BLOCK_HEIGHT, BLOCK_WIDTH, BLOCK_HEIGHT);
}
}
在SnakeFrame中new一个food,在paint()中添加food的draw(),即可在界面生成食物
五、蛇吃食物,蛇变长,食物重新出现
这里使用Rectangle类来获取食物的位置
Food类中写一个获取位置的函数
public Rectangle getRect(){//检测食物的位置“储存于Rantangle”中
//构造一个新的 Rectangle ,其左上角被指定为 (x,y) ,其宽度和高度由同名参数指定
return new Rectangle(col*BLOCK_WIDTH, row*BLOCK_HEIGHT, BLOCK_WIDTH, BLOCK_HEIGHT);
}
在Snake类中定义一个eatFood(),Snake中也要有一个getRect()函数
public void eatFood(Food food){
if (this.getRect().intersects(food.getRect())) {
addNodeInHead();//蛇长长
food.reAppear();//蛇重新出现
}
}
public Rectangle getRect(){//检测蛇头的位置“储存于Rantangle”中
return new Rectangle(head.row*BLOCK_WIDTH, head.col*BLOCK_HEIGHT, BLOCK_WIDTH, BLOCK_HEIGHT);
}
在paint()调用eatFood(),即可实现蛇吃食物的功能
六、碰撞检测(Snake类中)
public boolean isDead() {
// 蛇头边界检查
if (head.row < 0 || head.col < 0 || head.row > SnakeFrame.ROW || head.col > SnakeFrame.COL) {
return true;
}
// 是否与蛇身相撞
for (Node node = head.next; node != null; node = node.next) {
if (node.row == head.row && node.col == head.col) {
return true;
}
}
return false;
}
在调用repaint()前先判断断蛇是否死亡
至此,贪吃蛇基本功能实现
后续可自行添加开始游戏界面、暂停功能、重新开始、计分、关卡等功能
源码下载:https://download.csdn.net/download/trancybao/10485564