瞄具的装配——Unity随手记(2021.3.1)

今天实现的内容:

瞄具在游戏物体上的装配

这是我们的瞄具
在这里插入图片描述
将其硬装到枪械上,注意调整tag和layer。我知道AK上安装需要皮卡迪尼导轨才能安装的瞄具很不现实,这里作为一种演示。
在这里插入图片描述

配件物品

类似枪械物品,继承自BaseItem的AccessoryItem类是所有枪械配件物品的类。

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

namespace Scripts.Items
{
    
    
    // 枪械配件物品类
    public class AccessoryItem : BaseItem
    {
    
    
        // 配件类型
        public enum AccessoryType
        {
    
    
            Scope, //瞄准镜
            BarrelParts, //枪管配件
            Other //以后会添加的类型
        }

        // 当前物品的配件类型
        public AccessoryType currentItemAccessoryItem;
    }
}

将配件的模型单独拿出来做成游戏对象,挂上脚本,接下来制作捡起配件的功能。
在这里插入图片描述
在这里插入图片描述

逻辑实现

首先,我们先实现一个Scope类,将其挂载到枪上的Scope上,这个类描述了瞄具的参数。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Scripts.Items;

namespace Scripts.Weapon
{
    
    
    // 瞄具的信息
    [System.Serializable]
    public class Scope : MonoBehaviour
    {
    
    
        // 瞄具的名称
        public string scopeName;
        // 主摄像机的瞄准时FOV
        public float eyeCameraFOV;
        // 枪摄像机的瞄准时FOV
        public float gunCameraFOV;
        // 枪摄像机瞄准时的位置 用于适应瞄具的变化
        public Vector3 gunCameraPosition;

    }
}

在这里插入图片描述

和捡枪类似的,写一个PickUpAccessory方法,在PickupItem检测到Item为AccessoryItem 时,调用这个方法。

            // 如果BaseItem实际是AccessoryItem 将他转换为AccessoryItem
            if (temp_baseItem is AccessoryItem temp_accessoryItem)
            {
    
    
                PickUpAccessory(temp_accessoryItem);
            }

在PickUpAccessory中,进一步分析该AccessoryItem的类型,如果是Scope,调用PickUpScope方法。

// 捡起配件 其实就是将武器设置为可以使用
    private void PickUpAccessory(AccessoryItem _targetAccessoryItem)
    {
    
    
        // 会根据不同类型的配件 调用对应的方法
        switch (_targetAccessoryItem.currentItemAccessoryItem)
        {
    
    
            case AccessoryItem.AccessoryType.Scope:
                PickUpScope(_targetAccessoryItem.itemName, scopes);
                break;
            case AccessoryItem.AccessoryType.BarrelParts:
                PickUpBarrelPart(_targetAccessoryItem.itemName, barrelParts);
                break;
            default:
                break;
        }
    }

PickUpScope方法,将所有的瞄具隐藏,再将捡到的瞄具设置上来。也就是Active设置为true。同时调用m_currentWeapon.SetupCurrentScope来将瞄具的参数实际运用到摄像机。

private void PickUpScope(string _scopeName, List<Scope> _scopes)
    {
    
    
        // 如果已经找到要设置的瞄具了 就不再执行名称查找
        bool is_founded = false;
        // 对于所有的瞄具
        foreach (var temp_scope in _scopes)
        {
    
    
            // 如果之前设置了瞄具,将其设置为false
            temp_scope.gameObject.SetActive(false);
            // 找到Item在枪械GameObject上的对应瞄具
            if (!is_founded && _scopeName.CompareTo(temp_scope.scopeName) == 0)
            {
    
    
                temp_scope.gameObject.SetActive(true);
                m_currentWeapon.SetupCurrentScope(temp_scope);
            }
        }
    }

