package Update;
/*
枚举:就是定义几个常量
*/
public enum Direction {
UP,DOWN,LEFT,RIGHT
}
package Update;
/*
结点类:
每一条蛇是由若干个结点组成的链表集合,每一个结点通过横纵坐标来确定位置
食物是棋盘中单独的、随机的结点组成的集合
*/
public class Node {
int x;//结点的横坐标
int y;//结点的纵坐标
Node next;//指向下一个结点
Node prev;//指向上一个结点
public Node() {
this.next = null;
}
public Node(int x, int y) {
this.x = x;
this.y = y;
this.next = null;
}
public Node(int x, int y, Node prev, Node next) {
this.x = x;
this.y = y;
this.prev=prev;
this.next = next;
}
}
package Update;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
/*
食物的本质是结点的集合
*/
public class Food {
LinkedList<Node> foodList;
public Food(){
initFood();//初始化食物
}
private void initFood() {
foodList=new LinkedList<>();
Random r=new Random();
for(int i=0;i<40;i++){//初始化食物链表,有40个食物
foodList.offer(new Node(r.nextInt(39),r.nextInt(38)));
}
}
}
package Update;
import java.util.ArrayList;
import java.util.Random;
/*
怪物类:本质上是结点类的集合
*/
public class Monster {
ArrayList<Node>monsterList=new ArrayList<>();
public Monster(){
initMonster();//初始化小怪物集合
}
private void initMonster() {//一共有10个小怪物,小怪物的位置持续改变
for(int i=0;i<10;i++){
Random r=new Random();
monsterList.add(new Node(r.nextInt(40),r.nextInt(40)));
}
}
public void changePosition(){//小怪物改变位置
for(int i=0;i<monsterList.size();i++){
Node temp=monsterList.get(i);
Random r=new Random();
temp.x=r.nextInt(40);
temp.y=r.nextInt(40);
}
}
}
package Update;
/*
链表类:这是蛇身的储存结构
*/
public class SnackBody {
Node head=new Node(666,888,null,null);//不用来储存元素,作为哨兵结点,就不用考虑链表为空的情况
/*
add方法:就是在尾部添加结点的方法
*/
public void add(Node add){
Node p=findLast();
p.next=new Node(add.x,add.y,p,null);
}
/*
addFirst方法
*/
public void addFirst(Node add){
Node p=new Node(add.x,add.y,head,head.next);
head.next.prev=p;
head.next=p;
}
/*
寻找最后一个结点的方法
*/
public Node findLast() {
//链表非空
Node p;
for(p=head;p.next!=null;p=p.next){}
return p;
}
public Node getFirst(){
if(head.next!=null) {
return head.next;
}else{
throw new IllegalArgumentException() ;
}
}
public void removeLast(){
Node last=findLast();
Node latter=last.prev;
latter.next=null;
last.prev=null;
}
public int size(){
int i;
Node p;
for(p=head.next,i=0;p!=null;p=p.next,i++){
}
return i;
}
public Node get(int index){
int i;
Node p;
for(p=head.next,i=0;p!=null;p=p.next,i++){
if(i==index){
return p;
}
}
return null;
}
}
package Update;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
public class Snack {
SnackBody snackBody;
Direction direction = Direction.LEFT;//将蛇头的移动方向默认为左边
boolean isLiving = true;//蛇是否还活着
Queue<int[]> path=new LinkedList<>();//队列用于储存贪吃蛇的运动轨迹
int grade=0;//贪吃蛇的得分
/*
存活状态下:贪吃蛇发生了移动过程,就会将此时的横纵坐标储存到路径当中
一旦贪吃蛇死亡,就把队列中的所有元素倒出
*/
public Snack() {
initSnack();//初始化蛇的方法
}
//初始化蛇的方法
private void initSnack() {
//蛇出生的时候有6个结点
snackBody = new SnackBody();
snackBody.add(new Node(16, 20));
snackBody.add(new Node(17, 20));
snackBody.add(new Node(18, 20));
snackBody.add(new Node(19, 20));
snackBody.add(new Node(20, 20));
snackBody.add(new Node(21, 20));
}
/*
蛇��进行移动的方法move:
沿着蛇头的方向移动
具体实现就是蛇在蛇头移动的方向添加一个新的结点,在蛇的尾部删除一个结点
*/
public void move() {
//蛇是活着的可以移动
if (isLiving) {
Node head = snackBody.getFirst();//获取蛇头
switch (direction) {
case UP: {//蛇头移动方向向上,在蛇头的上面添加结点,在蛇尾删除结点:对snackBody操作
snackBody.addFirst(new Node(head.x, head.y - 1));//添加
snackBody.removeLast();//删除
break;
}
case DOWN: {//蛇头移动方向向下,在蛇头的下面添加结点,在蛇尾删除结点:对snackBody操作
snackBody.addFirst(new Node(head.x, head.y + 1));//添加
snackBody.removeLast();//删除
break;
}
case LEFT: {//蛇头移动方向向左,在蛇头的左边添加结点,在蛇尾删除结点:对snackBody操作
snackBody.addFirst(new Node(head.x - 1, head.y));//添加
snackBody.removeLast();//删除
break;
}
case RIGHT: {//蛇头移动方向向右,在蛇头的右边添加结点,在蛇尾删除结点:对snackBody操作
snackBody.addFirst(new Node(head.x + 1, head.y));//添加
snackBody.removeLast();//删除
break;
}
}
int[]arr={head.x, head.y};
path.offer(arr);
}
Node head = snackBody.getFirst();//获取蛇头
//判断是否撞墙:x<0撞到左墙壁,x>39撞到右墙壁,y<0撞到上墙壁,y>39撞到下墙壁
if (head.x <=0 || head.x >= 39 || head.y <=0 || head.y >=39) {
isLiving = false;//蛇死了,无法移动
}
//判断蛇头和身体的其他部分是否发生重合,如果撞到自己的身体也会死
for (int i = 1; i < snackBody.size(); i++) {
if (head.x == snackBody.get(i).x && head.y == snackBody.get(i).y) {
isLiving = false;//蛇死了,无法移动
}
}
}
public void eat(Node food) {//沿着蛇头的移动方向添加一个结点
Node head = snackBody.getFirst();//获取蛇头
switch (direction) {
case UP: {//蛇头移动方向向上,在蛇头的上面添加结点
snackBody.addFirst(new Node(head.x, head.y - 1));//添加
break;
}
case DOWN: {//蛇头移动方向向下,在蛇头的下面添加结点
snackBody.addFirst(new Node(head.x, head.y + 1));//添加
break;
}
case LEFT: {//蛇头移动方向向左,在蛇头的左边添加结点
snackBody.addFirst(new Node(head.x - 1, head.y));//添加
break;
}
case RIGHT: {//蛇头移动方向向右,在蛇头的右边添加结点
snackBody.addFirst(new Node(head.x + 1, head.y));//添加
break;
}
}
//改变被吃结点的位置
Random r=new Random();
food.x=r.nextInt(39);
food.y=r.nextInt(39);
}
}
package Update;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.LinkedList;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
public class MainJFrame extends JFrame{
JPanel jp;//游戏棋盘
private Snack snack;//蛇
Timer timer;//创建定时器,在指定的时间内调用蛇移动的方法
Timer timer1;//创建定时器,在指定的时间内调用改变怪兽位置的方法
Food food;//创建食物链表
Monster monster;//创建怪物链表
public MainJFrame(){
initFrame();//初始化窗体参数的方法
initGamePanel();//初始化棋盘画布的方法
initSnack();//初始化蛇
initTimer();//初始化定时器
setkeyListener();//设置键盘监听,让蛇根据键盘进行上下左右移动
initFood();//初始化食物
initMonster();//初始化怪物
}
/*
初始化窗体的方法
*/
private void initFrame() {
setSize(610,640);//设置窗体的大小
setLocation(400,200);//设置窗体的位置,左上角距离左边400,距离上边100
setResizable(false);//固定窗体的大小
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置关闭按钮就是退出程序
}
/*
初始化棋盘画布的方法:棋盘的横线,竖线,蛇,食物
*/
private void initGamePanel() {
jp=new JPanel(){//匿名内部类:用来绘制棋盘画布的内容
@Override
public void paint(Graphics g) {//g相当于画笔,可以用来绘制基本图形,如直线、矩形
//清空棋盘:对应重绘棋盘repaint()
g.clearRect(0,0,600,600);
//画出41条横线,有40个格子
for(int i=0;i<=40;i++){
g.drawLine(0,15*i,600,15*i);
}
//画出41条竖线,有1600个15*15的格子
for(int j=0;j<=40;j++){
g.drawLine(15*j,0,15*j,600);
}
//绘制蛇(由结点Node组成):绘制矩形
SnackBody body=snack.snackBody;//得到蛇的身体
for(int i=0;i<body.size();i++) {//遍历蛇的身体的每个结点
Node node=body.get(i);
int X = node.x;
int Y = node.y;
g.fillRect(node.x * 15, node.y * 15, 15, 15); //用画笔绘制填充矩形
}
//绘制食物
for (int i = 0; i < food.foodList.size(); i++) {
g.fillRect(food.foodList.get(i).x*15,food.foodList.get(i).y*15,15,15);
}
//绘制小怪兽
for(int i=0;i<monster.monsterList.size();i++){
g.fillRect(monster.monsterList.get(i).x*15,monster.monsterList.get(i).y*15,15,15);
g.setColor(Color.BLUE);//小怪兽的颜色是蓝色
}
}
};
add(jp);//将画布对象添加到窗体中去
}
/*
初始化蛇的方法
*/
private void initSnack() {
snack=new Snack();//创建蛇的对象
}
private void initMonster(){
monster=new Monster();//创建怪物对象
}
/*
初始化定时器的方法
*/
private void initTimer() {
timer=new Timer();//创建定时器对象
timer1=new Timer();//创建定时器对象
//初始化定时任务:在指定时间内调用蛇的移动方法
TimerTask timerTask=new TimerTask() {
@Override
public void run() {
snack.move();
Node head=snack.snackBody.getFirst();
//判断蛇头是否和食物重合
if(food!=null) {
for (Node node : food.foodList) {
if (head != null && node != null && head.x == node.x && head.y == node.y) {//蛇头和食物重合
snack.eat(node);//蛇吃食物,蛇的长度增加
//改变被吃结点的位置
Random r = new Random();
node.x = r.nextInt(39);
node.y = r.nextInt(39);
snack.grade+=1;//贪吃蛇的得分
}
}
}
//判断蛇头是否与小怪物重合:重合就会死
if(monster!=null&&monster.monsterList!=null) {
for (int i = 0; i < monster.monsterList.size(); i++) {
if (head != null && monster.monsterList.get(i) != null && head.x == monster.monsterList.get(i).x && head.y == monster.monsterList.get(i).y) {
snack.isLiving = false;
}
}
}
//界面的更新,重绘游戏棋盘
jp.repaint();
}
};
TimerTask timerTask1=new TimerTask() {
@Override
public void run() {
if(monster!=null&&monster.monsterList!=null&&snack.isLiving) {
monster.changePosition();
}
}
};
timer1.scheduleAtFixedRate(timerTask1,0,5000);
//表示该定时器没有任何延迟地每150ms执行一次设定的蛇的移动的任务
timer.scheduleAtFixedRate(timerTask,0,150);
}
/*
设置键盘监听的方法:重写键盘按下的操作
1.当键盘按下时,会自动调用move方法
2.键盘中每一个键都有编号,只需要获取?8?8?1?5?8?9?1?5?8?7?1?5?7?9?1?5键的编号,改变蛇的方向
3.注意蛇不能连续向两个相反的方向进行运动:if判断就行
*/
private void setkeyListener() {
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {//e是键盘的键
switch(e.getKeyCode()){
case 37:{//表示e是左键,把蛇的direction改成左
if(snack.direction!= Direction.RIGHT) {//蛇目前的运动方向不是向右,否则就会出现倒着走的情况
snack.direction = Direction.LEFT;
}
break;
}
case 38:{//表示e是上键,把蛇的direction改成上
if(snack.direction!= Direction.DOWN) { //蛇目前的运动方向不是向右,否则就会出现倒着走的情况
snack.direction = Direction.UP;
}
break;
}
case 39:{//表示e是右键,把蛇的方向改成右
if(snack.direction!= Direction.LEFT) {
snack.direction = Direction.RIGHT;//蛇目前的运动方向不是向左,否则就会出现倒着走的情况
}
break;
}
case 40:{//表示e是下键,把蛇的方向改成下
if(snack.direction!= Direction.UP) {//蛇目前的运动方向不是向上,否则就会出现倒着走的情况
snack.direction = Direction.DOWN;
}
break;
}
}
}
});
}
/*
初始化食物的方法
*/
private void initFood() {
food=new Food();//创建食物对象
}
public static void main(String[] args) {
MainJFrame mj=new MainJFrame();//创建窗体对象
mj.setVisible(true);//将窗体对象设置为可见
}
}
package Update;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
/*
食物的本质是结点的集合
*/
public class Food {
LinkedList<Node> foodList;
public Food(){
initFood();//初始化食物
}
private void initFood() {
foodList=new LinkedList<>();
Random r=new Random();
for(int i=0;i<40;i++){//初始化食物链表,有40个食物
foodList.offer(new Node(r.nextInt(39),r.nextInt(38)));
}
}
}