{"id":16977416,"url":"https://github.com/zhuyali/racing","last_synced_at":"2026-05-14T12:34:06.579Z","repository":{"id":148440401,"uuid":"96865795","full_name":"zhuyali/racing","owner":"zhuyali","description":"Racing game on Linux","archived":false,"fork":false,"pushed_at":"2017-07-13T07:24:50.000Z","size":8,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-07T01:48:47.530Z","etag":null,"topics":["c","linux","screen-program","signal"],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zhuyali.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-07-11T07:45:03.000Z","updated_at":"2017-07-11T08:49:15.000Z","dependencies_parsed_at":"2023-09-12T04:00:58.751Z","dependency_job_id":null,"html_url":"https://github.com/zhuyali/racing","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/zhuyali/racing","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhuyali%2Fracing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhuyali%2Fracing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhuyali%2Fracing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhuyali%2Fracing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zhuyali","download_url":"https://codeload.github.com/zhuyali/racing/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhuyali%2Fracing/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33025108,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-13T13:14:54.681Z","status":"online","status_checked_at":"2026-05-14T02:00:06.663Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","linux","screen-program","signal"],"created_at":"2024-10-14T01:28:54.384Z","updated_at":"2026-05-14T12:34:06.562Z","avatar_url":"https://github.com/zhuyali.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Racing\n\n## 1.游戏简介\n\n赛车，很多人看到这个词，脑海中首先会浮现出两辆车。但是我所做的赛车并非大家所想的那样。\n\n我所做的赛车从本质上来讲是一个躲避障碍物的游戏：有一辆车会在屏幕的最下方，障碍物不断地从屏幕上方掉落，会造成一种滚屏的效果，这时通过人为地输入‘A’和‘D’来控制赛车的左右移动以躲避障碍物，从画面上看就好像一辆车一直在前进的过程中不断躲避障碍，因而又叫“赛车”。每前进一格，分数加一，当碰上障碍物时，游戏立刻停止，并显示总得分与输赢情况；也可以通过进入游戏后输入‘Q’来退出游戏。关于游戏状态的提示信息会出现在屏幕右上角的提示框中。\n\n## 2.游戏客户端思路\n\n由于第一次对Linux进行屏幕编程，因而整个游戏的编写过程也是由易到难的。大致思路为：\n\n登录界面——\u003e游戏界面边框——\u003e赛车的移动动画——\u003e利用随机数生成障碍物坐标——\u003e制作出屏幕滚动效果——\u003e加入碰撞条件，并制作碰撞画面——\u003e制作提示框——\u003e连接服务器——\u003e与服务器进行通话。\n\n为此，我做了一些准备工作：由于最终整个界面效果位于屏幕的正中间，并且很多坐标也都依赖于屏幕的特殊位置(如中间，边界等)，因而我设置了一些常量值以方便以后的计算：MIDROW、MIDCOL、TOPDGE、DOWNDGE、LEFFDGE、RIGHTDGE。\n\n登录界面：对于竞赛的部分，我的设想是不同客户端连接服务器后，服务器通过比分判断输赢，然后将胜者的名字输出给各个客户端，因而需要有一个名字作为标识符更加直观，所以引入了登录功能，目的是接收用户输入的用户名。从登录界面接收username后将其写入socket中以告知服务器。\n\n游戏界面边框：主要是通过反白显示来画出游戏的两条界面分隔线。\n\n赛车移动动画：由于涉及到用户的键盘输入，因而我使用了termios结构体来对终端进行控制，使得终端关闭回显与标准输入处理，然后通过使用while循环不断等待键盘输入，仅当输入‘A’，’D’和’Q’时，会予以左移，右移和退出游戏的操作，其余输入忽略。界面效果主要通过不断地进行move和addstr操作来更新。\n\n障碍物生成：我的想法是在每行设置一个障碍物，障碍物的列数随机生成。因而使用srand和rand函数生成指定范围的一个整数，然后根据该坐标来进行障碍物坐标的确定。但是使用该方法却存在一个明显的问题：由于使用系统时间做随机数种子，因而每秒钟随机数种子才会变化一次，所以导致生成障碍物的时间较长，此处仍待改进。\n\n滚屏效果：实质上就是将障碍物不断地向下移动，最下方的障碍物消失，最上方填充新的障碍物，不断循环。我使用了定时器来为障碍物设置滚动间隔，当经过一定时间后，定时器发出信号，这时在程序中加入信号处理程序signal(SIGALRM,sigResponse)来对本次信号做出处理，处理函数为void sigResponse(int)，该函数主要有两个职能。职能一就是使用for循环将每一个障碍物进行滚动操作，同时对全局变量分数grade++。然后在函数中判断grade的值，分数值越高，那么定时器的间隔时间就越短，障碍物的下落速度就越快。\n\n碰撞：每当定时信号来一次，就同时需要判断小车是否与障碍物相撞，此时便涉及到sigResponse的第二个职能：对碰撞条件进行判断，当障碍物下落到与小车的高度相等的位置及以下\u0026\u0026障碍物的纵坐标与小车的纵坐标重合时，判断已碰撞，那么将画面停止并且弹出对话框提示分数，并且将分数输出到socket中，再从socket中接收服务器发送来的输赢信息并进行显示，最后退出游戏。\n\n制作提示框：考虑到游戏界面的生成时间比较长，为了使用户体验更佳，所以加入了提示框以提示用户当前的游戏状态。\n\n连接服务器：主要通过socket与服务器进行通话，客户端连接的建立主要分为两步：socket和connect，前者主要负责建立socket，后者负责通过服务器的IP地址和端口号，将客户端连接到服务器，其中有一些健壮性输出。在做完连接后，运行该客户端程序便需要输入服务器的IP地址和端口号，因而在main函数中也有了关于参数相关健壮性的判断。\n\n与服务器通话：主要有两步：写和读。写出到服务器的是用户名以及对应分数，从服务器端读入的是赢家姓名。不过没有处理两个客户端重名的情况，予以忽略。\n\n## 3.游戏服务端思路\n\n其实一开始做服务器的时候我的思维是很混乱的，因为处理完客户端的输入后要进行返回，但是返回给哪个客户端呢？这一直是困扰我的一个问题。后来我换了一个思路，给每个客户端都发一样的，由客户端自己判断结果即可。因而就有了现在的服务器思路。\n\n首先，服务器一定得实现多线程，否则无法同时处理多客户的请求，所以主要的工作量落在多线程的处理函数handle_msg上。在该函数中，与客户端相对的主要有两次读操作和一次写操作，读到的分别是用户的名字和分数，写出的为最高分者的名字，由于要得出最高分者，因而涉及比较操作；由于用户名和分数相对于，因而我使用结构体存储每个用户的信息。\n\n使用qsort()对结构体进行排序，排序的依据是结构体中的分数，排序后结构体数组下标为0的数据是当前最高分数者的姓名和分数信息，然后仅将最高分者的姓名写入到客户端中。客户端读入服务器消息后，比较自己的用户名与服务器发送来的用户名，若相等，则输出赢；否则，输出输并且显示出赢家的名字。\n\n## 4.关键代码\n\n### 4.1. 客户端关键代码\n\n![](http://cdn1.showjoy.com/images/18/18942f8c1f7242b78afbf66d937ca6a6.png)\n\n该函数是用来响应信号SIGALRM的。其中的if_else条件语句的作用是根据用户的分数来控制障碍物下落的速度，但是在此处有一个未能解决的问题：就是当定时器的时间间隔很短的时候，障碍物会变得十分稀疏：我判断出现该问题的原因应该是本次计时器的操作还没有执行完毕，就已经开始执行下次操作。在for循环内部，主要是判断小车的碰撞条件，若碰撞了就直接输出碰撞画面，否则移动障碍物。函数能执行到最后说明没有发生碰撞，此时分数++。\n\n![](http://cdn1.showjoy.com/images/2f/2f5a337e728344bf8a5b9c3270deb01b.png)\n\n该函数主要封装了连接服务器的代码，包含的两个参数分别为：服务器的IP地址和端口号，返回值为tcp_socket。其中主要进行了socket操作和connect操作，分别负责建立socket和与服务器建立连接。\n\n### 4.2. 服务端关键代码\n\n![](http://cdn1.showjoy.com/images/d1/d17b5497f6114e158539a66816e95af9.png)\n\n此while循环主要负责循环不断地接收从客户端来的请求而不阻塞服务器，其中主要用到的技术是多线程技术：对于每一个客户端的请求建立一个线程用于单独处理客户端逻辑，此处usleep(300)是可有可无的，我加入该函数的主要目的是为了尽量避免函数内部对arrIndex与外部对arrIndex的修改同时进行。\n\n![](http://cdn1.showjoy.com/images/ae/aeb0bc15af9949b285d7607086540953.png)\n\n该函数是服务器端每一个线程处理客户端逻辑的程序。其中读操作两次：分别为读取用户名，读取分数。写操作一次，用来写出赢家。而赢家的判断主要通过qsort排序函数来根据客户的分数对客户从大到小进行排序(排序的具体逻辑通过cmp函数实现)每次写出的都是位于数组下标为0的用户名称。但是在编写该服务器端的时候，必须加入fflush(stdout)函数，否则会出现读写的问题，原因尚未弄明白。\n\n## 5.运行结果\n\n### 5.1. 登录界面\n\n![](http://cdn1.showjoy.com/images/36/3687b49733a94bc48269015e89c49595.png)\n\n### 5.2. 游戏运行界面\n\n![](http://cdn1.showjoy.com/images/f7/f7770a2d847f4a578512f3a00420cad9.png)\n\n### 5.3. 显示分数和赢家\n\n![](http://cdn1.showjoy.com/images/46/469ee22fd5084c15b61ec33aad07b326.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzhuyali%2Fracing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzhuyali%2Fracing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzhuyali%2Fracing/lists"}