unity应用实例——从头撸一个全新的FPS游戏(2)

上一篇博客完成了第一人称视角的角色控制,这一张在开始武器系统之前,我们先做一个用户游戏时的UI界面,方便后面对武器系统进行检验。

通过GameObject--》UI--》Canvas创建一个画布,在画布下添加以下元素:

其中bullet为子弹图标,aimer为准星图标,将相应的图片资源添加进去,然后调整好位置,即可。

现在开始写武器系统,首先在主摄像机下创建一个空物体,命名为WeaponManager,然后在这个空物体下创建三个空物体来记录三个武器的位置,再将各个武器的prefab放进这三个空物体。

之所以专门创建一个空物体来标记武器位置,是因为武器播放动画时会产生一定的位移,而位移的中心为其父物体(这里可能是动画的问题,没办法bake,可以bake的话应该就不用创建Gunpos等三个空物体了)。然后,每一个武器的prefab中包括三个元素:武器模型、一个空物体(标记枪口位置)、一个点光源(提升开火效果)。

为每个武器的prefab添加好Auidio Sourse和Animation(没有用Animator是因为动画太少,逻辑很简单[其实是懒]),现在终于可以开始写代码了。

首先是不同武器的脚本,下面示例一个手枪的(需要注意的是,这里我们的武器开火时并没有真的发射出子弹,仅是利用一种视觉错觉,弹道实际上是从摄像头发出的一条射线):

using UnityEngine;
using UnityEngine.UI;

public class gun : MonoBehaviour
{
    public Transform m_camera;//主相机
    public Transform textruehit;//击中墙壁的图片
    public Transform spark;//击中物体的特效
    public Transform flashfire;//开火特效
    public Transform gunmouth;//枪口的位置
    public Light m_light;//为了增强开火特效的点光源
    public Text BulletText;//子弹数文本
    private RaycastHit hit; // 命中的对象

    [SerializeField]
    private int curbulletnum;
    [SerializeField]
    private int maxbulletnum;
    [SerializeField]
    private int storebulletnum;
    private float timer;//为了记录开火的时间间隔
    [SerializeField]
    private float AccuracyRange;//子弹的准度范围
    private float reloadtimer;//记录换弹的时间间隔
    [SerializeField]
    private int damage;
    [SerializeField]
    private int shootrange;

    public AudioClip ShootAudio;
    public AudioClip ReloadedAudio;

    private int m_animation;

    public AnimationClip IdleAnimation;
    public AnimationClip ReloadAnimation;
    public AnimationClip ShootAnimation;

    string IdleS;
    string ReloadS;
    string ShootS;

    void Start()
    {
        IdleS = IdleAnimation.name;
        ReloadS = ReloadAnimation.name;
        ShootS = ShootAnimation.name;
        m_light.transform.position = gunmouth.position;
        m_light.range = 0;

        m_animation = 0;
        reloadtimer = 0;
        timer = 0;
    }

    void Update()
    {
        SwicthAimation();

        #region 换子弹、开火
        //主动换弹
        if (Input.GetKeyDown("r") && storebulletnum > 0 && curbulletnum != maxbulletnum && m_animation != 1)
        {
            m_animation = 1;
        }
        if (Input.GetButton("Attack") && m_animation == 0)
        {
            if (curbulletnum == 0 && storebulletnum > 0) //自动换弹
            {
                m_animation = 1;
            }
            else if (curbulletnum > 0)
            {
                ShootFire();
                m_animation = 2;
            }
        }
        #endregion
        FreshBulletText();
    }
    //生成枪打到障碍物的特效
    void PlayHitEffect(Vector3 hitpos, Vector3 hitdirection)
    {
        //获得一个垂直于墙壁向上的方向
        Quaternion HitRotation = Quaternion.FromToRotation(Vector3.up, hit.normal);

        GameObject s = Instantiate(spark.gameObject, hitpos, HitRotation);
        GameObject h = Instantiate(textruehit.gameObject, hitpos, HitRotation);

        Destroy(s, 1);
        Destroy(h, 3);
    }

