Jump to content
UnitySpain
nokiz

[Solucionado] Mover cámara de un punto a otro

Recommended Posts

Hola! Mi problema es que quiero mover la cámara de un punto a otro al pulsar una tecla (v).

Quiero que el movimiento sea gradual, no que simplemente cambie de un punto a otro en un instante. Tambíen que si se vuelve a pulsar la tecla, vuelva a la primera posición.

Todo lo que he investigado me ha llevado a lo mismo, pero lo único que consigo es que la cámara se mueva por un instante pero inmediatamente vuelve a su posición inicial. ¿Qúe estoy haciendo mal?

Os dejo el script que le estoy aplicando a la cámara. Muchas gracias!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraController : MonoBehaviour
{
    // Assigned in inspector
    public Transform firstPersonCamera = null;
    public Transform thirdPersonCamera = null;
  
    private int state = 0;
    private Transform target = null;
    void Update () {
        if (Input.GetButton("LeftStickButton") || Input.GetKeyUp("v")) {
            state = 1;
        }
    }

    void LateUpdate() {
        if (state == 1) {
            if (Vector3.Distance(transform.position, thirdPersonCamera.position) < 0.001f) {
                target = firstPersonCamera;
                transform.position = Vector3.MoveTowards(transform.position, target.position, 2 * Time.deltaTime);
            } else {
                target = thirdPersonCamera;
                transform.position = Vector3.MoveTowards(transform.position, target.position, 2 * Time.deltaTime);
            }
        }

        if (state == 1 && Vector3.Distance(transform.position, target.position) < 0.001f) {
             state = 0;
             target = null;
        }
    }
}

Aclaración: firstPersonCamera y thirdPersonCamera son gameObjects vacios en los puntos a los que quiero que se mueva la cámara.

Edited by nokiz
Solucionado

Share this post


Link to post
Share on other sites

Yo tampoco he usado MoveTowards. Tengo un script muy similar que hice hace un tiempo, le cambié los nombres y le hice una pequeña adaptación para lo que buscas:
Quizá es demasiado complejo para lo que necesitas ya que hago uso de curvas, pero está bastante claro el tema del Lerp en vectores y cuaterniones.

using UnityEngine;

public class PointOfViewHandler: MonoBehaviour
{
    [Header("")]
#pragma warning disable IDE0044 // Add readonly modifier
    [SerializeField] private new Transform camera;

    [Header("References of points of view")]
    // Assigned in inspector
    [SerializeField] private Transform firstPerson = null;
    [SerializeField] private Transform thirdPerson = null;

    public enum PointsOfView
    {
        FIRST_PERSON,
        THIRD_PERSON
    }
    [Header("")]
    [SerializeField] private PointsOfView currentPointOfView = PointsOfView.FIRST_PERSON;

    [Header("Lerp parameters")]
    [SerializeField] private float lerpTime = 25;
    [SerializeField] private AnimationCurve smoothCurve = new AnimationCurve( new Keyframe[]{ new Keyframe( 0, 0 ) , new Keyframe( 1, 1 ) } );
#pragma warning restore IDE0044 // Add readonly modifier

    private float currentLerpTime;
    private Transform target;

    private void Awake()
    {
        GetCamera();
        SetInitialPosition();
    }

    private void GetCamera()
    {
        if ( camera == null )
            try { camera = Camera.main.transform; }
            catch
            {
                try { camera = FindObjectOfType<Camera>().transform; }
                catch { Debug.LogError( "Assign a camera" ); }
            }

        camera.transform.SetParent( transform );
        camera.transform.localPosition = Vector3.zero;
        camera.transform.localRotation = Quaternion.identity;
    }

    private void SetInitialPosition()
    {
        if ( firstPerson == null || thirdPerson == null )
        {
            Debug.LogError( "Point of view not assigned" );
            return;
        }
        switch ( currentPointOfView )
        {
            case PointsOfView.FIRST_PERSON:
                transform.position = firstPerson.position;
                transform.rotation = firstPerson.rotation;
                break;
            case PointsOfView.THIRD_PERSON:
                transform.position = thirdPerson.position;
                transform.rotation = thirdPerson.rotation;
                break;
        }
    }

    private void Update()
    {
        if ( Input.GetKeyDown( KeyCode.V ) )
            ChangePoint();

        if ( target == null ) return;

        if ( Vector3.Distance( transform.position, target.position ) < 0.01f )
        {
            transform.position = target.position;
        }

        Movement();
    }

    private void Movement()
    {
        if ( target == null ) return;

        currentLerpTime += Time.deltaTime;
        float percent = smoothCurve.Evaluate(currentLerpTime / lerpTime);

        Translate( percent );
        Rotate( percent );
    }

    private void Translate( float percent ) => transform.position = Vector3.Lerp( transform.position, target.position, percent );

    private void Rotate( float percent ) => transform.rotation = Quaternion.Lerp( transform.rotation, target.rotation, percent );

    private void ChangePoint()
    {
        currentLerpTime = 0f;
        currentPointOfView = currentPointOfView == PointsOfView.FIRST_PERSON ? PointsOfView.THIRD_PERSON : PointsOfView.FIRST_PERSON;
        switch ( currentPointOfView )
        {
            case PointsOfView.FIRST_PERSON:
                target = firstPerson;
                break;
            case PointsOfView.THIRD_PERSON:
                target = thirdPerson;
                break;
        }
    }
}

