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

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

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

1.ShooterSystem类

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

2.Bullet类

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

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

     //目标
public GameObject Target { get; set; }
//瞬时速度
public float Velocity { get; set; }
//剩余生命周期
public float LifeTime { get; set; }
//角速度
public float Palstance { get; set; }
//线性加速度
public float Acceleration { get; set; }
//最大速度
public float MaxVelocity { get; set; }

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

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

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

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

调用如下:

     public IEnumerator AutoRecycle()
{
yield return new WaitForSeconds(LifeTime);
ObjectPool.Instance.RecycleObj(gameObject);
}

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

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

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

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

     public bool bAuto = false;

     public GameObject bulletPrefab;
//子弹目标
public GameObject target;
//初速度
public float velocity = 0f;
//加速度
public float acceleration = 30f;
//总生命周期
public float lifeTime = 3f;
//初始方向
public Vector2 direction = Vector2.zero;
//最大速度
public float maxVelocity = ;
//角速度
public float palstance = ;
//角度波动范围
public float angelRange = 0f;
//延迟
public float delay = 1f;
//是否循环
public bool bLoop = false;
//时间间隔
public float timeCell = .1f;
//生成数量
public int count = ;
//伤害
public float damage;
//碰撞类型
public CollisionType collisionType;
//是否有子系统
public bool bChildShooter = false;
//子系统是谁
public GameObject childShooter;

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

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

生成单个子弹的方法:

     private void Creat(Transform parent)
{
//从对象池中取对象生成到指定物体下,复位坐标
var ins = ObjectPool.Instance.GetObj(bulletPrefab, parent.transform);
ins.transform.ResetLocal(); //对子弹的属性赋值
var bullet = ins.GetComponent<Bullet>();
bullet.Target = target;
bullet.Velocity = velocity;
bullet.Acceleration = acceleration;
bullet.LifeTime = lifeTime;
bullet.MaxVelocity = maxVelocity;
bullet.Palstance = palstance; //确定子弹生成方向的范围,默认Z轴正方向为子弹飞行方向
float x = Random.Range(direction.x - angelRange / , direction.x + angelRange / );
float y = Random.Range(direction.y - angelRange / , direction.y + angelRange / );
bullet.transform.localEulerAngles = new Vector3(x, y, ); parent.DetachChildren(); //开启子弹自动回收
bullet.StartCoroutine(bullet.AutoRecycle()); //判断子生成器并自动运行
if (bChildShooter)
{
var cscs = childShooter.GetComponent<ShooterSystem>();
if (lifeTime > cscs.delay)
StartCoroutine(cscs.AutoCreat(bullet.transform, this));
else
Debug.Log("子发射器延迟时间设置有误!");
}
}

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

     IEnumerator AutoCreat(Transform parent, ShooterSystem parShooter = null)
{
yield return new WaitForSeconds(delay);
if (bLoop)
{
if (parShooter != null)
{
//子生成器需要计算循环的次数,父生成器则是无限循环
int loopCount = (int)((parShooter.lifeTime - delay) / timeCell);
for (; loopCount > ; loopCount--)
{
//每次循环生成的子弹数量
for (int i = ; i < count; i++)
Creat(parent);
yield return new WaitForSeconds(timeCell);
}
}
else
{
for (; ; )
{
for (int i = ; i < count; i++)
Creat(parent);
yield return new WaitForSeconds(timeCell);
}
}
}
else
{
for (int i = ; i < count; i++)
Creat(parent);
}
}

有关伤害判断和碰撞检测不在此篇讨论范围内。

2019年12月12日更新:

增加以下几个功能:

1.可以控制子弹仅在单轴向的角度范围内散射,比如有时想让子弹只在同一个平面内散射,而不是在三维空间中。

2.可以控制子弹在散射范围内平均分布,而不是仅能随机分布。

3.可以控制子弹在非循环发射状态下按照固定时间间隔先后发射,比如追踪导弹一发发有序射击。

在此之前,先优化子弹中的一个小问题,子弹类的Update方法中,仅当存在追踪目标且角速度大于零时追踪目标:

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

