- Home /
Rotating A Vector2 Input Into A Vector3
I'm getting a normalized Vector2 direction from a touch screen. (Joystick) The input is supposed to control the movement of a ball. The ball can change its gravity when it comes in contact with a wall so that the wall becomes the new 'down'.
My issue arrives here because I was converting the Vector2 input into the XZ axis. However now the ball is rolling on, say, a surface along the XY axis, and so it cant properly move around on it. The issue gets more complicated when trying to use surfaces that aren't aligned to an axis.
I found several people talking about using "Quaternion.Euler()" and "vector = Quaternion.AngleAxis(-45, Vector3.up) * vector". However I don't always know the new angle or axis at which to use because I will be using surfaces of all different orientations.
It is probably useful to note that I am getting the new gravity directions from the normals of downward raycasts.
Answer by robertbu · Dec 22, 2013 at 01:48 AM
This sounds like it might be pretty challenging for a user to stay oriented...maybe if you oriented the camera to the new surface at each hit everything will work out okay. As for your question...
How you handle the situation will depend on what you build your surfaces out of. For my suggestions below, I'm going to assume that you build the surfaces out of cubes or quads. In particular, I'm going to assume that for any given surface, that gravity is applied in the negative 'y' direction of the object, that forward is the 'z' direction, and that right is the local 'x' of that object. It is important to be consistent on this definition no matter what orientation you place the surfaces.
Each time you hit a surface, you can get information about the surface in the OnCollisionEnter() function. In particular, you want to get information from the transform:
So on collision, your ball will do:
private var right = Vector3.right;
private var down = Vector3.down;
private var forward = Vector3.forward;
function OnCollisionEnter(col : Collision) {
var tr = col.collider.transform;
down = -tr.up;
right = tr.right;
forward = tr.forward;
}
Now you did not indicate how you move the ball. Let me assume rigidbody. For gravity you will do something like:
rigidbody.AddForce(down * forceOfGravityContant);
For the forward and right movement, you will do something like:
var movement = forward * Input.GetAxis("Vertical") * upAmount + right * Input.GetAxis("Horizontal) * rightAmount;
'movement' can then be passed to AddForce() or CharacterController.Move().
Since you did not provide any code, this is pretty soft, but this is the general concept.
Thanks for the reply. Your approach is a little different than $$anonymous$$e. I am planning on using tube models at some point, and so just getting the transform's rotation wont work very well. Also I misspoke when I said that the ball should change gravity when it comes in contact with a wall. It should have to come in contact with a surface that is more of an obtuse angle first. (Such as a 45 degree angled ramp leading up to a vertical wall.)
I have gotten everything working so far and the camera is almost fully functional. It is just getting the input Vector2 to rotate to be parallel to the surface that the ball is on that is causing me issues.
Here is my code: http://pastebin.com/FR6NekP$$anonymous$$ (I tried to comment things so you can understand whats happening) (Also ignore the ConstantForce variables, I have a plan for them later and they aren't used.) Also note that gravity is disabled on the rigidbody. The script is handling that 100%.
Also here is my wall that I am testing with. (The ball should be able to go up any kind of oriented surface though, as long as there is a ramp leading up to it.)
The primary issue with answering your question is that there are an infinite number of possible rotations that would produce a rotation parallel to a new surface. The issue is that you are looking for a specific rotation not just one of the infinite rotations.
You can multiple a vector by a Quaternion to rotate it. So if you know the mapping between surface and the wall, you can just use a rotation. Assu$$anonymous$$g the camera is looking roughly forward along the 'Z' axis, then at appears that the wall is rotated 90 degrees along the 'Z' axis. So you can do:
forceDir = Quaternion.Euler(0.0, 0.0, 90.0) * forceDir;
But if you are careful about how you setup your wall, then an easier solution would be to pull the rotation from the object. For example if the wall started out looking like the floor when the rotation is (0,0,0), then you can just use the wall rotation. You can get that from the collision. It would just be something like:
forceDir = wallTransform.rotation * forceDir;
Or you could store a rotation in a script and acquire it on a collision. This would allow you personalize all the walls and tubes while keeping the ball code general.
P.S. The Vector3.Angle() for input to the AngleAxis() rotation in your existing code won't work because Vector3.Angle() returns an unsigned angle.
Just for completeness, assu$$anonymous$$g a wall is just a rotated floor, you can produce a rotation that keeps forceDir parallel with this code:
forceDir = Quaternion.FromToRotation(Vector3.up, wallTransform.up) * forceDir;
But the direction will not necessarily be the one you envision.
Hmm... I decided to try attaching scripts to my 'floors' like you suggested. Also I went all out on setting up some Debug.Draw stuff to help me visualize everything. Also I got gravity working properly. All that is left to do is rotating the input.
Green line is downward gravity (-Y axis), blue is the forward direction (+Z axis), green is the right direction (+X axis). Finally the pink line is the directional force that is being applied to the ball based on the Vector2 joystick touch input.
You probably can't tell very well from the picture, but the pink line is restricted to a ZX oriented plane. I need it to be restricted to the same plane as the Red and Blue lines.
$$anonymous$$ultiplying the floor's rotation by the joystick input (As Vector3(X,0.0,Y)) did not work.
I really find it hard to believe that this should be this much trouble. I tried looking into possible solutions using $$anonymous$$atrices, but they confuse the heck out of me. (As do quaternions)
A possible solution I see is maybe having around 4 empty gameobjects as children of the ball to act as directions that the ball can go in. When a new 'gravity' is found, they will rotate themselves to be on the same plane with the plane's normal being the direction of gravity. The issue with this is that how do I convert an input direction that is not purely "up" or "left"? Would something like Vector3.cross work for this?
$$anonymous$$aybe this whole issue calls for an entirely new value type that represents a rotated Vector2 plane in 3D space...?
Thanks again for all the help given so far! :)
For someone who understands rotations, understands all particular what they want to achieve, and is directly modifying the project, figuring this out would be quick. But it sometimes takes an awful lot of typing and some educated guessing to working back and forth through UA to figure out the solution.
In order to use the rotation of the object you hit, your construction must be consistent, and the floor must have no rotation. So for example, if you build your surfaces out of cubes, then the top of the cube should act as the floor, and the top of the cube must also be the surface you use for the walls.
You should be able to figure this out in scene view, but if you want to figure it out in game view, add this script to an empty game object:
var target : Transform;
function Update() { if (target != null) transform.rotation = target.rotation;
Debug.DrawRay(transform.position, transform.forward * 5.0, Color.red);
Debug.DrawRay(transform.position, transform.right * 5.0, Color.green);
Debug.DrawRay(transform.position, -transform.up * 5.0, Color.blue);
}
You can drag and drop the various floors and walls into 'target' in the inspector while the app is running. If you are multiplying by the rotation of a specific surface, then 'forward' will be wherever direction the red ray is pointing.
Your answer
Follow this Question
Related Questions
Rotate floor plane in-game via C# script 1 Answer
Why does this code not work? 1 Answer
How can I translate a Vector2 control system to Vector3 and rotate? 1 Answer
Get mapping postion of 1 point on a plane defined by 3 points 1 Answer
How to Rotate Plane of Cube Around its Center? (Rotate Vectors Around Point/Axis/Direction) 1 Answer