PFM: Creación de un generador de enemigos en UDK

Para crear las oleadas de enemigos del juego, será necesario de alguna manera controlar dónde y cuándo aparecen los enemigos. Para facilitar esto se creará un Actor que sea simplemente un generador de enemigos.

Este actor, PFMEnemySpawner, creará un área a su alrededor en el que se generarán los enemigos (evitando que se generen siempre en el mismo punto). El código es el siguiente:


class PFMEnemySpawner extends Actor
placeable;

// Cilindro en el que se hará el spawn de los enemigos, editable en el editor
var() editconst const CylinderComponent CylinderComponent;

// Enemigos que quedan por "spawnear"
var int enemiesLeft;

auto state Spawning
{
    local Vector newSpawnLocation;

    Begin:
    while(true)
    {
        if(enemiesLeft > 0)
        {
            // Cálculo de localización dentro del cilindro
            newSpawnLocation = VRand();     //Vector unitario aleatorio
            newSpawnLocation.Z = 0;         //Se elimina la componente Z (vertical)
            Normal(newSpawnLocation);       //Se normaliza el nuevo vector
            newSpawnLocation *= CylinderComponent.CollisionRadius * FRand();  //Se le da una longitud aleatoria
            newSpawnLocation += Location;   //Se suma a la posición del spawner

            // Se intenta hacer spawn del enemigo
            if(SpawnPFMEnemy(newSpawnLocation) != none)
            {
                enemiesLeft--;
            }
        }
        Sleep(0.1);
    }
}

function Pawn SpawnPFMEnemy(Vector spawnLocation)
{
    local AIController EC;
    local Pawn EP;

    // Spawn del EnemyPawn
    EP = spawn(class'PFMEnemyPawn',self,,spawnLocation);
    if(EP == none)
    {
        // En caso de no haber sido posible hacer el spawn
        return none;
    }
    // Spawn del EnemyController
    EC = spawn(class'PFMEnemyController');
    // El EnemyController posee al EnemyPawn
    if(EC != none && EP != none)
    {
        EC.Possess(EP,false);
    }
    return EP;
}

function SpawnPFMEnemies(int num)
{
    enemiesLeft += num;
}

defaultproperties
{
    // Cilindro que define el area de spawn
    Begin Object Class=CylinderComponent Name=CollisionCylinder
        CollideActors=true
        CollisionRadius=+0040.000000
        CollisionHeight=+0040.000000
        bAlwaysRenderIfSelected=true
    End Object
    CollisionComponent=CollisionCylinder
    CylinderComponent=CollisionCylinder
    Components.Add(CollisionCylinder)

    // Sprite para verlo en el editor
    Begin Object Class=SpriteComponent Name=Sprite
        Sprite=Texture2D'EditorResources.S_Actor'
        HiddenGame=False
    End Object
    Components.Add(Sprite)

    enemiesLeft=0
}

En el estado por defecto Spawning, el generador está continuamente comprobando si le quedan enemigos por hacer aparecer. En caso afirmativo se busca una posición aleatoria dentro del área definida por un cilindro invisible durante el juego. Sabemos las dimensiones de dicho cilindro, pues se halla referenciado en la variable CylinderComponent. Hallada la posición aleatoria dentro del área permitida se intenta generar el nuevo enemigo, y sólo si se hace con éxito se reduce el número de enemigos restantes. Si no ha habido éxito en la generación se seguirá intentando en otras posiciones hasta que se consiga.

La función SpawnPFMEnemy es la que se encarga realmente de intentar generar un nuevo enemigo en la posición que se le pasa como argumento. Como sabemos, el PNJ básico que creamos necesitaba de un Pawn y un AIController para funcionar correctamente, por lo que en realidad habrá que generar ambas entidades y hacer que el controller ‘posea’ el Pawn para manejarlo.

En la zona de defaultproperties vemos cómo se crea un componente cilindrico que servirá para definir el área de generación de enemigos. Al principio de la clase también vemos que se define la variable CylinderComponent. Al usar la palabra var() en lugar de la habitual var para declararla, posibilitamos que esta variable sea editable directamente en el editor, dentro de las propiedades del actor PFMEnemySpawner, pudiendo así modificar su tamaño según convenga en el mapa.

pfm05_01

Para probar las funciones de generación de enemigos de momento las invocaremos mediante funciones exec. Las funciones exec son un tipo de funciones especiales que pueden invocarse desde la consola de juego, por tanto son muy útiles para pruebas. Estas funciones exec sólo pueden definirse en clases especiales como los GameType (entre otras), así que modificaremos el nuestro para incluirlas.

class PFMGame extends UTDeathMatch;

var PFMEnemySpawner enemySpawner;

simulated function PostBeginPlay()
{
    local PFMEnemySpawner ES;

    super.PostBeginPlay();

    //Se busca el enemySpawner
    foreach DynamicActors(class'PFMEnemySpawner',ES)
        enemySpawner = ES;
}

exec function SpawnEnemies(int num)
{
    enemySpawner.SpawnPFMEnemies(num);
}

exec function SpawnEnemy()
{
    enemySpawner.SpawnPFMEnemies(1);
}

defaultproperties
{
    //Se especifica el Pawn por defecto
    DefaultPawnClass=class'PFM.PFMPawn'
    //Se especifica el controlador
    PlayerControllerClass=class'PFM.PFMPlayerController'
}

El resultado final:

Los enemigos aparecen alrededor del icono y dentro del cilindro definido.
Banner Blog

