Unity study notes: fps game player shooting module (2)

  Function: The player fires a bullet, and the magazine has a bullet and no bullet is waiting to be replaced. The gun can be fired in a single burst, and play sound animation sparks.

  The protagonist’s bullets lose blood according to the enemy’s parts, and the bullets are destroyed when they hit the target. The enemy shoots at the player's head, with a slower rate of fire and easy hiding.

  demand analysis:

  Gun Gun, fire and replace the magazine.

  Single Gun SingleGun, inherited from Gun, fires and replaces the magazine according to player input.

  Automatic Gun, inherited from Gun, calls methods based on player input.

  Bullet, calculate the target point of the attack (ray detection environment enemy) (the enemy attack only detects the environment).

  Player Bullet PlayerBullet, inherited from Bullet, reduces blood according to the position of the enemy hit.

  The enemy bullet EnemyBullet, inherited from Bullet hitting the player to deduct blood.

  Player status information PlayerStatusInfo, blood volume.

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;
    }
}

Single Gun ↓

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();
        }
    }
 
}

Automatic Gun Slightly

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);
        }
    }
}

Player Status Info 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;
    } 

}

 

Guess you like

Origin blog.csdn.net/huanyu0127/article/details/105525952