完美世界GameJam参加报告——《解字》游戏的设计与开发

前言

这次我们组成了7人团队去参加完美世界组织的位于天河软件园的GameJam。GameJam的规则是利用48小时开发一个游戏,这次的游戏主题是:烎 。我们的团队由三个程序、三个美术、一个音乐组成(由于GameJam的特性,一般不会特化安排专职的策划),来自中山大学和广州美术学院。
本游戏最终取得广州赛区的第一名,并取得五万奖金。

演示视频https://www.bilibili.com/video/av24163649/

游戏概念构建

这次的题目来源于网络语言,实际上,由于网络联通的便利性古字新用变的越来越常见,而网络赋予他们的意义往往和古义完全不同。烎就是其中一个。
而现在我们的游戏主要就是想让人们重新发现汉字,有些结构很简单的字许多人甚至都不认识,我们希望能通过这个游戏能捍卫汉字的尊严

游戏设计

这是一个2d横板策略类游戏我。我们的核心玩法是两个(或多个)简单字组合起来成为一个技能字。每个技能字有独特的效果,可能会攻击敌人(异形字)或者恢复自己或者造成某种buff。每隔一段时间会有新的随机生成的字进入字槽。为了点题,我们设置开火组成的“烎”作为大招,可以打出必杀。组成技能字的基础字越多,技能的效果越强
当攻击累计到一定的时候,敌人的部首会被击飞。敌人可能抄起它的部首对主角操控的阿呆进行攻击。在我们原先的设计中,被击飞的部首可以以金钱(墨团)或道具的形式成为玩法的一部分,可惜时间关系没能实现。
另外一点迫于时间没能实现是敌人可能拥有一些特色部首比如木,而主角如果打出火系攻击(炎等)则会造成超量伤害,达成击溃效果。
惩罚机制:敌人会隔一段时间对主角进行攻击,主角受伤掉血的时候,底部的字槽会同步、等比例的被覆盖。被覆盖的字将无法被选中攻击。血量可以通过回复系技能回、森、沐等回复。但hp清零,即字槽被完全覆盖的时候,游戏结束,玩家失败。
本来我们在关卡中加入了同时面对多个敌人的场景,这样一开始是想着可以让作为敌人的字组成词语营造一种幽默感。但后来时间问题程序上有一些bug没有解决,就没有在demo中加入这个要素
对于场景设计,我提出了使用《设计诗》里面的一些场景作为过场动画来营造克制的幽默感,不过束于时间限制以及一些审美上的争议,这一步也没有实现。

实现技术

我负责的部分主要是UI交互、场景滚动、点选拖动卡牌效果、部分动效、部分动画、部分粒子系统、部首脱离效果、死亡演出效果、图鉴(成就)的实现。具体的代码出于后续运营考虑不会完整贴上来,此处讲解下思路。

UI交互——血条覆盖层的两种方案

一开始打算使用Strech的方法,生成一个长方体,然后设置其边界属性
造成延伸。这样实现的好处在于可以完全自定义长方形边界的左右两端。缺点在于不好定义血条的样式,而且思想的时候多次遇到了世界坐标和本地坐标的神奇转换。
后来考虑到我们的背景色是白色,所以我们可以直接拿一个白色的sprite随着hp值变化向左移动。这样做的唯一缺点在于右端必须从canvas外开始,虽然在本次的demo中看不到这个缺点。

场景滚动(弃用)

一开始选用的场景方法是弹幕射击类游戏中常见的技巧——屏幕滚动而人物不动,后来发现这是我没有吃透这样做的原理。对于弹幕类游戏这样能减少工作量,但是对于2d横板来讲这样是无意义的,既不增加工作量也不减少工作量,实际上平白增加思维难度

UI部件拖拽效果

这一部分主要使用了unity的OnDrag函数。主要遇到的难点在于拖拽后要改变被拖拽物体的parent,还有就是localposition转换的问题需要注意

演出效果——技能击出后sprite的放大缩小动画

本来有两种实现方法,一种是使物体更加靠近摄像机然后在移动回原先的位置。这样的主要问题是不兼容正交摄像机,所以最终没有使用这个方案
最终选用了调整Animation里property Scale的方法,因为Unity自带动画编辑,这部分还是比较轻松的。

特效——粒子系统

