1个系统节拍 c语言_【菜鸡C语言】菜鸡鼓起勇气用Dev-c++打起节奏来了

动次打次动次打次,打好节奏后,要进一步升华!学C语言这么久,不想搞点事情?5ca5f088d4f5648cb6aa0b03a5473418.png

本文中纯属原创,未经授权禁止转载

本篇幅巨长,非专业人士可直接下拉至结尾看成果,然后点个赞¬∨¬

学编程的一定不陌生,大多数学校的微机室中装着这样一款C语言萌新再熟悉不过的软件:

Dev-c++!

众所周知,Dev是入门萌新用起来最方便的C语言编程软件之一,不少人都用它来竞赛、学习……

但好像没什么大型游戏是用它编出来的!

许多大佬编游戏时用的都是Vista、CodeBlock等等编译软件,这可就让Dev玩家有点陌生了

而且像up这样从初二到高一一直在学算法,退役NOIP后又没学C语言的其他高级语句,刚刚从洛谷转过来,只用惯了Dev,只会玩控制台的菜鸡,想做个小游戏怎么办?

7aba61dd06bb27e89222f3de4d0bb298.png“你那两只眼睛是窟窿吗,这么大的实力差距看不出来吗”

大家都知道,Dev方便是方便,但就是库里的头文件少很多,比如著名的graphics.h,啥都要自己装,很麻烦!

所以,既然只会用控制台的程序,那就菜到底好了!

b416b2fb4a33470f96d82ff47376356d.png

废话了那么多,那就讲一下我们今天的目的:

在Dev-c++里做出一个节奏游戏来!

23b2cd089c2e312ec85d2f969726599f.png音乐自行脑补

(哈?为什么是节奏游戏?电音谁不喜欢啊!∩∨∩)

话不多说,先放代码示例一下    ↓9c1c2f75a25e2c3c01345acb4f3d04ec.png主函数

88e32fcd905df48ac5460a2542df5783.pngvoid列表

这时可能有同学问了,你写这么多函数不是增加理解难度了嘛?

别说,写这么多函数反而让代码的逻辑更简单了!打个不恰当的比方,有一个大柜子,你要用的工具都在里面,用的时候拿出来,用完放回去就好了。59b5a37e29377c90254eee3144d58bdc.png用完就放回去!

然后我们就面临着几个平时常见的小问题:

Part.1 基本准备

I.咋个隐藏光标?

II.咋个输入立即读入不回显?

III.咋个控制颜色?

其实这些度娘都知道的,大家可以勤快点去搜一下,这里就顺便为大家统一一下:

【已经知道的可以跳过!】

[正文走你!]

I.把光标藏起来

写这种小游戏时,有个光标在那里一闪一闪的是我们的硬伤之一,而解决这个硬伤的方法其实也就两段代码就完了:[来来来复制一波]

CONSOLE_CURSOR_INFO cursor_info = {1,0};

SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);

//CONSOLE_CURSOR_INFO:存光标信息的结构体(第一个数值:光标厚度    第二个数值:可见性)

//SetConsoleCursorInfo:设置控制台光标的厚度和可见度

//GetStdHandle:获取句柄(Windows API函数)

//STD_OUTPUT_HANDLE:标准输出的句柄

//cursor_info:你家光标,可以改名

再把它装进一个函数中[方便以后调用]:f8972d77f993b8e0a46422e0c7890021.png头文件:windows.h

第一部分完成了!(H₂O)

II.立马读取键盘输入

每次我们运行代码时,都得嗒嗒键盘几下,最后再点个回车。

懒人秘籍:解放你的回车键!

这也很好解决

把你的scanf("balabala")或getchar()换成getch()!

语法:

char c;

c=getch();

可能有的人这样用过,但发现编译不了,那你一定忘加这个了:

头文件:conio.h[记得打上!]

干货:

getchar():读取字符,输入完按回车再读取,带回显

getche():读取字符,输入后立刻读取不用按回车,带回显

getch():读取字符,输入后立刻读取不用按回车,不带回显

第二部分完成了!(2H₂O)

III.让游戏更有色♂调

谁不希望色彩与节奏的完美搭配呢?

很多时候,我们面对着那黑白的控制台程序一眼茫然,一脸不爽……

肯定有人试过用system("color ??")来进行染色,但发现一染就染到全屏了,这不和没染有什么区别嘛!

所以我们主要不用这个,因为这里有个更妙的玩意!

SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),?);

//GetStdHandle(STD_OUTPUT_HANDLE)前面解释过了,获取输出句柄

//SetConsoleTextAttribute:设置当前光标位置以后将要输出的内容的颜色

代表颜色的关键值,大家也看出来是哪个("?")了,那怎么知道哪个数是哪种颜色咩?

