Compare commits

...

No commits in common. 'main' and 'master' have entirely different histories.
main ... master

@ -0,0 +1 @@
Hello Git!

@ -1,2 +0,0 @@
# tes

@ -1,301 +0,0 @@
package com.black.play.entity;
import com.black.play.component.SnakeWindow;
import com.black.play.config.AppConfig;
import com.black.play.constant.AppConstant;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.geom.RoundRectangle2D;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
/**
*
* JPanel Food
*
*
* @author ylx
*/
public abstract class Snake extends JPanel {
// 蛇头坐标x
protected int headX;
// 蛇头坐标y
protected int headY;
// 蛇身长度
protected int length;
// 蛇身节点链表
protected LinkedList<Food> body;
// 下一食物
protected Food food;
// 蛇移动方向
protected Direction direction;
// 蛇移动定时器
protected Timer timer;
public int[][] offsets;
public static final int WIDTH = AppConfig.getInt("window.width");
public static final int HEIGHT = AppConfig.getInt("window.height");
public static final int INIT_LENGTH = AppConfig.getOrDefault("snake.init.length", 4);
public static final int MAX_DELAY = AppConfig.getInt("snake.move.max.delay");
public static final int MIN_DELAY = AppConfig.getInt("snake.move.min.delay");
public static final Random RANDOM = new Random();
// 匿名内部类加载配置
public static final Dimension NODE_SIZE = new Dimension(20, 20) {
{
int width = AppConfig.getInt("snake.node.width");
int height = AppConfig.getInt("snake.node.height");
if (width!=0 && height!=0) {
this.width = width;
this.height = height;
}
// 获取网格大小 该配置会覆盖节点大小
// 若不使用改配置 则空配置或配置0
int numX = AppConfig.getInt("snake.grid.numX");
int numY = AppConfig.getInt("snake.grid.numY");
if (numX!=0 && numY!=0) {
this.width = WIDTH / numX;
this.height = HEIGHT / numY;
}
}
};
/**
*
*/
public Snake() {
// 初始化蛇身节点链表
this.body = new LinkedList<>();
// 初始化移动定时器
this.timer = new Timer(MAX_DELAY, this::move);
// 不适用JavaSwing布局管理器 采用(x,y)坐标定位布局
// 默认布局管理器是FlowLayout
// 坐标x→递增 y↓递增 窗口左上角为原点(0,0)
this.setLayout(null);
// 设置画板大小铺满窗口
this.setPreferredSize(new Dimension(WIDTH, HEIGHT));
this.setBackground(SnakeWindow.BACKGROUND);
}
/**
*
*/
public void init() {
// 初始化蛇身长度 由配置决定
length = INIT_LENGTH;
if (length <= 0) {
throw new RuntimeException(AppConstant.INIT_ERROR);
}
// 初始化更新窗口内容
SnakeWindow instance = SnakeWindow.getInstance();
instance.updateLength(length);
instance.updateScore(0);
instance.updateSpeed((double) 1000 / MAX_DELAY);
// 清空蛇体列表
body.clear();
// 随机初始化蛇头坐标
Food head = randomFood();
headX = head.getX();
headY = head.getY();
// 设置头颜色并添加到蛇身第一个元素
head.setColor(Food.HEAD_COLOR);
body.add(head);
// 随机方向继续初始化蛇体
Direction[] directions = Direction.values();
init(head, directions, 1);
// 随机初始化下一食物
food = randomFood();
}
/**
*
*
*
*
* @return
*/
protected abstract Food randomFood();
/**
*
*
*
* @param food
* @param directions
*/
protected abstract void init(Food food, Direction[] directions, int length);
/**
*
*
* @param foods ( ; body)
* @param food
* @param direction
* @return , null
*/
public abstract Food getNextByDirection(List<Food> foods, Food food, Direction direction);
public abstract Direction getDirectionByNext();
/**
* ||
*/
public void start() {
timer.start();
}
/**
* ||
*/
public void stop() {
timer.stop();
SnakeWindow window = SnakeWindow.getInstance();
window.stopClock();
window.showCard(AppConstant.MENU);
}
/**
* Esc
*/
public void pause() {
timer.stop();
}
/**
*
*
* @param direction
*/
public void changeDirection(Direction direction) {
if (body.isEmpty()) {
return;
}
if (body.size()==1) {
this.direction = direction;
move();
start();
return;
}
// 获取蛇头和蛇身第二节
Food head = body.getFirst();
Food second = body.get(1);
// 获取该方向下一节点
Food next = getNextByDirection(body, head, direction);
// 如果第二节点和该方向下一节点不同 则改变方向并移动一次
// 否则不做改变 即不允许180°调头走
if (!second.touchTo(next)) {
this.direction = direction;
move();
}
// 到此没有对方向赋值说明是起步就进行180°掉头
// 赋予反向启动方向
if (this.direction==null) {
this.direction = direction.reverse();
}
// 启动定时器
if (!timer.isRunning()) {
start();
}
SnakeWindow.getInstance().startClock();
}
/**
*
*
* @param e
*/
public synchronized void move(ActionEvent... e) {
if (direction==null || body.isEmpty()) {
return;
}
// 根据方向移动蛇头坐标
int ordinal = direction.ordinal();
int[] offset = offsets[ordinal];
headX += offset[0];
headY += offset[1];
// 根据判断是否碰壁或自身
if (touchSelfOrWall()) {
stop();
return;
}
// 判断是否碰到食物
if (touchFood()) {
addBody();
} else {
moveBody();
}
updateUI();
}
/**
*
*
* @return
*/
protected boolean touchSelfOrWall() {
// 获取蛇头和尾部
Food head = body.getFirst();
Food last = body.getLast();
// 根据头部和移动方向 获取该方向在蛇体上的相邻点
Food nextOnBody = getNextByDirection(body, head, direction);
// 如果该方向在蛇体上存在节点 并且 不是尾部节点 则说明吃到自身 判定死亡
if (nextOnBody!=null) {
if (nextOnBody.equals(last)) {
Direction lastDirection = getDirectionByNext();
int ordinal = lastDirection.ordinal();
int[] offset = offsets[ordinal];
Food expectLast = new Food(last.getX() + offset[0], last.getY() + offset[1]);
Food expectHead = new Food(headX, headY);
return expectHead.touchTo(expectLast);
}
return true;
}
// 否则判定蛇头坐标是否会超出边界
return headX < 0 || headX > WIDTH - NODE_SIZE.width || headY < 0 || headY > HEIGHT - NODE_SIZE.height;
}
/**
*
*
* @return
*/
protected boolean touchFood() {
// 期望中移动后的蛇头与食物是否接触
Food expectHead = new Food(headX, headY);
return expectHead.touchTo(food);
}
protected abstract void addBody();
protected abstract void moveBody();
/**
*
*
* @param g the <code>Graphics</code>
*/
@Override
protected void paintComponent(Graphics g) {
// 父类设定边界大小 绘制背景等
super.paintComponent(g);
Graphics2D graphics2D = (Graphics2D) g;
// 设定边框颜色 绘制圆角边框
if (food!=null) {
graphics2D.setColor(Color.WHITE);
RoundRectangle2D.Float border = new RoundRectangle2D.Float(this.food.getX(), this.food.getY(), NODE_SIZE.width, NODE_SIZE.height, NODE_SIZE.width >> 2, NODE_SIZE.height >> 2);
graphics2D.draw(border);
// 设置食物颜色填充
graphics2D.setColor(this.food.getColor());
graphics2D.fill(border);
}
// 同理绘制身体
for (Food food : body) {
RoundRectangle2D.Float bodyBorder = new RoundRectangle2D.Float(food.getX(), food.getY(), NODE_SIZE.width, NODE_SIZE.height, NODE_SIZE.width >> 2, NODE_SIZE.height >> 2);
graphics2D.setColor(Color.WHITE);
graphics2D.draw(bodyBorder);
graphics2D.setColor(food.getColor());
graphics2D.fill(bodyBorder);
}
}
}