https://github.com/kotomineshiki/Particle
这周刚好学完了粒子效果正好可以运用到里面。我们的游戏美术上的色调是黑白和红,所以我们暂时选用了尽量贴近水墨风格的粒子特效
先上效果图
这是项目中二十多个特效的其中两个比较好看的,下面贴一些具体的参数
烎:由三种特效组成,一个是底部的“魔法阵”
这里写图片描述

一个是从底部冒出的小气泡这里写图片描述还有就是两个螺旋球
这里写图片描述
这两个螺旋球是有轨迹的这里写图片描述

焱:这个特效由
这里写图片描述

这里写图片描述

这里写图片描述

部首脱离效果——动作管理器

要求:在敌人血量每下降一定值的时候,敌人的一个部首会脱落并飞出。我一开始是使用一个canBlow的布尔类型变量来控制这个,当canBlow为true的时候,在Update中给部首sprite添加一个二维刚体组件,设定一个速度和重力,并设置canBlow为false;
这样的实现方法导致了一个严重的bug。如果某一个技能导致的伤害足够导致两次部首脱落,那么将会出现如下调用(示意)

SET canBLow=true//第一次脱落
SET canBLow=true//第二次脱落
Update{
if canBlow =true
部首击飞
canBlow=false;
}

我们注意到这时候实际上只会执行一次脱落。这个时候突然意识到,我们应该用队列来解决这种冲突,如果我们把canBlow设计成队列,那么

canBlow.EnQueue(部首1)
canBlow.EnQueue(部首2)
Update{
    while(canBLow非空){
        canBlow.Dequeue();
        部首击飞
    }
}

仔细想想,这不正是动作管理器吗!实际上在课上学习到的动作管理器结构十分复杂,一个功能写了好几个类来实现。这次的使用说明了动作管理器其实可以很简单,重要是队列化动作的思想。

部首脱离效果——障眼法实现动画和刚体效果的共存

上面提到,部首脱离效果是通过加2d刚体组件来实现的,但是这带来了一个问题:动画管理器是通过规定对应物体的位置来做动画的,而刚体组件和其在原理上冲突。那么如何解决这个问题呢?我采用了障眼法,把要吹飞的部分复制一下,吧原有的部分隐藏起来,再给复制的部分加上刚体组件,这样效果和理想中一样的。

协程——动效

这次大量使用了协程coroutine来实现动效,在某种程度上可以代替动画机。比如部首脱落后过一段时间会变淡,再过一段时间会消失。

部首脱离效果——观察者模式的一些坑

这一步遇到了一个有意思的问题。我在部首脱离效果的类中订阅了Enemy的血槽变化和死亡两个事件(但血槽为0时,角色也就死亡了)。但是发现了一些问题:角色死亡事件总是比最后一次血槽变化先传给观察者。这就是老师上课所说的“执行顺序问题”。

序列化读入Json数据——图鉴的数据层

这个对我来说是一个巨大的挑战,因为之前从来没有写过。之前在Unity中写管理数据的类的时候,都是用代码在类里的Start()中手动一条条写进去。仔细想想这样其实并不是符合软件工程标准的方法。主要通过调用JsonUtility.FromJson这个函数并创建序列化类来实现读入。这次选用的序列化读入降低了耦合度,实现了数据与数据管理类的分离,也更加方便分工——可以由数值策划编辑Json类里的数据,由程序员写程序。

观察者模式——图鉴的逻辑层

回想观察者模式(订阅发布模式),我们知道这一模式被设计出来就是为了解决成就系统。我们的图鉴管理器主要的数据结构是“字典” 我们可以通过键(技能字,用字符串存)来访问值(字的解释)。那么我们就把图鉴类作为一个观察者,观察玩家使用技能这一事件,把每一次玩家使用技能记录产生的字记录下来,这样就可以做到玩家每发现一个新字,都可以在图鉴中更新。

单例模式——图鉴

对于图鉴,他应该是一个全局性的类,因为在任何一个场景的任何一个位置发现了新字都应该被图鉴记录,所以我们采用了单例模式来方便访问。

心得和教训

之前上unity课的时候经常有疑惑:为什么简单的功能要用那么多复杂的结构。实际上这些设计模式被发明出来都是有他们道理的——他们能解决某些情况下潜在的BUG。
做游戏时很重要的是对细节的把控。每一个动效一定要修改到理想的效果为止,不能因为技术或者各种原因妥协。这里打折那里打折一下子手感就下去了。

猜你喜欢

转载自blog.csdn.net/kotomineshiki/article/details/80503447