Rotate around an object to its direction of velocity - c#

(For Unity 5.3.5f1)
Right now I am working on a 3D camera that is orbiting horizontally around the player. The player is a RigidBody rolling sphere. The player can freely rotate the axis horizontally but when there is no input I want the rotation to reset back to the direction of the velocity.
Right now all I need to know is how to situate the camera behind the player's direction of velocity by rotating the camera around the player from its previous rotation.
Here is a crude drawing of what I am looking for:
Currently to update the camera to orbit around the player I use this script on the camera (with comments):
using UnityEngine;
using System.Collections;
public class example : MonoBehaviour {
public GameObject Player;
//I assume you would use this PlayerRB to find data regarding the movement or velocity of the player.
//public Rigidbody PlayerRB;
private float moveHorizontalCamera;
private Vector3 offset;
// Use this for initialization
void Start ()
{ //the offset of the camera to the player
offset = new Vector3(Player.transform.position.x - transform.position.x, transform.position.y - Player.transform.position.y, transform.position.z - Player.transform.position.z);
}
// Update is called once per frame
void Update ()
{
moveHorizontalCamera = Input.GetAxis("Joy X"); //"Joy X" is my right joystick moving left, none, or right resulting in -1, 0, or 1 respectively
//Copied this part from the web, so I half understand what it does.
offset = Quaternion.AngleAxis(moveHorizontalCamera, Vector3.up) * offset;
transform.position = Player.transform.position + offset;
transform.LookAt(Player.transform.position);
}
}
Any help at all would be great!

I would suggest you use Vector3.RotateTowards (https://docs.unity3d.com/ScriptReference/Vector3.RotateTowards.html)
You take the Vector that is currently pointing from the Player to the camera (transform.position - Player.transform.position) and rotate it towards -Player.GetComponent<Rigidbody>().velocity.normalized * desiredDistance (the vector pointing in the opposite direction of the players velocity with magnitude corresponding the desired distance of the camera to the player). You can then use Vector3.RotateTowards to rotate the camera (smoothly or immediately) around the Player by setting the cameras position accordingly.
Something like:
transform.position = Player.transform.position + Vector3.RotateTowards(transform.position - Player.transform.position, -Player.GetComponent<Rigidbody>().velocity.normalized * desiredDistance, step, .0f);
where step if the angle update in radians. (Note: you should avoid calling GetComponent<Rigidbody>() every Update. You are better off storing it somewhere)
I hope I understood your question correctly and this helps.

Related

FlyCamera with collision

How to create collisions for a flying camera in Unity 3d? to prevent the camera from falling into objects on the scene. My camera script snippet:
public class FlyCamera : MonoBehaviour
{
void FixedUpdate()
{
if (!isAlternative)
SetCameraMovement();
}
private void SetCameraMovement()
{
lastMouse = Input.mousePosition - lastMouse;
lastMouse = new Vector3(-lastMouse.y * camSens, lastMouse.x * camSens, 0);
lastMouse = new Vector3(transform.eulerAngles.x + lastMouse.x, transform.eulerAngles.y + lastMouse.y, 0);
if (Input.GetMouseButton(1))
transform.eulerAngles = lastMouse;
lastMouse = Input.mousePosition;
GetBaseInput();
transform.position = Vector3.Lerp(transform.position, NewPosition, Time.deltaTime * movementTime);
}
private void GetBaseInput()
{
Speed = Input.GetKey(KeyCode.LeftShift) ? superSpeed : mainSpeed;
if (Input.GetKey(KeyCode.W))
NewPosition += transform.forward * Speed;
if (Input.GetKey(KeyCode.S))
NewPosition += transform.forward * -Speed;
if (Input.GetKey(KeyCode.A))
NewPosition += transform.right * -Speed;
if (Input.GetKey(KeyCode.D))
NewPosition += transform.right * Speed;
}
}
If you want to achieve a RTS-like flying camera, you can set the camera to a parent dolly-like object and keep it looking at that object:
Object (Camera Dolly) -> this is moved by wasd and is rotated to the desired view angle
Child object: Camera -> only the camera's position.y is ever changed
Then, on a custom script on the dolly in Update(), send a LineCast from the dolly to the camera. When the cast hits a collider (important to use e.g. tags here so it does not collide with anything, like e.g. units) in the world, gradually (over time) reduce the camera's position.y and if nothing is hit, gradually increase it to the desired height. It will not entirely prevent the camera from clipping trough bigger objects but move it so it doesn't stay "inside" a collider. Something like that is also typically used in RPG third person cameras.
Please let me know if more clarification is needed!
Edit: I'd do that only for really big view-blocking objects though, not for the terrain or smaller buildings/units - at least if your goal is more of an RTS or sim game.
Edit2: In a solar system setting, it is a bit more complicated. A few options:
Before translating the camera, do a SphereCast (in the desired (final) movement direction with sphere radius of minimum allowed distance to any object) against all celestial object SphereColliders and set your "NewPosition" to the nearest hitPoint minus the desired minimum allowed distance to a celestial object. Also add some margin here, so the camera doesn't get stuck. This would be a continuous collision detection approach.
Everytime after translating the camera , do a CheckSphere (sphere radius is allowed minium distance to any object) against all celestial objects and as long as there is any hitpoint, lerp the camera position out of it over time. This is a "softer" looking approach, but if you fly very fast, it can still clip into something.
You can savely do both in LateUpdate().
Better put you camera movement in the LateUpdate() as adviced in the docs.
With a rigidbody and a collider properly set for the collision detection. Check docs so that you can detect the collisions properly.
Check that " Notes: Collision events are only sent if one of the colliders also has a non-kinematic rigidbody attached", so your camera could be one gameObject with no rigidbody, so that you can fly free and collide.
I case you might need physics behaviour for yout camera and you want it also to behave as a rigidbody I would set the camera movement with rigidbody.SetPosition instead of with transform.position = new Vector3(), as you need to choose to set the transform in the geometric or the physics world.
Also you would need to uncheck the gravity option not to fall.

Unity: TopDown view - GameObject does not rotate toward mousepos

I've been doing some research on why my player(GameObject) is does not rotate toward my mouse position in my TopDown 3D game and I can't seem to find what is wrong with my code, so im making this post. The problem thats I have is that only the GameObject of my player (in my case, a capsule) rotate toward my mouse position but the axis of my player stays the same. In other word, I can't rotate the axis of my player, to face my mouse position, but I can rotate the GameObject of my player to face my mouse position. Its really hard to explain and this never happened to me before. Question is how can I rotate the axis of my player to face my mouse position. Keep in mind that my game is a top down view.
Here is the code im using for my playerMouvment and for my mouseLook:
public class Controller : MonoBehaviour
{
public float moveSpeed = 6;
Rigidbody rb;
Camera viewCamera;
Vector3 velocity;
void Start()
{
rb = GetComponent<Rigidbody>();
viewCamera = Camera.main;
}
void Update()
{
Vector3 mousePos = viewCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, viewCamera.transform.position.y));
transform.LookAt(mousePos + Vector3.up * transform.position.y);
velocity = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")).normalized * moveSpeed;
}
void FixedUpdate()
{
rb.MovePosition(rb.position + velocity * Time.fixedDeltaTime);
}
}
Again I tried and look for any error in my code and I can't find anything that cause this weird situation and so if anyone can help me find a better way to write this code and solve my problem it would be great!
If I understand you correctly everything is fine with your game and the player is turning as it should, but only in the editor that the axis of the player (red, green and blue arrows) don't turn with the player?
If this is the problem it might be that you are using global space handle instead of local space. Clicking the icon I highlighted in the image should do the trick.
Use this code instead of LookAt() function
Vector2 direction = mousePos.position - player.transform.position;
player.transform.right /* Maybe you need Up or -Up or -right */ = direction;
This would work too in some cases
Vector2 direction = new Vector2
(
mousePos.position.x - player.transform.position.x,
mousePos.positoin.y - player.transform.position.y
)
player.transform.right /* Maybe you need Up or -Up or -right */ = direction;

