【Unity2DMobileGame_PirateBomb07】—— 代码实现炸弹爆炸的物理效果

562 阅读7分钟

文章目录

1.分析炸弹所需要的参数

1.1 控制组件

炸弹的动画需要从引爆一段时间之后切换到另外一个动画即爆炸,那么首先需要一个最基本的参数anim用于控制Animator组件

private Animator anim;

1.2 时间变量

关于时间,需要记录炸弹引爆的开始时间,使用游戏时钟,还需要记录引爆的时长,也就是等待爆炸的时间。他们都是float型的变量,开始时间+等待时间就是爆炸动画效果播放的时间。

private float startTime;
private float waitTime;

1.3 检测元素

这里的主要指的是检测范围,炸弹爆炸的时候有一定的范围影响,需要设置炸弹的范围。其实炸弹爆炸后游戏物体也要收到物理影响,还需要知道受爆炸影响的layer层级。

public float radius; // 范围半径
public LayerMask targetLayer; // 受爆炸影响的层级

1.4 作用力

需要设置一个作用力,当爆炸范围检测到受爆炸影响的物体时,需要给他们添加上这个力, 让他们在爆炸的瞬间受力的影响弹出去。

public float bombForce;

2.添加并设置检测范围

2.1 添加可视化代码

unity系统提供了OnDrawGizmos函数用来可视化检测范围,在这里可视化一个球形的范围,传入检测的中心点就是当前炸弹的位置,检测范围就是前面定义的radius变量。

// 检测范围可视化
public void OnDrawGizmos()
{
    // 中心点/检测范围
    Gizmos.DrawWireSphere(transform.position, radius);
}

2.2 回到unity修改检测范围的半径

在脚本中添加了2.1的可视化代码之后,在unity中修改参数可以看到一圈白色的线,就是炸弹爆炸时的检测范围。

但是这个检测范围并不是随便设置的,应该是播放爆炸动画时的爆炸范围,需要根据爆炸特效的最大范围来确定,最后我把radius的取值确定在1.35~1.4之间。

检测的图层LayerMask是包括炸弹周围的环境,游戏Player和敌人,还有炸弹本身。那么在设置targetLayer时需要把Enviroment,NPC,Bomb都勾选。

3.炸弹的爆炸效果

3.1 确认检测物体

主要使用的方法是让炸弹爆炸的时候检测周围所有的碰撞体,然后为这些碰撞体施加一个作用力。使用物理检测的方法Physics2D来进行检测。
在文章前面做了一个圆形的检测范围,使用Physics2D.OverlapCircleAll方法可以获得该范围中所有的碰撞体,也就知道了有哪些物体是可以施加作用力的。该方法返回的是一个数组,此外还需要定义一个临时数组来存放检测到的碰撞体。

⚠️在进行检测的时候,炸弹本身也是会被检测进去的,但是爆炸影响的物体中不包含炸弹本身,所以在每次检测之前需要临时将炸弹的碰撞体脱离。但是物体的碰撞体一旦消失之后,该物体就会自然的下落脱离地面。所以还需要添加刚体组件把重力设置为0

private Collider2D coll; // 用于脱离炸弹碰撞体
private Rigidbody2D rb; // 用于设置炸弹重力防止掉出屏幕

Physics2D.OverlapCircleAll方法需要传入三个参数(检测中心点,检测范围,图层)

public void Explosion()
{
	// 检测范围之前脱离炸弹的碰撞体
    coll.enabled = false;
    // 重力设置为0防止掉落出地面
    rb.gravityScale = 0;
    // 坐标,检测范围,图层
    Collider2D[] aroundObjects = Physics2D.OverlapCircleAll(transform.position, radius, targetLayer);
}

3.2 施加作用力

物理检测的前提条件是物体需要有刚体和碰撞体,可以循环数组中所有的碰撞体获得他们的刚体,并为这个刚体施加弹出去的作用力。
在这个循环中,每一个检测的物体需要与当前的炸弹的坐标进行对比,得到物体在炸弹的某一方向坐标pos(当前炸弹的位置-物体的位置),上边或者下边或者左边或者右边等。每一个检测到的物体一定有刚体组件,使用组件的AddForce()方法为它添加一个反方向的作用力达到物体被弹开的效果。

