# Detecting 360-Degree Rotation

Get help using Construct 2

### » Wed Feb 04, 2015 12:17 pm

Hi there! I'm trying to have an action trigger whenever an object rotates 360 degrees in 2 seconds.

Simple enough, I imagined, just store the current angle for 2 seconds and check whether it has increased or decreased by 360 compared to the oldest stored value every tick. However, the Sprite.Angle function resets the value back to 0 after reaching 360, instead of continuing as the Angle value does in the Debug Mode.

I failed to find a way to get the object's 'absolute' angle, as in, if they had started at zero and rotated 360 degrees clockwise twice, it would return '720'. Is there one? If not, has anyone succeeded at detecting when an object rotates 360 degrees - regardless of the initial position - in another fashion?

B
7
Posts: 11
Reputation: 297

### » Wed Feb 04, 2015 1:05 pm

Hey gwerneck,

(Edit) I just checked the System >> "angleDiff" expression, and it always gives a zero or positive answer, meaning it doesn't give you the direction of the change in the angle, so it won't work here.

One possible approach is to calculate the change in angle between ticks,
store that change in a running total,
and track that running total instead of tracking the objects rotation property directly.

Here is a formula that will give you the change in angle as the shortest CW or CCW rotation, and it works seamlessly across the 360-to-0 transition.
angle_delta = ( ( ( a - b ) + 180 ) - floor( ( ( a - b ) + 180 ) / 360 ) * 360 ) - 180
Where a and b are your two angles.

e.g.
a = 5, b = 355: ... ( ( ( 5 - 355 ) + 180 ) - floor( ( ( 5 - 355 ) + 180 ) / 360 ) * 360 ) - 180 355[/u][/b]: ... = 10
a = 355, b = 5: ... ( ( ( 355 - 5 ) + 180 ) - floor( ( ( 355 - 5 ) + 180 ) / 360 ) * 360 ) - 180 355[/u][/b]: ... = -10

Remember, if you compare two angles with a difference greater than 180 degrees, the angles will be treated as a shorter rotation in the opposite direction.
e.g. A raw difference of +270 is treated as -90.
As long as the total distance rotated per tick is less than 180 degrees that shouldn't be a problem though.

So, to use this in place of directly tracking the object's angle property, you can do the following:
Create a custom variable "unwrapped_angle".
Every tick, get the change in angle between ticks, angle_delta( currentAngle , angleRecordedLastTick ), and add it to "unwrapped_angle".
Then you should be able to use the unwrapped_angle in place of the objects built-in angle property.
Last edited by fisholith on Sat Nov 21, 2015 2:37 am, edited 4 times in total.
B
21
S
13
G
8
Posts: 306
Reputation: 5,458

### » Wed Feb 04, 2015 11:43 pm

Thanks, fisholith! The mathematics behind your formula are somewhat beyond me, but it works flawlessly.

Inspired by your well-written, color-coded explanation, I made a simple commented project that uses your method, in case anyone stumbles here in search of the same thing I was looking for. Not very clean and surely not the best way to apply it, as I'm not very familiar with this stuff yet, but it should be enough for anyone who wishes to achieve this effect.

Thanks again!
You do not have the required permissions to view the files attached to this post.
B
7
Posts: 11
Reputation: 297

### » Thu Feb 05, 2015 3:39 pm

I built an example as well, though it only shows the process of calculating the unwrapped angle.
I'm afraid it doesn't apply it to a gloriously cucumber rotating cause.
fi_demo_TrackUnwrapedAngle_v02.capx

I checked out the cucumber example. Very cool, as one might expect a cucumber to be.

Using System > Wait to time travel
One way to compare the current unwrapedAngle against the 2 seconds old unwrapedAngle is to:
• Create a custom variable "unwrapedAngleOld".
• Every tick: System > Wait 2 seconds; and Set unwrapedAngleOld to the current unwrapedAngl value,
That should make unwrapedAngleOld continuously play back the unwrapedAngle values from 2 seconds ago.
I have no idea how optimal this is, but I've never seen a performance hit. Granted I'm using a desktop, and it might be a different story on a mobile device.

Angle delta math
As for the mathematics behind the angle_delta formula from my previous post, I can hopefully simplify it a bit.
In my prior post I wrote it out as:
angle_delta = ( ( ( a - b ) + 180 ) - floor( ( ( a - b ) + 180 ) / 360 ) * 360 ) - 180

The reason it looks so awful is that the formula contains a mod() function, which I've written out long hand.
Here is the formula as it appears if you have an appropriate mod() function available:
mod( ( a , b ) + 180 , 360 ) - 180
Unfortunatly you don't have the appropriate mod() function in C2 by default, and that's why I wrote it out in expanded ugly form.
(Further explained in "C2's modulus" section below.)

That mod formula I'm using above has the following behavior:
mod( val , div ) ... = ... val - ( floor( val / div ) * div )
This is a "floored division" style mod.

Terrible as it might sound, there are a few different versions of the mod formula, all called the same thing, but all slightly different.
There's a nice chart showing mod variations on the modulus wiki page.

C2's modulus
Now, C2 does provide a mod function in the form of the modulus operator "%", and this is essentially just JavaScript's modulus operator.
Unfortunately not all mod functions work the same way, and JavaScript's built-in mod is a variety that behaves differently depending on whether the val number is positive or negative.
This will not work for our purposes because we want consistent behavior for both negative and positive numbers. That means JavaScript's mod, and by extension C2's mod can't be used here.
(I'm actually building my own math utility plugin right now, and I was initially inspired by the lack of a "floored division" mod.)

Getting "floored division" mod into C2
Granted the formula does look a lot nicer with the mod(), and it would be nice to be able to use it in C2.
There is a way, and I show it in the example capx I attached.

You can use C2's Function object to create a custom "floored division" style mod function.
• Create an "On function" event with the function name "mod".
• Add the action "Set return value", and in the expression, use the "floored division" mod formula I described above.
• Within the expression you'll need to use Function.Param(0) and Function.Param(1) to get the arguments.
(Or you'll need to do what I did in the example capx: Store the args in local variables, and use those in the expression, which may be easier to read.)
• You can then call this custom mod function from another C2 expression.

Again, you can see this setup in the example capx if you're interested. It does make the angle_delta function much nicer to look at.
Note that I renamed the Function object from "Function" to "oF", which just makes it easier for me to read expressions that involve the function object.

I hope that helps clarify some of the stuff I explained a bit hastily in my prior post.
You do not have the required permissions to view the files attached to this post.
B
21
S
13
G
8
Posts: 306
Reputation: 5,458