How can a make the camera follow a rolling ball from behind?

I am using unity 2018.3.5f1 so a few solutions to this problem don't work.
Starting from the Roll-A-Ball demo project, I want to extend the camera script to follow the player, but also keeping track of the direction of movement. Ex: If the ball starts moving to the side, I need the camera to rotate around the player's axis to position behind him, and then start following him.
I have tried making the camera a child to the ball i'm trying to control and i've tried making a script but it won't follow the ball correctly it keeps rotating the camera as well as the ball (i'm using this bit of code and trying to modify it as it's the only one that works)
here is the code I have at the moment:
using UnityEngine;
using System.Collections;
public class CompleteCameraController : MonoBehaviour
{
public GameObject player; //Public variable to store a reference to the player game object
private Vector3 offset; //Private variable to store the offset distance between the player and camera
// Use this for initialization
void Start()
{
//Calculate and store the offset value by getting the distance between the player's position and camera's position.
offset = transform.position - player.transform.position;
}
// LateUpdate is called after Update each frame
void LateUpdate()
{
// Set the position of the camera's transform to be the same as the player's, but offset by the calculated offset distance.
transform.position = player.transform.position + offset;
}
}
I understand why it is rotating but no reasearch is helping me find out how to lock the camera so it is looking behind the ball at all times
Have you tried adding some restraints to your camera? If it is only supposed to turn to follow the ball, you can freeze the position of 2 axes (x and z), and only let the camera rotate around the y axis of the ball.
You can play around with the restraints on the Inspector, or use GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezeRotationX; in the camera script.
(EDIT) Sorry, this is how you would do it if the gameObject has a rigidbody, but the idea is the same if it doesn't have one.
When you're setting the position or rotation of the camera, think about which components of the position/rotation you actually want to change (x,y,z).
Use offset = new Vector3 (offset.x, offset.y, offset.z)and replace anything that shouldn't change with a constant.
Also, when you made the Camera the child of the ball, you could have set it so that every frame, the Camera updates in such a way that it won't roll with the ball. You could do this by putting code in the update method of the camera that sets the component x, y, or z equal to some constant. Whatever axis the ground plane is on is probably the correct choice.
If you want the same camera angle that the camera should follow while following the ball (similar to Temple Run), then try the below code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class CompleteCameraController : MonoBehaviour
{
public Transform Player;
public float distanceFromObject = 3f;
void Update()
{
Vector3 lookOnObject = Player.position - transform.position;
lookOnObject = Player.position - transform.position;
transform.forward = lookOnObject.normalized;
Vector3 playerLastPosition;
playerLastPosition = Player.position - lookOnObject.normalized * distanceFromObject;
playerLastPosition.y = Player.position.y + distanceFromObject / 2;
transform.position = playerLastPosition;
}
When the ball moves left or right, the camera will follow the same angle as the ball moves towards.

How to make LookAt align the transform.up vector instead of transform.forward?

The problem is that i cannot make a script that makes the enemy rotate so that "up" is pointing against the player, (with that i mean so that it will come to the player with transform.up) And i have not found any working solution yet, (from what i can tell)
I am trying to make a small 2D game, just for my friends to play. What i have tried is making the enemy look at the player and then set the "z" rotation to the same as the "x" rotation and after that reset the "x" and "y" rotation (so that "up" is pointing at the player). But the enemy just goes straight up.
using UnityEngine;
public class Enemy : MonoBehaviour
{
public Transform Player; // The Transform of the Player that you can controll
public float MoveSpeed = 4; // How fast the enemy should go
void FixedUpdate()
{
transform.LookAt(Player);
transform.rotation = Quaternion.Euler(transform.rotation.x, 0, transform.rotation.x);
transform.rotation = Quaternion.Euler(0, 0, transform.rotation.z);
transform.position += transform.up * MoveSpeed * Time.deltaTime;
}
}
So what i want to happen is that the Enemy will move to the Player but the rotation part is not working and it just goes straight up in the air instead of following the player. I might just be stupid and have missed something simple...
By using
transform.rotation = Quaternion.Euler(transform.rotation.x, 0, transform.rotation.x);
transform.rotation = Quaternion.Euler(0, 0, transform.rotation.z);
both times you overwrite the rotation with new values additionally mixing Euler (Vector3) with Quaternion fields ... transform.rotation.x is not the pure rotation around the X-axis you see in the Unity inspector.
It depends a bit on how exactly you want the enemy rotated in the end but you could simply use Transform.RotateAround like
transform.LookAt(Player);
transform.RotateAround(transform.position, transform.right, 90);
LookAt by default results in a rotation such that transform.forward is pointing at the target, transform.right remains pointing right(ish) and transform.up remains pointing up(ish). So all you have to do is rotating around that local X-axis transform.right exactly 90° so now transform.forward points down instead and transform.up points at the target.
which results in
If you than also want to change the other axis you can still additionally rotate it around the local transform.up axis as well e.g. to flip the forward vector "upwards":
transform.LookAt(Player);
transform.RotateAround(transform.position, transform.right, 90);
transform.RotateAround(transform.position, transform.up, 180);
Is there actually a special reason why you are doing that in FixedUpdate and not rather in Update? FixedUpdate should only be used for Physics related stuff.

Unity 3D C# camera - nudging/moving camera in direction of mouse or joystick

I am making a top down shooter with a 45 degree camera angle. In my scene I have the camera childed to the player to follow the player. I have seen in a couple of games like "Secret Ponchos" where the camera slightly floats to the direction the player is aiming. It's subtle but adds polish.
I have tried a couple of different ways but don't know how to get the Vector for the Lerp.
Is there a way I can base the nudging based on mouseMovement? If so How?
To get the position to use for the Lerp of your camera control, you just need to figure out the direction you want the camera to nudge in and add that to the player's position.
One option is to use transform.forward to use the direction your player is facing however this requires you to rotate your player character.
//Assign the player's transform here
public Transform Target;
Vector3 GetNudgeDirection () {
return Target.forward;
}
Another method would be to get the direction of the mouse relative to the player.
public Transform Target;
Vector3 GetNudgeDirection () {
//Get the position of the mouse
Vector3 mousePos = Input.mousePosition;
mousePos.z = -Camera.main.transform.position.z;
Vector2 inputPos = Camera.main.ScreenToWorldPoint(mousePos);
//returns direction from the player to the mouse pos.
return (inputPos - (Vector2)Target.position).normalized;
}
You would then add the nudge direction to the target's position to get where your camera should aim at.
//This field determines how far to nudge
private float nudgeDistance = 2f;
Vector3 GetTargetPosition () {
return Target.position + (GetNudgeDirection() * nudgeDistance);
}
Keep in mind the target position is where your camera should look, not where it should move to! So when you are actually moving your camera, add an offset to the target position so it maintains its distance.

Categories

Resources