6

通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学...

 3 years ago
source link: https://blog.csdn.net/zhangay1998/article/details/117947174
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!

  • 📢博客主页:https://blog.csdn.net/zhangay1998
  • 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
  • 📢本文由 God Y.原创,首发于 CSDN🙉
  • 📢未来很长,值得我们全力奔赴更美好美好的生活✨




  • ☔️上海已经入梅,小雨蒙蒙笼罩了整个上海。
  • 🐵窗外雨声滴滴答,室内键盘咔咔咔。
  • 👻正在无聊的时候呢,之前的学妹小云儿发来了消息~
  • 小云儿👩:学长哥哥,上次看完你的打飞机游戏,发现我差不多学会了哎~
  • 小Y(博主):那挺好呀,可以跟你的小姐妹们一起玩打飞机游戏了~
  • 小云儿👩:打飞机我们已经玩腻啦,想玩一款真正的射击游戏~就像CS那样刺激的啦!
  • 小Y:emm…你倒是挺喜欢玩刺激的哈~
  • 小云儿👩:嘿嘿~哥哥你不是说下次带人家玩更刺激的嘛,我这就来找你啦!
  • 小Y:好伐,但是一口气吃不成胖子,我现在先教你使用动画系统实现CS中的一些基本游戏操作,比- 如开枪、换枪、开火和换弹药等操作~
  • 小云儿👩:好咧哥哥,我小脑袋瓜本来也不够灵光,学这些正好!
  • 小Y:好咧,上一篇文章已经对Unity的动画基础做介绍了,现在我们就开始动手吧!
  • 小云儿👩:好咧哥哥,动手可以,不阔以动脚哦!
    在这里插入图片描述

🌟通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,憨憨学妹都学会了!

在这里插入图片描述

🎄导入一个资源包

  • 小Y:话说小云儿,我上一篇文章介绍[Unity中的动画系统]你有认真看完一遍嘛?
  • 小云儿👩:看完啦,小Y哥哥~ 就是有的地方还没有太明白~
  • 小Y:那正好,借助制作这个游戏能让你对Unity的动画有一个更深刻的认识!
  • 小云儿👩:好呀好呀~
  • 小Y:第一步要新建一个工程,然后才能正式开始!
  • 小云儿👩:哎呀,小Y哥哥 这个我都会了,这么简单就不用再介绍啦~
  • 小Y:猴急猴急的呢,咱们慢慢来~

既然想要做一个CS那样的射击游戏,场景跟模型肯定得有啊
我们这就来导入一个简单的资源包,里面有简单的一个场景和几把枪械
**图

  • 小云儿:场景和枪械都有啦,接下来该写脚本了吧?
  • 我:是的呢,小云儿你变聪明了!
  • 小云儿:哼唧,跟着哥哥学必须变聪明!

🎅第一人称视角控制、人物自由移动和移动声音控制

  • 小Y:那第一步就是写一个挂在人物身上的脚本,让角色可以自由移动。
  • 小云儿👩:好啊,先让角色动起来~
  • 小Y:还要加上第一人称视角控制,这样才有CS的既视感
  • 小云儿👩:对的对的,如果能给人物移动加上声音就更好了
  • 小Y:没错,还要给人物移动,奔跑和跳跃加上声音,这样就更有感觉了!

Unity场景设置

  • 首先,我们在场景中新建一个游戏对象GameObject,将其改名为FPSController

  • 然后给他添加上角色控制器Character ControllerAudio Source

  • 角色控制器Character Controller可以更容易的处理有碰撞的运动

  • Audio SourceUnity 中的 Audio 组件,其主要用来播放游戏场景中的 - AudioClipAudioClip 就是导入到 Unity 中的声音文件。

  • 其次,将场景中默认创建的Camera删掉或者取消激活,我们这里会自己新建一个Camera使用,所以暂时用不到这个默认的相机

  • 在这个游戏对象下再新建一个GameObject,改名为FirstPersonCharacter,给他添加上CameraAudioListener组件

  • 这个Camera就是我们能看到的第一视角,AudioListener是游戏中的声音接收器,用来接收场景中的声音,一般都挂在相机上

  • 主要是控制人物的行走、移动、跳跃

  • 还有人物身上的一些声音的控制

  • 定义一个值用来声明人物是否是行走状态,再定义两个参数分别用来用来处理移动速度和奔跑速度

  • 然后还有对第一人称视角的控制

  • 以及几个声音数组,用来控制人物行走,奔跑和跳跃的不同声音

  • 这块地方实现的方法有好多种,所以这里只是提供一种方法作为参考

  • 这里写的有点多,嫌麻烦的可直接复制这个脚本的代码挂在我们的FPSController身上即可

