Unity学习笔记:fps游戏玩家射击模块(2)

  功能:玩家发射子弹,弹夹有子弹发射 无子弹等待更换弹夹。枪可以单发 连发,播放音效 动画 火花。

  主角子弹根据敌人部位减血,子弹击中目标销毁。敌人向玩家头部射击,射速较慢 易于躲藏。

  需求分析:

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

}

 

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值