新人第一篇带有纪念性的文章
游戏demo来源于b站up主M_Studio麦扣老师的教程《勇士传说》,在此基础上进行的拓展内容实现

这是成品,除了隐现动画没有素材,其他都达到了我想要实现的效果😋
当然我自己不太会这个逻辑,于是我寻求了gpt的帮助
核心逻辑如下:
scss
private void Slash(InputAction.CallbackContext context)
{
if (fadeCoroutine != null)
{
StopCoroutine(fadeCoroutine);//防止上次协程没清空
}
fadeCoroutine = StartCoroutine(FadeOutAndBack());//启动新协程
}
在这里使用了StartCoroutine启动协程后调用IEnumerator状态机来实现闪现,为什么要使用协程呢?
1.不用手写计时器、不用在 Update() 里管理状态机,逻辑更清晰
2.避免阻塞主线程,协程在每帧之间自动暂停,不会卡住游戏
3.可以嵌套使用其他协程(渐隐 → 冲刺 → 停顿 → 渐显),方便管理各种状态
底下是管理各种协程的状态机
csharp
private IEnumerator FadeOutAndBack()
{
yield return StartCoroutine(FadeTo(0f, fadeDuration));//启动渐隐协程
yield return StartCoroutine(Dash(dashDistance, dashDuration));//启动冲刺协程
yield return new WaitForSeconds(invisibleTime);//等待一段时间
yield return StartCoroutine(FadeTo(1f, fadeDuration));//启动渐显协程
fadeCoroutine = null;
}
当然最重要的是我们要实现在一段时间内渐隐,渐显,涉及到时间的持续用协程是比较方便的。
因为Unity给了我们很方便的工具去调用,比如在IEnumerator状态机里面调用yield return new WaitForSeconds(0.5f);可以让协程暂停0.5秒而不会影响主线程的逻辑,防止阻塞。
ini
private IEnumerator Dash(float distance, float duration)
{
float faceSign = Mathf.Sign(transform.localScale.x);
Vector2 dir = new Vector2(-faceSign, 0f).normalized; // 朝向向量,向右为 (1,0)
Vector2 start = rb.position;//起始位置
Vector2 target = start + dir * distance;//目标位置
rb.velocity = new Vector2(0f, rb.velocity.y);//设置冲刺的时候水平速度先归零
rb.MovePosition(target);
}
可以看到人物调用了MovePosition这个方法来实现快速移动,根据unity官方手册我们可以看到关于他的描述:
MovePositon是在调用后的下一次物理更新(应该就是物理帧)将物体移动至指定位置,并且在移动过程中检测物理碰撞,相比于直接改变transform.position来说更不容易穿墙,但是为什么能穿过野猪呢?
我在代码里面并没有特别写关于忽略闪现过程中敌人触发器的碰撞,所以我找到了野猪身上有关伤害触发的代码
arduino
private void OnTriggerStay2D(Collider2D collision)
{
collision.GetComponent<Character>()?.TakeDamage(this);
}
可以看到我调用的是OnTriggerStay2D方法,再次查询Unity手册可以看到关于OnTriggerStay2D方法的描述:
编辑
稍微有点看不懂,问过gpt之后可以知道:OnTriggerStay2D是两个collider触发器在一帧内有重叠就会触发这个方法,所以我们可以得出为什么MovePosition可以穿过野猪而不能穿墙的结论了。
由于MovePosition的操作在两次物理帧之间完成,开始的第一帧我没有与野猪重叠,并且在下一帧我将人物移动至目标点,这两帧期间我的人物并没有与野猪产生重叠,所以不会触发OnTriggerStay2D,那么同理OnTriggerEnter2D和OnTriggerExit2D也不会被触发,因为并没有产生实际的触发器重叠。
ok闪现的逻辑讲完了我们来看看人物逐渐消失又逐渐出现的实现逻辑,先来看看源代码:
ini
private IEnumerator FadeTo(float targetAlpha, float duration)
{
Color c = sprite.color;//由于color是值类型,所以不能单独改color.a,这里用c来改
float startAlpha = c.a;
if (Mathf.Approximately(startAlpha, targetAlpha) || duration <= 0f)
{
c.a = targetAlpha;
sprite.color = c;
yield break;
}
float t = 0f;
while (t < duration)
{
t += Time.deltaTime;
float a = Mathf.Lerp(startAlpha, targetAlpha, t / duration);//核心实现
c.a = a;
sprite.color = c;
yield return null;
}
c.a = targetAlpha;
sprite.color = c;
}
其实核心逻辑就是如何实现从0到1的平滑过渡,这一点的话我一开始有想到通过动画器自带的平滑过渡来实现,但是实际写下来发现人物的隐现并不能跟随上一帧人物的形态,隐现的时候只会执行动画中的人物动画,所以我放弃了在动画器里去实现这个功能。
于是我询问了gpt(哎呀这gpt怎么这么好用),他给出的核心方法就是Mathf.Lerp,查询unity手册可以看到:
这个方法可以根据最后一个参数来平滑地从第一个参数变化到第二个参数,这行代码
ini
float a = Mathf.Lerp(startAlpha, targetAlpha, t / duration);
就是实现隐现的核心,duration是隐现的持续时间,t/duration就可以得到一个0-1的值,而t是根据Time.deltatime变化的,每次更新只会变化非常小的一个单位,所以能够很平滑地从0过渡到1,而float a就是决定人物color.a的参数,而我们的隐现逻辑又是在协程里实现的,整个过程是异步进行的,并且运行平滑,所以就实现了人物隐现的功能。
好了这篇文章到这里就差不多结束了,最后跟大家讲一点题外话吧。
本人是北京科技大学的一名大二学生,现在大二上,大一当了一年做题蛆,把绩点卷到了保研线,当时也不知道读研能干什么,只知道大家都在说读研究生好找工作,都支持读研,所以在入学的时候我的潜目标就是把绩点卷起来然后保研,然后找个好工作。
然而在大二的时候我加入了我们学校勤敏轩"806"实验室里面的游戏部,因为我十分热爱游戏开发,也在大一卷绩点的同时自学了Unity和C#的一些知识,进来之后我看到了许多开发能力很强的大佬们,他们不在乎绩点,每天泡在实验室旷课写代码,就那时候我觉得我大一一年都浪费了,卷的绩点好像也没那么有用了,我的最终目标是开发游戏,最后入职游戏公司跟他们一起做游戏。那么反过来想,如果我是游戏公司的HR,我会招一个有绩点,有学历,但是Unity的一些基本操作都不熟悉,没有开发过完整游戏项目的一个人吗?我想是不会的。
所以思索再三,我下定决心抛弃我大一卷过的所有成绩,放弃保研,全力投入游戏开发,提升自己的能力,跟我们游戏部成员沟通之后,先找到了这款较为完整的教程,带我们熟悉Unity的一些基本操作,之后再开发属于自己的游戏。(其实还有一个主要原因是我咨询了学长,学长说不做引擎的话,读研对游戏开发没什么帮助)
我在跟教程的过程中想尝试自己实现一些炫酷的闪现功能,有感而发写了这篇文章,顺便以此来纪念我从做题蛆到一名游戏开发者的蜕变。
大家关于游戏开发这方面有什么好的见解也欢迎与我进行交流😋(什么都行)
感谢您观看我的文章