Add ultimate xr
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user