直接上代码,挂载到我们的角色控制器FPSController上就行

using System;
using UnityEngine;
using UnityStandardAssets.CrossPlatformInput;
using UnityStandardAssets.Utility;
using Random = UnityEngine.Random;

namespace UnityStandardAssets.Characters.FirstPerson
{
    [RequireComponent(typeof (CharacterController))]
    [RequireComponent(typeof (AudioSource))]
    public class FirstPersonController : MonoBehaviour
    {
        [SerializeField] private bool m_IsWalking;
        [SerializeField] private float m_WalkSpeed;
        [SerializeField] private float m_RunSpeed;
        [SerializeField] [Range(0f, 1f)] private float m_RunstepLenghten;
        [SerializeField] private float m_JumpSpeed;
        [SerializeField] private float m_StickToGroundForce;
        [SerializeField] private float m_GravityMultiplier;
        [SerializeField] private MouseLook m_MouseLook;
        [SerializeField] private bool m_UseFovKick;
        [SerializeField] private FOVKick m_FovKick = new FOVKick();
        [SerializeField] private bool m_UseHeadBob;
        [SerializeField] private CurveControlledBob m_HeadBob = new CurveControlledBob();
        [SerializeField] private LerpControlledBob m_JumpBob = new LerpControlledBob();
        [SerializeField] private float m_StepInterval;
        [SerializeField] private AudioClip[] m_FootstepSounds;    //从中随机选择的一组足迹声音。
        [SerializeField] private AudioClip m_JumpSound;           //角色离开地面时发出的声音。
        [SerializeField] private AudioClip m_LandSound;           //角色触地时播放的声音。

        private Camera m_Camera;
        private bool m_Jump;
        private float m_YRotation;
        private Vector2 m_Input;
        private Vector3 m_MoveDir = Vector3.zero;
        private CharacterController m_CharacterController;
        private CollisionFlags m_CollisionFlags;
        private bool m_PreviouslyGrounded;
        private Vector3 m_OriginalCameraPosition;
        private float m_StepCycle;
        private float m_NextStep;
        private bool m_Jumping;
        private AudioSource m_AudioSource;

        private void Start()
        {
            m_CharacterController = GetComponent<CharacterController>();
            m_Camera = Camera.main;
            m_OriginalCameraPosition = m_Camera.transform.localPosition;
            m_FovKick.Setup(m_Camera);
            m_HeadBob.Setup(m_Camera, m_StepInterval);
            m_StepCycle = 0f;
            m_NextStep = m_StepCycle/2f;
            m_Jumping = false;
            m_AudioSource = GetComponent<AudioSource>();
			m_MouseLook.Init(transform , m_Camera.transform);
        }

