Control player z axis with mouse x axis - c#

I'm trying to move the player to the position of the mouse in my game.
Right now movement along the x axis works fine, but I want the mouse y axis to control the characters movement along the z axis (because of my top-down camera's y being world z).
Right now mouse y controls player y, which looks like this in game.
And the code for it looks like this:
public Vector2 mousePos;
private Vector3 playerPos;
void update()
{
// Get mouse position from player
mousePos = Input.mousePosition;
// Move player with mouse
playerPos = new Vector3(mousePos.x, 0, mousePos.y);
transform.position = Camera.main.ScreenToWorldPoint(playerPos);
}
I then tried to just swap the y and z like this
playerPos = new Vector3(mousePos.x, mousePos.y, 0);
But instead of allowing me to control the z axis this snippet instead causes the player to lose all movement.
I'm very new to coding so I might be missing something completely obvious. What am I doing wrong?
Unity version: 2018.4.21f1

The example you gave isn't working because you're providing a z coordinate of 0 and when called on a perspective camera, Camera.ScreenToWorldPoint does some vector math, so in your scene view, your player is probably floating right on top of your camera. I can't explain the actual math because I don't understand it, but luckily for both of us, we don't need to! Essentially, the z coordinate is saying how far away from the camera to place the point, and since the view frustum of a perspective camera gets narrower closer to the camera, placing it at 0 means there's nowhere for it to go. how moving the object farther from the camera requires it to move farther to match the mouse position.
The bigger problem is that your method is wrong and there's a better way. Camera.ScreenToWorldPoint converts a mouse coordinate to a world space coordinate depending on what the camera is looking at, so you're essentially flippng the y and z coordinate, then feeding it into a method that figures out what coordinates need to be flipped.
It looks like this script is attached to your player gameObject, so it should look like this:
Camera cam;
[Range(1,10)] //this just creates a slider in the inspector
public int distanceFromCamera;
void Start()
{
//calling Camera.main is a shortcut for
//FindGameObjectsWithTag("MainCamera")
//so you should avoid calling it every frame
cam = Camera.main;
}
void Update()
{
//This will work for both perspective and orthographic cameras
transform.position = cam.ScreenToWorldPoint(
Input.mousePosition + new Vector3(0,0,distanceFromCamera));
}
If your camera is going to move closer or farther from the plane, make sure to add something that keeps the distance from camera updated.
That said, for most games having the player tied to the mouse cursor isn't usually what you're looking for. Generally you want some kind of input to move the player in some direction a certain amount. If that's what you're looking for, this part of the space shooter tutorial is a good introduction to player movement (though the code itself may be outdated).

Related

How do I instantiate a Unity prefab at the mouse's position in C#?

I'm trying to instantiate my ballPrefab on click at the cursor's position.
I know that Input.mousePosition only takes the x and y coordinates. However, my camera looks in the direction of x (global) (as you can see here). So I changed x to 0. The problem is that the prefabs spawn at the same y and z position every time, no matter where I left-click.
This is my code.
[SerializeField]
private GameObject ballPrefab;
void Update()
{
Vector3 mousePos = Input.mousePosition;
mousePos = Camera.main.ScreenToWorldPoint(mousePos);
mousePos.x = 0;
if (Input.GetButtonDown("Fire1"))
{
Instantiate(ballPrefab, mousePos, ballPrefab.transform.rotation);
}
}
I searched the internet for like an hour.
What I came across was Cursor.Position, which seems to be old (?), since System.Windows.Forms doesn't exist.
I also tried making the mouse position a new Point , but then I can't convert it to a Vector3.
I also thought of transform.localPosition since my camera is facing the "wrong" direction, but I can't apply transform if I use Input.mousePosition .
Finally, I read about raycasts, but I don't really understand them.
To understand the issue it's best to understand how ScreenToWorldPoint works. Close one of your eyes and look straight ahead and don't move your eye (like a 2D camera). Then extend one of your arms and take your pointer finger and place it on top of something in the distance of your view (like a mouse pointer) but don't move your eye or look directly at it. The ScreenToWorldPoint function works somewhat by taking note of where your finger is on the X and Y axis of your eye's view, but it will also need to know how far out you extended your arm and finger on the Z axis (depth) to make your finger align with what you are pointing at. Like drawing a line from your eye to your finger.
The issue is that Input.mousePosition will return a Vector3 where the Z axis (depth) is 0. That means when you pass it to ScreenToWorldPoint it basically doesn't know how far out you extended your arm and finger. That is like taking your finger and placing it directly on top of your eyeball, but adding very little X or Y so the finger is still pointing at your object. You will get very small X and Y values, and the Z value will be whereever your eyeball (camera) is.
The other issue is that you are manually setting the resulting X value to 0. So X will always be 0, and the Z will always be at the camera's Z position, and the Y value will be very small, all because no depth value was passed to the ScreenToWorldPoint function. This will result in the ballPrefab always ending up in roughly the same location in the world.
So try to give it a depth reference like this:
public float distanceFromCamera = 5f;
public GameObject ballPrefab;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
Vector3 mousePos = Input.mousePosition;
mousePos = Camera.main.ScreenToWorldPoint(
new Vector3(mousePos.x, mousePos.y, distanceFromCamera));
Instantiate(ballPrefab, mousePos, ballPrefab.transform.rotation);
}
}
It seems you want the ball to always be on the X axis of the world, so another solution is to use the absolute value of the camera's X position (which is the distance the camera is from X = 0) as the depth value.
mousePos = Camera.main.ScreenToWorldPoint(
new Vector3(mousePos.x, mousePos.y,
Mathf.Abs(Camera.main.transform.position.x)));
This will allow you to move your camera in and out (in your case along the X axis) without having to adjust any distanceFromCamera variable.

