Java写一条吃满屏幕的贪吃蛇(A*自动寻路算法和一些简单的策略) 一

    一个月没有写博客,中间其实学了不少东西,因为是自学,所以进度很慢,同时在看Java核心技术还有李刚的疯狂Java,这两本书讲的很详细,另外也学着看一点源代码。特别是IO流的部分,类太多让人比较晕,一连学了快一个星期,看了一点源代码,最后才缕清了思路。

    之前学到list,发现这个ArrayList真是一个好东西,不用像数组一样预先要声明空间大小,可以动态的增长和缩减,可以保存任意类型的引用数据类型,当然基本数据类型可以自动拆装箱,所以也很方便。那一天学着学着突然福至心灵(没错就是这样),随手写了一个贪吃蛇,因为那一天在看内部类,匿名内部类这些知识。

    最开始的版本,就是一个控制台程序,思路是创建一个贪吃蛇类,贪吃蛇类里面有一个节点内部类,模拟贪吃蛇是有很多节点组成的,然后有一个食物类,只要保存食物的坐标系就好了。游戏流程是:贪吃蛇在一个范围内游走寻找食物,“吃”到食物后就将自身增加一个节点,当贪吃蛇吃满屏幕或者撞到障碍物(蛇自己和墙壁)游戏结束。

    首先要实现蛇的运动,其实就是改变蛇的每一个节点的坐标,蛇头往某一个方向前进一步(上下左右),蛇头后面一个节点的坐标更新为原来蛇头的坐标,第三节坐标更新成第二节的,第n节坐标更新为n-1节的坐标,这样蛇就移动啦,当然你也可以改进这个思路,比如蛇前进就在蛇的头节点前面增加一个节点,尾巴减少一个节点,减少一点运算量,当初没有想到,就用了最野蛮的一种方法,看官老爷见谅。

    那么蛇怎么吃东西呢?也很简单,分为三部分:1.检测到食物 2.蛇吃食物 3.刷新食物坐标         具体的实验细节,检测食物其实就是当蛇头部坐标和食物坐标重合的时候,我们认为找到了食物; 而吃到食物就是蛇这个类内部增加一个节点类对象,具体来说就是ArrayList 里面添加一个 节点对象; 刷新食物坐标只要用到random类就好了,但是注意的点是食物不能刷新到蛇身上去。

    最后检测蛇有没有撞到自己那就要遍历蛇身每一个坐标了,检测蛇头坐标有没有和蛇身重合,如果有那就是游戏结束,如果

没有游戏继续。以上主要分析给第一次写贪吃蛇的看官老爷听,实现的代码如下:

1.蛇简化版本:

package com.ycs.LinkList;

import java.util.ArrayList;
import java.util.Scanner;

public class Snake {
	/**
	 * 成员变量
	 * x:当前头部 x 坐标
	 * y:当前头部 y 坐标
	 * length:蛇的长度
	 * len:蛇能活动的区域长度
	 * width:蛇能够活动的区域宽度
	 * pace:蛇每一次前进的长度
	 * snake:模拟一条蛇
	 */
	private int x;
	private int y;
	private int length;
	private int len;
	private int width;
	private int pace;
	private ArrayList snake = new ArrayList();
/**
 * 构造函数,完成蛇的初始化
 * @param  x     [蛇初始x坐标]
 * @param  y     [蛇初始y坐标]
 * @param  len   [蛇活动区域长度]
 * @param  width [活动区域宽度]
 * @return       [description]
 */
	public Snake(int x, int y, int len, int width) {
		super();
		this.x = x;
		this.y = y;
		this.len = len;
		this.width = width;
		this.pace=1;
		snake.add(new Node(x, y));
	}

/**
 * 内部类,蛇的每一节,仅仅保存每一节的x,y坐标
 */
	private class Node {
		private int x;
		private int y;

		public Node(int x, int y) {
			super();
			length++;
			this.x = x;
			this.y = y;
		}

		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;
		}

	}
/**
 * 蛇吃食物的函数
 * @param  fd [食物对象]
 * @return    [吃成功true,失败false]
 */
	public boolean eat(Food fd) {
		if (fd.getX() == this.x && fd.getY() == this.y) {
		//添加新的节点,新节点位置不确定,暂时设为-1 -1
			snake.add(new Node(-1, -1));
			return true;
		}
		return false;
	}
