Unity子弹生成系统

▼魔方 西西 提交于 2019-11-30 07:50:59

子弹系统和粒子系统比较类似,为了创建和五花八门的子弹,例如追踪,连续继承,散弹等,需要一个拥有众多参数的子弹生成器,这里叫它Shooter好了。

Shooter负责把玩各类子弹造型和参数,创建出子弹,创建完了之后接下来就交给子弹自己来管理自己了。

所以,一个子弹系统包含:

1.ShooterSystem类

一个能生成各种类型子弹的发射器。

2.Bullet类

按照给定的初始参数不断向前飞行的子弹个体。

 

先思考每一个单独的子弹需要有哪些物理参数:

 1     //目标
 2     public GameObject Target { get; set; }
 3     //瞬时速度
 4     public float Velocity { get; set; }
 5     //剩余生命周期
 6     public float LifeTime { get; set; }
 7     //角速度
 8     public float Palstance { get; set; }
 9     //线性加速度
10     public float Acceleration { get; set; }
11     //最大速度
12     public float MaxVelocity { get; set; }

这些参数不需要子弹自己来配置,而是交给把玩它们的Shooter来进行,但是子弹自身需要知道这些参数。

其中指得一提的是角速度,正常的子弹是没有追踪功能的,生成之后就只能自动向前飞,但一旦设置了子弹的目标后,子弹就必须根据角速度转向目标位置的向量,保证自己的前向能尽快和目标向量对齐;而这一对齐的过程,就需要用角速度来描述。

子弹在生命周期到了之后要自动销毁,因为它经常反复创建和销毁,最好使用对象池来进行这一过程:

https://www.cnblogs.com/koshio0219/p/11572567.html

调用如下:

1     public IEnumerator AutoRecycle()
2     {
3         yield return new WaitForSeconds(LifeTime);
4         ObjectPool.Instance.RecycleObj(gameObject);
5     }

 

子弹每一帧的状态都会有所变化,例如位置,速度的更新,向前运行的方向的更新等:

 1     private void Update()
 2     {
 3         float deltaTime = Time.deltaTime;
 4         //由当前子弹位置指向目标位置的向量,记为瞬时偏移向量
 5         Vector3 offset = (Target.transform.position - transform.position).normalized;
 6         //子弹的当前前进方向与瞬时偏移向量之间的夹角
 7         float angle = Vector3.Angle(transform.forward, offset);
 8         //夹角除以角速度计算需要转到相同方向所需要的总时间
 9         float needTime = angle*1.0f / Palstance;
10         //插值运算出当前帧的前向方向向量,也即是需要偏移的角度
11         transform.forward = Vector3.Lerp(transform.forward, offset, deltaTime / needTime).normalized;
12         //处理线性加速度对于速度的增量
13         if (Velocity < MaxVelocity)
14         {
15             Velocity += deltaTime * Acceleration;
16         }
17         //按当前速度向前移动一帧的距离,赋值给当前位置
18         transform.position += transform.forward * Velocity * deltaTime;
19     }

如果不想让子弹追踪,也很简单,把角速度传为0即可,float除数为0也是没有问题的。

 

子弹生成器主要是创建子弹,所以需要包含子弹类的所有参数,除此之外,还需要有一些其他的参数:

 1     public bool bAuto = false;
 2 
 3     public GameObject bulletPrefab;
 4     //子弹目标
 5     public GameObject target;
 6     //初速度
 7     public float velocity = 0f;
 8     //加速度
 9     public float acceleration = 30f;
10     //总生命周期
11     public float lifeTime = 3f;
12     //初始方向
13     public Vector2 direction = Vector2.zero;
14     //最大速度
15     public float maxVelocity = 600;
16     //角速度
17     public float palstance = 120;
18     //角度波动范围
19     public float angelRange = 0f;
20     //延迟
21     public float delay = 1f;
22     //是否循环
23     public bool bLoop = false;
24     //时间间隔
25     public float timeCell = .1f;
26     //生成数量
27     public int count = 1;
28     //伤害
29     public float damage;
30     //碰撞类型
31     public CollisionType collisionType;
32     //是否有子系统
33     public bool bChildShooter = false;
34     //子系统是谁
35     public GameObject childShooter;

初始方向就是子弹生成后的前向方向,如果想制造散弹效果,则子弹就需要在一定的角度波动范围内生成前向方向,但生成的位置依然是统一的。

生成器还需要能循环生成子弹,能够在生成的子弹飞行过程中继续生成不一样效果的分裂子弹,所以还需要子系统,子系统和父系统可以写为同一个生成器类。需要注意的就是,子系统的生命周期需要依赖父系统生成的子弹的生命周期。

生成单个子弹的方法:

 1     private void Creat(Transform parent)
 2     {
 3         //从对象池中取对象生成到指定物体下,复位坐标
 4         var ins = ObjectPool.Instance.GetObj(bulletPrefab, parent.transform);
 5         ins.transform.ResetLocal();
 6 
 7         //对子弹的属性赋值
 8         var bullet = ins.GetComponent<Bullet>();
 9         bullet.Target = target;
10         bullet.Velocity = velocity;
11         bullet.Acceleration = acceleration;
12         bullet.LifeTime = lifeTime;
13         bullet.MaxVelocity = maxVelocity;
14         bullet.Palstance = palstance;
15 
16         //确定子弹生成方向的范围
17         float x = Random.Range(direction.x - angelRange / 2, direction.x + angelRange / 2);
18         float y = Random.Range(direction.y - angelRange / 2, direction.y + angelRange / 2);
19         bullet.transform.eulerAngles = new Vector3(x, y, 0);
20 
21         parent.DetachChildren();
22 
23         //开启子弹自动回收
24         StartCoroutine(bullet.AutoRecycle());
25 
26         //判断子生成器并自动运行
27         if (bChildShooter)
28         {
29             var cscs = childShooter.GetComponent<ShooterSystem>();
30             if (lifeTime > cscs.delay)
31                 StartCoroutine(cscs.AutoCreat(bullet.transform, this));
32             else
33                 Debug.Log("子发射器延迟时间设置有误!");
34         }
35     }

对于子生成器来说,它也同样可能拥有自己的子生成器,在AutoCreat的方法中需要传递它的父生成器是谁,默认情况下为空:

 1     IEnumerator AutoCreat(Transform parent, ShooterSystem parShooter = null)
 2     {
 3         yield return new WaitForSeconds(delay);
 4         if (bLoop)
 5         {
 6             if (parShooter != null)
 7             {
 8                 //子生成器需要计算循环的次数,父生成器则是无限循环
 9                 int loopCount = (int)(parShooter.lifeTime - delay / timeCell);
10                 for (; loopCount > 0; loopCount--)
11                 {
12                     //每次循环生成的子弹数量
13                     for (int i = 0; i < count; i++)
14                         Creat(parent);
15                     yield return new WaitForSeconds(timeCell);
16                 }
17             }
18             else
19             {
20                 for (; ; )
21                 {
22                     for (int i = 0; i < count; i++)
23                         Creat(parent);
24                     yield return new WaitForSeconds(timeCell);
25                 }
26             }
27         }
28         else
29         {
30             for (int i = 0; i < count; i++)
31                 Creat(parent);
32         }
33     }

这里的伤害和碰撞类型不在此篇讨论范围内。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!