    void ShootFire()
    {
        // 播放枪发射子弹时枪口的火焰
        GetComponent<AudioSource>().PlayOneShot(ShootAudio);
        //确定弹道方向
        Vector3 direction = m_camera.TransformDirection
            (Vector3.forward + new Vector3
            (Random.Range(-AccuracyRange, AccuracyRange), Random.Range(-AccuracyRange, AccuracyRange)));
        // 播放枪发射子弹时枪口的火焰
        GameObject f = Instantiate
(flashfire.gameObject, gunmouth.transform.position, gameObject.transform.rotation);

        if (Physics.Raycast(m_camera.position, direction, out hit, shootrange))
        {
            Debug.Log(hit.collider.gameObject.name);
         //这里的代码以后会进行改动,对于击中不同的物体会有不同的效果

            PlayHitEffect(hit.point, direction);
        }
        Destroy(f, 0.04f);

        curbulletnum--;
    }

    void Reload()
    {
        if (storebulletnum > maxbulletnum - curbulletnum)
        {
            storebulletnum -= maxbulletnum - curbulletnum;
            curbulletnum = maxbulletnum;
        }
        else
        {
            curbulletnum += storebulletnum;
            storebulletnum = 0;
        }
    }//装弹计算

    void FreshBulletText()
    {
        BulletText.text = curbulletnum.ToString() + "/" + storebulletnum.ToString();
    }

    void SwicthAimation()//转换动画
    {
        if (m_animation == 0)
        {
            GetComponent<Animation>().CrossFade(IdleS);
        }
        if (m_animation == 1)
        {
            GetComponent<Animation>().CrossFade(ReloadS);
            reloadtimer += Time.deltaTime;
        }
        if (m_animation == 2)
        {
            GetComponent<Animation>().Play(ShootS);
            m_light.range = 3;
            timer += Time.deltaTime;
        }

        if (reloadtimer >= GetComponent<Animation>()[ReloadS].length && m_animation == 1)// 当装弹动画结束时
        {
            Reload();
            m_animation = 0;
            reloadtimer = 0;
        }

        if (timer >= GetComponent<Animation>()[ShootS].length && m_animation == 2)// 当射击动画结束时
        {
            m_light.range = 0;
            timer = 0;
            m_animation = 0;
        }
    }

    public int GetCurBulletNum()
    {
        return curbulletnum;
    }
};

其他两个武器的脚本基本和手枪的差不多,就不放上来了,另外狙击枪需要再添加一个瞄准镜脚本:

扫描二维码关注公众号,回复: 2939725 查看本文章
//瞄准镜脚本
using UnityEngine;

public class sniperscope : MonoBehaviour
{
    [HideInInspector]
    public bool IsAiming;
    [SerializeField]
    private float ZoomLevel;
    [SerializeField]
    private float ZoomInSpeed;
    [SerializeField]
    private float ZoomOutSpeed;
    private float initFOV;//初始值

    public Texture aimtexture;//瞄准时加载的图片

    [HideInInspector]
    public bool CanAiming;

    void Start()
    {
        CanAiming = true;
       initFOV = Camera.main.fieldOfView;
    }
    void Update()
    {
        //如果条件允许
        if (gameObject&& CanAiming)
        {
            if (Input.GetMouseButton(1)) ZoomView();
            else  ZoomOut(); 
         }
        //如果正在瞄准,但当前条件不允许,回到正常状态
        if (IsAiming && !CanAiming)
        {
            Camera.main.fieldOfView=initFOV;
            IsAiming = false;
        }
        }

    private void ZoomView()
    {
        if (Camera.main.fieldOfView - (Time.deltaTime * ZoomInSpeed) >= initFOV / ZoomLevel)
        {
            Camera.main.fieldOfView -= Time.deltaTime * ZoomInSpeed;
        }
        IsAiming = true;
    }
    private void ZoomOut()
    {
        if (Camera.main.fieldOfView + (Time.deltaTime * ZoomOutSpeed) <= initFOV)
        {
            Camera.main.fieldOfView += Time.deltaTime * ZoomOutSpeed;
        }
        IsAiming = false;
    }
    void OnGUI()
    {
        if (IsAiming)
        {
            Rect rect = new Rect(0,//矩形的X轴坐标  
              0,//矩形的y轴的坐标  
              Screen.width,//矩形的宽  
            Screen.height);//矩形的高  
            GUI.DrawTexture(rect, aimtexture);//绘制图形 
        }
    }
}