下面开始实现前面的几个功能:

定义可选轴向,理论上只要绕两个方向的轴向就可以定义三维空间中的任何一个方向,这里将Z轴作为初始的前进方向因此不对Z轴作任何操作和改变。

 public enum AngelRangeAxis
{
//仅在绕Y轴的平面上,也即是X-Z平面
RYAxis,
//仅在绕X轴的平面上,也即是Y-Z平面
RXAxis,
//三维空间中
BothXY
}

在ShooterSystem类中增加定义以下属性:

     //是否固定单位角度
public bool bFixedAngel = false;
//单数量时间间隔
public float EachCountDur = 0f;
//计算得出的固定单位角度
private float FixAngel;
//范围轴向设置
public AngelRangeAxis RangeAxis;

在Start方法中根据一次发射数量计算单位角度:

         if (bFixedAngel)
{
FixAngel = AngelRange / (Count - );
}

在Creat方法中增加参数——当前创建的子弹索引idx,默认值为-1,可以不传递该参数,当传递该参数时,用于计算每一子弹在范围内应处于的角度:

         //确定子弹生成方向的范围,默认z轴正方向为子弹飞行方向
switch (RangeAxis)
{
case AngelRangeAxis.RYAxis:
bullet.transform.localEulerAngles = new Vector3(Direction.x, GetLocalEulerAxis(Direction.y, idx), );
break;
case AngelRangeAxis.RXAxis:
bullet.transform.localEulerAngles = new Vector3(GetLocalEulerAxis(Direction.x, idx), Direction.y, );
break;
case AngelRangeAxis.Both:
bullet.transform.localEulerAngles = new Vector3(GetLocalEulerAxis(Direction.x, idx), GetLocalEulerAxis(Direction.y, idx), );
break;
}

其中方法GetLocalEulerAxis定义如下,主要用于确定轴向的最终值(无论是固定角度还是随机):

     private float GetLocalEulerAxis(float dirAxis,int idx)
{
if (bFixedAngel)
return dirAxis- AngelRange / + FixAngel * idx;
else
return Random.Range(dirAxis - AngelRange / , dirAxis + AngelRange / );
}

在AutoCreat协程中的非循环生成条件中进行如下修改:

             for (int i = ; i < Count; i++)
{
if (bFixedAngel)
Creat(parent, i);
else
Creat(parent);
yield return new WaitForSeconds(EachCountDur);
}

修改后可以发射出类似于这样的追踪导弹:

或者这样同一平面内的等间距子弹:

Unity子弹生成系统的更多相关文章

  1. Spine学习七 - spine动画资源+ Unity Mecanim动画系统

    前面已经讲过 Spine自己动画状态机的动画融合,但是万一有哥们就是想要使用Unity的动画系统,那有没有办法呢?答案是肯定的,接下来,就说说如何实现: 1. 在project面板找打你导入的Spin ...

  2. CCF真题之模板生成系统

    问题描述 成成最近在搭建一个网站,其中一些页面的部分内容来自数据库中不同的数据记录,但是页面的基本结构是相同的.例如,对于展示用户信息的页面,当用户为 Tom 时,网页的源代码是 而当用户为 Jerr ...

  3. Unity 梯子生成算法

    Unity之生成梯子算法的实现. 1.通过预制物体动态生成角度可设置的梯子形状. 1.1 主要涉及到的数学知识点,角度与弧度的转化. 弧度=角度乘以π后再除以180 角度=弧度除以π再乘以180 1. ...

  4. CCF系列之模板生成系统( 201509-3 )

    试题名称: 模板生成系统 试题编号: 201509-3 时间限制: 1.0s 内存限制: 256.0MB 问题描述 成成最近在搭建一个网站,其中一些页面的部分内容来自数据库中不同的数据记录,但是页面的 ...

  5. 极速创建 IOS APP !涛舅舅苹果 IOS APP自助生成系统正式上线

    经过大量的测试和开发工作,涛舅舅苹果 IOS APP自助生成系统正式上线! 本系统主要功能: 1.用最最简单的方式将H5网站打包生成一个苹果APP 2.只需要提供APP标题,H5网站首页url地址,一 ...

  6. 极速创建 IOS APP !涛舅舅苹果 IOS APP自助生成系统!不用证书、不用越狱、永久可用

    不用签名将网页封装成苹果APP,无需苹果企业签名,IPA签名,ios签名,免越狱安装 (本方法只支持网站封装app,原生的用不了,详细请咨询客服) 近期很多朋友问我把网站变成app的方法,原因很多种, ...

  7. 企业信息化-Excel快速生成系统

    企业信息化,主要是指对企业生产运营过程所形成的信息数字化,最终形成了数字资产.大型企业为了节约成本,提高协同工作效率,都会定制ERP.办公OA.流程审批等系统做信息化支撑.但是中小企业精力投入到生成中 ...

  8. Leaf——美团点评分布式ID生成系统 UUID & 类snowflake

    Leaf——美团点评分布式ID生成系统 https://tech.meituan.com/MT_Leaf.html

  9. 分布式ID生成系统 UUID与雪花(snowflake)算法

    Leaf——美团点评分布式ID生成系统 -https://tech.meituan.com/MT_Leaf.html 网游服务器中的GUID(唯一标识码)实现-基于snowflake算法-云栖社区-阿 ...

