Coordinate Point.java
Record the horizontal and vertical coordinate values.package cn.xeblog.snake.model; import java.util.Objects; /** * 坐标 * * @author anlingyi * @date 2022/8/2 3:35 PM */ public class Point { public int x; public int y; public Point(int x, int y) { this.x = x; this.y = y; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Point point = (Point) o; return x == point.x && y == point.y; } @Override public int hashCode() { return Objects.hash(x, y); } @Override public String toString() { return "Point{" + "x=" + x + ", y=" + y + '}'; } }
Moving direction Direction.java
Provides an enumeration of four moving directions: up, down, left, and right.package cn.xeblog.snake.model; /** * @author anlingyi * @date 2022/8/2 5:25 PM */ public enum Direction { UP, DOWN, LEFT, RIGHT }
SNAKESnake.java
Stores snake body coordinate information, provides snake body movement, removes snake tail coordinates, obtains snake head, snake tail coordinates, snake body length, etc. . Here's a talk about the implementation principle of snake movement:When the game starts, a moving direction will be fixed, and the snake will always move in this direction. We can change the moving direction of the snake through the direction keys. Moving actually means moving the coordinates of the snake body. For example, the length of the snake body (excluding the snake head) is 6. When moving, you only need to move the coordinates of the snake body at position 5 to the coordinates of position 6, and the length of the snake body at position 4. To move the coordinates to the coordinates at position 5, simply put, it is to change the previous coordinate to the one behind it. This is the movement of the snake body. The movement of the snake head needs to be calculated separately. If it moves in the up and down direction, it is the movement of the y For coordinate addition and subtraction operations, upward movement requires subtracting one height of the snake's body, while downward movement requires adding one height of the snake's body. The same applies to left and right movements.
package cn.xeblog.snake.model; import java.util.List; /** * 蛇 * * @author anlingyi * @date 2022/8/2 3:32 PM */ public class Snake { public static int width = 10; public static int height = 10; /** * 蛇身坐标列表 */ public List<Point> body; public Snake(List<Point> body) { this.body = body; } /** * 添加蛇身坐标 * * @param x * @param y */ public void add(int x, int y) { this.body.add(new Point(x * width, y * height)); } /** * 移除蛇尾坐标 */ public void removeLast() { int size = size(); if (size == 0) { return; } this.body.remove(size - 1); } /** * 获取蛇头坐标 * * @return */ public Point getHead() { if (size() > 0) { return this.body.get(0); } return null; } /** * 获取蛇尾坐标 * * @return */ public Point getTail() { int size = size(); if (size > 0) { return this.body.get(size - 1); } return null; } /** * 蛇身长度 * * @return */ public int size() { return this.body.size(); } /** * 蛇移动 * * @param direction 移动方向 */ public void move(Direction direction) { if (size() == 0) { return; } for (int i = this.size() - 1; i > 0; i--) { // 从蛇尾开始向前移动 Point point = this.body.get(i); Point nextPoint = this.body.get(i - 1); point.x = nextPoint.x; point.y = nextPoint.y; } // 蛇头移动 Point head = getHead(); switch (direction) { case UP: head.y -= height; break; case DOWN: head.y += height; break; case LEFT: head.x -= width; break; case RIGHT: head.x += width; break; } } }
Pill Pill.java
Stores the coordinates and type information of "Pill".package cn.xeblog.snake.model; /** * 药丸 * * @author anlingyi * @date 2022/8/2 4:49 PM */ public class Pill { public static int width = 10; public static int height = 10; /** * 坐标 */ public Point point; /** * 药丸类型 */ public PillType pillType; public enum PillType { /** * 红色药丸 */ RED(5), /** * 蓝色药丸 */ BLUE(2), /** * 绿色药丸 */ GREEN(1), ; /** * 分数 */ public int score; PillType(int score) { this.score = score; } } public Pill(int x, int y, PillType pillType) { this.point = new Point(x * width, y * height); this.pillType = pillType; } }
package cn.xeblog.snake.ui; import cn.xeblog.snake.model.Direction; import cn.xeblog.snake.model.Pill; import cn.xeblog.snake.model.Point; import cn.xeblog.snake.model.Snake; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.ArrayList; import java.util.Collections; import java.util.Random; /** * @author anlingyi * @date 2022/8/2 3:51 PM */ public class SnakeGameUI extends JPanel implements ActionListener { /** * 宽度 */ private int width; /** * 高度 */ private int height; /** * 蛇 */ private Snake snake; /** * 药丸 */ private Pill pill; /** * 移动方向 */ private Direction direction; /** * 停止游戏标记 */ private boolean stop; /** * 游戏状态 0.初始化 1.游戏胜利 2.游戏失败 */ private int state = -1; /** * 定时器 */ private Timer timer; /** * 移动速度 */ private int speed = 100; /** * 分数 */ private int score; /** * 特殊药丸列表 */ private ArrayList<Pill.PillType> specialPill; public SnakeGameUI(int width, int height) { this.width = width; this.height = height; this.timer = new Timer(speed, this); this.stop = true; initPanel(); } /** * 初始化 */ private void init() { this.score = 0; this.state = 0; this.stop = true; this.timer.setDelay(speed); initSnake(); initPill(); generatePill(); repaint(); } /** * 初始化游戏面板 */ private void initPanel() { this.setPreferredSize(new Dimension(this.width, this.height)); this.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (stop && e.getKeyCode() != KeyEvent.VK_SPACE) { return; } switch (e.getKeyCode()) { case KeyEvent.VK_UP: if (direction == Direction.DOWN) { break; } direction = Direction.UP; break; case KeyEvent.VK_DOWN: if (direction == Direction.UP) { break; } direction = Direction.DOWN; break; case KeyEvent.VK_LEFT: if (direction == Direction.RIGHT) { break; } direction = Direction.LEFT; break; case KeyEvent.VK_RIGHT: if (direction == Direction.LEFT) { break; } direction = Direction.RIGHT; break; case KeyEvent.VK_SPACE: if (state != 0) { init(); } stop = !stop; if (!stop) { timer.start(); } break; } } }); } /** * 初始化蛇 */ private void initSnake() { this.direction = Direction.LEFT; int maxX = this.width / Snake.width; int maxY = this.height / Snake.height; this.snake = new Snake(new ArrayList<>()); this.snake.add(maxX - 2, 3); this.snake.add(maxX - 1, 3); this.snake.add(maxX - 1, 2); this.snake.add(maxX - 1, 1); for (int i = maxX - 1; i > 0; i--) { this.snake.add(i, 1); } for (int i = 1; i < maxY - 1; i++) { this.snake.add(1, i); } for (int i = 1; i < maxX - 1; i++) { this.snake.add(i, maxY - 2); } } /** * 初始化药丸 */ private void initPill() { this.specialPill = new ArrayList<>(); for (int i = 0; i < 5; i++) { this.specialPill.add(Pill.PillType.RED); } for (int i = 0; i < 10; i++) { this.specialPill.add(Pill.PillType.BLUE); } Collections.shuffle(specialPill); } /** * 生成药丸 */ private void generatePill() { // 是否获取特殊药丸 boolean getSpecialPill = new Random().nextInt(6) == 3; Pill.PillType pillType; if (getSpecialPill && this.specialPill.size() > 0) { // 生成特殊药丸 int index = new Random().nextInt(this.specialPill.size()); pillType = this.specialPill.get(index); this.specialPill.remove(index); } else { // 生成绿色药丸 pillType = Pill.PillType.GREEN; } // 随机坐标 int x = new Random().nextInt(this.width / Pill.width - 1); int y = new Random().nextInt(this.height / Pill.height - 1); this.pill = new Pill(x, y, pillType); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setColor(new Color(66, 66, 66)); g2.fillRect(0, 0, this.width, this.height); if (this.snake != null) { // 画蛇 g2.setColor(new Color(255, 255, 255)); for (int i = this.snake.size() - 1; i >= 0; i--) { Point point = this.snake.body.get(i); if (i == 0) { // 蛇头 g2.setColor(new Color(255, 92, 92)); } else { g2.setColor(new Color(215, 173, 173)); } g2.fillRect(point.x, point.y, Snake.width, Snake.height); } } if (this.pill != null) { // 画药丸 Color pillColor; switch (this.pill.pillType) { case RED: pillColor = new Color(255, 41, 41); break; case BLUE: pillColor = new Color(20, 250, 243); break; default: pillColor = new Color(97, 255, 113); break; } g2.setColor(pillColor); g2.fillOval(pill.point.x, pill.point.y, Pill.width, Pill.height); } if (state > 0) { // 显示游戏结果 String tips = "游戏失败!"; if (state == 1) { tips = "游戏胜利!"; } g2.setFont(new Font("", Font.BOLD, 20)); g2.setColor(new Color(208, 74, 74)); g2.drawString(tips, this.width / 3, this.height / 3); g2.setFont(new Font("", Font.PLAIN, 18)); g2.setColor(Color.WHITE); g2.drawString("得分:" + this.score, this.width / 2, this.height / 3 + 50); } if (stop) { g2.setFont(new Font("", Font.PLAIN, 18)); g2.setColor(Color.WHITE); g2.drawString("按空格键开始/暂停游戏!", this.width / 4, this.height - 50); } } @Override public void actionPerformed(ActionEvent e) { // 是否吃药 boolean isAte = false; if (!this.stop) { // 移动蛇 this.snake.move(this.direction); Point head = this.snake.getHead(); if (head.equals(this.pill.point)) { // 吃药了 isAte = true; // 药丸分数 int getScore = this.pill.pillType.score; // 累计分数 this.score += getScore; for (int i = 0; i < getScore; i++) { // 移除蛇尾 this.snake.removeLast(); if (this.snake.size() == 0) { // 游戏胜利 this.state = 1; this.stop = true; break; } } pill = null; if (this.score % 10 == 0) { int curSpeed = this.timer.getDelay(); if (curSpeed > 30) { // 加速 this.timer.setDelay(curSpeed - 10); } } } if (state == 0) { // 判断蛇有没有咬到自己或是撞墙 int maxWidth = this.width - this.snake.width; int maxHeight = this.height - this.snake.height; boolean isHitWall = head.x > maxWidth || head.x < 0 || head.y > maxHeight || head.y < 0; boolean isBiting = false; for (int i = this.snake.size() - 1; i > 0; i--) { if (head.equals(this.snake.body.get(i))) { isBiting = true; break; } } if (isHitWall || isBiting) { // 游戏失败 this.state = 2; this.stop = true; } } } if (this.stop) { this.timer.stop(); } else if (isAte) { // 重新生成药丸 generatePill(); } repaint(); } }
package cn.xeblog.snake; import cn.xeblog.snake.ui.SnakeGameUI; import javax.swing.*; /** * 启动游戏 * * @author anlingyi * @date 2022/8/2 3:41 PM */ public class StartGame { public static void main(String[] args) { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); frame.setVisible(true); frame.setResizable(false); frame.setTitle("不贪吃蛇"); frame.setSize(400, 320); frame.setLocationRelativeTo(null); JPanel gamePanel = new SnakeGameUI(400, 300); frame.add(gamePanel); gamePanel.requestFocus(); } }
The above is the detailed content of Tutorial on writing small games in Java: No Snake Game. For more information, please follow other related articles on the PHP Chinese website!