        private void Update()
        {
            RotateView();
            // 跳转状态需要在这里读取,以确保它不会丢失
            if (!m_Jump)
            {
                m_Jump = CrossPlatformInputManager.GetButtonDown("Jump");
            }

            if (!m_PreviouslyGrounded && m_CharacterController.isGrounded)
            {
                StartCoroutine(m_JumpBob.DoBobCycle());
                PlayLandingSound();
                m_MoveDir.y = 0f;
                m_Jumping = false;
            }
            if (!m_CharacterController.isGrounded && !m_Jumping && m_PreviouslyGrounded)
            {
                m_MoveDir.y = 0f;
            }
            m_PreviouslyGrounded = m_CharacterController.isGrounded;
        }
        private void PlayLandingSound()
        {
            m_AudioSource.clip = m_LandSound;
            m_AudioSource.Play();
            m_NextStep = m_StepCycle + .5f;
        }
        private void FixedUpdate()
        {
            float speed;
            GetInput(out speed);
            //始终沿着相机向前移动,因为这是它瞄准的方向
            Vector3 desiredMove = transform.forward*m_Input.y + transform.right*m_Input.x;

            //获取被接触曲面的法线以沿其移动
            RaycastHit hitInfo;
            Physics.SphereCast(transform.position, m_CharacterController.radius, Vector3.down, out hitInfo,
                               m_CharacterController.height/2f, Physics.AllLayers, QueryTriggerInteraction.Ignore);
            desiredMove = Vector3.ProjectOnPlane(desiredMove, hitInfo.normal).normalized;

            m_MoveDir.x = desiredMove.x*speed;
            m_MoveDir.z = desiredMove.z*speed;


            if (m_CharacterController.isGrounded)
            {
                m_MoveDir.y = -m_StickToGroundForce;

                if (m_Jump)
                {
                    m_MoveDir.y = m_JumpSpeed;
                    PlayJumpSound();
                    m_Jump = false;
                    m_Jumping = true;
                }
            }
            else
            {
                m_MoveDir += Physics.gravity*m_GravityMultiplier*Time.fixedDeltaTime;
            }
            m_CollisionFlags = m_CharacterController.Move(m_MoveDir*Time.fixedDeltaTime);

            ProgressStepCycle(speed);
            UpdateCameraPosition(speed);

            m_MouseLook.UpdateCursorLock();
        }
        private void PlayJumpSound()
        {
            m_AudioSource.clip = m_JumpSound;
            m_AudioSource.Play();
        }
        private void ProgressStepCycle(float speed)
        {
            if (m_CharacterController.velocity.sqrMagnitude > 0 && (m_Input.x != 0 || m_Input.y != 0))
            {
                m_StepCycle += (m_CharacterController.velocity.magnitude + (speed*(m_IsWalking ? 1f : m_RunstepLenghten)))*
                             Time.fixedDeltaTime;
            }
            if (!(m_StepCycle > m_NextStep))
            {
                return;
            }
            m_NextStep = m_StepCycle + m_StepInterval;

            PlayFootStepAudio();
        }

        private void PlayFootStepAudio()
        {
            if (!m_CharacterController.isGrounded)
            {
                return;
            }
            // 从数组中拾取并播放随机的足迹声音,不包括索引0处的声音
            int n = Random.Range(1, m_FootstepSounds.Length);
            m_AudioSource.clip = m_FootstepSounds[n];
            m_AudioSource.PlayOneShot(m_AudioSource.clip);
            // 将拾取的声音移动到索引0,以便下次不再拾取
            m_FootstepSounds[n] = m_FootstepSounds[0];
            m_FootstepSounds[0] = m_AudioSource.clip;
        }

        private void UpdateCameraPosition(float speed)
        {
            Vector3 newCameraPosition;
            if (!m_UseHeadBob)
            {
                return;
            }
            if (m_CharacterController.velocity.magnitude > 0 && m_CharacterController.isGrounded)
            {
                m_Camera.transform.localPosition =
                    m_HeadBob.DoHeadBob(m_CharacterController.velocity.magnitude +
                                      (speed*(m_IsWalking ? 1f : m_RunstepLenghten)));
                newCameraPosition = m_Camera.transform.localPosition;
                newCameraPosition.y = m_Camera.transform.localPosition.y - m_JumpBob.Offset();
            }
            else
            {
                newCameraPosition = m_Camera.transform.localPosition;
                newCameraPosition.y = m_OriginalCameraPosition.y - m_JumpBob.Offset();
            }
            m_Camera.transform.localPosition = newCameraPosition;
        }


        private void GetInput(out float speed)
        {
            // Read input
            float horizontal = CrossPlatformInputManager.GetAxis("Horizontal");
            float vertical = CrossPlatformInputManager.GetAxis("Vertical");

            bool waswalking = m_IsWalking;

#if !MOBILE_INPUT
            // 在独立构建中,步行/跑步速度通过按键进行修改。
            //跟踪角色是在行走还是在跑步
                        m_IsWalking = !Input.GetKey(KeyCode.LeftShift);
#endif
            // set the desired speed to be walking or running
            speed = m_IsWalking ? m_WalkSpeed : m_RunSpeed;
            m_Input = new Vector2(horizontal, vertical);

            // 如果组合长度超过1,则规范化输入:
            if (m_Input.sqrMagnitude > 1)
            {
                m_Input.Normalize();
            }

            // 只有当运动员准备跑步、正在跑步并且要使用fovkick时,处理速度变化以提供fovkick
            if (m_IsWalking != waswalking && m_UseFovKick && m_CharacterController.velocity.sqrMagnitude > 0)
            {
                StopAllCoroutines();
                StartCoroutine(!m_IsWalking ? m_FovKick.FOVKickUp() : m_FovKick.FOVKickDown());
            }
        }


