2019 China Gamejam (CGJ2019) 《新宝岛酱》随记

继CGJ2018,GGJ2019之后,CGJ2019是我的第三场Gamejam。这次的组员有懵圈的程序Osoii(我),摸鱼的建模师红魔(RedMou),爆肝的美术傅绅士(SPG),在打街霸之余帮忙做谱的Actsins(LUC)以及提供了重要远程协助的灵魂第五人瑞翠(Wxzxuir)。

CGJ2019 队牌

 主题

在公布本次主题之前,主办人Simon问了一句现场有多少人参加过GGJ2019,之后感叹“居然有这么多人啊”的时候,我心中不由得一慌,难道是类似的主题?这次CGJ的题目依旧是一张图

CGJ2019 主题

图片中一个有着三角形需求的人得到的却是其他人心形的帮助。第一眼看上去,这是一个很直白的主题:人与人之间的不理解、你付出你认为是好的东西,但对方却完全不需要。 由此直接联想到的作品就是,两个原本不理解的个体,通过玩家的操作与交互,最终让他们理解彼此。但基于我参加Gamejam的经验,做这种情感体验或者交互叙事的游戏是很困难的,到最后往往会陷入完成度地狱(虽然一旦做好了就会十分惊艳,比如令我印象深刻的CGJ2018的作品《卡塔琳娜的消失》),并且这个含义太过直接,大家都会往这方面考虑,所以就pass掉了。

另一个出发点就是改变形状。游戏指定某个形状,玩家利用机制产生形变并最终达到要求,如NS上的Snipperclips。基于这个想法,我提出了2个案子,一个是做一个类似橡皮泥物理的物体,玩家将它反复摔打在地面上来改变形状,另一个是做一个漂浮场景,玩家可以控制自己的图案在Y轴上的浮力去触碰场景顶端的电锯,电锯会磨掉图案的边缘产生形变,在此期间玩家还可以控制图案的旋转来选择被打磨的位置,并最终达成游戏要求的图案。其中第二个案子我还设想做成双人游戏,两人同屏看谁能更快的磨出正确的图案,并且还可以加入一些干扰对方的机制。不过后来考量到了实现的难度和实际的趣味性,这两个案子最后都没有敲。

《Snipperclips》

周五晚上在会场并没有得到任何实质性的进展,我们决定各回各家。顺带一提,这一场我们组十分佛系,两天都享受了安稳的睡眠,而且睡觉的地方离会场还有1小时的通勤,也就是48小时的开发时间里我们有接近20个小时是在摸鱼。回到住处后又和队友稍作讨论,我突然灵光一闪:做音游!

游戏大体类似于《太鼓达人》,代表节奏点的音符从屏幕右侧不断向左移动,在音符移动到判定点位置时玩家敲击按键。为了体现“改变形状”这一主题,游戏中的音符有着不同的形状(圆形、三角形、正方形、六边形),对应的,判定点也有着不同的形状,玩家需要观察下一个音符的形状,修改判定点的形状,再在正确的时机敲击音符,区别于太鼓达人中红色蓝色音符的划分,游戏中修改判定点形状这一操作并不与节奏点绑定。周六在会场见面后,由于表现层上的需求,组里的两位美术对这一机制产生了些许意见,就在此时,老练策划LUC出面,提出了一个精妙的点子:音符的形状不再直接表现在音符上,也就是说音符出现时的形状都是一样的,对形状的要求单独用一个序列体现出来,提示玩家接下来的几个音符的形状。至此,gameplay的部分确定。

《太鼓达人》

开发

音游的内核定下来了,我们想给他套一个有趣的外皮(就像《节奏天国》那样),两位美术讨论了一波,最后定下来的表现层是敲铁锭:音符出现时是初始形状的铁锭,判定点是可以切换形状的模具,敲击时有一个锤子挥下来把铁锭铸成模具的样子,音符滚动的卷轴体现成一条传送带,传送带末端是一个悬崖,有一个潜藏在下面的怪兽想要吃这些螺丝钉,当然怪兽是挑食的,如果你喂了它错误形状的螺丝钉,他就会很生气。就这样,SPG出了概念图。

界面上方的4个旗帜就是前文提到的提示序列

因为是gamejam的开发,所以我们很果断地砍掉了需求:音符敲击没有准确度的分级,只有敲中和没敲中、敲击种类不加入连打或者长按,只有普通的敲击。不过即便是这样,缺乏开发经验的我也依然对如何实现这样一个音游系统一脸懵逼。这个音游系统简单用模型来描述就是:每个音符有一个对应的时间(游戏开始x秒后,该音符应该被敲击),如何让这些音符从右向左开始运动,并且恰好在x秒时到达判定点的位置。我最初的尝试是给音符带上刚体,提前a秒在距离判定点d的位置生成这个音符,然后赋予这个音符\frac{d}{a}大小的速度。不过我在调试时发现如果改变a的值,原先可以准时到达的音符却出现了时差。后来经由Wxzuir指点,游戏引擎的物理系统是靠不住的,更恰当的方法应该是自己手写每个音符的运动。依然是提前Vtime秒在position位置处生成音符,然后每一帧都计算这个音符应该所处的位置,并修改它的位置。

