3
\$\begingroup\$

I would like to trigger an event each time my rigidbody2D is at a particular value.

The event should only be triggered once for each given value per complete object revolution.

Rotating Object - Yellow circle indicates current rotation

Initial attempts at achieving this have resulted in the following method which checks against an array of values, logging an event and incrementing an index when the value has been passed.

static readonly float[] angles = new float[] { 90f, 180f }; int angleIndex = 0; void CheckForTrigger() { if (rigidbody2D.IsSleeping()) return; var rotation = Mathf.Abs(rigidbody2D.rotation); if (rotation % 360f >= angles[angleIndex]) { Debug.Log("Fire"); angleIndex++; if (angleIndex== angles.Length) angleIndex= 0; } } 

This suffers from a couple of issues:

  • Floating point inaccuracy means that values close to 360f will often be missed (e.g. 359f)
  • Fire will be logged each frame between the last and first value (i.e. between 180f and 90f)

Does anyone have any advice for approaching this problem and improving the above method?

\$\endgroup\$
1
  • \$\begingroup\$ One fundamental flaw of this approach is that an object can pass through both angles without ever satisfying your check. There's no simple way to deal with that. Can you give more details on exactly are you trying to do? \$\endgroup\$ Commented Aug 16, 2014 at 5:13

1 Answer 1

1
\$\begingroup\$

This is somewhat long and might not work well if they are rotating faster than the deadzone value, but then you can just increase the deadzone. I've commented the below code: //declare new array of the dataclass we made below

static AngleData[] angleDatas = new AngleData[] { new AngleData(90f), new AngleData(180f) }; int angleIndex = 0; //the deadzone is how close it has to be to the target angle to fire, and how far it needs to be to 'reset itself' float deadzone = 15f; void CheckForTrigger() { if (rigidbody2D.IsSleeping()) return; var rotation = Mathf.Abs(rigidbody2D.rotation) % 360f; //we get the current AngleData AngleData currentAngle = angleDatas[angleIndex]; //find the absolute difference from the target to the current rotation float difference = Mathf.Abs(rotation - currentAngle.angle); //if we haven't 'used up' this angle for this revolution if (currentAngle.available) { //if it's within the deadzone if (difference < deadzone) { Debug.Log("Fire:" + currentAngle.angle); currentAngle.available = false; } } else { //if it's outside the deadzone if (difference > deadzone) { currentAngle.available = true; //move on to next angle in list angleIndex = (angleIndex + 1) % angleDatas.Length; } } } public class AngleData { public float angle; public bool available = true; //could have an event declared here if you want specific event methods to be fired for every different angle public AngleData(float angle) { this.angle = angle; } } 

Let me know if you have any questions or if it doesn't work as intended.

\$\endgroup\$
3
  • \$\begingroup\$ Thank you for your response. It works but it is not accurate enough when increasing the angular velocity. This is still true when increasing the deadzone. I need to rethink my approach, perhaps a delta rotation approach will be more accurate? \$\endgroup\$ Commented Aug 3, 2014 at 11:00
  • \$\begingroup\$ Is this because of how fast the angular velocity is reaching? What would be some typical output when printing out the value of rotation every frame? Also, are you looking for your algorithm to be dynamic (to handle any set of angles you specify) or only for 90 and 180? \$\endgroup\$ Commented Aug 3, 2014 at 14:21
  • \$\begingroup\$ The algorithm will need be dynamic. I am thinking that I can accumulate rotations from delta orientations each frame, which can be calculated from Atan2 or Quaternions. \$\endgroup\$ Commented Aug 3, 2014 at 15:31

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.