Skip to content

搞英语 → 看世界

翻译英文优质信息和名人推特

Menu
  • 首页
  • 作者列表
  • 独立博客
  • 专业媒体
  • 名人推特
  • 邮件列表
  • 关于本站
Menu

C 中的 ASCII 蛇

Posted on 2024-11-20

介绍

我口袋里有一些小型/微小的游戏开发项目,但我没有在我的 Bearblog 帐户中记录这些项目。第一个是仅使用curses 库的ASCII 贪吃蛇游戏。将会有另一篇关于使用 Raylib 制作贪吃蛇游戏的类似文章。

#1 图书馆

#include <curses.h> #include <stdbool.h> #include <stdlib.h> #include <time.h> #include <unistd.h> #include <string.h>

#2 常量、结构和全局变量

#define MAX_SCORE 256 #define FRAME_TIME 180000 typedef struct { int x; int y; } Vec2; int score = 0; char score_message[32]; bool skip = false; bool is_running = true; int screen_width = 25; int screen_height = 20; // initialize screen WINDOW *win; // snake Vec2 head = {0, 0}; Vec2 segments[MAX_SCORE + 1]; Vec2 dir = {1, 0}; // berry Vec2 berry;

#3 函数声明

void game_over(); void init(); void quit_game(); void restart_game(); bool collide(Vec2 a, Vec2 b); bool collide_snake_body(Vec2 point); Vec2 spawn_berry(); void process_input(); void draw_border(int y, int x, int width, int height); void draw(); void update(); void game_over();

#4 碰撞:碰撞函数

  • 碰撞:检查两个点是否具有相同的坐标。
  • collide_snake_body:检查点是否与蛇的身体碰撞。
 bool collide(Vec2 a, Vec2 b) { if (ax == bx && ay == by) { return true; } else return false; } bool collide_snake_body(Vec2 point) { for (int i = 0; i < score; i++) { if (collide(point, segments[i])) { return true; } } return false; }

#5 产卵浆果

  • 在屏幕上为“浆果”生成一个随机位置,确保它不在蛇的头部或身体上,并且从边缘开始有 1 像素的填充