处理好三个武器以后,再写一个武器管理脚本,主要对武器的切换进行管理,挂在命名为weaponmanager 的空物体上:

using UnityEngine;

public class weaponmanager : MonoBehaviour
{
    public int CurrentWeapon;//现在的武器
    public bool CanChangeWeapon;//是否可以更换武器

    public GameObject weapon1;//weapon1 prefab
    public GameObject weapon2;//weapon2 prefab
    public GameObject weapon3;//weapon3 prefab

    public gun m_gun;	//手枪
    public subgun m_subgun; //冲锋枪
    public gun m_sniperrifle;// 狙击枪和手枪用的是同一个脚本,所以类型是一样的
    public sniperscope m_sniperscope;//瞄准镜

    void Start()
    {
        NullWeapons();
        CurrentWeapon = 1;
        weapon1.SetActive(true);//默认将武器1设置为可用
        weapon1.GetComponent<Animation>().Play("Gun_On");

        Cursor.visible = false;//隐藏鼠标
    }

    void Update()
    {

        if (!m_sniperscope.IsAiming)//当狙击枪没有瞄准时可以更换武器
          CanChangeWeapon = true;  
        else  CanChangeWeapon = false;

        if (m_sniperrifle.GetCurBulletNum()==0)//当狙击无弹时不可瞄准
            m_sniperscope.CanAiming = false;
        else m_sniperscope.CanAiming = true;

        #region 更换武器
        //通过滑轮换武器
        if ((Input.GetAxis("Mouse ScrollWheel") < 0) & CanChangeWeapon == true)
        {
            CurrentWeapon -= 1;
            if (CurrentWeapon > 3) CurrentWeapon = 1;
            if (CurrentWeapon < 1) CurrentWeapon = 3;
            Switch(); 
        }
        if ((Input.GetAxis("Mouse ScrollWheel") > 0) & CanChangeWeapon == true)
        {
            CurrentWeapon += 1;
            if(CurrentWeapon>3) CurrentWeapon = 1;
            if(CurrentWeapon<1) CurrentWeapon = 3;
            Switch(); 
        }

        //通过按键换武器
        if (Input.GetKeyDown("1") & CanChangeWeapon == true)
        {
            if (!m_gun.isActiveAndEnabled)
            {
                CurrentWeapon = 1;
                Switch(); 
            }
        }
        if (Input.GetKeyDown("2") & CanChangeWeapon == true)
        {
            if (!m_subgun.isActiveAndEnabled)
            {
                CurrentWeapon = 2;
                Switch(); 
            }
        }
        if (Input.GetKeyDown("3") & CanChangeWeapon == true)
        {
            if (!m_sniperrifle.isActiveAndEnabled)
            {
                CurrentWeapon = 3;
                Switch(); 
            }
        }

        #endregion
    }

    public void NullWeapons()//隐藏所有武器
    {
        weapon1.SetActive(false);
        weapon2.SetActive(false);
        weapon3.SetActive(false);
    }

    public void Switch()// 转换武器
    {

        if (CurrentWeapon == 1)
        {
            NullWeapons();
            weapon1.SetActive(true);//将武器1设置为可用
            weapon1.GetComponent<Animation>().Play("Gun_On");
        }

        if (CurrentWeapon == 2)
        {
            NullWeapons();
            weapon2.SetActive(true);//将武器2设置为可用
            weapon2.GetComponent<Animation>().Play("SubGun_On"); 
        }

        if (CurrentWeapon == 3)
        {
            NullWeapons();
            weapon3.SetActive(true);//将武器3设置为可用
            weapon3.GetComponent<Animation>().Play("SniperRifle_On");
        }
    }
}

这样武器这边就基本弄好了,整体效果:

下一篇进行对敌人巡逻机器人的设计。

猜你喜欢

转载自blog.csdn.net/qq_37553152/article/details/81904033