这就来了!b2c0c16fc0d62a82ce8706d3b6b54a95.png稍微有点计算机知识的人都知道,计算机一共有256种标准颜色,控制台也不例外

这些颜色分为前景和背景,想要哪种颜色直接输数值就好了

也一样可以把它放函数里面:d9eaf7526878e4ba21f9a6dfa6417b14.png头文件windows.h

颜色解决了,那如何在我们想要染色的地方把它渲染出来呢?

这就轮到光标出场啦!放语句!

HANDLE hOut;

hOut=GetStdHandle(STD_OUTPUT_HANDLE);

COORD pos= {x,y};

SetConsoleCursorPosition(hOut,pos);

//GetStdHandle(STD_OUTPUT_HANDLE):你再叫我解释我就难了

//COORD:结构体,内容是光标的位置(第一个数值列,第二个数值排)

//SetConsoleCursorPosition:把光标强行绑架到pos(x,y)那去

继续放进函数里方便调用49e743205aa7936dec386c4fec66e51f.png头文件windows.h

这里为了方便理解,把x和y调换了个位置,表示第几排第几个(从0开始的异世界之旅数)

前面不远处才提到过,染色的方法是在此时光标位置后的输出内容生效(也就是你输出什么它就染什么,直到你下一次改变色彩数值)

所以现在,我们的光标可以移动了,颜色可以控制了,想造出一个多彩的节奏游戏还不简单?¬∪¬

第三部分完成了!(H₂O)a77934c446d6a5a23dfa727dc2bc8472.png效果演示

接下来,就开始编写主体了。

Part.2  游戏主干

I.游戏的各种菜单

II.音乐处理

III.节奏生成与判断

I.游戏菜单设置

这个过程很简单,只要把输出和一大堆函数返回的逻辑弄清楚就可以了

a.菜单页面

eaac435aa8af23b2e04d87c9202fec9a.png一大堆printf不用我解释了

b.操作选项

前面对键盘的读入已经讲过了,所以我们:

char _;

while(1)

{

_=getch();

}

然后对于每一次读入都要有一个箭头指向对应的选项,得把这个箭头显示出来

int a,b;

char _;

a=1;//默认为第一个选项"游玩"

while(1)

{

_goto(a+1,0);//a代表的选项所在位置(从0开始数),把光标移动到本排开头

SetConsoleTextAttribute(handle,a+25);//对接下来将要输出的光标染色

b=a;//记录上一次的位置,以输出空格代替

printf(" >");//输出箭头

_=getch();//读取玩家操作

_goto(b+1,0);//把光标移到上一次箭头位置的开头

printf("  ");//用空格还原

if(a>6) a-=6;//对a取余,防止越界,用%也行

else if(a<1) a+=6;

}

【大家也看出来了,菜单的选项颜色是递增的,这里方便箭头的颜色随a值改变】    接下来,我们只需要对键入的数据进行判断就OK了

inta,b;

char _;

a=1;

while(1)

{

_goto(a+1,0);

SetConsoleTextAttribute(handle,a+25);

b=a;

printf(" >");

_=getch();

if(_==72||_=='w'||_=='W') a--;//①

else if(_==80||_=='s'||_=='S') a++;//②

else if(_==27) quit();//③,注意这里调用的quit是自己创造的函数,后面会写

else if(_==13)//④

return a;//返回对应选项的值,函数的递归

else continue;//感觉这句多余的

_goto(b+1,0);

printf("  ");

if(a>6) a-=6;

else if(a<1) a+=6;

}

[①②③④不多讲了,ASCII码,分别是↑、↓、Esc、回车]

最后,加点“料”整合进一个函数里

f8e8deba6543a7a60cb3928cbb2831f3.png具体增加细节就不讲了,大家应该都看得懂的

好好好关键的要来了!

II.音乐处理

那么接下来,如何将劲爆的主料加入到我们的Project中呢?

a.搞一个音乐目录

这里就要用到system的命令提示符dir了

system("dir /b *.wav>> sted.in");

作用:将本文件夹内(不包含子文件夹)的所有wav文件的名称[名称不要过长!]列举于sted.in文件内(没有会创建)

对没错,我们把本文件夹内所有的音乐名称都收在一个文件里了,这时只需要再打开文件读取就好了(文件读取不解释,这里用的是文件指针)

int i;

FILE *fin=NULL;

fin=fopen("sted.in","rb");

i=1;

while(!feof(fin))//判断文件是否读完

{

fgets(name[i],100,fin);

name[i][strlen(name[i])-6]='\0';//把".wav"去掉,加上终止符'\0',只保存音乐名

i++;

}

num=i-2;

fclose(fin);

一样继续打包

d1e1bcfa7b5cc60f79034357147aab9f.png最后一排别管

这样,我们就把文件夹下所有wav文件名装进name[]的字符数组里面了

