PFM: Apuntando con el personaje

Si nuestro personaje va a utilizar armas para acabar con sus enemigos, es de suponer que el jugador deberá apuntar hacia ellos para tratar de acertar el tiro. Sin embargo, tal cual está ahora, nuestro personaje no se inmuta cuando el jugador mueve la cámara hacia arriba o hacia abajo. Lo deseable es que el personaje apunte con su arma en la misma dirección que la cámara. En este tutorial verémos cómo conseguirlo.

La solución que proporciona el motor Unreal consiste en interpolar entre diferentes posiciones extremas que especificaremos para obtener la posición de apuntado correcta. Por ejemplo, nosotros especificaremos una posición del personaje apuntando hacia delante y otra apuntando hacia arriba, siendo el motor el encargado de obtener las posiciones intermedias cuando sean necesarias. Para cubrir todos los ángulos posibles de un personaje podríamos especificar hasta 9 posiciones diferentes (CentroCentro, CentroArriba, CentroAbajo, IzquierdaArriba, IzquiedaCentro…). Para este ejemplo nuestro personaje podrá apuntar hacia arriba y hacia abajo, por lo que necesitaremos 3 posiciones: CentroCentro, CentroArriba y CentroAbajo.

Estas posiciones se importan en UDK como animaciones del personaje. Por tanto gereraremos 3 ficheros FBX diferentes con las distintas posturas que podrá tener el personaje al apuntar. La duración de la animación es indiferente, pudiendo ser incluso de 1 frame, pues lo que nos interesa es la posición del esqueleto. Estas son las posiciones que utilizaré:

pfm06_01

De momento el personaje no tiene arma, así que apunta un poco a lo Buzz Lightyear.

Cuando tengamos las 3 posiciones en sus respectivos ficheros FBX las importamos al AnimSet del personaje como cuando importamos las animaciones de correr y saltar.

pfm06_02

AnimTree

Una vez tenemos las animaciones relacionadas con el personaje, hay que decirle al motor cómo utilizarlas, y eso se hace en el AnimTree. Para esto de apuntar hay un nodo/cajita que se encargará de interpolar entre las diferentes posturas, AnimNodeAimOffset (Podemos encontrarlo en New Animation Node > AnimNodeAimOffset). Al crearlo vemos que tiene en su interior un control en forma de cuadrado negro. Este nos servirá para previsualizar las posturas que adquiera el personaje. A partir del AnimTree que creamos cuando dimos vida a nuestro personaje, expandámoslo para obtener el siguiente:

pfm06_04

Mi nodo AnimNodeAimOffset se llama PFMAimOffset porque se lo he especificado así en las propiedades. Esto es importante porque necesitaremos conocer el nombre del nodo para acceder a él posteriormente desde UnrealScript.

pfm06_03

Ahora hay que especificar al AnimNodeAimOffset las posiciones que usaremos para el apuntado. Para ello lo primero es crear un perfil en el nodo. Esto de los perfiles es útil para casos en los que tengamos diferentes posiciones según el arma que porte el personaje en cada caso. Para crear el perfil hacemos doble clic en el nodo, lo que abrirá el editor de AimOffset. Simplemente pincharemos el botón New y crearemos un nuevo perfil con el nombre que queramos.

pfm06_05

Hay una manera alternativa de especificar las posiciones que es hacerlo manualmente en este editor, donde indicaríamos a qué huesos del esqueleto afectan las distintas posturas y la rotación que cada uno tendrían en cada una de ellas. Como hemos dicho nosotros especificaremos las posturas mediante las animaciones que ya tenemos preparadas. Una vez creado el perfil cerramos el editor de AimOffset y vamos a las propiedades del nodo (simplemente seleccionándolo), donde le diremos cuáles son las animaciones que contienen las posturas del personaje.

pfm06_06

