文章目录
今天实现的内容:
瞄具在游戏物体上的装配
这是我们的瞄具
将其硬装到枪械上,注意调整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;
}
}
}
卸下瞄具
暂时没有制作该功能,其实丢枪都做了,卸下瞄具还不简单?
BUG以及缺陷:
我相信更好的办法是美工多干活,制作出挂好了配件的不同模型(像COD),或者使用skinned mesh。而不是将瞄具直接硬上到枪上。
在安装上瞄具以后,我发现我的枪的瞄具并不能完全反应子弹落点,我的子弹是真正的从枪口射出来的,而不是从摄像机射出来的。
值得注意的:
对于枪口配件,我们甚至可以用类似的办法。