Simplemente creás un objeto en la escena y le añadís este componente.
Para cambiar puntos de vista del player te recomiendo una curva simple (probablemente la que seteo por defecto) y una velocidad bastante alta (Lerp Time bajo), esto lo configurás en el inspector.
Si no asignás la cámara, el script tomará la primera que encuentre en la jerarquía de la escena.

Share this post


Link to post
Share on other sites
10 minutes ago, Cappa said:

Yo tampoco he usado MoveTowards

52 minutes ago, pioj said:

Nunca he usado MoveTowards

MoveTowards nace de estar siempre haciendo (Ej con Vector3):

public float deltaPos;  //suponiendo que se elige el salto
Vector3 start , end;

...

float distanciaRestante = Distance( start , end );
"Move"( distanciaRestante < deltaPos ? distanciaRestante : deltaPos );
//Move incluye el vector de direccion
// cuando se da que (distanciaRestante < deltaPos) la pos se iguala al destino

La versión MoveTowards:

Vector3 resultado = Vector3.MoveTowards( start , end , deltaPos );

 

3 hours ago, nokiz said:

Hola! Mi problema es que quiero mover la cámara de un punto a otro al pulsar una tecla (v).

Quiero que el movimiento sea gradual, no que simplemente cambie de un punto a otro en un instante. Tambíen que si se vuelve a pulsar la tecla, vuelva a la primera posición.

Todo lo que he investigado me ha llevado a lo mismo, pero lo único que consigo es que la cámara se mueva por un instante pero inmediatamente vuelve a su posición inicial. ¿Qúe estoy haciendo mal?

Os dejo el script que le estoy aplicando a la cámara. Muchas gracias!

Hola Nokiz, un par de cosas:

  • Por gradual te referís a suavizado? si es así usá Lerp o SmoothDamp (o mejor aun curvas personalizadas de animación, pero quizás sea más dificil de implementar)
  • Usar Update y luego LateUpdate en tu código no tiene tanto sentido, distinto si usaras físicas, en ese caso acumular inputs en update y resetearlas en FixedUpdate tiene sentido, pero acá no tanto (quizás sacaste la idea de algun ejemplo ?)
  • Usar movetowards te asegura que el objeto llega siempre a destino, no es necesario andar midiendo si es menor a 0.001f o parecido
  • Planteatelo como estados, todo es más fácil que andar poniendo if's por todos lados, está bien (por lo menos el nombre) que hayas puesto state, pero lo que menos hace eso es ser un estado, y sobretodo porque es un int, usá algo así:
// Por ej fuera o dentro de la clase

public enum MovementState
{
	Idle, Moving
}
MovementState currentState;
Vector3 currentTarget;

--- (en Update/LateUpdate, etc) ---

switch(currentState)
{
	case MovementState.Idle:
	
	if( presionas la tecla esa )
	{
		target = "el target que te guste"
		currentState = MovingState.Moving;
	}

	break;

	case MovementState.Moving:

	if( presionas la tecla esa )
		target = target == start ? end : start; //se conmuntan los targets,start vs end

	if( se da la condición de arribo exitoso )
		currentState = MovingState.Idle;

	Move();

	break;
}


void Move()
{
	// lo que haga tu método de movimiento aquí, por ej Lerp
	...
}

(Lo hice al vuelo)

Share this post


Link to post
Share on other sites

Perdón por tardar en contestar, pero gracias a todos!

Al final he conseguido el efecto que quería ayudado por vuestros consejos.

Quote

Nunca he usado MoveTowards, así que mi pregunta es "¿Por qué no estás usando LERP?

En respuesta a @pioj he de decir que leí que muchas veces se abusa del lerp cuando se puede usar MoveTowards que es más eficiente para la cpu.

Quote

-Usar Update y luego LateUpdate en tu código no tiene tanto sentido, distinto si usaras físicas, en ese caso acumular inputs en update y resetearlas en FixedUpdate tiene sentido, pero acá no tanto (quizás sacaste la idea de algun ejemplo ?)
-Usar movetowards te asegura que el objeto llega siempre a destino, no es necesario andar midiendo si es menor a 0.001f o parecido

Toda la razón, @lightbug

Gracias, @Cappa, me guardo tu script. Me vendrá bien más adelante.

Os dejo con el código con el que al final lo he solucionado por si le viene bien a alguien en un futuro.

public class CameraController : MonoBehaviour
{
    // Vars assigned in inspector (public)
    public Transform firstPersonCamera = null;
    public Transform thirdPersonCamera = null;
    // Private vars
    private Transform target = null;
    void Update () {
        if (Input.GetKeyUp("v")) {

            if (transform.position == thirdPersonCamera.position) {
                target = firstPersonCamera;
            } else {
                target = thirdPersonCamera;
            } 
        }
        if (target) {
            transform.position = Vector3.MoveTowards(transform.position, target.position, 5 * Time.deltaTime);
        }
    }
}

 

Share this post


Link to post
Share on other sites
On 4/5/2019 at 5:39 PM, nokiz said:

transform.position = Vector3.MoveTowards(transform.position, target.position, 5 * Time.deltaTime);

¡A mi me ha ayudado!

Una línea que hace justo lo que necesito y sin complicaciones :)

Share this post


Link to post
Share on other sites

El componente de Cinemamachine es muy fácil de utilizar y puede realizar movimientos de cámara muy complejos.

Se pueden colocar diferentes cámaras e ir cambiando entre ellas con solamente enable true/false incluido movimiento transicional entre ellas. Estas cámaras pueden realizar seguimiento de cualquier target con smoothing y damping autómaticamente.

Share this post


Link to post
Share on other sites

×
×
  • Create New...