Jump to content
UnitySpain

Aceptamos donaciones vía Paypal.

UnitySpain.com es un servicio gratuito, pero mantener la Comunidad conlleva una serie de gastos.

Fondo Anual Unityspain: Donados 15,00€ de 150,00€

  • Servidor: Dominio.com y Hosting Web
  • Mantenimiento de los Foros
  • Contenido y Servicios Extras
  • Mantenimiento para Redes Sociales
Sign in to follow this  
nomoregames

Gravedad moldeable

Recommended Posts

Buenas noches a todos

Yebo tiempo intentando hacer una especie de gravedad moldeable (como la de super mario galaxi), se me ocurrió en hacer que un raycast saliera de los pies de el personaje hacia abajo,  y usando normalized, calcular en que dirección se tenia que aplicar la fuerza de gravedad

    Rigidbody RB;
    private void Start()
    {
        RB = GetComponent<Rigidbody>();
    }
    void Update()
    {
       
        RaycastHit hit;
        if (Physics.Raycast(transform.position,transform.forward, out hit, 10000)){
            transform.rotation = Quaternion.LookRotation(-hit.normal);
            RB.AddForce(-hit.normal * 100);
           RB.velocity = ( transform.up * Input.GetAxis("Vertical") * 10);
    }
}

el caso es que, a parte de ser muy ineficaz, en algunas zonas el personaje empieza a dar vueltas de 180 grado... (no se si me explico)

alguna idea de que puede estar pasando, o si sabéis otro modo de lograr lo que busco  agradecería mucho que lo explicarais.

Gracias de antemano

 

 

Share this post


Link to post
Share on other sites

hola,

acabo de hacer esto probando

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

public class playerGalaxy : MonoBehaviour {

    private float camAngleX = 0;
    private float camAngleY = 0;
    private float sensitivity = 10f;

    private float speed = 10;
    private float gravy = 0;
    private float gravyPower = 8.8f;

    public Transform cam; //camera
    private Vector3 oldPos = Vector3.zero;
    private Vector3 move = Vector3.zero;


    // Use this for initialization
    void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {

        float dTime = Time.deltaTime;
        
        //look
        camAngleY *= 0.8f;
        camAngleY += (Input.GetAxis("Mouse X")) * sensitivity * dTime;
        camAngleX -= (Input.GetAxis("Mouse Y")) * sensitivity * dTime;
        camAngleX = Mathf.Clamp(camAngleX, -80f, 80f);
        cam.position = oldPos + (transform.up * 0.6f);
        transform.Rotate(0, camAngleY, 0);
        cam.rotation = transform.rotation;
        cam.Rotate(camAngleX, 0, 0);
        cam.position -= cam.forward * 5f;


        //move
        oldPos = transform.position;
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        Vector3 mov = new Vector3(h, 0, v);
        if (mov.magnitude > 1) {
            mov.Normalize();
        }
        move = Vector3.Lerp(move, mov, 0.1f);
        Vector3 moveDir = ((move.x * transform.right) + (move.z * transform.forward)) * speed * dTime;
        //transform.position += moveDir;


        //physics
        Vector3 newUp = Vector3.zero;
        RaycastHit hit;
        float radius = 0.4f;
        bool inGround = false;
        //forward (move dir)
        if (Physics.SphereCast(transform.position, radius, moveDir.normalized, out hit, moveDir.magnitude+0.01f)) {
            transform.position += moveDir.normalized * hit.distance;
            //newUp = hit.normal;
        } else {
            transform.position += moveDir;
        }

        //down
        if (gravy > 0) {//falling
            if (Physics.SphereCast(transform.position, radius, -transform.up, out hit, (gravy * dTime) + 0.3f)) {
                transform.position -= transform.up * (hit.distance-0.1f);
                newUp = hit.normal;
                gravy = 0;
                inGround = true;
            } else {
                transform.position -= transform.up * gravy * dTime;
            }
        } else {//jumping
            if (Physics.SphereCast(transform.position, radius, transform.up, out hit, -((gravy * dTime) + 0.2f))) {
                transform.position += transform.up * hit.distance;
                //newUp = hit.normal;
                gravy = 0;
            } else {
                transform.position -= transform.up * gravy * dTime;
            }
        }

        if (inGround) {
            if (Input.GetButtonDown("Fire1") || Input.GetButtonDown("Jump")) {
                gravy = -7f;//jump
            }
        }

        gravy += gravyPower * dTime;
        
        //rotation
        if (newUp.magnitude > 0.1f) {
            Vector3 left = Vector3.Cross(transform.forward, newUp);
            Vector3 newForward = Vector3.Cross(newUp, left);
            Quaternion oldRot = transform.rotation;
            Quaternion newRot = Quaternion.LookRotation(newForward, newUp);
            transform.rotation = Quaternion.Lerp(oldRot, newRot, 0.15f);
        }



    }
}

