Rotate sprite to the direction of a vector - swift

I'm not too great at math but what I am trying to accomplish is rotating my sprite to the direction of a vector impulse that is applied to it. My game is in 2D
If I tap below my sprite it goes upwards, if I tap below my sprite and to the right, it goes upwards and to the left, etc. etc.
I am doing this by just applying a normalized vector (unit vector?) impulse to my sprite which i got from the difference in position between the sprite and my tap.
let x = (ball.position.x - location.x)
let y = (ball.position.y - location.y)
let vectorLength = sqrt((x*x) + (y*y))
// Ignore constants
let unitVector = CGVector(dx: (x/vectorLength) * 25, dy: (y/vectorLength) * 50)
ball.physicsBody!.applyImpulse(unitVector)
Now what I would like to accomplish is the ability to rotate the sprite in the direction of that vector.
So for example if my sprite is traveling up and to the left at lets say a 45 degree angle, how can I rotate the sprite counterclockwise 45 degrees using the vector so that it appears to be facing the facing the direction its traveling?

something like sprite.zRotation = atan(unitVector.dy, unitVector.dx)?

Related

Rotate rigid body in one direction with AddTorque

I have a main camera that rotates around an object (which I already move with AddForce). Using AddTorque, I would like the object to rotate its Y axis of rotation in the direction in which the camera's Y axis of rotation points so as to simulate the rotation of a person turning around. To do this I thought of using a force that gave me 0 when the Y axes of the two objects are the same, but I tried and the rigidbody covers only one part of the rotation of the camera while the other part is as if it not is detected.
var currentR = rb.rotation.y;
var targetR = Camera.main.transform.rotation.y;
rb.AddTorque(transform.up * 1000f * (targetR - currentR));
rotation my object
desired rotation
Vector3 camForward = Camera.main.transform.forward;
Vector3 rbForward = rb.transform.forward;
Vector3 torque = Vector3.Cross(camForward, rbForward);
rb.AddTorque(torque);

Keep Camera relative to left half of a gameobject

I am trying to place my camera on the x-axis relative to a gameobject (in my case a sphere). I need this to preview only the half of that sphere no matter what resolution the window will be.
So if the user resizes the window, the camera should adjust itself to only see the left half. See the attached Image. Red is the cameras view and black is the Sphere
Using the Camera.ViewerportPointToRay function you can get a position and direction in world space from a point on the screen. Screen coordinates are defined as (0,0) on the bottom left to (1, 1) on the top right. So to position the sphere on the middle right of the screen you'll want to convert the screen coordinate (1, 0.5) to world coordinates.
Here's an idea of how to get the point on the middle-right of the screen half-way between the near and far clip planes of the camera:
// get the world ray from screen coordinates
Ray ray = camera.ViewpoirtPointToRay(new Vector3(1, 0.5, 0));
// make the z-component of the direction 1
ray.direction /= ray.direction.z;
// get the world position for where to place the sphere by scaling the ray
// halfway down the camera's frustum
Vector3 worldPos = ray.origin + ray.direction * (camera.far - camera.near) * 0.5;
sphere.position = worldPos;
Hopefully that helps! Let me know if anything is unclear!

SceneKit invert Y axis without inverting geometry normals

Generally what I'm trying to achieve: we have map data that historically was all 2D, and the coordinate system we use is the origin point (0,0) at the top left, positive x goes right, positive y goes down. We have now added 3D data by adding a z axis, positive z coming out of the screen towards you (think top-down map view). This is a left handed coordinate system, but SceneKit is a right handed coordinate system. I would like to apply some transform at the top level of my SceneKit Scene that will convert the Scene into a left handed coordinate system such that I can modify/position/add nodes to the scene in terms of our custom mapping coordinate system and things will just work.
So far I have this:
let scene = SCNScene()
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.scale = SCNVector3(1,-1,1)
scene.rootNode.addChildNode(cameraNode)
This achieves exactly what I want, but has one big problem. It inverts all of the geometry faces, so my geometry's disappear unless I change their material's cullMode:
let mapLength = 1000 //max X axis
let mapWidth = 800 //max Y axis
let mapHeight = 100 //max Z axis
cameraNode.position = SCNVector3(mapLength / 2, mapWidth / 2, 2000)
let mapPlane = SCNNode()
mapPlane.position = SCNVector3(mapLength / 2, mapWidth / 2, 0)
mapPlane.geometry = SCNPlane(width: mapLength, height: mapWidth)
mapPlane.geometry?.firstMaterial?.diffuse.contents = UIColor.blackColor()
scene.rootNode.addChildNode(mapPlane)
mapPlane doesn't show at all! You have to rotate the camera to the underside of mapPlane in order to see it. You can easily fix this by adding a single line:
mapPlane.geometry?.firstMaterial?.cullMode = .Front
But I don't want to have to change the cullMode for every geometry/material. Is there a way to achieve this without requiring extra code at each geometry/material? Some transform that would invert the geometry face normals for all child nodes of rootNode? Ideally this would be achieved entirely by settings on the actual Scene, or by transforms on rootNode or the camera.

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.