Problem with 3rd person camera rotation in unity

I'm trying to make the camera rotate around an object according to the movement of the mouse, and this is what I've come up with:
void Start()
{
direction = new Vector3(1f,0f,0);
direction.Normalize();
Debug.Log(direction);
mousePosCurr = Input.mousePosition;
}
void Update()
{
mousePosPrev = mousePosCurr;
mousePosCurr = Input.mousePosition;
mouseVector = (mousePosCurr - mousePosPrev);
mouseVector = mouseVector * sensitivity;
direction = Quaternion.Euler(mouseVector.y, mouseVector.x, 0) * direction;
direction.Normalize();
Debug.Log(direction);
this.transform.position = (target.position + tOffset + (direction * distance));
transform.LookAt(target.position + tOffset, Vector3.up);
}
Now, I'm sure that there are at least a million problems with this code, however the main one I'm facing right now is that the x rotation does not work correctly, unless the y rotation is in a specific direction.
What's happening is basically that the more I rotate in the y axis the more "inprecise" the x movement becomes. So for example, when the camera is south of the object I'm rotating it around, the x rotation works exactly as it should, you move the mouse up, and the camera points up and viceversa, but when it's west of it the movement of the mouse required for the vertical rotation is no longer vertical, but diagonal, and when it's north of the object the movement is inverted: moving the mouse up makes the camera point down and viceversa.
This seems most likely caused by the fact that regardless of the horizontal rotation of the direction vector, I'm still rotating along the x axis in the same direction, as if the camera was south of the object.
However, even having (hopefully) identified the problem, I'm still clueless as to how to solve it, so I'm asking for help here.
Note: I just noticed that I probably used the term "rotation" wrong in this post. When I write "rotation of the camera", I'm actually referring to the rotation of the "direction" vector.
alright so I am not the best in Unity so this might be completely useless but when I try to rotate my camera around an object I create an empty game Object and make the camera a child of it, like this
so that I can just just rotate the Game Object to rotate the camera around a fixed point maybe your code will work

Make GameObject's Y Rotation be the same as the camera's

I have a Cinemachine Freelook camera and i want it similar to Skyrim, where the character's rotation follows where the camera is pointing, but only on the Y axis so that i can move my mouse and look at the character from up and down;
This video demonstrates how MrKrabs can move in 3 dimensions, but won't turn.
I already tried creating a blank object and putting it at the center of the character, then using transform.LookAt(Camera) to make it point to where the camera is, and finally getting the Y value of the object's rotation, inverting it with -transform.position.y and applying it to MrKrabs, but it didn't work: it was jittery and overall just a bad user experience, is there a way to turn the gameobject based on the camera's rotation? (having the camera always watching his back)
Something like this should do the trick.
transform.rotation = Quaternion.Euler(0, Camera.main.transform.eulerAngles.y, 0);
Beware that if your camera is a child of your object it will rotate with it.

How can I do this kind of 3D camera movement in Unity?

