配置Raylib库
- 工具链
- 主函数模板
- Draw: 绘制网格
- Snake: 初始化
- Draw:绘制蛇与果
- Input:移动
- Logic:游戏主要逻辑
- Draw: 游戏结束
工具链
mkdir snake
cd snake
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(snake)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
find_package(raylib CONFIG REQUIRED)
add_executable(snake main.cpp)
target_link_libraries(snake PRIVATE raylib)
CMakePresets.json
{"version": 2,"configurePresets": [{"name": "default","generator": "Ninja","binaryDir": "${sourceDir}/build","cacheVariables": {"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"}}]
}
主函数模板
main
#include "raylib.h"#define SNAKE_LENGTH 256
#define SQUARE_SIZE 31typedef struct Snake {Vector2 position;Vector2 size;Color color;Vector2 speed;
} Snake;typedef struct Fruit {Vector2 position;Vector2 size;Color color;bool active;
} Fruit;static const int screenWidth = 800;
static const int screenHeight = 450;static int framesCounter = 0;
static bool allowMove = false;
static bool gameOver = false;static Snake snake[SNAKE_LENGTH];
static Vector2 snakePosition[SNAKE_LENGTH];
static Vector2 offset;
static int nTail;
static Fruit fruit;static void InitGame(void);
static void Draw(void);
static void Input(void);
static void Logic(void);
int main(void) {InitWindow(screenWidth, screenHeight, "snake game with raylib");SetTargetFPS(60);InitGame();while (!WindowShouldClose()) {Draw();Input();Logic();}CloseWindow();return 0;
}
void InitGame(void) {framesCounter = 0;allowMove = true;gameOver = false;nTail = 1;offset.x = screenWidth % SQUARE_SIZE;offset.y = screenHeight % SQUARE_SIZE;// @TODO: init snake// @TODO: init fruit
}
// Draw game (one frame)
void Draw(void) {BeginDrawing();ClearBackground(RAYWHITE);if (!gameOver) {// @TODO: Draw grid lines// @TODO: Draw fruit to pick} else {// @TODO: print help message}EndDrawing();
}void Input(void) {}
void Logic(void) {}
vcpkg new --application
vcpkg add port raylib
cmake --preset=default
cmake --build build
Draw: 绘制网格
DrawLineV(Vector2{SQUARE_SIZE * 1 + offset.x / 2, offset.y / 2},Vector2{SQUARE_SIZE * 1 + offset.x / 2, screenHeight - offset.y / 2},LIGHTGRAY);DrawLineV(Vector2{offset.x / 2, SQUARE_SIZE * 2 + offset.y / 2},Vector2{screenWidth - offset.x / 2, SQUARE_SIZE * 2 + offset.y / 2},LIGHTGRAY);for (int i = 0; i < screenWidth / SQUARE_SIZE + 1; i++) {DrawLineV(Vector2{SQUARE_SIZE * i + offset.x / 2, offset.y / 2},Vector2{SQUARE_SIZE * i + offset.x / 2, screenHeight - offset.y / 2},LIGHTGRAY);}for (int i = 0; i < screenHeight / SQUARE_SIZE + 1; i++) {DrawLineV(Vector2{offset.x / 2, SQUARE_SIZE * i + offset.y / 2},Vector2{screenWidth - offset.x / 2, SQUARE_SIZE * i + offset.y / 2},LIGHTGRAY);}
Snake: 初始化
// @TODO: init snakefor (int i = 0; i < SNAKE_LENGTH; i++) {snake[i].position = Vector2{offset.x / 2, offset.y / 2};snake[i].size = Vector2{SQUARE_SIZE, SQUARE_SIZE};snake[i].speed = Vector2{SQUARE_SIZE, 0};if (i == 0)snake[i].color = DARKBLUE;elsesnake[i].color = BLUE;}for (int i = 0; i < SNAKE_LENGTH; i++) {snakePosition[i] = Vector2{0.0f, 0.0f};}// @TODO: init fruitfruit.size = Vector2{SQUARE_SIZE, SQUARE_SIZE};fruit.color = SKYBLUE;fruit.active = false;fruit.position = Vector2{GetRandomValue(0, (screenWidth / SQUARE_SIZE) - 1) * SQUARE_SIZE +offset.x / 2,GetRandomValue(0, (screenHeight / SQUARE_SIZE) - 1) * SQUARE_SIZE +offset.y / 2};
Draw:绘制蛇与果
// Draw snakefor (int i = 0; i < nTail; i++)DrawRectangleV(snake[i].position, snake[i].size, snake[i].color);// Draw fruit to pickDrawRectangleV(fruit.position, fruit.size, fruit.color);
Input:移动
if (!gameOver) {// Player controlif (IsKeyPressed(KEY_RIGHT) && (snake[0].speed.x == 0) && allowMove) {snake[0].speed = Vector2{SQUARE_SIZE, 0};allowMove = false;}if (IsKeyPressed(KEY_LEFT) && (snake[0].speed.x == 0) && allowMove) {snake[0].speed = Vector2{-SQUARE_SIZE, 0};allowMove = false;}if (IsKeyPressed(KEY_UP) && (snake[0].speed.y == 0) && allowMove) {snake[0].speed = Vector2{0, -SQUARE_SIZE};allowMove = false;}if (IsKeyPressed(KEY_DOWN) && (snake[0].speed.y == 0) && allowMove) {snake[0].speed = Vector2{0, SQUARE_SIZE};allowMove = false;}}
Logic:游戏主要逻辑
if (!gameOver) {for (int i = 0; i < nTail; i++)snakePosition[i] = snake[i].position;if ((framesCounter % 5) == 0) {for (int i = 0; i < nTail; i++) {if (i == 0) {snake[0].position.x += snake[0].speed.x;snake[0].position.y += snake[0].speed.y;allowMove = true;} elsesnake[i].position = snakePosition[i - 1];}}// Wall behaviourif (((snake[0].position.x) > (screenWidth - offset.x)) ||((snake[0].position.y) > (screenHeight - offset.y)) ||(snake[0].position.x < 0) || (snake[0].position.y < 0)) {gameOver = true;}// Collision with yourselffor (int i = 1; i < nTail; i++) {if ((snake[0].position.x == snake[i].position.x) &&(snake[0].position.y == snake[i].position.y))gameOver = true;}// Fruit position calculationif (!fruit.active) {fruit.active = true;fruit.position = Vector2{GetRandomValue(0, (screenWidth / SQUARE_SIZE) - 1) * SQUARE_SIZE +offset.x / 2,GetRandomValue(0, (screenHeight / SQUARE_SIZE) - 1) * SQUARE_SIZE +offset.y / 2};for (int i = 0; i < nTail; i++) {while ((fruit.position.x == snake[i].position.x) &&(fruit.position.y == snake[i].position.y)) {fruit.position = Vector2{GetRandomValue(0, (screenWidth / SQUARE_SIZE) - 1) * SQUARE_SIZE +offset.x / 2,GetRandomValue(0, (screenHeight / SQUARE_SIZE) - 1) *SQUARE_SIZE +offset.y / 2};i = 0;}}}// Collisionif ((snake[0].position.x < (fruit.position.x + fruit.size.x) &&(snake[0].position.x + snake[0].size.x) > fruit.position.x) &&(snake[0].position.y < (fruit.position.y + fruit.size.y) &&(snake[0].position.y + snake[0].size.y) > fruit.position.y)) {snake[nTail].position = snakePosition[nTail - 1];nTail += 1;fruit.active = false;}framesCounter++;} else {if (IsKeyPressed(KEY_ENTER)) {InitGame();gameOver = false;}}
Draw: 游戏结束
DrawText("PRESS [ENTER] TO PLAY AGAIN",GetScreenWidth() / 2 -MeasureText("PRESS [ENTER] TO PLAY AGAIN", 20) / 2,GetScreenHeight() / 2 - 50, 20, GRAY);
该例子来自raylib官方样例