Car turning circle and moving the sprite

I would like to use Cocos2d on the iPhone to draw a 2D car and make it steer from left to right in a natural way.
Here is what I tried:
Calculate the angle of the wheels and just move it to the destination point where the wheels point to. But this creates a very unnatural feel. The car drifts half the time
After that I started some research on how to get a turning circle from a car, which meant that I needed a couple of constants like wheelbase and the width of the car.
After a lot of research, I created the following code:
float steerAngle = 30; // in degrees
float speed = 20;
float carWidth = 1.8f; // as in 1.8 meters
float wheelBase = 3.5f; // as in 3.5 meters
float x = (wheelBase / abs(tan(steerAngle)) + carWidth/ 2);
float wheelBaseHalf = wheelBase / 2;
float r = (float) sqrt(x * x + wheelBaseHalf * wheelBaseHalf);
float theta = speed * 1 / r;
if (steerAngle < 0.0f)
theta = theta * -1;
drawCircle(CGPointMake(carPosition.x - r, carPosition.y),
r, CC_DEGREES_TO_RADIANS(180), 50, NO);
The first couple of lines are my constants. carPosition is of the type CGPoint. After that I try to draw a circle which shows the turning circle of my car, but the circle it draws is far too small. I can just make my constants bigger, to make the circle bigger, but then I would still need to know how to move my sprite on this circle.
I tried following a .NET tutorial I found on the subject, but I can't really completely convert it because it uses Matrixes, which aren't supported by Cocoa.
Can someone give me a couple of pointers on how to start this? I have been looking for example code, but I can't find any.
EDIT After the comments given below
I corrected my constants, my wheelBase is now 50 (the sprite is 50px high), my carWidth is 30 (the sprite is 30px in width).
But now I have the problem, that when my car does it's first 'tick', the rotation is correct (and also the placement), but after that the calculations seem wrong.
The middle of the turning circle is moved instead of kept at it's original position. What I need (I think) is that at each angle of the car I need to recalculate the original centre of the turning circle. I would think this is easy, because I have the radius and the turning angle, but I can't seem to figure out how to keep the car moving in a nice circle.
Any more pointers?
You have the right idea. The constants are the problem in this case. You need to specify wheelBase and carWidth in units that match your view size. For example, if the image of your car on the screen has a wheel base of 30 pixels, you would use 30 for the WheelBase variable.
This explains why your on-screen circles are too small. Cocoa is trying to draw circles for a tiny little car which is only 1.8 pixels wide!
Now, for the matter of moving your car along the circle:
The theta variable you calculate in the code above is a rotational speed, which is what you would use to move the car around the center point of that circle:
Let's assume that your speed variable is in pixels per second, to make the calculations easier. With that assumption in place, you would simply execute the following code once every second:
// calculate the new position of the car
newCarPosition.x = (carPosition.x - r) + r*cos(theta);
newCarPosition.y = carPosition.y + r*sin(theta);
// rotate the car appropriately (pseudo-code)
[car rotateByAngle:theta];
Note: I'm not sure what the correct method is to rotate your car's image, so I just used rotateByAngle: to get the point across. I hope it helps!
update (after comments):
I hadn't thought about the center of the turning circle moving with the car. The original code doesn't take into account the angle that the car is already rotated to. I would change it as follows:
...
if (steerAngle < 0.0f)
theta = theta * -1;
// calculate the center of the turning circle,
// taking int account the rotation of the car
circleCenter.x = carPosition.x - r*cos(carAngle);
circleCenter.y = carPosition.y + r*sin(carAngle);
// draw the turning circle
drawCircle(circleCenter, r, CC_DEGREES_TO_RADIANS(180), 50, NO);
// calculate the new position of the car
newCarPosition.x = circleCenter.x + r*cos(theta);
newCarPosition.y = circleCenter.y + r*sin(theta);
// rotate the car appropriately (pseudo-code)
[car rotateByAngle:theta];
carAngle = carAngle + theta;
This should keep the center of the turning circle at the appropriate point, even if the car has been rotated.