之前使用python里面的pygame可以很容易构建snake游戏,其实pygame后端也是封装了sdl库,所以pygame编写的snake,使用C++
SDL2编写应该很容易移植过来
不过我不想自己亲自编写,我想偷懒让人工智能帮我完成看看,结果表明要一次达成目标似乎也不是那么顺利
经过几次对话,好歹勉强实现了贪吃蛇的基础功能
第一次尝试
代码
#include <SDL.h>
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
using namespace std;
// 定义游戏窗口的宽度和高度
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
// 定义Snake的单元格大小
const int CELL_SIZE = 20;
// 定义Snake的初始长度
const int INITIAL_LENGTH = 5;
// 定义Snake的方向
enum class Direction { UP, DOWN, LEFT, RIGHT };
// 定义Snake的单元格结构体
struct Cell {
int x, y;
};
// Snake类
class Snake {
public:
Snake();
void HandleInput(SDL_Event &e);
void Move();
bool CheckCollision();
void Grow();
void Render(SDL_Renderer* renderer);
private:
vector<Cell> body;
Direction direction;
};
Snake::Snake() {
// 初始化Snake的位置和方向
for (int i = 0; i < INITIAL_LENGTH; ++i) {
body.push_back({SCREEN_WIDTH / 2 - i * CELL_SIZE, SCREEN_HEIGHT / 2});
}
direction = Direction::RIGHT;
}
void Snake::HandleInput(SDL_Event &e) {
// 处理用户输入,改变Snake的方向
if (e.type == SDL_KEYDOWN) {
switch (e.key.keysym.sym) {
case SDLK_UP:
if (direction != Direction::DOWN)
direction = Direction::UP;
break;
case SDLK_DOWN:
if (direction != Direction::UP)
direction = Direction::DOWN;
break;
case SDLK_LEFT:
if (direction != Direction::RIGHT)
direction = Direction::LEFT;
break;
case SDLK_RIGHT:
if (direction != Direction::LEFT)
direction = Direction::RIGHT;
break;
}
}
}
void Snake::Move() {
// 移动Snake的头部
int newHeadX = body.front().x;
int newHeadY = body.front().y;
switch (direction) {
case Direction::UP:
newHeadY -= CELL_SIZE;
break;
case Direction::DOWN:
newHeadY += CELL_SIZE;
break;
case Direction::LEFT:
newHeadX -= CELL_SIZE;
break;
case Direction::RIGHT:
newHeadX += CELL_SIZE;
break;
}
// 添加新的头部
body.insert(body.begin(), {newHeadX, newHeadY});
}
bool Snake::CheckCollision() {
// 检查是否与自身相撞
for (size_t i = 1; i < body.size(); ++i) {
if (body[i].x == body.front().x && body[i].y == body.front().y) {
return true;
}
}
// 检查是否超出屏幕边界
if (body.front().x < 0 || body.front().x >= SCREEN_WIDTH || body.front().y < 0 || body.front().y >= SCREEN_HEIGHT) {
return true;
}
return false;
}
void Snake::Grow() {
// Snake吃到食物时增加长度
int tailX = body.back().x;
int tailY = body.back().y;
switch (direction) {
case Direction::UP:
tailY += CELL_SIZE;
break;
case Direction::DOWN:
tailY -= CELL_SIZE;
break;
case Direction::LEFT:
tailX += CELL_SIZE;
break;
case Direction::RIGHT:
tailX -= CELL_SIZE;
break;
}
body.push_back({tailX, tailY});
}
void Snake::Render(SDL_Renderer* renderer) {
// 渲染Snake
for (const Cell& cell : body) {
SDL_Rect rect = {cell.x, cell.y, CELL_SIZE, CELL_SIZE};
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); // Snake颜色
SDL_RenderFillRect(renderer, &rect);
}
}
// 主程序
int main(int argc, char* args[]) {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window = SDL_CreateWindow("Snake Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
srand(time(nullptr));
Snake snake;
// 游戏主循环
bool quit = false;
SDL_Event e;
while (!quit) {
while (SDL_PollEvent(&e) != 0) {
if (e.type == SDL_QUIT) {
quit = true;
}
snake.HandleInput(e);
}
// 移动Snake
snake.Move();
// 检查碰撞
if (snake.CheckCollision()) {
quit = true;
}
// 渲染
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // 清空屏幕
SDL_RenderClear(renderer);
snake.Render(renderer);
SDL_RenderPresent(renderer);
// 控制游戏速度
SDL_Delay(100);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
运行结果,看起来不符合预期
第二次尝试
代码
#include <SDL.h>
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
using namespace std;
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int CELL_SIZE = 20;
enum class Direction { UP, DOWN, LEFT, RIGHT };
struct Cell {
int x, y;
};
class Snake {
public:
Snake();
void HandleInput(SDL_Event &e);
void Move();
bool CheckCollision();
void Grow();
void Render(SDL_Renderer* renderer);
private:
vector<Cell> body;
Direction direction;
};
Snake::Snake() {
body.push_back({SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2});
direction = Direction::RIGHT;
}
void Snake::HandleInput(SDL_Event &e) {
if (e.type == SDL_KEYDOWN) {
switch (e.key.keysym.sym) {
case SDLK_UP:
if (direction != Direction::DOWN)
direction = Direction::UP;
break;
case SDLK_DOWN:
if (direction != Direction::UP)
direction = Direction::DOWN;
break;
case SDLK_LEFT:
if (direction != Direction::RIGHT)
direction = Direction::LEFT;
break;
case SDLK_RIGHT:
if (direction != Direction::LEFT)
direction = Direction::RIGHT;
break;
}
}
}
void Snake::Move() {
int newHeadX = body.front().x;
int newHeadY = body.front().y;
switch (direction) {
case Direction::UP:
newHeadY -= CELL_SIZE;
break;
case Direction::DOWN:
newHeadY += CELL_SIZE;
break;
case Direction::LEFT:
newHeadX -= CELL_SIZE;
break;
case Direction::RIGHT:
newHeadX += CELL_SIZE;
break;
}
body.insert(body.begin(), {newHeadX, newHeadY});
}
bool Snake::CheckCollision() {
for (size_t i = 1; i < body.size(); ++i) {
if (body[i].x == body.front().x && body[i].y == body.front().y) {
return true;
}
}
if (body.front().x < 0 || body.front().x >= SCREEN_WIDTH || body.front().y < 0 || body.front().y >= SCREEN_HEIGHT) {
return true;
}
return false;
}
void Snake::Grow() {
int tailX = body.back().x;
int tailY = body.back().y;
switch (direction) {
case Direction::UP:
tailY += CELL_SIZE;
break;
case Direction::DOWN:
tailY -= CELL_SIZE;
break;
case Direction::LEFT:
tailX += CELL_SIZE;
break;
case Direction::RIGHT:
tailX -= CELL_SIZE;
break;
}
body.push_back({tailX, tailY});
}
void Snake::Render(SDL_Renderer* renderer) {
for (size_t i = 0; i < body.size(); ++i) {
SDL_Rect rect = {body[i].x, body[i].y, CELL_SIZE, CELL_SIZE};
if (i == 0) {
SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255); // Snake头部为黄色
} else {
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); // Snake身体为绿色
}
SDL_RenderFillRect(renderer, &rect);
}
}
int main(int argc, char* args[]) {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window = SDL_CreateWindow("Snake Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
srand(time(nullptr));
Snake snake;
bool quit = false;
SDL_Event e;
while (!quit) {
while (SDL_PollEvent(&e) != 0) {
if (e.type == SDL_QUIT) {
quit = true;
}
snake.HandleInput(e);
}
snake.Move();
if (snake.CheckCollision()) {
quit = true;
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
snake.Render(renderer);
SDL_RenderPresent(renderer);
SDL_Delay(100);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
结果貌似还是不符合预期
第三次尝试
代码
#include <SDL.h>
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
using namespace std;
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int CELL_SIZE = 20;
enum class Direction { UP, DOWN, LEFT, RIGHT };
struct Cell {
int x, y;
};
class Snake {
public:
Snake();
void HandleInput(SDL_Event &e);
void Move();
bool CheckCollision();
void Grow();
void Render(SDL_Renderer* renderer);
Cell GetHeadPosition();
private:
vector<Cell> body;
Direction direction;
};
Snake::Snake() {
body.push_back({SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2});
direction = Direction::RIGHT;
}
void Snake::HandleInput(SDL_Event &e) {
if (e.type == SDL_KEYDOWN) {
switch (e.key.keysym.sym) {
case SDLK_UP:
if (direction != Direction::DOWN)
direction = Direction::UP;
break;
case SDLK_DOWN:
if (direction != Direction::UP)
direction = Direction::DOWN;
break;
case SDLK_LEFT:
if (direction != Direction::RIGHT)
direction = Direction::LEFT;
break;
case SDLK_RIGHT:
if (direction != Direction::LEFT)
direction = Direction::RIGHT;
break;
}
}
}
void Snake::Move() {
int newHeadX = body.front().x;
int newHeadY = body.front().y;
switch (direction) {
case Direction::UP:
newHeadY -= CELL_SIZE;
break;
case Direction::DOWN:
newHeadY += CELL_SIZE;
break;
case Direction::LEFT:
newHeadX -= CELL_SIZE;
break;
case Direction::RIGHT:
newHeadX += CELL_SIZE;
break;
}
body.insert(body.begin(), {newHeadX, newHeadY});
}
bool Snake::CheckCollision() {
for (size_t i = 1; i < body.size(); ++i) {
if (body[i].x == body.front().x && body[i].y == body.front().y) {
return true;
}
}
if (body.front().x < 0 || body.front().x >= SCREEN_WIDTH || body.front().y < 0 || body.front().y >= SCREEN_HEIGHT) {
return true;
}
return false;
}
void Snake::Grow() {
// 在Snake的尾部增加一个单元格
int tailX = body.back().x;
int tailY = body.back().y;
switch (direction) {
case Direction::UP:
tailY += CELL_SIZE;
break;
case Direction::DOWN:
tailY -= CELL_SIZE;
break;
case Direction::LEFT:
tailX += CELL_SIZE;
break;
case Direction::RIGHT:
tailX -= CELL_SIZE;
break;
}
body.push_back({tailX, tailY});
}
void Snake::Render(SDL_Renderer* renderer) {
for (size_t i = 0; i < body.size(); ++i) {
SDL_Rect rect = {body[i].x, body[i].y, CELL_SIZE, CELL_SIZE};
if (i == 0) {
SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255); // Snake头部为黄色
} else {
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); // Snake身体为绿色
}
SDL_RenderFillRect(renderer, &rect);
}
}
Cell Snake::GetHeadPosition() {
return body.front();
}
int main(int argc, char* args[]) {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window = SDL_CreateWindow("Snake Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
srand(time(nullptr));
Snake snake;
// 初始化食物位置
Cell food = {rand() % (SCREEN_WIDTH / CELL_SIZE) * CELL_SIZE, rand() % (SCREEN_HEIGHT / CELL_SIZE) * CELL_SIZE};
bool quit = false;
SDL_Event e;
while (!quit) {
while (SDL_PollEvent(&e) != 0) {
if (e.type == SDL_QUIT) {
quit = true;
}
snake.HandleInput(e);
}
snake.Move();
if (snake.GetHeadPosition().x == food.x && snake.GetHeadPosition().y == food.y) {
// Snake吃到食物时,增加长度并生成新的食物位置
snake.Grow();
food = {rand() % (SCREEN_WIDTH / CELL_SIZE) * CELL_SIZE, rand() % (SCREEN_HEIGHT / CELL_SIZE) * CELL_SIZE};
}
if (snake.CheckCollision()) {
quit = true;
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
snake.Render(renderer);
// 渲染食物
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); // 食物为绿色
SDL_Rect foodRect = {food.x, food.y, CELL_SIZE, CELL_SIZE};
SDL_RenderFillRect(renderer, &foodRect);
SDL_RenderPresent(renderer);
SDL_Delay(100);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
运行结果,依然不符合预期,真是糟糕
第四次尝试,看看
代码
#include <SDL.h>
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
using namespace std;
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int CELL_SIZE = 20;
enum class Direction { UP, DOWN, LEFT, RIGHT };
struct Cell {
int x, y;
};
class Snake {
public:
Snake();
void HandleInput(SDL_Event &e);
void Move();
bool CheckCollision();
void Grow();
void Render(SDL_Renderer* renderer);
Cell GetHeadPosition();
private:
vector<Cell> body;
Direction direction;
bool growing; // 用于标记是否吃到食物后需要增长
};
Snake::Snake() {
body.push_back({SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2});
direction = Direction::RIGHT;
growing = false; // 初始化时不需要增长
}
void Snake::HandleInput(SDL_Event &e) {
if (e.type == SDL_KEYDOWN) {
switch (e.key.keysym.sym) {
case SDLK_UP:
if (direction != Direction::DOWN)
direction = Direction::UP;
break;
case SDLK_DOWN:
if (direction != Direction::UP)
direction = Direction::DOWN;
break;
case SDLK_LEFT:
if (direction != Direction::RIGHT)
direction = Direction::LEFT;
break;
case SDLK_RIGHT:
if (direction != Direction::LEFT)
direction = Direction::RIGHT;
break;
}
}
}
void Snake::Move() {
int newHeadX = body.front().x;
int newHeadY = body.front().y;
switch (direction) {
case Direction::UP:
newHeadY -= CELL_SIZE;
break;
case Direction::DOWN:
newHeadY += CELL_SIZE;
break;
case Direction::LEFT:
newHeadX -= CELL_SIZE;
break;
case Direction::RIGHT:
newHeadX += CELL_SIZE;
break;
}
body.insert(body.begin(), {newHeadX, newHeadY});
// 如果吃到食物,则不删除尾部,实现增长
if (!growing) {
body.pop_back();
} else {
growing = false;
}
}
bool Snake::CheckCollision() {
for (size_t i = 1; i < body.size(); ++i) {
if (body[i].x == body.front().x && body[i].y == body.front().y) {
return true;
}
}
if (body.front().x < 0 || body.front().x >= SCREEN_WIDTH || body.front().y < 0 || body.front().y >= SCREEN_HEIGHT) {
return true;
}
return false;
}
void Snake::Grow() {
// 在Snake的尾部增加一个单元格
int tailX = body.back().x;
int tailY = body.back().y;
switch (direction) {
case Direction::UP:
tailY += CELL_SIZE;
break;
case Direction::DOWN:
tailY -= CELL_SIZE;
break;
case Direction::LEFT:
tailX += CELL_SIZE;
break;
case Direction::RIGHT:
tailX -= CELL_SIZE;
break;
}
body.push_back({tailX, tailY});
growing = true; // 标记为需要增长
}
void Snake::Render(SDL_Renderer* renderer) {
for (size_t i = 0; i < body.size(); ++i) {
SDL_Rect rect = {body[i].x, body[i].y, CELL_SIZE, CELL_SIZE};
if (i == 0) {
SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255); // Snake头部为黄色
} else {
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); // Snake身体为绿色
}
SDL_RenderFillRect(renderer, &rect);
}
}
Cell Snake::GetHeadPosition() {
return body.front();
}
int main(int argc, char* args[]) {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window = SDL_CreateWindow("Snake Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
srand(time(nullptr));
Snake snake;
// 初始化食物位置
Cell food = {rand() % (SCREEN_WIDTH / CELL_SIZE) * CELL_SIZE, rand() % (SCREEN_HEIGHT / CELL_SIZE) * CELL_SIZE};
bool quit = false;
SDL_Event e;
while (!quit) {
while (SDL_PollEvent(&e) != 0) {
if (e.type == SDL_QUIT) {
quit = true;
}
snake.HandleInput(e);
}
snake.Move();
if (snake.GetHeadPosition().x == food.x && snake.GetHeadPosition().y == food.y) {
// Snake吃到食物时,增加长度并生成新的食物位置
snake.Grow();
food = {rand() % (SCREEN_WIDTH / CELL_SIZE) * CELL_SIZE, rand() % (SCREEN_HEIGHT / CELL_SIZE) * CELL_SIZE};
}
if (snake.CheckCollision()) {
quit = true;
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
snake.Render(renderer);
// 渲染食物
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); // 食物为绿色
SDL_Rect foodRect = {food.x, food.y, CELL_SIZE, CELL_SIZE};
SDL_RenderFillRect(renderer, &foodRect);
SDL_RenderPresent(renderer);
SDL_Delay(100);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
运行结果,看起来达到预期
透过这几次对话,我们可以看到要想快速达到预期的结果,提问非常关键,如果一开始就给出比较细致的说明,应该能够一次成功,需要试试!
透过比较几次代码的差异,也可以从中学习到程序bug产生的原因