配置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 31
typedef 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 snake
for (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;
else
snake[i].color = BLUE;
}
for (int i = 0; i < SNAKE_LENGTH; i++) {
snakePosition[i] = Vector2{0.0f, 0.0f};
}
// @TODO: init fruit
fruit.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 snake
for (int i = 0; i < nTail; i++)
DrawRectangleV(snake[i].position, snake[i].size, snake[i].color);
// Draw fruit to pick
DrawRectangleV(fruit.position, fruit.size, fruit.color);
Input:移动
if (!gameOver) {
// Player control
if (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;
} else
snake[i].position = snakePosition[i - 1];
}
}
// Wall behaviour
if (((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 yourself
for (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 calculation
if (!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;
}
}
}
// Collision
if ((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官方样例