for(int i = 0; i < Nodes.Count; i++)
{
    if (Nodes[i] == null) continue;
    Vector3 pos = Nodes[i].transform.position;
    float newx = position.x + (judge.transform.position.x - position.x) / Vtime * (currentTime - time[i] + Vtime);
    pos = new Vector3(newx, pos.y, pos.z);
    Nodes[i].transform.position = pos;
    if (newx <= -5.5f) Destroy(Nodes[i]);
}

接下来要解决的就是敲击判定,我设置了一个指示器now表示下一个应该被敲击的是第now个音符,如果玩家进行了敲击输入,则判断当前的时间是否处在当前音符的判定区间内,如果为真则敲击成功,指示器+1。另外如果当前时间已经超过了当前音符判定区间的上限,则指示器+1。不过这样只有一个指示器的写法必然是错误的,如果两个音符之间的时差特别短,甚至小于判定区间的话,只用一个指示器就会压缩第二个音符的判定区间,不过在gamejam里肯定是够用了。

游戏中还有两个模型变换的需求:在敲中音符后,音符要根据当前模具发生改变,并且上方的指示器序列也要变化。这次我的做法是把所有可能的变换结果放到一个父物体下面,并且排好顺序,利用GetChild()函数遍历子物体,依次修改它们的显示状态,把我想要的结果显示出来,其他的结果隐藏掉。对子物体排序是因为GetChild(int index)是获取第index个子物体,排序才能使用循环操作,把Transform的对象转换成Gameobject对象的方法是transform.gameObject。

for(int i = 0; i < 5; i++)
{
    if(i==currentNail)
        generator.Nodes[now].transform.GetChild(i).gameObject.SetActive(true);
    else
        generator.Nodes[now].transform.GetChild(i).gameObject.SetActive(false);
}

此外这次还简单的使用了协程,也顺便粗浅的记录一下协程的用法。协程是一个很方便的产生时延的工具,游戏中我希望控制模具的轮盘在旋转的过程中玩家的操作会失效,轮盘的旋转动画显然是很好实现的,可是我要怎么知道轮盘当前到底是不是在旋转动画的过程中呢?常规做法就是开一个布尔值,动画开始时修改这个布尔值,动画结束时再把它改回去,像这样相隔特定时间之后做某件事用协程来写就十分舒服。用协程实现时延是使用下面这样的方法

yield return new WaitForSeconds(float seconds);

这句话会将控制权移交给主线程,在seconds秒之后再把控制权移交回协程,也就是这段时间内协程不会继续执行下面的代码,进而达成了延时。协程是一种函数类型,可以带参,调用时使用StartCoroutine()方法。

public IEnumerator StartAniNail(bool dir)
{
    isAni = true;
    if (dir)
        Nail.transform.DORotate(Nail.transform.rotation.eulerAngles + new Vector3(0, 0, 90), nailTime);
    else
        Nail.transform.DORotate(Nail.transform.rotation.eulerAngles + new Vector3(0, 0, -90), nailTime);

    yield return new WaitForSeconds(nailTime);
    isAni = false;
}

在使用DoTween插件里的DORotate函数时,所需的endValue是欧拉角而不是直接使用transform.rotation(四元数),我因为这个吃了个大苍蝇耽误了好久的时间还把心态搞崩了,这里要感谢Wxzuir帮我解决了bug。

游戏最后的画面是这个样子的,这里SPG有一个精妙的UI设计。最开始我们的四个指示器就像概念图那样,大小一致,而且缺少动画,于是很多玩家并没有意识到那是一个从左到右顺序排列的序列,有的玩家以为最上面的四个图案都是随机生成的,还有一个玩家最开始甚至以为只要敲出上面四个图案中的任意一个就可以。SPG后来把最左边的旗帜(也就是当前要敲的图案)进行了放大,还加了绿色的外边框,这样“序列”的含义就变得明显了很多。另外最右侧出音符的地方由RedMou加上了持续呼扇的门扉,整个游戏效果马上提升了一个档次,这里不得不感慨美术对于游戏的重要性。

后记

这次我们组真的太佛系了,四个人只带了3台电脑,2个鼠标,RedMou忘带数位板的笔,然后我在这台电脑做了点东西,在那台电脑也做了点东西,另外一边LUC还需要我帮他调整读谱工具,于是我就到处跑,资源整合浪费了好多时间。用SPG的话说就是,我好像在一个人玩overcooked。

佛系的后果就是,游戏根本没做完,锤子的动画我一直没整好,最后索性就把锤子删掉了,计分,Combo数都没加上去,我们甚至连反馈都没做——你敲了音符音符确实会改变形状,但是变对了还是错了,我们根本没来得及做,正常应该是怪物吃下去音符,然后根据对错有不同的动画来反馈。

虽然完成度不高,但总归是把核心的gameplay做好了,最后,新宝岛真快乐。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值