I'm looking to find the point that is 200 pixels in front of an enemy object. My method to try calculate this point is this:
//all sprites start facing down, so to begin with the point 200 pixels infront of the sprite is its current pos -200 on the y axis.
CGPoint predictedPoint = CGPointMake(self.position.x, self.position.y - 200);
//get the direction of this vector from the current position.
predictedPoint = [Utilities MinusVector:predictedPoint Vector2:self.position];
predictedPoint = [Utilities CGPointNormalize:predictedPoint];
//multiply it by 200 to get 200 pixels ahead.
predictedPoint = [Utilities MultiplyVector:predictedPoint Scalar:200];
//work out which way to rotate the enemy based on its velocity. (this code works as the enemies face the way they move!)
CGPoint facingVector = [Utilities MinusVector:self.position Vector2:CGPointMake(self.position.x + self.velocity.x, self.position.y + self.velocity.y)];
float theta = (atan2f(facingVector.y, facingVector.x) - SK_DEGREES_TO_RADIANS(90.0f));
//rotate
float cs = cosf(theta);
float sn = sinf(theta);
float px = predictedPoint.x * cs - predictedPoint.y * sn;
float py = predictedPoint.x * sn + predictedPoint.y * cs;
CGPoint thePoint = CGPointMake(px, py);
NSLog(#"Player x: %f. thePoint x: %f. Player y: %f. thePoint y: %f.", self.position.x, px, self.position.y, py);
So the calculation should be
green.center.x = triangle.center.x + 200 * cos( theta );
green.center.y = triangle.center.y + 200 * sin( theta );
where theta is the current rotation angle of the triangle. This assumes that theta == 0 has the triangle pointing to the right. If the 0 angle has the sprite pointing down, then I think you need to subtract M_PI_2, e.g.
green.center.x = triangle.center.x + 200 * cos( theta - M_PI_2 );
green.center.y = triangle.center.y + 200 * sin( theta - M_PI_2 );
Related
I am building a game and want one of my sprites to move directly away from the place the player taps.
[+] (tap location)
[+] (player)
In the example above, the player would move to the left and downwards.
Can anyone assist me in the physics?
I realise I’m to use trigonometry to calculate the angle and therefore the vector (for applyImpulse:) but the calculations I have don’t work for each quadrant surrounding the player.
Here is my code (in touchesBegan:)
UITouch *touch = [touches.allObjects objectAtIndex:0];
CGPoint touchPosition = [touch locationInNode:self];
CGPoint playerPosition = self.playerBubble.position;
double oppositeLength = (touchPosition.y - playerPosition.y);
double adjacentLength = (touchPosition.x - playerPosition.x);
double angle = atan(oppositeLength / adjacentLength);
The vector is computed as:
CGFloat playerMass = 0.0000013;
CGVector vector = CGVectorMake(playerMass * cosl(angle), playerMass * sinl(angle));
Here are the vector outputs for each quadrant surrounding the player:
+, +:
O: 133.000000, A: 42.500000, Angle: 72.278778, Theta: 1.261503
{3.9570166773706921e-07, 1.2383134543301224e-06}
-, +:
O: 95.247955, A: -79.580551, Angle: -50.120930, Theta: -0.874775
{8.335201515172361e-07, -9.9761925504652419e-07}
+, -:
O: -145.927795, A: 52.148361, Angle: -70.335281, Theta: -1.227582
{4.3747011829674742e-07, -1.2241813250586403e-06}
-, -:
O: -138.968933, A: -92.015755, Angle: 56.490189, Theta: 0.985940
{7.1770369806877642e-07, 1.0839286982100348e-06}
EDIT:
Here is my updated answer which gives the right vectors (it’s called on a SKSpriteNode instance):
- (void)moveWithTouchPosition:(CGPoint)touchPosition
{
double heightLength = (self.position.y - touchPosition.y);
double widthLength = (self.position.x - touchPosition.x);
double angle = 0.0f;
if (touchPosition.y > self.position.y && touchPosition.x > self.position.x) {
//
// +, +
//
angle = M_PI + atan(heightLength / widthLength);
} else if (touchPosition.y > self.position.y && touchPosition.x < self.position.x) {
//
// -, +
//
angle = M_PI + M_PI_2 + atan(widthLength / heightLength);
} else if (touchPosition.y < self.position.y && touchPosition.x > self.position.x) {
//
// +, -
//
angle = M_PI_2 + atan(widthLength / heightLength);
} else if (touchPosition.y < self.position.y && touchPosition.x < self.position.x) {
//
// -, -
//
angle = atan(heightLength / widthLength);
}
CGVector vector = CGVectorMake(self.physicsBody.mass * cosl(angle), self.physicsBody.mass * sinl(angle));
[self.physicsBody applyImpulse:vector];
}
To simplify your code, you could use atan2 instead of atan. Atan2 does the quadrants for you.
Another option is to translate and multiply vectors instead of using trigonometry at all, which might look something like:
CGVector vector = CGVectorMake(
(playerPosition.x - touchPosition.x) * playerMass + touchPosition.x,
(playerPosition.y - touchPosition.y) * playerMass + touchPosition.y
);
Thanks to #Bemmu it appears that all I really needed to do was to use the atan2(y, x) function and parse in the Opp and Adj lengths of the triangle which were the differences between the y and x values respectively.
E.g:
double heightLength = (self.position.y - touchPosition.y);
double widthLength = (self.position.x - touchPosition.x);
double angle = atan2(heightLength, widthLength);
CGFloat acceleration = 100.0f;
CGVector vector = CGVectorMake(self.physicsBody.mass * (cosl(angle) * acceleration), self.physicsBody.mass * (sinl(angle) * acceleration));
[self.physicsBody applyImpulse:vector];
I need to rotate a single point expressed in cartesian XYZ coordinates about the Z axis. The following 2 attempts are not working properly - I believe the first one is more correct..
I tried to rotate the points using the math on this website: http://farside.ph.utexas.edu/teaching/336k/newton/node153.html
// Rotate the XYZ coordinate for the pin image
if ( [satName isEqualToString:#"pin"] ) {
double x = xyz.x;
double y = xyz.y;
double radians = self.timeSinceOpenGlStarted;
x = x * cos(radians) + y * sin(radians);
y = -x * sin(radians) + y * cos(radians);
xyz.x = x;
xyz.z = y;
}
I also tried this function by extracting the points after GLKMatrix4Rotate:
// This function rotates XYZ a certain of radians about the origin and gives back XYZ
- (GLKVector4)rotateXYZCoordinates:(XYZ*)coords {
// Get the current modelview matrix
GLKMatrix4 currMat = self.effect.transform.modelviewMatrix;
// Print the coords before
NSLog(#"Before: %f %f %f",coords->x,coords->y,coords->z);
NSLog(#"Rotation Before: %f %f %f",currMat.m00,currMat.m10,currMat.m20);
// Construct the rows in the new matrix
float d = sqrt( pow(currMat.m00,2) + pow(currMat.m10,2) + pow(currMat.m20,2) );
GLKVector4 columnToInsert0 = GLKVector4Make(d, 0, 0, coords->x);
GLKVector4 columnToInsert1 = GLKVector4Make(0, d, 0, coords->y);
GLKVector4 columnToInsert2 = GLKVector4Make(0, 0, d, coords->z);
GLKVector4 columnToInsert3 = GLKVector4Make(0, 0, 0, 1);
// Build the new Matrix
GLKMatrix4 noTranslationInfo = GLKMatrix4SetRow(currMat, 0, columnToInsert0);
noTranslationInfo = GLKMatrix4SetRow(noTranslationInfo, 1, columnToInsert1);
noTranslationInfo = GLKMatrix4SetRow(noTranslationInfo, 2, columnToInsert2);
noTranslationInfo = GLKMatrix4SetRow(noTranslationInfo, 3, columnToInsert3);
// Throw the world translation coordinates in the matrix
noTranslationInfo.m30 = ( noTranslationInfo.m30 );
noTranslationInfo.m31 = ( noTranslationInfo.m31 );
noTranslationInfo.m32 = ( noTranslationInfo.m32 );
// Now rotate the matrix so many angles
noTranslationInfo = GLKMatrix4Rotate(noTranslationInfo, self.timeSinceOpenGlStarted, 0, 0, 1);
// Latch the output
coords->x = noTranslationInfo.m30;
coords->y = noTranslationInfo.m31;
coords->z = noTranslationInfo.m32;
// Print the coords After
NSLog(#"AFter: %f %f %f",coords->x,coords->y,coords->z);
NSLog(#"Rotation After: %f %f %f",noTranslationInfo.m00,noTranslationInfo.m10,noTranslationInfo.m20);
}
I have a globe spinning along the Z axis and a billboarded sprite specified at a specific spherical coordinate ( representing a lat/lon location ) and need the ability to have the point rotate along with the earth or not.
What am I doing wrong? How do I calculate a new X and Y coordinate ( Z is constant ) to rotate an XYZ point around the Z axis when I know the number of radians I want to rotate? Thanks!
UPDATE: Now I've tried this:
// Rotate the XYZ coordinate for the pin image
/* http://www.blitzbasic.com/Community/posts.php?topic=70536
;rotate offset around Z axis
newx# = x# * Cos#(zr#) - y# * Sin#(zr#)
newy# = x# * Sin#(zr#) + y# * Cos#(zr#)
x# = newx#
y# = newy#
;rotate offset around X axis
newy# = y# * Cos#(xr#) - z# * Sin#(xr#)
newz# = y# * Sin#(xr#) + z# * Cos#(xr#)
y# = newy#
z# = newz#
;rotate offset around Y axis
newx# = z# * Sin#(-yr#) + x# * Cos#(-yr#)
newz# = z# * Cos#(-yr#) - x# * Sin#(-yr#)
x# = newx#
z# = newz#
*/
if ( [satName isEqualToString:#"pin"] && self.shouldAnimate == YES ) {
//NSLog(#"ONE %f %f %f %f",xyz.x,xyz.y,xyz.z,sqrt(pow(xyz.x, 2)+pow(xyz.y,2)+pow(xyz.z,2)));
double x = xyz.x;
double y = xyz.y;
double z = xyz.z;
NSLog(#"%f",self.timeSinceOpenGlStarted); // Values like: 32521.473728
double zr = self.timeSinceOpenGlStarted;
double yr = 0.0f;
double xr = 0.0f;
// Rotations must be in this order: Z then X then Y
// Rotate around Z
x = x * cos(zr) - y * sin(zr);
y = x * sin(zr) + y * cos(zr);
// Rotate around X
y = y * cos(xr) - z * sin(xr);
z = y * sin(xr) + z * cos(xr);
// Rotate around Y
x = z * sin(-yr) + x * cos(-yr);
z = z * cos(-yr) + x * sin(-yr);
// Get the coordinates back
xyz.x = x;
xyz.y = y;
xyz.z = z;
//NSLog(#"TWO %f %f %f %f",xyz.x,xyz.y,xyz.z,sqrt(pow(xyz.x, 2)+pow(xyz.y,2)+pow(xyz.z,2)));
}
The problem is that my image dances around the lat/lon it should be at - it almost does a figure 8.
I either don't understand what you want to achieve or these methods of yours are a bit strange. If you need to rotate a single point around centre (0,0,0) around Z axis (on the XY plane) then you should use something like this:
float x, y;
float currentAngle;
float radius = sqrt(x*x + y*y);
x = radius*cos(currentAngle);
y = radius*sin(currentAngle);
To make it even easier you can simply use radius (which should be constant in your case) and the angle in radians. In this case you only need last 2 lines of this snippet.
It looks like you are adding each frame to your angle. You can compute an "delta angle" just the angle to rotate from the previous frame, or to use the angle as it is now but apply the rotation to the initial orientation, not to last frame's result.
In my application, a user taps 3 times and an angle will be created by the 3 points that were tapped. It draws the angle perfectly. I am trying to calculate the angle at the second tap, but I think I am doing it wrong (probably a math error). I haven't covered this in my calculus class yet, so I am going off of a formula on wikipedia.
http://en.wikipedia.org/wiki/Law_of_cosines
Here is what I am trying:
Note: First, Second, and Third are CGPoints created at the user's tap.
CGFloat xDistA = (second.x - third.x);
CGFloat yDistA = (second.y - third.y);
CGFloat a = sqrt((xDistA * xDistA) + (yDistA * yDistA));
CGFloat xDistB = (first.x - third.x);
CGFloat yDistB = (first.y - third.y);
CGFloat b = sqrt((xDistB * xDistB) + (yDistB * yDistB));
CGFloat xDistC = (second.x - first.x);
CGFloat yDistC = (second.y - first.y);
CGFloat c = sqrt((xDistC * xDistC) + (yDistC * yDistC));
CGFloat angle = acos(((a*a)+(b*b)-(c*c))/((2*(a)*(b))));
NSLog(#"FULL ANGLE IS: %f, ANGLE IS: %.2f",angle, angle);
Sometimes, it gives the angle as 1 which doesn't make sense to me. Can anyone explain why this is, or how to fix it please?
Not sure if this is the main problem but it is a problem
Your answer gives the angle at the wrong point:
To get the angle in green (which is probably angle you want based on your variable names "first", "second" and "third), use:
CGFloat angle = acos(((a*a)+(c*c)-(b*b))/((2*(a)*(c))));
Here's a way that circumvents the law of cosines and instead calculates the angles of the two vectors. The difference between the angles is the searched value:
CGVector vec1 = { first.x - second.x, first.y - second.y };
CGVector vec2 = { third.x - second.x, third.y - second.y };
CGFloat theta1 = atan2f(vec1.dy, vec1.dx);
CGFloat theta2 = atan2f(vec2.dy, vec2.dx);
CGFloat angle = theta1 - theta2;
NSLog(#"angle: %.1f°, ", angle / M_PI * 180);
Note the atan2 function that takes the x and y components as separate arguments and thus avoids the 0/90/180/270° ambiguity.
The cosine formula implementation looks right; did you take into account that acos() returns the angle in radians, not in degrees? In order to convert into degrees, multiply the angle by 180 and divide by Pi (3.14159...).
The way I have done it is to calculate the two angles separately using atan2(y,x) then using this function.
static inline double
AngleDiff(const double Angle1, const double Angle2)
{
double diff = 0;
diff = fabs(Angle1 - Angle2);
if (diff > <Pi>) {
diff = (<2Pi>) - diff;
}
return diff;
}
The function deals in radians, but you can change <Pi> to 180 and <2Pi> to 360
Using this answer to compute angle of the vector:
CGFloat angleForVector(CGFloat dx, CGFloat dy) {
return atan2(dx, -dy) * 180.0/M_PI;
}
// Compute angle at point Corner, that is between AC and BC:
CGFloat angle = angleForVector(A.x - Corner.x, A.y - Corner.y)
- angleForVector(B.x - Corner.x, B.y - Corner.y);
NSLog(#"FULL ANGLE IS: %f, ANGLE IS: %.2f",angle, angle);
I'm in the course of developing a metronome for iPad. I'm using CGAffineTransformRotate for the metronomeArm animation, NSTimer(I'm not interested in great precision) for sound and a UIPanGestureRecognizer for dragging the metronomeWeight on the metronomeArm.
My problem is that I don't know how to update the bpm by dragging the weight using the pan. For now I have this : metronomeWeight.center.y is 240 and the default bpm for this position is 80.The weight goes from top 140 to a maximum of 450. I have implemented this method but it is not correct :
-(void)updateBPM
{
CGFloat weightYPosition = metronomeWeight.center.y;
NSUInteger newBPM = (weightYPosition/3);
self.bpm = newBPM;
}
and the selector for the pan is this :
-(void)handlePan:(UIPanGestureRecognizer*)gesture
{
CGPoint translation = [gesture translationInView:metronomeArm];
CGPoint location = [gesture locationInView:metronomeArm];
NSLog(#"miscarea pe oy are valoare de: %f", location.y);
CGPoint newCenter = CGPointMake(metronomeArm.frame.size.width/2, gesture.view.center.y + translation.y );
if (newCenter.y >= 140 && newCenter.y <= 450)
{
gesture.view.center = newCenter;
[gesture setTranslation:CGPointZero inView:metronomeArm];
[self updateBPMFromWeightLocation];
tempoLabel.text = [NSString stringWithFormat:#"%d", self.bpm];
NSLog(#"metronomeWeight position : %f ",metronomeWeight.center.y);
}
}
The sound and animation update but not as desired, meaning that the lower limit bpm should be 225 and the upper one should be 1. In my case they are 150 and 46 respectively.
My calculations are not good, so it will be fantastic if you can help me solve this problem... I have looked at apple's metronome project for days and can't understand how they do this...
Thanks
The new updateBPM method thanks to #zimmryan suggestion
-(void)updateBPMFromWeightLocation
{
CGFloat weightYPosition = metronomeWeight.center.y;
float lengthInM = ((weightYPosition - 140) * 0.00041333);
float time = 2 * M_PI * sqrt(lengthInM / 9.8);
NSUInteger newBPM = floor(60.0 / time);
self.bpm = newBPM;
}
From my understanding of physics and calculus, the equation for the period of a pendulum is T=2pi sqrt(l/g) where T is time in seconds, l is length in meters, and g is gravity.
You are picking a base point of 290 (pixels) and a BPM of 120. A BPM of 120 converts to a period of .5 seconds. So T = .5. Solving the equation you get .062 for l, or 6.2cm.
But your length is not in cm it is in pixels s now you have to convert it. Since your range is from 140 to 350, your zero point is 350. So first you take 350 - 390 to get an offset of 60. Now create your equation of 60pixels * k = .062 so your k = .001033
Your final function should read
-(void)updateBPM
{
CGFloat weightYPosition = metronomeWeight.center.y;
float lengthInM = ((350 - weightYPosition) * .001033);
float time = 2 * M_PI * sqrt(lengthInM / 9.8);
NSUInteger newBPM = floor(60 / time);
self.bpm = newBPM;
}
or
-(void)updateBPM
{
self.bpm = floor(60 / (2 * M_PI * sqrt(((350 - metronomeWeight.center.y) * .001033) / 9.8)));
}
I'm struggling with rotating a triangle resulting from a UIRotationGestureRecognizer. If you could look over my approach and offer suggestions, I'd greatly appreciate it.
I ask the gesture recognizer object for the rotation, which the documentation says is returned in radians.
My strategy had been to think of each vertex as a point on a circle that exists between the center of the triangle and the vertex, and then use the radians of rotation to find the new point on that circumference. I'm not totally sure this is a valid approach, but I wanted to at least try it. Visually I'd know whether or not it was working.
Here's the code I created in that attempt:
- (CGPoint)rotateVertex:(CGPoint)vertex byRadians:(float)radians
{
float deltaX = center.x - vertex.x;
float deltaY = center.y - vertex.y;
float currentAngle = atanf( deltaX / deltaY );
float newAngle = currentAngle + radians;
float newX = cosf(newAngle) + vertex.x;
float newY = sinf(newAngle) + vertex.y;
return CGPointMake(newX, newY);
}
When executed, there's a slight rotation at the beginning, but then as I continue rotating my fingers the vertices just start getting farther away from the center point, indicating I'm confusing something here.
I looked at what the CGContextRotateCTM could do for me, but ultimately I need to know what the vertices are after the rotation, so just rotating the graphics context doesn't appear to leave me with those changed coordinates.
I also tried the technique described here but that resulted in the triangle being flipped about the second vertex, which seems odd, but then that technique works with p and q being the x and y coordinates of the second vertex.
Thanks for taking a look!
Solved: Here is the corrected function. It assumes you have calculated the center of the triangle. I used the 1/3(x1 + x2 + x3), 1/3(y1 + y2 + y3) method described on the Wikipedia article on Centroids.
- (CGPoint)rotatePoint:(CGPoint)currentPoint byRadians:(float)radiansOfRotation
{
float deltaX = currentPoint.x - center.x;
float deltaY = currentPoint.y - center.y;
float radius = sqrtf(powf(deltaX, 2.0) + powf(deltaY, 2.0));
float currentAngle = atan2f( deltaY, deltaX );
float newAngle = currentAngle + radiansOfRotation;
float newRun = radius * cosf(newAngle);
float newX = center.x + newRun;
float newRise = radius * sinf(newAngle);
float newY = center.y + newRise;
return CGPointMake(newX, newY);
}
Of noteworthy relevance to why the first code listing did not work was that the arguments to atan2 were reversed. Also, the correct calculation of the delta values was reversed.
You're forgetting to multiply by the radius of the circle. Also, since the Y axis points down in the UIKit coordinate system, you have to subtract instead of add the radians and negate the y coordinate at the end. And you need to use atan2 only gives output in the range -pi/2 to pi/2:
float currentAngle = atan2f(deltaY, deltaX);
float newAngle = currentAngle - radians;
float radious = sqrtf(powf(deltaX, 2.0) + powf(deltaY, 2.0));
float newX = radius * cosf(newAngle) + vertex.x;
float newY = -1.0 * radius * sinf(newAngle) + vertex.y;
The answer is embedded now in the original question. Gun shy about proper decorum ;-)