2D-Platformer/Assets/Scripts/Systems/ProjectileCastSystem.cs
2022-02-12 12:53:50 +02:00

151 lines
No EOL
4.8 KiB
C#

using System;
using System.Collections.Generic;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
using Zenject;
using Random = UnityEngine.Random;
namespace DefaultNamespace
{
[UpdateInGroup(typeof(SimulationSystemGroup))]
public class ProjectileCastSystem : InjectableComponentSystem, IComparer<RaycastHit2D>
{
[Serializable]
public class Settings
{
public ParticleSystem BloodHitParticles;
public SoundLibrary BloodHitSounds;
public SoundLibrary HitSounds;
public float HitSpatialBlend;
public float MaxRicochetAngle;
public ParticleSystem ProjectileHitParticles;
public SoundLibrary RicochetSounds;
public float RicochetSpatialBlend;
}
[Inject] private readonly Settings settings;
[Inject] private readonly SoundManager soundManager;
private int enemyLayer;
private int playerLayer;
private readonly RaycastHit2D[] raycasts = new RaycastHit2D[8];
public int Compare(RaycastHit2D x, RaycastHit2D y)
{
return x.distance.CompareTo(y.distance);
}
protected override void OnCreate()
{
enemyLayer = LayerMask.NameToLayer("Enemies");
playerLayer = LayerMask.NameToLayer("Player");
}
protected override void OnSystemUpdate()
{
var deltaTime = Time.fixedDeltaTime;
Entities.WithAll<Rigidbody2D>()
.ForEach(
(Entity projectileEntity, ProjectileSharedData shared, ref ProjectileData projectile) =>
{
var rigidbody = EntityManager.GetComponentObject<Rigidbody2D>(projectileEntity);
var speed = math.length(projectile.Velocity);
var moveDelta = speed;
var dir = projectile.Velocity / speed;
var filter = new ContactFilter2D { useLayerMask = true, layerMask = projectile.HitMask };
var hitCount = rigidbody.Cast(dir, filter, raycasts, speed * deltaTime);
if (hitCount > 0)
{
Array.Sort(raycasts, 0, hitCount, this);
for (var j = 0; j < hitCount && projectile.Life > 0; j++)
{
var hadHitFlag = true;
var hit = raycasts[j];
if (hit.rigidbody != null)
{
var actorFacade = hit.rigidbody.gameObject.GetComponent<ActorFacade>();
if (actorFacade != null)
{
var entity = actorFacade.Entity;
if (EntityManager.TryGetComponentData<ActorData>(entity, out var actorData))
{
actorData.Health = Mathf.Max(0, actorData.Health - shared.Damage);
if (actorData.Health <= 0)
{
PostUpdateActions.Enqueue(
() =>
{
if (EntityManager.HasComponent<ActorDeathData>(entity))
{
var deathData = EntityManager.GetComponentData<ActorDeathData>(entity);
deathData.Force += shared.Damage;
deathData.Direction = (deathData.Direction - hit.normal).normalized;
EntityManager.SetComponentData(entity, deathData);
}
else
{
EntityManager.AddComponentData(
entity,
new ActorDeathData { Direction = -hit.normal, Force = shared.Damage });
}
});
hadHitFlag = false;
}
EntityManager.SetComponentData(actorFacade.Entity, actorData);
}
}
}
if (hadHitFlag)
{
var hitLayer = hit.transform.gameObject.layer;
if (hitLayer == enemyLayer || hitLayer == playerLayer)
{
projectile.Life = 0;
soundManager.PlayClip(PostUpdateCommands, settings.BloodHitSounds, hit.point);
settings.BloodHitParticles.Emit(
new ParticleSystem.EmitParams
{
position = hit.point, rotation = -Vector2.SignedAngle(Vector2.down, dir)
},
1);
}
else
{
var angle = Vector2.Angle(dir, hit.normal);
if (angle < settings.MaxRicochetAngle && Random.value < shared.RicochetChance)
{
projectile.Velocity = Vector2.Reflect(dir, hit.normal) * speed;
rigidbody.velocity = projectile.Velocity;
soundManager.PlayClip(
PostUpdateCommands,
settings.RicochetSounds,
hit.point,
settings.RicochetSpatialBlend);
}
else
{
projectile.Life = 0;
soundManager.PlayClip(PostUpdateCommands, settings.HitSounds, hit.point, settings.HitSpatialBlend);
}
settings.ProjectileHitParticles.Emit(
new ParticleSystem.EmitParams { position = hit.point, rotation = angle },
1);
}
}
}
}
rigidbody.MovePosition(new float2(rigidbody.position) + dir * moveDelta * deltaTime);
rigidbody.MoveRotation(Vector2.SignedAngle(Vector2.right, dir));
projectile.Life -= deltaTime;
});
}
}
}