es andar por las paredes al estilo mario galaxy.

en tercera persona.

lo he provado con una Sphere de tamaño 1 (la que sale por defecto) poniendole un rigidBody al que le he desactivado la gravedad y le he "freezeado" los tres angulos de rotacion (los tres "freeze rotation")

tiene algun problema como que caes hacia abajo (del transform) en vez de hacia al "suelo" mas cercano...

habria que ponerle un "Physics.OverlapSphere"  que fuera creciendo para buscar el "suelo" mas cercano cuando estas en el aire...

ya le pondre luego, ahora me tengo que ir a hacer unos recados...

espero que te sirva...

luego (o mañana) ya lo mejorare... lo he hecho en 5 minutos... y tiene "fallos"

 

Share this post


Link to post
Share on other sites
6 minutes ago, nomoregames said:

Wow, no entendi nada...

es un script que he hecho pa probar a hacer algo tipo mario galaxy... tiene algun fallo, ya lo arreglare....

la primera parte es para controlar la camara (y hacia donde mira el player)

la segunda parte es para mover el player.

la siguiente parte son dos "sphereCast", uno hacia donde te muevas y otro hacia abajo (segun este orientado el player)

y con ese ultimo "sphereCast" cojo el valor de como esta orientado el suelo "newUp"... y en la ultima parte roto el player para que su "up" coincida con el "newUp", osea para que se ponga "de pie" en la superficie inclinada/pared como en mario galaxy...

prueba a hacer una sphere (tamaño default) ponerle un rigidBody (sin gravity, y dandole a los tres "tick" de freeze rotation) ...y ponerle el script... y deveria medio bien.... falla alguna cosilla... y si el angulo entre una superficie y otras es demasiado, el player no cambiara de superficie, no se adaptara al nuevo suelo... osea de suelo liso a pared de 90 grados no va a funcionar.... aunque si quieres se puede hacer que si funcione... pero de momento pense que seria mejor que no... asi hay "paredes" y no todo es "suelo"

Share this post


Link to post
Share on other sites

Creo que ya empiezo a comprender...

pero  aparir de lo entendido, por que usas spherecast y no raycast??

por que multiplicas cam angle *= 0.8?

 

Estoy intentando entenderlo, pero esto (por ridículo que suene), es demasiado complejo para mi..., así que no te frustres si te hago demasiadas preguntas estúpidas :7_sweat_smile:

Share this post


Link to post
Share on other sites

hola 

a "cam Angle X" le sumo lo que muevas el raton (arriba/abajo)... para que valla "disminullendo" poco a poco hasta cero lo que hago es multiplicarlo por 0.8f asi poco a poco frena.. y el movimiento de la camara queda mas suabe al detenerse.. en vez de parar de golpe es como que sigue un poco en la direccion que moviste el raton parando lentamente.... es algo que suelo hacer...

 

esfereCast tiene volume y rayCast no...

es un poco lio explicarlo...

porejemplo si justo esta el player en un el borde de dos planos con diferente inclinacio.... donde se juntan los dos planos.... en un pico, (en montecito)

imaginatelo en 2d...

algo asi: /\