This is a school project. I have a big cube which is like a planet. I have a small cube that can move on every side of the cube planet. The player moves forward automatically on its local z axis and I rotate it by 90 or -90 degrees to move left or right- but the player can control it like it's an up-down-left-right movement (made a script so they don't have to rotate the cube with 2 keys). It's like a Tron game so if I press the up key, I can't press down immediately because I leave a trail so I have to press the left or right keys. I have a problem with my camera movement. The camera follows the little cube from above, but I want it to lean a little when the little cube player gets close to the edge of a side so it lets me see the other side - where I'm planning to go.
I have a script where I use the center of the cube planet and the player's center as the direction vector and multiply it with the camera-player distance.
Vector3 direction = (target.position - center.position).normalized;
transform.position = target.position + direction * distance;
Then I point my camera's forward vector to the player cube's direction. I tried two codes but none of them worked perfectly. First I tried to rotate the camera with:
transform.rotation = Quaternion.LookRotation(-direction);
Shown in video. The problem with this is that since I don't tell the program the Up and Right vectors (I think), it sets them for itself to some random numbers so the camera sometimes do its own thing like it does a pirouette.
Next I tried this:
Quaternion rot = Quaternion.FromToRotation(transform.forward, -direction);
transform.Rotate(rot.eulerAngles, Space.World);
Shown in video. It's almost good but as the little cube moves, the camera starts steering slightly in the beginning and it gets worse and worse.
Do you have any ideas how can I make it work? I hope you understand what I would like to achieve. I'm trying to set the camera as to always show that when I press Up, my cube moves upward, when I press Right my cube always goes right etc. and when I reach the edge of my side the camera leans a bit towards the side I'm moving to.
Use dot product to find which orthogonal direction of your cube is closest to the camera's current up.
Vector3 closestToUp;
float greatestDot = 0f;
foreach (Vector3 dir in new Vector3[]{
target.forward, target.right, -target.forward, -target.right})
{
float curDot = Vector3.Dot(dir,transform.up);
if (curDot > greatestDot)
{
greatestDot = curDot;
closestToUp = dir;
}
}
Than, use that direction as the second parameter of Quaternion.LookRotation. This will keep its local up direction as aligned with the cube's "top" as possible given whatever new forward direction:
transform.rotation = Quaternion.LookRotation(-direction, closestToUp);

How to rotate around a corner when the distance is unknown

I feel like this is a solvable problem but I am just to stupid to solve it.
I want to able to move a player around a cube in third person in a 3D game. Before I worry about the corners where 3 edges of the cube meet I would first like to be able to move around two edges. If I use the LookAt method while jumping around a corner some problems occur with the vertical rotation of the player. So if he enters the trigger of the edge he turns around 180 degree somehow. That is why I am searching for a different solution.
(Green rectangle is the player. The dotted area is a trigger.)
So what I am given on the entry of the trigger is the edge position P2 and obviously the player position P3. The player should be able to stop the velocity while being mid air, so he could just fall down to the edge thanks to the gravity. That means that I can not just lerp it from the entry point to some exit point. The entry and exit points are dynamic.
What I already calculated is the distance from the player to the edge. Therefore I could tell if the player is closer to the right surface or the left surface judging by whether X or Y is higher.
But if the player enter on a higher position the numbers are also higher.
I am very confused about how to solve this. I need to lerp rotate the player around the corner P2 as long as he is within the two dotted lines.
Here's something to get you started. This will rotate your player so that their down is pointing at the edge, and their forward vector tries to remain unchanged as much as possible. This will mean that moving in playerTransform.forward will move them where they are facing but will rotate them so that as they move around the edge, they will rotate around it.
transform playerTransform;
Vector3 edgeWorldDirection;
Vector3 edgeWorldPosition;
Vector3 playerWorldPosition;
// Determine up direction
Vector3 player2Edge = edgeWorldPosition - playerWorldPosition;
Vector3 playerUpDirection = (
Vector3.Dot(player2Edge, edgeWorldDirection) * edgeWorldDirection
- player2Edge).normalized;
// rotate player to align local up with playerUpDirection and forward with
// previous forward as much as possible.
Vector3 playerRightDirection = Vector3.Cross(playerUpDirection, playerTransform.forward);
// handle player looking "up"
if (playerRightDirection == Vector3.zero) playerRightDirection = player.Transform.right;
Vector3 playerForwardDirection = Vector3.Cross(playerRightDirection, playerUpDirection);
playerTransform.rotation = Quaternion.LookRotation(playerForwardDirection, playerUpDirection);
For corners it's actually easier because your up is based on the location of the corner, you don't have to calculate the closest point on a line.
transform playerTransform;
Vector3 cornerWorldPosition;
Vector3 playerWorldPosition;
// Determine up direction
Vector3 playerUpDirection = (playerWorldPosition - cornerWorldPosition).normalized;
// rotate player to align local up with playerUpDirection and forward with
// previous forward as much as possible. Same logic here as previously.
Vector3 playerRightDirection = Vector3.Cross(playerUpDirection, playerTransform.forward);
// handle player looking "up"
if (playerRightDirection == Vector3.zero) playerRightDirection = player.Transform.right;
Vector3 playerForwardDirection = Vector3.Cross(playerRightDirection, playerUpDirection);
playerTransform.rotation = Quaternion.LookRotation(playerForwardDirection, playerUpDirection);
Depending on how you're handling movement & player orientation, you may not actually want to rotate the player's transform, but with the results of Quaternion.LookRotation(...) you can multiply the local direction the player tries to go by that rotation to get the world direction that would correspond to.:
Vector3 localDesiredMove;
Quaternion boxOrientationQuat = Quaternion.LookRotation(
playerForwardDirection,
playerUpDirection);
Vector3 worldBoxOrientedMove = boxOrientationQuat * localDesiredMove;

Categories