En las propiedades veremos que hay un perfil creado con el nombre que le hemos dado antes. Sólo tendremos que desplegar el perfil y escribir el nombre de las diferentes animaciones que conforman nuestro conjunto de posturas. En nuestro caso rellenamos los campos Anim Name CU (CenterUp), Anim Name CC (CenterCenter) y Anim Name CD (CenterDown), con los respectivos nombres de las animaciones. Una vez hecho hacemos clic en la propiedad Bake From Animations para indicarle que obtenga las posturas de las animaciones. Hecho esto podremos comprobar qué tal apunta el personaje moviendo la cajita negra del nodo AimOffset y…

pfm06_07

funciona… a medias. El personaje mira para arriba pero no pone los brazos en la posición que queríamos. ¿Por qué ocurre esto? El motivo es que como hemos comentado, el nodo AimOffset hace una interpolación entre las posiciones que le indicamos, pero esta interpolación se realiza entre las diferencias entre las rotaciones de cada hueso implicado en la postura. Es decir, el nodo toma la posición central dada como la base y calcula la rotación de los huesos necesaria para llegar a las otras posiciones. Por ejemplo, en el caso de apuntar hacia arriba, esa rotación se produce en los hombros, la cabeza y el tronco, pero realmente entre la posición de apuntar hacia delante y la posición de apuntar hacia arriba los codos no varían su rotación, por lo que el nodo no efectúa ninguna operación en ellos y, por tanto, vemos los brazos estirados, con la orientación que determina la animación Idle. Resumiendo, el nodo AimOffset aplica a la animación entrante las diferencias de rotaciones en los huesos de las posiciones que se le especifican, no sustituye la postura de la animación.

Para solucionarlo en este caso usaremos el nodo AnimNodeBlendPerNode, que permite eliminar información de animación en ciertos huesos y sustituirla por otra animación. De este modo, eliminaremos la información de animación de los brazos que hace que estos se mantengan estirados, y la sustituiremos por la información contenida en una de las posturas para que el personaje cruce un brazo sobre el otro, adoptando así la postura deseada. Creando el nuevo nodo desde New Animation Node > Filter > AnimNodeBlendPerBone y añadiendo una secuencia de animación con la postura de apuntado hacia adelante, el nuevo AnimTree quedará así.

pfm06_08

Para que el nuevo nodo haga su función sólo falta decirle qué huesos sobreescribirá el nodo de animación recién creado. Vamos a sus propiedades y vemos que podemos especificar las ramas de huesos que deberán sobreescribirse. En este caso serán todos los huesos que cuelguen de cada uno de los brazos:

pfm06_09

Probamos ahora a mover la cajita negra del AimOffset y….

pfm06_10

Ahora sí! El personaje apunta con sus brazos correctamente. Si probamos ahora el juego sin embargo, veremos que no funciona como queríamos, y es que falta el último paso, relacionar ese AimOffset con el movimiento de la cámara y hacer que actúe desde UnrealScript.

UnrealScript

Desde el código deberemos acceder al nodo AimOffset y decirle cuando estaremos apuntando y en qué dirección para que haga que nuestro personaje reaccione como esperamos. Todo el código irá en nuestra clase PFMPawn, encargada como sabemos de representar el avatar del jugador en el mundo del juego.

En primer lugar declaramos estas variables en la parte superior de la clase (antes de cualquier función).


var AnimNodeAimOffset PFMAimNode;   // Referencia al nodo de animación de AimOffset
var Rotator DesiredAim;             // Aim deseado
var Rotator CurrentAim;             // Aim actual
var float AimSpeed;                 // Velocidad de apuntado

DesiredAim y CurrentAim servirán para interpolar el movimiento del personaje y que tenga un cierto “retraso” determinado por AimSpeed, que habrá que inicializar en la zona de defaultproperties de la clase


AimSpeed=8

A continuación escribiremos las siguientes 3 funciones:


simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp)
{
    // Se busca la referencia del nodo de Aiming
    PFMAimNode = AnimNodeAimOffset(SkelComp.FindAnimNode('PFMAimNode'));
}

simulated event Destroyed()
{
    Super.Destroyed();
    PFMAimNode = None;  //Es necesario desreferenciar el nodo para que el Pawn se destruya correctamente
}

