Jump to content
Biarox1166

Ayuda con la script y el character controller

Recommended Posts

Hola a todos! La duda que traigo el día de hoy XD es una basada en la script que utilizo para mover a mi enemigo, lo hago mediante el uso del character controller. La cosa es que (fuera del tema) no se si es debido a la animación que interfiere con el character o con el capsule collider que le puse a mi enemigo que cuando le doy a Play, este reproduce todo perfecto pero se "teletransporta" un par de centímetros(o la unidad con que se maneje Unity : p) hacia arriba en el eje Y. Por lo que para solucionarlo, le puse un rigidbody(va ya lo tenía) y le "congelé" el movimiento en el eje Y con Contrains, y me funcionó bien el tema es que mi mapa tiene caminos con subidas y bajadas y al subir una colina o algo alto el enemigo lo hace bien pero al tener que "bajar" este se sigue moviendo por la altura que alcanzó en la colina(debido a lo del Contrains). Mi duda es como evitar el problema de la "teletransportación" para no tener que usar el freeze position en el eje Y, y así poder evitar este problema de las alturas. Acá les dejo la script que estoy haciendo:

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]
[RequireComponent(typeof(CharacterController))]

public class ControladorEnemigo : MonoBehaviour {

	public Animator ani;
	public CharacterController Char;
	public Rigidbody RB;
	public float gravity;

	public Transform Jugador;
	public float MinCercania, MaxCercania, velocidad, Dano;
	public bool Perseguir = false;

	// Use this for initialization
	void Start () {
	
		ani = GetComponent<Animator>();
		Char = GetComponent<CharacterController>();
		RB  = GetComponent<Rigidbody>();

	}
	
	// Update is called once per frame
	void Update () {

		float Distancia = Vector3.Distance(transform.position, Jack.position);

		if(Distancia < MaxCercania && Distancia > MinCercania){
			Perseguir = true;
		}else{
			if(Distancia <= MinCercania){
				Perseguir = false;
				ani.SetBool("Detectado", false);
				ani.Play("AtaqueBase");
			}
		}

		Vector3 rotacion = new Vector3(Jugador.position.x, transform.position.y, Jugador.position.z);

		if(Perseguir){
			transform.LookAt(rotacion);
			ani.SetBool("Detectado", true);
			Char.Move(transform.forward * velocidad * Time.deltaTime);
		}

		if(Distancia <= MinCercania){
			Perseguir = false;
			ani.SetBool("Detectado", false);
			ani.Play("AtaqueBase");
		}
	}
}

Entre otras cosas también leí en la scripting reference de Unity, que el uso del character controller "anula" las físicas del gameobject que lo lleve por lo que también me surgieron dudas para usarlo como forma de movimiento(si alguien me puede explicar eso o darme otro método lo agradecería muchísimo).

Y la última cosa con la que estoy luchando, es con un error que me sucede cuando mi enemigo persigue a mi Player(esto lo hace bien), el problema es que cuando está cerca(donde se reproduce la animación de ataque) este como que no se detiene y atraviesa al jugador y no se le despega de encima, pero en la script aclaré que cuando estuviera lo suficientemente cerca, reproduciera la animación de ataque,lo cual sí hace, y que se detenga(volviendo la variable "Perseguir" igual a false) pero no lo hace. Si me ayudan con estas dudas se los agradecería muchooo jajaja Gracias de antemano.

Share this post


Link to post
Share on other sites

Crea una variable para saber si esta atacando

void Update () {

		float Distancia = Vector3.Distance(transform.position, Jack.position);
		if(!isAttack){
			if(Distancia < MaxCercania && Distancia > MinCercania){
				Perseguir = true;
			}else if(Distancia <= MinCercania ){
				Perseguir = false;
				ani.SetBool("Detectado", false);
                isAttack = true                           
				ani.Play("AtaqueBase");
              
			}
		}
		Vector3 rotacion = new Vector3(Jugador.position.x, transform.position.y, Jugador.position.z);

		if(Perseguir){
			transform.LookAt(rotacion);
			ani.SetBool("Detectado", true);
			Char.Move(transform.forward * velocidad * Time.deltaTime);
		}
 
	}
}

