init
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using VOID.Generic.AreaOfEffect;
|
||||
using VOID.Generic.Objects.Unit;
|
||||
using VOID.Generic.Projectile;
|
||||
using VOID.Generic.Trajectory;
|
||||
|
||||
namespace VOID.Generic.Player
|
||||
{
|
||||
/// <summary>
|
||||
/// Visualizes <see cref="ProjectileObject"/> by displaying <see cref="AbstractTrajectory"/> and <see cref="AbstractAreaOfEffect"/>.<br/>
|
||||
/// TODO: So far only first step of projectile is supported!
|
||||
/// </summary>
|
||||
[DefaultExecutionOrder(-1)]
|
||||
public class ProjectileVisualizer : MonoBehaviour
|
||||
{
|
||||
private UnitObject _caster;
|
||||
private ProjectileStep _projectileStep;
|
||||
private AbstractTrajectory _trajectory;
|
||||
private Transform _transformStart;
|
||||
private Transform _transformEnd;
|
||||
|
||||
private Vector3 _positionStart;
|
||||
private Vector3 _positionEnd;
|
||||
private Vector3 _lastPositionStart;
|
||||
private Vector3 _lastPositionEnd;
|
||||
private Vector3 _collisionOnPosition;
|
||||
|
||||
private LineRenderer _lineRenderer;
|
||||
private MeshFilter _meshFilter;
|
||||
private MeshRenderer _meshRenderer;
|
||||
|
||||
private GameObject _visualizerGameObject;
|
||||
|
||||
private void Awake() => enabled = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes and starts projectile visualizing.
|
||||
/// </summary>
|
||||
/// <param name="caster">UnitObject who is casting this projectile</param>
|
||||
/// <param name="projectileSteps">Definition how projectile will move and hit</param>
|
||||
/// <param name="trajectoryMaterial">Material to draw Trajectory</param>
|
||||
/// <param name="areaOfEffectMaterial">Material to draw AreaOfEffect</param>
|
||||
/// <param name="transformEnd">Target position for projectile</param>
|
||||
public void Init(
|
||||
UnitObject caster,
|
||||
ProjectileStep[] projectileSteps,
|
||||
Material trajectoryMaterial,
|
||||
Material areaOfEffectMaterial,
|
||||
Transform transformEnd
|
||||
)
|
||||
{
|
||||
// TODO: So far only first step of projectile is supported!
|
||||
_projectileStep = projectileSteps.FirstOrDefault();
|
||||
if (_projectileStep == default) return;
|
||||
|
||||
_caster = caster;
|
||||
_trajectory = _projectileStep.trajectory.Clone();
|
||||
_transformStart = FindStartTransform(_projectileStep);
|
||||
_transformEnd = transformEnd;
|
||||
|
||||
// GameObject containing meshes of Trajectory and AreaOfEffect
|
||||
_visualizerGameObject = new GameObject("Projectile Visualizer");
|
||||
_visualizerGameObject.transform.parent = gameObject.transform;
|
||||
_visualizerGameObject.transform.rotation =
|
||||
_projectileStep.areaOfEffect.GetRotation(_transformStart.position, _transformEnd.position);
|
||||
|
||||
// Trajectory visualizing
|
||||
if (_trajectory is not InstantTrajectory)
|
||||
{
|
||||
_lineRenderer = _visualizerGameObject.AddComponent<LineRenderer>();
|
||||
_lineRenderer.enabled = false;
|
||||
_lineRenderer.startWidth = _projectileStep.radius;
|
||||
_lineRenderer.endWidth = _projectileStep.radius;
|
||||
_lineRenderer.shadowCastingMode = ShadowCastingMode.Off;
|
||||
_lineRenderer.sharedMaterial = trajectoryMaterial;
|
||||
}
|
||||
|
||||
// AreaOfEffect visualizing
|
||||
if (_projectileStep.areaOfEffect is not NoneAreaOfEffect)
|
||||
{
|
||||
_meshFilter = _visualizerGameObject.AddComponent<MeshFilter>();
|
||||
_meshFilter.mesh = _projectileStep.areaOfEffect.GetMesh();
|
||||
_meshRenderer = _visualizerGameObject.AddComponent<MeshRenderer>();
|
||||
_meshRenderer.enabled = false;
|
||||
_meshRenderer.shadowCastingMode = ShadowCastingMode.Off;
|
||||
_meshRenderer.sharedMaterial = areaOfEffectMaterial;
|
||||
}
|
||||
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
_positionStart = _transformStart.position;
|
||||
_positionEnd = _transformEnd.position;
|
||||
|
||||
// If positions didn't change then there is no need to recalculate whole Trajectory and AreaOfEffect
|
||||
if (_positionStart == _lastPositionStart && _positionEnd == _lastPositionEnd) return;
|
||||
|
||||
_lastPositionStart = _positionStart;
|
||||
_lastPositionEnd = _positionEnd;
|
||||
|
||||
UpdateTrajectory();
|
||||
UpdateAreaOfEffect();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates whole <see cref="AbstractTrajectory"/>
|
||||
/// </summary>
|
||||
private void UpdateTrajectory()
|
||||
{
|
||||
// Its instant, that means there is no trajectory
|
||||
if (_trajectory is InstantTrajectory) return;
|
||||
|
||||
_trajectory.Init(_positionStart, _positionEnd);
|
||||
var pointsNeeded = Mathf.FloorToInt(_trajectory.trajectoryDistance / _projectileStep.speed / Time.fixedDeltaTime);
|
||||
var points = _trajectory.GetPositions(pointsNeeded);
|
||||
|
||||
// Predict collision on trajectory
|
||||
points = GetPointsToFirstCollision(points);
|
||||
|
||||
_lineRenderer.positionCount = points.Length;
|
||||
_lineRenderer.SetPositions(points);
|
||||
_lineRenderer.enabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="ProjectileCollisionType"/> specified then try to detect collision on trajectory.
|
||||
/// </summary>
|
||||
private Vector3[] GetPointsToFirstCollision(Vector3[] points)
|
||||
{
|
||||
int layerMask;
|
||||
switch (_projectileStep.collisionType)
|
||||
{
|
||||
case ProjectileCollisionType.StaticOrDynamicCollision:
|
||||
layerMask = LayerManager.Static | LayerManager.StaticHideable | LayerManager.Dynamic;
|
||||
break;
|
||||
case ProjectileCollisionType.StaticCollision:
|
||||
layerMask = LayerManager.Static | LayerManager.StaticHideable;
|
||||
break;
|
||||
default:
|
||||
return points;
|
||||
}
|
||||
|
||||
// To prevent hitting caster and ground under caster ignore everything in caster's radius
|
||||
var skip = 0;
|
||||
if (_projectileStep.startFrom is ProjectileStepStartFrom.FromCaster)
|
||||
skip = Array.FindIndex(points, point => Vector3.Distance(point, _positionStart) >= _caster.basicData.radius);
|
||||
if (skip == - 1) skip = 0;
|
||||
|
||||
// Check if projectile will hit anything on its trajeactory (also prevent hitting caster)
|
||||
var collisionIndex = Array.FindIndex(points, skip, point =>
|
||||
Physics.OverlapSphere(point, _projectileStep.radius, layerMask)
|
||||
.Any(collider => collider.GetComponentInParent<UnitObject>() != _caster));
|
||||
if (collisionIndex == - 1) collisionIndex = points.Length - 1;
|
||||
|
||||
_collisionOnPosition = points[collisionIndex];
|
||||
return points.Take(collisionIndex + 1).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates whole <see cref="AbstractAreaOfEffect"/>
|
||||
/// </summary>
|
||||
private void UpdateAreaOfEffect()
|
||||
{
|
||||
if (_projectileStep.areaOfEffect is NoneAreaOfEffect) return;
|
||||
|
||||
_visualizerGameObject.transform.position = _collisionOnPosition != default ? _collisionOnPosition : _positionEnd;
|
||||
_visualizerGameObject.transform.rotation = _projectileStep.areaOfEffect.GetRotation(_positionStart, _positionEnd);
|
||||
_meshRenderer.enabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns start position for current step by defined <see cref="ProjectileStepStartFrom"/>.
|
||||
/// </summary>
|
||||
private Transform FindStartTransform(ProjectileStep step)
|
||||
{
|
||||
return step.startFrom switch
|
||||
{
|
||||
ProjectileStepStartFrom.FromStart => _caster.unitData.castPosition,
|
||||
ProjectileStepStartFrom.FromCaster => _caster.transform,
|
||||
ProjectileStepStartFrom.FromHit => _caster.unitData.castPosition,
|
||||
_ => throw new ArgumentOutOfRangeException(
|
||||
nameof(step.startFrom), step.startFrom, null)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user