simulated function Tick(float DeltaTime)
{
    Super.Tick(DeltaTime);

    // Se obtiene la rotación actual del controller y se clampea con el max y min
    DesiredAim = GetBaseAimRotation();
    DesiredAim.Pitch = Clamp(DesiredAim.Pitch, ViewPitchMin, ViewPitchMax);

    // Interpolación de la rotación
    if (DesiredAim != CurrentAim)
    {
        CurrentAim = RLerp(CurrentAim, DesiredAim, AimSpeed * DeltaTime, false);
    }

    // Se cambia el nodo de animación
    if (CurrentAim.Pitch < 0)
    {
        PFMAimNode.Aim.Y = -float(CurrentAim.Pitch) / ViewPitchMin;
    }
    else if (CurrentAim.Pitch > 0)
    {
        PFMAimNode.Aim.Y = float(CurrentAim.Pitch) / ViewPitchMax;
    }
    else
    {
        PFMAimNode.Aim.Y = 0.f;
    }
}

El evento PostInitAnimTree se llama poco después de crear el Pawn y es aquí donde accederemos al AnimTree del personaje y conseguiremos una referencia al nodo de AimOffset para poder modificarlo, mediante el nombre que le dimos al nodo en el editor.

En el evento Destroyed, llamado cuando se destruye el Pawn, será necesario desreferenciar el nodo para que el Pawn se destruya correctamente, de lo contrario quedaría en memoria.

En la función Tick (ejecutada cada frame del juego), es donde realmente modificaremos el valor del nodo AimOffset. Lo que haremos será obtener la orientación de la cámara y traducirla a una orientación para el AimOffset (que como hemos visto al mover la cajita negra, se mueve entre -1 y 1). Con la función GetBaseAimRotation se consigue la rotación de la cámara, que se guarda en DesiredAim. Interpolándola con el AimSpeed, guardamos la rotación que mostraremos en este frame en CurrentAim. Finalmente, dado que la rotación de la cámara no se corresponde con el rango -1 y 1 del AimOffset, habrá que hacer un ajuste para traducirla a valores que el nodo entienda. Como vemos sólo se modifica el valor de la coordenada Y del nodo, pues el personaje sólo modificará su postura al apuntar hacia arriba o hacia abajo.

Este es todo el código que necesitaremos en esta ocasión. Relanzamos el editor para que se compile y si no hay ningún error podemos probarlo. Al apuntar hacia arriba o abajo deberíamos ver que el personaje dobla la cabeza y el tronco y mantiene los brazos en la posición que queremos. Como viéndolo desde la espalda quizá cueste apreciar el movimiento, podemos modificar el offset de la cámara en la sección de defaultproperties de PFMPawn para moverla un poco hacia la derecha y poder ver mejor el lateral del personaje.


CamOffset=(X=4.0,Y=60.0,Z=-13.0)

Y este es el resultado!

Pronto podremos disparar a esos cansinos PNJ que no dejan de perseguirnos…
Banner Blog

Anuncios