        private void RotateView()
        {
            m_MouseLook.LookRotation (transform, m_Camera.transform);
        }


        private void OnControllerColliderHit(ControllerColliderHit hit)
        {
            Rigidbody body = hit.collider.attachedRigidbody;
            //如果角色在刚体上,不要移动刚体
            if (m_CollisionFlags == CollisionFlags.Below)
            {
                return;
            }

            if (body == null || body.isKinematic)
            {
                return;
            }
            body.AddForceAtPosition(m_CharacterController.velocity*0.1f, hit.point, ForceMode.Impulse);
        }
    }
}


🎈枪械开火、换弹、换枪动画设置

  • 小Y:角色的基本移动和视角切换有了,那接下来就是对枪械进行动画设置了,接下来是重点,你可要好好听哦!
  • 小云儿👩:那必须的,小Y哥哥说的话我都要认真听!

在这个资源包中已经有基本的动画片段了,如下图,我们要做的就是使用Animation Controller将动画片段控制起来
在这里插入图片描述

拿MP5举例,看一下动画片段
开火

换子弹

动画控制值Animator配置

这里我们先来给神器AK47(阿卡47)来进行动画配置
点开这个
Animation Controller
就是这个默认界面
在这里插入图片描述

  • 然后将AK47的动画片段直接拖进去,分别是默认动画、开火动画和换弹动画
  • 然后新建两个Trigger类型的过度参数,因为我们的开火和换弹都是点击触发的,类似开关,所以这里选择使用Trigger,其实使用Bool也可以。这里只是提供一方法。
    如下图:
    在这里插入图片描述
  • 然后将过度参数,分别添加到对应的箭头上
  • 还要注意将过度参数从Normal向其他两个动画过度时,将这个Has Exit Time 参数取消勾选,上篇文章动画基础也介绍过了
  • 还没有理解的可以去上一篇文章看一下哦,把链接摆在这了,嘿嘿
    Unity零基础到进阶 ☀️| 近万字教程 对 Unity 中的 动画系统基础 全面解析+实战演练

在这里插入图片描述

  • 这里一同样的方式来给MP5喷子设置上动画控制器,方法都是一样的

  • 先添加动画过度参数,然后点击小箭头,将动画过度的条件添加上,Has Exit Time 参数配置好
    在这里插入图片描述

  • 看一下过度的实际动画过度效果,这里可能不够明显,因为两个动画间并没有很大的差异:
    在这里插入图片描述

  • 小云儿👩:小Y哥哥,这样动画控制器就配置好啦,接下来就是使用代码和实现动画的调用了对叭!

  • 小Y:没错,因为动画片段都是现成的,我们只需要把这个动画控制器 Animation Controller做好就可以了,是不是很简单呢~

  • 小云儿👩:动画很流畅,就是这个开火换弹太慢了,我想枪射的更快一点,有什么办法解决吗?

  • 小Y:Emma…有办法的!简单给你说一下,但是射太快也有缺点哦,就是子弹浪费的太快了哈哈~

我们在这个动画窗口中选择对应的动画状态,然后右面的属性面板中有一个Speed属性

从名字就可以知道他是控制动画的播放速度的,所以我们在这里将速度的值改的越大,它播放的速度就越快,那我们的换弹速度就会更快啦!
在这里插入图片描述


🎉枪械开火、换弹、换枪脚本实现

  • 小Y:这里要进行代码编写控制动画了,你可要好好听哦!
  • 小云儿👩:好咧,小Y哥哥~ 我会做认真听讲的好孩子的!

其实这一块也很简单
我们首先在FPSController下新建一个GameObject,改名为:GunMansger
然后将AK47MP5喷子这三把枪的预制体拖到这个GunMansger下,并根据摄像机视角调整好视角。

