贪吃蛇(字符版)

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

分析里面的有几个事物:
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);
	}
}
效果如下:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值