贪吃蛇游戏: 一个游戏最核心的部分是游戏的业务逻辑代码。



分析里面的有几个事物:


1. 地图


2. 蛇


3. 食物



了解游戏规则: 


1.蛇初始化的时候是三个节点,而且一开始蛇是出现在地图的中间位置。


2. 蛇吃到东西会长长一节。 


3. 蛇咬到自己会死,蛇撞墙也会死。


4. 食物是随机生成的,食物不能长在石头上,食物可以长在蛇身上。



建议:  由于目前我们经验不足,所以在做项目的时候往往会出现比较大幅度修改代码 。 自己建立自己的代码库。



地图:


***********************************


*                                                 *


*                      @                        *


*                                                 *


*            ##$                              *


*                                                *


*                                                *


*                                                *


***********************************


char[][]  宽  高


把这个地图存储到一个二维数组中



贪吃蛇代码如下:


package com.cn.snake_Version1;
import java.awt.BorderLayout;
import java.awt.Point;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.LinkedList;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import com.cn.utils.FrameUtil;
public class SnakeGame {
/**
* 地图的宽(列数)
*/
public static final int WIDTH = 35;

/**
* 地图的高(行数)
*/
public static final int HEIGHT = 9;

/**
* 地图
*/
private char[][] background= new char[HEIGHT][WIDTH];

/**
* 使用集合保存蛇节点的所有信息
*/
LinkedList<Point> snake = new LinkedList<Point>();

/**
* 食物(坐标)
*/
Point food;

//使用四个常量表示四个方向
/**
* 蛇向上走
*/
public static final int UP_DIRECTION = 1;
/**
* 蛇向下走
*/
public static final int DOWN_DIRECTION = -1;
/**
* 蛇向左走
*/
public static final int LEFT_DIRECTION = 2;
/**
* 蛇向右走
*/
public static final int RIGHT_DIRECTION = -2;

/**
* 蛇当前的移动方向
*/
int currentDirection = -2; //默认蛇向右行走

/**
* 定义标识,判断游戏是否结束,蛇是否撞到障碍物,或者吃到自己,默认没有死亡
*/
static boolean isDead = false;

/*
*//**
* 蛇向上移动
*//*
public void moveUp(){
Point head = snake.getFirst();//取出原来的蛇头
// Point tail = snake.getLast();// 标记1 取出原来的蛇尾
snake.addFirst(new Point(head.x, head.y-1));//添加新的蛇头
snake.removeLast();//删除蛇尾
// background[tail.y][tail.x] = ' ';//放开注释标记1 有这个就不需要使用refresh()方法刷新。地图上蛇尾留下的信息清空。
}

*//**
* 蛇向右移动
*//*
public void moveRight(){
Point head = snake.getFirst();//取出原来的蛇头
// Point tail = snake.getLast();// 标记1 取出原来的蛇尾
snake.addFirst(new Point(head.x+1, head.y));//添加新的蛇头
snake.removeLast();//删除蛇尾
// background[tail.y][tail.x] = ' ';//放开注释标记1 有这个就不需要使用refresh()方法刷新。地图上蛇尾留下的信息清空。
}

*//**
* 蛇向下移动
*//*
public void moveDown(){
Point head = snake.getFirst();//取出原来的蛇头
// Point tail = snake.getLast();// 标记1 取出原来的蛇尾
snake.addFirst(new Point(head.x, head.y+1));//添加新的蛇头
snake.removeLast();//删除蛇尾
// background[tail.y][tail.x] = ' ';//放开注释标记1 有这个就不需要使用refresh()方法刷新。地图上蛇尾留下的信息清空。
}

*//**
* 蛇向左移动
*//*
public void moveLeft(){
Point head = snake.getFirst();//取出原来的蛇头
// Point tail = snake.getLast();// 标记1 取出原来的蛇尾
snake.addFirst(new Point(head.x-1, head.y));//添加新的蛇头
snake.removeLast();//删除蛇尾
// background[tail.y][tail.x] = ' ';//放开注释标记1 有这个就不需要使用refresh()方法刷新。地图上蛇尾留下的信息清空。
}
*/

/**
* 蛇移动的方法
* @param currentDirection
*/
public void move(){
Point head = snake.getFirst();//取出原来的蛇头
//蛇是根据当前方向移动的
switch (currentDirection) {
case SnakeGame.UP_DIRECTION:
snake.addFirst(new Point(head.x, head.y-1));//添加新的蛇头
break;
case SnakeGame.DOWN_DIRECTION:
snake.addFirst(new Point(head.x, head.y+1));//添加新的蛇头
break;
case SnakeGame.LEFT_DIRECTION:
if(head.x == 0){
snake.addFirst(new Point(SnakeGame.WIDTH-1, head.y));//添加新的蛇头
}else{
snake.addFirst(new Point(head.x-1, head.y));//添加新的蛇头
}
break;
case SnakeGame.RIGHT_DIRECTION:
if(head.x == SnakeGame.WIDTH-1){
snake.addFirst(new Point(0, head.y));//添加新的蛇头
}else{
snake.addFirst(new Point(head.x+1, head.y));//添加新的蛇头
}
break;
default:
break;
}

if(eatFood()){//吃到了食物
//重新生成食物
createFood();
}else{//如果没有吃到食物,删除蛇尾
snake.removeLast();//删除蛇尾
}
}

/**
* 改变当前方向的方法
* @param newDirection
*/
public void changeDirection(int newDirection){
//判断新方向是否与当前方向是否是相反方向,才允许其改变
if(newDirection + currentDirection != 0){
this.currentDirection = newDirection;
}
}

/**
* 生成食物
*/
public void createFood(){
//创建一个随机数对象
Random random = new Random();
while(true){
int x = random.nextInt(SnakeGame.WIDTH);
int y = random.nextInt(SnakeGame.HEIGHT);
if(background[y][x] != '*' && background[y][x] != '$' && background[y][x] != '#'){ //非石头,非蛇身蛇头蛇尾的地方产生食物
food = new Point(x, y);
break;
}
}
}

/**
* 显示食物
*/
public void showFood(){
background[food.y][food.x] = '@';
}


/**
*初始化蛇
*/
public void initSnake(){
//蛇开始时候的中心位置
int x = SnakeGame.WIDTH>>1; //地图的宽度除以2
int y = SnakeGame.HEIGHT>>1;//地图的高度除以2

snake.addFirst(new Point(x-1, y)); //蛇尾
snake.addFirst(new Point(x, y)); //蛇的中心
snake.addFirst(new Point(x+1, y));//蛇头
}

/**
* 显示蛇,实际上即使将蛇节点的坐标信息反馈到地图上,在地图上画上对应的字符而已
*/
public void showSnake(){
//取出蛇头
Point head = snake.getFirst();
//先画蛇身,除了蛇头之外,其余的节点都填充蛇身
for(int i=1; i<snake.size(); i++){
Point body = snake.get(i);
background[body.y][body.x] = '#';
}
//后填蛇头,这样蛇头即使咬到了蛇身也不会覆盖蛇头。
background[head.y][head.x] = '$';
}

/**
* 定义一个方法,初始化地图
*/
public void initBackground(){
//把地图,食物,障碍物等信息储存在二维数组中
for(int row=0; row<SnakeGame.HEIGHT; row++){
for(int col=0; col<SnakeGame.WIDTH; col++){
if(row == 0 || row == SnakeGame.HEIGHT-1 ){//第一行,最后一行
background[row][col] = '*';
}else{
background[row][col] = ' ';
}
}
}
}

/**
* 显示游戏的所有事物(地图,蛇,障碍物等)
*/
public void showBackground() {
//打印二维数组
for(int row=0; row<SnakeGame.HEIGHT; row++){
for(int col=0; col<SnakeGame.WIDTH; col++){
System.out.print(background[row][col]);
}
System.out.println();
}
}

/**
* 刷新游戏的状态
*/
public void refresh(){
//清空游戏之前的所有状态信息
initBackground();
//把食物的信息反馈到地图上
showFood();
//把蛇最新的状态反馈到地图上
showSnake();
//显示当前地图的信息
showBackground();
}

/**
* 蛇吃食物
* @return
*/
public boolean eatFood(){
//获取到原来的蛇头
Point head = snake.getFirst();
// if(head.x==food.x && head.y==food.y){
if(head.equals(food)){//蛇头坐标和食物坐标重合,表明吃到了食物
return true;
}
return false;
}

/**
* 判断蛇是否撞到障碍物,或者吃到自己,默认没有死亡
* @return
*/
public boolean isGameOver(){
//获取蛇头
Point head = snake.getFirst();
//判断蛇头是否撞到障碍物
if(background[head.y][head.x] == '*'){
isDead = true;
}
//判断蛇头是否吃到自己,首先获取身体节点,判断头部坐标是否与身体某节点重合
for(int i=1; i<snake.size(); i++){
Point body = snake.get(i);
if(head.equals(body)){
isDead = true;
}
}

return isDead;
}

/**
* 主函数入口
* @param args
*/
public static void main(String[] args) {
final SnakeGame snakeGame = new SnakeGame();
//初始化地图
snakeGame.initBackground();

//初始化蛇
snakeGame.initSnake();
//显示蛇
snakeGame.showSnake();

//初始化食物
snakeGame.createFood();
//显示食物
snakeGame.showFood();
snakeGame.showBackground();

final JFrame frame = new JFrame("方向盘");
frame.add(new JButton("↑"), BorderLayout.NORTH);
frame.add(new JButton("↓"), BorderLayout.SOUTH);
frame.add(new JButton("←"), BorderLayout.EAST);
frame.add(new JButton("→"), BorderLayout.WEST);

JButton button = new JButton("点击控制方向");
frame.add(button);//默认添加在中间
//自定义窗体初始化函数初始化窗体
FrameUtil.initFrame(frame, 300, 300);

//给按钮添加事件监听器
button.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
switch (code) {
case KeyEvent.VK_UP://上
snakeGame.changeDirection(SnakeGame.UP_DIRECTION);
break;
case KeyEvent.VK_DOWN://下
snakeGame.changeDirection(SnakeGame.DOWN_DIRECTION);
break;
case KeyEvent.VK_LEFT://左
snakeGame.changeDirection(SnakeGame.LEFT_DIRECTION);
break;
case KeyEvent.VK_RIGHT://右
snakeGame.changeDirection(SnakeGame.RIGHT_DIRECTION);
break;
default:
break;
}
snakeGame.move();//移动
isDead = snakeGame.isGameOver();//判断游戏是否结束
snakeGame.refresh();//这里刷新
if(isDead){//死亡
JOptionPane.showMessageDialog(frame, "游戏结束");
System.exit(0);//退出JVM
}
}
});
}
}


游戏中用到的自定义窗体工具类:

package com.cn.utils;
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.JFrame;
/**
* 自定义窗体工具
* @author zhiyong
*
*/
public class FrameUtil {
public static void initFrame(JFrame frame, int width, int height){
//获取默认工具包。
Toolkit toolkit = Toolkit.getDefaultToolkit();
//获取屏幕大小
Dimension dimension = toolkit.getScreenSize();
int x = dimension.width;
int y = dimension.height;
//窗体位置设置在屏幕中心位置
frame.setBounds((x-width)>>1, (y-height)>>1, width, height);
//设置窗体可见
frame.setVisible(true);
//设置窗体关闭事件
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

效果如下:



贪吃蛇(字符版)_游戏




贪吃蛇(字符版)_java_02




贪吃蛇(字符版)_初始化_03