swift calculating degree of joystick rotation - swift

I'm working on a spritekit game using swift and I implemented a joystick using this library. I'm having a hard time trying to figure out how to calculate the degree of the rotation. the library gives you this information when you move the joystick around
joystick.trackingHandler = { jData in
// something...
// jData contains angular && velocity (jData.angular, jData.velocity)
}
I don't need to rotate the player since the game is a 4 directional jrpg style, so i'll just be triggering movement based on a range of degrees.
does anybody have any useful articles or information on turning the velocity returned into a degree?
I've seen people using atan2 but it seems to only accept Double's and the velody returned is of type CGPoint.

The angular value in jData contains the angle in radians.
To convert, use the following code for values between -180 and +180:
let degrees = jData.angular * 360 / (2 * M_PI)
and this for values between 0 and +360:
var radians = jData.angular
radians = radians > 0 ? radians : (2 * M_PI + radians)
let degrees = radians * 360 / (2 * M_PI)
adapted from https://stackoverflow.com/a/1311134/968577

Related

Converting radian angle to CGVector

Using Sprite Kit I am trying to set an SKPhysicsBody moving according to a given angle, so for example if you wanted the sprite to travel to the right you would specify 1.571 radians. To turn the specified angle into a velocity I am using the method below to convert radians to a CGVector. The ORIGINAL version that I implemented from memory has the strange effect of offsetting all the angles by 90degrees. (i.e. if 0 degrees is used the sprite moves right (just like it would if you specified 90degrees)
Question:
I have fixed this in the NEW version by swapping the dx and dy assignments. My question is why does this happen, do I have it wrong in the original (there do seem to be others doing it that way on the web) or is there some reason based on the particular coordinate system being used.
// ORIGINAL
- (CGVector)convertAngleToVector:(CGFloat)radians {
CGVector vector;
vector.dx = cos(radians) * 10;
vector.dy = sin(radians) * 10;
NSLog(#"DX: %0.2f DY: %0.2f", vector.dx, vector.dy);
return vector;
}
// NEW, SWAPPED DX & DY
- (CGVector)convertAngleToVector:(CGFloat)radians {
CGVector vector;
vector.dy = cos(radians) * 10;
vector.dx = sin(radians) * 10;
NSLog(#"DX: %0.2f DY: %0.2f", vector.dx, vector.dy);
return vector;
}
NOTE: also in Sprite Kit clockwise rotations are negative, so far convertAngleToVector is doing positive clockwise rotations (i.e. 1.571 radians is right, where it should be left) I could just do cos(radians*-1) and sin(radians*-1) but there might be some underlying reason for this based on me swapping dx and dy.
Sprite Kit (SKView Coordinates):
Yeah, SpriteKit defaults to the right. The Physics Collision sample project solves this by implementing this method:
- (CGFloat)shipOrientation
{
// The ship art is oriented so that it faces the top of the scene, but Sprite Kit's rotation default is to the right.
// This method calculates the ship orientation for use in other calculations.
return self.zRotation + M_PI_2;
}
You can then just get the existing orientation by calling something like:
CGFloat shipDirection = [self shipOrientation];
And then adjust the zRotation property from there.
From the Sprite Kit Programming Guide (emphasis added):
Sprite Kit also has a standard rotation convention. Figure 4-2 shows the polar coordinate convention. An angle of 0 radians specifies the positive x axis. A positive angle is in the counterclockwise direction.
In this coordinate system, an angle of zero radians pointing to the right is correct. If you want to use a system in which a zero angle is straight up (along positive y axis) and increase clockwise, you'll want to transform your angles before converting them to vectors.

CMDeviceMotion yaw values unstable when iPhone is vertical

In a iOS prototype I use a combination of CMDeviceMotion.deviceMotion.yaw and CLHeading.trueHeading to make stable compass heading that is responsive and accurate. This works well when the iPhone is held flat, where I have a graphical arrow that point to a stable compass heading.
The problem appear when the iPhone is held vertical in portait mode. The UIDeviceOrientation constantly changes from UIDeviceOrientationFaceDown to UIDeviceOrientationFaceUp and back. This makes the yaw value to skip back and forth +/-180 degrees based on small changes of the pitch. Is it possible to lock the device to one orientation that gives a stable yaw value, predict the change without glitches or compute the gyro yaw (or roll in this orientation) in other ways?
This poor guy have the same problem, with no answers. Double points possible people! :)
https://stackoverflow.com/questions/10470938/euler-angle-yaw-not-working-when-iphone-orientation-changes
I was just searching for an answer to this problem. It broke my heart a bit to see that you posted this over a year ago, but I figured maybe you or someone else could benefit from the solution.
The issue is gimbal lock. When pitch is about 90 degrees, yaw and roll match up and the gyro loses a degree of freedom. Quaternions are one way of avoiding gimbal lock, but I honestly didn't feel like wrapping my mind around that. Instead, I noticed that yaw and roll actually match up and can simply be summed to to solve the problem (assuming you only care about yaw).
SOLUTION:
float yawDegrees = currentAttitude.yaw * (180.0 / M_PI);
float pitchDegrees = currentAttitude.pitch * (180.0 / M_PI);
float rollDegrees = currentAttitude.roll * (180.0 / M_PI);
double rotationDegrees;
if(rollDegrees < 0 && yawDegrees < 0) // This is the condition where simply
// summing yawDegrees with rollDegrees
// wouldn't work.
// Suppose yaw = -177 and pitch = -165.
// rotationDegrees would then be -342,
// making your rotation angle jump all
// the way around the circle.
{
rotationDegrees = 360 - (-1 * (yawDegrees + rollDegrees));
}
else
{
rotationDegrees = yawDegrees + rollDegrees;
}
// Use rotationDegrees with range 0 - 360 to do whatever you want.
I hope this helps someone else!
If somebody is interested in the implementation in iOS Swift the code is given below:
let queue = NSOperationQueue()
motionManager.startDeviceMotionUpdatesToQueue(queue) {
[weak self] (data: CMDeviceMotion!, error: NSError!) in
var yawDegrees: Double = self!.motionManager.deviceMotion.attitude.yaw * (180.0 / M_PI)
var pitchDegrees: Double = self!.motionManager.deviceMotion.attitude.pitch * (180.0 / M_PI)
var rollDegrees: Double = self!.motionManager.deviceMotion.attitude.roll * (180.0 / M_PI)
if(rollDegrees < 0 && yawDegrees < 0){
self!.rotationDegrees = 360 - (-1 * (yawDegrees + rollDegrees))
}
else {
self!.rotationDegrees = yawDegrees + rollDegrees
}
}
However I am having some problems and I hope #blkhp19 can help me with this because at certain points the angles go into negative values which then messes up the entire calculation and I can't figure out what the problem is.
The problem is a bit confusing because there are at least two different ways to think about Yaw. One is from the phone's perspective, and one from the world perspective.
I'll use this image from Apple to explain further:
If the phone is flat on a table:
Rotations along the phone's yaw (or Z axis): change the compass heading.
Rotations along the phone's roll (or Y axis): do not change compass heading.
Rotations along the phone's pitch (or X axis): do not change compass heading.
If the phone is flat against a wall:
Rotations along the phone's yaw (or Z axis): change the compass heading.
Rotations along the phone's roll (or Y axis): change the compass heading.
Rotations along the phone's pitch (or X axis): do not change compass heading.
For the remainder of this answer, I'll assume the phone is upright and yaw, pitch, and roll refer to exactly what's in the photo above.
Yaw
You'll need to use atan2 and inspect gravity as in this example.
let yaw = -Angle(radians: .pi - atan2(motion.gravity.x, motion.gravity.y))
Pitch
Similar to the above, I primarily just swapped x and z and it seems to be returning the correct values:
let pitch = Angle(radians: .pi - atan2(motion.gravity.z, motion.gravity.y))
Roll (aka Compass Heading)
Use blkhp19's code above which sums up the attitude yaw and roll. If you import SwiftUI, you can leverage the Angle struct to make radian + degrees conversion easier:
func roll(motion: CMDeviceMotion) -> Angle {
let attitudeYaw = Angle(radians: motion.attitude.yaw)
let attitudeRoll = Angle(radians: motion.attitude.roll)
var compassHeading: Angle = attitudeYaw + attitudeRoll
if attitudeRoll.degrees < 0 && attitudeYaw.degrees < 0 {
compassHeading = Angle(degrees: 360 - (-1 * compassHeading.degrees))
}
return compassHeading
}
Also note that if you don't need the actual angle, and all you need is the relationship (e.g. isPhoneUpright), you can simply read gravity values for those.
extension CMDeviceMotion {
var yaw: Angle {
-Angle(radians: .pi - atan2(gravity.x, gravity.y))
}
var pitch: Angle {
Angle(radians: .pi - atan2(gravity.z, gravity.y))
}
var roll: Angle {
let attitudeYaw = Angle(radians: attitude.yaw)
let attitudeRoll = Angle(radians: attitude.roll)
var compassHeading: Angle = attitudeYaw + attitudeRoll
if attitudeRoll.degrees < 0 && attitudeYaw.degrees < 0 {
compassHeading = Angle(degrees: 360 - (-1 * compassHeading.degrees))
}
return compassHeading
}
}

Cant Understand Angle of Inclination Calculation using Accelerometer on iPhone

double = rollingZ = acceleration.x;
double = rollingX = acceleration.y;
if (rollingZ > 0.0) {
self.centerCoordinate.inclination = atan(rollingX / rollingZ) + M_PI / 2.0; //LINE 1
}
else if (rollingZ < 0.0) {
self.centerCoordinate.inclination = atan(rollingX / rollingZ) - M_PI / 2.0; // LINE 2
}
else if (rollingX < 0) {
self.centerCoordinate.inclination = M_PI/2.0; //atan returns a radian
}
else if (rollingX >= 0) {
self.centerCoordinate.inclination = 3 * M_PI/2.0;
Im just trying to fully understand this piece of code. I'm looking to build AR apps on the iphone and this code has the function of calculating the angle of inclination of the device using the accelerometer readings.
My understanding is this:
Assuming a portrait orientation if i roll the device forward the x axis of the accelerometer increases towards a negative number of -1.0 (i.e. the device is laid flat with the screen facing up). If i tilt the device towards me the x axis value increases towards a value of 1.0 (until the device is flat facing the ground).
The y axis changes up and down its axis between -1.0 and 0.0 (0 implies the device is horizontal).
If we take some example readings say x = 0.5 (a -45 degree angle, tilting the device towards me) and y = 0.8. If i plotted this on a cartesian coordinate graph with y (rollingX as the vertical axis) and x (rollingZ as the horizontal) and draw a line between them i understand that i can use the reverse tangent function (atan) to calculate the angle. My confusion comes on line 1. I dont understand why that line adds 90 degrees (in radians) to the calculated angle given by the atan function?
I just cant seem to visualise on a graph whats going on. If someone could shed some light on this - that would be much appreciated.
I suppose that these +90 degrees or -90 degrees (in case of negative rollingZ) are added to bring inclination value to widely used Polar coordinate system with angle between -180 and 180 degrees.
Assuming that you have Z line projecting upward when you look at the screen of the device and Z line looking at you from the screen, the result of calculations above vill give you an angle between screen plane and horizontal plane.
Let us assume that acceleration value is positive when it is goes "inside" the device:
1) Device is in vertical position, we have rollingZ = 1, rollingX = 0. The code returns 90 degrees.
2) Device is tilted towards user. Let rollingZ be 0.7 and rollingX be -0.7. This will give us 45 degree angle.
3) Device is in upside-down position, now we have rollingZ = -1 and rollingX = 0, and it is -90 degrees.