20 comentarios en “PFM: Creación de un generador de enemigos en UDK

  1. Veo que los enemigos identifican las geometrias del escenario como obstáculos para evitarlas. ¿Pero si un obstáculo tuviera transparencia – cristal, humo, agua- lo podría atravesar o su lógica dl pathfinder sería evitarlo?.

    • En realidad aquí todavía no evitan obstáculos, simplemente avanzan hacia su objetivo chocando contra los objetos que se encuentran.

      Contestando a tu pregunta, los mapas de navegación para hacer pathfinding se crean utilizando la geometría de las mallas estáticas del escenario (aunque también puede hacerse con objetos móviles). Si el cristal es geometría el personaje lo evitará, si el humo o el agua es un sistema de partículas no lo evitará. Precisamente la siguiente entrada que publique será sobre mapas de navegación y pathfinding, y ahí sí los personajes evitarán los obstáculos para llegar a su objetivo.

  2. Pingback: PFM: Scripting de las oleadas de enemigos con Kismet | El Blog de Marcos

  3. Saludos de nuevo Marco….
    tengo un nuevo problema xd…. me genera un error en al linea 77 del sprite en el
    » PFMEnemySpawner» … aqui te dejo una imagen del error….
    pense q era por la textura en 2D .. asi q decidi cambiarla por otra textura en 2D q ya estaba en el counter browser …. pero aun asi me siguio dando el mismo error ….

    «http://fotos.subefotos.com/86a47bfff275b10f724f16f854e169a1o.png»

    gracias de antemano xD

  4. por que en algunos casos las clases las pones con la ruta completa y en otros no?
    Ej.:
    class’PFMEnemySpawner’ & class’PFM.PFMPlayerController’

    • Pues… no me había fijado :D. No lo sé seguro pero imagino que mientras no haya lugar a confusión da un poco igual poner la ruta completa o no. Como yo pongo el prefijo PFM a todas mis clases no he tenido problemas con clases ya existentes.

  5. Ey Marcos soy yo otra vez, perdona mi ignorancia, pero te quería preguntar que si los códigos que dejas de ejemplo los podemos copiar y pegar o hay que escribirlo a mano, y donde ¿en un bloc de notas que luego guardas en la carpeta «classes»?, porque lo he hecho así y me da error en los scrips, ¿como lo hago?

    • Hola Alejandro, da igual si los copias o los escribes a mano. Lo que sí es importante es que entiendas lo que estás haciendo jejeje
      Si me dices qué error te da quizá pueda ayudarte 😉

  6. pues mira:
    1º: creo un blog de notas, y le pongo de nombre por ejemplo: PFMHUDScene.
    2º: Luego lo abro, copio los ejemplos de los códigos que pones de ese apartado y luego lo guardo, después creo otro, por ejemplo PFMHUDSceneInGameMenu, y vuelvo a hacer lo mismo con todos los blogs de notas, pero al recopilar los scrips cuando inicio el UDK me aparece al final una advertencia en amarillo, algo como esto:

    Warning, Can´t find files matching C:\UDK\UDK-2013-02\Binaries\Win 64\..\..\Development\Src\PFM\Classes\*.uc

    Warning/Error Sumary
    ——————–
    Warning, Can´t find files matching C:\UDK\UDK-2013-02\Binaries\Win 64\..\..\Development\Src\PFM\Classes\*.uc

    Success – 0 error (s), 1 warning (s)

    Otras veces me sale un error parecido pero en rojo, si no te importa cuando me salga te lo envío.

  7. Hola Marcos he realizado todos los pasos, los scripts no me dan errores, puedo crear el «PFMEnemySpawner» y darle un valor para que sea mas grande, pero cuando lo ejecuto y escribo en la consola SpawnEnemy no sale ningun enemigo, si pongo SpawnEnemies 10, tampoco sucede nada.

    Por otro lado el EnemyPawn si que me funciona, puedo poner una «Arañita» que cuando me ve me sigue.

    No se que puede ser, alguna idea?

    Gracias de ante mano, me esta sirviendo de mucha ayuda tu blog.

  8. Otra pregunta que podria ser la respuesta, el archivo .uc del PFMGame, que sirve para que se puedan ejecutar comandos exec ¿se ha de llamar PFMGame.uc o PFMGameType.uc? es que es lo unico que no tengo claro.

    Gracias de nuevo.

    • Hola Luis, el archivo debe llamarse igual que la clase que contiene, en este caso sería PFMGame, por lo que el archivo debe llamarse PFMGame.uc. De otra manera no compilarían los scripts. Lo que no importa es el nombre que se le ponga a la clase, puede ser cualquiera, pero el archivo sí debe llamarse igual. Saludos.

  9. hola marcos tengo un problema o mas bien dos yo siempre habia tenido el udk vercion 2012 y nunca tuve ningun problema pero me cambien al 2014 la mas reciente de febrero y al abrirlo el primer prolema es que me da error : la aplicacion no se pudo iniciar correctamente (x000007b)
    y solo la puedo cerrar entonces la abro con el fronted editor o algo asi y el otro es que al intentar modificar el actor factory como lo haces en el video al cambiar el factory y selecionar utactorfactory ai o mas o menos asi se escrive se pone pero no sale nada mas los cuandros de seleccion no slae ni nada del inventorio de armas que puedo hacer a y lla reistale la 2012 y pasa lo mismo espero que me puedas ayudar muy uen bideo bien explicado spero que sigas haci gracias.

Deja un comentario