Add ultimate xr
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ea572aaf7320f74ea8c4a8fba20559e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,93 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="RotateShoulder.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.CyborgAvatar
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that rotates the Cyborg shoulder so that the opening points in the arm direction to leave it
|
||||
/// more space.
|
||||
/// </summary>
|
||||
public class RotateShoulder : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private Transform _rotatingShoulder;
|
||||
[SerializeField] private Vector3 _rotatingShoulderAxis;
|
||||
[SerializeField] private Vector3 _rotatingShoulderOpeningAxis;
|
||||
[SerializeField] private Transform _arm;
|
||||
[SerializeField] private Vector3 _armLocalForward;
|
||||
[SerializeField] private float _rotationDampingMin = 1.0f;
|
||||
[SerializeField] private float _rotationDampingMax = 0.2f;
|
||||
[SerializeField] private float _armAngleToRotateMin = 30.0f;
|
||||
[SerializeField] private float _armAngleToRotateMax = 60.0f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to events.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
UxrManager.AvatarsUpdated += UxrManager_AvatarsUpdated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from events.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
UxrManager.AvatarsUpdated -= UxrManager_AvatarsUpdated;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Performs the shoulder rotation.
|
||||
/// </summary>
|
||||
private void UxrManager_AvatarsUpdated()
|
||||
{
|
||||
Vector3 armForward = _arm.TransformDirection(_armLocalForward);
|
||||
Vector3 rotatingShoulderAxis = _rotatingShoulder.TransformDirection(_rotatingShoulderAxis);
|
||||
|
||||
float armAngle = Vector3.Angle(armForward, rotatingShoulderAxis);
|
||||
|
||||
if (armAngle > _armAngleToRotateMin)
|
||||
{
|
||||
float t = Mathf.Clamp01((armAngle - _armAngleToRotateMin) / (_armAngleToRotateMax - _armAngleToRotateMin));
|
||||
|
||||
Vector3 openingCurrent = _rotatingShoulder.TransformDirection(_rotatingShoulderOpeningAxis);
|
||||
Vector3 openingTarget = Vector3.ProjectOnPlane(armForward, rotatingShoulderAxis);
|
||||
float currentAngle = Vector3.SignedAngle(openingCurrent, openingTarget, rotatingShoulderAxis);
|
||||
float dampedAngle = Mathf.SmoothDampAngle(currentAngle, 0.0f, ref _currentAngleVelocity, Mathf.Lerp(_rotationDampingMin, _rotationDampingMax, t));
|
||||
|
||||
_rotatingShoulder.Rotate(_rotatingShoulderAxis, currentAngle - dampedAngle, Space.Self);
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentAngleVelocity = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private float _currentAngleVelocity;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66858c08b0019284683fe834e616d2d2
|
||||
timeCreated: 1548833695
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,75 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="WristConnectionRays.RayProperties.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.CyborgAvatar
|
||||
{
|
||||
public partial class WristConnectionRays
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Stores the properties of a connection ray.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
private class RayProperties
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] [ColorUsage(true, true)] private Color _color;
|
||||
[SerializeField] private float _thickness;
|
||||
[SerializeField] private float _offset;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ray thickness.
|
||||
/// </summary>
|
||||
public float Thickness => _thickness;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ray offset.
|
||||
/// </summary>
|
||||
public float Offset => _offset;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ray color.
|
||||
/// </summary>
|
||||
public Color Color
|
||||
{
|
||||
get => _color;
|
||||
set => _color = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the GameObject created at runtime for the ray.
|
||||
/// </summary>
|
||||
public GameObject GameObject { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the line renderer component created at runtime for the ray.
|
||||
/// </summary>
|
||||
public LineRenderer LineRenderer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the offset in the 2d section of the ray direction.
|
||||
/// </summary>
|
||||
public Vector2 OffsetXY { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start color.
|
||||
/// </summary>
|
||||
public Color StartColor { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d398127f73748839a21126772e4539b
|
||||
timeCreated: 1643732645
|
||||
@@ -0,0 +1,194 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="WristConnectionRays.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.CyborgAvatar
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that drives the two devices that connect the Cyborg wrist to the arm.
|
||||
/// </summary>
|
||||
public partial class WristConnectionRays : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private float _gradientPosStart1 = 0.15f;
|
||||
[SerializeField] private float _gradientPosStart2 = 0.2f;
|
||||
[SerializeField] private float _gradientPosEnd1 = 0.8f;
|
||||
[SerializeField] private float _gradientPosEnd2 = 0.85f;
|
||||
[SerializeField] private Material _rayMaterial;
|
||||
[SerializeField] private bool _useMaterialNoiseParameters;
|
||||
[SerializeField] private Transform _src;
|
||||
[SerializeField] private Transform _dst;
|
||||
[SerializeField] private List<RayProperties> _rays;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to avatar update event.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
UxrManager.AvatarsUpdated += UxrManager_AvatarsUpdated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from avatar update events.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
UxrManager.AvatarsUpdated -= UxrManager_AvatarsUpdated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
Create(_src.position, _dst.position);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Updates the component.
|
||||
/// </summary>
|
||||
private void UxrManager_AvatarsUpdated()
|
||||
{
|
||||
if (_src != null && _dst != null)
|
||||
{
|
||||
UpdateRays(_src.position, _dst.position);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Creates the connections.
|
||||
/// </summary>
|
||||
/// <param name="src">Source position</param>
|
||||
/// <param name="dst">Destination position</param>
|
||||
private void Create(Vector3 src, Vector3 dst)
|
||||
{
|
||||
foreach (RayProperties ray in _rays)
|
||||
{
|
||||
ray.GameObject = new GameObject("Ray");
|
||||
ray.GameObject.transform.SetParent(transform, true);
|
||||
ray.GameObject.transform.localPosition = Vector3.zero;
|
||||
ray.GameObject.transform.localRotation = Quaternion.identity;
|
||||
|
||||
ray.LineRenderer = ray.GameObject.AddComponent<LineRenderer>();
|
||||
ray.LineRenderer.material = _rayMaterial;
|
||||
|
||||
if (_useMaterialNoiseParameters)
|
||||
{
|
||||
ray.LineRenderer.material.SetFloat(DistortTimeStartVarName, Random.value * 10000.0f);
|
||||
}
|
||||
|
||||
ray.LineRenderer.textureMode = LineTextureMode.Stretch;
|
||||
ray.OffsetXY = Random.insideUnitCircle;
|
||||
}
|
||||
|
||||
UpdateRays(src, dst);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the connection rays.
|
||||
/// </summary>
|
||||
/// <param name="src">Source position</param>
|
||||
/// <param name="dst">End position</param>
|
||||
private void UpdateRays(Vector3 src, Vector3 dst)
|
||||
{
|
||||
foreach (RayProperties ray in _rays)
|
||||
{
|
||||
if (ray.GameObject == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ray.GameObject.transform.position = src;
|
||||
ray.GameObject.transform.LookAt(dst);
|
||||
|
||||
float rayLength = Vector3.Distance(src, dst) / ray.LineRenderer.transform.lossyScale.z;
|
||||
|
||||
Vector3[] positions =
|
||||
{
|
||||
new Vector3(0.0f, 0.0f, 0.0f),
|
||||
new Vector3(0.0f, 0.0f, rayLength * _gradientPosStart1),
|
||||
new Vector3(0.0f, 0.0f, rayLength * _gradientPosStart2),
|
||||
new Vector3(0.0f, 0.0f, rayLength * _gradientPosEnd1),
|
||||
new Vector3(0.0f, 0.0f, rayLength * _gradientPosEnd2),
|
||||
new Vector3(0.0f, 0.0f, rayLength)
|
||||
};
|
||||
|
||||
Vector3 offset = (ray.GameObject.transform.right * ray.OffsetXY.x + ray.GameObject.transform.up * ray.OffsetXY.y).normalized * ray.Offset;
|
||||
|
||||
for (int pos = 0; pos < positions.Length; ++pos)
|
||||
{
|
||||
positions[pos] = ray.LineRenderer.transform.InverseTransformPoint(ray.GameObject.transform.TransformPoint(positions[pos]) + offset);
|
||||
}
|
||||
|
||||
ray.LineRenderer.useWorldSpace = false;
|
||||
ray.LineRenderer.positionCount = 6;
|
||||
ray.LineRenderer.SetPositions(positions);
|
||||
ray.LineRenderer.startWidth = ray.Thickness;
|
||||
ray.LineRenderer.endWidth = ray.Thickness;
|
||||
ray.LineRenderer.material.color = ray.Color;
|
||||
|
||||
if (ray.LineRenderer.material.mainTexture != null)
|
||||
{
|
||||
ray.LineRenderer.material.mainTextureScale = new Vector2(rayLength / ray.Thickness / (ray.LineRenderer.material.mainTexture.width / (float)ray.LineRenderer.material.mainTexture.height), 1.0f);
|
||||
}
|
||||
|
||||
Gradient colorGradient = new Gradient();
|
||||
|
||||
colorGradient.colorKeys = new[]
|
||||
{
|
||||
new GradientColorKey(Color.white, 0.0f),
|
||||
new GradientColorKey(Color.white, _gradientPosStart1),
|
||||
new GradientColorKey(Color.white, _gradientPosStart2),
|
||||
new GradientColorKey(Color.white, _gradientPosEnd1),
|
||||
new GradientColorKey(Color.white, _gradientPosEnd2),
|
||||
new GradientColorKey(Color.white, 1.0f)
|
||||
};
|
||||
|
||||
colorGradient.alphaKeys = new[]
|
||||
{
|
||||
new GradientAlphaKey(0.0f, 0.0f),
|
||||
new GradientAlphaKey(0.0f, _gradientPosStart1),
|
||||
new GradientAlphaKey(1.0f, _gradientPosStart2),
|
||||
new GradientAlphaKey(1.0f, _gradientPosEnd1),
|
||||
new GradientAlphaKey(0.0f, _gradientPosEnd2),
|
||||
new GradientAlphaKey(0.0f, 1.0f)
|
||||
};
|
||||
|
||||
ray.LineRenderer.colorGradient = colorGradient;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private readonly string DistortTimeStartVarName = "_DistortTimeStart";
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 956417d4411d10142ac02fcd9618b664
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Assets/UltimateXR/Runtime/Scripts/Mechanics/Weapons.meta
Normal file
3
Assets/UltimateXR/Runtime/Scripts/Mechanics/Weapons.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fc0a56be6f684a1db8ea8b54a0afd95d
|
||||
timeCreated: 1643904891
|
||||
226
Assets/UltimateXR/Runtime/Scripts/Mechanics/Weapons/UxrActor.cs
Normal file
226
Assets/UltimateXR/Runtime/Scripts/Mechanics/Weapons/UxrActor.cs
Normal file
@@ -0,0 +1,226 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrActor.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// An actor in the Weapons module is an entity that can inflict and/or take damage.
|
||||
/// </summary>
|
||||
public class UxrActor : UxrComponent<UxrActor>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private float _life;
|
||||
[SerializeField] private Animator _animator;
|
||||
[SerializeField] private string _takeDamageAnimationTriggerVarName;
|
||||
[SerializeField] private string _dieAnimationTriggerVarName;
|
||||
[SerializeField] private AudioClip _takeDamageAudioClip;
|
||||
[SerializeField] private AudioClip _dieAudioClip;
|
||||
[SerializeField] private float _destroyAfterDeadSeconds = -1.0f;
|
||||
[SerializeField] private bool _automaticDamageHandling = true;
|
||||
[SerializeField] private bool _automaticDeadHandling = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered right before the actor is about to receive damage.
|
||||
/// Setting <see cref="UxrDamageEventArgs.Cancel" /> will allow not to take the damage.
|
||||
/// </summary>
|
||||
public event EventHandler<UxrDamageEventArgs> DamageReceiving;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered right after the actor received damage.
|
||||
/// Setting <see cref="UxrDamageEventArgs.Cancel" /> is not supported, since the damage was already taken.
|
||||
/// </summary>
|
||||
public event EventHandler<UxrDamageEventArgs> DamageReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether damage should be handled automatically. Automatic damage handling will take care of computing
|
||||
/// the new life value when receiving damage.
|
||||
/// </summary>
|
||||
public bool AutomaticDamageHandling
|
||||
{
|
||||
get => _automaticDamageHandling;
|
||||
set => _automaticDamageHandling = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to handle death automatically when the actor's life reaches zero.
|
||||
/// </summary>
|
||||
public bool AutomaticDeadHandling
|
||||
{
|
||||
get => _automaticDeadHandling;
|
||||
set => _automaticDeadHandling = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the actor's life value.
|
||||
/// </summary>
|
||||
public float Life
|
||||
{
|
||||
get => _life;
|
||||
set => _life = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the actor is dead.
|
||||
/// </summary>
|
||||
public bool IsDead { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Makes the actor receive a damaging projectile impact.
|
||||
/// </summary>
|
||||
/// <param name="actorSource">Actor source of the projectile</param>
|
||||
/// <param name="raycastHit">Raycast that hit the actor</param>
|
||||
/// <param name="damage">Damage to be taken</param>
|
||||
public void ReceiveImpact(UxrActor actorSource, RaycastHit raycastHit, float damage)
|
||||
{
|
||||
OnReceiveDamage(new UxrDamageEventArgs(actorSource, this, raycastHit, damage, damage >= Life));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes the actor receive explosive damage.
|
||||
/// </summary>
|
||||
/// <param name="actorSource">Actor source of the projectile</param>
|
||||
/// <param name="position">Explosion source</param>
|
||||
/// <param name="damage">Damage to be taken</param>
|
||||
public void ReceiveExplosion(UxrActor actorSource, Vector3 position, float damage)
|
||||
{
|
||||
OnReceiveDamage(new UxrDamageEventArgs(actorSource, this, position, damage, damage >= Life));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes the actor receive generic damage.
|
||||
/// </summary>
|
||||
/// <param name="damage">Damage to be taken</param>
|
||||
public void ReceiveDamage(float damage)
|
||||
{
|
||||
OnReceiveDamage(new UxrDamageEventArgs(damage, damage >= Life));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces the actor to die after a certain amount of seconds.
|
||||
/// </summary>
|
||||
/// <param name="delaySeconds">Seconds to wait for the actor to die</param>
|
||||
public void Die(float delaySeconds)
|
||||
{
|
||||
Invoke(nameof(DieInternal), delaySeconds);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Makes sure the <see cref="UxrWeaponManager" /> singleton instance is available so that actors are registered."/>
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
UxrWeaponManager.Instance.Poke();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <summary>
|
||||
/// Handles receiving damage and calls the appropriate events.
|
||||
/// </summary>
|
||||
/// <param name="e">Damage event parameters</param>
|
||||
private void OnReceiveDamage(UxrDamageEventArgs e)
|
||||
{
|
||||
if (IsDead)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DamageReceiving?.Invoke(this, e);
|
||||
|
||||
if (!e.IsCanceled)
|
||||
{
|
||||
bool destroy = false;
|
||||
|
||||
if (_automaticDamageHandling)
|
||||
{
|
||||
_life -= e.Damage;
|
||||
}
|
||||
|
||||
if (_life <= 0.0f)
|
||||
{
|
||||
// Deadly damage
|
||||
|
||||
if (_automaticDeadHandling)
|
||||
{
|
||||
destroy = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsDead = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-deadly damage
|
||||
|
||||
if (_animator != null && string.IsNullOrEmpty(_takeDamageAnimationTriggerVarName) == false)
|
||||
{
|
||||
_animator.SetTrigger(_takeDamageAnimationTriggerVarName);
|
||||
}
|
||||
|
||||
if (_takeDamageAudioClip)
|
||||
{
|
||||
AudioSource.PlayClipAtPoint(_takeDamageAudioClip, transform.position);
|
||||
}
|
||||
}
|
||||
|
||||
DamageReceived?.Invoke(this, e);
|
||||
|
||||
if (destroy)
|
||||
{
|
||||
DieInternal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Makes the actor die.
|
||||
/// </summary>
|
||||
private void DieInternal()
|
||||
{
|
||||
Life = 0.0f;
|
||||
IsDead = true;
|
||||
|
||||
if (_animator != null && string.IsNullOrEmpty(_dieAnimationTriggerVarName) == false)
|
||||
{
|
||||
_animator.SetTrigger(_dieAnimationTriggerVarName);
|
||||
}
|
||||
|
||||
if (_dieAudioClip)
|
||||
{
|
||||
AudioSource.PlayClipAtPoint(_dieAudioClip, transform.position);
|
||||
}
|
||||
|
||||
Destroy(gameObject, _destroyAfterDeadSeconds > 0.0f ? _destroyAfterDeadSeconds : 0.0f);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 618f9ab40c56ca4438d3a53036e0d785
|
||||
timeCreated: 1504854550
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,127 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrDamageEventArgs.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Damage event parameters.
|
||||
/// </summary>
|
||||
public class UxrDamageEventArgs : EventArgs
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of damage.
|
||||
/// </summary>
|
||||
public UxrDamageType DamageType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actor that inflicted the damage, or null if the damage didn't come from any specific actor.
|
||||
/// </summary>
|
||||
public UxrActor ActorSource { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actor that received the damage.
|
||||
/// </summary>
|
||||
public UxrActor ActorTarget { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the raycast information for projectile hits. Only valid if <see cref="DamageType" /> is
|
||||
/// <see cref="UxrDamageType.ProjectileHit" />.
|
||||
/// </summary>
|
||||
public RaycastHit RaycastHit { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the source position for explosive damage. Only valid if <see cref="DamageType" /> is
|
||||
/// <see cref="UxrDamageType.Explosive" />
|
||||
/// </summary>
|
||||
public Vector3 ExplosionPosition { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of damage taken/inflicted.
|
||||
/// </summary>
|
||||
public float Damage { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the damage will result in the death of the receiving actor.
|
||||
/// </summary>
|
||||
public bool Dies { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the damage was canceled for damage pre-events. Damage post-events cannot be canceled since the damage was
|
||||
/// already inflicted.
|
||||
/// </summary>
|
||||
public bool IsCanceled { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for projectile damage.
|
||||
/// </summary>
|
||||
/// <param name="source">Source actor</param>
|
||||
/// <param name="target">Target actor</param>
|
||||
/// <param name="raycastHit">Raycast hit</param>
|
||||
/// <param name="damage">Damage amount</param>
|
||||
/// <param name="dies">Whether the damage results in death</param>
|
||||
public UxrDamageEventArgs(UxrActor source, UxrActor target, RaycastHit raycastHit, float damage, bool dies)
|
||||
{
|
||||
DamageType = UxrDamageType.ProjectileHit;
|
||||
ActorSource = source;
|
||||
ActorTarget = target;
|
||||
RaycastHit = raycastHit;
|
||||
Damage = damage;
|
||||
Dies = dies;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for explosive damage.
|
||||
/// </summary>
|
||||
/// <param name="source">Source actor or null if the damage didn't come from another actor</param>
|
||||
/// <param name="target">Target actor</param>
|
||||
/// <param name="explosionPosition">Explosion world position</param>
|
||||
/// <param name="damage">Damage amount</param>
|
||||
/// <param name="dies">Whether the damage results in death</param>
|
||||
public UxrDamageEventArgs(UxrActor source, UxrActor target, Vector3 explosionPosition, float damage, bool dies)
|
||||
{
|
||||
DamageType = UxrDamageType.Explosive;
|
||||
ActorSource = source;
|
||||
ActorTarget = target;
|
||||
ExplosionPosition = explosionPosition;
|
||||
Damage = damage;
|
||||
Dies = dies;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for generic damage.
|
||||
/// </summary>
|
||||
/// <param name="damage">Damage amount</param>
|
||||
/// <param name="dies">Whether the damage results in death</param>
|
||||
public UxrDamageEventArgs(float damage, bool dies)
|
||||
{
|
||||
DamageType = UxrDamageType.Other;
|
||||
Damage = damage;
|
||||
Dies = dies;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Allows pre-events to cancel the damage. post-events can not be cancelled since the damage was already taken.
|
||||
/// </summary>
|
||||
public void Cancel()
|
||||
{
|
||||
IsCanceled = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8987630d8e0d4546858455b2e5ef7e5
|
||||
timeCreated: 1535631921
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrDamageType.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different damage types that can be taken.
|
||||
/// </summary>
|
||||
public enum UxrDamageType
|
||||
{
|
||||
/// <summary>
|
||||
/// Damage due to projectile hit.
|
||||
/// </summary>
|
||||
ProjectileHit,
|
||||
|
||||
/// <summary>
|
||||
/// Damage due to explosion.
|
||||
/// </summary>
|
||||
Explosive,
|
||||
|
||||
/// <summary>
|
||||
/// Other types of damage (falls, elements from scenario that generate damage...).
|
||||
/// </summary>
|
||||
Other
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02fe82ff5ef54cbd9015b60d1eb34664
|
||||
timeCreated: 1642859942
|
||||
@@ -0,0 +1,52 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrDeflectEventArgs.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Projectile deflection event parameters.
|
||||
/// </summary>
|
||||
public class UxrDeflectEventArgs : EventArgs
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the projectile source.
|
||||
/// </summary>
|
||||
public UxrProjectileSource ProjectileSource { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the raycast that was used to detect the collision.
|
||||
/// </summary>
|
||||
public RaycastHit RaycastHit { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new projectile direction after being deflected.
|
||||
/// </summary>
|
||||
public Vector3 NewDirection { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="projectileSource">The projectile source</param>
|
||||
/// <param name="raycastHit">The raycast used to detect projectile collision</param>
|
||||
/// <param name="newDirection">The new, deflected, projectile direction</param>
|
||||
public UxrDeflectEventArgs(UxrProjectileSource projectileSource, RaycastHit raycastHit, Vector3 newDirection)
|
||||
{
|
||||
ProjectileSource = projectileSource;
|
||||
RaycastHit = raycastHit;
|
||||
NewDirection = newDirection;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12118d803c3748b4b907fa4778d35e9e
|
||||
timeCreated: 1642860517
|
||||
@@ -0,0 +1,278 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrExplodeHierarchy.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Animation.GameObjects;
|
||||
using UltimateXR.Audio;
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that allows to explode a GameObject and all its rigidbody children.
|
||||
/// If the component is attached to a GameObject that also has a <see cref="UxrActor" /> component the explosion will
|
||||
/// be triggered when the actor dies.
|
||||
/// The explosion can also be called explicitly using <see cref="Explode" /> and <see cref="ExplodeNow" />.
|
||||
/// </summary>
|
||||
public class UxrExplodeHierarchy : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private UxrAudioSample[] _audioExplodePool;
|
||||
[SerializeField] private float _minExplodeSpeed = 5.0f;
|
||||
[SerializeField] private float _maxExplodeSpeed = 20.0f;
|
||||
[SerializeField] private float _minExplodeAngularSpeed = 1.0f;
|
||||
[SerializeField] private float _maxExplodeAngularSpeed = 8.0f;
|
||||
[SerializeField] private float _secondsToExplode = -1.0f;
|
||||
[SerializeField] private float _piecesLifeSeconds = 5.0f;
|
||||
[SerializeField] private float _piecesFadeoutSeconds = 1.0f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the explode timer. A negative value will either indicate that it is not using any timer (if
|
||||
/// <see cref="HasExploded" /> is false) or that it already exploded (if <see cref="HasExploded" /> is true).
|
||||
/// </summary>
|
||||
public float ExplodeTimer { get; private set; } = -1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the object has exploded.
|
||||
/// </summary>
|
||||
public bool HasExploded { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum random speed that the chunks will have when the object explodes.
|
||||
/// </summary>
|
||||
public float MinExplodeSpeed
|
||||
{
|
||||
get => _minExplodeSpeed;
|
||||
set => _minExplodeSpeed = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum random speed that the chunks will have when the object explodes.
|
||||
/// </summary>
|
||||
public float MaxExplodeSpeed
|
||||
{
|
||||
get => _maxExplodeSpeed;
|
||||
set => _maxExplodeSpeed = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum random angular speed that the chunks will have when the object explodes.
|
||||
/// </summary>
|
||||
public float MinExplodeAngularSpeed
|
||||
{
|
||||
get => _minExplodeAngularSpeed;
|
||||
set => _minExplodeAngularSpeed = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum random angular speed that the chunks will have when the object explodes.
|
||||
/// </summary>
|
||||
public float MaxExplodeAngularSpeed
|
||||
{
|
||||
get => _maxExplodeAngularSpeed;
|
||||
set => _maxExplodeAngularSpeed = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the seconds it will take for the object to explode after the component is enabled. A negative value
|
||||
/// will disable the user of a timer and will only explode when the object was added to an <see cref="UxrActor" /> that
|
||||
/// dies.
|
||||
/// </summary>
|
||||
public float SecondsToExplode
|
||||
{
|
||||
get => _secondsToExplode;
|
||||
set
|
||||
{
|
||||
ExplodeTimer = value;
|
||||
_secondsToExplode = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the seconds it will take for the chunks to disappear after the object explodes.
|
||||
/// </summary>
|
||||
public float PiecesLifeSeconds
|
||||
{
|
||||
get => _piecesLifeSeconds;
|
||||
set => _piecesLifeSeconds = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the seconds it will take for the chunks to fade-out when the chunks disappear after
|
||||
/// <see cref="PiecesLifeSeconds" />.
|
||||
/// </summary>
|
||||
public float PiecesFadeoutSeconds
|
||||
{
|
||||
get => _piecesFadeoutSeconds;
|
||||
set => _piecesFadeoutSeconds = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Explodes an object.
|
||||
/// </summary>
|
||||
/// <param name="root">Root object to explode</param>
|
||||
/// <param name="minExplodeVelocity">Minimum random velocity assigned to the chunks</param>
|
||||
/// <param name="maxExplodeVelocity">Maximum random velocity assigned to the chunks</param>
|
||||
/// <param name="secondsToExplode">Seconds to wait before exploding</param>
|
||||
/// <param name="piecesLifeSeconds">Life in seconds to assign to the chunks, after which they will be destroyed</param>
|
||||
public static void Explode(GameObject root, float minExplodeVelocity, float maxExplodeVelocity, float secondsToExplode, float piecesLifeSeconds)
|
||||
{
|
||||
UxrExplodeHierarchy explodeHierarchy = root.AddComponent<UxrExplodeHierarchy>();
|
||||
|
||||
explodeHierarchy.MinExplodeSpeed = minExplodeVelocity;
|
||||
explodeHierarchy.MaxExplodeSpeed = maxExplodeVelocity;
|
||||
explodeHierarchy.SecondsToExplode = secondsToExplode;
|
||||
explodeHierarchy.PiecesLifeSeconds = piecesLifeSeconds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explodes an object immediately using the current parameters.
|
||||
/// </summary>
|
||||
public void ExplodeNow()
|
||||
{
|
||||
if (HasExploded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (Collider chunkCollider in _colliders)
|
||||
{
|
||||
chunkCollider.enabled = true;
|
||||
chunkCollider.transform.SetParent(null);
|
||||
|
||||
if (!chunkCollider.gameObject.TryGetComponent<Rigidbody>(out var rigidBodyChunk))
|
||||
{
|
||||
rigidBodyChunk = chunkCollider.gameObject.AddComponent<Rigidbody>();
|
||||
}
|
||||
|
||||
rigidBodyChunk.isKinematic = false;
|
||||
rigidBodyChunk.velocity = Random.insideUnitSphere * Random.Range(MinExplodeSpeed, MaxExplodeSpeed);
|
||||
rigidBodyChunk.angularVelocity = Random.insideUnitSphere * Random.Range(MinExplodeAngularSpeed, MaxExplodeAngularSpeed);
|
||||
|
||||
float startAlpha = 1.0f;
|
||||
Renderer renderer = chunkCollider.gameObject.GetComponent<Renderer>();
|
||||
|
||||
if (renderer)
|
||||
{
|
||||
startAlpha = renderer.material.color.a;
|
||||
}
|
||||
|
||||
UxrObjectFade.Fade(chunkCollider.gameObject, startAlpha, 0.0f, PiecesLifeSeconds - PiecesFadeoutSeconds, PiecesFadeoutSeconds);
|
||||
Destroy(rigidBodyChunk.gameObject, PiecesLifeSeconds);
|
||||
}
|
||||
|
||||
HasExploded = true;
|
||||
ExplodeTimer = -1.0f;
|
||||
|
||||
if (_audioExplodePool != null)
|
||||
{
|
||||
int i = Random.Range(0, _audioExplodePool.Length);
|
||||
_audioExplodePool[i].Play(transform.position);
|
||||
}
|
||||
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
_colliders = gameObject.GetComponentsInChildren<Collider>(true);
|
||||
HasExploded = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to events and initializes the explosion timer.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
if (!HasExploded)
|
||||
{
|
||||
ExplodeTimer = SecondsToExplode;
|
||||
}
|
||||
|
||||
UxrActor actor = GetCachedComponent<UxrActor>();
|
||||
|
||||
if (actor)
|
||||
{
|
||||
actor.DamageReceived += Actor_Damaged;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from events.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
UxrActor actor = GetCachedComponent<UxrActor>();
|
||||
|
||||
if (actor)
|
||||
{
|
||||
actor.DamageReceived -= Actor_Damaged;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the explosion timer and checks if the object needs to explode.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (ExplodeTimer >= 0.0f && !HasExploded)
|
||||
{
|
||||
ExplodeTimer -= Time.deltaTime;
|
||||
|
||||
if (ExplodeTimer < 0.0f)
|
||||
{
|
||||
ExplodeNow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called when the component was added to an object with an <see cref="UxrActor" /> component and it took damage.
|
||||
/// </summary>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void Actor_Damaged(object sender, UxrDamageEventArgs e)
|
||||
{
|
||||
if (e.Dies)
|
||||
{
|
||||
ExplodeNow();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private Collider[] _colliders;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d47748b13f81fbe419af1d5c560c07a4
|
||||
timeCreated: 1505383405
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,99 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrFirearmAmmoLabel.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that draws the ammo left in a firearm magazine.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(UxrFirearmWeapon))]
|
||||
public class UxrFirearmAmmoLabel : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private Text _textTarget;
|
||||
[SerializeField] private int _triggerIndex;
|
||||
[SerializeField] private bool _showCapacity = true;
|
||||
[SerializeField] private int _digits = 2;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
_firearm = GetComponent<UxrFirearmWeapon>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to events.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
UxrManager.AvatarsUpdated += UxrManager_AvatarsUpdated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from events.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
UxrManager.AvatarsUpdated -= UxrManager_AvatarsUpdated;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called after all avatars were updated. Updates the ammo information.
|
||||
/// </summary>
|
||||
private void UxrManager_AvatarsUpdated()
|
||||
{
|
||||
if (!_textTarget || !_firearm)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string ammoRemaining = _firearm.HasMagAttached(_triggerIndex) ? _firearm.GetAmmoLeft(_triggerIndex).ToString() : string.Empty;
|
||||
string ammoCapacity = _firearm.HasMagAttached(_triggerIndex) ? _firearm.GetAmmoCapacity(_triggerIndex).ToString() : string.Empty;
|
||||
|
||||
if (_firearm.HasMagAttached(_triggerIndex))
|
||||
{
|
||||
ammoRemaining = ammoRemaining.PadLeft(_digits, '0');
|
||||
ammoCapacity = ammoCapacity.PadLeft(_digits, '0');
|
||||
}
|
||||
else
|
||||
{
|
||||
ammoRemaining = ammoRemaining.PadLeft(_digits, '-');
|
||||
ammoCapacity = ammoCapacity.PadLeft(_digits, '-');
|
||||
}
|
||||
|
||||
_textTarget.text = _showCapacity ? $"{ammoRemaining}/{ammoCapacity}" : ammoRemaining;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private UxrFirearmWeapon _firearm;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7bf7ee2c23f42ed4aafb6a70b591259b
|
||||
timeCreated: 1533114909
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,27 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrFirearmMag.StateSave.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.StateSave;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
public partial class UxrFirearmMag
|
||||
{
|
||||
#region Protected Overrides
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SerializeState(bool isReading, int stateSerializationVersion, UxrStateSaveLevel level, UxrStateSaveOptions options)
|
||||
{
|
||||
base.SerializeState(isReading, stateSerializationVersion, level, options);
|
||||
|
||||
if (level > UxrStateSaveLevel.ChangesSincePreviousSave)
|
||||
{
|
||||
SerializeStateValue(level, options, nameof(_rounds), ref _rounds);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e13b4d310cd347bbbbdc3ca7e92b7fd9
|
||||
timeCreated: 1706634432
|
||||
@@ -0,0 +1,53 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrFirearmMag.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Manipulation;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// A magazine that contains ammo for a <see cref="UxrFirearmWeapon" />. Magazines can be attached to firearms using
|
||||
/// <see cref="UxrGrabbableObject" /> functionality.
|
||||
/// </summary>
|
||||
public partial class UxrFirearmMag : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private int _rounds;
|
||||
[SerializeField] private int _capacity;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Total ammo capacity.
|
||||
/// </summary>
|
||||
public int Capacity => _capacity;
|
||||
|
||||
/// <summary>
|
||||
/// Remaining ammo.
|
||||
/// </summary>
|
||||
public int Rounds
|
||||
{
|
||||
get => Mathf.Clamp(_rounds, 0, _capacity);
|
||||
set
|
||||
{
|
||||
_rounds = Mathf.Clamp(value, 0, _capacity);
|
||||
RoundsChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event called whenever the number of rounds changed.
|
||||
/// </summary>
|
||||
public Action RoundsChanged;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 097a8eab230aadf40bdce734556f5482
|
||||
timeCreated: 1533114909
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,136 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrFirearmTrigger.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Audio;
|
||||
using UltimateXR.Core.Math;
|
||||
using UltimateXR.Haptics;
|
||||
using UltimateXR.Manipulation;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores all the information related to a trigger in a <see cref="UxrFirearmWeapon" />. The projectile that will be
|
||||
/// shot is described by a separate <see cref="UxrShotDescriptor" />. <see cref="ProjectileShotIndex" /> determines
|
||||
/// which <see cref="UxrShotDescriptor" /> in a <see cref="UxrProjectileSource" /> will be shot. It usually is 0, but
|
||||
/// can be higher if the projectile source supports multiple shot types.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
internal class UxrFirearmTrigger
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private int _projectileShotIndex;
|
||||
[SerializeField] private UxrShotCycle _cycleType;
|
||||
[SerializeField] private int _maxShotFrequency;
|
||||
[SerializeField] private UxrAudioSample _shotAudio;
|
||||
[SerializeField] private UxrAudioSample _shotAudioNoAmmo;
|
||||
[SerializeField] private UxrHapticClip _shotHapticClip = new UxrHapticClip(null, UxrHapticClipType.Shot);
|
||||
[SerializeField] private UxrGrabbableObject _triggerGrabbable;
|
||||
[SerializeField] private int _grabbableGrabPointIndex;
|
||||
[SerializeField] private Transform _triggerTransform;
|
||||
[SerializeField] private UxrAxis _triggerRotationAxis = UxrAxis.X;
|
||||
[SerializeField] private float _triggerRotationDegrees = 40.0f;
|
||||
[SerializeField] private UxrGrabbableObjectAnchor _ammunitionMagAnchor;
|
||||
[SerializeField] private float _recoilAngleOneHand = 0.5f;
|
||||
[SerializeField] private float _recoilAngleTwoHands = 2.0f;
|
||||
[SerializeField] private Vector3 _recoilOffsetOneHand = -Vector3.forward * 0.03f;
|
||||
[SerializeField] private Vector3 _recoilOffsetTwoHands = -Vector3.forward * 0.01f;
|
||||
[SerializeField] private float _recoilDurationSeconds;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index in the <see cref="UxrProjectileSource" /> component of the shot fired whenever the triggers is
|
||||
/// pressed.
|
||||
/// </summary>
|
||||
public int ProjectileShotIndex => _projectileShotIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the shot cycle type.
|
||||
/// </summary>
|
||||
public UxrShotCycle CycleType => _cycleType;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum shooting frequency.
|
||||
/// </summary>
|
||||
public int MaxShotFrequency => _maxShotFrequency;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the audio played when the user pulls the trigger and the weapon shoots.
|
||||
/// </summary>
|
||||
public UxrAudioSample ShotAudio => _shotAudio;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the audio played when the user pulls the trigger and the weapon isn't loaded.
|
||||
/// </summary>
|
||||
public UxrAudioSample ShotAudioNoAmmo => _shotAudioNoAmmo;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the haptic feedback sent whenever the weapon shoots.
|
||||
/// </summary>
|
||||
public UxrHapticClip ShotHapticClip => _shotHapticClip;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object that is required to grab in order to access the trigger.
|
||||
/// </summary>
|
||||
public UxrGrabbableObject TriggerGrabbable => _triggerGrabbable;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index point for <see cref="TriggerGrabbable" />.
|
||||
/// </summary>
|
||||
public int GrabbableGrabPointIndex => _grabbableGrabPointIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the transform that will rotate when the trigger is pressed.
|
||||
/// </summary>
|
||||
public Transform TriggerTransform => _triggerTransform;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the trigger rotation axis.
|
||||
/// </summary>
|
||||
public UxrAxis TriggerRotationAxis => _triggerRotationAxis;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of degrees that the trigger will rotate when it is fully pressed.
|
||||
/// </summary>
|
||||
public float TriggerRotationDegrees => _triggerRotationDegrees;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the anchor where mags for ammo that will be shot using the trigger will be attached to.
|
||||
/// </summary>
|
||||
public UxrGrabbableObjectAnchor AmmunitionMagAnchor => _ammunitionMagAnchor;
|
||||
|
||||
/// <summary>
|
||||
/// Recoil rotation in degrees when a single hand is grabbing the weapon.
|
||||
/// </summary>
|
||||
public float RecoilAngleOneHand => _recoilAngleOneHand;
|
||||
|
||||
/// <summary>
|
||||
/// Recoil rotation in degrees when two hands are grabbing the weapon.
|
||||
/// </summary>
|
||||
public float RecoilAngleTwoHands => _recoilAngleTwoHands;
|
||||
|
||||
/// <summary>
|
||||
/// Recoil offset when a single hand is grabbing the weapon.
|
||||
/// </summary>
|
||||
public Vector3 RecoilOffsetOneHand => _recoilOffsetOneHand;
|
||||
|
||||
/// <summary>
|
||||
/// Recoil offset when two hands are grabbing the weapon.
|
||||
/// </summary>
|
||||
public Vector3 RecoilOffsetTwoHands => _recoilOffsetTwoHands;
|
||||
|
||||
/// <summary>
|
||||
/// Recoil animation duration in seconds. The animation will be procedurally applied to the weapon.
|
||||
/// </summary>
|
||||
public float RecoilDurationSeconds => _recoilDurationSeconds;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5e96be893ae465a870923f59e5e626a
|
||||
timeCreated: 1643104910
|
||||
@@ -0,0 +1,195 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrFirearmWeapon.RuntimeTriggerInfo.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Core.Serialization;
|
||||
using UltimateXR.Core.StateSave;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
public partial class UxrFirearmWeapon
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
private class RuntimeTriggerInfo : IUxrSerializable, ICloneable
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the trigger is being pressed.
|
||||
/// </summary>
|
||||
public bool TriggerPressed
|
||||
{
|
||||
get => _triggerPressed;
|
||||
set => _triggerPressed = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the trigger just started being pressed.
|
||||
/// </summary>
|
||||
public bool TriggerPressStarted
|
||||
{
|
||||
get => _triggerPressStarted;
|
||||
set => _triggerPressStarted = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the trigger just finished being pressed.
|
||||
/// </summary>
|
||||
public bool TriggerPressEnded
|
||||
{
|
||||
get => _triggerPressEnded;
|
||||
set => _triggerPressEnded = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the decreasing timer in seconds that will reach zero when the firearm is ready to shoot again.
|
||||
/// </summary>
|
||||
public float LastShotTimer
|
||||
{
|
||||
get => _lastShotTimer;
|
||||
set => _lastShotTimer = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the weapon is currently loaded.
|
||||
/// </summary>
|
||||
public bool HasReloaded
|
||||
{
|
||||
get => _hasReloaded;
|
||||
set => _hasReloaded = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the trigger's initial local rotation.
|
||||
/// </summary>
|
||||
public Quaternion TriggerInitialLocalRotation
|
||||
{
|
||||
get => _triggerInitialLocalRotation;
|
||||
set => _triggerInitialLocalRotation = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the decreasing timer in seconds that will reach zero when the recoil animation finished.
|
||||
/// </summary>
|
||||
public float RecoilTimer
|
||||
{
|
||||
get => _recoilTimer;
|
||||
set => _recoilTimer = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implicit ICloneable
|
||||
|
||||
/// <summary>
|
||||
/// Clones the object. Helps <see cref="UxrStateSaveImplementer{T}" /> avoid using serialization to create a deep copy.
|
||||
/// </summary>
|
||||
/// <returns>Copy</returns>
|
||||
public object Clone()
|
||||
{
|
||||
RuntimeTriggerInfo copy = new RuntimeTriggerInfo();
|
||||
|
||||
copy._triggerPressed = _triggerPressed;
|
||||
copy._triggerPressStarted = _triggerPressStarted;
|
||||
copy._triggerPressEnded = _triggerPressEnded;
|
||||
copy._lastShotTimer = _lastShotTimer;
|
||||
copy._hasReloaded = _hasReloaded;
|
||||
copy._triggerInitialLocalRotation = _triggerInitialLocalRotation;
|
||||
copy._recoilTimer = _recoilTimer;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implicit IUxrSerializable
|
||||
|
||||
/// <inheritdoc />
|
||||
public int SerializationVersion => 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Serialize(IUxrSerializer serializer, int serializationVersion)
|
||||
{
|
||||
serializer.Serialize(ref _triggerPressed);
|
||||
serializer.Serialize(ref _triggerPressStarted);
|
||||
serializer.Serialize(ref _triggerPressEnded);
|
||||
serializer.Serialize(ref _lastShotTimer);
|
||||
serializer.Serialize(ref _hasReloaded);
|
||||
serializer.Serialize(ref _triggerInitialLocalRotation);
|
||||
serializer.Serialize(ref _recoilTimer);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Overrides object
|
||||
|
||||
/// <summary>
|
||||
/// Compares this object to another. Helps <see cref="UxrStateSaveImplementer{T}" /> compare to avoid unnecessary
|
||||
/// serialization.
|
||||
/// </summary>
|
||||
/// <param name="obj">Object to compare it to</param>
|
||||
/// <returns>Boolean telling whether the object is equal to <paramref name="obj" /></returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as RuntimeTriggerInfo);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
// Use XOR (^) to combine hash codes for booleans that are used in Equals().
|
||||
return _triggerPressed.GetHashCode() ^ _triggerPressStarted.GetHashCode() ^ _triggerPressEnded.GetHashCode() ^ _hasReloaded.GetHashCode();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Compares this object to another. Helps <see cref="UxrStateSaveImplementer{T}" /> compare to avoid unnecessary
|
||||
/// serialization.
|
||||
/// </summary>
|
||||
/// <param name="other">Object to compare it to</param>
|
||||
/// <returns>Boolean telling whether the object is equal to <paramref name="other" /></returns>
|
||||
public bool Equals(RuntimeTriggerInfo other)
|
||||
{
|
||||
if (ReferenceEquals(null, other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Check if we need to add the remaining fields in the comparison. We avoid it for now to avoid detecting unnecessary changes all the time.
|
||||
|
||||
return _triggerPressed == other._triggerPressed &&
|
||||
_triggerPressStarted == other._triggerPressStarted &&
|
||||
_triggerPressEnded == other._triggerPressEnded &&
|
||||
_hasReloaded == other._hasReloaded;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private bool _triggerPressed;
|
||||
private bool _triggerPressStarted;
|
||||
private bool _triggerPressEnded;
|
||||
private float _lastShotTimer;
|
||||
private bool _hasReloaded;
|
||||
private Quaternion _triggerInitialLocalRotation;
|
||||
private float _recoilTimer;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55cb3d8b82224584bb0674d4181f6048
|
||||
timeCreated: 1706006024
|
||||
@@ -0,0 +1,29 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrFirearmWeapon.StateSave.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.StateSave;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
public partial class UxrFirearmWeapon
|
||||
{
|
||||
#region Protected Overrides UxrComponent
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SerializeState(bool isReading, int stateSerializationVersion, UxrStateSaveLevel level, UxrStateSaveOptions options)
|
||||
{
|
||||
base.SerializeState(isReading, stateSerializationVersion, level, options);
|
||||
|
||||
// Logic is already handled through events, we don't serialize these parameters in incremental changes
|
||||
|
||||
if (level > UxrStateSaveLevel.ChangesSincePreviousSave)
|
||||
{
|
||||
SerializeStateValue(level, options, nameof(_runtimeTriggers), ref _runtimeTriggers);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 829cda3eb2b441ff87890a63f9e39e5a
|
||||
timeCreated: 1706004543
|
||||
@@ -0,0 +1,700 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrFirearmWeapon.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Devices;
|
||||
using UltimateXR.Manipulation;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of weapon that shoots projectiles. A firearm has one or more <see cref="UxrFirearmTrigger" /> entries. Each
|
||||
/// trigger allows to shoot a different type of projectile, and determines properties such as the shot cycle, shot
|
||||
/// frequency, ammunition, recoil and grabbing.
|
||||
/// A <see cref="UxrFirearmWeapon" /> requires a <see cref="UxrProjectileSource" /> component that defines the
|
||||
/// projectiles being shot. If a firearm has more than one trigger (for instance, a rifle that shoots bullets and has a
|
||||
/// grenade launcher), the <see cref="UxrProjectileSource" /> will require the same amount of entries in
|
||||
/// <see cref="UxrProjectileSource.ShotTypes" />.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(UxrProjectileSource))]
|
||||
public partial class UxrFirearmWeapon : UxrWeapon
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] protected Transform _recoilAxes;
|
||||
[SerializeField] private List<UxrFirearmTrigger> _triggers;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Event called right after the weapon shot a projectile using the given trigger index.
|
||||
/// </summary>
|
||||
public event Action<int> ProjectileShot;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a trigger is in a loaded state, meaning it is ready to shoot if pressed and there is any ammo left.
|
||||
/// </summary>
|
||||
/// <param name="triggerIndex">Index in <see cref="_triggers" /></param>
|
||||
/// <returns>Whether it is ready to shoot</returns>
|
||||
public bool IsLoaded(int triggerIndex)
|
||||
{
|
||||
if (_runtimeTriggers.TryGetValue(triggerIndex, out RuntimeTriggerInfo runtimeTrigger))
|
||||
{
|
||||
return runtimeTrigger.HasReloaded;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the given weapon trigger loaded state so that it is ready to shoot if there is ammo left.
|
||||
/// </summary>
|
||||
/// <param name="triggerIndex">Index in <see cref="_triggers" /></param>
|
||||
public void Reload(int triggerIndex)
|
||||
{
|
||||
if (!_runtimeTriggers.TryGetValue(triggerIndex, out RuntimeTriggerInfo runtimeTrigger))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BeginSync();
|
||||
runtimeTrigger.HasReloaded = true;
|
||||
EndSyncMethod(new object[] { triggerIndex });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether there is a magazine attached that fires shots using the given trigger. It may or may not have ammo.
|
||||
/// </summary>
|
||||
/// <param name="triggerIndex">Index in <see cref="_triggers" /></param>
|
||||
/// <returns>Whether there is a magazine attached</returns>
|
||||
public bool HasMagAttached(int triggerIndex)
|
||||
{
|
||||
if (triggerIndex < 0 || triggerIndex >= _triggers.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UxrFirearmTrigger trigger = _triggers[triggerIndex];
|
||||
return trigger.AmmunitionMagAnchor != null && trigger.AmmunitionMagAnchor.CurrentPlacedObject != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attached magazine maximum capacity.
|
||||
/// </summary>
|
||||
/// <param name="triggerIndex">Index in <see cref="_triggers" /></param>
|
||||
/// <returns>Maximum capacity of ammo in the attached magazine. If there isn't any magazine attached it returns 0</returns>
|
||||
public int GetAmmoCapacity(int triggerIndex)
|
||||
{
|
||||
if (triggerIndex < 0 || triggerIndex >= _triggers.Count)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
UxrFirearmTrigger trigger = _triggers[triggerIndex];
|
||||
|
||||
if (trigger.AmmunitionMagAnchor != null && trigger.AmmunitionMagAnchor.CurrentPlacedObject != null)
|
||||
{
|
||||
UxrFirearmMag mag = trigger.AmmunitionMagAnchor.CurrentPlacedObject.GetCachedComponent<UxrFirearmMag>();
|
||||
return mag != null ? mag.Capacity : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ammo left in the attached magazine.
|
||||
/// </summary>
|
||||
/// <param name="triggerIndex">Index in <see cref="_triggers" /></param>
|
||||
/// <returns>Ammo left in the attached magazine. If there isn't any magazine attached it returns 0</returns>
|
||||
public int GetAmmoLeft(int triggerIndex)
|
||||
{
|
||||
if (triggerIndex < 0 || triggerIndex >= _triggers.Count)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
UxrFirearmTrigger trigger = _triggers[triggerIndex];
|
||||
|
||||
if (trigger.AmmunitionMagAnchor != null)
|
||||
{
|
||||
if (trigger.AmmunitionMagAnchor.CurrentPlacedObject != null)
|
||||
{
|
||||
UxrFirearmMag mag = trigger.AmmunitionMagAnchor.CurrentPlacedObject.GetCachedComponent<UxrFirearmMag>();
|
||||
|
||||
return mag != null ? mag.Rounds : 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return int.MaxValue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the ammo left in the attached magazine.
|
||||
/// </summary>
|
||||
/// <param name="triggerIndex">Index in <see cref="_triggers" /></param>
|
||||
/// <param name="ammo">New ammo</param>
|
||||
public void SetAmmoLeft(int triggerIndex, int ammo)
|
||||
{
|
||||
if (triggerIndex < 0 || triggerIndex >= _triggers.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UxrFirearmTrigger trigger = _triggers[triggerIndex];
|
||||
|
||||
if (trigger.AmmunitionMagAnchor != null && trigger.AmmunitionMagAnchor.CurrentPlacedObject != null)
|
||||
{
|
||||
UxrFirearmMag mag = trigger.AmmunitionMagAnchor.CurrentPlacedObject.GetCachedComponent<UxrFirearmMag>();
|
||||
|
||||
if (mag != null)
|
||||
{
|
||||
mag.Rounds = ammo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the trigger pressed amount.
|
||||
/// </summary>
|
||||
/// <param name="triggerIndex">Index in <see cref="_triggers" /></param>
|
||||
/// <param name="amount">Pressed amount between range [0.0, 1.0]</param>
|
||||
public void SetTriggerPressedAmount(int triggerIndex, float amount)
|
||||
{
|
||||
if (triggerIndex < 0 || triggerIndex >= _triggers.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_runtimeTriggers.TryGetValue(triggerIndex, out RuntimeTriggerInfo runtimeTrigger))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UxrFirearmTrigger trigger = _triggers[triggerIndex];
|
||||
|
||||
if (trigger.TriggerTransform)
|
||||
{
|
||||
trigger.TriggerTransform.localRotation = runtimeTrigger.TriggerInitialLocalRotation * Quaternion.AngleAxis(trigger.TriggerRotationDegrees * amount, trigger.TriggerRotationAxis);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to shoot a round using the given trigger.
|
||||
/// </summary>
|
||||
/// <param name="triggerIndex">Index in <see cref="_triggers" /></param>
|
||||
/// <returns>
|
||||
/// Whether a round was shot. If no round was shot it can mean that:
|
||||
/// <list type="bullet">
|
||||
/// <item>The trigger index references an entry that doesn't exist.</item>
|
||||
/// <item>The firearm isn't loaded.</item>
|
||||
/// <item>The firearm doesn't have any ammo left or there is no magazine attached.</item>
|
||||
/// <item>The shoot frequency doesn't allow to shoot again so quickly.</item>
|
||||
/// </list>
|
||||
/// </returns>
|
||||
public bool TryToShootRound(int triggerIndex)
|
||||
{
|
||||
if (triggerIndex < 0 || triggerIndex >= _triggers.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_runtimeTriggers.TryGetValue(triggerIndex, out RuntimeTriggerInfo runtimeTrigger))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UxrFirearmTrigger trigger = _triggers[triggerIndex];
|
||||
|
||||
if (GetAmmoLeft(triggerIndex) > 0 && runtimeTrigger.LastShotTimer <= 0.0f)
|
||||
{
|
||||
SetAmmoLeft(triggerIndex, GetAmmoLeft(triggerIndex) - 1);
|
||||
|
||||
runtimeTrigger.LastShotTimer = trigger.MaxShotFrequency > 0 ? 1.0f / trigger.MaxShotFrequency : -1.0f;
|
||||
|
||||
// TODO: here we probably should add some randomization depending on recoil using the additional optional parameters
|
||||
_weaponSource.Shoot(trigger.ProjectileShotIndex);
|
||||
|
||||
runtimeTrigger.RecoilTimer = trigger.RecoilDurationSeconds;
|
||||
|
||||
// Audio
|
||||
|
||||
trigger.ShotAudio?.Play(_weaponSource.GetShotOrigin(trigger.ProjectileShotIndex));
|
||||
|
||||
// Raise events
|
||||
|
||||
OnProjectileShot(triggerIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
for (int i = 0; i < _triggers.Count; ++i)
|
||||
{
|
||||
if (!_runtimeTriggers.ContainsKey(i))
|
||||
{
|
||||
_runtimeTriggers.Add(i, new RuntimeTriggerInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to events.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
if (RootGrabbable)
|
||||
{
|
||||
RootGrabbable.ConstraintsApplied += RootGrabbable_ConstraintsApplied;
|
||||
}
|
||||
|
||||
UxrManager.AvatarsUpdated += UxrManager_AvatarsUpdated;
|
||||
|
||||
foreach (UxrFirearmTrigger trigger in _triggers)
|
||||
{
|
||||
if (trigger.TriggerGrabbable != null)
|
||||
{
|
||||
trigger.TriggerGrabbable.Released += Trigger_Released;
|
||||
trigger.TriggerGrabbable.Placed += Trigger_Placed;
|
||||
}
|
||||
|
||||
if (trigger.AmmunitionMagAnchor != null)
|
||||
{
|
||||
trigger.AmmunitionMagAnchor.Placed += MagTarget_Placed;
|
||||
trigger.AmmunitionMagAnchor.Removed += MagTarget_Removed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from events.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
if (RootGrabbable)
|
||||
{
|
||||
RootGrabbable.ConstraintsApplied -= RootGrabbable_ConstraintsApplied;
|
||||
}
|
||||
|
||||
UxrManager.AvatarsUpdated -= UxrManager_AvatarsUpdated;
|
||||
|
||||
foreach (UxrFirearmTrigger trigger in _triggers)
|
||||
{
|
||||
if (trigger.TriggerGrabbable != null)
|
||||
{
|
||||
trigger.TriggerGrabbable.Released -= Trigger_Released;
|
||||
trigger.TriggerGrabbable.Placed -= Trigger_Placed;
|
||||
}
|
||||
|
||||
if (trigger.AmmunitionMagAnchor != null)
|
||||
{
|
||||
trigger.AmmunitionMagAnchor.Placed -= MagTarget_Placed;
|
||||
trigger.AmmunitionMagAnchor.Removed -= MagTarget_Removed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
_weaponSource = GetCachedComponent<UxrProjectileSource>();
|
||||
|
||||
for (int i = 0; i < _triggers.Count; i++)
|
||||
{
|
||||
UxrFirearmTrigger trigger = _triggers[i];
|
||||
|
||||
if (_runtimeTriggers.TryGetValue(i, out RuntimeTriggerInfo info))
|
||||
{
|
||||
info.LastShotTimer = -1.0f;
|
||||
info.HasReloaded = true;
|
||||
|
||||
if (trigger.TriggerTransform)
|
||||
{
|
||||
info.TriggerInitialLocalRotation = trigger.TriggerTransform.localRotation;
|
||||
}
|
||||
}
|
||||
|
||||
if (trigger.AmmunitionMagAnchor != null)
|
||||
{
|
||||
if (trigger.AmmunitionMagAnchor.CurrentPlacedObject != null)
|
||||
{
|
||||
// Disable mag collider while it is attached
|
||||
|
||||
Collider magCollider = trigger.AmmunitionMagAnchor.CurrentPlacedObject.GetComponentInChildren<Collider>();
|
||||
|
||||
if (magCollider != null)
|
||||
{
|
||||
magCollider.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called when the grip of a grabbable object for a given trigger was released.
|
||||
/// </summary>
|
||||
/// <param name="sender">Sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void Trigger_Released(object sender, UxrManipulationEventArgs e)
|
||||
{
|
||||
if (e.Grabber != null && e.Grabber.Avatar.AvatarMode == UxrAvatarMode.Local)
|
||||
{
|
||||
SyncAmmoLeft(e.GrabbableObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the grip of a grabbable object for a given trigger was placed.
|
||||
/// </summary>
|
||||
/// <param name="sender">Sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void Trigger_Placed(object sender, UxrManipulationEventArgs e)
|
||||
{
|
||||
if (e.Grabber != null && e.Grabber.Avatar.AvatarMode == UxrAvatarMode.Local)
|
||||
{
|
||||
SyncAmmoLeft(e.GrabbableObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after the avatars have been updated. Updates the hand trigger blend value.
|
||||
/// </summary>
|
||||
private void UxrManager_AvatarsUpdated()
|
||||
{
|
||||
for (int i = 0; i < _triggers.Count; i++)
|
||||
{
|
||||
UxrFirearmTrigger trigger = _triggers[i];
|
||||
|
||||
if (!_runtimeTriggers.TryGetValue(i, out RuntimeTriggerInfo runtimeTrigger))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if we are grabbing the given grip using the local avatar
|
||||
|
||||
if (trigger.TriggerGrabbable && UxrGrabManager.Instance.GetGrabbingHand(trigger.TriggerGrabbable, trigger.GrabbableGrabPointIndex, out UxrGrabber grabber))
|
||||
{
|
||||
if (grabber.Avatar.AvatarMode == UxrAvatarMode.Local)
|
||||
{
|
||||
// Get the trigger press amount and use it to send it to the animation var that controls the hand trigger. Use it to rotate the trigger as well.
|
||||
|
||||
float triggerPressAmount = UxrAvatar.LocalAvatarInput.GetInput1D(grabber.Side, UxrInput1D.Trigger);
|
||||
trigger.TriggerGrabbable.GetGrabPoint(trigger.GrabbableGrabPointIndex).GetGripPoseInfo(grabber.Avatar).PoseBlendValue = triggerPressAmount;
|
||||
SetTriggerPressedAmount(i, triggerPressAmount);
|
||||
|
||||
// Now depending on the weapon type check if we need to shoot
|
||||
|
||||
SyncTriggerPressStates(i,
|
||||
UxrAvatar.LocalAvatarInput.GetButtonsPress(grabber.Side, UxrInputButtons.Trigger),
|
||||
UxrAvatar.LocalAvatarInput.GetButtonsPressDown(grabber.Side, UxrInputButtons.Trigger),
|
||||
UxrAvatar.LocalAvatarInput.GetButtonsPressUp(grabber.Side, UxrInputButtons.Trigger));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remote avatars will get the trigger pressed amount from the avatar pose blend amount, because poses are synchronized.
|
||||
SetTriggerPressedAmount(i, grabber.Avatar.GetCurrentHandPoseBlendValue(grabber.Side));
|
||||
}
|
||||
|
||||
bool shoot = false;
|
||||
|
||||
switch (trigger.CycleType)
|
||||
{
|
||||
case UxrShotCycle.ManualReload:
|
||||
{
|
||||
shoot = runtimeTrigger.TriggerPressStarted && runtimeTrigger.HasReloaded;
|
||||
|
||||
if (shoot)
|
||||
{
|
||||
runtimeTrigger.HasReloaded = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case UxrShotCycle.SemiAutomatic:
|
||||
|
||||
shoot = runtimeTrigger.TriggerPressStarted;
|
||||
break;
|
||||
|
||||
case UxrShotCycle.FullyAutomatic:
|
||||
|
||||
shoot = runtimeTrigger.TriggerPressed;
|
||||
break;
|
||||
}
|
||||
|
||||
if (runtimeTrigger.TriggerPressStarted && GetAmmoLeft(i) == 0)
|
||||
{
|
||||
trigger.ShotAudioNoAmmo?.Play(trigger.TriggerTransform != null ? trigger.TriggerTransform.position : trigger.TriggerGrabbable.GetGrabPointGrabProximityTransform(grabber, trigger.GrabbableGrabPointIndex).position);
|
||||
}
|
||||
|
||||
if (shoot)
|
||||
{
|
||||
// Shoot!
|
||||
|
||||
if (TryToShootRound(i))
|
||||
{
|
||||
if (grabber.Avatar.AvatarMode == UxrAvatarMode.Local)
|
||||
{
|
||||
// Send haptic to the hand grabbing the grip
|
||||
|
||||
UxrAvatar.LocalAvatarInput.SendHapticFeedback(grabber.Side, trigger.ShotHapticClip);
|
||||
|
||||
// Send haptic to the other hand if it is also grabbing the weapon
|
||||
|
||||
if (UxrGrabManager.Instance.IsHandGrabbing(grabber.Avatar, trigger.TriggerGrabbable, grabber.OppositeSide, true))
|
||||
{
|
||||
UxrAvatar.LocalAvatarInput.SendHapticFeedback(grabber.OppositeSide, trigger.ShotHapticClip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (grabber.Avatar.AvatarMode == UxrAvatarMode.Local && runtimeTrigger.TriggerPressEnded)
|
||||
{
|
||||
// Sync ammo after shooting a fully automatic weapon to make sure the ammo left is the same.
|
||||
if (trigger.CycleType == UxrShotCycle.FullyAutomatic)
|
||||
{
|
||||
SyncAmmoLeft(i, GetAmmoLeft(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runtimeTrigger.TriggerPressStarted = false;
|
||||
runtimeTrigger.TriggerPressEnded = false;
|
||||
|
||||
if (runtimeTrigger.LastShotTimer > 0.0f)
|
||||
{
|
||||
runtimeTrigger.LastShotTimer -= Time.deltaTime;
|
||||
}
|
||||
|
||||
if (runtimeTrigger.RecoilTimer > 0.0f)
|
||||
{
|
||||
runtimeTrigger.RecoilTimer -= Time.deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a mag is removed.
|
||||
/// </summary>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void MagTarget_Removed(object sender, UxrManipulationEventArgs e)
|
||||
{
|
||||
foreach (UxrFirearmTrigger trigger in _triggers)
|
||||
{
|
||||
if (e.GrabbableAnchor == trigger.AmmunitionMagAnchor)
|
||||
{
|
||||
Collider magCollider = e.GrabbableObject.GetComponentInChildren<Collider>();
|
||||
|
||||
if (magCollider != null)
|
||||
{
|
||||
magCollider.enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a mag is attached.
|
||||
/// </summary>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void MagTarget_Placed(object sender, UxrManipulationEventArgs e)
|
||||
{
|
||||
foreach (UxrFirearmTrigger trigger in _triggers)
|
||||
{
|
||||
if (e.GrabbableAnchor == trigger.AmmunitionMagAnchor)
|
||||
{
|
||||
Collider magCollider = e.GrabbableObject.GetComponentInChildren<Collider>();
|
||||
|
||||
if (magCollider != null)
|
||||
{
|
||||
magCollider.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called right after applying constraints to the main grabbable object. It is used to apply the recoil after the
|
||||
/// constraints to do it in the appropriate order.
|
||||
/// </summary>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void RootGrabbable_ConstraintsApplied(object sender, UxrApplyConstraintsEventArgs e)
|
||||
{
|
||||
// Get the grabbable object and apply recoil depending on the number of hands that are grabbing the gun
|
||||
|
||||
UxrGrabbableObject grabbableObject = sender as UxrGrabbableObject;
|
||||
Transform grabbableTransform = grabbableObject.transform;
|
||||
int grabbingHandCount = UxrGrabManager.Instance.GetGrabbingHandCount(grabbableObject);
|
||||
|
||||
for (int i = 0; i < _triggers.Count; i++)
|
||||
{
|
||||
UxrFirearmTrigger trigger = _triggers[i];
|
||||
|
||||
if (_runtimeTriggers.TryGetValue(i, out RuntimeTriggerInfo runtimeTrigger) && runtimeTrigger.RecoilTimer > 0.0f)
|
||||
{
|
||||
float recoilT = trigger.RecoilDurationSeconds > 0.0f ? (trigger.RecoilDurationSeconds - runtimeTrigger.RecoilTimer) / trigger.RecoilDurationSeconds : 0.0f;
|
||||
|
||||
Vector3 recoilRight = _recoilAxes != null ? _recoilAxes.right : grabbableTransform.right;
|
||||
Vector3 recoilUp = _recoilAxes != null ? _recoilAxes.up : grabbableTransform.up;
|
||||
Vector3 recoilForward = _recoilAxes != null ? _recoilAxes.forward : grabbableTransform.forward;
|
||||
Vector3 recoilPosition = _recoilAxes != null ? _recoilAxes.position : grabbableTransform.position;
|
||||
|
||||
float amplitude = 1.0f - recoilT;
|
||||
Vector3 recoilOffset = grabbingHandCount == 1 ? amplitude * trigger.RecoilOffsetOneHand : amplitude * trigger.RecoilOffsetTwoHands;
|
||||
|
||||
grabbableTransform.position += recoilRight * recoilOffset.x + recoilUp * recoilOffset.y + recoilForward * recoilOffset.z;
|
||||
grabbableTransform.RotateAround(recoilPosition, -recoilRight, grabbingHandCount == 1 ? amplitude * trigger.RecoilAngleOneHand : amplitude * trigger.RecoilAngleTwoHands);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="ProjectileShot" />.
|
||||
/// </summary>
|
||||
/// <param name="triggerIndex">The weapon trigger index</param>
|
||||
protected virtual void OnProjectileShot(int triggerIndex)
|
||||
{
|
||||
ProjectileShot?.Invoke(triggerIndex);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Sets the trigger pressed state, to sync multiplayer.
|
||||
/// </summary>
|
||||
/// <param name="triggerIndex">The trigger index</param>
|
||||
/// <param name="pressed">Whether the trigger is pressed</param>
|
||||
/// <param name="pressDown">Whether the trigger just started being pressed down</param>
|
||||
/// <param name="pressUp">Whether the trigger just started being released</param>
|
||||
private void SyncTriggerPressStates(int triggerIndex, bool pressed, bool pressDown, bool pressUp)
|
||||
{
|
||||
if (_runtimeTriggers.TryGetValue(triggerIndex, out RuntimeTriggerInfo runtimeTrigger))
|
||||
{
|
||||
if (runtimeTrigger.TriggerPressed != pressed || runtimeTrigger.TriggerPressStarted != pressDown || runtimeTrigger.TriggerPressEnded != pressUp)
|
||||
{
|
||||
BeginSync();
|
||||
|
||||
runtimeTrigger.TriggerPressed = pressed;
|
||||
runtimeTrigger.TriggerPressStarted = pressDown;
|
||||
runtimeTrigger.TriggerPressEnded = pressUp;
|
||||
|
||||
EndSyncMethod(new object[] { triggerIndex, pressed, pressDown, pressUp });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="SyncAmmoLeft(int, int)" />.
|
||||
/// </summary>
|
||||
/// <param name="grabbableTrigger">The grabbable object for the trigger</param>
|
||||
private void SyncAmmoLeft(UxrGrabbableObject grabbableTrigger)
|
||||
{
|
||||
UxrFirearmTrigger trigger = _triggers.FirstOrDefault(t => t.TriggerGrabbable == grabbableTrigger);
|
||||
|
||||
if (trigger != null && trigger.CycleType == UxrShotCycle.FullyAutomatic)
|
||||
{
|
||||
int index = _triggers.IndexOf(trigger);
|
||||
|
||||
if (index != -1)
|
||||
{
|
||||
SyncAmmoLeft(index, GetAmmoLeft(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the ammo left, to sync multiplayer after a fully automatic gun stopped firing.
|
||||
/// </summary>
|
||||
/// <param name="triggerIndex"> The trigger index</param>
|
||||
/// <param name="ammo">The ammo left</param>
|
||||
private void SyncAmmoLeft(int triggerIndex, int ammo)
|
||||
{
|
||||
BeginSync();
|
||||
|
||||
SetAmmoLeft(triggerIndex, ammo);
|
||||
|
||||
EndSyncMethod(new object[] { triggerIndex, ammo });
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the root grabbable object.
|
||||
/// </summary>
|
||||
private UxrGrabbableObject RootGrabbable
|
||||
{
|
||||
get
|
||||
{
|
||||
UxrGrabbableObject firstTriggerGrabbable = _triggers.Count > 0 && _triggers[0].TriggerGrabbable != null ? _triggers[0].TriggerGrabbable : null;
|
||||
|
||||
if (firstTriggerGrabbable)
|
||||
{
|
||||
// A normal setup will have just one grabbable point but for rifles and weapons with multiple parts we may have different grabbable objects.
|
||||
// We will just get the root one so that we can subscribe to its ApplyConstraints event to apply recoil effects
|
||||
|
||||
Transform weaponRootGrabbableTransform = firstTriggerGrabbable.UsesGrabbableParentDependency ? firstTriggerGrabbable.GrabbableParent.transform : firstTriggerGrabbable.transform;
|
||||
UxrGrabbableObject rootGrabbable = weaponRootGrabbableTransform.GetComponent<UxrGrabbableObject>();
|
||||
|
||||
return rootGrabbable;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private UxrProjectileSource _weaponSource;
|
||||
private Dictionary<int, RuntimeTriggerInfo> _runtimeTriggers = new Dictionary<int, RuntimeTriggerInfo>();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c1d9becc057b5a042859f71c7df57fdf
|
||||
timeCreated: 1530189801
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrenadeActivationMode.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different modes to activate a grenade.
|
||||
/// </summary>
|
||||
public enum UxrGrenadeActivationMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Grenade has no activation mode.
|
||||
/// </summary>
|
||||
NoActivation,
|
||||
|
||||
/// <summary>
|
||||
/// Grenade requires to remove a pin to activate a detonation timer.
|
||||
/// </summary>
|
||||
TriggerPin,
|
||||
|
||||
/// <summary>
|
||||
/// A detonator timer is started after being launched.
|
||||
/// </summary>
|
||||
OnHandLaunch,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 428ff67b3dd04f44a26ef93f0e0b456e
|
||||
timeCreated: 1643109873
|
||||
@@ -0,0 +1,313 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrGrenadeWeapon.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using UltimateXR.Audio;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Core.Caching;
|
||||
using UltimateXR.Haptics;
|
||||
using UltimateXR.Manipulation;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Grenade weapon. A grenade inflicts explosive damage to <see cref="UxrActor" /> components.
|
||||
/// </summary>
|
||||
public class UxrGrenadeWeapon : UxrWeapon, IUxrPrecacheable
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
// General parameters
|
||||
[SerializeField] private UxrGrenadeActivationMode _activationTrigger;
|
||||
[SerializeField] private bool _explodeOnCollision;
|
||||
|
||||
// Timer
|
||||
[SerializeField] private float _timerSeconds = 3.0f;
|
||||
|
||||
// Pin
|
||||
[SerializeField] private UxrGrabbableObject _pin;
|
||||
[SerializeField] private UxrAudioSample _audioRemovePin;
|
||||
[SerializeField] private UxrHapticClip _hapticRemovePin = new UxrHapticClip(null, UxrHapticClipType.Click);
|
||||
|
||||
// Impact
|
||||
[SerializeField] private LayerMask _impactExplosionCollisionMask = ~0;
|
||||
|
||||
// Explosion
|
||||
[SerializeField] private GameObject[] _explosionPrefabPool;
|
||||
[SerializeField] private float _explosionPrefabLife = 4.0f;
|
||||
|
||||
// Damage
|
||||
[SerializeField] private float _damageRadius = 10.0f;
|
||||
[SerializeField] private float _damageNear = 10.0f;
|
||||
[SerializeField] private float _damageFar;
|
||||
|
||||
// Physics
|
||||
[SerializeField] private bool _createPhysicsExplosion;
|
||||
[SerializeField] private float _physicsExplosionForce = 10.0f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the grenade has been activated and the detonation timer is running.
|
||||
/// </summary>
|
||||
public bool IsActivated => _timer >= 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the seconds left to explode. If the grenade hasn't been activated yet, it will return
|
||||
/// <see cref="TimerDuration" />.
|
||||
/// </summary>
|
||||
public float Timer => _timer < 0.0f ? _timerSeconds : _timer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the seconds it will take for the grenade to explode once it has been activated.
|
||||
/// </summary>
|
||||
public float TimerDuration => _timerSeconds;
|
||||
|
||||
/// <summary>
|
||||
/// Gets distance from the explosion the grenade will start inflicting damage.
|
||||
/// </summary>
|
||||
public float DamageRadius => _damageRadius;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum damage, applied at the very point of the explosion.
|
||||
/// </summary>
|
||||
public float DamageNear => _damageNear;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the minimum damage, applied at <see cref="DamageRadius" /> distance.
|
||||
/// </summary>
|
||||
public float DamageFar => _damageFar;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implicit IUxrPrecacheable
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<GameObject> PrecachedInstances => _explosionPrefabPool;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Starts the detonation timer.
|
||||
/// </summary>
|
||||
public void ActivateTimer()
|
||||
{
|
||||
_timer = _timerSeconds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Freezes or resumes the detonation timer.
|
||||
/// </summary>
|
||||
/// <param name="freeze">Whether to freeze or resume the detonation timer</param>
|
||||
public void FreezeTimer(bool freeze = true)
|
||||
{
|
||||
_timerFrozen = freeze;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores the detonation timer to the initial time.
|
||||
/// </summary>
|
||||
public void RestoreTimer()
|
||||
{
|
||||
_timer = _timerSeconds;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to events.
|
||||
/// </summary>
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
if (_pin)
|
||||
{
|
||||
_pin.Grabbed += Pin_Grabbed;
|
||||
}
|
||||
|
||||
if (_activationTrigger == UxrGrenadeActivationMode.OnHandLaunch)
|
||||
{
|
||||
if (GrabbableObject != null)
|
||||
{
|
||||
GrabbableObject.Released += Grenade_Released;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from events.
|
||||
/// </summary>
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
if (_pin)
|
||||
{
|
||||
_pin.Grabbed -= Pin_Grabbed;
|
||||
}
|
||||
|
||||
if (_activationTrigger == UxrGrenadeActivationMode.OnHandLaunch)
|
||||
{
|
||||
if (GrabbableObject != null)
|
||||
{
|
||||
GrabbableObject.Released -= Grenade_Released;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
if (_pin)
|
||||
{
|
||||
// Disable collider. We will enable it once the pin is grabbed.
|
||||
|
||||
Collider pinCollider = _pin.GetCachedComponent<Collider>();
|
||||
|
||||
if (pinCollider)
|
||||
{
|
||||
pinCollider.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the component.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (_timer > 0.0f && !_timerFrozen)
|
||||
{
|
||||
_timer -= Time.deltaTime;
|
||||
|
||||
if (_timer <= 0.0f)
|
||||
{
|
||||
Explode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by Unity when the physics-driven rigidbody collider hit something.
|
||||
/// </summary>
|
||||
/// <param name="collision">Collision object</param>
|
||||
private void OnCollisionEnter(Collision collision)
|
||||
{
|
||||
if (_explodeOnCollision && (_impactExplosionCollisionMask.value & 1 << collision.collider.gameObject.layer) != 0)
|
||||
{
|
||||
Explode();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called when the grenade was released.
|
||||
/// </summary>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void Grenade_Released(object sender, UxrManipulationEventArgs e)
|
||||
{
|
||||
if (e.IsGrabbedStateChanged && _activationTrigger == UxrGrenadeActivationMode.OnHandLaunch)
|
||||
{
|
||||
_timer = _timerSeconds;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the pin was grabbed. It will remove the pin from the grenade.
|
||||
/// </summary>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void Pin_Grabbed(object sender, UxrManipulationEventArgs e)
|
||||
{
|
||||
if (e.IsGrabbedStateChanged && _activationTrigger == UxrGrenadeActivationMode.TriggerPin && !IsActivated)
|
||||
{
|
||||
_timer = _timerSeconds;
|
||||
|
||||
Collider pinCollider = _pin.GetCachedComponent<Collider>();
|
||||
|
||||
if (pinCollider)
|
||||
{
|
||||
pinCollider.enabled = true;
|
||||
}
|
||||
|
||||
_audioRemovePin?.Play(transform.position);
|
||||
|
||||
// Send haptic to the hand that grabbed the pin
|
||||
|
||||
e.Grabber.Avatar.ControllerInput.SendGrabbableHapticFeedback(e.GrabbableObject, _hapticRemovePin);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Explodes the grenade, causing explosion and damage.
|
||||
/// </summary>
|
||||
private void Explode()
|
||||
{
|
||||
if (_exploded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_exploded = true;
|
||||
|
||||
if (_explosionPrefabPool.Length > 0)
|
||||
{
|
||||
GameObject newExplosion = Instantiate(_explosionPrefabPool[Random.Range(0, _explosionPrefabPool.Length)], transform.position, Quaternion.LookRotation(-UxrAvatar.LocalAvatar.CameraForward));
|
||||
|
||||
if (_explosionPrefabLife > 0.0f)
|
||||
{
|
||||
Destroy(newExplosion, _explosionPrefabLife);
|
||||
}
|
||||
}
|
||||
|
||||
if (_createPhysicsExplosion)
|
||||
{
|
||||
Collider[] colliders = Physics.OverlapSphere(transform.position, _damageRadius);
|
||||
|
||||
foreach (Collider targetCollider in colliders)
|
||||
{
|
||||
if (targetCollider.TryGetComponent<Rigidbody>(out var targetRigidbody))
|
||||
{
|
||||
targetRigidbody.AddExplosionForce(_physicsExplosionForce, transform.position, _damageRadius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UxrWeaponManager.Instance.ApplyRadiusDamage(Owner, transform.position, _damageRadius, _damageNear, _damageFar);
|
||||
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private float _timer = -1.0f;
|
||||
private bool _timerFrozen;
|
||||
private bool _exploded;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c2b9039ba07bb74e886b059d2d0ce7b
|
||||
timeCreated: 1533207471
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,165 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrImpactDecal.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using UltimateXR.Audio;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Caching;
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Extensions.Unity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that defines a decal generated as a result of the impact of a projectile.
|
||||
/// </summary>
|
||||
public class UxrImpactDecal : UxrComponent<UxrImpactDecal>, IUxrPrecacheable
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private bool _ignoreDynamicObjects;
|
||||
[SerializeField] private float _decalOffset = 0.005f;
|
||||
[SerializeField] private Renderer[] _decalRenderers;
|
||||
[SerializeField] private UxrAudioSample _audioImpact = new UxrAudioSample();
|
||||
[SerializeField] private bool _audioOnDynamicObjects = true;
|
||||
[SerializeField] private string _decalRendererColorName = UxrConstants.Shaders.StandardColorVarName;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implicit IUxrPrecacheable
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<GameObject> PrecachedInstances
|
||||
{
|
||||
get { yield return gameObject; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a given impact should generate a decal, and creates it if necessary.
|
||||
/// </summary>
|
||||
/// <param name="raycastHit">Impact raycast hit</param>
|
||||
/// <param name="checkLayerMask">Layer mask that should generate a decal</param>
|
||||
/// <param name="prefabDecal">The decal prefab to use when if the decal should be generated</param>
|
||||
/// <param name="lifeTime">New decal life time, after which it will fade out and be destroyed</param>
|
||||
/// <param name="fadeOutDurationSeconds">Decal fade out duration in seconds</param>
|
||||
/// <param name="createDoubleSidedDecal">Whether to also generate a secondary decal for the other side of the impact</param>
|
||||
/// <param name="doubleSidedDecalThickness">
|
||||
/// Surface thickness to consider when generating the secondary decal for the other
|
||||
/// side
|
||||
/// </param>
|
||||
/// <returns>New decal or null if no decal was generated</returns>
|
||||
public static UxrImpactDecal CheckCreateDecal(RaycastHit raycastHit,
|
||||
LayerMask checkLayerMask,
|
||||
UxrImpactDecal prefabDecal,
|
||||
float lifeTime,
|
||||
float fadeOutDurationSeconds,
|
||||
bool createDoubleSidedDecal = false,
|
||||
float doubleSidedDecalThickness = 0.001f)
|
||||
{
|
||||
if (prefabDecal != null && raycastHit.collider != null && prefabDecal._ignoreDynamicObjects && raycastHit.collider.gameObject.IsDynamic())
|
||||
{
|
||||
// Dynamic objects have been set up to not generate decals. Play impact audio only.
|
||||
|
||||
if (prefabDecal._audioImpact != null && prefabDecal._audioOnDynamicObjects)
|
||||
{
|
||||
prefabDecal._audioImpact.Play(raycastHit.point);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (prefabDecal != null && (checkLayerMask & 1 << raycastHit.collider.gameObject.layer) != 0)
|
||||
{
|
||||
UxrImpactDecal decal = Instantiate(prefabDecal, raycastHit.point + raycastHit.normal * prefabDecal._decalOffset, Quaternion.LookRotation(raycastHit.normal));
|
||||
decal.transform.parent = raycastHit.collider.transform;
|
||||
|
||||
if (lifeTime > 0.0f)
|
||||
{
|
||||
Destroy(decal.gameObject, lifeTime);
|
||||
}
|
||||
|
||||
decal._fadeOutDuration = fadeOutDurationSeconds;
|
||||
decal._fadeOutTimer = lifeTime;
|
||||
|
||||
if (createDoubleSidedDecal)
|
||||
{
|
||||
UxrImpactDecal decalDoubleSided = Instantiate(prefabDecal, raycastHit.point - raycastHit.normal * (prefabDecal._decalOffset + doubleSidedDecalThickness), Quaternion.LookRotation(-raycastHit.normal));
|
||||
decalDoubleSided.transform.parent = raycastHit.collider.transform;
|
||||
|
||||
if (lifeTime > 0.0f)
|
||||
{
|
||||
Destroy(decalDoubleSided.gameObject, lifeTime);
|
||||
}
|
||||
|
||||
decalDoubleSided._fadeOutDuration = fadeOutDurationSeconds;
|
||||
decalDoubleSided._fadeOutTimer = lifeTime;
|
||||
}
|
||||
|
||||
if (prefabDecal._audioImpact != null)
|
||||
{
|
||||
prefabDecal._audioImpact.Play(raycastHit.point);
|
||||
}
|
||||
|
||||
return decal;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
if (_decalRenderers != null)
|
||||
{
|
||||
foreach (Renderer decalRenderer in _decalRenderers)
|
||||
{
|
||||
_startColors.Add(decalRenderer, decalRenderer.sharedMaterial.HasProperty(_decalRendererColorName) ? decalRenderer.sharedMaterial.GetColor(_decalRendererColorName) : Color.white);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the component.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
_fadeOutTimer -= Time.deltaTime;
|
||||
|
||||
if (_fadeOutTimer < _fadeOutDuration)
|
||||
{
|
||||
foreach (Renderer decalRenderer in _decalRenderers)
|
||||
{
|
||||
Material material = decalRenderer.material;
|
||||
Color color = _startColors[decalRenderer];
|
||||
color.a = _startColors[decalRenderer].a * (_fadeOutTimer / _fadeOutDuration);
|
||||
material.SetColor(_decalRendererColorName, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private readonly Dictionary<Renderer, Color> _startColors = new Dictionary<Renderer, Color>();
|
||||
private float _fadeOutTimer;
|
||||
private float _fadeOutDuration;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d84dd744b1aa82b459278eda7f83ebf3
|
||||
timeCreated: 1504853692
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,82 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrMuzzleFlash.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Muzzle flash component for weapons that are firing shots.
|
||||
/// </summary>
|
||||
public class UxrMuzzleFlash : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private Material _material;
|
||||
[SerializeField] private int _textureColumns = 1;
|
||||
[SerializeField] private int _textureRows = 1;
|
||||
[SerializeField] private bool _randomizeAngle = true;
|
||||
[SerializeField] private float _minRandomizeScale = 1.0f;
|
||||
[SerializeField] private float _maxRandomizeScale = 1.0f;
|
||||
[SerializeField] private string _scaleOffsetVarName = UxrConstants.Shaders.StandardMainTextureScaleOffsetVarName;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
_meshRenderers = GetComponentsInChildren<MeshRenderer>();
|
||||
|
||||
if (_randomizeAngle)
|
||||
{
|
||||
transform.Rotate(Vector3.forward, Random.value * 360.0f, Space.Self);
|
||||
}
|
||||
|
||||
foreach (MeshRenderer meshRenderer in _meshRenderers)
|
||||
{
|
||||
int randomColumn = Random.Range(0, _textureColumns);
|
||||
int randomRow = Random.Range(0, _textureRows);
|
||||
|
||||
if (meshRenderer.sharedMaterial == _material)
|
||||
{
|
||||
Vector4 vecScaleOffset = meshRenderer.material.GetVector(_scaleOffsetVarName);
|
||||
|
||||
if (_textureColumns > 0)
|
||||
{
|
||||
vecScaleOffset.x = 1.0f / _textureColumns;
|
||||
vecScaleOffset.z = randomColumn * vecScaleOffset.x;
|
||||
}
|
||||
|
||||
if (_textureRows > 0)
|
||||
{
|
||||
vecScaleOffset.y = 1.0f / _textureRows;
|
||||
vecScaleOffset.w = randomRow * vecScaleOffset.y;
|
||||
}
|
||||
|
||||
meshRenderer.material.SetVector(_scaleOffsetVarName, vecScaleOffset);
|
||||
}
|
||||
}
|
||||
|
||||
float randomScale = Random.Range(_minRandomizeScale, _maxRandomizeScale);
|
||||
transform.localScale *= randomScale;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private MeshRenderer[] _meshRenderers;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: daa942b3dd922ad4ca6eada519005e7a
|
||||
timeCreated: 1532593431
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,53 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrNonDamagingImpactEventArgs.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Event parameters for projectile impacts that do not cause any damage to actors, such as impacts on the
|
||||
/// scenario or other elements.
|
||||
/// </summary>
|
||||
public class UxrNonDamagingImpactEventArgs : EventArgs
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actor that fired the shot.
|
||||
/// </summary>
|
||||
public UxrActor WeaponOwner { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The projectile source.
|
||||
/// </summary>
|
||||
public UxrProjectileSource ProjectileSource { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The raycast that detected the hit.
|
||||
/// </summary>
|
||||
public RaycastHit RaycastHit { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="weaponOwner">Owner of the weapon that fired the shot</param>
|
||||
/// <param name="projectileSource">Projectile source</param>
|
||||
/// <param name="raycastHit">Raycast that detected the hit</param>
|
||||
public UxrNonDamagingImpactEventArgs(UxrActor weaponOwner, UxrProjectileSource projectileSource, RaycastHit raycastHit)
|
||||
{
|
||||
WeaponOwner = weaponOwner;
|
||||
ProjectileSource = projectileSource;
|
||||
RaycastHit = raycastHit;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02fc4e983a7341fcb56deb4aeb0d3261
|
||||
timeCreated: 1642855038
|
||||
@@ -0,0 +1,36 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrOverrideImpactDecal.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that, added to a GameObject, will allows to override the decal generated by a projectile impact.
|
||||
/// When a projectile impact coming from an <see cref="UxrProjectileSource" /> hits anything, an
|
||||
/// <see cref="UxrOverrideImpactDecal" /> will be looked for traversing upwards in the hierarchy starting from the
|
||||
/// collider.
|
||||
/// If no <see cref="UxrOverrideImpactDecal" /> was found, the decal specified in the
|
||||
/// <see cref="UxrProjectileSource" /> component will be used.
|
||||
/// </summary>
|
||||
public class UxrOverrideImpactDecal : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private UxrImpactDecal _decalToUse;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the decal to override with when the object was hit.
|
||||
/// </summary>
|
||||
public UxrImpactDecal DecalToUse => _decalToUse;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d02330c0eb2fbd428bed238a91d7099
|
||||
timeCreated: 1505571355
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,161 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrProjectileDeflect.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UltimateXR.Audio;
|
||||
using UltimateXR.Core.Components.Composite;
|
||||
using UltimateXR.Manipulation;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that, added to a <see cref="GameObject" /> with a collider, will allow to deflect shots coming from
|
||||
/// <see cref="UxrProjectileSource" /> components.
|
||||
/// </summary>
|
||||
public class UxrProjectileDeflect : UxrGrabbableObjectComponent<UxrProjectileDeflect>
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private UxrAudioSample _audioDeflect;
|
||||
[SerializeField] private UxrImpactDecal _decalOnReflect;
|
||||
[SerializeField] private float _decalLife = 5.0f;
|
||||
[SerializeField] private float _decalFadeoutDuration = 1.0f;
|
||||
[SerializeField] private bool _twoSidedDecal;
|
||||
[SerializeField] private float _twoSidedDecalThickness = 0.01f;
|
||||
[SerializeField] private LayerMask _collideLayersAddOnReflect = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Event called when a projectile got deflected after hitting the object.
|
||||
/// </summary>
|
||||
public event EventHandler<UxrDeflectEventArgs> ProjectileDeflected;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sound to play when a projectile was deflected.
|
||||
/// </summary>
|
||||
public UxrAudioSample AudioDeflect => _audioDeflect;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the decal to instantiate when a projectile was deflected.
|
||||
/// </summary>
|
||||
public UxrImpactDecal DecalOnReflect => _decalOnReflect;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the decal life in seconds after which it will fade out and be destroyed.
|
||||
/// </summary>
|
||||
public float DecalLife => _decalLife;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the decal fadeout duration in seconds.
|
||||
/// </summary>
|
||||
public float DecalFadeoutDuration => _decalFadeoutDuration;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the decal requires to generate another copy on the other side.
|
||||
/// </summary>
|
||||
public bool TwoSidedDecal => _twoSidedDecal;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object thickness in order to know how far the other side is to generate the copy on the backside of the
|
||||
/// impact.
|
||||
/// </summary>
|
||||
public float TwoSidedDecalThickness => _twoSidedDecalThickness;
|
||||
|
||||
/// <summary>
|
||||
/// Optional layer mask to add to the collider after a projectile was deflected.
|
||||
/// </summary>
|
||||
public LayerMask CollideLayersAddOnReflect => _collideLayersAddOnReflect;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the owner in case the deflection object is part of an avatar or can be grabbed.
|
||||
/// </summary>
|
||||
public UxrActor Owner { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
Owner = GetComponentInParent<UxrActor>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called when the object was grabbed. Will change the <see cref="Owner" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected override void OnObjectGrabbed(UxrManipulationEventArgs e)
|
||||
{
|
||||
base.OnObjectGrabbed(e);
|
||||
|
||||
if (e.IsGrabbedStateChanged && UxrGrabManager.Instance.GetGrabbingHand(e.GrabbableObject, e.GrabPointIndex, out UxrGrabber grabber))
|
||||
{
|
||||
Owner = grabber.Avatar.GetComponentInChildren<UxrActor>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the object was released. Will reset the <see cref="Owner" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected override void OnObjectReleased(UxrManipulationEventArgs e)
|
||||
{
|
||||
base.OnObjectReleased(e);
|
||||
|
||||
if (e.IsGrabbedStateChanged)
|
||||
{
|
||||
Owner = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the object was released. Will reset the <see cref="Owner" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected override void OnObjectPlaced(UxrManipulationEventArgs e)
|
||||
{
|
||||
base.OnObjectPlaced(e);
|
||||
|
||||
if (e.IsGrabbedStateChanged && UxrGrabManager.Instance.GetGrabbingHand(e.GrabbableObject, e.GrabPointIndex, out UxrGrabber grabber))
|
||||
{
|
||||
Owner = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="ProjectileDeflected" /> event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
internal void RaiseProjectileDeflected(UxrDeflectEventArgs e)
|
||||
{
|
||||
ProjectileDeflected?.Invoke(this, e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrGrabbableObjectComponent<UxrProjectileDeflect>
|
||||
|
||||
/// <summary>
|
||||
/// The grabbable object is not required. When it is present it will be used to assign the <see cref="Owner" /> so that
|
||||
/// the damage will be attributed to the actor instead of the original source.
|
||||
/// </summary>
|
||||
protected override bool IsGrabbableObjectRequired => false;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e7805ce0b14da774880ea8149eec105e
|
||||
timeCreated: 1505824554
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,206 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrProjectileSource.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using UltimateXR.Core.Caching;
|
||||
using UltimateXR.Core.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that has the ability to fire shots.
|
||||
/// </summary>
|
||||
public class UxrProjectileSource : UxrComponent<UxrProjectileSource>, IUxrPrecacheable
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private Animator _weaponAnimator;
|
||||
[SerializeField] private List<UxrShotDescriptor> _shotTypes;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// The different shots that can be fired using the component.
|
||||
/// </summary>
|
||||
public IReadOnlyList<UxrShotDescriptor> ShotTypes => _shotTypes;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implicit IUxrPrecacheable
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<GameObject> PrecachedInstances
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (UxrShotDescriptor shotType in _shotTypes)
|
||||
{
|
||||
if (shotType.PrefabInstantiateOnImpact)
|
||||
{
|
||||
yield return shotType.PrefabInstantiateOnImpact;
|
||||
}
|
||||
|
||||
if (shotType.PrefabInstantiateOnTipWhenShot)
|
||||
{
|
||||
yield return shotType.PrefabInstantiateOnTipWhenShot;
|
||||
}
|
||||
|
||||
if (shotType.PrefabScenarioImpactDecal && shotType.PrefabScenarioImpactDecal.gameObject)
|
||||
{
|
||||
yield return shotType.PrefabScenarioImpactDecal.gameObject;
|
||||
}
|
||||
|
||||
if (shotType.ProjectilePrefab)
|
||||
{
|
||||
yield return shotType.ProjectilePrefab;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the <see cref="UxrActor" /> that holds the <see cref="UxrWeapon" /> that has the
|
||||
/// <see cref="UxrProjectileSource" /> component.
|
||||
/// </summary>
|
||||
/// <returns>Actor component or null if it wasn't found</returns>
|
||||
public UxrActor TryGetWeaponOwner()
|
||||
{
|
||||
UxrWeapon weapon = GetComponentInParent<UxrWeapon>();
|
||||
|
||||
if (weapon)
|
||||
{
|
||||
return weapon.Owner;
|
||||
}
|
||||
|
||||
return GetComponentInParent<UxrActor>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shoots a round.
|
||||
/// </summary>
|
||||
/// <param name="shotTypeIndex">Index in <see cref="ShotTypes" />, telling which shot type to fire</param>
|
||||
public void Shoot(int shotTypeIndex)
|
||||
{
|
||||
if (shotTypeIndex >= 0 && shotTypeIndex < _shotTypes.Count)
|
||||
{
|
||||
Shoot(shotTypeIndex, _shotTypes[shotTypeIndex].ShotSource.position, _shotTypes[shotTypeIndex].ShotSource.rotation);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shoots a round, overriding the source position and orientation.
|
||||
/// </summary>
|
||||
/// <param name="shotTypeIndex">Index in <see cref="ShotTypes" />, telling which shot type to fire</param>
|
||||
/// <param name="projectileSource">Source shot position</param>
|
||||
/// <param name="projectileOrientation">Shot source orientation. The shot will be fired in the z (forward) direction</param>
|
||||
public void Shoot(int shotTypeIndex, Vector3 projectileSource, Quaternion projectileOrientation)
|
||||
{
|
||||
if (shotTypeIndex >= 0 && shotTypeIndex < _shotTypes.Count)
|
||||
{
|
||||
if (_shotTypes[shotTypeIndex].PrefabInstantiateOnTipWhenShot)
|
||||
{
|
||||
GameObject newInstance = Instantiate(_shotTypes[shotTypeIndex].PrefabInstantiateOnTipWhenShot, _shotTypes[shotTypeIndex].Tip.position, _shotTypes[shotTypeIndex].Tip.rotation);
|
||||
|
||||
if (_shotTypes[shotTypeIndex].PrefabInstantiateOnTipParent)
|
||||
{
|
||||
newInstance.transform.parent = transform;
|
||||
}
|
||||
else
|
||||
{
|
||||
newInstance.transform.parent = null;
|
||||
}
|
||||
|
||||
if (_shotTypes[shotTypeIndex].PrefabInstantiateOnTipLife >= 0.0f)
|
||||
{
|
||||
Destroy(newInstance, _shotTypes[shotTypeIndex].PrefabInstantiateOnTipLife);
|
||||
}
|
||||
}
|
||||
|
||||
UxrWeaponManager.Instance.RegisterNewProjectileShot(this, _shotTypes[shotTypeIndex], projectileSource, projectileOrientation);
|
||||
|
||||
if (_weaponAnimator != null && string.IsNullOrEmpty(_shotTypes[shotTypeIndex].ShotAnimationVarName) == false)
|
||||
{
|
||||
_weaponAnimator.SetTrigger(_shotTypes[shotTypeIndex].ShotAnimationVarName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shoots a round pointing to the given target.
|
||||
/// </summary>
|
||||
/// <param name="shotTypeIndex">Index in <see cref="ShotTypes" />, telling which shot type to fire</param>
|
||||
/// <param name="target">Position where the shot will be going towards</param>
|
||||
public void ShootTo(int shotTypeIndex, Vector3 target)
|
||||
{
|
||||
if (shotTypeIndex >= 0 && shotTypeIndex < _shotTypes.Count)
|
||||
{
|
||||
Vector3 direction = (target - _shotTypes[shotTypeIndex].ShotSource.position).normalized;
|
||||
Shoot(shotTypeIndex, _shotTypes[shotTypeIndex].ShotSource.position, Quaternion.LookRotation(direction));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the distance where a shot using the current position and orientation will impact.
|
||||
/// </summary>
|
||||
/// <param name="shotTypeIndex">Index in <see cref="ShotTypes" />, telling which shot type to use</param>
|
||||
/// <returns>Shot distance or a negative value telling the current target is out of range</returns>
|
||||
public float ShotRaycastDistance(int shotTypeIndex)
|
||||
{
|
||||
if (shotTypeIndex >= 0 && shotTypeIndex < _shotTypes.Count)
|
||||
{
|
||||
if (Physics.Raycast(_shotTypes[shotTypeIndex].ShotSource.position,
|
||||
_shotTypes[shotTypeIndex].ShotSource.forward,
|
||||
out RaycastHit raycastHit,
|
||||
_shotTypes[shotTypeIndex].ProjectileMaxDistance,
|
||||
_shotTypes[shotTypeIndex].CollisionLayerMask,
|
||||
QueryTriggerInteraction.Ignore))
|
||||
{
|
||||
return raycastHit.distance;
|
||||
}
|
||||
}
|
||||
|
||||
return -1.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current world-space origin of projectiles fired using the given shot type.
|
||||
/// </summary>
|
||||
/// <param name="shotTypeIndex">Index in <see cref="ShotTypes" />, telling which shot type to use</param>
|
||||
/// <returns>Projectile world-space source</returns>
|
||||
public Vector3 GetShotOrigin(int shotTypeIndex)
|
||||
{
|
||||
if (shotTypeIndex >= 0 && shotTypeIndex < _shotTypes.Count)
|
||||
{
|
||||
return _shotTypes[shotTypeIndex].ShotSource.position;
|
||||
}
|
||||
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current world-space direction of projectiles fired using the given shot type.
|
||||
/// </summary>
|
||||
/// <param name="shotTypeIndex">Index in <see cref="ShotTypes" />, telling which shot type to use</param>
|
||||
/// <returns>Projectile world-space direction</returns>
|
||||
public Vector3 GetShotDirection(int shotTypeIndex)
|
||||
{
|
||||
if (shotTypeIndex >= 0 && shotTypeIndex < _shotTypes.Count)
|
||||
{
|
||||
return _shotTypes[shotTypeIndex].ShotSource.forward;
|
||||
}
|
||||
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b09fc2a58519c04382be9afd085eb88
|
||||
timeCreated: 1504852557
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,29 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrShotCycle.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the supported firearm shot cycles.
|
||||
/// </summary>
|
||||
public enum UxrShotCycle
|
||||
{
|
||||
/// <summary>
|
||||
/// Weapon requires a manual reload to fire the next round.
|
||||
/// </summary>
|
||||
ManualReload,
|
||||
|
||||
/// <summary>
|
||||
/// Weapon fires a single round each time the trigger is pressed. The next round requires to release the trigger and
|
||||
/// press it again.
|
||||
/// </summary>
|
||||
SemiAutomatic,
|
||||
|
||||
/// <summary>
|
||||
/// Weapon keeps firing one round after another while the trigger is being pressed.
|
||||
/// </summary>
|
||||
FullyAutomatic
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e568b53c93d42c1ae79c5d81703d695
|
||||
timeCreated: 1643104513
|
||||
@@ -0,0 +1,169 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrShotDescriptor.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Class describing all the information of a type of projectile that a GameObject having a
|
||||
/// <see cref="UxrProjectileSource" /> component can shoot.
|
||||
/// Normally there will be a <see cref="UxrFirearmWeapon" /> with a <see cref="UxrProjectileSource" /> component
|
||||
/// supporting one or more <see cref="UxrShotDescriptor" />.
|
||||
/// For example, a rifle with a grenade launcher attachment will be able to fire two types of projectiles: bullets and
|
||||
/// explosive grenades.
|
||||
/// <see cref="UxrProjectileSource" /> components, however, do not require to be part of a
|
||||
/// <see cref="UxrFirearmWeapon" /> and can be used on their own.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class UxrShotDescriptor
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private Transform _shotSource;
|
||||
[SerializeField] private Transform _tip;
|
||||
[SerializeField] private bool _useAutomaticProjectileTrajectory = true;
|
||||
[SerializeField] private string _shotAnimationVarName;
|
||||
[SerializeField] private GameObject _prefabInstantiateOnTipWhenShot;
|
||||
[SerializeField] private float _prefabInstantiateOnTipLife = 5.0f;
|
||||
[SerializeField] private bool _prefabInstantiateOnTipParent = true;
|
||||
[SerializeField] private GameObject _projectilePrefab;
|
||||
[SerializeField] private float _projectileSpeed = 30.0f;
|
||||
[SerializeField] private float _projectileMaxDistance = 300.0f;
|
||||
[SerializeField] private float _projectileLength = 0.01f;
|
||||
[SerializeField] private float _projectileDamageNear = 20.0f;
|
||||
[SerializeField] private float _projectileDamageFar = 20.0f;
|
||||
[SerializeField] private float _projectileImpactForceMultiplier = 1.0f;
|
||||
[SerializeField] private LayerMask _collisionLayerMask = -1;
|
||||
[SerializeField] private GameObject _prefabInstantiateOnImpact;
|
||||
[SerializeField] private float _prefabInstantiateOnImpactLife = 5.0f;
|
||||
[SerializeField] private UxrImpactDecal _prefabScenarioImpactDecal;
|
||||
[SerializeField] private float _prefabScenarioImpactDecalLife = 10.0f;
|
||||
[SerializeField] private float _decalFadeoutDuration = 7.0f;
|
||||
[SerializeField] private LayerMask _createDecalLayerMask = -1;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Transform" /> that is used to fire projectiles from, using the forward vector as direction.
|
||||
/// </summary>
|
||||
public Transform ShotSource => _shotSource;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Transform" /> that is used to instantiate effects on the tip when a shot was fired, using
|
||||
/// <see cref="PrefabInstantiateOnTipWhenShot" />.
|
||||
/// </summary>
|
||||
public Transform Tip => _tip;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the projectiles fired should be updated automatically to compute their trajectory or they will be
|
||||
/// updated manually.
|
||||
/// </summary>
|
||||
public bool UseAutomaticProjectileTrajectory => _useAutomaticProjectileTrajectory;
|
||||
|
||||
/// <summary>
|
||||
/// Optional <see cref="Animator" /> trigger variable name that will be triggered on the weapon each time a round is
|
||||
/// fired.
|
||||
/// </summary>
|
||||
public string ShotAnimationVarName => _shotAnimationVarName;
|
||||
|
||||
/// <summary>
|
||||
/// An optional prefab that will be instantiated on the <see cref="Tip" /> each time a round is fired.
|
||||
/// </summary>
|
||||
public GameObject PrefabInstantiateOnTipWhenShot => _prefabInstantiateOnTipWhenShot;
|
||||
|
||||
/// <summary>
|
||||
/// Life in seconds of <see cref="PrefabInstantiateOnTipWhenShot" /> after which it will be destroyed.
|
||||
/// </summary>
|
||||
public float PrefabInstantiateOnTipLife => _prefabInstantiateOnTipLife;
|
||||
|
||||
/// <summary>
|
||||
/// Whether <see cref="PrefabInstantiateOnTipWhenShot" /> will be parented to the <see cref="Tip" /> after being
|
||||
/// instantiated or will remain unparented.
|
||||
/// </summary>
|
||||
public bool PrefabInstantiateOnTipParent => _prefabInstantiateOnTipParent;
|
||||
|
||||
/// <summary>
|
||||
/// Prefab that will be instantiated as the projectile.
|
||||
/// </summary>
|
||||
public GameObject ProjectilePrefab => _projectilePrefab;
|
||||
|
||||
/// <summary>
|
||||
/// Speed at which the projectile will move.
|
||||
/// </summary>
|
||||
public float ProjectileSpeed => _projectileSpeed;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum reach of the projectile, after which it will be destroyed.
|
||||
/// </summary>
|
||||
public float ProjectileMaxDistance => _projectileMaxDistance;
|
||||
|
||||
/// <summary>
|
||||
/// The physical length of the projectile, used in ray-casting computations.
|
||||
/// </summary>
|
||||
public float ProjectileLength => _projectileLength;
|
||||
|
||||
/// <summary>
|
||||
/// The damage a projectile will do if it were to hit at the closest distance. Damage will linearly decrease over
|
||||
/// distance down to <see cref="ProjectileDamageFar" /> until the projectile reaches
|
||||
/// <see cref="_projectileMaxDistance" />.
|
||||
/// </summary>
|
||||
public float ProjectileDamageNear => _projectileDamageNear;
|
||||
|
||||
/// <summary>
|
||||
/// The damage a projectile will do if it were to hit at the farthest distance. Damage will linearly decrease over
|
||||
/// distance from the start down to <see cref="ProjectileDamageFar" /> until the projectile reaches
|
||||
/// <see cref="_projectileMaxDistance" />.
|
||||
/// </summary>
|
||||
public float ProjectileDamageFar => _projectileDamageFar;
|
||||
|
||||
/// <summary>
|
||||
/// The force multiplier applied to a rigidbody that was hit by a projectile. The total force applied will be speed *
|
||||
/// ForceMultiplier.
|
||||
/// </summary>
|
||||
public float ProjectileImpactForceMultiplier => _projectileImpactForceMultiplier;
|
||||
|
||||
/// <summary>
|
||||
/// The layer mask used to determine which objects can be hit.
|
||||
/// </summary>
|
||||
public LayerMask CollisionLayerMask => _collisionLayerMask;
|
||||
|
||||
/// <summary>
|
||||
/// An optional prefab to instantiate at the point of impact.
|
||||
/// </summary>
|
||||
public GameObject PrefabInstantiateOnImpact => _prefabInstantiateOnImpact;
|
||||
|
||||
/// <summary>
|
||||
/// Life in seconds after which <see cref="PrefabInstantiateOnImpact" /> will be destroyed.
|
||||
/// </summary>
|
||||
public float PrefabInstantiateOnImpactLife => _prefabInstantiateOnImpactLife;
|
||||
|
||||
/// <summary>
|
||||
/// Default decal that will be used when the projectile impacted with something. Can be overriden using the
|
||||
/// <see cref="UxrOverrideImpactDecal" /> component.
|
||||
/// </summary>
|
||||
public UxrImpactDecal PrefabScenarioImpactDecal => _prefabScenarioImpactDecal;
|
||||
|
||||
/// <summary>
|
||||
/// Life in seconds after which <see cref="PrefabScenarioImpactDecal" /> will fadeout and be destroyed.
|
||||
/// </summary>
|
||||
public float PrefabScenarioImpactDecalLife => _prefabScenarioImpactDecalLife;
|
||||
|
||||
/// <summary>
|
||||
/// Duration of the fadeout effect before a <see cref="PrefabScenarioImpactDecal" /> is destroyed.
|
||||
/// </summary>
|
||||
public float DecalFadeoutDuration => _decalFadeoutDuration;
|
||||
|
||||
/// <summary>
|
||||
/// The layer mask used to determine if an impact will generate a decal.
|
||||
/// </summary>
|
||||
public LayerMask CreateDecalLayerMask => _createDecalLayerMask;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96ddb71598ce41cd8fe0e4380569ba02
|
||||
timeCreated: 1643100819
|
||||
@@ -0,0 +1,30 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrShotgunPump.State.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
public partial class UxrShotgunPump
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the different states in a <see cref="UxrShotgunPump" /> component.
|
||||
/// </summary>
|
||||
private enum State
|
||||
{
|
||||
/// <summary>
|
||||
/// Waiting for the pump action in the first direction.
|
||||
/// </summary>
|
||||
WaitPump,
|
||||
|
||||
/// <summary>
|
||||
/// Waiting for the pump action back.
|
||||
/// </summary>
|
||||
WaitPumpBack
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2746817ac1b94b3d888360921776ad90
|
||||
timeCreated: 1643728993
|
||||
@@ -0,0 +1,117 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrShotgunPump.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Audio;
|
||||
using UltimateXR.Avatar;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components;
|
||||
using UltimateXR.Haptics;
|
||||
using UltimateXR.Manipulation;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that, added to a GameObject with a <see cref="UxrFirearmWeapon" /> component, allows to communicate
|
||||
/// whenever the shotgun is reloaded using a pump action using a <see cref="UxrGrabbableObject" />. The shot cycle in
|
||||
/// the firearm should be set to <see cref="UxrShotCycle.ManualReload" />.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(UxrFirearmWeapon))]
|
||||
public partial class UxrShotgunPump : UxrComponent
|
||||
{
|
||||
#region Inspector Properties/Serialized Fields
|
||||
|
||||
[SerializeField] private int _triggerIndex;
|
||||
[SerializeField] private UxrGrabbableObject _pump;
|
||||
[SerializeField] private Vector3 _localPumpDirection = Vector3.forward;
|
||||
[SerializeField] private Vector3 _localPumpOffset = Vector3.forward * 0.2f;
|
||||
[SerializeField] [Range(0, 1)] private float _slideThreshold = 0.7f;
|
||||
[SerializeField] private UxrAudioSample _audioSlide = new UxrAudioSample();
|
||||
[SerializeField] private UxrAudioSample _audioSlideBack = new UxrAudioSample();
|
||||
[SerializeField] private UxrAudioSample _audioSlideAlreadyLoaded = new UxrAudioSample();
|
||||
[SerializeField] private UxrAudioSample _audioSlideBackAlreadyLoaded = new UxrAudioSample();
|
||||
[SerializeField] private UxrHapticClip _hapticClipSlide = new UxrHapticClip(null, UxrHapticClipType.Click);
|
||||
[SerializeField] private UxrHapticClip _hapticClipSlideBack = new UxrHapticClip(null, UxrHapticClipType.Click);
|
||||
[SerializeField] private UxrHapticClip _hapticClipSlideAlreadyLoaded = new UxrHapticClip(null, UxrHapticClipType.Slide);
|
||||
[SerializeField] private UxrHapticClip _hapticClipSlideBackAlreadyLoaded = new UxrHapticClip(null, UxrHapticClipType.Slide);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
_state = State.WaitPump;
|
||||
_localStart = _pump != null ? _pump.transform.localPosition : Vector3.zero;
|
||||
_firearm = GetComponent<UxrFirearmWeapon>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the component, looking for the pump action if necessary.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (_pump == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float currentSlide = Vector3.Scale(_pump.transform.localPosition - _localStart, _localPumpDirection).magnitude / _localPumpOffset.magnitude;
|
||||
|
||||
if (_state == State.WaitPump && currentSlide > _slideThreshold)
|
||||
{
|
||||
_state = State.WaitPumpBack;
|
||||
|
||||
if (_firearm.IsLoaded(_triggerIndex))
|
||||
{
|
||||
_audioSlideAlreadyLoaded.Play(_pump.transform.position);
|
||||
}
|
||||
else
|
||||
{
|
||||
_audioSlide.Play(_pump.transform.position);
|
||||
}
|
||||
|
||||
if (UxrGrabManager.Instance.GetGrabbingHand(_pump, 0, out UxrGrabber grabber) && grabber.Avatar.AvatarMode == UxrAvatarMode.Local)
|
||||
{
|
||||
UxrAvatar.LocalAvatarInput.SendHapticFeedback(grabber.Side, _firearm.IsLoaded(_triggerIndex) ? _hapticClipSlideAlreadyLoaded : _hapticClipSlide);
|
||||
}
|
||||
}
|
||||
else if (_state == State.WaitPumpBack && currentSlide < _slideThreshold * 0.9f)
|
||||
{
|
||||
if (_firearm.IsLoaded(_triggerIndex))
|
||||
{
|
||||
_audioSlideBackAlreadyLoaded.Play(_pump.transform.position);
|
||||
}
|
||||
else
|
||||
{
|
||||
_audioSlideBack.Play(_pump.transform.position);
|
||||
}
|
||||
|
||||
if (UxrGrabManager.Instance.GetGrabbingHand(_pump, 0, out UxrGrabber grabber) && grabber.Avatar.AvatarMode == UxrAvatarMode.Local)
|
||||
{
|
||||
UxrAvatar.LocalAvatarInput.SendHapticFeedback(grabber.Side, _firearm.IsLoaded(_triggerIndex) ? _hapticClipSlideBackAlreadyLoaded : _hapticClipSlideBack);
|
||||
}
|
||||
|
||||
_firearm.Reload(_triggerIndex);
|
||||
_state = State.WaitPump;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private UxrFirearmWeapon _firearm;
|
||||
private Vector3 _localStart;
|
||||
private State _state;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00cec3188f04edb4cb6dc7b4fa4c540a
|
||||
timeCreated: 1533114909
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,29 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrWeapon.StateSave.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.StateSave;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
public abstract partial class UxrWeapon
|
||||
{
|
||||
#region Protected Overrides UxrComponent
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SerializeState(bool isReading, int stateSerializationVersion, UxrStateSaveLevel level, UxrStateSaveOptions options)
|
||||
{
|
||||
base.SerializeState(isReading, stateSerializationVersion, level, options);
|
||||
|
||||
// Logic is already handled through events, we don't serialize these parameters in incremental changes
|
||||
|
||||
if (level > UxrStateSaveLevel.ChangesSincePreviousSave)
|
||||
{
|
||||
SerializeStateValue(level, options, nameof(_owner), ref _owner);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a251ea42b89455fafdc31e73801fc38
|
||||
timeCreated: 1706004719
|
||||
102
Assets/UltimateXR/Runtime/Scripts/Mechanics/Weapons/UxrWeapon.cs
Normal file
102
Assets/UltimateXR/Runtime/Scripts/Mechanics/Weapons/UxrWeapon.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrWeapon.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UltimateXR.Core.Components.Composite;
|
||||
using UltimateXR.Manipulation;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for weapons. Weapons are used by <see cref="UxrActor" /> components to inflict damage to other actor
|
||||
/// components.
|
||||
/// </summary>
|
||||
public abstract partial class UxrWeapon : UxrGrabbableObjectComponent<UxrWeapon>
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets who is in possession of the weapon, to attribute the inflicted damage to.
|
||||
/// </summary>
|
||||
public UxrActor Owner
|
||||
{
|
||||
get => _owner;
|
||||
protected set => _owner = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the component.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
UxrActor.GlobalUnregistering += UxrActor_GlobalUnregistering;
|
||||
|
||||
Owner = GetComponentInParent<UxrActor>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when it's going to be destroyed.
|
||||
/// </summary>
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
UxrActor.GlobalUnregistering -= UxrActor_GlobalUnregistering;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever an actor is about to be destroyed.
|
||||
/// </summary>
|
||||
/// <param name="actor"></param>
|
||||
private void UxrActor_GlobalUnregistering(UxrActor actor)
|
||||
{
|
||||
if (Owner == actor)
|
||||
{
|
||||
Owner = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called when the object was grabbed. It is used to set the weapon owner.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
protected override void OnObjectGrabbed(UxrManipulationEventArgs e)
|
||||
{
|
||||
base.OnObjectGrabbed(e);
|
||||
|
||||
if (e.IsGrabbedStateChanged && UxrGrabManager.Instance.GetGrabbingHand(e.GrabbableObject, e.GrabPointIndex, out UxrGrabber grabber))
|
||||
{
|
||||
Owner = grabber.Avatar.GetComponentInChildren<UxrActor>();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Overrides UxrGrabbableObjectComponent<UxrWeapon>
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool IsGrabbableObjectRequired => false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private UxrActor _owner;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 102dfa92408c7034a9b3d0294424e47a
|
||||
timeCreated: 1535641242
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,37 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrWeaponManager.DamageActorInfo.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
public partial class UxrWeaponManager
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Stores information about an actor in the
|
||||
/// </summary>
|
||||
private class ActorInfo
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
public Transform Transform { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
public ActorInfo(UxrActor target)
|
||||
{
|
||||
Transform = target.GetComponent<Transform>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce80fc4320b94062a4e49dd779bfbc63
|
||||
timeCreated: 1643733896
|
||||
@@ -0,0 +1,132 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrWeaponManager.ProjectileInfo.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
public partial class UxrWeaponManager
|
||||
{
|
||||
#region Private Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Stores information of a projectile currently travelling through the world.
|
||||
/// </summary>
|
||||
private class ProjectileInfo
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="UxrActor" /> that shot the projectile, or deflected it. It will be used know who to attribute
|
||||
/// the damage to.
|
||||
/// </summary>
|
||||
public UxrActor WeaponOwner { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the source that shot the projectile.
|
||||
/// </summary>
|
||||
public UxrProjectileSource ProjectileSource { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the shot descriptor that was used to shoot the projectile.
|
||||
/// </summary>
|
||||
public UxrShotDescriptor ShotDescriptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the layer mask, used to determine which objects the shot can collide with.
|
||||
/// </summary>
|
||||
public LayerMask ShotLayerMask { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the projectile GameObject instance.
|
||||
/// </summary>
|
||||
public GameObject Projectile { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the world-space point the projectile came from.
|
||||
/// </summary>
|
||||
public Vector3 ProjectileOrigin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current projectile speed in units/second.
|
||||
/// </summary>
|
||||
public float ProjectileSpeed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the projectile's position during the previous frame.
|
||||
/// </summary>
|
||||
public Vector3 ProjectileLastPosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently travelled distance.
|
||||
/// </summary>
|
||||
public float ProjectileDistanceTravelled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the deflector that deflected the shot or null if there wasn't any.
|
||||
/// </summary>
|
||||
public UxrProjectileDeflect ProjectileDeflectSource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the current state is the first frame in the shot.
|
||||
/// </summary>
|
||||
public bool FirstFrame { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors & Finalizer
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="weaponOwner">Weapon owner, that fired the shot</param>
|
||||
/// <param name="projectileSource">Projectile source component</param>
|
||||
/// <param name="shotDescriptor">Shot descriptor</param>
|
||||
/// <param name="position">World-space position where the shot started</param>
|
||||
/// <param name="orientation">
|
||||
/// World space orientation where the shot started. The shot will travel in the z (forward)
|
||||
/// position of these axes
|
||||
/// </param>
|
||||
public ProjectileInfo(UxrActor weaponOwner, UxrProjectileSource projectileSource, UxrShotDescriptor shotDescriptor, Vector3 position, Quaternion orientation)
|
||||
{
|
||||
WeaponOwner = weaponOwner;
|
||||
ProjectileSource = projectileSource;
|
||||
ShotDescriptor = shotDescriptor;
|
||||
ShotLayerMask = shotDescriptor.CollisionLayerMask;
|
||||
Projectile = Instantiate(shotDescriptor.ProjectilePrefab, position, orientation);
|
||||
Projectile.transform.parent = null;
|
||||
ProjectileOrigin = position;
|
||||
ProjectileSpeed = shotDescriptor.ProjectileSpeed;
|
||||
ProjectileLastPosition = ProjectileOrigin;
|
||||
ProjectileDistanceTravelled = 0.0f;
|
||||
ProjectileDeflectSource = null;
|
||||
FirstFrame = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Adds a value to the layer mask that is used to determine the objects the projectile can collide with.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to add to the mask</param>
|
||||
public void AddShotLayerMask(int value)
|
||||
{
|
||||
_shotLayerMask |= value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private LayerMask _shotLayerMask;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1db86a63784643c7a06668bb5d11f7f6
|
||||
timeCreated: 1643733924
|
||||
@@ -0,0 +1,357 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="UxrWeaponManager.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Components.Singleton;
|
||||
using UltimateXR.Core.Settings;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Mechanics.Weapons
|
||||
{
|
||||
/// <summary>
|
||||
/// Singleton manager in charge of updating projectiles, computing hits against entities and damage done on
|
||||
/// <see cref="UxrActor" /> components.
|
||||
/// </summary>
|
||||
public partial class UxrWeaponManager : UxrSingleton<UxrWeaponManager>
|
||||
{
|
||||
#region Public Types & Data
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered right before an <see cref="UxrActor" /> is about to receive damage.
|
||||
/// Setting <see cref="UxrDamageEventArgs.Cancel" /> will allow not to take the damage.
|
||||
/// </summary>
|
||||
public event EventHandler<UxrDamageEventArgs> DamageReceiving;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered right after the actor received damage.
|
||||
/// Setting <see cref="UxrDamageEventArgs.Cancel" /> is not supported, since the damage was already taken.
|
||||
/// </summary>
|
||||
public event EventHandler<UxrDamageEventArgs> DamageReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Event called whenever there was a projectile impact but no <see cref="UxrActor" /> was involved. Mostly hits
|
||||
/// against the scenario that still generate decals, FX, etc.
|
||||
/// </summary>
|
||||
public event EventHandler<UxrNonDamagingImpactEventArgs> NonActorImpacted;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Updates the manager.
|
||||
/// </summary>
|
||||
public void UpdateManager()
|
||||
{
|
||||
UpdateProjectiles();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a new projectile shot so that it gets automatically update by the manager from that moment until it hits
|
||||
/// something or gets destroyed.
|
||||
/// </summary>
|
||||
/// <param name="projectileSource">Projectile source</param>
|
||||
/// <param name="shotDescriptor">Shot descriptor</param>
|
||||
/// <param name="position">World position</param>
|
||||
/// <param name="orientation">World orientation. The projectile will travel in the forward (z) direction</param>
|
||||
public void RegisterNewProjectileShot(UxrProjectileSource projectileSource, UxrShotDescriptor shotDescriptor, Vector3 position, Quaternion orientation)
|
||||
{
|
||||
_projectiles.Add(new ProjectileInfo(projectileSource.TryGetWeaponOwner(), projectileSource, shotDescriptor, position, orientation));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies radius damage to all elements around a source position.
|
||||
/// </summary>
|
||||
/// <param name="actorSource">The actor that was responsible for the damage or null if there wasn't any</param>
|
||||
/// <param name="position">Explosion world position</param>
|
||||
/// <param name="radius">Radius</param>
|
||||
/// <param name="nearDamage">Damage at the very same point of the explosion</param>
|
||||
/// <param name="farDamage">Damage at the distance set by <paramref name="radius" /></param>
|
||||
public void ApplyRadiusDamage(UxrActor actorSource, Vector3 position, float radius, float nearDamage, float farDamage)
|
||||
{
|
||||
foreach (KeyValuePair<UxrActor, ActorInfo> damageActorPair in _damageActors)
|
||||
{
|
||||
float distance = Vector3.Distance(damageActorPair.Value.Transform.position, position);
|
||||
|
||||
if (distance < radius)
|
||||
{
|
||||
float damage = Mathf.Lerp(nearDamage, farDamage, distance / radius);
|
||||
damageActorPair.Key.ReceiveExplosion(actorSource, position, damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the manager. Subscribes to actor enable/disable events.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
UxrActor.GlobalEnabled += Actor_Enabled;
|
||||
UxrActor.GlobalDisabled += Actor_Disabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the manager. Unsubscribes from actor enable/disable events.
|
||||
/// </summary>
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
UxrActor.GlobalEnabled -= Actor_Enabled;
|
||||
UxrActor.GlobalDisabled -= Actor_Disabled;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling Methods
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever an <see cref="UxrActor" /> was enabled. Create a new entry in the internal dictionary.
|
||||
/// </summary>
|
||||
/// <param name="actor">Actor that was enabled</param>
|
||||
private void Actor_Enabled(UxrActor actor)
|
||||
{
|
||||
if (_damageActors.ContainsKey(actor) == false)
|
||||
{
|
||||
_damageActors.Add(actor, new ActorInfo(actor));
|
||||
actor.DamageReceiving += Actor_DamageReceiving;
|
||||
actor.DamageReceived += Actor_DamageReceived;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever an <see cref="UxrActor" /> was disabled. Remove the entry from the internal dictionary.
|
||||
/// </summary>
|
||||
/// <param name="actor">Actor that was disabled</param>
|
||||
private void Actor_Disabled(UxrActor actor)
|
||||
{
|
||||
if (_damageActors.ContainsKey(actor))
|
||||
{
|
||||
_damageActors.Remove(actor);
|
||||
actor.DamageReceiving -= Actor_DamageReceiving;
|
||||
actor.DamageReceived -= Actor_DamageReceived;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever an actor is about to receive any damage. The damage can be canceled using
|
||||
/// <see cref="UxrDamageEventArgs.Cancel" />.
|
||||
/// </summary>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void Actor_DamageReceiving(object sender, UxrDamageEventArgs e)
|
||||
{
|
||||
OnDamageReceiving(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called right after an actor received any damage. Damage can not be canceled using
|
||||
/// <see cref="UxrDamageEventArgs.Cancel" /> because it was already taken.
|
||||
/// </summary>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void Actor_DamageReceived(object sender, UxrDamageEventArgs e)
|
||||
{
|
||||
OnDamageReceived(e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Trigger Methods
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="DamageReceiving" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void OnDamageReceiving(UxrDamageEventArgs e)
|
||||
{
|
||||
DamageReceiving?.Invoke(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event trigger for <see cref="DamageReceived" />.
|
||||
/// </summary>
|
||||
/// <param name="e">Event parameters</param>
|
||||
private void OnDamageReceived(UxrDamageEventArgs e)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelWeapons >= UxrLogLevel.Relevant)
|
||||
{
|
||||
string sourceInfo = e.ActorSource != null ? $" from actor {e.ActorSource.name}." : string.Empty;
|
||||
|
||||
if (UxrGlobalSettings.Instance.LogLevelWeapons >= UxrLogLevel.Relevant)
|
||||
{
|
||||
Debug.Log($"{UxrConstants.WeaponsModule}: Actor {e.ActorTarget.name} received {e.Damage} damage of type {e.DamageType}{sourceInfo}. Is dead? {e.Dies}.");
|
||||
}
|
||||
}
|
||||
|
||||
DamageReceived?.Invoke(this, e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Updates all current projectiles.
|
||||
/// </summary>
|
||||
private void UpdateProjectiles()
|
||||
{
|
||||
for (int i = 0; i < _projectiles.Count; ++i)
|
||||
{
|
||||
if (_projectiles[i].Projectile == null)
|
||||
{
|
||||
// Dynamic instance removed somewhere
|
||||
_projectiles.RemoveAt(i);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_projectiles[i].FirstFrame)
|
||||
{
|
||||
// Render first frame where the weapon is
|
||||
_projectiles[i].FirstFrame = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector3 oldPos = _projectiles[i].ProjectileLastPosition;
|
||||
Vector3 newPos = _projectiles[i].Projectile.transform.position;
|
||||
|
||||
Vector3 projectileForward = _projectiles[i].Projectile.transform.forward;
|
||||
|
||||
if (_projectiles[i].ShotDescriptor.UseAutomaticProjectileTrajectory)
|
||||
{
|
||||
newPos = oldPos + (_projectiles[i].ProjectileSpeed * Time.deltaTime * projectileForward);
|
||||
_projectiles[i].Projectile.transform.position = newPos;
|
||||
}
|
||||
|
||||
_projectiles[i].ProjectileLastPosition = newPos;
|
||||
_projectiles[i].ProjectileDistanceTravelled += Vector3.Distance(oldPos, newPos);
|
||||
|
||||
if (_projectiles[i].ProjectileDistanceTravelled >= _projectiles[i].ShotDescriptor.ProjectileMaxDistance)
|
||||
{
|
||||
// Max distance travelled
|
||||
Destroy(_projectiles[i].Projectile);
|
||||
_projectiles.RemoveAt(i);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
if (_projectiles[i].ShotDescriptor.UseAutomaticProjectileTrajectory)
|
||||
{
|
||||
float rayLength = Vector3.Distance(oldPos, newPos + projectileForward * _projectiles[i].ShotDescriptor.ProjectileLength);
|
||||
|
||||
if (Physics.Raycast(oldPos, projectileForward, out RaycastHit raycastHit, rayLength, _projectiles[i].ShotLayerMask, QueryTriggerInteraction.Ignore))
|
||||
{
|
||||
UxrProjectileDeflect projectileDeflect = raycastHit.collider.GetComponentInParent<UxrProjectileDeflect>();
|
||||
|
||||
// Has UxrProjectileDeflect component?
|
||||
|
||||
if (projectileDeflect != null)
|
||||
{
|
||||
Vector3 newForward = Vector3.Reflect(projectileForward, raycastHit.normal).normalized;
|
||||
_projectiles[i].Projectile.transform.SetPositionAndRotation(raycastHit.point + newForward * 0.05f, Quaternion.LookRotation(newForward)); // + rayLength - raycastHit.distance));
|
||||
_projectiles[i].ProjectileDeflectSource = projectileDeflect;
|
||||
|
||||
projectileDeflect.AudioDeflect?.Play(raycastHit.point);
|
||||
|
||||
if (projectileDeflect.DecalOnReflect != null)
|
||||
{
|
||||
// Generate decal?
|
||||
UxrImpactDecal.CheckCreateDecal(raycastHit,
|
||||
-1,
|
||||
projectileDeflect.DecalOnReflect,
|
||||
projectileDeflect.DecalLife,
|
||||
projectileDeflect.DecalFadeoutDuration,
|
||||
projectileDeflect.TwoSidedDecal,
|
||||
projectileDeflect.TwoSidedDecalThickness);
|
||||
}
|
||||
|
||||
_projectiles[i].AddShotLayerMask(projectileDeflect.CollideLayersAddOnReflect.value);
|
||||
_projectiles[i].ProjectileLastPosition = _projectiles[i].Projectile.transform.position;
|
||||
|
||||
projectileDeflect.RaiseProjectileDeflected(new UxrDeflectEventArgs(_projectiles[i].ProjectileSource, raycastHit, newForward));
|
||||
}
|
||||
else
|
||||
{
|
||||
UxrActor targetActor = raycastHit.collider.GetComponentInParent<UxrActor>();
|
||||
|
||||
if (targetActor != null)
|
||||
{
|
||||
// Impact with an actor.
|
||||
|
||||
float normalizedDistance = _projectiles[i].ProjectileDistanceTravelled / _projectiles[i].ShotDescriptor.ProjectileMaxDistance;
|
||||
float damage = Mathf.Lerp(_projectiles[i].ShotDescriptor.ProjectileDamageNear, _projectiles[i].ShotDescriptor.ProjectileDamageFar, normalizedDistance);
|
||||
|
||||
if (_projectiles[i].ProjectileDeflectSource != null)
|
||||
{
|
||||
// Came from a shot deflected by a UxrProjectileDeflect
|
||||
targetActor.ReceiveImpact(_projectiles[i].ProjectileDeflectSource.Owner, raycastHit, damage);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Direct hit from a projectile
|
||||
targetActor.ReceiveImpact(_projectiles[i].ProjectileSource.TryGetWeaponOwner(), raycastHit, damage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Impact with something that isn't an actor, probably the scenario.
|
||||
|
||||
if (_projectiles[i].ShotDescriptor.PrefabInstantiateOnImpact)
|
||||
{
|
||||
Vector3 instantiateForward = Vector3.Reflect(projectileForward, raycastHit.normal).normalized;
|
||||
GameObject newInstance = Instantiate(_projectiles[i].ShotDescriptor.PrefabInstantiateOnImpact, raycastHit.point, Quaternion.LookRotation(instantiateForward));
|
||||
|
||||
if (_projectiles[i].ShotDescriptor.PrefabInstantiateOnImpactLife >= 0.0f)
|
||||
{
|
||||
Destroy(newInstance, _projectiles[i].ShotDescriptor.PrefabInstantiateOnImpactLife);
|
||||
}
|
||||
}
|
||||
|
||||
Rigidbody rigidbody = raycastHit.collider.GetComponentInParent<Rigidbody>();
|
||||
|
||||
if (rigidbody != null)
|
||||
{
|
||||
rigidbody.AddForceAtPosition(_projectiles[i].ProjectileSpeed * _projectiles[i].ShotDescriptor.ProjectileImpactForceMultiplier * projectileForward, raycastHit.transform.position);
|
||||
}
|
||||
|
||||
UxrOverrideImpactDecal overrideDecal = raycastHit.collider.GetComponentInParent<UxrOverrideImpactDecal>();
|
||||
|
||||
// Generate decal?
|
||||
UxrImpactDecal.CheckCreateDecal(raycastHit,
|
||||
_projectiles[i].ShotDescriptor.CreateDecalLayerMask,
|
||||
overrideDecal != null ? overrideDecal.DecalToUse : _projectiles[i].ShotDescriptor.PrefabScenarioImpactDecal,
|
||||
_projectiles[i].ShotDescriptor.PrefabScenarioImpactDecalLife,
|
||||
_projectiles[i].ShotDescriptor.DecalFadeoutDuration);
|
||||
|
||||
NonActorImpacted?.Invoke(_projectiles[i].ProjectileSource, new UxrNonDamagingImpactEventArgs(_projectiles[i].ProjectileSource.TryGetWeaponOwner(), _projectiles[i].ProjectileSource, raycastHit));
|
||||
}
|
||||
|
||||
Destroy(_projectiles[i].Projectile);
|
||||
_projectiles.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private readonly Dictionary<UxrActor, ActorInfo> _damageActors = new Dictionary<UxrActor, ActorInfo>();
|
||||
private readonly List<ProjectileInfo> _projectiles = new List<ProjectileInfo>();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7bd5b3cc3b4e328428572f39944e1803
|
||||
timeCreated: 1535629434
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user