37 pensamientos en “PFM: Apuntando con el personaje

  1. Pingback: PFM: Meeting Mr. Pepe Dummy | El Blog de Marcos

  2. Pingback: PFM: Combate cuerpo a cuerpo en UDK (parte 1) | El Blog de Marcos

  3. Tengo una duda sobre el sistema. En el nodo AIM se pueden definir perfiles, supuestamente para diferentes posturas según el arma.

    Pero en el siguiente nodo, en el blend per bone, aparece una aninación definida, que tendría que variar según el perfil o lo que contenga weapontype con un anim node blend by property.

    Y además en el código que pones arriba, sustituir “PFMAimNode.Aim” por el perfil que esté usando en ese momento.

    ¿Sería la forma correcta de hacerlo?

    He añadido en mis pruebas otra arma (un XM8) y la postura es diferente. Lo he resuelto duplicando el nodo aim y el resto, y usando en el código un findnode cada vez que cambia de arma.
    Funciona, pero me da que la otra manera sería más correcta de hacerlo y quedaría un animtree más limpio.

    • La cosa es que en este tutorial sólo hay un único perfil para apuntar y de hecho aún no había pensado lo de las posturas. El Blend per bone hace como filtro para meterle a piñón la postura de los brazos con arma al muñeco, pero ni siquiera podía no llevar arma.

      El paso siguiente fue ese, tener la posibilidad de no llevar arma, que lo implementé con Pepe Dummy
      En el AnimTree puedes ver que aquí ya utilicé el WeaponType para escoger entre la postura normal o la de apuntado (mezclada con el blend per bone con el resto de animaciones)

      Pero sólo había 2 posturas (sin arma o con arma). En el personaje final ya sí hay 2 perfiles diferentes en el Nodo Aim y cambio entre ellos desde código. Mañana seguramente suba la tercera parte del diseño del personaje y pondré el nuevo AnimTree, que utiliza el WeaponType para saber en qué postura está el personaje, el Nodo Aim con 2 perfiles diferentes (arma izquierda y arma derecha), y varios Blend per bone para mezclar las animaciones con las posturas de las armas.

      Yo cambio de perfil el Aim Node cuando se cambia de arma en el pawn, así:

      function SetWeaponType(PawnWeaponType NewWeaponType)
      {
      WeaponType = NewWeaponType;
      // Se activa el perfil adecuado en PFMAimNode
      if(WeaponType == PWT_AimRight)
      {
      PFMAimNode.SetActiveProfileByIndex(0);
      }
      else if(WeaponType == PWT_AimLeft)
      {
      PFMAimNode.SetActiveProfileByIndex(1);
      }
      }

      Luego ya con el WeaponType en el AnimTree se determinan distintos “caminos” de la animación, habiendo casos en los que el AimNode no se usa (sin arma, arma cuerpo a cuerpo)

  4. ¿Entonces, al usar setactiveprofilebyindex ya no haría falta hacer referencia a “aim” en el tick, en lugar de usar “PFMAimNode.Aim.Y” se usaría “PFMAimNode.Y” ?

    • Si sigue haciendo falta, pues Aim es una variable predefinida dentro del AimNode, que se llame como el perfil de este tutorial es casual.

  5. Llevo toda la tarde atascado en tratar de conseguir que el pawn apunte hacia los lados, pero me lío con los rotators y demás.

    He estado mirando el tutorial de UDN, http://udn.epicgames.com/Three/AnimationNodes.html#AnimNodeAimOffset , pero no he conseguido hacerlo andar.

    Funciona cuando el Pawn tiene rotation.yaw=0 . Cuando mira en sentido opuesto, el aim apunta al contrario. Y en los lados, una mezcla… supongo que necesito colocar rotation del pawn por algún lado, o calcular alguna normal. Pero no encuentro la forma.

    Esto es lo que tengo:

    Super.Tick(DeltaTime);

    //esto lo coloco a mano porque devolvía siempre 0, a diferencia de ViewPitch, que también da valores constantes.
    ViewYawMin=-16384;
    ViewYawMax=16383;

    DesiredAim = GetBaseAimRotation();

    DesiredAim.Pitch = Clamp(DesiredAim.Pitch, ViewPitchMin, ViewPitchMax);
    DesiredAim.Yaw = Clamp(DesiredAim.Yaw, ViewYawMin, ViewYawMax);

    // Interpolación de la rotación
    if (DesiredAim != CurrentAim)
    {
    CurrentAim = RLerp(CurrentAim, DesiredAim, AimSpeed * DeltaTime, false);
    }

    // Se cambia el nodo de animación
    if (CurrentAim.Pitch 0)
    {
    TESTAimNode.Aim.Y = float(CurrentAim.Pitch) / ViewPitchMax;
    }
    else
    {
    TESTAimNode.Aim.Y = 0.f;
    }

    // Adjust the yaw
    if (CurrentAim.Yaw > 0)
    {
    TESTAimNode.Aim.X = Abs(float(CurrentAim.Yaw) / ViewYawMax);
    // TESTAimNode.Aim.X = float(CurrentAim.Yaw) / ViewYawMax;
    }
    else if (CurrentAim.Yaw < 0)
    {
    TESTAimNode.Aim.X = Abs(float(CurrentAim.Yaw) / ViewYawMin) * -1.f;
    // TESTAimNode.Aim.X = -float(CurrentAim.Yaw) / ViewYawMin;
    }
    else
    {
    TESTAimNode.Aim.X = 0.f;
    }

    He probado a mezclar tú código con el de UDN, usando playercontroller en lugar de getbaseaimrotation, pero sin éxito.

    • Pues no te sé decir porque el apuntado lateral no lo he implementado nunca, pero por lo que dices tiene pinta de que haya que meter la rotación del Pawn por algún lado para que se tenga en cuenta al modificar el nodo AimOffset.

      Por lo que veo en el ejemplo de UDN no lo tienen en cuenta, y por eso el Pawn que tienen de prueba y que apunta al jugador todo el rato no gira más que el torso y hasta el límite del AimOffset.. habría que ver si girando el Pawn en el editor también se comporta raro como a ti.

      También es curioso que en el código del UT no encuentro dónde modifican el nodo AimOffset, así que no sé muy bien cómo hacen para que funcione cuando se ve en tercera persona.

  6. Pues sí, he estado sumando y restando Rotation.Yaw a ViewYawMax y ViewYawMin, en todas las combinaciones, y siempre ocurre lo mismo, al girar se invierte.

    Aquí se ve el problema:

    Voy a preguntar en el foro de epic, a ver si allí alguien ha tenido el mismo problema.
    Si lo soluciono te cuento por aquí.

  7. Me voy acercando, hay que implicar a la posición de la cámara también. Usando:

    GetPlayerViewPoint(CameraLocation,CameraRotation);

    Normal(Vector(Rotation)) dot Normal(cameralocation – Location) + 1)

    Obtengo 1, 0, 1 mirando a la izquierda, centro y derecha respectivamente, con el pawn visto desde atrás. Ahora tengo que ver cómo detectar si está mirando a la izquierda o derecha, para multiplicar por -1 si es a la izquierda.
    Y luego ver cómo hago cuando la vista es frontal al pawn.

    He estado probando otro método, restando la rotación de la cámara a la del pawn, pero éste último parece que va acumulando la rotación, no va de 0 a 65535, sino que si das una vuelta más, se va acumulando. Mientras que la de la cámara sí es cíclica.

  8. Hola Marcos, tengo un problema siguiendo este tema.
    Lo hago todo y en el editor de animntree funciona todo bien, pero luego en la partida la animacion de apuntar hacia arriba no se reproduce y la de apuntar hacia abajo lo hace a partir de un punto muy temprano y la reproduce por completo, no lo hace de forma progresiva en todo el recorrido. Video demostrativo:

    Lo unico que no entidendo bien es:
    “Para que el nuevo nodo haga su función sólo falta decirle qué huesos sobreescribirá el nodo de animación recién creado. Vamos a sus propiedades y vemos que podemos especificar las ramas de huesos que deberán sobreescribirse. En este caso serán todos los huesos que cuelguen de cada uno de los brazos:”

    • Haciendo un poco de debug, me he encontrado que el pitch en la horizontal vale 16376 y si se mira al suelo no varia, cuando se mira al cielo empiezan a subir los valores de 0 a 16376, me he quedao con el culo torcido, no se por donde coger este problema…

    • Hola Guille. Pues es muy raro eso.. Si en el editor de AnimTree se comporta correctamente el problema debería estar en el código, pero si usas el mismo que yo no se qué puede estar fallando. Debuguea como has hecho el valor de todas las variables, empezando por DesiredAim, para ver si tienen valores razonables al girar la cámara, a ver si ves al menos qué puede estar funcionando mal.

      Por otro lado, el nodo ANimNodeBlendPerBone lo que hace es filtrar datos de animación a partir de un hueso. Por ejemplo, si tenemos la animación de correr, pero queremos que el personaje vaya con el brazo extendido como apuntando, habrá que filtrar los datos de animación del brazo para que no se mueva siguiendo la animación de correr. Para eso se determina a partir de qué hueso no se van a utilizar los datos de animación, mezclándola entonces con la animación que guarda la postura de apuntar con el brazo.

    • Pues el problema esta en el pitch de la rotacion base y no se como meterlo entre unos valores coherentes, aqui un video con el debug de los valores que toma, perdon por el spotify a toda ostia :p

    • Buenoooo al final lo he conseguido.

      Estaba trasteando cuando me ha puesto los valores max y min del pitch.
      En el foro del beyound encontre un tipo que le pasaba lo mismo y la verdad es que la solucion era facil, pero no se me habia ocurrido, aunque pienso que es una chapuza, el pitch no deberia tomar esos valores tan raros.
      Total que el tio lo que hac es que trima el valor a partir de donde empieza a hacer cosas raras, que por cierto a el le pasa en otro valor.

      simulated function Tick(float DeltaTime)
      {
      Super.Tick(DeltaTime);

      // Se obtiene la rotación actual del controller y se clampea con el max y min
      DesiredAim = GetBaseAimRotation();
      if ( DesiredAim.Pitch < 49160)
      {
      DesiredAim.Pitch = DesiredAim.Pitch;
      } else
      {
      DesiredAim.Pitch = (DesiredAim.Pitch)-65536 ;
      }
      Worldinfo.game.Broadcast(self, DesiredAim.Pitch);
      DesiredAim.Pitch = Clamp(DesiredAim.Pitch, ViewPitchMin, ViewPitchMax);

      // Interpolación de la rotación
      if (DesiredAim != CurrentAim)
      {
      CurrentAim = RLerp(CurrentAim, DesiredAim, AimSpeed * DeltaTime, false);
      }

      Asi es como me ha quedado la primera parte de la funcion tick.
      he de reconocer que he estado haciendo el gambitero un buen rato, por que el tipo usaba un valor de 49000 y con el broadcast que tengo puesto yo iva mirando el valor maximo que tomaba el pitch y me rondaba los 49151, peor luego habia veces que se salia de rango y no iva bien y no sabia por que. Yo vwnga a probar valores aproximados, hasta que me he dado cuenta que los valores clavados te los habia puesto yo en un comentario anterior, y ha sido ponerlo y funcionar al pelo.

      Los valores de pitch que me has sugerido eran muy altos y pasaban la vertical del personaje en su valor maximo, lo que me ha dado la idea de bajarlos un poco, bastante, para que el tiro de camara no haga cosas raras como por ejemplo esa de pasar la vertical del personaje.

      Ahora a por las poses de las armas.

      Muchas gracias por el interes Marcos.

  9. Bueno, finalmente lo encontré, después de mil y un intentos. En el tick del player:

    local PlayerController PlayerController;
    local rotator CameraRotation;
    local vector CameraLocation;
    local vector X,Y,Z,Y1,Y2;

    PlayerController = GetALocalPlayerController();
    Playercontroller.getPlayerViewPoint(CameraLocation,CameraRotation);

    GetAxes(rotation,x,y,z);
    Y1 = Y;
    GetAxes(camerarotation,x,y,z);
    Y2 = Y;
    TESTAimNode.Aim.X = (Y1.x * Y2.Y) – (Y2.x * Y1.Y);

    Con eso se obtiene directamente el valor horizontal para el AIMnode. Lo único que he visto es que no interpola bien entre las animaciones verticales y horizontales, habría que hacer las animaciones para las diagonales también. Un coñazo, vamos. El improbable día que haga algo serio lo haré.

  10. Quería restar las rotaciones de la cámara y el pawn, pero mientras la cámara iba de -32768 a 32767, el del pawn se iba acumulando, cada vuelta que le dabas sumaba 65536 más. Así que recurrí a GetAxes, que te devuelve un valor entre -1 y +1 para pawn y cámara.

    El problema que tenía era el mismo que con el anterior método, al girar el pawn los valores se invertían, el -1 pasaba de izquierda a derecha.

    Me fijé en las otras componentes de Y, y ví que también se invertían, así que las multipliqué cruzadas, y funcionó.

  11. He puesto un vídeo (me estoy aficionando a los vídeos, voy a parecer uno de esos que cuenta todo lo que hace en facebook):

    El efecto queda muy bien, se hace notar más que el apuntado vertical, y te permite correr de lado y disparar al frente, cosa que no podía hacer con el setup de mi cámara.

  12. Sobre destruir las variables de los animnode en destroyed, ¿es todo lo que hay que destruir en un pawn?

    Lo digo porque estoy haciendo pruebas con un mapa grande con niveles. Es un terreno de 2,5 km2, y tengo varios niveles sobre él. En uno de los niveles he puesto en una especie de cuartel a 100 enemigos (para probar).

    Me ocurre lo siguiente, antes de aparecer el nivel, el stat unit me indica en “game” un tiempo de 4 ms. Al aparecer el nivel sube a 15, y entrando en el cuartel y poniéndote delante de los enemigos sube a 30 o más.
    Todo eso es normal.
    Pero al alejarme y descargarse el nivel, el tiempo de “game” aumenta un poco, pasa de 4 ms a 7 ú 8 ms. Cada vez que muestro y oculto el nivel, el tiempo aumenta. Así el juego inicialmente iba a 60 fps y después de un rato acaba yendo a 14…

    Parece como si el recolector de basura se dejase algo cada vez y se fuese acumulando. Tengo tres variables conteniendo animnodes y las tres las pongo en “none” en el evento destroyed. También he probado a no colocarles nada en el inventario. Pero todo sigue igual.

    ¿Hay algo más que haya que incluir en el destroyed?

  13. He estado probando y el problema parece estar en el controller, si no le pongo controller a los bots no hay problema. Luego he probado a quitar los states y quitar el tick. Sigue habiendo un aumento de tiempo en “game”, más pequeño porque hay menos proceso, pero sigue “acumulándose” algo.

    He visto que al descargar el nivel no hay evento destroyed para los pawns. Es posible que se vayan acumulando controllers cada vez que se carga el nivel, pero no tengo ni idea de cómo solucionarlo.

    • Pues no tengo experiencia con los streamed levels así que me temo que mucho no puedo ayudarte. Es extraño que se acumulen controllers si no se llegan a eliminar los pawns. ¿Cada vez que se carga el nivel se vuelven a generar todos los pawns y sus controllers?
      Quizá hay alguna manera de que el pawn sepa cuando se descarga el nivel en que se encuentra con algún evento o algo así que pueda sobreescribirse para forzar el destroy del controller también.
      No tengo mucho tiempo últimamente así que no puedo investigar mucho más el tema 😦 Ponme al día si encuentras la solución.

  14. Tengo un mensaje en el post begin play y otro en el destroy. Cada vez que cargo el nivel se ejecuta el post begin, pero al descargarlo no hay ningún destroy. No sé qué ocurrirá entre descarga y carga, pero sospecho que deja “dormidos” los pawns con sus respectivos controllers.

    He encontrado una forma de minimizar el problema, que ha sido colocar el Tick que tenía en el controller y meterlo dentro de los states. Así tras cargar y descargar varias veces el nivel no hay grandes diferencias, quizás un par de milisegundos más.

    Puede que al descargar el nivel, no destruya los pawns y los coloque en un state especial. El tick, cuando iba fuera de los states, se quedaría ejecutándose siempre. Lo que no entiendo es por qué se iba acumulando el retraso. Es como si fuese añadiendo controllers cada vez que hay un post begin, y cada pawn tuviese funcionando varios controllers a la vez con sus respectivos ticks. No lo sé, la verdad.

    • Pues por lo que dices si parece que sea un problema de que se acumulen procesos que no terminan… esos controllers que se quedan en el limbo en teoría ya no tienen asociado ningún pawn no? puedes probar a ver si se llama a unpossess y en tal caso destruirlos.. no se me ocurre otra cosa xD

  15. He estado mirando los logs y parece que es algo así lo que ocurre:

    – Me acerco al nivel, éste se carga junto a los bots. Todo perfecto.

    – Me alejo del nivel, y éste se descarga.

    – Instantes después salen constantemente estos mensajes:

    [0716.56] ScriptWarning: Accessed None ‘Pawn’ TESTEnemyController SandBox-landscape.TheWorld:PersistentLevel.TESTEnemyController_9 Function Test.TESTEnemyController:Roaming.Tick:002A

    – Vuelvo a cargar el nivel, y siguen saliendo esos mensajes.

    Parece que el controller se ha quedado sin pawn que lo controle, se elimina el pawn pero el controller se queda en el limbo, por lo visto. Y al cargar el nivel y aparecer los pawns, se les asigna un nuevo controller, pero el antiguo sigue ahí buscando a su dueño. Y así se van acumulando controllers cada recarga del nivel.

    Puse un evento unpossess con un mensaje y parece que no se ejecuta.

    Y poner todos bots en el nivel base no es opción, porque aunque los tengas parados y sin renderizar, consumen bastante del “game” como se ve en los stats.

  16. Quizás un “destroy” si pawn == none en alguna parte del controller lo solucionaría, pero quisiera ver primero si estoy haciendo algo mal en algún sitio.

  17. Le echaré un ojo. Le puse un destroy al controller y funciona bien, pero los paws “desaparecen” al rato de descargarse el nivel, que es cuando el controller se queda sin dueño. Puede tardar un minuto desde que se descargan. Además me salen cosas como éstas:

    [0010.22] ScriptWarning: Accessed None ‘Game’
    UTVehicleFactory_Scorpion tests.TheWorld:PersistentLevel.UTVehicleFactory_Scorpion_2
    Function utgame.UTVehicleFactory:Active.BeginState:002B

    o
    Warning: Warning, Failed to load ‘Class None.’: Failed to find object ‘Class None.’

    o
    ScriptLog: SpawnDefaultController TESTPawn_0 , Controller != None AIController_8

    o sea, el player con AIController, que no sé de dónde lo saca.

    Acojonao voy cada vez que miro los logs…

  18. Aquí una cosa que hago cuando los bots están lejos:

    1er. paso quitarles las físicas a media distancia:
    Mesh.PhysicsAssetInstance.SetFullAnimWeightBonesFi xed(True, Mesh);
    Mesh.SetHasPhysicsAssetInstance(false);

    2o. paso en larga distancia “dormirlos” para que no gasten cpu:
    SetCollision(false,false);
    SetTickIsDisabled(TRUE);
    SetHidden(true);

    Además los envío a un state mínimo con un sleep largo. Desde ese state compruebo la distancia para despertarlos (haciendo lo contrario a lo de arriba).
    Así en un mapa con 1.000 bots gasta 6 ms en “game” cuando sin bots gastaba 4 ms. Sin las optimizaciones se iba a 30 ms o más.

  19. Por favor necesito ayuda llevo intentandolo hace mucho tiempo y me que de estancado en esto T-T hago todo el tutorial y en el editor funciona bien al mover el cuadrado arriba y abajo pero en el juego no se mueve, pero si en el editor lo dejo mirando hacia arriba aparece haci en el juego , pero todo el tiempo asi por favor si puedes ayudarme estare eternamente agradecido , y buenos tutoriales 🙂

    • Quizá haya algún error en el código que asocia el AimNode, comprueba que los nombres coincidan. Si en el editor funciona es que el AnimTree está bien montado, será algún fallo pequeño de código.

  20. Termine por cambiarle el nombre al aimoffset como aimnode y en el script lo mismo y ahora funciona 🙂 muy buenas las explicaciones y gracias por tomarte el tiempo de responder

  21. Buenas tardes amigo en caja PFMAimNode voy la propiedad anim node anim offset …para selecionar …bake from animations ..no medeja selecionarlo ya que me sale una ventana diciendo EXPORT FINIST CHECK LOG FOR DETAILS…agradeceria que me ayudaras gracias por sus tutosss

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s