我们这里只演示这三把枪,其实再多也一样,就是体力活啦~
如下图所示:
在这里插入图片描述
将三把枪的位置和视角都调整好之后,就开始上代码了

  • 首先 ,写一个挂在每个枪械上的脚本FPSGun,用于控制开火和换枪,包括动画和声音的播放
    代码很简单,先在Awake中找到挂载该脚本的游戏对象身上的Animator组件

  • OnEnable中播放readyClip音频,readyClip是切换到当前枪械时的声音
    然后定义了三个方法,分别是FireReloadSwitch,自然是开火、换弹和换枪的方法

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FPSGun : MonoBehaviour
{
    private Animator animator;

    public AudioClip fireClip;
    public AudioClip reloadClip;
    public AudioClip readyClip;

    private void Awake()
    {
        animator = GetComponent<Animator>();
    }
    private void OnEnable()
    {
        //播放准备声音
        AudioSource.PlayClipAtPoint(readyClip,transform.position);
    }

    public void Fire()
    {
        //将开火名称转换为哈希
        int fire = Animator.StringToHash("Fire");
        //播放开火动画
        animator.SetTrigger(fire);
        //播放声音
        AudioSource.PlayClipAtPoint(fireClip ,transform.position);
    }
    public void Reload()
    {
        int reload = Animator.StringToHash("Reload");
        animator.SetTrigger(reload);
        //播放声音
        AudioSource.PlayClipAtPoint(reloadClip, transform.position);

    }
    public void Switch()
    {
        //播放声音
        AudioSource.PlayClipAtPoint(readyClip, transform.position);

    }
}

将对应的枪械声音添加到各自枪械身上:

20210615224723469.png#pic_center20210615224735617.png#pic_center20210615224745439.png#pic_center

然后是管理枪支的脚本GunManger,这个脚本挂在我们刚创建的游戏对象GunMansger身上

在这个脚本中,我们要做的事情也很简单
就是管理枪械的切换和控制开火换弹
FPSGun 脚本中我们之定义了方法,并没与调用,所以正是这个脚本中调用了

  1. 首先,在该脚本中声明一个列表List,用于管理枪械:private List managerdGuns;
  2. 然后再声明一个FPSGun类,用于拿到当前手持的枪械:private FPSGun currentGun;
  3. AwakeNew一个FPSGun列表
    然后在Start中写一个简单的For循环,将我们之前在游戏对象GunMansger下添加的三把枪全添加到列表中去
    并且在列表大于0的情况下,将第一把枪赋值给我们定义的currentGun
  4. 最后在Update中进行判断当前枪械不为空的情况下可以进行开火、换弹和换枪!

这里有个小细节就是一定要在Awake中创建列表,然后再Start中将枪械添加上去
因为AwakeStart都是在程序运行的第一帧运行,但是AwakeStart执行的早!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GunManger : MonoBehaviour
{
    //管理枪支
    private List<FPSGun> managerdGuns;
    //当前的枪支
    private FPSGun currentGun;
    //枪支编号
    private int index=0;

    private void Awake()
    {
        managerdGuns = new List<FPSGun>();
    }
    private void Start()
    {
        for (int i = 0; i < transform.childCount; i++)
        {
            //添加每一把枪
            managerdGuns.Add(transform.GetChild(i).GetComponent<FPSGun>());
        }
        if (managerdGuns.Count>0)
        {
            //当前先使用的第一把枪
            currentGun = managerdGuns[index];
        }
    }
    private void Update()
    {
        //当前是否有一把枪
        if (!currentGun)
        {
            return;
        }
        if (Input.GetButtonDown("Fire"))
        {
            currentGun.Fire();//开火
        }
        if (Input.GetButtonDown("Reload"))
        {
            currentGun.Reload();//换单
        }
        if (Input.GetButtonDown("Switch"))
        {
            currentGun.Switch();//换枪
            currentGun.gameObject.SetActive(false);

            //方法二
            //计算新枪的编号
            index = ++index % managerdGuns.Count;
            currentGun = managerdGuns[index];//更新当前枪支
            currentGun.gameObject.SetActive(true);//新枪
        }
    }
}
  • 这里要注意一下,我在脚本中写的这三个键盘输入还需要在Unity中设置一下:Edit -> Project Setting ->Input
  • 如下图设置,这样我们按下Q键就是换枪,R键就是换弹,鼠标左键就是开火,同步游戏设置~
    在这里插入图片描述
  • 小Y:好啦,一个控制枪械开火、换弹、换枪的脚本就算实现啦,是不是很简单呢!
  • 小云儿👩:Emma…不是很难,但是好像也并没有小Y哥哥你说的这么简单哦~
  • 小Y:哈哈,其实真的很简单啦,只是你还不熟练而已,多练几次就好啦!
  • 小云儿👩:好咧小Y哥哥
  • 小Y:还有最后一个开火特效小模块没有解决,我来给你演示一般怎么简单添加的添加一个特效!