Luego para saber si ya ha dejado de atacar porque esta acabando la animacion tienes 3 opciones

-Crear un evento en el clip de animacion que llame una funcion que ponga el isAttack en false, esta en Animation Import Settings

-En el clip de animacion crear una propety que modifique el isAttack del script

-O por script utilizar el AniamtorStateInfo y el AnimatorClipInfo para saber el estado de la animacion

 

Lo de que se levante unos centimetros cuando le das al play puede ser muchas cosas, podrias mirar en Animation Import Settings, tiene unas opciones para Root Position (Y),eso si es por la animacion,puedes mirar tambien de rehacer el avatar si es por el rigging,tambien revisaria las posiciones de todo,malla,esqueleto,collider.....

Por ultimo, no es que el character controller anule las fisicas, tampoco se muy bien que hace para poder explicarlo mejor xD xD, pero es mas una ayuda al Rigidbody para las fisicas, teniendo en cuenta que la posicion del gameobject se va a actualizar por script y no utilizando Physics , si lo mueves con AddForce  y tal no hace falta.No me hagas mucho caso en esto tampoco que no te lo puedo decir seguro XD, pero por ahi van los tiros

Share this post


Link to post
Share on other sites

Si te es posible acompaña de alguna imagen o incluso clip que muestre el error, porque a priori es precipitado decir que el error está en el código.

Como bien dice hammer 

hace 2 horas, hammer said:

Lo de que se levante unos centimetros cuando le das al play puede ser muchas cosas, podrias mirar en Animation Import Settings, tiene unas opciones para Root Position (Y),eso si es por la animacion,puedes mirar tambien de rehacer el avatar si es por el rigging,tambien revisaria las posiciones de todo,malla,esqueleto,collider.....

Puede ser muchas cosas, pero por como has descrito el problema yo tiraría por mirar si la animación esta bien pivotada y las opciones de root en el impor settings.

De todas formas si no consigues dar con el problema con lo que ha dicho, manda imágenes del antes del Play y el después junto con el inspector, seguro que de ahí sale alguna pista más.

Share this post


Link to post
Share on other sites

Gracias!!! @hammer Aunque un par de cosas ya las pude solucionar(lo de la "telestransportación" surgía por el character controller se ve que interfería en eso ya que lo desactivé y ya no me pasa). Solo dos preguntas más XD :

1)Estoy utilizando el RigidBody para mover al enemigo y estoy utilizando la variable "velocity" del componente, te quería preguntar si está bien o debería usar el AddForce u otro método que me recomiendes.

2)No se si sabes de animaciones pero te lo pregunto igual o para cualquiera que me pueda ayudar, tengo una animación descargada de mixamo(para el enemigo) de ataque. El problema es que la animación no es "in-place" o en el lugar, estática o como se le diga, y cuando la reproduzco queda muy bien ya que pareciera que el movimiento que realiza al atacar generara el movimiento de su posición(ACÁ ESTÁ LA ANIMACIÓN PARA QUE LA VEAN) pero el problema es que cuando la reproduce y finaliza, vuelve a reproducirse desde su primera posición, lo que logré es que no sea tan de la nada y que pareciera como que se va para atras pero sigue quedando raro(video) si alguien sabe como modificar esto porfa que me lo diga : p les dejo también ,bien una foto del clip de la animación

Desktop 12-08-2016 11-41-56-175.png

Edited by Biarox1166

Share this post


Link to post
Share on other sites

El componente CharacterController es un 2x1, es decir, Collider+Rigidbody, no necesitas un Collider complementario.

En cambio, el componente Rigidbody necesita un Collider(tipo capsula, esfera, malla, ...).

PRECAUCION: Elige un sistema para todo el proyecto y actores, o CharacterController, o Rigidbody+Collider. No es aconsejable combinar los dos sistemas porque lo eventos de físicas(enter, exit, stay, ... ) no funcionan correctamente. Ten en cuenta, que mucho eventos de físicas funcionan solo con Rigidbody+Collider. Se suele utilizar el CharacterController en cosas simples que no necesitan eventos de físicas.

 

 