Change listener angle in FMOD

I have set up an event system in FMOD with 3D sound triggered when the listener is close to them. I want to be able to change the listener angle of the listener so that my entire sound landscape shifts the same amount of degrees as my listener rotates. Nice huh?
Is it possible? What would be the iPhone code for that?
Would I pass something to the method set3DListenerAttributes?
I'm not sure I fully understand, do you just want to rotate the listener in 3D space? If so you adjust the listener forward vector to point in the new direction.
** Based on your response **
FMOD works with a cartesian co-ordinate system, it has a unit length vector which points in the direction an object (or listener) is facing. I would recommend you do some reading on trigonometry to fully understand how to convert an angle (in degrees or radians) to a vector.
For your forward vector the equation is (if I remember correctly):
x = cos(angle)
z = sin(angle)
y = 0;
This assumes angle is in radians, to convert from degrees to radians:
radians = degrees * (180 / Pi)
where Pi is roughly 3.14159265
This was the code I used in the end:
float degree = 90;
float radians = (degree) * (M_PI/180);
float fx = cos(radians);
float fz = sin(radians);
forward.x = fx;
forward.z = fz;
listenerpos.x = lxPos * DISTANCEFACTOR;
listenerpos.z = lyPos * DISTANCEFACTOR;
result = eventSystem->set3DListenerAttributes(0, &listenerpos,&vel,&forward,NULL);