⭐️枪械开火特效设置

让我们再来添加一个简单的开火特效吧!

  • 在资源包中有一个叫MuzzleFlash的预设体,他是一个简单的材质搭配了Shader做的,我们这里直接拿来用就好了

  • 添加一个特效也有很多种方法,我们这里采用一个超级简单的办法

  • 就是直接将这个预制体拖到枪械上,并摆放好合适的位置

如下图所示,直接放枪口处即可:
在这里插入图片描述

或者更夸张一点就下图这样,随便怎么高兴怎么设计,这就是自己开发游戏的快乐吧!
在这里插入图片描述

  • 然后我们在FPSGun脚本中添加两个方法,分别控制播放特效和取消特效:
   /// <summary>
    /// 播放开火特效
    /// </summary>
    public void PlayerFireEffect()
    {
        //启动特效
        muzzleFlash.SetActive(true);
        //0.2秒关闭
        Invoke("UnEffect", 0.1f);
    }
    /// <summary>
    /// 取消特效
    /// </summary>
    private void UnEffect()
    {
        muzzleFlash.SetActive(false);
    }
  • 然后新建一个脚本PlayerAudioAndEffect,这个脚本有点特殊,因为他不是挂在游戏对象身上的,而是挂在动画片段身上的,创建完脚本后会直接继承StateMachineBehaviour,然后会有几个override重写方法,我们只需要在OnStateEnter方法中使用即可
    在这里插入图片描述

  • 创建方法:要在Animator视窗中选中动画片段,然后Add Behaviour上去
    在这里插入图片描述

代码也很简单,获取到当前的动画,然后在播放动画的时候展示特效即可,声音也可以在这里设置:

using UnityEngine;

public class PlayerAudioAndEffect : StateMachineBehaviour
{
    public bool fire = true;
    public bool reload = true;

    // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
    override public void OnStateEnter(Animator animator, AnimatorStateInfo stateinfo, int layerindex)
    {
        FPSGun currentGun = animator.GetComponent<FPSGun>();
        if (fire)
        {
            //获取开火声音
            AudioClip fireClip = currentGun.fireClip;
            //播放
            AudioSource.PlayClipAtPoint(fireClip, animator.transform.position);
            //播放特效
            currentGun.PlayerFireEffect();

        }
        if (reload)
        {
            //获取换单声音
            AudioClip reloadClip = currentGun.reloadClip;
            //播放
            AudioSource.PlayClipAtPoint(reloadClip, animator.transform.position);
        }
    }
}

✨游戏展示

小Y:每当进行到这一步,就是我们最激动人心的时刻啦!
小云儿👩:是的呢 小Y哥哥,快来给我演示一下成果吧~
小Y:好咧!

动图演示

  • 因为有些小伙伴懒得看视频演示,所以准备了动图演示,由于只能上传5M以内的图片,我可是煞费苦心剪辑出来的动图呀
    在这里插入图片描述
    视频演示:

自制第一人称射击游戏简单游戏视频

  • 小Y:因为时间和素材的原因,这篇文章只是把一个射击游戏的基本功能实现了,还有些地方不够完善。
  • 小云儿👩:是的呢,小Y哥哥~ 不过这些已经是将关键性的功能都实现啦,已经够我学的了!
  • 小Y:好咧,那这次就学到这里吧。我在文中提到的每一个步骤和细节都要仔细看哦,漏掉了一步可能就会出Bug啦!
  • 小云儿👩:那下次再见啦小Y哥哥,我这就回去自己动手做一遍!

文中没有做的地方还有挺多,比如一把枪的子弹限制,还有近战武器,投掷物和地图等等。

这些设置起来也都是比较麻烦的,但是作为一个第一人称射击游戏来说,本文中已经把最基础重要的功能给实现啦。

如果你想开发一款属于自己的射击游戏,那本文章可以作为一个启蒙Demo哦,可以让你知道原来网上一些很火的游戏我们也可以自己动手做!

Demo的工程资源包在这里可以下载,感兴趣的小伙伴赶紧去试一下吧!

如果觉得文章还不错,可以三连支持一下博主哦,加油!
在这里插入图片描述


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK