This is basically a simple issue, which I can't get around ...
So, I have an object of UIImageView of a certain frame over which, I implement CAAnimation with rotation and translation, and it is at new coordinates (x,y), and has been rotated by some degrees.
This animation works beautifully. But if I again do a rotation and movement from THAT state, I want the object to use the new frame and new properties from STEP 1 after its rotation and again rotate by the new angle.
As of now, when I try rotation again, it uses its standard state & frame size(during initialization) and performs rotation on it...
And by this I mean... If I have a square of frame (100, 200, 10, 10), and I rotate it by 45 degrees, the shape is now a different square, with different frame size and end points compared to the original square, and I implement a new rotation by (say) 152 degrees on it and it needs to perform a rotation on the newer square... But it turns out that it uses the same frame size as the previous one (x,y, 10, 10).
How can I continue rotating / moving the object with its updated position and state ??
Note: (if you need to see the code for animation)
This is the code for my animation, which involves simple rotation and movement ! http://pastebin.com/cR8zrKRq
You need to save the rotation step and update object rotation in animationDidStop: method. So, in your cas, you should apply:
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
//angle is global
CATransform3D rotationTransform = CATransform3DMakeRotation((angle%4)*M_PI_4, 0, 0, 1);
[object.layer setTransform:rotationTransform];
object.center = tempFrame; //already there
}
where angle is an integer counter of animations(steps) with values 0,1,2,3. My step is M_PI_4. There is probably a better solution to the problem, but this should do the trick
Related
I'm trying to constrain an object's rotation, so that it behaves like a joystick (meaning it can only rotate up to some maximum angle from center).
I tried to constrain rotation on each individual axis, but they behaved really weirdly when rotating (the angle values didn't just grow linearly). The input I'm using to supply this rotation is the rotation of physical controller. How can I do this?
This is how it works now and how I want it to work:
Images are 2D but it applies to all axes.
It sounds to me like there are two parts to the problem:
Determine whether supplied rotation exceeds maximum allowable rotation of object
If the supplied rotation is too large, reducing that rotation so it fits within the rotational constraints, then applying it
In my code examples, I'll be assuming that the initial rotation of the virtual joystick is zero across the board (Quaternion.identity), and that your supplied rotation is called newRotation.
For the first part, Quaternion.Angle() comes to mind. This gives the angle between two given rotations, and can be used like so:
if (Quaternion.Angle(Quaternion.identity, newRotation) < 30){
// Angle from initial to new rotation is under 30 degrees
}
For the second part, you'll need some way of reducing the supplied rotation so it is within the allowable angle from the initial rotation. For that, Quaternion.Slerp() is useful. This allows you to interpolate between two rotations, returning a Quaternion that is a combination of the two supplied. For example, this gives back half of newRotation:
Quaternion.Slerp(Quaternion.identity, newRotation, 0.5f);
Putting these two methods together, you can write a clamping method which ensures that the supplied rotation never exceeds a certain angle from the initial rotation. Here's an example usage:
// Maximum angle joystick can tilt at
public float tiltAngle;
// If this isn't your initial rotation, set it in Awake() or Start()
Quaternion initialRotation = Quaternion.identity;
void Update(){
Quaternion newRotation = MethodToGetSuppliedRotation();
Quaternion clampedRotation = ClampRotation(initialRotation, newRotation, tiltAngle);
transform.localRotation = clampedRotation;
}
// Clamps "b" such that it never exceeds "maxAngle" degrees from "a"
Quaternion ClampRotation(Quaternion a, Quaternion b, float maxAngle){
float newAngle = Quaternion.Angle(a, b);
if (newAngle <= maxAngle){
// Rotation within allowable constraint
return b;
}
else{
// This is the proportion of the new rotation that is within the constraint
float angleRatio = maxAngle / newAngle;
return Quaternion.Slerp(a, b, angleRatio);
}
}
Hope this helps! Let me know if you have any questions.
I have an object that rotates according to mouse position, but I want to clamp it so it doesn't get further or lower than certain value. Here is my code:
void LookAt () {
float distance = transform.position.z - Camera.main.transform.position.z;
Vector3 position = new Vector3(Input.mousePosition.x, Input.mousePosition.y, distance);
position = Camera.main.ScreenToWorldPoint(position);
position.x = Mathf.Clamp(position.x, -70, 70);
position.z = Mathf.Clamp(position.z, -70, 70);
Vector3 target = new Vector3 (position.x, transform.position.y, position.z); // Use current object positin.y
transform.LookAt(target);
}
But unfortunately it doesn't work, it keeps rotating 360.
Edit:
This is a 3D top-down game, I have a tank and I want to rotate it's upper half. The code I wrote above works perfect for the job, but now I don't know how to limit it so the barrel( the part I'm rotating) always facing upwards where the enemies will come from. 70 or whatever are just random values I was testing, first I want to figure what exactly the proper code is, then determining the values is the easy part.
Actually, the problem is that you're clamping a position, not a rotation. You're having it look at a certain point, but limiting that point rather than the angle that it will need to rotate to meet it. You'll have to use trigonometry to calculate the angle it wants to point in (more specifically, the atan2 function), clamp that value to (-70, 70), and then apply that rotation to the object (using euler angles). Do you require further clarification on any of these steps?
Cheers.
P.S. Note that atan2 returns a value in radians, but your range and euler angles use degrees.
You limit your target position by 70 units in world space, which is usually a lot, but depends on your game scale. What I think you wanted to do is to limit mouse position by 70 pixels around the screen center. (Please, provide this remarks in the question itself, so we won't have to guess). However, because you used the same variable both for screen space and world space position of the target, you likely got confused and clamped the position after converting it to world space.
Also, you made the y coordinate of the target to be the same as the object. But this means that the object would have to rotate 360 degrees every time the target passed it. I assume that what you wanted to do instead is to assume that the target location is located on camera place.
void LookAt () {
var cursorPosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, camera.main.nearClipPlane);
cursorPosition.x = Mathf.Clamp(position.x, -70, 70);
cursorPosition.z = Mathf.Clamp(position.z, -70, 70);
var targetPosition = Camera.main.ScreenToWorldPoint(cursorPosition);
transform.LookAt(targetPosition);
}
Please, provide details about your reasoning and desired behavior when you ask to find errors in your code.
How to rotate a 3d game object around its center point in unity 3d.
Just use bounds.center from the renderer
Vector3 position = myGameObject.GetComponent<Renderer>().bounds.center;
myGameObject.transform.RotateAround(position, rotationVector, degreesPerSecond * Time.deltaTime);
where rotationVector is your rotation axis (Vector3)
The two common ways to rotate an object are
The rotate attribute in the transform. Using this one you can set the exact coordinates for the target object rotation. However, you'll have to manage the smoothness by yourself if you want to make animations and the values are given through Quaternion type. I recommend to use the static method Quaternion.Euler so you can pass values in a X, Y, Z. The example below set the object to turn 90 degrees clockwise in the Y axis:
transform.rotation = Quaternion.Euler (0, 90, 0);
The second approach uses the Rotation method in the same transform attribute. This method allow you to pass the amount of degrees in which object will rotate and already accept X, Y and Z coordinates instead Quaternion.
The example below rotate the object 1 degree clockwise in the Y axis:
transform.Rotate (0, 1, 0);
To best understand the difference between both methods, if you use the first one in an Update method you'll see the object static rotated 90 degrees in the Y axis. The second example used in an Update will make the object spin clockwise in the Y axis (too fast).
use
Transform.rotation
look here for Examples/Documentation :
Unity Script Reference
I have a view that I am performing a transform on
originalTransform = self.appContainer.transform;
self.appContainer.transform = CGAffineTransformMakeScale(.8, .8);
If I do not rotate the device I can revert this back to the original by using
self.appContainer.transform = CGAffineTransformScale(originalTransform, 1, 1);
Unfortunately, if I rotate the device, the transformation matrix for the view gets modified by the view rotation and breaks the original scale. If I undo the scale after rotation I am left with the UIView not returning to its original full size.
I'm sure that I need to make calculations between the original transformation matrix and the new one but I am unsure how to do this. The only thing I have been able to do to make this work is to revert the scale back to 1 before rotation
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
self.appContainer.transform = CGAffineTransformScale(originalTransform, 1, 1);
}
And then rescale it with the newly modified transformation after the
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
self.appContainer.transform = CGAffineTransformMakeScale(.8, .8);
}
This makes it work as I expect, but instead of going back to full screen, and then back to .8 scale, I'd like it to go from its new transformation to the correct .8 scale.
If you want to revert it to original, try setting the transform to CGAffineTransformIdentity.
self.appContainer.transform = CGAffineTransformIdentity;
I think you have to remove all autoresizing masks using.....
[self.appContainer setAutoresizingMask:UIViewAutoresizingNone];
I have a label that I rotate using
pieceBlack.transform = CGAffineTransformMakeRotation((M_PI * (180) / 180.0));
and that works perfectly, EXCEPT:
I rotate this label during the game to either right side up or upside down. How do I say, "Whatever angle you are at, go back to upright." I'm thinking maybe like an:
int PreviousAngle = ?;
pieceBlack.transform = CGAffineTransformMakeRotation(degreesToRadian(0-PreviousAngle));
so I guess what I'm asking is how you ask for the rotation angle. Or, alternately, maybe there is a sort of
pieceBlack.transform = CGAffineTransformMakeRotation(RotateToUpright);
From what I remember transform is always relative from the upright position (original), so 0.0f? So you can just do pieceBlack.transform = CGAffineTransformIdentity
What I did was to first position the (in my case) view in the "straight up" orientation. Then I used the CGAffineTransformMakeRotation to create the somewhat off-kilter view. Finally, I applied the identity transform to bring the view back to its straight-up position.
you don't want to set the transform, you want to modify it,
view.transform = CGAffineTransformRotate(view.transform, angle);
if you need to keep the old one around, then do