158 lines
5.0 KiB
C#
158 lines
5.0 KiB
C#
// Projectile2D.cs
|
|
using System.Collections;
|
|
using UnityEngine;
|
|
|
|
namespace Projectiles2D
|
|
{
|
|
public interface IDamageable2D
|
|
{
|
|
void ApplyDamage(float amount, Vector2 hitPoint, Vector2 force, GameObject source);
|
|
}
|
|
|
|
[RequireComponent(typeof(Rigidbody2D))]
|
|
[RequireComponent(typeof(Collider2D))]
|
|
public abstract class Projectile2D : MonoBehaviour
|
|
{
|
|
public float initialSpeed = 15f;
|
|
|
|
public float maxLifetime = 5f;
|
|
|
|
public bool alignRotationToVelocity = true;
|
|
|
|
protected float damage = 10f;
|
|
|
|
public float knockback = 0f;
|
|
|
|
public LayerMask hitLayers = ~0;
|
|
|
|
public int maxPierce = 0;
|
|
|
|
public bool useGravity = false;
|
|
|
|
public float gravityScale = 1f;
|
|
|
|
protected Rigidbody2D rb;
|
|
protected Collider2D col;
|
|
|
|
protected int remainingPierce;
|
|
protected bool launched;
|
|
public GameObject owner;
|
|
|
|
protected virtual void Awake()
|
|
{
|
|
rb = GetComponent<Rigidbody2D>();
|
|
col = GetComponent<Collider2D>();
|
|
|
|
// Базовые настройки физики
|
|
rb.bodyType = RigidbodyType2D.Dynamic;
|
|
rb.gravityScale = useGravity ? gravityScale : 0f;
|
|
rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
|
|
col.isTrigger = false;
|
|
}
|
|
|
|
protected virtual void OnEnable()
|
|
{
|
|
remainingPierce = maxPierce;
|
|
//if (maxLifetime > 0f)
|
|
// Invoke(nameof(Despawn), maxLifetime);
|
|
}
|
|
|
|
public virtual void Launch(Vector2 direction, float speed = -1f, GameObject owner = null, float ignoreOwnerTime = 0.1f)
|
|
{
|
|
this.owner = owner;
|
|
launched = true;
|
|
|
|
float finalSpeed = (speed > 0f) ? speed : initialSpeed;
|
|
rb.linearVelocity = direction.normalized * finalSpeed;
|
|
|
|
if (owner != null && ignoreOwnerTime > 0f)
|
|
StartCoroutine(IgnoreOwnerTemporarily(owner, ignoreOwnerTime));
|
|
}
|
|
|
|
protected virtual IEnumerator IgnoreOwnerTemporarily(GameObject who, float time)
|
|
{
|
|
var ownerColliders = who.GetComponentsInChildren<Collider2D>();
|
|
foreach (var oc in ownerColliders)
|
|
if (oc && col) Physics2D.IgnoreCollision(col, oc, true);
|
|
|
|
yield return new WaitForSeconds(time);
|
|
|
|
foreach (var oc in ownerColliders)
|
|
if (oc && col) Physics2D.IgnoreCollision(col, oc, false);
|
|
}
|
|
|
|
protected virtual void FixedUpdate()
|
|
{
|
|
if (!launched) return;
|
|
|
|
if (alignRotationToVelocity && rb.linearVelocity.sqrMagnitude > 0.0001f)
|
|
{
|
|
float angle = Mathf.Atan2(rb.linearVelocity.y, rb.linearVelocity.x) * Mathf.Rad2Deg;
|
|
rb.SetRotation(angle);
|
|
}
|
|
}
|
|
|
|
protected virtual void OnCollisionEnter2D(Collision2D collision)
|
|
{
|
|
var other = collision.collider;
|
|
if (!IsInHitLayer(other.gameObject.layer)) return;
|
|
|
|
Vector2 point = collision.GetContact(0).point;
|
|
HandleHit(other, point, collision.relativeVelocity, isTriggerEvent: false, collision);
|
|
}
|
|
|
|
protected virtual void OnTriggerEnter2D(Collider2D other)
|
|
{
|
|
if (!col.isTrigger) return;
|
|
if (!IsInHitLayer(other.gameObject.layer)) return;
|
|
|
|
Vector2 point = other.ClosestPoint(transform.position);
|
|
HandleHit(other, point, rb.linearVelocity, isTriggerEvent: true, collision: null);
|
|
}
|
|
|
|
protected bool IsInHitLayer(int layer)
|
|
{
|
|
return ((1 << layer) & hitLayers.value) != 0;
|
|
}
|
|
|
|
protected void HandleHit(Collider2D target, Vector2 point, Vector2 relativeVelocity, bool isTriggerEvent, Collision2D collision)
|
|
{
|
|
TryDealDamage(target, point, relativeVelocity);
|
|
|
|
OnHit(target, point, relativeVelocity);
|
|
|
|
if (remainingPierce > 0)
|
|
{
|
|
remainingPierce--;
|
|
return;
|
|
}
|
|
|
|
if (isTriggerEvent) OnImpactFinal(target);
|
|
else OnImpactFinal(collision);
|
|
}
|
|
|
|
protected void TryDealDamage(Collider2D target, Vector2 hitPoint, Vector2 relativeVelocity)
|
|
{
|
|
var dmg = target.GetComponentInParent<IDamageable2D>();
|
|
if (dmg != null)
|
|
{
|
|
Vector2 force = (rb != null && rb.linearVelocity.sqrMagnitude > 0.001f)
|
|
? rb.linearVelocity.normalized * knockback
|
|
: Vector2.zero;
|
|
dmg.ApplyDamage(damage, hitPoint, force, owner ?? gameObject);
|
|
}
|
|
}
|
|
|
|
protected virtual void OnHit(Collider2D target, Vector2 point, Vector2 relativeVelocity) { }
|
|
|
|
protected virtual void OnImpactFinal(Collision2D collision) => Despawn();
|
|
|
|
protected virtual void OnImpactFinal(Collider2D other) => Despawn();
|
|
|
|
public virtual void Despawn()
|
|
{
|
|
Destroy(gameObject);
|
|
}
|
|
}
|
|
}
|