用户:
ParseYPasy查看:0 回复:3 评论:0 创建时间:2022-01-24T18:03:10
#Part 1 音游的总体概述
我们所称的“音游”,全称“音乐游戏”,或“节奏类游戏”,要求玩家跟随音乐节奏进行对应操作。
狭义上,音游的基本元素:
1.玩家能够击打的Note(音符)
2.随音乐节奏出现的Note
3.操作的判定范围(分为范围型与线型,如一条判定线或一个判定区)
广义上,音游指任何跟随节奏的打击类游戏,如Dance Line,Piano Tile(不同的是它是打击出音,而非音出打击)等
音游最大的特点与要求就是:随节奏打击
任何不随节奏打击的“音游”,或没有节奏也要打击的游戏,都不叫“音游”,充其量算是“打击类游戏”,对于一个音游,如果其谱面游玩时完全不按节奏,我们一般称为“喵谱”
一些基本名词:
Note - 音符,是玩家与游戏的交互对象,需要进行击打/按住/划动等操作以得分
Track/Key - 轨道,是Note下落时的路径,可以把Note比喻成列车,只能在一个Track(轨道)上直线移动
判定线(区) - 是玩家进行操作时应当所在的范围(Note必须在线的位置上或区域内击打,有些游戏例外)
Combo(连击)—— 一般指玩家连续打击的音符数,如果漏击了Note就会归零
判定 - 是游戏基于玩家的击打表现给出的成绩判定
判定范围 - 是取得各种判定的时间误差范围
一般有如下判定:
Perfect(完美)——一般来说是最好的判定,时间误差通常在40ms内(对,是毫秒,这是基础时间单位)
Good(好)/Great ———— 次于Perfect,时间误差通常在40-80ms内(也可能超过这个范围,请以游戏规定为准)
Bad(坏)/ Miss(漏击)———— 这种判定会令Combo断掉,并且不增加分数
谱面 ———— 是由设置好的Note与音乐组成的一个“关卡”,一首音乐的游玩“乐谱”
喵谱 ———— 指不随节奏而布置Note的谱面,或没有节奏硬塞入Note
音游一般类型有:
1.下落式
最经典的是多key(多条轨道)下落式,Note从固定的区域落到判定线上,统称为“有轨下落式”
有DanceRail3(其30key的数量让其很像下面的无轨下落式),Osu!Mania,Deemo(古树旋律),Kalpa,Project Sekai,节奏医生
等
一些另类的下落式是没有key这一概念的,Note可以从任意区域落下,甚至不局限于直线,但最终仍会落到判定线上,称为“无轨下落式”
最经典的是Phigros,Malody,Lanota(有轨无轨均有)
又如太鼓达人,Muse Dash这类横向轨道,某种意义上也算是下落式(因为是朝着判定线落去)
以及为了适应街机而六面开花的舞立方
2.点击式
这类不多,典型的是Osu!中的戳泡泡,全屏幕打击 ,以及Jubeat (16键打击),A Dance Of Dire And Ice(冰与火之舞,线性打击)
3.其他
Cytus/Cytoid 这类Note不动判定线在动的特殊“下落”式
Arcaea 这类有“天空音符”与“音弧”的4key-无轨混合式
音游的Note类型繁多,但大体分为以下几类:
1.Tap
最常见的Note,点击即得分
2.Hold
线性Note,在不同的游戏中,会有“头判”(首次点击时的判定),“尾判”(松开点击时的判定)
3.Drag(有些游戏没有)
Hold与Tap的合并,按住即为最高判定
4.Flick(触控游戏独有)
向特定方向划动即为最高判定
#Part 2 在Kitten 4上的逻辑方法推导
(逻辑部分均以C++编写,我们假定任何片段均为有效程序的一部分)
1.Tap的实现
我们对于Tap,最大的问题在于:
如果单纯
class note{
...
public:
void check(time_t now_time){
if(pass_line && press_key)//如果经过了判定线,并且对应键按下
bust(now_time,press_time);//则根据实际判定时间进行对应判定并销毁这个类
}
};
有一个问题就是:假定有许多Note都经过了判定线,此时按下一个键就会有很多Note一起被判定!
这对于速度极快的纵连是灾难性的(很容易就可以糊过去了!)
那么怎么解决呢?
一个方法是:每次只判定第一个
那怎么判定第一个note呢?
不难想到,我们可以采取列表的思想。
我们为Note加上id作为唯一标识符,并在其出现时,将其放入对应的列表里。
然后每次点击时,检查列表第一个元素,如果在判定范围内,则判定,并将这个Note与列表内的这个id一并销毁,让下一个id成为列表的首元素。
伪代码如下:
std::queue<Note> note_list;//Note队列(在Kitten 4中实现为列表)
while(true){
if(press_key && !note_list.empty())/*当对应键按下且队列中有Note*/ {
if(inbust(note_list.begin()->id)){
bust(note_list.begin()->id);
note_list.pop_front();
}//inbust假定是通过给定的id,查找其判定时间,并决定是否判定的全局方法
}
}
这样,只需要实现inbust(在Kitten4中可以很轻松的从C++转换为一个打包好的函数)以及bust即可,其他均在Kitten4中有模块实现。
我们再设置两个列表:note_track与note_hittime
double note_hittime[] = {};//note的打击时间
int note_track[] = {};//note的对应轨道号码
由此,在制谱的时候,只需手动加入或者自己跟着音乐打,把自己打的假想谱面记录下来,就可以在游玩谱面的时候获取信息了。
至此,我们完成了Tap按键的制作。
2.Drag的制作
直接上代码了,很简单
...
if(press_key){
bust(id,perfect);//直接为Perfect判定
}
3.Hold的制作
我觉得,对于一个自制音游,评判其制作水平的最好办法,就是看它有没有实现Hold。迄今为止我还没有在编程猫看到任何一个实现Hold类型Note的教程或作品。
这是最难的Note类型,因为这是一个复合Note,包括头部判定(与尾部判定),中间还要加入判断是否按住的Note,同时要保持贴图的连贯性……
简单来说,它的判定分为两部分:
1.头部判定
2.中间判定
一:头部判定
偷懒的方法是直接将Tap的判定搬过来,换个贴图就行
需要注意的是头部判定没有松开的说法,因此这时候的按键需要分开处理,不能像之前处理叠键一样,等待下一次键击
二:中间判定
偷懒的方法是直接将Drag的判定搬过来,同样换个贴图(但不能全算作Combo)
最重要的是保持连贯性,由于音游的速度快,需要有很高精度的时序控制,一般我们开的加速模式并不能很好保证时钟的稳定性与精度(计时器),如果使用变量进行高精度计时,会出现无法修复的误差,如果使用计时器,则精度难以保证毫秒级判定的准确性
int milisecond = 0;
for(int i = 0;i<1000;i++){
milisecond++;
usleep(1000);//unistd.h中的函数,这里我们可以理解为“等待1毫秒”
}
//方法1:精度高,稳定性差(每次都会因为感人的调用速度而慢下不到1ms)
double now_time = clock();//我们假定它就是计时器
//方法2:稳定性强(与游戏的主进程是分开的),但只能提供0.1s精度(10ms)的计时,对区间造成了很大的限制
总的来说,如果官方出了稳定且高精度的计时器,那么音游就很容易制作框架,配以优秀的动效与美术设计,就可以有远超所谓“音游”与那些用画笔做出来的所谓“弹幕游戏”以及“卡点游戏”强的多的作品。
现在,全套理论在这里,静待各位的实现!