Hi guys,
If I have a disk object divided into 10 radial polygons, how can I detect which of those polygons has been hit by a thrown rigidbody object? It’s not a dart board, but something along those lines. The principle would be the same.
I’ve read the scripting reference on collisions and am still confused about how to get and use contact point information. The examples there aren’t very specific.
As always, thanks for the help.
If I were to do so, I’d add a tag to a GameObject with Rigidbody (let’s say “Body”). Then add similar code to each piece of your disk (it must consist of gameObjects within parent one) and change var dependingly on how many points each piece represents:
var pieceOfDiskPoints = 10;
function OnCollisionEnter (collision : Collision)
{
if(collision.collider.tag == "body"){
//Here You can use a function to e.g. change material for highlight effect of hit piece
print("scored"+pieceOfDiskPoints+"points");
}
}
That would certainly work. I guess my question would be how do you make each polygon into a separate game object with its own collider? I suppose I could use a mesh collider that takes the shape of the polygon (essentially a triangular wedge), but is there a way to divide the entire disk into 10 such game objects? My only other worry would be whether this is an “expensive” way of detecting this kind of collision from a processor perspective.
At first I was thinking that a disk collider would work if I could specify exactly where on the disk the collision took place and then simply use math to figure out if the collision was in a particular sector (wedge of the pie). This is where my knowledge of collision detection falls apart. Just haven’t got my mind around it yet.
Thanks so much for your guidance and quick reply! :lol:
As a general rule it’s better to solve these kinds of things mathematically instead of adding more content.
IME the secret sauce of serious 3D programming is the innocuous-looking Transform.InverseTransformPoint() and [InverseTransformDirection(),too].
Once you transform the collision point from world space into model space, it’s matter of trigonometry to determine which segment was hit – just look at the y value of the euler angles of the vector from model center to the collision point. If your 10 regions are equal size then each segment will cover 36 degrees of this rotation:
Eg:
Vector3 local_vec = transform.InverseTransformPoint(contact.point);
local_vec.y = 0; //? not sure you need this but better safe
float angle = Vector3.Angle(new Vector3(0,0,1), local_vec);
Or you can use the Atan2 function (I think this is right):
float angle = Mathf.Atan2(local_vec.z, local_vec.x)*Mathf.Rad2Deg;
anyhoo, once you get the angle, the sector is easy:
int sector = angle / 36;
I’m going to have to study your script for a while to understand it, but I get the basic idea. I’m fairly new to this so the syntax still throws me. From what I get of it, you’re setting the contact point to 0 in local space, then getting the change in angle from a fixed angle on the disk. Is that about right?
Now, if I wanted a sector to change color on collision, that might be problematic using the euler angle approach since the mesh is all one thing. This isn’t a necessity, but it would be nice to have that option.
I have a lot to work out.
In any event, thank you so much for your help. I wish I had your knowledge of scripting! :roll:
Here’s a tested version:
using UnityEngine;
using System.Collections;
public class TargetController : MonoBehaviour
{
void OnCollisionEnter(Collision collision)
{
ContactPoint contact = collision.contacts[0];
Debug.Log("-----------------------");
Debug.Log("World Contact = "+contact.point);
Vector3 local_vec = transform.InverseTransformPoint(contact.point);
Debug.Log("Local Contact = "+local_vec);
Debug.Log("Atan Angle = "+ Mathf.Atan2(local_vec.y, -local_vec.x)*Mathf.Rad2Deg);
}
}
The model is oriented with target face in the XY plane and the target surface normal vector pointing along the positive Z axis. Note that because the target is pointing toward z I negate the local x coordinate in the Atan2 function.
(this is exactly like a mirror image where x is negated, this makes sense after a couple of Physics classes, LOL).
The Atan2 function returns the angle from the positive X axis on the target. 0 is directly right, 45 is upper right, -45 is lower right, -135 is lower left, etc. If the angle is less than 0 you can always add 360 to it to get the continuous angle from the x axis.
As for changing colors, once you figure out the segment you can just modify the model’s material via scripting, after you create a model with multiple materials.
Also note that this won’t work very well for high resolution very close to the (0,0) point, so hopefully you have a “bullseye” zone there, LOL.
1 Like
Awesome, dude. Thanks. Once again, I’ll have to stare at it for a while to figure out exactly what you’re saying (obviously you’re much better at math than I’ve been or ever will be), but it’ll sink in eventually.
Thanks so much for the help. I’ll give it a try and let you know how I fare (which could take some time) :shock:
ContactPoint contact = collision.contacts[0];
Debug.Log("-----------------------");
Debug.Log("World Contact = "+contact.point);
Vector3 local_vec = transform.InverseTransformPoint(contact.point);
Debug.Log("Local Contact = "+local_vec);
Debug.Log("Atan Angle = "+ Mathf.Atan2(local_vec.y, -local_vec.x)*Mathf.Rad2Deg);
}
}
Much Thanks. Very Helpful indeed.