how can i get the sinf MATH functions answer in DEGREE?

For example,
sin 33.35 = 0.5523 in degree
sin 33.35 = 0.9347 in radian
As Xcode gives answers by default in radian.
So is there any way to get answer in degree ???
Yes, you multiply radians by a constant, 180/pi, to get degrees. That's because there are 2 * pi radians in a complete circle of 360 degrees (so half a circle is pi radians and 180 degrees).
Just keep in mind that's a conversion you have to apply to the input of the sine function (the angle), not the output (which is a length).
The pi constant can be used by including math.h and using the M_PI symbol.
A full circle in degrees exists of 360 degrees, and a full circle in radians is 2*pi. So, to convert radians to degrees, you divide by pi and multiply by 180.
radians * 180 / M_PI
If you would convert from degrees to radians, for example to provide degrees for a animation, use this:
degrees * M_PI / 180
I am using #DEFINEs for this myself:
#define DEGREES(radians)((radians)*180/M_PI)
#define RADIANS(degree)((degree)*M_PI/180)
Simply use by saying RADIANS(degree) or DEGREES(radians) in any part of your code, where you replace the degree and radians by the degree or radian value you had. This also keeps it more readable in my opinion if you are not used to radians.
Get the value in radians, then do:
CGFloat degrees = (radians * 180) / M_PI;
M_PI is a #define located in math.h.
Fortunately, the Objective-C language is a proper superset of C, which includes multiplication and division operators.