En las animaciones pueden incorporar claves de posición, rotación y escala.

Si las animaciones incorporan claves de posición(movimiento), no es necesario mover con físicas, aunque sigue necesitando los componentes Collider y Rigidbody. También funciona con el componente CharacterController.

Puedes anular(que no las aplique) las claves de posición desde la pestaña Animations que muestra la imagen anterior.

Aunque lleve claves de movimiento o posición, si no esta bien configurada la animación: bien asignado el hueso principal(root) que aparece en la pestaña Rig, y bien configurada la pestaña Animations, no funcionará. Fíjate en el proyecto esencial de Unity como están configuradas la animaciones de salto, caminar, etc... https://www.assetstore.unity3d.com/en/#!/content/7677

 

Edited by kaito

Share this post


Link to post
Share on other sites

1)No hay un metodo mejor que otro,para algunas cosas te ira mejor uno y para otras el otro, juegos de coches o naves y tal esta claro que que por fisicas mejor, si es un plataformas 2d por fisicas tambien,si es un fps quiza por character controller sea mejor..

2) Como dice Kaito mirate como esta montado el proyecto del link que te ha puesto y asi ves como lo hacen,tienes que configurar el avatar en la pestaña rig correctamente para que te de las opciones de Root position en la pestaña de Animations,luego por script si hace falta tienes Animator.ApplyRootMotion y el Animator.OnAnimatorMove miratelo a ver si te sirve

 

Share this post


Link to post
Share on other sites

Hola de nuevo, ya pude solucionar todo pero nuevamente me surgió un problema XD. A la script que puse anteriormente en este post le modifiqué un par de cosas, y lo que le agregué es simplemente una nueva void(llamada "Ataque") a la cual mediante un evento en la animación de un enemigo, llamo y hago que le reste vida al jugador(siempre y cuando esté "dentro del rango"), los problemas que me surgieron fueron 2: 

1) El enemigo(al cual muevo con la variable "SimpleMove" del character controller) no se mueve, y antes de crear la void si lo hacía, bah mejor dicho se "mueve" o lo intenta pero hay como una fuerza que lo tira para atras.

2)La vida de mi jugador no se reduce(por más que esté dentro del rango especificado).

Lo que más me sorprende es que lo había hecho antes en una script la cual sin querer borré y me andaba todo perfecto(por lo que temo que esté haciendo algo mal o salteandome un paso). Les dejo la script:

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(CharacterController))]

public class ControladorEnemigo : MonoBehaviour {

	public Animator ani;
	public CharacterController Char;

	public Transform Player;
	public float MinCercania, MaxCercania, velocidad, dano;
	public bool Perseguir;

	// Use this for initialization
	void Start () {
	
		Char = GetComponent<CharacterController>();
		ani = GetComponent<Animator>();
		Perseguir = false;
		Invoke("TiempoAttack", );
	}
	
