-
-
Save iamtanmay/a36762127f250f18b59fa6fe5c44ca04 to your computer and use it in GitHub Desktop.
Piercing Arrow in Unity
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
public class ArrowTipTrigger : MonoBehaviour | |
{ | |
void OnTriggerEnter(Collider other) | |
{ | |
SendMessageUpwards("OnArrowTipTriggerEnter", other, SendMessageOptions.RequireReceiver); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
public class PiercingArrow : MonoBehaviour | |
{ | |
public enum ArrowState | |
{ | |
Flying, | |
Penetrating, // After body hits, before creating joint | |
Embedded | |
} | |
// Configuration | |
public float pierceAngleTolerance = 30f; | |
public float minImpactVelocity = 5f; | |
public float maxRelativeVelocityForJoint = 0.5f; | |
public float penetrationDampingFactor = 5f; | |
public float rigidbodyVelDampingOnPeneteration = .75f; | |
public Collider[] tipColliders; | |
public CapsuleCollider bodyCollider; | |
public Collider arrowTipTriggerCol; | |
public bool debug = false; | |
// Runtime state | |
public ArrowState currentState = ArrowState.Flying; | |
private Vector3 penetrationDirection; | |
// Cached components | |
private Rigidbody arrowRigidbody; | |
private ArticulationBody targetArticulationBody; | |
private void Awake() | |
{ | |
arrowRigidbody = GetComponent<Rigidbody>(); | |
} | |
private void HandlePenetrationSlowdown() | |
{ | |
// Project current velocity onto penetration direction | |
Vector3 velocityAlongPenetration = Vector3.Project(arrowRigidbody.linearVelocity, penetrationDirection); | |
Vector3 velocityPerpendicular = arrowRigidbody.linearVelocity - velocityAlongPenetration; | |
// Apply damping only to the penetration component | |
Vector3 dampedVelocityAlongPenetration = velocityAlongPenetration / (1f + Time.fixedDeltaTime * penetrationDampingFactor); | |
// Calculate and apply impulse based on the change in velocity | |
Vector3 velocityChange = dampedVelocityAlongPenetration - velocityAlongPenetration; | |
Vector3 impulse = arrowRigidbody.mass * velocityChange; | |
targetArticulationBody.AddForceAtPosition(impulse, arrowTipTriggerCol.transform.position, ForceMode.Impulse); | |
// Recombine velocities and apply | |
arrowRigidbody.linearVelocity = velocityAlongPenetration + velocityPerpendicular; | |
} | |
void OnArrowTipTriggerEnter(Collider other) // Called via SendMessageUpwards on arrow tip trigger collider | |
{ | |
if (debug) | |
Debug.Log($"OnArrowTipTriggerEnter: {other.gameObject.name}"); | |
if (currentState != ArrowState.Flying) | |
return; | |
if // IS NOT AN OBJECT YOU WANT TO PENETRATE | |
return; | |
Vector3 relativeVelocity = arrowRigidbody.linearVelocity - other.attachedArticulationBody.linearVelocity; | |
if (relativeVelocity.magnitude < minImpactVelocity) | |
{ | |
if (debug) | |
Debug.Log($"Penetration rejected: velocity {relativeVelocity.magnitude} < {minImpactVelocity}"); | |
return; | |
} | |
float impactAngle = _getPiercingAngle(arrowTipTriggerCol, other); | |
if (impactAngle > pierceAngleTolerance) | |
{ | |
if (debug) | |
Debug.Log($"Penetration rejected: angle {impactAngle} > {pierceAngleTolerance}"); | |
return; | |
} | |
if (debug) | |
Debug.Log($"Penetration accepted! collision.articulationBody: {other.attachedArticulationBody}"); | |
targetArticulationBody = other.attachedArticulationBody; | |
DisableCollisionsWithTarget(other); | |
currentState = ArrowState.Penetrating; | |
penetrationDirection = transform.forward; | |
arrowRigidbody.linearVelocity *= (1 - rigidbodyVelDampingOnPeneteration); | |
Vector3 impulse = arrowRigidbody.mass * arrowRigidbody.linearVelocity; | |
targetArticulationBody.AddForceAtPosition(impulse, arrowTipTriggerCol.transform.position, ForceMode.Impulse); | |
} | |
private float _getPiercingAngle(Collider triggerCollider, Collider otherCollider) | |
{ | |
if (Physics.ComputePenetration( | |
triggerCollider, triggerCollider.transform.position, triggerCollider.transform.rotation, | |
otherCollider, otherCollider.transform.position, otherCollider.transform.rotation, | |
out Vector3 direction, out float distance)) | |
{ | |
// direction is pointing from triggerCollider to otherCollider | |
// We want angle between arrow's forward and the negative of this direction | |
return Vector3.Angle(transform.forward, -direction); | |
} | |
return 180f; // No penetration detected | |
} | |
void OnTriggerStay(Collider col) | |
{ | |
if (currentState == ArrowState.Penetrating && col.attachedArticulationBody != null && col.attachedArticulationBody == targetArticulationBody) | |
{ | |
HandlePenetrationSlowdown(); | |
float relativeVelocity = (targetArticulationBody.linearVelocity - arrowRigidbody.linearVelocity).magnitude; | |
if (debug) | |
Debug.Log($"Penetrating - RelativeVelocity: {relativeVelocity}, Target: {maxRelativeVelocityForJoint}"); | |
if (relativeVelocity <= maxRelativeVelocityForJoint) | |
{ | |
if (debug) | |
Debug.Log("Velocity check passed, creating joint"); | |
CreateFixedJoint(); | |
currentState = ArrowState.Embedded; | |
} | |
} | |
} | |
void OnTriggerExit(Collider col) | |
{ | |
if (col.attachedArticulationBody == targetArticulationBody) | |
{ | |
currentState = ArrowState.Flying; | |
} | |
} | |
private void DisableCollisionsWithTarget(Collider targetCollider) | |
{ | |
foreach (Collider tipCollider in tipColliders) | |
{ | |
Physics.IgnoreCollision(tipCollider, targetCollider); | |
} | |
Physics.IgnoreCollision(bodyCollider, targetCollider); | |
} | |
private void CreateFixedJoint() | |
{ | |
FixedJoint joint = gameObject.AddComponent<FixedJoint>(); | |
joint.connectedArticulationBody = targetArticulationBody; | |
joint.breakForce = Mathf.Infinity; | |
joint.breakTorque = Mathf.Infinity; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment