文章目录
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
防止物体对象穿过所有静态碰撞体,此外场景中的所有物体都需要需改这一参数。