AddForce需要传入的参数( -pos炸弹威力+向上方向炸弹威力,冲击力ForceMode2D.Impulse)

foreach (var i in aroundObjects)
{
    // 判断物体的方向
    Vector3 pos = transform.position - i.transform.position;
    i.GetComponent<Rigidbody2D>().AddForce((-pos + Vector3.up) * bombForce, ForceMode2D.Impulse);
}

3.3 处理爆炸事件

上面写的爆炸方法是一个爆炸瞬间的事件方法,需要返回Unity找到bomb_explosion动画,为动画的第一帧添加这个方法。
同时炸弹爆炸之后应该消失,还应该写一个销毁的方法。

// 炸弹消失的事件方法
public void Destroy()
{
    Destroy(gameObject);
}

4.添加动画切换

在添加了事件方法以后,动画效果还需要最后一步,进行炸弹引爆到爆炸的动画切换。此前炸弹的动画默认是引爆动画的,不进行切换之前会一直处于引爆状态。
开始时间获取当前的游戏时钟。
在Start()方法里添加startTime = Time.time;
在Update里利用开始事件和等待事件进行检测是否已经满足固定的炸弹引爆cd,当满足条件后,进行动画的切换。

void Update()
{
    // 如果当前的游戏时间大于开始时间+等待时间
    if (Time.time > startTime + waitTime)
        anim.Play("bomb_explosion");
}

5.测试效果

5.1 Bomb.cs的完整代码

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bomb : MonoBehaviour
{
    private Animator anim; // 控制动画切换
    private Collider2D coll; // 用于脱离炸弹碰撞体
    private Rigidbody2D rb; // 用于设置炸弹重力防止掉出屏幕


    // 记录炸弹引爆的开始时间以及等待爆炸的时间
    private float startTime;
    public float waitTime;

    // 爆炸作用力
    public float bombForce;

    // 检测元素
    [Header("Check")] public float radius; // 范围半径
    public LayerMask targetLayer; // 受爆炸影响的层级

    void Start()
    {
        anim = GetComponent<Animator>();
        coll = GetComponent<Collider2D>();
        rb = GetComponent<Rigidbody2D>();
        startTime = Time.time;
    }

    void Update()
    {
        // 如果当前的游戏时间大于开始时间+等待时间
        if (Time.time > startTime + waitTime)
            anim.Play("bomb_explosion");
    }

    // 检测范围可视化
    public void OnDrawGizmos()
    {
        // 中心点/检测范围
        Gizmos.DrawWireSphere(transform.position, radius);
    }

    // 爆炸效果,是一个Animation Event
    public void Explosion()
    {
        // 检测范围之前脱离炸弹的碰撞体
        coll.enabled = false;
        // 重力设置为0防止掉落出地面
        rb.gravityScale = 0;
        // 坐标,检测范围,图层
        Collider2D[] aroundObjects = Physics2D.OverlapCircleAll(transform.position, radius, targetLayer);
        foreach (var i in aroundObjects)
        {
            // 判断物体的方向
            Vector3 pos = transform.position - i.transform.position;
            i.GetComponent<Rigidbody2D>().AddForce((-pos + Vector3.up) * bombForce, ForceMode2D.Impulse);
        }
    }

    // 炸弹消失的事件方法
    public void Destroy()
    {
        Destroy(gameObject);
    }
}

在unity中为等待时间和作用力赋值后启动游戏进行测试。我在这里设置的等待引爆时间waitTime = 2,爆炸力boomForce = 6
爆炸效果测试如下图:
在这里插入图片描述

5.2 存在的测试bug

当我加大boomForce修改为8的时候再次测试发现了一个bug。
在这里插入图片描述
如上图所示,凳子爆炸后弹出被卡进了墙体,这需要修改椅子的刚体组件中的Collision Detection 参数修改为Continuous防止物体对象穿过所有静态碰撞体,此外场景中的所有物体都需要需改这一参数。