pero menos inclinado

y el player justo en el medio, arriba

...si lo hago con rayCast, y el player esta un pelin a la derecha, muy muy poco, detectara que el suelo esta inclinado hacia la derecha.... y al rotar un poco el player para adaptarse al suelo, el rayo saldria justo hacia abajo suya (igual que si fuera el sphereCast) pero al ser fino, delgado, colisionara en el otro plano, en el de la izquierda, porque el player esta inclinado hacia la derecha, entonces como ha detectado el plano de la izquierda, se rotara para inclinarse a la izaquierda, pero entonces el rayCast (fino) colisionara con el plano de la derecha.... y entonces el player se inclinara a la derecha... y asi todo el rato... ira haciendo efecto pendulo, o mas bien efecto metronomo (el cacharro para medir el tiempo al tocar instrumentos musicales)...

pero si el rayo es gordo (sphereCast) eso no pasara, porque colisionara antes con el suelo que tenga debajo,  osea si esta inclinado a la derecha, en vez de colisionar el rayo (fino) con el plano de la izquierda, como el sphere es gordo colisionara con el plano de la derecha, y entonces el player se mantendra inclinado a la derecha correctamente

no se si me he explicado bien....

si te mando unos dibujos explicando igual sera mas facil de entender...:7_sweat_smile:

Untitled.png

arriba seria con rayCast

1- lanza rayo (en rojo) hacia abajo, detecta que el suelo esta inclinado a la derecha (en verde)

2-al inclinarse a la derecha el rayCast detecta el suelo de la izquierda, que esta inclinado hacia el otro lado... y entonces el player rotara para inclinarse hacia el otro lado... 

3-al inclinarse hacia el otro lado, vuelve a detectar el suelo de la derecha....

y asi el player entraria en un bucle entre las imagenes 2 y 3.

pero si lo hacemos con sphere (imagenes de abajo) eso no pasa

 

PD: sorry por los dibujos tan cutres...

Edited by Igor

Share this post


Link to post
Share on other sites
On 1/24/2020 at 2:59 PM, nomoregames said:

if (Physics.Raycast(transform.position,transform.forward, out hit, 10000)){ transform.rotation = Quaternion.LookRotation(-hit.normal); RB.AddForce(-hit.normal * 100); RB.velocity = ( transform.up * Input.GetAxis("Vertical") * 10);

HOla, Sumando a lo que dijo @Igor, fijate que en las líneas de RB en una estás agregando fuerzas, todo bien, pero después estás pisando la velocidad directamente (anulando lo anterior). Está bien eso?

El efecto está muy bueno,  lo quiero implementar para mi asset, para mi este efecto siempre funcionó de manera algo predictiva o usando un campo vectorial (no solo es un cast y listo. Fijate que cuando salta hacia un muro recto, lentamente va cambiando la gravedad).

Predictiva: Una posibilidad puede ser que (por ej) cuando mario salta mientras corre tenés el típico caso del tiro oblicuo (caso general), con eso podés detectar en el aire si va a colisionar con una pared varios frames antes (simplemente mediante la velocidad que tiene), de esta forma podés ir rotando a mario antes de que toque la superficie. Con planetas super suavizados no te das cuenta, pero con superficies más abruptas es útil.

Campo vectorial (y para mi la que usó nintendo): es calcular disponer de un campo vectorial, cuyos vectores sean los valores de gravedad (o también funciona con los "up"). Sería como un bake. Eso sí, si el objeto es dinámico no se, quizás se pueda calcular frame a frame para resoluciones bajas.

Share this post


Link to post
Share on other sites

ah, es verdad!

@lightbug tiene un asset muy bueno que hace eso (pero en 2d)

y como dice el, creo que nintendo utiliza "gravedad por zonas", por areas.... quizas usando triggers (colliders) que cambien la gracedad depende de donde estes.... no como lo que he hecho yo... pero el efecto se aproxima....

Share this post


Link to post
Share on other sites

Saludos

 

1 hour ago, lightbug said:

Está bien eso?

No, no era intencional.... :7_sweat_smile:

 

43 minutes ago, Igor said:

"gravedad por zonas"

Si... si ya lo pensé, pero no quiero llenar la escena de colliders, y aparte, tampoco sabría como hacerlo... jejejeje

Por lo demás sigo intentando comprender tu script

PD: Tengo otra duda

por que haces que la posición de cámara sea igual a oldPos (que bale cero) + transform.up * 0.6?

Share this post


Link to post
Share on other sites
4 hours ago, Igor said:

@lightbug tiene un asset muy bueno que hace eso (pero en 2d)

:12_slight_smile::91_thumbsup:

4 hours ago, nomoregames said:

Si... si ya lo pensé, pero no quiero llenar la escena de colliders, y aparte, tampoco sabría como hacerlo... jejejeje

Yo tampoco tengo en claro como haría algo así. Se me ocurre recorrer toda la escena (o por lo menos lo relevante) e ir sampleando normales mediante algun cast. Puede parecer una locura pero no, no es tan demandante, sobretodo si se hace una sola vez (además de que se puede hacer en el mismo editor :10_wink:).

El tema de los colliders puede ser algo pesado de hacer, pero poder se puede.

Ya que está dejo un video que encontré recientemente donde el autor dice como hizo lo que hizo (no se si es 100% lo que querés pero puede sumar):

Y él/ella dice:

" I do a raycast from the player to the center of each gravity body collider in the scene, check for which raycasthit point is the nearest, then use the normal to determine the gravity direction each timestep. Hope that gives you an idea! "

Share this post


Link to post
Share on other sites
On 1/26/2020 at 3:15 AM, lightbug said:

" I do a raycast from the player to the center of each gravity body collider in the scene, check for which raycasthit point is the nearest, then use the normal to determine the gravity direction each timestep. Hope that gives you an idea! "

¡Algo similar se me paso por la cabeza hacer! Pero lo descarte por el siguiente motivo...

Esta "gravedad amoldable"  la aplicare a una escena similar a estaSin título.pngDe

De modo, que al haber solo un objeto que que ejerza gravedad no lo vi necesario.... No se si me estoy explicando muy bien :35_thinking:

 

 

Share this post


Link to post
Share on other sites
17 hours ago, Igor said:

no se si me he explicado bien.

Creo que si que te entiendo, como el jugador esta justo en el centro de la "montañita", al rotar en su dirección, como el rayo es recto dará con el otro lado... y así constantemente, pero eso solo ocurriria si esta en el centro de la montaña, lo e entendido bien?

Share this post


Link to post
Share on other sites

hola de nuevo.

On 1/25/2020 at 10:02 PM, nomoregames said:

por que haces que la posición de cámara sea igual a oldPos (que bale cero) + transform.up * 0.6?

"oldPos" no vale cero porque coje el valor de la posicion del player (pero justo antes de moverlo en cada frame) ...lo que hago es colocar la camara en la posicion vieja del player para que la camara valla siempre como un paso por detras del player, si e player se mueve hacia la derecha la camara se quedara un poco atras a la izquierda del player ...super poco, pero suelo hacerlo siempre asi, da menos sensacion de "rigided" a la camara...  y lo de sumarle (transform.up*0.8f) es para que este un poco por encima del player, para que el player no quede en el centro de la pantall sino un poco mas abajo del centro....   despues de eso la roto en la direccion correcta y luego la muevo hacia atras para que quede detras de player, (y un poco por encima, porque la haviamos movido antes hacia arriba, ...que no hace falta que sea antes... pero lo suelo hacer asi)

he retocao un palin el script, añadiendo comentarios por todos los lados a ver si asi queda mas entendible... y le he añadido una parte en la que "lanzo" seis (6) sphereCast para buscar el suelo cuando estas en el aire... para que el player sepa hacia donde tiene que caer... con eso arreglo el mayor problema que habia de caer hacia el infinito

he cambiado tambien para que rote cuando choca "frontalmente" contra una "pared"... ahora se sube por la pared directamente (adaptandose, claro)

ahi va el codigo:

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

public class playerGalaxy : MonoBehaviour {

    private float camAngleX = 0;
    private float camAngleY = 0;
    private float sensitivity = 10f;//sensivilidad del raton para mirar

    private float speed = 10;//velocidad de mobvimiento
    private float gravy = 0;
    private float gravyPower = 8.8f;//fuerza de gravedad
    private Vector3 newUp = Vector3.up;//vector que marcara el "hacia arriba" del player

    public Transform cam; //camera
    private Vector3 oldPos = Vector3.zero;
    private Vector3 move = Vector3.zero;
    
    private float inAirSphereSize = 0;//para comprobar gravedad cuando estas en el aire (no estas en el suelo)

    private Rigidbody rb;
    

    // Use this for initialization
    void Start () {
        rb = GetComponent<Rigidbody>();
	}
	
	// Update is called once per frame
	void Update () {

        float dTime = Time.deltaTime;
        
        //look
        camAngleY *= 0.8f;
        camAngleY += (Input.GetAxis("Mouse X")) * sensitivity * dTime;
        camAngleX -= (Input.GetAxis("Mouse Y")) * sensitivity * dTime;
        camAngleX = Mathf.Clamp(camAngleX, -80f, 80f);
        cam.position = oldPos;//pongo la camara en la poscion vieja 
                              //para que valla como siguiendo al personaje en vez de esta justo en su posicion 

        cam.position += (transform.up * 0.6f);//pongo la camara un poco mas arriba que la posicion del personaje
        transform.Rotate(0, camAngleY, 0);
        cam.rotation = transform.rotation;
        cam.Rotate(camAngleX, 0, 0);
        cam.position -= cam.forward * 5f;//muevo la camara hacia atras despues de rotarla, para que este detras del personaje


        //move
        oldPos = transform.position;//cojo la posicion anted de moverlo
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        Vector3 mov = new Vector3(h, 0, v);
        if (mov.magnitude > 1) {
            mov.Normalize();
        }
        move = Vector3.Lerp(move, mov, 0.1f);//para suavizar la aceleracion y deceleracion
        Vector3 moveDir = ((move.x * transform.right) + (move.z * transform.forward)) * speed * dTime;//vector final de movimiento
        //transform.position += moveDir;


        //physics
        RaycastHit hit;
        float radius = 0.4f;//radio del la sphereCast
        bool inGround = false;//para luego saber si esta en el suelo

        //forward (move dir) (en direccion del movimiento)
        if (Physics.SphereCast(transform.position, radius, moveDir.normalized, out hit, moveDir.magnitude+0.01f)) {
            transform.position += moveDir.normalized * hit.distance;//parar al chocar
            
            newUp += hit.normal;
            //esto es para rotar tambien si choca hacia donde se mueve (adelante) (se puede quitar)
            
        } else {
            //si NO
            transform.position += moveDir;//mover normal
        }

        //down (en direccion de la gravedad) (hacia abajo del player)
        if (gravy > 0) {//falling
            if (Physics.SphereCast(transform.position, radius, -transform.up, out hit, (gravy * dTime) + 0.3f)) {
                transform.position -= transform.up * (hit.distance-0.1f);//poner al player en el suelo

                newUp += hit.normal;//esto es para rotar (collision con el suelo, el principal)

                gravy = 0;//parar gravedad (dejar de caer)
                inGround = true;//esta en el suelo
            } else {
                //si NO
                transform.position -= transform.up * gravy * dTime;//caer normal
            }
        } else {//jumping (hacia arriba del player)
            if (Physics.SphereCast(transform.position, radius, transform.up, out hit, -((gravy * dTime) + 0.2f))) {
                transform.position += transform.up * hit.distance;//chocar con "techo"                
                
                newUp += hit.normal;
                //esto es para rotar tambien si choca hacia donde se salta (hacia arriba, con el techo) 
                
                gravy = 0;//parar de subir(parar el salto)
            } else {
                transform.position -= transform.up * gravy * dTime;//subir normal(salto) 
            }
        }

        if (inGround) {//esta en el suelo
            inAirSphereSize = radius*0.5f;
            if (Input.GetButtonDown("Fire1") || Input.GetButtonDown("Jump")) {
                gravy = -7f;//jump saltar
            }
        } else {//NO esta en el suelo
            bool groundFound = false;
            //seis (6) sphereCast una en cada direccion (adelante, atras, izquierda, derecha, arriba, abajo)
            //los sphereCast van creciendo (en radio y distancia) hasta que encuentren un "suelo"
            //para intentar caer en la direccion del suelo mas cercano
            if (Physics.SphereCast(transform.position, inAirSphereSize, transform.forward, out hit, inAirSphereSize)) {
                newUp += hit.normal;
                groundFound = true;
            }
            if (Physics.SphereCast(transform.position, inAirSphereSize, -transform.forward, out hit, inAirSphereSize)) {
                newUp += hit.normal;
                groundFound = true;
            }
            if (Physics.SphereCast(transform.position, inAirSphereSize, -transform.right, out hit, inAirSphereSize)) {
                newUp += hit.normal;
                groundFound = true;
            }
            if (Physics.SphereCast(transform.position, inAirSphereSize, transform.forward, out hit, inAirSphereSize)) {
                newUp += hit.normal;
                groundFound = true;
            }
            if (Physics.SphereCast(transform.position, inAirSphereSize, transform.up, out hit, inAirSphereSize)) {
                newUp += hit.normal;
                groundFound = true;
            }
            if (Physics.SphereCast(transform.position, inAirSphereSize, -transform.up, out hit, inAirSphereSize)) {
                newUp += hit.normal;
                groundFound = true;
            }

            if (!groundFound) {
                //si NO ha encontrado suelo sigue creciendo la esfereCast
                inAirSphereSize += 0.1f;
            }

        }


        newUp.Normalize();//como he ido sumando los valores a "newUp" ahora toca normalizarlo (hacer que la longitud del vector sea 1)

        gravy += gravyPower * dTime;//aumentar greavedad (caer)
        
        //rotation
        if (newUp.magnitude > 0.1f) {
            //esto es para finalmente rotar el player para hacer que su "up" coincida con el "newUp"
            Vector3 left = Vector3.Cross(transform.forward, newUp);
            Vector3 newForward = Vector3.Cross(newUp, left);
            Quaternion oldRot = transform.rotation;
            Quaternion newRot = Quaternion.LookRotation(newForward, newUp);

            if (inGround) {
                //si esta en el suelo
                transform.rotation = Quaternion.Lerp(oldRot, newRot, 0.18f);//rotarlo lentamente
            } else {
                //si No esta en el suelo (esta en el aire)
                transform.rotation = Quaternion.Lerp(oldRot, newRot, 0.08f);//rotarlo MUY MUY lentamente
            }
        }


        rb.velocity *= 0.95f;//ir frenando el rigid body
        //porque a veces se queda con alguna fuerza moviendolo todo el rato
        

    }
}

 

Edited by Igor

Share this post


Link to post
Share on other sites

Hola

!Perfecto¡ funciona muy bien, pero...

para acabar de rizar el rizo, me ayudaría mucho saber, entonces, cuales fueron los errores que cometí en el script original

    Rigidbody RB;
    private void Start()
    {
        RB = GetComponent<Rigidbody>();
    }
    void Update()
    {
       
        RaycastHit hit;
        if (Physics.Raycast(transform.position,transform.forward, out hit, 10000)){
            transform.rotation = Quaternion.LookRotation(-hit.normal);
            RB.AddForce(-hit.normal * 100);
           RB.velocity = ( transform.up * Input.GetAxis("Vertical") * 10);
    }
}

gracias por la ayuda... sois la os*ia :1_grinning::91_thumbsup:

Share this post


Link to post
Share on other sites
Sign in to follow this  

×
×
  • Create New...