Vec2 spawn_berry() { // spawn a new berry with 1 pixel padding from edges and not inside of the snake Vec2 berry = { 1 + rand() % (screen_width - 2), 1 + rand() % (screen_height - 2) }; while (collide(head, berry) || collide_snake_body(berry)) { berry.x = 1 + rand() % (screen_width - 2); berry.y = 1 + rand() % (screen_height - 2); } return berry; }

#6 游戏区:绘制边框

  • 使用 ncurses 库函数,使用指定宽度和高度的 ASCII 字符在屏幕上绘制边框。
 void draw_border(int y, int x, int width, int height) { // top row mvaddch(y, x, ACS_ULCORNER); mvaddch(y, x + width * 2 + 1, ACS_URCORNER); for (int i = 1; i < width * 2 + 1; i++) { mvaddch(y, x + i, ACS_HLINE); } // vertical lines for (int i = 1; i < height + 1; i++) { mvaddch(y + i, x, ACS_VLINE); mvaddch(y + i, x + width * 2 + 1, ACS_VLINE); } // bottom row mvaddch(y + height + 1, x, ACS_LLCORNER); mvaddch(y + height + 1, x + width * 2 + 1, ACS_LRCORNER); for (int i = 1; i < width * 2 + 1; i++) { mvaddch(y + height + 1, x + i, ACS_HLINE); } }

#7 退出并重新启动

  • quit_game:彻底退出游戏,恢复终端状态。
  • restart_game:将游戏状态重置为初始值。
 void quit_game() { // exit cleanly from application endwin(); // clear screen, place cursor on top, and un-hide cursor printf("\e[1;1H\e[2J"); printf("\e[?25h"); exit(0); } void restart_game() { head.x = 0; head.y = 0; dir.x = 1; dir.y = 0; score = 0; sprintf(score_message, "[ Score: %d ]", score); is_running = true; }

#8 初始化函数

此代码通过以下方式初始化游戏:

  • 为随机数生成器播种
  • 使用 ncurses 设置终端窗口
  • 初始化颜色和颜色对
  • 随机将“浆果”放置在屏幕上
  • 初始化分数消息
void init() { srand(time(NULL)); // initialize window win = initscr(); // take player input and hide cursor keypad(win, true); noecho(); nodelay(win, true); curs_set(0); // initialize color if (has_colors() == FALSE) { endwin(); fprintf(stderr, "Your terminal does not support color\n"); exit(1); } start_color(); use_default_colors(); init_pair(1, COLOR_BLUE, -1); init_pair(2, COLOR_GREEN, -1); init_pair(3, COLOR_YELLOW, -1); berry.x = rand() % screen_width; berry.y = rand() % screen_height; // update score message sprintf(score_message, "[ Score: %d ]", score); }

#9 游戏结束状态

game_over:显示“游戏结束”消息并等待用户输入以重新启动或退出。

 void game_over() { while (is_running == false) { process_input(); mvaddstr(screen_height / 2, screen_width - 16, " Game Over "); mvaddstr(screen_height / 2 + 1, screen_width - 16, "[SPACE] to restart, [ESC] to quit "); attron(COLOR_PAIR(3)); draw_border(screen_height / 2 - 1, screen_width - 17, 17, 2); attroff(COLOR_PAIR(3)); usleep(FRAME_TIME); } }

#10 运动 – process_input

此代码通过以下方式处理用户输入:

  • 从终端窗口读取按键
  • 根据箭头键按下更新蛇的方向
  • 防止蛇立即改变方向
  • 按下空格键时重新启动游戏
  • 按下退出键时退出游戏
void process_input() { int pressed = wgetch(win); if (pressed == KEY_LEFT) { if (dir.x == 1) { return; skip = true; } dir.x = -1; dir.y = 0; } if (pressed == KEY_RIGHT) { if (dir.x == -1) { return; skip = true; } dir.x = 1; dir.y = 0; } if (pressed == KEY_UP) { if (dir.y == 1) { return; skip = true; } dir.x = 0; dir.y = -1; } if (pressed == KEY_DOWN) { if (dir.y == -1) { return; skip = true; } dir.x = 0; dir.y = 1; } if (pressed == ' ') { if (!is_running) restart_game(); } if (pressed == '\e') { is_running = false; quit_game(); } }

#11 更新游戏状态和抽奖功能

更新:

  • 更新蛇的分段和位置
  • 检查与身体或墙壁的碰撞
  • 处理吃浆果并更新分数
  • 等待一小段时间来控制游戏的帧率

画:

  • 清除屏幕并重新绘制游戏元素
  • 使用不同的颜色绘制浆果、蛇和边框
  • 显示分数消息
void update() { // update snake segments for (int i = score; i > 0; i--) { segments[i] = segments[i - 1]; } segments[0] = head; // move snake head.x += dir.x; head.y += dir.y; // collide with body or walls if (collide_snake_body(head) || head.x < 0 || head.y < 0 \ || head.x >= screen_width || head.y >= screen_height) { is_running = false; game_over(); } // eating a berry if (collide(head, berry)) { if (score < MAX_SCORE) { score += 1; sprintf(score_message, "[ Score: %d ]", score); } else { // WIN! printf("You Win!"); } berry = spawn_berry(); } usleep(FRAME_TIME); } void draw() { erase(); attron(COLOR_PAIR(1)); mvaddch(berry.y+1, berry.x * 2+1, '@'); attroff(COLOR_PAIR(1)); // draw snake attron(COLOR_PAIR(2)); for (int i = 0; i < score; i++) { mvaddch(segments[i].y+1, segments[i].x * 2 + 1, ACS_DIAMOND); } mvaddch(head.y+1, head.x * 2+1, 'O'); attroff(COLOR_PAIR(2)); attron(COLOR_PAIR(3)); draw_border(0, 0, screen_width, screen_height); attroff(COLOR_PAIR(3)); mvaddstr(0, screen_width - 5, score_message); }

#12 主要

此代码是游戏的主要入口点:

  • 处理命令行参数以设置屏幕尺寸
  • 初始化游戏状态
int main(int argc, char *argv[]) { // process user args if (argc == 1) {} else if (argc == 3) { if (!strcmp(argv[1], "-d")) { if (sscanf(argv[2], "%dx%d", &screen_width, &screen_height) != 2) { printf("Usage: snake [options]\nOptions:\n -d [width]x[height]" "\tdefine dimensions of the screen\n\nDefault dimensions are 25x20\n"); exit(1); } } } else { printf("Usage: snake [options]\nOptions:\n -d [width]x[height]" "\tdefine dimensions of the screen\n\nDefault dimensions are 25x20\n"); exit(1); } init(); while(true) { process_input(); if (skip == true) { skip = false; continue; } // ---------- update ---------- update(); // ------------ draw ------------ draw(); } quit_game(); return 0; }

结果

c-蛇-ascii

原文: https://hwisnu.bearblog.dev/ascii-snake-in-c/

本站文章系自动翻译,站长会周期检查,如果有不当内容,请点此留言,非常感谢。
  • Abhinav
  • Abigail Pain
  • Adam Fortuna
  • Alberto Gallego
  • Alex Wlchan
  • Answer.AI
  • Arne Bahlo
  • Ben Carlson
  • Ben Kuhn
  • Bert Hubert
  • Bits about Money
  • Brian Krebs
  • ByteByteGo
  • Chip Huyen
  • Chips and Cheese
  • Cool Infographics
  • Dan Sinker
  • David Walsh
  • Dmitry Dolzhenko
  • Elad Gil
  • Ellie Huxtable
  • Ethan Marcotte
  • Exponential View
  • FAIL Blog
  • Founder Weekly
  • Geoffrey Huntley
  • Geoffrey Litt
  • Greg Mankiw
  • Henrique Dias
  • Hypercritical
  • IEEE Spectrum
  • Investment Talk
  • Jaz
  • Jeff Geerling
  • Jonas Hietala
  • Josh Comeau
  • Lenny Rachitsky
  • Lou Plummer
  • Luke Wroblewski
  • Matt Stoller
  • Mert Bulan
  • Mostly metrics
  • News Letter
  • NextDraft
  • Non_Interactive
  • Not Boring
  • One Useful Thing
  • Phil Eaton
  • Product Market Fit
  • Readwise
  • ReedyBear
  • Robert Heaton
  • Ruben Schade
  • Sage Economics
  • Sam Altman
  • Sam Rose
  • selfh.st
  • Shtetl-Optimized
  • Simon schreibt
  • Slashdot
  • Small Good Things
  • Taylor Troesh
  • Telegram Blog
  • The Macro Compass
  • The Pomp Letter
  • thesephist
  • Thinking Deep & Wide
  • Tim Kellogg
  • 英文媒体
  • 英文推特
  • 英文独立博客
©2025 搞英语 → 看世界 | Design: Newspaperly WordPress Theme