随机推荐

  1. RN 性能优化

    按需加载: 导出模块使用属性getter动态require 使用Import语句导入模块,会自动执行所加载的模块.如果你有一个公共组件供业务方使用,例如:common.js import A from ...

  2. ASP.NET Core 2.2 : 二十六. 应用JWT进行用户认证

    本文将通过实际的例子来演示如何在ASP.NET Core中应用JWT进行用户认证以及Token的刷新方案(ASP.NET Core 系列目录) 一.什么是JWT? JWT(json web token ...

  3. Comupter Tools 清单------包含但不限于此

  4. awrcrt更新到2.1(重大更新)

    awrcrt更新到了2.1 awrcrt迎来了最近一年的最大一次更新,从2.03直接跳跃了2.1版本.本次更新,给awrcrt带了全面的改变. 最主要的更新内容是什么呢?请看 更新了图表javascr ...

  5. Python——常用模块(time/datetime, random, os, shutil, json/pickcle, collections, hashlib/hmac, contextlib)

    1.time/datetime 这两个模块是与时间相关的模块,Python中通常用三种方式表示时间: #时间戳(timestamp):表示的是从1970年1月1日00:00:00开始按秒计算的偏移量. ...

  6. MySql优化相关概念的理解笔记

    MySQL架构 查询执行流程 查询执行的流程是怎样的: 连接1.1客户端发起一条Query请求,监听客户端的‘连接管理模块’接收请求1.2将请求转发到‘连接进/线程模块’1.3调用‘用户模块’来进行授 ...

  7. Codeforces Round #479 (Div. 3) B. Two-gram

    原题代码:http://codeforces.com/contest/977/problem/B 题解:有n个字符组成的字符串,输出出现次数两个字符组合.例如第二组样例ZZ出现了两次. 方法:比较无脑 ...

  8. 阿里巴巴资深技术专家雷卷:值得开发者关注的 Java 8 后时代的语言特性

    作者 | 阿里巴巴资深技术专家  雷卷,GitHub ID @linux-china 导读:在 Python.JavaScript 等一众编程语言崛起风靡之际,一代霸主 Java 风采虽不及当年,但仍 ...

  9. 《Ansible自动化运维:技术与佳实践》第二章读书笔记

    Ansible 安装与配置 本章主要讲的是 Ansible 安装与基本配置,主要包含以下内容: Ansible 环境准备 安装 Ansible 配置运行环境 Ansible 环境准备 从 GitHub ...

  10. Docker版本与centos和ubuntu环境下docker安装介绍

    # Docker版本与安装介绍 * Docker-CE 和 Docker-EE * Centos 上安装 Docker-CE * Ubuntu 上安装 Docker-CE ## Docker-CE和D ...