Unity2D:角色死亡重生逻辑与协程的使用

是时候编写脚本来控制角色的死亡重生了。在众多平台跳跃游戏中,我们不难看到有“地刺”一类的能够造成玩家即死的地形,这次我就来记录如何实现这一功能。话不多说,我们直接进入正题。

死亡逻辑

首先我们需要把造成玩家即死的贴图放在地形中,并添加碰撞器。为了与其他会碰撞的物体做区分,将其标签更改为“InstantDeath”,意为即死。如果这里的碰撞物体像我一样使用瓦片贴图的话,可以添加瓦片碰撞器(Tilemap Collider 2D),Unity会自动帮你调整好碰撞器的轮廓。

然后在此基础之上编写我们将要挂载到人物身上的死亡脚本。人物死亡时,销毁该物体。

//碰撞检测
private void OnCollisionEnter2D(Collision2D collision)
{   //这是用来检测标签的方法
    if (collision.gameObject.CompareTag("InstantDeath"))
    {
        Destroy(gameObject);
    }
}

重生逻辑

现在角色碰到这坨危险物体就会立马被销毁,现在来编写重生逻辑,好让我们继续操纵我们的角色。首先创建一个空物体,这个物体用来挂载我们的重生脚本,通过这种方式可以指定人物重生的位置。将我们之前的人物物体拖入资产中,成为预制件,方便我们在销毁人物后重生时,得到一个一模一样的人物。

然后编写以下的重生脚本:

public class SpawnPoint : MonoBehaviour
{
    //用来存放玩家的预制件
    public GameObject player;
    public void NewPlayer()
    {
        if (player != null)
        {   //用来初始化人物 括号里分别为(对象、位置、旋转)
            Instantiate(player, transform.position, Quaternion.identity);
        }
    }
}

然后我们将挂载重生脚本的这个物体挪到想要让角色重生的位置。同时在角色死亡脚本中添加一串“public SpawnPoint spawnPoint”的声明,在角色销毁后调用其包含的NewPlayer()方法。与此同时将挂载重生脚本的物体也制为预制件,并拖入到死亡脚本的“重生点”栏里。

这样一来我们的人物就可以在死亡后无限复活啦!

更新游戏状态

但在每次角色死亡后,某些组件仍然维持着原有的状态。比如,我的虚拟摄像机(Cinemachine)在角色第一次死亡后就缺失了跟随对象,导致其停止工作。

这时间,考虑在我们重生点脚本中获取虚拟摄像机的组件,从而能够在角色重生时直接修改跟随的对象。我们将虚拟摄像机的标签更改为“VirtualCamera”,方便我们在脚本中找到它。

[HideInInspector]
public CinemachineVirtualCamera virtualCamera;
private void Awake()
{
    //获取虚拟摄像机的组件
    virtualCamera = GameObject.FindWithTag("VirtualCamera").GetComponent<CinemachineVirtualCamera>();
}

那么这时,我们角色死亡脚本中的重生点成员就不能像之前一样使用预制件了,否则就是对象仅存在于资源当中,并没有实例化,不会调用到重生点类的Awake()方法。所以也像虚拟摄像机一样,将重生点物体的标签改为“SpawnPoint”,在场景中寻找存在的重生点。

    [HideInInspector]
    public CinemachineVirtualCamera virtualCamera;
    private void Awake()
    {
        //获取虚拟摄像机的组件
        virtualCamera = GameObject.FindWithTag("VirtualCamera").GetComponent<CinemachineVirtualCamera>();
    }
    public void NewPlayer()
    {
        if (player != null)
        {
            GameObject newPlayer = Instantiate(player, transform.position, Quaternion.identity);
            //重新设置跟随目标
            virtualCamera.Follow = newPlayer.transform;
        }
    }

现在我们的摄像机就可以重新跟上人物了。

协程 Coroutine

现在角色死亡重生算是弄好了,但是人物就这样转瞬即逝还瞬间死而复生,多少有点不太自然。所以我们将要用到协程(Coroutine),来延长角色死亡的时间,并在这段时间中添加受伤闪烁,在彻底消失后再重生。

协程是一种可以暂停执行并在稍后恢复的函数,依赖于 IEnumerator 接口,其定义在 System.Collections 命名空间中。返回类型必须是 IEnumerator,并使用 yield 关键字定义暂停点。同时协程依赖于 MonoBehaviour 的生命周期,如果 MonoBehaviour 被销毁,协程也会停止。以下是几种常见的协程暂停方法。

