找回密码
 立即注册
查看: 367|回复: 8

【Unity开发】【游戏思考】关于Unity人物属性值的开发方法 ...

[复制链接]
发表于 2022-2-7 17:57 | 显示全部楼层 |阅读模式
这算是这个专栏的第一次更新,我尽量不要把内容写成流水账。
PS:JetBrain的吉祥物真的很奇怪,看了就掉san。(如果侵权请联系我删除)。
今天的开发进度仍然在人生中的第一款游戏上;该游戏是一个视觉小说类的游戏,故事被设定在了自己身上,讲述了一些关于自己的事情。
总而言之,今天遇到了一个挑战:当人物的属性值在某一阈值的时候引发一些事件。比如:当人物的饥饿值为50的时候发出事件:提醒玩家应该控制自己去吃饭了。
一开始,我用的是这个方法(代码基于C#,因为是Unity的开发):
public float Hungry = 100f;

private void Update()
{
    Hungry -= Time.deltaTime * 0.5f; // 每秒减0.5的饥饿值
    if (Mathf.Approximately(Hungry, 50f))
    {
        // Do Something
    }
}
之后我发现,这个方法不能保证在Hungry这个属性“大概”为50的时候正确地引发事件。这里面的“大概”为我们人类理解的大概,比如50 和 50.0005,这两个值即为大概相等。导致这个结果的原因为:因为这个值在本质上不是连续地被减下来的,而是每帧对这个值减下去0.008(假设开启垂直同步,帧数被锁定在了60帧)。
这样的话,如果50这个数值正好处于0.008的间隔中的时候,就会使事件不能正确地被引发,如下图所示:



红色的数杠显示为每帧变化之后的数值,两个红色数杠之间的距离恒定,我们认定其为0.008

当50 (或者是我们设定的阈值)在数轴上可以和某一条红色数杠重叠时,我的方法才会起效果。
经过测试之后,觉得不对,愤而删除重来。
这是第二版的代码:
public float Hungry = 100f;

private void Update()
{
    Hungry -= Time.deltaTime * 0.5f // 每秒减0.5的饥饿值
    if (Hungry - 50f < 0.01f) // 0.01 为“分界值”
    {
        // Do Something
    }
}
把这段代码写到知乎里面的时候,我仍然还在想:为什么自己有的时候可以那么的蠢。
当然了,这段代码一看就是不能被用的代码,存在三个问题:
第一种问题是: 当分界值大于每次变化的值的时候,有可能同一个事件被触发多次。
第二种问题是: 当分界值小于每次变化的值的时候,有可能会像之前一样,触发不了时间。
第三种问题是: 这应该用Mathf.Abs()来计算,要不你就会像我一样,当Hungry小于50的时候看着每秒100多次事件的被触发而感到懵逼。
反正,最后这个当然也是被否决了。否决的原因有两个:一种是计算分界值太麻烦,而且会对代码以后的修改带来巨大的问题;一种是这个方法看起来太不优雅了,会让人感觉到我特别的蠢。
第二种方法其实就是第一种垃圾方法的改良,因为其实就是将阈值的范围加大,变成一个阈值范围,如下图:



红色的数杠显示为每帧变化之后的数值,两个红色数杠之间的距离恒定,我们认定其为0.008。当有红色数杠在两个黑色数杠之间的时候,事件将会被触发。

所以说,之前存在的问题还是存在,而且还增加了好几个问题。于是再次愤而给代码全部删除。
最后,是比较优雅,比较可用的第三种方法:属性法,代码如下:
private float _hungry = 100f;
public float Hungry
{
    get => _hungry;
    set
    {
        if (_hungry >= 50f && value < 50f)
        {
            // Do Something
        }
         _hungry = value;
    }
}

private void Update()
{
    Hungry -= Time.deltaTime * 0.5f;
}
这个方法,在我看来,才叫真正的阈值方法。阈值是被真真切切地卡在了一个点上。只有当值穿越过去的时候才会触发事件。而且实现的方法也非常的优雅。以后需要增加更多的事件的时候,只需要在Set里面多写几个if就可以了。测试了一下,可以正常触发事件之后,我将其他的属性都改成了这个模式,更多的问题现在还没有出现,但是我觉得现在至少基础的方法已经找对了。
以上就是一个关于Unity里面的人物属性跟随时间来进行恒定的变化,并且根据规定的阈值来触发事件或者做其他操作的具体例子。我这个肯定还不是最好的解决方案,但是如果你知道的话,欢迎在评论区和给我发私信探讨一下这个问题,十分的感谢。
关于游戏设计艺术这本书,年夜饭上喝多了,所以咕咕咕了。感谢大家支持与厚待,祝大家节日快乐。
更新:在评论区里面,有一位名叫“猴与花果山”的开发老哥提出了我个人认为比较好的一种解决方案(至少比我在上面写的代码好)。他的方案大致为:将运算的过程和逻辑写在FixedUpdate里面,因为FixedUpdate的执行速率是固定的,所以我们只要算出来每一“物理帧(每次调用FixedUpdate)”该改变多少属性就可以了。这个方法真的是非常的优雅,而且毫无其他的顾虑。比如这种方法就不会产生我在上面说的属性值的改变不能正确触发事件的问题。另外,这个文章真的是让我明白了自己还有好多事情得学而且必须多多实践。就比如FixedUpdate我之前的理解一直为“应该把和物理有关的逻辑写在这个函数里面”。但是“猴与花果山”老哥确实是给我开拓了思路。感谢! @猴与花果山 (如果打扰到您了,十分抱歉。)
另外,我已经将老哥的评论置顶,欢迎大家前去评论区和我一同学习。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2022-2-7 18:01 | 显示全部楼层
其实根本的问题是:这些游戏逻辑的时间单位是帧,而不应该是秒,秒和帧虽然有换算关系,但只有(游戏开发)外行才会用Update去做游戏逻辑的事情。在游戏的世界里,单位就是“帧”,即Unity的FixedUpdate,从你的描述来看,hungry这种属性应该是int,而你的设计应该定义的也不是每秒-多少,而是每帧减多少。
关键在于——他是随着游戏时间推进,而非现实时间推进来变化的,同时,你非常不希望因为现实时间间隔(deltaTime)过大或者过小导致一些“补帧”(也就是错过了一些事情的发生,需要追溯等,在这个问题上就是hungry大多时候会高于设计需要的50点)。
Update是给你用来做渲染逻辑的,比如你的UI是一个瓶子,里面是晃动的水,代表了你的hungry值(但怎么看是fill值),然后这个瓶子里面的水变化的时候是有个过程的,类似很多游戏的血条减少的过程,这个过程是在Update里面用缓动函数(你自定义的一个数学函数,很多Tween相关的插件里也有现成的Ease函数)来实现的,每次Update(每个视觉Tick)里面根据当前值(UI上所处的值)和目标值(逻辑上的hungry值),来根据Tween重新计算这一次Update(其实是补上一个update)该变化多少。Update和FixedUpdate的关系本当如此,尽管FixedUpdate本意是为了解决一些“物理问题”的,但是游戏的“物理问题”就是“帧”才是逻辑世界的时间,你不必关心2帧之间间隔究竟多少秒(这是无意义的和现实时间做了换算),尽可能保证间隔一致和足够快就行了。
这是游戏设计与开发技术在这10多年的商业化变迁中,因为“职能分工”而导致失传的,也是游戏设计与开发最基础的功夫之一了。
发表于 2022-2-7 18:08 | 显示全部楼层
呃……我自己通常用的方法是把通用且常用的数据放入一个单例类中,然后把这个类附加到一个不会因为场景变换而消除的游戏物体上。这样可以很有效地控制一些数据的变动,比如游戏内日夜变换、控制角色生命值或者剧情变量等
发表于 2022-2-7 18:10 | 显示全部楼层
是的是的,但是这样的话还是会有一些问题,比如在使用Fungus插件制作一些剧情的时候。这个问题处理不好也是很麻烦的,等我把手头的这个游戏做完了以后总结一下可以发一期我关于单例类存数据的想法,到时候也欢迎您继续提点意见。[谢邀]
发表于 2022-2-7 18:16 | 显示全部楼层
逻辑写在属性里....感觉就像代码里的坏味道
发表于 2022-2-7 18:25 | 显示全部楼层
呃,这倒是确实。但是这是目前我能想象到的最方便的解决方法了,那一般情况下,这种问题应该怎么解决呢?
发表于 2022-2-7 18:32 | 显示全部楼层
加油加油,Fungus插件我了解的不多,就等你的教程来学习一下呢[赞同]
发表于 2022-2-7 18:36 | 显示全部楼层
感谢支持!尽量速度把手头上的游戏做完,把这件事分享出来,因为我也很好奇大佬们一般都是怎么做的哈哈哈哈
发表于 2022-2-7 18:42 | 显示全部楼层
您说的对,我仔细读了您的建议,并且再次阅读了一下网上关于“FixedUpdate的时间间隔的问题”和Unity关于FixedUpdate的官方文档,我觉得在绝大多数情况下,确实将游戏属性的变化(根据时间线性变化的所有逻辑)放到FixedUpdate里面是比放在Update里面更好的一种解决方案。在这方面我还是仍然需要更多的学习。感谢您的讲解,让我学到了很多,谢谢![赞同]
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-22 21:30 , Processed in 0.095547 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表