/**
 * 检测有没有和i,j坐标重合
 * @param  i [任意的横坐标]
 * @param  j [任意的纵坐标]
 * @return   [重合返回重合的蛇节点编号,不重合返回-1]
 */
	public int show(int i, int j) {
		int num = 0;
		for (Node node : snake) {
			if (node.getX() == i && node.getY() == j) {
				// System.out.println("冲突检测");
				return num;
			}
			num++;
		}
		return -1;
	}

	/**
	 * 根据字符串控制蛇的前进方向
	 * @param wasd [键盘输入的字符串,但只有“w”“a”“s”“d”能被识别]
	 */
	public void run(String wasd) {

		switch (wasd.toLowerCase()) {
		case "w":
			this.direction = wasd;
			this.y = this.y <= 0 ? this.width - this.pace : this.y - this.pace;
			break;
		case "s":
			this.direction = wasd;
			this.y = this.y >= this.width - this.pace ? 0 : this.y + this.pace;
			break;
		case "a":
			this.direction = wasd;
			this.x = this.x <= 0 ? this.len - this.pace : this.x - this.pace;
			break;
		case "d":
			this.direction = wasd;
			this.x = this.x >= this.len - this.pace ? 0 : this.x + this.pace;
			break;
		default:
			switch (this.direction) {
			case "w":
				this.y = this.y <= 0 ? this.width - this.pace : this.y - this.pace;
				break;
			case "s":
				this.y = this.y >= this.width - this.pace ? 0 : this.y + this.pace;
				break;
			case "a":
				this.x = this.x <= 0 ? this.len - this.pace : this.x - this.pace;
				break;
			case "d":
				this.x = this.x >= this.len - this.pace ? 0 : this.x + this.pace;
				break;
			}
			break;
		}
		moveNode(this.x,this.y);
		
	}

	/**
	 * 移动蛇的函数,可以移动蛇身,并且可以判断有没有撞上自己
	 * @param nextHeadX [下一步x坐标]
	 * @param nextHeadY [下一步y坐标]
	 */
	public void moveNode(int nextHeadX, int nextHeadY) {

		this.x = nextHeadX;
		this.y = nextHeadY;
		int alterX = nextHeadX, alterY = nextHeadY;
		for (Node node : snake) {
			int tmpX = node.getX();
			int tmpY = node.getY();

			if (nextHeadX == tmpX && nextHeadY == tmpY) {
				System.out.println("游戏结束!你撞到自己了");
				System.out.println(tmpX + "," + tmpY + "---" + this.x + "," + this.y);			
				System.exit(0);
			}
			node.setX(alterX);
			node.setY(alterY);
			alterX = tmpX;
			alterY = tmpY;
		}
	}

	public int getX() {
		return x;
	}

	public void setWidth(int width) {
		this.width = width;
	}

	public void setLength(int length) {
		this.len = length;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}

	

}

2.食物类

package com.ycs.LinkList;

public class Food {
	private int x;
	private int y;
	
	
	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;
	}
	public Food(int x, int y) {
		super();
		this.x = x;
		this.y = y;
		
	}
	
}

3.游戏运行类

package com.ycs.LinkList;

import java.io.IOException;
import java.util.Random;
import java.util.Scanner;

public class Play {
	private static int length =20;
	private static int width =20;
	public static void show(Snake sn,Food fd){
		
		for (int j = 0; j < length; j++) {
			for (int i = 0; i < width; i++) {
				if(i==fd.getX()&&j==fd.getY()){
					System.out.print("1");
				}else if(sn.show(i, j))	{
					System.out.print("\1");
				}else
				System.out.print(" ");
			}
			System.out.println();
		}
	}
	/**
	 * 开始游戏类,只能输入w a s d,输完你要按回车,水平有限,我没有实现直接接收键值的功能
	 * @param args [系统传参,在dos窗口可以把参数传过来,很少用]
	 */
	public static void main(String[] args)  {
		try {
			Random random = new Random();
			Snake snake = new Snake(9,9,20,20);
			Food fd = new Food(random.nextInt(length),random.nextInt(width));
		
			Scanner sc = new Scanner(System.in);
			String wasd="w";
			while(!wasd.equals("e")){
				
				if(System.in.available()>0)
				{
					wasd = sc.nextLine();
					
					System.out.println(wasd);
				}
				long st = System.currentTimeMillis();
				snake.run(wasd);
				if(snake.eat(fd)){
					int fx = random.nextInt(length);
					int fy = random.nextInt(width);
					while(snake.show(fx, fy))
					{
						fx = random.nextInt(length);
						fy = random.nextInt(width);
					//System.out.println("那啥子!");
					}
					fd.setX(fx);
					fd.setY(fy);
				}
				show(snake,fd);
				long end = System.currentTimeMillis();
				while(end-st<500){
					end = System.currentTimeMillis();
				}
			}
			sc.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

若上述代码不能运行,请见谅,后面改动比较大,这里只是一个简单的demo,继续看下一篇,代码将保证能运行,而且会用上图形化界面,其实写贪吃蛇我只是在用一个数据结构,并不是为了游戏,所以界面粗糙点,下一章将会介绍贪吃蛇自动吃食物的算法,有两种算法贪婪算法和A*算法,我会逐步介绍两种算法并且通过一些策略让贪吃蛇实现吃满屏幕。效果如下:


你可能感兴趣的:(Java写的小玩意)