IEnumerator MyCoroutine()
    {   //暂停一帧
        yield return null;
        //暂停一秒
        yield return new WaitForSeconds(1);
        //暂停到这一帧结束
        yield return new WaitForEndOfFrame();
        //暂停到bool变量为true
        yield return new WaitUntil(isTrue);
        //暂停到bool变量为false
        yield return new WaitWhile(isFalse);
        //暂停,等待另一个协程完成后继续
        yield return new StartCoroutine(otherCoroutine);
    }

//通过以下两个方法开始与终止
Coroutine myCoroutine = StartCoroutine(MyCoroutine());
StopCoroutine(myCoroutine);

通过这种方式延迟角色死亡,并添加闪烁效果。将刚体的位置冻结,让玩家无法进行移动。

private IEnumerator DeadProcess()
{
    Renderer renderer = gameObject.GetComponent<Renderer>();
    Rigidbody2D rb = gameObject.GetComponent<Rigidbody2D>();
    // 闪烁效果
    for (int i = 0; i < 6; i++)
    {
        //冻结刚体,让玩家无法操作
        rb.constraints = RigidbodyConstraints2D.FreezeAll;
        //交替闪烁
        renderer.enabled = false;
        yield return new WaitForSeconds(0.06f);
        renderer.enabled = true;
        yield return new WaitForSeconds(0.06f);
    }
    //让角色变透明
    renderer.enabled = false;
    //停个半秒钟
    yield return new WaitForSeconds(0.5f);
    //再生成
    spawnPoint.NewPlayer();
    //最后销毁当前对象,这个脚本也会跟着销毁
    Destroy(gameObject);
}

现在我们角色的死亡以及重生逻辑就写好了!

以下为完整脚本。

using System.Collections;
using UnityEngine;
public class Dead : MonoBehaviour
{
    [HideInInspector]
    public SpawnPoint spawnPoint;
    private void Awake()
    {
        //寻找场景中存在的重生点
        spawnPoint = GameObject.FindWithTag("SpawnPoint").GetComponent<SpawnPoint>();
    }
    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.CompareTag("InstantDeath"))
        {
            StartCoroutine(DeadProcess());
            //当协程当中所有代码执行完毕后,协程会自动停止
        }
    }
    private IEnumerator DeadProcess()
    {
        Renderer renderer = gameObject.GetComponent<Renderer>();
        Rigidbody2D rb = gameObject.GetComponent<Rigidbody2D>();
        // 闪烁效果
        for (int i = 0; i < 6; i++)
        {
            //冻结刚体,让玩家无法操作
            rb.constraints = RigidbodyConstraints2D.FreezeAll;
            //交替闪烁
            renderer.enabled = false;
            yield return new WaitForSeconds(0.06f);
            renderer.enabled = true;
            yield return new WaitForSeconds(0.06f);
        }
        //让角色变透明
        renderer.enabled = false;
        //停个半秒钟
        yield return new WaitForSeconds(0.5f);
        //再生成
        spawnPoint.NewPlayer();
        //最后销毁当前对象,这个脚本也会跟着销毁
        Destroy(gameObject);
    }
}
using Cinemachine;
using UnityEngine;
public class SpawnPoint : MonoBehaviour
{
    public GameObject player;
    [HideInInspector]
    public CinemachineVirtualCamera virtualCamera;
    private void Awake()
    {
        //获取虚拟摄像机的组件
        virtualCamera = GameObject.FindWithTag("VirtualCamera").GetComponent<CinemachineVirtualCamera>();
    }
    private void Start()
    {
        NewPlayer();
    }
    public void NewPlayer()
    {
        if (player != null)
        {
            GameObject newPlayer = Instantiate(player, transform.position, Quaternion.identity);
            //重新设置跟随目标
            virtualCamera.Follow = newPlayer.transform;
        }
    }
}

小结

关于死亡的逻辑,可以在协程中加入各种其他的功能,比如角色渐变效果等等。在编写脚本时,我发现关于碰撞器与触发器的使用有挺深的学问,还有,如果角色死亡重生过多,可以建立对象池进行优化,在此之后我会仔细讨论,玩去喽~

如有补充纠正欢迎留言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值