JAVA学习,2个小时写了个简单的贪吃蛇
程序员文章站
2022-04-09 10:51:27
...
一个非常简单的贪吃蛇
共4个类:
Main---------------入口类
GameCanvas----主体类
Snake-------------蛇
Food--------------食物
截图:
代码:
类Main:
/** * 入口类 * @author kyda */ public class Main { public Main() { } public static void main(String[] args) { new GameCanvas(); } }
类GameCanvas:
import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.*; /** * 游戏主体类 * @author kyda */ public class GameCanvas extends JFrame implements Runnable { //public static final int UP_BORDER = 30, LEFT_BORDER = 10; public static final int FRAME_WIDTH = 800, FRAME_HEIGHT = 600; //游戏界面大小 private Random rand = new Random(); private Thread thread; private Snake snake; private Food food; private boolean isGameOver = false; // 双缓冲的成员变量 private Image offScreenImage = null; private Graphics offScreen = null; public GameCanvas() { super("贪吃蛇游戏"); setSize(FRAME_WIDTH, FRAME_HEIGHT); setLocationRelativeTo(null); setVisible(true); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); addKeyListener(new GameKeyListener()); init(); repaint(); thread = new Thread(this); thread.start(); } /** * 游戏初始化 */ public void init() { snake = new Snake(); createFood(); } /** * 随机位置创建食物 */ public void createFood() { int x = rand.nextInt(FRAME_WIDTH / Food.FOOD_WIDTH) * Food.FOOD_WIDTH; int y = rand.nextInt(FRAME_HEIGHT / Food.FOOD_HEIGHT) * Food.FOOD_HEIGHT; food = new Food(x, y); } /** * 绘制游戏物体 * 使用双缓冲防止闪烁 */ @Override public void paint(Graphics g) { if (offScreenImage == null) { offScreenImage = this.createImage(FRAME_WIDTH, FRAME_HEIGHT); } if (offScreen == null) offScreen = offScreenImage.getGraphics(); super.paint(offScreen); Color c = offScreen.getColor(); offScreen.setColor(Color.WHITE); offScreen.fillRect(0, 0, FRAME_WIDTH, FRAME_HEIGHT); offScreen.setColor(c); snake.drawSnake(offScreen); food.drawFood(offScreen); offScreen.drawString("Total length: " + snake.body.size(), 10, 50); if (isGameOver) { offScreen.setColor(Color.RED); offScreen.drawString("GameOver! press enter retry!", FRAME_WIDTH / 2 - 70, FRAME_HEIGHT / 2); } offScreen.setColor(c); g.drawImage(offScreenImage, 0, 0, null); } /** * 线程200ms检测碰撞并重画 */ @Override public void run() { while (true) { if (!isGameOver) { if (snake.snakeMove(food)) { // 行动中吃到食物 createFood(); } if (snake.collidedWallOrSelf()) { isGameOver = true; } repaint(); } try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * 内部类,按键监听 * @author kyda */ private class GameKeyListener extends KeyAdapter { @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_LEFT: if (snake.direct != Snake.RIGHT) snake.direct = Snake.LEFT; break; case KeyEvent.VK_RIGHT: if (snake.direct != Snake.LEFT) snake.direct = Snake.RIGHT; break; case KeyEvent.VK_UP: if (snake.direct != Snake.DOWN) snake.direct = Snake.UP; break; case KeyEvent.VK_DOWN: if (snake.direct != Snake.UP) snake.direct = Snake.DOWN; break; case KeyEvent.VK_ENTER: // gameover后按enter重来 if (isGameOver) { isGameOver = false; init(); } break; default: break; } } } }
类Snake:
import java.util.*; import java.awt.Graphics; /** * 蛇的类 * @author kyda */ public class Snake { // 四个方向 public static final int LEFT = 0, UP = 1, RIGHT = 2, DOWN = 3; // 链表储存身体的每个部分,各个部分用Food表示 public List<Food> body = new LinkedList<Food>(); // 蛇行进方向 public int direct; /** * 初始化蛇,默认方向向右 */ public Snake() { for (int i = 0; i < 3; ++i) { body.add(new Food((21 - i) * Food.FOOD_WIDTH, 15 * Food.FOOD_HEIGHT)); } direct = RIGHT; } /** * 画整条蛇,通过依次画出身体每个部分 * @param g */ public void drawSnake(Graphics g) { for (Food food : body) { food.drawFood(g); } } /** * 蛇的行走,并判断是否碰到食物 * @param food * @return 是否吃下食物 */ public boolean snakeMove(Food food) { int desx = body.get(0).x; int desy = body.get(0).y; switch (direct) { case LEFT: desx = body.get(0).x - Food.FOOD_WIDTH; break; case RIGHT: desx = body.get(0).x + Food.FOOD_WIDTH; break; case UP: desy = body.get(0).y - Food.FOOD_HEIGHT; break; case DOWN: desy = body.get(0).y + Food.FOOD_HEIGHT; break; } // 吃下食物则整体不动 if (desx == food.x && desy == food.y) { eatFood(food); return true; } // 将身体前一部分位置赋给后一部分 for (int i = body.size() - 1; i > 0; --i) { body.get(i).x = body.get(i - 1).x; body.get(i).y = body.get(i - 1).y; } body.get(0).x = desx; body.get(0).y = desy; return false; } /** * 吃下食物,插入链表开头 * @param food */ public void eatFood(Food food) { body.add(0, food); } /** * 判断是否撞墙或吃到自己 * @return 是否撞墙或吃到自己 */ public boolean collidedWallOrSelf() { Food head = body.get(0); if (head.x < 0 || head.x >= GameCanvas.FRAME_WIDTH || head.y < 0 || head.y >= GameCanvas.FRAME_HEIGHT) return true; for (int i = 1; i < body.size(); ++i) { Food food = body.get(i); if (head.x == food.x && head.y == food.y) return true; } return false; } }
类Food:
import java.awt.*; /** * 食物类 * @author kyda */ public class Food { // 食物宽度和高度 public static final int FOOD_WIDTH = 20, FOOD_HEIGHT = 20; // 食物坐标 public int x, y; public Food() { } public Food(int x, int y) { this.x = x; this.y = y; } /** * 画出食物 * @param g */ public void drawFood(Graphics g) { g.drawRect(x, y, FOOD_WIDTH, FOOD_HEIGHT); } /** * 检测是否与Food碰撞 * @param food * @return 是否与Food碰撞 */ public boolean isCollideWith(Food food) { if (this.x == food.x && this.y == food.y) { return true; } return false; } }