在Unity上开发游戏时,我遇到了一个有趣的任务:如何延长角色的消极或积极影响的时间。
简而言之,我有一个角色,可以应用某些效果,例如减弱,增益,速度增加,速度降低等。 为了通知玩家某种效果的效果,游戏提供了状态行。
该行的第一个版本包含所有状态的变暗图标,并且在发生效果时,所需的指示灯会亮起。

每种状态都有Corutin,可在一定时间后取消效果。
这个决定有一个相当重要的缺点。 如果由于游戏中某些事件的结果,在比以前的类似效果的持续时间短的时间之后,对角色施加了相同的效果,则可以有两种版本的事件。
- 完全错误:第二个corutin与第一个同时启动。 当第一个协程完成后,它会恢复为原始值,即删除效果。

- 同样是错误的,但在某些情况下可以接受:取消第一个协程并运行第二个协程。 在这种情况下,该效果的持续时间将等于第一个效果的持续时间,直到协程被取消为止+第二个协程的持续时间。

这两种方法都不适合我的任务,因为我需要延长效果的持续时间,以便获得每种效果的持续时间的总和,而不管应用了效果的次数。
如果角色踩在尖峰上,则他的腿会受到有条件的损坏,并且无法继续以相同的速度运动。 假设速度降低了5秒。 如果3秒钟后角色踩到其他尖峰,则速度应再降低5秒钟。 也就是说,已经过了3秒,距离新的峰值还剩2 + 5秒。 从攻击第二个尖峰开始(总共10个)起,效果的持续时间应再持续7秒。
在协程的帮助下,我没有找到解决该问题的方法,因为不可能找到完成协程的剩余时间才能将其添加到新的协程中。
我为这个问题找到的解决方案是使用字典。 与List相比,它的优点是字典具有一个键和一个值,这意味着我可以通过键访问任何值。 另外,此解决方案使您可以摆脱行中的永久状态图标,并根据需要包括必要的图标,并按照它们出现的顺序将它们设置在行中的位置。
Dictionary<string, float> statusTime = new Dictionary<string, float>();
在这种情况下,字典比使用队列更具优势,因为队列按照先进先出的原则工作,但是效果的持续时间不同,这意味着需要删除的状态可能不是队列中的第一个。
为此,我添加了三种方法。
新增状态将所需的状态添加到字典中,如果字典中已经存在这样的状态,则添加持续时间。 如果没有状态,则我们计算结束时间并将其添加到字典中。
private void AddStatus(string status, float duration) { if (statusTime.ContainsKey(status)) { statusTime[status] += duration; } else { float endTime = Time.timeSinceLevelLoad + duration; statusTime.Add(status, endTime); } }
RemoveStatus我们从字典中删除状态,恢复原始值。
private void RemoveStatus(string status) { statusTime.Remove(status); RestoreStats(status); }
检查状态如果字典中有状态,则我们检查其时间是否已到期。
如果已过期,则从字典中删除状态。 由于在循环中更改字典使得不可能同步字典的值,因此我们将字典的键放在此处作为常规列表。
private void CheckStatuses() { if (statusTime.Count > 0) { float currTime = Time.timeSinceLevelLoad; List<string> statuses = new List<string>(statusTime.Keys); foreach (string stat in statuses) { if (currTime > statusTime[stat]) { RemoveStatus(stat); } } } }
显然,在优缺点中,这是可延长的效果持续时间。 即,问题得以解决。
但是这里存在一个相当大的负数。 在每帧更新中检查状态。 在我的游戏中,最多有4位玩家,这意味着此方法每帧并行执行4次。 对于4个字符,我认为这并不重要,但是我敢肯定,使用更多字符会导致性能问题。 还值得注意的是,通过检查字典中元素的存在来保护该方法,并且使用空字典时,应减少负载。
展望未来(对于这款游戏仍然完全迷茫),我也对在线模式下的这一决定充满信心,因为只会对当前本地玩家而不是所有实例化玩家进行玩家状态检查。