功能:玩家发射子弹,弹夹有子弹发射 无子弹等待更换弹夹。枪可以单发 连发,播放音效 动画 火花。
主角子弹根据敌人部位减血,子弹击中目标销毁。敌人向玩家头部射击,射速较慢 易于躲藏。
需求分析:
枪 Gun,开火 更换弹夹。
单发枪 SingleGun,继承自Gun根据玩家输入调用 开火 更换弹夹。
连发枪 AutomaticGun,继承自Gun根据玩家输入调用方法。
子弹 Bullet,计算攻击到的目标点(射线检测 环境 敌人)(敌人攻击只检测环境)。
玩家子弹 PlayerBullet,继承自Bullet根据击中敌人部位减血。
敌人子弹 EnemyBullet,继承自Bullet击中玩家扣血。
玩家状态信息 PlayerStatusInfo,血量。
枪 Gun↓
using UnityEngine;
using System.Collections;
/// <summary>
/// 枪
/// </summary>
public class Gun : MonoBehaviour
{
//攻击力
public float atk;
//攻击距离
public float atkDistance=200;
/// <summary>
/// 子弹预制件
/// </summary>
public GameObject bulletPrefab;
/// <summary>
/// 开火点
/// </summary>
public Transform firePoint;
//音频
private AudioSource source;
//枪动画
private GunAnimation anim;
//枪口动画
private MuzzleFlash flash;
private void Start()
{
source = GetComponent<AudioSource>();
anim = GetComponent<GunAnimation>();
flash = firePoint.GetComponent<MuzzleFlash>();
}
//发射子弹
//玩家枪发射的子弹,朝向枪口正方向
//敌人发射的子弹,朝向玩家
public bool Firing(Vector3 dir)
{
//判定能否发射子弹(弹匣子弹数 攻击动画是否播放)
if (!Ready()) return false;
//如果可以发射子弹
//1.创建子弹(创建谁?在哪?旋转?)
GameObject bulletGO = Instantiate(bulletPrefab, firePoint.position, Quaternion.LookRotation(dir)) as GameObject;
//初始化,传递攻击力、攻击距离
bulletGO.GetComponent<Bullet>().Init(atk, atkDistance);
//2.播放声音
source.Play();
//3.播放动画
//anim.Play(anim.fireAnimName); 因为录制的动画片段很短,所以使用PlayQueued播放
anim.PlayQueued(anim.fireAnimName);
//4.显示火花
flash.DisplayFlash();
return true;
}
public int ammoCapacity;//弹匣容量
public int currentAmmoBullets; //当前弹匣内子弹数
public int remainBullets;//剩余子弹数
//准备发射子弹
private bool Ready()
{
if (currentAmmoBullets <= 0 || anim.IsPlaying(anim.updateAnimName)) return false;
currentAmmoBullets--;
//如果弹匣没有子弹 则 播放缺少子弹动画
if (currentAmmoBullets == 0)
anim.PlayQueued(anim.lackBulletAnimName);
return true;
}
//更换弹匣
public void UpdateAmmo()
{
//能否更换(剩余子弹数 当前弹匣子弹数)
if (remainBullets <= 0 || currentAmmoBullets == ammoCapacity) return;
//播放更换弹匣动画
anim.PlayQueued(anim.updateAnimName);
//向当前弹匣内添加子弹
currentAmmoBullets = remainBullets >= ammoCapacity ? ammoCapacity : remainBullets;
//扣除剩余子弹
remainBullets -= currentAmmoBullets;
}
}
单发枪 SingleGun↓
using UnityEngine;
using System.Collections;
/// <summary>
/// 玩家单发枪控制
/// </summary>
public class SingleGun: Gun
{
private void Update()
{
if (Input.GetMouseButtonDown(0))
{//朝向枪口正前方发射子弹
base.Firing(gun.firePoint.forward);
}
if (Input.GetMouseButtonDown(1))
{//朝向枪口正前方发射子弹
base.UpdateAmmo();
}
}
}
连发枪 AutomaticGun 略
子弹 Bullet↓
using UnityEngine;
using System.Collections;
/// <summary>
/// 子弹
/// </summary>
public class Bullet : MonoBehaviour
{
[HideInInspector]
public float atk;
[HideInInspector]
public RaycastHit hit;
[HideInInspector]
public float atkDistance;
public LayerMask layer;
public void Init(float atk,float distance)
{
this.atk = atk;
atkDistance = distance;
CalculateTargetPoint();
}
public Vector3 targetPos;
//通过射线计算击中物体
private void CalculateTargetPoint()
{
//当前位置(枪口位置),当前方向(枪口方向),受击目标信息,攻击最大距离,射线检测层
if (Physics.Raycast(transform.position, transform.forward, out hit, atkDistance, layer))
{
targetPos = hit.point;
}
else
{
targetPos = transform.TransformPoint(0, 0, atkDistance);
}
}
private void Update()
{
Movement();
if ((transform.position - targetPos).sqrMagnitude < 0.1f)
{ //到达目标点
//销毁子弹
Destroy(gameObject);
//生成特效
GenerateContactEffect();
}
}
private void Movement()
{
transform.position = Vector3.MoveTowards(transform.position, targetPos, Time.deltaTime * moveSpeed);
}
//生成接触特效
private void GenerateContactEffect()
{ // Resources/ContactEffects/xxxx
//根据 受击物体标签( hit.collider.tag) 创建相应特效
//规定:读取的资源必须放置在 Resources 文件夹内
//GameObject go = Resources.Load<GameObject>("ContactEffects/xx");
if (hit.collider == null) return;
//特效命名规则:Effects+标签
string prefabName = "ContactEffects/Effects" + hit.collider.tag;
GameObject go = Resources.Load<GameObject>(prefabName);
if (go)
{
//击中特效资源(资源预设,目标点位置 向法线方向移动0.01m,z轴朝向法线方向)为了保证子弹产生的弹孔特效 垂直于平面
Instantiate(go, targetPos + hit.normal *0.01f, Quaternion.LookRotation(hit.normal));
}
}
public float moveSpeed = 100;
}
玩家子弹 PlayerBullet↓
/// <summary>
/// 玩家子弹
/// </summary>
public class PlayerBullet : Bullet
{
//提示:开枪 --》发射子弹 --》射线检测 --》调用敌人受伤方法
private void Start()
{
//根据击中部位(受击物体的名称 hit.collider.name),调用敌人受伤方法
if (hit.collider !=null && hit.collider.tag == "Enemy")
{
float atk = CalculateAttackForce();
print(atk);
hit.collider.GetComponentInParent<EnemyStatusInfo>().Damage(atk);
}
}
//计算攻击力
private float CalculateAttackForce()
{
//建议查询配置文件进行修改
//敌人的部位
switch (hit.collider.name)
{
case "Coll_Head":
return atk * 2;
case "Coll_Body":
return atk;
default:
return atk * 0.5f;
}
}
}
敌人子弹 EnemyBullet↓
/// <summary>
/// 敌人子弹
/// </summary>
public class EnemyBullet : Bullet
{
//通过碰撞检测攻击玩家(调用玩家受伤方法 atk)
//PlayerStatusInfo.Instance.Damage(atk);
private void OnTriggerEnter(Collider other)
{
//如果与玩家接触
if (other.tag == "Player")
{
//减血
PlayerStatusInfo.Instance.Damage(atk);
//销毁子弹
Destroy(gameObject);
}
}
}
玩家状态信息 PlayerStatusInfo↓
/// <summary>
/// 玩家状态信息
/// </summary>
public class PlayerStatusInfo : MonoBehaviour
{
//静态 自身类型 (对外)只读属性
public static PlayerStatusInfo Instance { get; private set; }
private void Awake()
{
Instance = this;
}
public float HP;
public float maxHP;
//玩家头部位置
public Transform headTF;
public void Damage(float amount)
{
HP -= amount;
}
}