分析里面的有几个事物:
1. 地图
2. 蛇
3. 食物
了解游戏规则:
1.蛇初始化的时候是三个节点,而且一开始蛇是出现在地图的中间位置。
2. 蛇吃到东西会长长一节。
3. 蛇咬到自己会死,蛇撞墙也会死。
4. 食物是随机生成的,食物不能长在石头上,食物可以长在蛇身上。
效果如下:
代码如下:
package com.cn.snake_Version2;
import java.awt.Color;
import java.awt.Graphics;
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.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import com.cn.utils.FrameUtil;
public class SnakeGame extends JPanel{
/**
* 地图的宽(列数)
*/
public static final int WIDTH = 30;
/**
* 地图的高(行数)
*/
public static final int HEIGHT = 20;
/**
* 格子的宽
*/
public static final int CELLWIDTH = 20;
/**
* 格子的高
*/
public static final int CELLHEIGHT = 20;
/**
* 地图
*/
private boolean[][] background= new boolean[HEIGHT][WIDTH];
/**
* 使用集合保存蛇节点的所有信息
*/
LinkedList<Point> snake = new LinkedList<Point>();
//使用四个常量表示四个方向
/**
* 蛇向上走
*/
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 canMoving = true; //默认蛇能移动
/**
* 定义标识,判断游戏是否结束,蛇是否撞到障碍物,或者吃到自己,默认没有死亡
*/
static boolean isDead = false;
/**
* 食物(坐标)
*/
Point food;
/**
* 定义一个方法,初始化地图
*/
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] = true;
}
}
}
}
/**
*初始化蛇
*/
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));//蛇头
}
/**
* 改变当前方向的方法
* @param newDirection
*/
public void changeDirection(int newDirection){
//判断新方向是否与当前方向是否是相反方向,不相反才允许其改变
if(newDirection + currentDirection != 0){
this.currentDirection = newDirection;
canMoving = true;
}else{//新方向是否与当前方向相反,蛇不能移动
canMoving = false;
}
}
/**
* 蛇吃食物
* @return
*/
public boolean eatFood(){
//获取到原来的蛇头
Point head = snake.getFirst();
if(head.equals(food)){//蛇头坐标和食物坐标重合,表明吃到了食物
return true;
}
return false;
}
/**
* 生成食物
*/
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] != true){ //非石头的地方产生食物
food = new Point(x, y);
break;
}
}
}
/**
* 蛇移动的方法
* @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();//删除蛇尾
}
}
/**
* 判断蛇是否撞到障碍物,或者吃到自己,默认没有死亡
* @return
*/
public boolean isGameOver(){
//获取蛇头
Point head = snake.getFirst();
//判断蛇头是否撞到障碍物
if(background[head.y][head.x] == true){
isDead = true;
}
//判断蛇头是否吃到自己,首先获取身体节点,判断头部坐标是否与身体某节点重合
for(int i=1; i<snake.size(); i++){
Point body = snake.get(i);
if(head.equals(body)){
isDead = true;
}
}
return isDead;
}
@Override
public void paint(Graphics g) {
/**
* 画地图
*/
for(int row=0; row<SnakeGame.HEIGHT; row++){
for(int col=0; col<SnakeGame.WIDTH; col++){
if(background[row][col]){//石头
g.setColor(Color.GRAY);
}else{//空地
g.setColor(Color.white);
}
//画矩形格子
g.fill3DRect(col*CELLWIDTH, row*CELLHEIGHT, CELLWIDTH, CELLHEIGHT, true);
}
}
/**
* 画蛇
*/
//取出蛇头
Point head = snake.getFirst();
g.setColor(Color.green);
//先画蛇身,除了蛇头之外,其余的节点都填充蛇身
for(int i=1; i<snake.size(); i++){
Point body = snake.get(i);
g.fill3DRect(body.x*CELLWIDTH, body.y*CELLHEIGHT, CELLWIDTH, CELLHEIGHT, true);
}
//后填蛇头,这样蛇头即使咬到了蛇身也不会覆盖蛇头。
g.setColor(Color.RED);
g.fill3DRect(head.x*CELLWIDTH, head.y*CELLHEIGHT, CELLWIDTH, CELLHEIGHT, true);
/**
* 画食物
*/
g.setColor(Color.YELLOW);
g.fill3DRect(food.x*CELLWIDTH, food.y*CELLHEIGHT, CELLWIDTH, CELLHEIGHT, true);
}
public static void main(String[] args) {
final JFrame frame = new JFrame("贪吃蛇 【木丁西1012421396】");
final SnakeGame snakeGUI = new SnakeGame();
snakeGUI.initBackground();
snakeGUI.initSnake();
snakeGUI.createFood();
frame.add(snakeGUI);
FrameUtil.initFrame(frame, WIDTH*CELLWIDTH+17, HEIGHT*CELLHEIGHT+40);
//给窗口添加事件监听
frame.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
switch (code) {
case KeyEvent.VK_UP://上
snakeGUI.changeDirection(SnakeGame.UP_DIRECTION);
break;
case KeyEvent.VK_DOWN://下
snakeGUI.changeDirection(SnakeGame.DOWN_DIRECTION);
break;
case KeyEvent.VK_LEFT://左
snakeGUI.changeDirection(SnakeGame.LEFT_DIRECTION);
break;
case KeyEvent.VK_RIGHT://右
snakeGUI.changeDirection(SnakeGame.RIGHT_DIRECTION);
break;
default:
break;
}
if(canMoving)//方向正确能移动
snakeGUI.move();//移动
isDead = snakeGUI.isGameOver();//判断游戏是否结束
snakeGUI.repaint();//刷新,调用repaint方法的时候实际上就是调用了paint方法
if(isDead){//死亡
JOptionPane.showMessageDialog(frame, "游戏结束", "游戏结束提示", JOptionPane.ERROR_MESSAGE);
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);
}
}