I have a UIImageView that is set to move up and down the screen with the value of the accelerometer, using the following code:
ship.center = CGPointMake(ship.center.x, ship.center.y+shipPosition.y);
Where shipPosition is a CGPoint set in the accelerometerDidAccelerate method using:
shipPosition.y = acceleration.x*60;
Obviously this works fine, it is very simple. I run into trouble when I try to something equally simple, vary the rotation of the image depending on its acceleration. I do this using:
ship.transform = CGAffineTransformMakeRotation(shipPosition.y);
For some reason this causes a very strange thing to happen, in that the image snaps back to its origin every time the main method is called. I can see frames where the image moves to where it should be, but then instantly snaps back.
This problem only happens when I have the rotation line in, commented out it works fine. I have no idea what is going on here, I have done this many times for different apps and i never had such a problem. In fact I copied my code from a different app I created where it works fine.
EDIT:
What really confuses me is when I change the angle of the rotation from the acceleration to the position of the ship using:
ship.transform = CGAffineTransformMakeRotation(ship.center.y/10);
When I do this, the ship actually rotates based on the accelerometer but does not move, which is crazy because a changing ship.center.y means the position of the ship is changing, but it's not!!
You should set the transform of you view back to CGAffineTransformIdentity before you set his center coordinates or frame and after that apply the new transformation.
The frame property returns the transformed coordinates of a view if it is transformed and not the true (well actually the transformed are true) coordinates.
Quote from the docs:
Warning: If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.
Update/Actual Answer:
Well the actual problem is
shipPosition.y = acceleration.x*60;
Since you set the y pos in accelerometerDidAccelerate.
The acceleration won't remember it's old value. So if you move your device it will get a peak and as you slow down it will decelerate again.
Your ship will be +/-60 at the highest acceleration speed but will be 0 when you stop moving your device and shipPosition.y will be 0.
CGAffineTransformMakeRotation expects angle in radians, not in degrees.
1 radian = M_PI / 180.0 degrees
Related
I'm trying to make a pick up objects mechanic like the one in Amnesia. It's easy to calculate needed rigidbody's velocity, so that the held object stays in front of camera, but my problem is that the object doesn't rotate at all when I hold it. And I would rather have it always be rotated towards the camera. This could easily be achieved with simply parenting the object to player's camera, but...
The behaviour I'm after is as follows: if the bottle I picked up was standing on a table, with neck of the bottle facing ceiling, I would like to see this bottle always with its neck facing ceiling while I hold it. But if this bottle collides with something, it should behave like it actually bumped onto something, so it should rotate some small amount, but it should always try to return to its "original" rotation (in this case, neck facing ceiling).
I think that I need to calculate angular velocity for that and probably have some lerp to return to original rotation, but I'm at a loss on how to do that properly.
I think that the first thing I would need to do is to store the initial direction the moment player picks object up:
Vector3 targetDirection = playerCamera.transform.position - transform.position;
Script is on the held object, so "transform" refers to it. In FixedUpdate() I probably need to have some interpolation, so that angular velocity always tries to rotate the object to original rotation:
rigidbody.angularVelocity = Vector3.Lerp(rigidbody.angularVelocity, targetAngularVelocity, lerpSpeed * Time.fixedDeltaTime);
I don't know how to calculate targetAngularVelocity, because after all I would like the held object to return to original rotation smoothly. I'm not even sure if that's the right way to do this thing and perhaps I should do something else than to calculate angular velocity needed to rotate object properly. I tried just interpolating localRotation to original local rotation, but that did not allow the held object to bump on stuff (the movement then was very jittery). Any ideas?
You need a stabilizer. A script which will add torque/angular velocity to the object, whose angle is different from the target one. Say, you have two variables: targetDirection and currentDirection aka transform.forward. Then you write something like this in fixed update:
var rotation = Quaternion.FromToRotation(currentDirection, targetDirection).eulerAngles * sensitivity;
rigidbody.angularVelocity = rotation;
I recommend to set sensitivity about 0.05 and then increase it if the object stabilizes too slow.
Probably I confused the order, so you should put minus somewhere, but the approach itself is applicable.
I encountered another issue with hand-object interaction using a Leap Motion device. In particular, I use the LM to perform orientation manipulations on virtual objects.
I want to use the difference between the current and last orientation of the hand to manipulate the object:
Quaternion relativeOrientationHands = Quaternion.Inverse(currentHandOrientation) * updatedHand;
transform.rotation = transform.rotation * relativeOrientationHands;
It works fine, however the problem is that, let's say I rotate the object 180 degrees around the x axis (after that, y is pointing downwards). If I release the object afterwards, and grasp it again, the orientation changes get from then on applied to the new orientation of the object which is super confusing to use. (Original: turn hand to the left - object turns counter-clockwise; After: turn hand to the left - object turns clockwise)
Unfortunately, I don't know the math to fix it, but maybe someone can help me out. How can I apply the orientation changes of the hand to my virtual object, with respect to the default object orientation using Quaternions. Maybe, something with normal vectors?
EDIT:
The 3 figures illustrate my problem. The first figure shows the object in its original orientation (x-right, y-upwards). I turn the cube 180 degrees by rotating my hand 180 degrees clockwise around z (blue axis), after I release it, x is pointing to the left and y is pointing downwards (figure 2). If I grasp the object (figure 3) in its current orientation, and let's say I would want to perform a 90 degrees clockwise rotation around the y-axis. In figure one, I could do that by moving my hand 90 degrees clockwise. However, I changed the orientation of the cube with my previous manipulation. Therefore, I would have to perform a 90 degrees counter-clockwise rotation with my hand to move the object in clockwise direction, because the y-axis is flipped (figure 2-3). I always want that turning your hand 90 degrees clockwise results in a 90 degrees clockwise rotation of the object regardless of its current orientation.
Thank you very much
Best
R.Devel
I'm implementing a basic speedometer using an image and rotating it. However, when I set the initial rotation (at something like 240 degrees, converted to radians) It rotates the image and makes it much smaller than it otherwise would be. Some values make the image disappear entirely. (like M_PI_4)
the slider goes from 0-360 for testing.
the following code is called on viewDidLoad, and when the slider value is changed.
-(void) updatePointer
{
double progress = testSlider.value;
progress += pointerStart
CGAffineTransform rotate = CGAffineTransformMakeRotation((progress*M_PI)/180);
[pointerImageView setTransform:rotate];
}
EDIT: Probably important to note that once it gets set the first time, the scale remains the same. So, if I were to set pointerStart to 240, it would shrink, but moving the slider wouldn't change the scale (and it would rotate it as you'd suspect) Replacing "progress" with 240 in the transformation does the same thing. (shrinks it.)
I was able to resolve the issue for anybody who stumbles across this question. Apparently the image is not fully loaded/measured when viewDidLoad is called, so the matrix transforms that cgAffineTransform does actually altered the size of the image. Moving the update code to viewDidAppear fixed the problem.
Take the transform state of the view which you want to rotate and then apply the rotation transform to it.
CGAffineTransform trans = pointerImageView.transform;
pointerImageView.transform = CGAffineTransformRotate(trans, 240);
I am using
float theAngle = atan2( location.y-self.center.y, location.x-self.center.x );
to rotate a wheel at a certain angle.
and
self.transform = CGAffineTransformMakeRotation(angleRadians);
for the transformation to take place.
But everytime the user represses the wheel to turn it, it goes back to the original location and starts from there. How may I stop this from happening please?
Thanks!
You may setting the rotation angle value of the wheel rather than adding the value to the current angle.
I would hazzard a guess that the reason is that you are calculating based on the original position of the wheel rather than the previous position of the wheel.
i.e. Presuming that self.center represents the original position of the wheel as first displayed, location represents the users desired new rotation and looking up atan2 on wikipedia (trig is not my strong point :-) I would guess you need self.location to be updated each time to the be location so that the next rotation starts from the last rotation.
Just a guess :-)
I am trying to figure out how can you drag an image while constraining its movement along a certain path.
I tried several tricks including animation along a path, but couldn't get the animation to play and pause and play backwards - so that seems out of the question.
Any ideas ? anyone ?
What you're basically trying to do is match finger movement to a 'translation' transition.
As the user touches down and starts to move their finger you want to use the current touch point value to create a translation transform which you apply to your UIImageView. Here's how you would do it:
On touch down, save the imageview's starting x,y position.
On move, calculate the delta from old point to new one. This is where you can clamp the values. So you can ignore, say, the y change and only use the x deltas. This means that the image will only move left to right. If you ignore the x and use y, then it only moves up and down.
Once you have the 'new' calculated/clamped x,y values, use it to create a new transform using CGAffineTransformMakeTranslation(x, y). Assign this transform to the UIImageView. The image moves to that place.
Once the finger lifts, figure out the delta from the original starting x,y, point and the lift-off point, then adjust the ImageView's bounds and reset the transform to CGAffineTransformIdentity. This doesn't move the object, but it sets it so subsequent accesses to the ImageView use the actual position and don't have to keep adjusting for transforms.
Moving along on a grid is easy too. Just round out the x,y values in step 2 so they're a multiple of the grid size (i.e. round out to every 10 pixel) before you pass it on to make the translation transform.
If you want to make it extra smooth, surround the code where you assign the transition with UIView animation blocks. Mess around with the easing and timing settings. The image should drag behind a bit but smoothly 'rubber-band' from one touch point to the next.
See this Sample Code : Move Me