	// Update is called once per frame
	void Update () {		

		float Distancia = Vector3.Distance(transform.position, Player.position);

		if(Distancia < MaxCercania){
			Perseguir = true;
		}else{
			Perseguir = false;
		}

		Vector3 rot = new Vector3(Player.position.x, transform.position.y, Player.position.z);
		Vector3 forward = transform.TransformDirection(Vector3.forward);
		if(Perseguir){
			transform.LookAt(rot);
			ani.SetBool("Detectado", true);
			ani.SetBool("AtaqueBase", false);
			Char.SimpleMove(forward * velocidad * Time.deltaTime);
			}else{
			ani.SetBool("Detectado", false);

		}

		if(Distancia <= MinCercania){
			Perseguir = false;
			ani.SetBool("AtaqueBase", true);
		}

	}
	void TiempoAttack () {
		SaludyMana.Salud -= dano; 
		RaycastHit hit;
		if(Physics.Raycast(transform.position, transform.forward, out hit, 2)){
			if(ani.GetCurrentAnimatorStateInfo().IsName("AtaqueBase")){				//Si se estareproduciendo la animacion "AtaqueBase"
				if (hit.collider.tag == "Player"){										//y el ray colisiona con el Player
					SaludyMana.Salud -= dano;                                         //se le quite X cantidad de vida	
				}
			}
		}
	}

Porfa @hammer dime como poder solucionarlo jajaja

Share this post


Link to post
Share on other sites

xD, pues no se si podre ayudarte, yo soy un picacodigo no un programador XD xD,pero lo intentare

Es dificil saber ya que se podria dar el caso que el error estuviera en el animator y las transiciones a traves de los parametros, pero veo algunas cosas mal.

Nunca he utilizaod el invoke pero diria que solo estas llamando una vez a la funcion TiempoAttack,justo en el primer frame luego no la vuelves a llamar,invoke no creo que sea una courutine,puedes poner un Debug.Log("hola hola"); en el principio del TiempoAttack() para que veas que solo se produce una vez y no a cada frame,Tendrias que poner en el start(), StartCourutine(TiempoAttack());para que funcione una courutine TiempoAttack no puede ser del tipo void, tiene que ser del tipo IEnumerator y al final del TiempoAttack() tienes que hacerle un yield return null dentro de algun bucle

he hecho una prueba rapida con dos capsulas y asi funciona, fijate que he puesto en // todas las lineas de animator y he quitado el SaludyMana para poder probarlo rapidamente.

public class ControladorEnemigo : MonoBehaviour {

	public Animator ani;
	public CharacterController Char;

	public Transform Player;
	public float MinCercania, MaxCercania, velocidad, dano;
	public bool Perseguir;

	// Use this for initialization
	void Start () {

		Char = GetComponent<CharacterController>();
		//ani = GetComponent<Animator>();
		Perseguir = false;
		StartCoroutine (TiempoAttack ());
	}

	// Update is called once per frame
	void Update () {		

		float Distancia = Vector3.Distance(transform.position, Player.position);
		Debug.Log (Distancia);
		if(Distancia < MaxCercania){
			Perseguir = true;
		}else{
			Perseguir = false;
		}

		Vector3 rot = new Vector3(Player.position.x, transform.position.y, Player.position.z);
		Vector3 forward = transform.TransformDirection(Vector3.forward);
		if(Perseguir){
			transform.LookAt(rot);
			//ani.SetBool("Detectado", true);
			//ani.SetBool("AtaqueBase", false);
			Char.SimpleMove(forward * velocidad * Time.deltaTime);
		}else{
			//ani.SetBool("Detectado", false);

		}

		if(Distancia <= MinCercania){
			Perseguir = false;
			//ani.SetBool("AtaqueBase", true);
		}

	}
	IEnumerator TiempoAttack () {
		while (true) {
			Debug.Log ("LERELE");
			RaycastHit hit;
			if (Physics.Raycast (transform.position, transform.forward, out hit, MinCercania)) {
				//if(ani.GetCurrentAnimatorStateInfo().IsName("AtaqueBase")){				//Si se estareproduciendo la animacion "AtaqueBase"
				if (hit.collider.tag == "Player") {										//y el ray colisiona con el Player
					Debug.Log ("DAño");                                       //se le quite X cantidad de vida	
				}
				//}
			}
			yield return null;
		}
	}

}

Te aconsejo que`reahagas el codigo un poquillo ,no tiene sentido estar cada frame llamando a TiempoAttack  cuando  sabes la distancia a la que se encuentra, tendrias que llamar a TiempoAttack cuando este a la distancia que quieras dentro del update,dentro del if(distancia <= minCercania) por ejemplo,no es que te vaya a afectar tampoco mucho al rendimiento a maneos que tengas 300 enemigos funcionando pero es mas correcto.

Luego intentaria limitar mas los if, fijate que es posible que se cumplan los dos if el de maxCercania y el de minCercania en el mismo frame, diria que en ese caso puedes tener al enemigo moviendose y pegandote al mismo tiempo, que se yo por ejemplo

Cita

 

If(Distance < = MaxCercania && Distance >= MinCercania)

else if(Distance <= MinCercania)

else //este seria el de no perseguir porque esta muy lejos.

 

 

 

Share this post


Link to post
Share on other sites

UnitySpain © Todos los derechos reservados 2020
×
×
  • Create New...