选择音乐的操作和前面一样

至于输出列表和其他调整,对大家来说一定不是难事26bb9cae3bf7d7466eff8c1564b50ee9.png代码及效果演示

[其中s变量以及其后各种函数后面会写的]

是时候放歌了!

b.让音乐唱起来和停下来

PlaySound(文件名字符串,NULL,属性);

(其实第二个参数可以设置为其他的,不过太过复杂而且我们用不到)

【有的人问为什么不用其他能够播放mp3等其它类型的音乐,因为我们的环境是Dev-c++,大多数默认环境设置下PlaySound是最‘稳’的!】[唯一的缺点就是只能播放wav文件]

PlaySound 顾名思义,这就是我们要用到的播放语句

头文件及准备:

#include//windows.h在这里真是万能的

#include//与多媒体有关的接口[windows环境下]

#pragma commen(lib,"winmm.lib")//链接winmm.lib这个库(播放需要)

具体数据:

文件名字符串:就是音乐加上后缀名的全文件名

属性,用“|”连接(干货):

SND_FILENAME:指明音乐的路径及文件名(最好加上,确保稳定)

SND_ASYNC:音乐异步播放(边执行代码边播放)

SND_SYNC:音乐同步播放(播放完再执行下一段代码)

SND_LOOP:音乐循环播放

SND_PURGE:停止音乐的播放

[先列这四种,其他的大家自己去百度吧]

至于音乐的文件名,我们在前面列表时已经存在一个全局变量name[]中了

如果大家认真看过刚才那幅图片,就会发现多了几段字符处理的玩意

这时就是大家都熟悉的代码

char music_name[70];

memset(music_name,0,70);//将music_name腾空

strcat(music_name,name[t]);//把name[t]加到music_name中去

strcat(music_name,".wav");//把".wav"加到music_name中去

这样一番洗刷刷之后,music_name就是我们要播放的文件名啦!

代入刚才那个语句,播放暂停就完全不是问题了!

//播放:PlaySound(music_name,NULL,SND_FILENAME|SND_ASYNC);

//停止:PlaySound(NULL,NULL,SND_PURGE);

然后开始玩家的拿手部分!

III.节奏走起!

a.生成节奏

由于去分析波形文件太过于麻烦而且不太精准,这里手动生成节奏对我们来说更简单,换句话说就是手动踩点!

节奏游戏大家都玩过,玩法之一就是总会有一个两个的节奏规律地顺着轨道滑过来

那如何确定这些节奏的规律和间隔呢?两个因素:记录和时间

1.时间

最简单粗暴的方法:Sleep()    [这里就不解释了]

只要将Sleep的数值修改到越小,那时间间隔就越小,踩点就更加精准,当然节奏的移动速度也就越快!

为什么?

这里就要讲到我们节奏的移动方式了

没错就是不断输出来造成节奏向上移动的动画(菜鸡操作)

c0457a30e02de62c4a22a1806f43719b.png像图中这样(忘加'\n'了)

加上时间间隔的输出,我们就可以做到让节奏跑起来了!(推荐为30[毫秒])

2.记录

用我们前面说过的键入读取和数组就可以了

数组(up命名为oops[],名字up乱取的)存储01来判断是否生成节奏

再在打出节奏时用rand()随机值来给节奏染随机颜色

这里就直接放代码了

201fe11db49dc588dc01fb89b2b542a2.png代码不难,理解得透的

有一点提一下,就是里利用了kbhit()函数

kbhit():判断当前是否有按键按下(只要缓冲区有数据在,就视为键盘按下)

最后完整打包1b64962dd80146ca1ed5be0fb977e297.png分成了两段,看序号连接,众多变量的作用不复杂,可以懂

几番操作后,节奏随时间的输出规律就被录入到oops[]里面了

接下来,踩点的时候只需要随时间判断oops[]就可以了

b.踩节奏

和刚才刻录节奏差不了多少,主要主要主主要要的还是键入读取和数组判断

整体和刚才刻录很相似,只是多了些音画延迟的操作

我们用oops[]来作为输出在屏幕上的节奏,另创一个ot[]复制上oops[]的值来作为判断的基础(ot[]与音乐同步),而它们之间时间上的差值就是延迟

用一幅图来解释

ad3a214dd10c521f34d05d84c0147f01.png从屏幕低端开始输出

实际上,我们要的延迟就是我们屏幕能显示的排数

但延迟处理并不难嘛,在for循环里定义个i和j,i用来走oops[],j用来走ot[],i比j多起步一个值(music_delay)就可以了