@ -1,107 +0,0 @@
package com.black.play.listener;
import com.black.play.component.SnakeWindow;
import com.black.play.constant.AppConstant;
import com.black.play.entity.Direction;
import com.black.play.entity.Snake;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
/**
* snake
*
* @author ylx
*/
public class SnakeListener extends KeyAdapter {
// 与SnakeWindow持有同一Snake引用
private final Snake snake;
public SnakeListener(Snake snake) {
this.snake = snake;
}
/**
*
*
* @param e
*/
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
switch (keyCode) {
case KeyEvent.VK_UP:
case KeyEvent.VK_W:
handleDirection(Direction.UP);
break;
case KeyEvent.VK_DOWN:
case KeyEvent.VK_S:
handleDirection(Direction.DOWN);
break;
case KeyEvent.VK_LEFT:
case KeyEvent.VK_A:
handleDirection(Direction.LEFT);
break;
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_D:
handleDirection(Direction.RIGHT);
break;
case KeyEvent.VK_ESCAPE:
handleEscape();
break;
case KeyEvent.VK_SPACE:
handleSpace();
break;
}
}
private void handleDirection(Direction direction) {
SnakeWindow snakeWindow = SnakeWindow.getInstance();
if (!AppConstant.GAME.equals(snakeWindow.getActiveCard())) {
return;
}
snake.changeDirection(direction);
}
/**
*
*
*
*
*/
private void handleSpace() {
SnakeWindow snakeWindow = SnakeWindow.getInstance();
String activeCard = snakeWindow.getActiveCard();
switch (activeCard) {
case AppConstant.GAME:
snakeWindow.pauseGame();
break;
case AppConstant.PAUSE:
snakeWindow.continueGame();
break;
case AppConstant.MENU:
snakeWindow.startGame();
break;
}
}
/**
* Esc
* 退
* 退
*
*/
private void handleEscape() {
SnakeWindow snakeWindow = SnakeWindow.getInstance();
String activeCard = snakeWindow.getActiveCard();
switch (activeCard) {
case AppConstant.GAME:
snakeWindow.pauseGame();
break;
case AppConstant.PAUSE:
case AppConstant.MENU:
System.exit(0);
break;
}
}
}
Loading…
Cancel
Save