Camera movement using WASD on x/z plane only - unity3d

I've been wearing my enter key down on google searches - I have a camera script based on the MouseOrbit.js asset. That's all working fine, but in addition to the basic orbiting and the zooming that I've added, I would like to use the WASD keys to move the camera around the world.
The W key would move the camera straight forward, however it would ignore the y axis. For example, using
transform.Translate(Vector3.Forward*Time.Delta*20);
moves the camera forward relative to the camera. This results in you quickly hitting the ground. Moving back oibviously does the opposite. The desired effect is sliding across the world without getting any closer/farther to it, regardless of the angle the camera is at.
The closest I can get is using the Space.World parameter of Translate(), but this does not take into account the rotation of my camera. I think if I could take that into account, this would be solved but I'm not clear on how to do that.
Thanks,
Chris

(From Tetrad on http://Gamedev.stackecxchange.com)
You don't need to use transform.Translate. Just calculate how much the camera should move forward for a given frame (something like if the W key is held down do deltaPos = transform.forward * Time.deltaTime * 20), set the Y value of that Vector3 to zero, then add that delta vector to the original position by adding it to the current position transform.position += deltaPos;

Related

Questions related to transform.rotate() in Unity Scripting

I have four rectangular platforms put around a square platform. All other three rectangular platforms were rotated from the '0' platform. On the end of each rectangular platforms, I set up some spawn points to generate cubes with arrows sign on its face(the image of this cube is shown in the second link). I hope to rotate the cube to change the orientation of the arrow on it. But my original codes that rotating the cube do not work for platform 1 & 3, but work for platform 0&2.
Although I have found how to change it to make it works, I still cannot understand why my original codes failed. The version I used is 2019.4.18f1.
The top view of these platforms are shown below.
enter image description here
Unworkable way
dirCubeTypes is an array that stores all my different kinds of cube objects.
dirCubePoints is an array that stores all my spawn points.
The first statement works well. The problem is in the second statement.
GameObject cube = Instantiate(dirCubeTypes[Random.Range(0, 6)], dirCubePoints[Random.Range(0, 4)]);
cube.transform.Rotate(transform.forward, 90 * Random.Range(0, 4));
The figure below shows the cube generated on the platfrom 3. I want it to rotate around z-axis, but the codes above make it rotate around its x-axis instead of z-aixs. I know the third parameter is relativeTo, and the default value is Space.self. Why doesn't the codes above make it rotate along the z-axis in its own local coordinate system?
enter image description here
Workable ways
I have tried out three workable ways, but for two of them, I still don't know why it works.
This one works well. If I use Vector3.forward in Space.self, I think it should rotate around the x-axis of the cube itself, but it rotate around its z-axis.
GameObject cube = Instantiate(dirCubeTypes[Random.Range(0, 6)], dirCubePoints[Random.Range(0, 4)]);
cube.transform.Rotate(Vector3.forward, 90 * Random.Range(0, 4));
This one also works well. But I am confused either.
GameObject cube = Instantiate(dirCubeTypes[Random.Range(0, 6)], dirCubePoints[Random.Range(0, 4)]);
cube.transform.Rotate(transform.forward, 90 * Random.Range(0, 4), Space.World
);
The last one is the only one that works as I think.
GameObject cube = Instantiate(dirCubeTypes[Random.Range(0, 6)], dirCubePoints[Random.Range(0, 4)]);
cube.transform.RotateAround(transform.position, transform.forward, 90*Random.Range(0,4));
First of all
Vector3.forward just equals new Vector3(0,0,1) so it is just a generic Z axis vector (either in the world or local depending on how it is used)
transform.forward rather is the local Z axis of your object in world space
And Rotate can either be used in world space or in local space (this is the default since the space parameter is optional and defaults to Space.Self).
When you passed in
cube.transform.Rotate(transform.forward * 90 * Random.Range(0,4));
it of course behaves strange since you treat the world space vector transform.forward like a local space one. So as soon as your object is somehow rotated in the world it breaks.
So either you want the world space then do
// Using the world space transform.forward and passing in Space.World
cube.transform.Rotate(transform.forward * 90 * Random.Range(0,4), Space.World);
or the same in local space
// Using the local space Vector3.right
// in this case - since it will use Space.Self if nothing is passed in -
// the Vector3.forward is treated as relative to the local space
cube.transform.Rotate(Vector3.forward * 90 * Random.Range(0,4));
I just saw your question update. I believe your misunderstanding stems from not knowing what Vector3.forward and Transform.forward are and their core difference. Vector3.forward is the unit vector defined by (0, 0, 1). The Transform.forward is a relative direction based on the orientation of your object.
Your first example works in some cases as it just so happens that the Transform.forward and Vector3.forward happen to be the same direction.
Your second example works as you are using Vector3.forward not the relative Transform.forward
Your third example works as you provide the Space.World, so instead of using local orientations, it is rotating the object in world space relative to the scene.
Your fourth example works as you are using RotateAround. RotateAround is different than Rotate in that it will rotate the transform about your inputted axis (transform.forward), which pass through the point (transform.position) in world coordinates by the angle (90*Random.Range(0,4)) degrees. Effectively moving the local rotation around an axis back to world space.
I hope this clears up why the first example does not work while the last three do.

Walk around square platform. Unity2D

I'm trying to make a character that walks around a platform and if the character reach a corner, it rotates and continue walking on the side of the platform, same with the bottom part.
This is a visual representation of what I'm trying to achive.
Movement
The specific problem is when the character reach the corners, the rotation just go crazy. I'm trayng to achive this using a raycast from the character to the platform and if the raycast doesn't find floor, I start the rotation like this:
times++;
transform.rotation = Quaternion.Slerp(transform.rotation,Quaternion.AngleAxis(times*-89.9f,Vector3.forward),Time.deltaTime * RotationSpeed);
_characterGravity.SetGravityAngle(transform.localEulerAngles.z);
I'm using a characterGravity script that allows me to change the gravity direction for the character in order to not fall when is walking upside down or on the sides. But this is not working propperly. Is there a better way to do this?
Assuming your 2D view is aligned on the X-Y plane, with the Z-axis aimed into the screen (the same direction the camera is facing), I suggest using Transform.Rotate() instead of trying to linearly interpolate the rotation between two values:
if (ShouldRotateAroundCorner()) {
transform.Rotate(Vector3.forward, RotationSpeed * Time.deltaTime);
}
You'll just need to make sure your ShouldRotateAroundCorner() knows when to start and stop the 90 degree turn, which will take a little bit of additional code to keep track of when it's in this state change.

Find the offset between the rotation of a bone in Unity and in the original rig

Goal:
Calculate a rotation, that can be used, to correct the Transform of the bones, so they get rotated as expected when manually rotated.
In Detial:
Character's bone Transform is not imported to Unity correctily, ie. the left hand's Z axis does not looks towards the top of the hand, but backwards, while the right hand's Z axis looks forward.
The bones default rotation is not the same as you would expect if you have seen the rig in another application before.
I had the idea to create a class, that puts the character in T-Pose, then maps the bones rotation.
(I would assume that Unity does something similiar under the hood, that's how I got the idea.)
So the class would be used with an API like this:
/// This should return the rotation, where the bone pointing forward rotated by rotation.
/// Ie. when rotation = Quaternion.Identity the left hand should look forward.
Quaternion CorrectRotation(HumanBodyBones bone, Quaternion rotation)
{
return Quaternion.Inverse(tPoseRotations[bone]) * rotation;
}
The problem is, that I can't seem to find a good way to map theese rotations.
My last attempt was this:
Vector3 boneDirection = (boneTransform.position - parentTransform.position).normalized;
Quaternion mappedRotation = Quaternion.LookRotation(boneDirection, parentTransform.up);
As you can see in this image, with this method the hands look in the same direction, forward, but still not rotated correctly, they have an offset from the desired result. (The correct rotation is shown by the purple hands.)
When given other rotations, the hands follow with the same offset, so other then this unwanted offset they work correctly.
So basically, I would appriciate any help to fix my problem, either this way, or with a different solution, that works with different rigs.

Placing objects right in front of camera

I am trying to figure out how to modify HelloARController.cs from the example ARCore scene to place objects directly in front of the camera. My thinking is that we are raycasting from the camera to a Vector3 on an anchor or tracked plane, so can't we get the Vector3 of the start of that ray and place an object at or near that point?
I have tried lots, and although I am somewhat a beginner, I have come up with this
From my understanding, ScreenToWorldPoint should output a vector3 of the screen position corresponding to the world, but it is not working correctly. I have tried other options instead of ScreenToWorldPoint, but nothing has presented the desired effect. Does anyone have any tips?
To place the object right at the middle of the camera's view, you would have to change the target gameObject's transform.position (as AlmightyR has said).
The ready code would look something like this:
GameObject camera;
GameObject object;
float distance = 1;
object.transform.position = camera.transform.position + camera.transform.forward * distance;
Since camera's forward component (Z axis) is always poiting at the direction where Camera is looking to, you take that vector's direction and multiply it by a distance you want your object to be placed on. If you want your object to always stay at that position no matter how camera moves, you can make it a child of camera's transform.
object.transform.SetParent(camera.transform);
object.transform.localPosition = Vector3.forward * distance;
Arman's suggestion works. Also giving credit to AlmightyR since they got me started in the right direction. Here's what I have now:
// Set a position in front of the camera
float distance = 1;
Vector3 cameraPoint = m_firstPersonCamera.transform.position + m_firstPersonCamera.transform.forward * distance;
// Intanstiate an Andy Android object as a child of the anchor; it's transform will now benefit
// from the anchor's tracking.
var andyObject = Instantiate(m_andyAndroidPrefab, cameraPoint, Quaternion.identity,anchor.transform);
The only problem with this is that because of the existing HelloAR example code, an object is only placed if you click on a point in the point cloud in my case (or a point on a plane by default). I would like it to behave so that you click anywhere on screen, and it places an object anchored to a nearby point in the point cloud, not necessarily one that you clicked. Any thoughts for how to do that?
Side tip for those who don't know: If you want to place something anchored to a point in the cloud, instead of on a plane, change
TrackableHitFlag raycastFilter = TrackableHitFlag.PlaneWithinBounds | TrackableHitFlag.PlaneWithinPolygon;
to
TrackableHitFlag raycastFilter = TrackableHitFlag.PointCloud;

Camera: "Linear" zoom

I want to move a camera closer to an object in Unity, so that the zooming is linear. Take a look at the zooming in the Scene window in the Unity Editor. The camera isn't moving with a constant speed. The more zoomed in you are, the slower the camera moves. And I do want to move the camera, I don't want to change the FOV.
First off, yes do not use the FOV to do that, it would be a massive pain to control the movement. Being in low FOV, would mean you are focus on a tiny part and movement would be hard to control since one unit would be a large movement in small FOV. Also, a large FOV creates fish eye effect. So nope.
One way you could achieve this fast to slow effect is to use a distance to define the amount of movement
Let's consider you use the mouse wheel to zoom in and out, you listens to the value and create a movement based on that.
float movement = Input.GetAxis("Mouse ScrollWheel");
if(movement == 0)
{
return;
}
Transform camTr = Camera.main.transform;
float distance = Vector3.Distance(camTr.position, origin.position) + 1;
camTr.Translate(camTr.forward * distance * movement * Time.deltaTime * speed);
speed variable is meant to control how fast regardless of the distance. Distance gets a plus 1 so you can still move while real close to the origin.
The origin position needs to be defined, it could be based on a raycast going forward from the camera and hitting at y = 0. Just an idea. It could also be a focused object, that all depends on what you are doing.
The whole point here is that your camera will move faster if you are far from the focus point. Also, you may want to clamp the movement so that begin far away does not result in gigantic movement.
i propose you to use FOV (because player feel good)
1- use Transform.localPosition to change distance of camera to near or far - you should change z axis to changing distance of camera
2- use camera rendertotexture and in GUI change scale of it ( isn't good because need pro unity license and get allot of memory);
but can't know whats reason for you want forget changing FOV
One step of perspective projection of a point is the division by distance of that point. This means that the projected size of any object does not change linearly with its distance.
projectedSize = originalSize / distance
So if you want to approach an object in a way that its projected size grows linearly, you'll have to change your distance non-linearly.
We do this by also dividing the distance by some value f. The exact value of f depends on the stepSize and (I think) the FoV of your camera.
By using FixedUpdate() instead of Update() we make sure we have a constant stepSize which allows us to assume f as being constant as well.
void FixedUpdate()
{
newDistance = oldDistance / f;
}
For the definition of f you can use any value bigger than, but close to 1. For example f = 1.01f. This will approximate the desired behaviour quite well. If you want it to be perfectly linear, you'll have to dig deeper into the calculations behind perspective projection and calculate the exact value of f yourself: https://en.wikipedia.org/wiki/3D_projection#Perspective_projection