for(int i=0,j=music_delay+i;i

然后,输出和刚才刻录函数是实打实的一模一样8fa548ddfd92401a3390b989fccee0a8.png一模一样

之后打击判断的键入和刻录也是一毛一样,我们只需要加个判断就好了

(这里为了降低游戏难度,将判断范围扩大到x5)64e203c08ece96edc716f4495a6dc650.png其中cambo是计算连击的次数

注:while那句是将缓存区清零

到了结尾再记录好本次成绩与最大成绩的比较(变量要声明,这里最大值up用的是全局变量)

if(combo>=mco) mco=combo;//最后再更新一次本次游戏中最长连击

if(m_good

if(m_miss

if(m_sum

if(m_cambo

老规矩,整合打包06a3f71e05c6bb9c25d6f2650d9503c9.png好吧我承认,写的代码太长了,为了篇幅着想放图了,前面也是这样

至此,我们的游戏玩法主体已经基本上成形了!

但肯定还差点什么

存档捏?

Part.3  文件存档

实际上,存档问题前面提到过,大家也不会陌生,这里只需要注意一些其它的点

先放大家熟悉不过的模板[未知用x代替]

FILE *fin;

fin=fopen("xxx.xxx","x");

fclose(fin);

这些模板大家一定都或多或少在书上见到过,要用到的fscanf()、fprintf()、fgets()等函数也不陌生,这里就不一一解释了

在fopen()里的第一个参数是音乐存档文件名,最好把存档名合成为音乐名+某个字母+后缀名,方便读取文件时检测。(合成名称的方法不用我说了吧)

这里说一下,因为文件被指针打开"fopen()"后只有到指针被关闭"fclose()"才会更新,所以我们每次用完一个文件指针后就需要立刻关闭,这也是不用freopen的理由(话说freopen是C++的吧?)

a.这首歌的存档到底在不在

我们的程序是可以添加多首歌曲的,它的范围是本文件夹内所有的wav音乐文件(不含子文件夹)

所以当我们添加一首新歌时,我们得让程序明白这是新歌没有任何记录文件

正巧,C语言中恰好有这个功能的语句!

access(存档名字符串,属性)[头文件:unistd.h]

属性分类(干货):

F_OK  (数值为 0):文件是否存在

X_OK  (数值为 1):文件是否有权限执行

W_OK  (数值为 2):文件是否有权限写入

R_OK  (数值为 4):文件是否有权限读取

这个函数返回的是一个值,成功返回0,失败返回-1

有了这个,问题解决了!自己去实现吧!

b.这些文件里是否有东西?

知道文件存不存在后,我们还得知道如果存在的话,里面有没有东西?

文件属性结构体:[头文件: sys/stat.h ]

struct stat f;//f是变量

stat内含有文件的所有属性数据,这里我们只用到st_size,其他大家感兴趣的话可以去百度

stat("tmp.in",&f);//将tmp.in文件的属性导入到f中

if(f.st_size<=0)//判断文件的长度是否为0

printf("上次你并没有保存什么");

例子就是这样,大家也可以去进一步改进

c.更新存档

如果获得了比历史更好的成绩,当然想把它存起来炫耀一下

但直接输出到存档的话,会在原来存档的后面直接跟着输出,怎么达到替换效果?

我们抄近道:把原来的存档删了!

remove(文件名字符串);//移除一个文件

然后再用指针重写一个:

fout=fopen(文件名字符串,"w+");

完成!

最后,需要的语句完全写好后照样打包到一个个函数里13b605c4d76967376a1f915298cebe40.png整合到了一张图里,有点模糊

基本完成了!再来个主函数342999ca44fe4bb3350221424289ed85.png对没错就是开头那张

当然还有很多函数up没有在专栏里写出来,但其实那些函数和up讲的都是千篇一律,大家可以自己试着去敲一下

然后把所有头文件加上11e3b50edebb67c6b08c7127464a3065.png用它不香吗?

最后一句在前面大家一定有注意到cls;这段代码,up把它放预设里了,清屏操作,大家一定知道的!

完成了!

如果你有耐心可以一步一步看到这里,那么很高兴你一定学到了不少

如果你直接就翻到了这里,那么很高兴你来鉴赏up的成果!

因为所有代码详细讲解太过冗杂,所以本文只讲解了部分主干语法

因为我们做的是控制台程序,所以在音画延迟的方面可能会受到处理器的影响,导致半途音画逐渐脱轨,但一般只要处理器的占用比不大就可以保持音画同步

上面的内容可能还有一些概括上的不足,毕竟up太菜了T∀T,有错还请大佬在评论区更正!

up技术有限,程序肯定还有一堆bug,就交给大家一步步去探索吧!

程序在此,提取码:xqbe

https://pan.baidu.com/s/1WnH38kihZRA6t4E9-r6F3g

up总是喜欢用C语言搞些带bug的小程序,这次就选取了技术性语句和玩法都比较有趣的一个,希望能拓展到大家的知识!

最后,感谢大家阅读至此!不妨点个赞再走吧?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值