瞄具显示出来了,接下来在脚本中控制gunCamera的FOV和位置,以及eyeCamera的FOV来实现瞄具的放大效果。这里我们要新增一些成员,作为两台摄像机的控制参数。

        // 枪摄像机的Transform 用于修改位置以适配不同的瞄准镜
        protected Transform gunCameraTransform;
        // 枪摄像机没有瞄准时的位置
        protected Vector3 originalGunCameraLocalPosition;
        // 枪摄像机使用机瞄瞄准时的位置
        protected Vector3 originalGunCameraTargetLocalPosition = Vector3.zero;
        // 枪摄像机瞄准时的位置
        protected Vector3 gunCameraTargetLocalPosition;
        // 没有瞄准时的摄像机FOV
        protected float originEyeCameraFOV;
        protected float originGunCameraFOV;
        // 瞄准镜的GameObject列表
        public List<Scope> scopes;
        // 当前装备的瞄准镜 
        // 为null说明当前没有装备瞄准镜 使用默认的机瞄
        protected Scope currentAssembledScope;
        
        // 瞄准时的目标FOV
        protected float eyeCameraTargetFOV;
        protected float gunCameraTargetFOV;
        // 枪械使用机瞄瞄准时的目标FOV
        [Tooltip("枪械使用机瞄瞄准时的目标FOV")]
        public float originEyeCameraTargetFOV = 48f;
        [Tooltip("枪械使用机瞄瞄准时的目标FOV")]
        public float originGunCameraTargetFOV = 40f;

接下来,定义新方法SetupCurrentScope,该方法在捡起瞄具时调用,将瞄具的参数设置到脚本,进而控制两台摄像机。

        // 设置瞄准镜及倍径
        internal void SetupCurrentScope(Scope _scope)
        {
    
    
            currentAssembledScope = _scope;
            // 当前没有安装瞄具 按照默认机瞄的参数来
            if (_scope == null)
            {
    
    
                gunCameraTargetLocalPosition = originalGunCameraTargetLocalPosition;
                eyeCameraTargetFOV = originEyeCameraTargetFOV;
                gunCameraTargetFOV = originGunCameraTargetFOV;
            }
            else //否则说明安装了瞄具 按照瞄具的参数来
            {
    
    
                gunCameraTargetLocalPosition = _scope.gunCameraPosition;
                eyeCameraTargetFOV = _scope.eyeCameraFOV;
                gunCameraTargetFOV = _scope.gunCameraFOV;
            }
        }

修改后的DoAim,将控制两台摄像机的FOV和gunCamera的localPosition。

// 定义瞄准时需要做的 枪械瞄准时摄像机FOV都会放大
        protected IEnumerator DoAim()
        {
    
    
            float temp_currentEyeCameraFOV = 0;
            float temp_currentGunCameraFOV = 0;
            Vector3 temp_currentGunCameraLocalPosition = new Vector3();
            while (true)
            {
    
    
                yield return null;

                // 处理瞄准时视野放大功能
                eyeCamera.fieldOfView =
                       Mathf.SmoothDamp(eyeCamera.fieldOfView,
                       m_isAiming ? eyeCameraTargetFOV : originEyeCameraFOV,
                       ref temp_currentEyeCameraFOV,
                       Time.deltaTime * 10f);
                gunCamera.fieldOfView =
                       Mathf.SmoothDamp(gunCamera.fieldOfView,
                       m_isAiming ? gunCameraTargetFOV : originGunCameraFOV,
                       ref temp_currentGunCameraFOV,
                       Time.deltaTime * 10f);
                // 修改摄像机位置
                gunCameraTransform.localPosition =
                    Vector3.SmoothDamp(gunCameraTransform.localPosition,
                       m_isAiming ? gunCameraTargetLocalPosition : originalGunCameraLocalPosition,
                       ref temp_currentGunCameraLocalPosition,
                       Time.deltaTime * 10f);
                if (Mathf.Abs(eyeCamera.fieldOfView - eyeCameraTargetFOV) < 0.001f)
                {
    
    
                    eyeCamera.fieldOfView = eyeCameraTargetFOV;
                    yield break;
                }
            }
        }

卸下瞄具

暂时没有制作该功能,其实丢枪都做了,卸下瞄具在代码逻辑上还不简单?相对于代码逻辑,卸下瞄具在游戏设计上反而是困难的。我能想到的在游戏进行时能卸瞄具的游戏是孤岛危机,其它游戏要么不能卸瞄具,要么只能在一个UI界面里面卸瞄具。其实说起来安装瞄具也是这样的。


BUG以及缺陷:

我相信更好的办法是美工多干活,制作出挂好了配件的不同模型(像COD),或者使用skinned mesh。而不是将瞄具直接硬上到枪上。
在安装上瞄具以后,我发现我的枪的瞄具并不能完全反应子弹落点,我的子弹是真正的从枪口射出来的,而不是从摄像机射出来的。


值得注意的:

对于枪口配件,我们甚至可以用类似的办法。


猜你喜欢

转载自blog.csdn.net/qq_37856544/article/details/114266766