- Home /
Collision detection for Arrows and Melee. How to properly setup physics/collision?
I'm building a game that involves shooting arrows (which stick to the object hit) and swinging melee weapons like swords/axes.
I've tried many different combinations of colliders trigger/non, rigidbodies kinematic/non. I've tried adding a rigid body for every collider, I've tried adding just 1 parent rigid body for each NPC with compound colliders on the mesh children beneath which are animated.
Each way I have tried this had some draw back or didn't work quite right. For example, when using just triggers my arrows will stop not where they originally should of hit, but some where farther probably where the arrow was when the frame started that detected the trigger.
When using colliders (non trigger) I found that only non-kinematic rigidbodies will fire collisions, so then I get very strange behavior with my swords swing animations mixing with physics ( they fly around ).
So what is the proper setup for physics of arrows and melee weapons such as swords?
Answer by Cherno · Mar 08, 2015 at 09:55 PM
For accurate collision detection with fast-moving objects like your arrows, you need to customize a bit:
public LayerMask layerMask;
private bool impacted = false;
private float skinWidth = 0.1f; //probably doesn't need to be changed
private float minimumExtent;
private float partialExtent;
private float sqrMinimumExtent;
private Vector3 previousPosition;
private Rigidbody myRigidbody;
private Vector3 lastPosition;
void Awake() {
myRigidbody = GetComponent<Rigidbody>();
previousPosition = myRigidbody.position;
minimumExtent = Mathf.Min(Mathf.Min(GetComponent<Collider>().bounds.extents.x, GetComponent<Collider>().bounds.extents.y), GetComponent<Collider>().bounds.extents.z);
partialExtent = minimumExtent * (1.0f - skinWidth);
sqrMinimumExtent = minimumExtent * minimumExtent;
}
void FixedUpdate() {
Vector3 direction = transform.position - lastPosition;
Ray ray = new Ray(lastPosition, direction);
RaycastHit hit = new RaycastHit();
if (Physics.Raycast(ray, out hit, direction.magnitude, layerMask)) {
Impact(hit.point, hit.normal, hit.collider.gameObject);
}
this.lastPosition = transform.position;
}
}
void OnCollisionEnter(Collision collision) {
if(impacted == true) {
return;
}
Impact(collision.contacts[0].point, collision.contacts[0].normal, collision.collider.gameObject);
impacted = true;
}
void Impact(Vector3 pos, Vector3 normal, GameObject hitObject) {
//Apply damage to the hit gameObject, or whatever
Destroy(gameObject);
}
Kinematic rigidbodies indeed don't reigster collisions. If you want to make a rigidbody register a collision with another collider without making it react to physics, set the other collider to be IsTrigger = true. You can also use things like SphereCastAll to hit multiple enemies in front of you when you swing your sword, or just check if there's actually one.
What is this Impact() function you're calling? LOL nvm
Answer by JannickL · Feb 22, 2018 at 05:37 PM
Hey there, this works flawless for me (pretty simple but effective):
Actually i'm sending a ray from the arrows position to its forward vector to check if he hits something. However you should only use that code for a skyrim like archery system (sending the ray costs some performance).
using UnityEngine;
public class Arrow : MonoBehaviour {
public Transform tipTransform;
public GameObject _explosionEffect;
private AudioSource _audioSource;
private Rigidbody _rb;
private void Awake() {
_rb = GetComponent<Rigidbody>();
_audioSource = GetComponent<AudioSource>();
// Set the center of mass to the arrows tip to make the arrow flight realistic
_rb.centerOfMass = tipTransform.localPosition;
Destroy(gameObject, 5f); // Make sure arrow gets always destroyed
}
private void FixedUpdate() {
RaycastHit hit;
// Send raycast from arrows position to is forward vector to detect if something gets hit
if(Physics.Raycast(_rb.position, _rb.transform.forward, out hit, 2f))
Explosion(hit.collider, hit.point);
}
private void Explosion(Collider other, Vector3 point) {
Debug.Log("Arrow hitted: " + other.transform.name);
// Spawn an explosion effect
Destroy(Instantiate(_explosionEffect, point, Quaternion.identity), 2f);
Destroy(gameObject);
}
}
Greets
Answer by ifisch · Mar 08, 2015 at 08:07 PM
This might help http://docs.unity3d.com/Manual/CollidersOverview.html