简易贪吃蛇-基于C++和OpenCV的实现
2022/8/13 1:25:37
本文主要是介绍简易贪吃蛇-基于C++和OpenCV的实现,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
简易贪吃蛇-基于C++和OpenCV的实现
2022-08-12 11:20:01
1. 目的
做一些 application 方面 demo 的尝试。
使用 OpenCV 而不是 EasyX 或 SDL 的原因是: 对 OpenCV 比较熟悉觉得比较简单, 能够跨平台, 对于验证想法的小demo还是够用的。
代码大约200行。
主要思路是状态转移,即:当前帧和下一帧, 根据用户输入的方向键改变或维持方向, 根据方向情况更新 snake 的每个 body 部分(block)的位置, 清理屏幕和绘制更新位置后的 snake; 随机位置生成食物, 吃到食物后 body 需要增加一个元素。
使用到了 std::deque
双端队列数据结构, 解决了 snake 改变方向后计算新位置的问题。
2. 代码
#include <opencv2/highgui.hpp> #include <opencv2/opencv.hpp> #include <stdio.h> #include <stdlib.h> class Point { public: int x; int y; }; // Divide the whole image(canvas) into blocks // block is the basic plot unit class Block { public: int width; int height; public: Block(int _height, int _width) : height(_height), width(_width) {} }; // A randomly generated block class Food { public: Point pos; cv::Scalar color; bool exist; }; // User use arrow keys for snake movement // Esc key corresponds to EXIT, means exit the game enum Direction { LEFT, RIGHT, TOP, BOTTOM, EXIT }; class Snake { public: Snake(const Point& pos, Direction dir, cv::Scalar color); public: std::vector<Point> points; std::deque<Direction> directions; cv::Scalar color; }; Snake::Snake(const Point& init_point, Direction dir, cv::Scalar _color) { points.push_back(init_point); directions.push_back(dir); color = _color; } Point get_center_point(int w, int h) { Point pt; pt.x = w / 2; pt.y = h / 2; return pt; } void draw_block(cv::Mat& image, int block_x_idx, int block_y_idx, int block_width, int block_height, cv::Scalar color) { for (int i = 1; i < block_height-1; i++) { int y = block_y_idx * block_height + i; for (int j = 1; j < block_width-1; j++) { int x = block_x_idx * block_width + j; image.ptr(y, x)[0] = color[0]; image.ptr(y, x)[1] = color[1]; image.ptr(y, x)[2] = color[2]; } } } int get_random_int(int a, int b) { return (rand() * 1.0 / RAND_MAX) * (b - a) + a; } bool is_same_block(const Point& p1, const Point& p2, const Block& block) { int p1_block_idx_x = p1.x / block.width; int p1_block_idx_y = p1.y / block.height; int p2_block_idx_x = p2.x / block.width; int p2_block_idx_y = p2.y / block.height; return p1_block_idx_x == p2_block_idx_x && p1_block_idx_y == p2_block_idx_y; } void draw_image(cv::Mat& image, Snake& snake, Direction new_direction, const Block& block, Food& food) { image = cv::Scalar(0); // clean canvas const int w = image.cols; const int h = image.rows; // TODO: 检查 block 大小是否符合窗口整除倍数关系 int num_bodies = snake.points.size(); printf("num bodies is %d\n", num_bodies); snake.directions.pop_back(); snake.directions.push_front(new_direction); for (int i = 0; i < num_bodies; i++) { Point& point = snake.points[i]; if (snake.directions[i] == RIGHT) { point.x = (point.x + block.width) % w; } else if (snake.directions[i] == LEFT) { point.x = (point.x - block.width + w) % w; } else if (snake.directions[i] == TOP) { point.y = (point.y - block.height + h) % h; } else if (snake.directions[i] == BOTTOM) { point.y = (point.y + block.height) % h; } } for (int i = 0; i < num_bodies; i++) { Point& point = snake.points[i]; int block_x_idx = point.x / block.width; int block_y_idx = point.y / block.height; draw_block(image, block_x_idx, block_y_idx, block.width, block.height, snake.color); } // 随机生成食物的绘制 Point& pos = food.pos; if (!food.exist) { pos.x = get_random_int(0, w); pos.y = get_random_int(0, h); food.exist = true; } if (is_same_block(snake.points[0], food.pos, block)) { food.exist = false; Point new_point; Point& last_point = snake.points[num_bodies - 1]; Direction last_direction = snake.directions[num_bodies - 1]; int x_shift = 0; int y_shift = 0; if (last_direction == RIGHT) { x_shift = -1; } else if (last_direction == LEFT) { x_shift = 1; } else if (last_direction == TOP) { y_shift = 1; } else if (last_direction == BOTTOM) { y_shift = -1; } new_point.x = (last_point.x + x_shift * block.width) % w; new_point.y = (last_point.y + y_shift * block.height) % h; snake.points.push_back(new_point); snake.directions.push_back(last_direction); } else { int block_x_idx = pos.x / block.width; int block_y_idx = pos.y / block.height; draw_block(image, block_x_idx, block_y_idx, block.width, block.height, food.color); } } int main() { const int w = 640; const int h = 480; cv::Size size; size.width = w; size.height = h; cv::Mat image(size, CV_8UC3); image = cv::Scalar(0); Block block(40, 40); Point center = get_center_point(w, h); Direction direction = RIGHT; Snake snake(center, direction, cv::Scalar(255, 255, 255)); for (int i = 1; i < 4; i++) { Point pt; pt.y = center.y; pt.x = center.x - block.width * i; snake.points.push_back(pt); snake.directions.push_back(direction); } int i = 0; Food food; food.color = cv::Scalar(255, 0, 0); food.exist = false; const char* title = "snake"; cv::namedWindow(title); while (true) { draw_image(image, snake, direction, block, food); i++; std::string image_path = std::to_string(i) + ".png"; cv::imwrite(image_path, image); cv::imshow(title, image); int key_ret = cv::waitKey(500); // 官方文档说用 waitKeyEx; 下面的数值是我在 Linux KDE 下试出来的。 printf("key_ret is %d\n", key_ret); switch (key_ret) { case 81: direction = LEFT; break; case 83: direction = RIGHT; break; case 82: direction = TOP; break; case 84: direction = BOTTOM; break; case 27: direction = EXIT; break; } if (direction == EXIT) { printf("Bye~\n"); break; } } return 0; }
这篇关于简易贪吃蛇-基于C++和OpenCV的实现的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享
- 2024-11-22ansible 的archive 参数是什么意思?-icode9专业技术文章分享
- 2024-11-22ansible 中怎么只用archive 排除某个目录?-icode9专业技术文章分享
- 2024-11-22exclude_path参数是什么作用?-icode9专业技术文章分享
- 2024-11-22微信开放平台第三方平台什么时候调用数据预拉取和数据周期性更新接口?-icode9专业技术文章分享
- 2024-11-22uniapp 实现聊天消息会话的列表功能怎么实现?-icode9专业技术文章分享
- 2024-11-22在Mac系统上将图片中的文字提取出来有哪些方法?-icode9专业技术文章分享
- 2024-11-22excel 表格中怎么固定一行显示不滚动?-icode9专业技术文章分享
- 2024-11-22怎么将 -rwxr-xr-x 修改为 drwxr-xr-x?-icode9专业技术文章分享
- 2024-11-22在Excel中怎么将小数向上取整到最接近的整数?-icode9专业技术文章分享