前言:此项目是面向过程思想
一、Node类
package com.example.snack;
import java.awt.*;
import java.util.Objects;
/**
* 节点类:
*/
public class Node {
private int x;
private int y;
public Node() {
}
public Node(int x, int y) {
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 g
* 传递一个画笔过来
*/
public void drawNode(Graphics g,int i){
if(0==i){
g.fillOval(this.x,this.y,20-1,20-1);
}else {
g.fillRect(this.x, this.y, 20 - 1, 20 - 1);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Node node = (Node) o;
return x == node.x && y == node.y;
}
}
二、SnackFrame08类
package com.example.snack;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
/**
*1.绘制窗体内容
*展示游戏的主界面
*Frame->窗体【位置、大小、背景色、标题】
* 设置窗体可见
* 关闭窗口的事件监听
*窗口不能重新设置大小
*
* 2.绘制图形
*
* 3.绘制蛇的身体
*
* 4动起来
* 1>窗口一直重绘
* 2>修改坐标
*
*5.手动控制方向
* 1.添加键盘监听事件
* 2.根据按键修改方向
*
* 6.食物
*主体Node
* 位置:满足窗体中隐形的格子
*
* 7.蛇吃食物
* 何时吃到食物
* 蛇的身体加长
*
*8.结束游戏
* 头部碰到边界|头部碰到身体
*/
public class SnackFrame08 extends Frame {
public static final int WINDOW_X=200;
public static final int WINDOW_Y=100;
public static final int WINDOW_W=600;
public static final int WINDOW_H=500;
//上 1,下 3;右 0; 左 2
public static final int UP=1;
public static final int DOWN=3;
public static final int RIGHT=0;
public static final int LEFT=2;
private int speed =200;
//程序是否还在运行
private boolean isRunning =true;
//上 1,下 3;右 0; 左 2
private int direction = 0;
private Node food =new Node(randomNum(0,WINDOW_W/20-1)*20,randomNum(2,WINDOW_H/20-1)*20);
//蛇的主体部分
private Node[] body ={
new Node(140,100),
new Node(120,100),
new Node(100,100),
};
//构造器当中初始化窗体
public SnackFrame08(){
this.setLocation(WINDOW_X,WINDOW_Y);//窗体位置
this.setSize(WINDOW_W,WINDOW_H);//窗体的大小
this.setVisible(true);//设置窗体可见
this.setResizable(false);//不能改变窗体大小
this.setTitle("贪吃蛇V1.0");//标题
this.setBackground(Color.MAGENTA);//背景颜色
//注册窗口监听
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e){
System.exit(0);//退出虚拟机
}
});
//注册键盘监听
this.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
//System.out.println(e.getKeyCode());
//System.out.println(KeyEvent.VK_DOWN);
//上 1,下 3;右 0; 左 2
int dir=-1;
switch (e.getKeyCode()){
case KeyEvent.VK_UP://向上走
dir=UP;
break;
case KeyEvent.VK_DOWN://向下走
dir =DOWN;
break;
case KeyEvent.VK_RIGHT://向右走
dir=RIGHT;
break;
case KeyEvent.VK_LEFT://向左走
dir =LEFT;
break;
}
//根据合法性修改方向(不允许在同一条直线修改)
if(direction%2 != dir%2){
direction = dir;
}
}
});
start();
}
/**
* * 为绘制蛇的主题做准备
* 窗体显式|更新的时候 会自动调用
* 绘制图形、线条、文本
* @param g 画笔
*/
@Override
public void paint(Graphics g) {
g.setColor(Color.ORANGE);
//绘制蛇的主体部分
for(int i=0;i<body.length;i++){
body[i].drawNode(g,i);
}
g.setColor(Color.BLUE);
food.drawNode(g,-1);
if(isRunning){
if(isEat()){
//System.out.println("吃到食物");
addBody();
}else{
move();//修改坐标
}
isOver();
}else {
//System.out.println("game over");
g.setColor(new Color(29, 65, 208));
g.setFont(new Font(null,Font.ITALIC,100));
g.drawString("gameover",WINDOW_W/2-200-20,WINDOW_H/2);
}
}
/**
* 游戏是否结束
* @return true 游戏结束 false 游戏继续
*/
public void isOver(){
Node head =body[0];
//边界
if(head.getX()<20 || head.getX()>=WINDOW_W-20||head.getY()<30 || head.getY()>=WINDOW_H-20){
isRunning =false;
}
//头碰到身体
for(int i =1;i<body.length;i++){
if(head.equals(body[i])){
isRunning =false;
}
}
}
/**
* 吃到食物了,增加蛇的身体
* 将食物变为蛇的头部
*食物重新产生
* 食物消失
*/
public void addBody(){
Node[] temp =new Node[body.length+1];
temp[0] =food;
System.arraycopy(body,0,temp,1,body.length);
body=temp;
food =null;
productFood();
}
/**
* 是否吃到食物:下一步的头结点的位置 刚好和食物位置相等
*/
public boolean isEat(){
Node newHead =getNewHead();
if(newHead.equals(food)){
return true;
}
return false;
}
/**
* 随机产生食物
*/
public void productFood(){
food=new Node(randomNum(0,WINDOW_W/20-1)*20,randomNum(2,WINDOW_H/20-1)*20);
}
/**
* 通过修改坐标来达到移动的效果
*/
public void move(){
//向右移动
//每一次通过一个新的头节点,去掉尾节点即可
Node[] temp =new Node[body.length];//创建一个新的数组,里面是空的
temp[0] =getNewHead();//头部里的第一个结点通过方向确定新节点的位置
//去掉尾部
System.arraycopy(body,0,temp,1,body.length-1);
//把body里面新节点的内容拷贝
this.body=temp;
}
/**
* 根据方向来确定新的头结点
* @return
*/
public Node getNewHead(){
Node newNode =body[0];
switch(this.direction){
//上 1,下 3;右 0; 左 2
case 0:
newNode =new Node(newNode.getX()+20,newNode.getY());
break;
case 2:
newNode =new Node(newNode.getX()-20,newNode.getY());
break;
case 1:
newNode =new Node(newNode.getX(),newNode.getY()-20);
break;
case 3:
newNode =new Node(newNode.getX(),newNode.getY()+20);
break;
}
return newNode;
}
/**
* 手动一直重绘窗体
*/
public void start(){
while(isRunning){
super.repaint();
try {
Thread.sleep(speed);//暂停事件
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
super.repaint();
}
public static int randomNum(int min,int max){
return (int)(Math.random()*(max-min+1)+min);
}
public static void main(String[] args) {
new SnackFrame08();
}
}