好处
把射出子弹抽象出一个类,专门管理射出子弹的一系列动作,
这样的话就没必要在每个类中写生成子弹,
还可以方便改变子弹的发射方式:
通过显示其中一个GameObject选择子弹发射方式
子弹发射器类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// 子弹数据初始化功能
/// </summary>
public abstract class ShootBase : MonoBehaviour
{
public GameObject prefab; //子弹预制体
public int maxBulletNum = 20; //最大数量
public float speed = 5.0f; //子弹速度
public float acceleration = 20.0f; //加速度
public float accelerationTurn = 0f;//旋转加速度
public bool useMaxSpeed;
[UbhConditionalHide("useMaxSpeed")]
public float maxSpeed;
public bool useMinSpeed;
[UbhConditionalHide("useMinSpeed")]
public float minSpeed;
public float life = 5.0f;
protected bool isShooting; //是否发射中
public UnityEvent fireEvent = new UnityEvent();//每发射一颗子弹调用的事件
public UnityEvent finishFireEvent = new UnityEvent();//子弹发射完之后调用的事件
public abstract void shoot();
public Bullet getBullet(Transform transPoint)
{
GameObject goBullet = Instantiate(prefab, transPoint.position,transPoint.rotation);
print("goBullet=" + goBullet);
if (!goBullet)
{
return null;
}
Bullet b = goBullet.GetComponent<Bullet>();
return b;
}
public void shootBullet(Bullet b, float speed, float acceleration,
float accelerationTurn, float offsetAngle, bool useMaxSpeed = false,
float maxSpeed = 20f, bool useMinSpeed = false, float minSpeed = 0f, float life = 5f)
{
b.shoot(speed, acceleration, maxSpeed, minSpeed, useMaxSpeed, useMinSpeed, accelerationTurn, life, offsetAngle);
}
public void finishShoot()
{
isShooting = false;
finishFireEvent.Invoke();
}
protected void firedShoot()
{
fireEvent.Invoke();
}
}
直线发射
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LineShoot : ShootBase
{
public float dtTime = 0.2f; //发射间隔
float remainTime; //剩余时间
public Transform firePoint;
int curBulletNum; //当前子弹数量
protected float offsetAngle; //发射器的旋转角度
public override void shoot()
{
isShooting = true;
remainTime = 0;
curBulletNum = 0;
}
void Start()
{
isShooting = true;
}
public virtual void Update()
{
print("LineShootUpdate");
if (!isShooting)
{
print("isShooting");
return;
}
if (remainTime>0)
{
remainTime -= Time.deltaTime;
if (remainTime>0)
{
return;
}
}
//发射子弹
Bullet b = getBullet(firePoint);
print("B=" + b);
if (!b)
{
return;
}
shootBullet(b, speed, acceleration, accelerationTurn, offsetAngle);
curBulletNum++;
if (curBulletNum>=maxBulletNum)
{
finishShoot();
}
else
{
remainTime = dtTime;
}
}
}
直线追踪子弹
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LineAimShoot : LineShoot
{
public string tagName = "Player";
Transform targetTransform;
public bool isAiming = true;
public Transform getTransformWithTagName()//获取Player游戏对象的transform
{
if (string.IsNullOrEmpty(tagName))
{
return null;
}
GameObject goTarget = GameObject.FindWithTag(tagName);
if (!goTarget)
{
return null;
}
return goTarget.transform;
}
public void aimTarget()
{
if (!targetTransform)
{
targetTransform = getTransformWithTagName();
}
else
{
Vector3 dir = (targetTransform.position - transform.position).normalized;
float aimAngle = Quaternion.LookRotation(dir).eulerAngles.y;
offsetAngle = aimAngle - transform.eulerAngles.y;//计算偏移角度
}
}
public override void shoot()
{
aimTarget();
base.shoot();
}
public override void Update()
{
if (isShooting && isAiming)
{
aimTarget();
}
base.Update();
}
}
散射子弹
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
public class NWayShoot : ShootBase
{
public int wayNum = 5;//线路数量
public float rotateAngle = 0f;//发射的旋转角度
[Range(0f, 360f), FormerlySerializedAs("_BTAngle")]
public float betweenAngle = 10f;//每条线路之间的角度
float remainTime;//下一次发射剩余时间
public float delayTime = 0.2f;//发射间隔时间
private int curBulletNum;//当前子弹数量
public override void shoot()
{
isShooting = true;
remainTime = 0f;
curBulletNum = 0;
}
void Start()
{
shoot();
}
public float GetShiftedAngle(int wayIndex, float baseAngle, float betweenAngle)
{
float angle = wayIndex % 2 == 0 ?
baseAngle - (betweenAngle * (float)wayIndex / 2f) :
baseAngle + (betweenAngle * Mathf.Ceil((float)wayIndex / 2f));
return angle;
}
protected virtual void Update()
{
if (!isShooting) return;
if (remainTime > 0)
{
remainTime -= Time.deltaTime;
if (remainTime > 0) return;
}
for (int i = 0; i < wayNum; i++)
{
Bullet b = getBullet(transform);
if (!b) break;
float baseAngle = wayNum % 2 == 0 ? rotateAngle - (betweenAngle / 2f) : rotateAngle;
float _offsetAngle = GetShiftedAngle(i, baseAngle, betweenAngle);
print("_offsetAngle=" + _offsetAngle);
shootBullet(b, speed, acceleration, accelerationTurn, _offsetAngle);
curBulletNum++;
if (curBulletNum >= maxBulletNum)
{
break;
}
}
firedShoot();
if (curBulletNum >= maxBulletNum)
{
finishShoot();
}
else
{
remainTime = delayTime;
}
}
}
散射追踪子弹
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NWayAimShoot : NWayShoot
{
public string tagName = "Player";
Transform target;
public bool isAiming = true;
public Transform getTransformWithTagName()
{
if (string.IsNullOrEmpty(tagName))
{
return null;
}
GameObject goTarget = GameObject.FindWithTag(tagName);
if (!goTarget)
{
return null;
}
return goTarget.transform;
}
public void aimTarget()
{
if (target == null)
{
target = getTransformWithTagName();
}
else
{
Vector3 dir = (target.position - transform.position).normalized;
float aimAngle = Quaternion.LookRotation(dir).eulerAngles.y;
rotateAngle = aimAngle - transform.eulerAngles.y;
}
}
public override void shoot()
{
aimTarget();
base.shoot();
}
protected override void Update()
{
if (isShooting && isAiming)
{
aimTarget();
}
base.Update();
}
}
随机散射子弹
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
public class RandomShoot : ShootBase
{
private float m_delayTimer;
private List<int> m_numList;
[FormerlySerializedAs("_RandomSpeedMin")]
public float m_randomSpeedMin = 1f;
[FormerlySerializedAs("_RandomSpeedMax")]
public float m_randomSpeedMax = 3f;
[Range(0f, 360f), FormerlySerializedAs("_RandomCenterAngle")]
public float m_randomCenterAngle = 0;
[Range(0f, 360f), FormerlySerializedAs("_RandomRangeSize")]
public float m_randomRangeSize = 360f;
[FormerlySerializedAs("_EvenlyDistribute")]
public bool m_evenlyDistribute = true;//是否使用均匀分布
[FormerlySerializedAs("_RandomDelayMin")]
public float m_randomDelayMin = 0.01f;
[FormerlySerializedAs("_RandomDelayMax")]
public float m_randomDelayMax = 0.1f;
public override void shoot()
{
if (maxBulletNum <= 0 || m_randomSpeedMin <= 0f || m_randomSpeedMax <= 0)
{
return;
}
if (isShooting)
{
return;
}
isShooting = true;
m_delayTimer = 0f;
if (m_numList != null)
{
m_numList.Clear();
m_numList = null;
}
m_numList = new List<int>(maxBulletNum);
for (int i = 0; i < maxBulletNum; i++)
{
m_numList.Add(i);
}
}
// Start is called before the first frame update
void Start()
{
shoot();
}
// Update is called once per frame
void Update()
{
if (isShooting == false)
{
return;
}
if (m_delayTimer >= 0f)
{
m_delayTimer -= Time.deltaTime;
if (m_delayTimer >= 0f)
{
return;
}
}
int index = Random.Range(0, m_numList.Count);
Bullet bullet = getBullet(transform);
if (bullet == null)
{
return;
}
float bulletSpeed = Random.Range(m_randomSpeedMin, m_randomSpeedMax);
float minAngle = m_randomCenterAngle - (m_randomRangeSize / 2f);
float maxAngle = m_randomCenterAngle + (m_randomRangeSize / 2f);
float angle = 0f;
if (m_evenlyDistribute)
{
float oneDirectionNum = Mathf.Floor((float)maxBulletNum / 4f);
float quarterIndex = Mathf.Floor((float)m_numList[index] / oneDirectionNum);
float quarterAngle = Mathf.Abs(maxAngle - minAngle) / 4f;
angle = Random.Range(minAngle + (quarterAngle * quarterIndex), minAngle + (quarterAngle * (quarterIndex + 1f)));
}
else
{
angle = Random.Range(minAngle, maxAngle);
}
shootBullet(bullet, speed, acceleration, accelerationTurn, angle);
firedShoot();
m_numList.RemoveAt(index);
if (m_numList.Count <= 0)
{
finishShoot();
}
else
{
m_delayTimer = Random.Range(m_randomDelayMin, m_randomDelayMax);
}
}
}