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

167 lines
No EOL
6 KiB
C#

using System;
using Tween;
using Unity.Entities;
using UnityEngine;
using Zenject;
namespace DefaultNamespace
{
[UpdateInGroup(typeof(PresentationSystemGroup)), UpdateBefore(typeof(WeaponFiringSystem))]
public class PlayerCoverSystem : InjectableComponentSystem
{
[Serializable]
public class Settings
{
public AnimationCurve AccuracyLossCurve;
[Range(0, 1)] public float AccuracyLossMultiply;
public float CheckDistance;
public float MaxVaultAngle;
public float MaxVaultFromAngle;
public float MinVaultHeight;
public float Offset;
}
[Inject] private readonly Settings settings;
[Inject] private TweenSystem tweenSystem;
protected override void OnSystemUpdate()
{
Entities.WithAllReadOnly<RigidBody2DData, ActorData>()
.ForEach(
(
Entity entity,
ActorBodyParts parts,
ref ActorCoverRaycastData raycast,
ref ActorWeaponReferenceData weapon,
ref PlayerInput input,
ref Rotation2D rotation,
ref ActorBoundsData bounds) =>
{
var actor = EntityManager.GetComponentData<ActorData>(entity);
var rigidbody = EntityManager.GetComponentData<RigidBody2DData>(entity);
var weaponAccuracy = EntityManager.GetComponentData<WeaponAccuracyData>(weapon.Weapon);
var height = Mathf.Clamp(
raycast.TopHit.y + settings.Offset - parts.WeaponContainer.transform.position.y,
0,
settings.CheckDistance);
var heightPercent = Mathf.Clamp01(height / settings.CheckDistance);
weaponAccuracy.Accuracy *=
Mathf.Clamp01(1 - settings.AccuracyLossCurve.Evaluate(heightPercent) * settings.AccuracyLossMultiply);
var hasCover = raycast.UpDistance + raycast.TopDistance >= 0.1f && raycast.HadTopHit;
var canVault = raycast.Height >= settings.MinVaultHeight &&
raycast.UpDistance + raycast.TopDistance >= bounds.Rect.height &&
!tweenSystem.HasTween(entity) &&
Vector2.Angle(Vector2.up, raycast.TopNormal) <= settings.MaxVaultAngle &&
Vector2.Angle(actor.GroundUp, Vector2.up) <= settings.MaxVaultFromAngle;
var halfWidth = bounds.Rect.width * 0.5f;
var offset = Vector2.right * rotation.Axis * halfWidth;
var topRight = new Vector2(raycast.ForwardHit.x, raycast.TopHit.y);
PostUpdateCommands.KeepData(EntityManager, hasCover, entity, new PlayerCoverData());
PostUpdateCommands.KeepData(EntityManager, canVault, entity, new PlayerVaultData { VaultPoint = topRight });
if (input.Vault && canVault)
{
topRight += offset;
var topLeft = raycast.TopHit;
var bottomRight = new Vector2(raycast.ForwardHit.x, raycast.TopHit.y - raycast.Height) + offset;
var playerPos = rigidbody.Position;
var path = Vector2.Distance(playerPos, bottomRight) >= halfWidth
? new[] { playerPos, bottomRight, topRight, topLeft }
: new[] { bottomRight, topRight, topLeft };
PostUpdateCommands.StartTween(entity, 0.6f, EaseType.easeInOutQuart, TweenFollowPath.Build(path, Space.World));
}
PostUpdateCommands.SetComponent(weapon.Weapon, weaponAccuracy);
});
}
}
[UpdateInGroup(typeof(PresentationSystemGroup)), UpdateBefore(typeof(PlayerWeaponSystem)), UpdateBefore(typeof(ActorIkSystem))]
public class PlayerCoverSystemWeaponAdjust : InjectableComponentSystem
{
[Inject] private readonly PlayerCoverSystem.Settings settings;
protected override void OnSystemUpdate()
{
Entities.ForEach(
(ActorBodyParts parts, ref ActorCoverRaycastData raycast, ref PlayerCoverData coverData) =>
{
var height = Mathf.Clamp(
raycast.TopHit.y + settings.Offset - parts.WeaponContainer.transform.position.y,
0,
settings.CheckDistance);
parts.WeaponContainer.transform.position += new Vector3(0, height, 0);
coverData.WeaponOffset = height;
});
}
}
[UpdateInGroup(typeof(SimulationSystemGroup))]
public class PlayerCoverSystemRaycast : InjectableComponentSystem
{
[Inject] private readonly PlayerCoverSystem.Settings settings;
protected override void OnSystemUpdate()
{
Entities.WithAllReadOnly<ActorBodyParts>()
.WithNone<ActorCoverRaycastData>()
.ForEach(entity => { PostUpdateCommands.AddComponent(entity, new ActorCoverRaycastData()); });
Entities.ForEach(
(ref ActorCoverRaycastData raycast, ref Rotation2D rotation, ref ActorBoundsData bounds) =>
{
var boundsSize = bounds.Rect.size;
var boundsCenter = bounds.Rect.center;
var forward = boundsCenter + Vector2.left * rotation.Axis * 0.5f + Vector2.up * boundsSize.y * 0.5f;
var bottomHit = Physics2D.BoxCast(
forward,
new Vector2(boundsSize.x, 0.05f),
0,
Vector2.down,
boundsSize.y,
LayerMask.GetMask("Ground"));
var forwardHit = Physics2D.BoxCast(
boundsCenter,
boundsSize * 0.9f,
0,
Vector2.left * rotation.Axis,
1,
LayerMask.GetMask("Ground"));
var topHit = Physics2D.BoxCast(
forward,
new Vector2(boundsSize.x, 0.05f),
0,
Vector2.up,
boundsSize.y,
LayerMask.GetMask("Ground"));
var hasBottom = bottomHit.collider != null;
var hasForward = forwardHit.collider != null;
var hitDistance = hasBottom ? bottomHit.distance + 0.05f : boundsSize.y;
var height = Mathf.Max(boundsSize.y - hitDistance, 0);
raycast.Height = height;
raycast.TopHit = hasBottom ? bottomHit.point : forward + Vector2.down * boundsSize.y;
raycast.TopNormal = hasBottom ? bottomHit.normal : Vector2.up;
raycast.HadTopHit = hasBottom;
raycast.TopDistance = hasBottom ? bottomHit.distance : boundsSize.y;
raycast.ForwardDistance = hasForward ? forwardHit.distance : 1;
raycast.ForwardHit = hasForward ? forwardHit.point : boundsCenter + Vector2.left * rotation.Axis;
raycast.ForwardNormal = hasForward ? forwardHit.normal : Vector2.right * rotation.Axis;
raycast.HadForwardHit = hasForward;
raycast.UpDistance = topHit.collider != null ? topHit.distance : boundsSize.y;
});
}
}
}