Archivo de la categoría: UnrealScript

Tutoriales sobre UnrealScript, el lenguaje de programación del motor de juegos Unreal Engine 3

Compilando con Unreal FrontEnd y Configurando un arma para el Player – parte 1

Hola, aquí está la tercera entrega de esta serie de tutoriales sobre UnrealScript en esta entrega veremos dos temas completamente distintos: Compilar con Unreal FrontEnd y Configurar un arma para el Player, debido a lo extenso de este segundo tema lo dividiremos en dos partes.

Hoy veremos:
– Cómo compilar y ejecutar nuestro juego con Unreal FrontEnd (UFE)
– Cómo configurar la resolución por defecto del juego
– Cómo agregarle un arma al Player y configurarla.
– Cómo procesar el impacto del disparo del arma
– Cómo reproducir un efecto de sonido al disparar el arma.

Compilando y ejecutando con Unreal FrontEnd (UFE)

Antes de ponernos a modificar nuestros scripts, veremos como compilar y ejecutar nuestro juego desde Unreal FrontEnd (UFE).

En los dos tutoriales pasados compilamos los scripts mediante el UDK Editor o sea, modificamos los scripts, abrimos el UDK Editor, y este nos pregunta si queremos recompilarlos, le damos que sí. Cuando termina, abrimos de nuevo el UDK Editor, cargamos el Level en el que estábamos trabajando, nos aseguramos de que en el Game Type for PIE en World properties esté indicando nuestro juego y damos Play From Here. Esta variante para compilar nuestro juego es válida, pero evidentemente extremadamente tediosa, y más cuando no necesitamos para nada el Editor.

UDK trae una herramienta para compilar y empaquetar nuestro juego sin necesidad de tocar para nada el Editor. Esta herramienta es: Unreal FrontEnd (UFE), la podemos encontrar en Ruta_de_instalación_del_UDKBinariesUnrealFrontend.exe

Esta es la aplicación que se va a encargar de compilar y ejecutar nuestro juego. Como ves, su interfaz es relativamente sencilla. Hoy no vamos a ver todas las opciones que tiene, veremos solamente las que usaremos para este tutorial, y en próximos tutoriales, a medida que usemos nuevas funcionalidades las explicamos. De todas formas si desde ahora quieres saber para qué sirve cada uno de los rincones de esta herramienta, mira aquí

Antes de comenzar a trabajar con el UFE, abre el archivo DefaultGame.ini (UDKGameConfig). Verás casi al comienzo del archivo el bloque [Engine.GameInfo] sustituye todo ese bloque por el siguiente

[Engine.GameInfo]
DefaultGame=SampleGame.SampleGame
DefaultServerGame=SampleGame.SampleGame
PlayerControllerClassName=SampleGame.SamplePlayerController
DefaultGameType="SampleGame.SampleGame"

Con estos cambios estamos configurando el GameType por defecto para el Engine.

Bien, ahora sí vamos a configurar el UFE para compilar nuestro juego. Lo primero que debemos hacer es asegurarnos de que en el panel de Perfiles (a la izquierda de la ventana) tenga seleccionado UDKGame on PC. Este es nuestro juego, si entras a la raíz de la instalación del UDK verás que hay una carpeta llamada UDKGame. Esa es la raíz de nuestro juego, dentro están todos los paquetes de sonido, armas, mapas, etc (Content). Los archivos de configuración (Config), etc. En conclusión, todo lo que conforma nuestro juego. Aquí vamos a estar trabajando indistintamente en el transcurso de los tutoriales, por ese motivo no me voy a detener en explicar cada una de las carpetas y su contenido en este momento, aunque por sus nombres te puedes llevar una buena idea de su objetivo.

Después de garantizar que tengamos seleccionado el perfil UDK Game on PC podemos compilar a ver que tenemos. Da clic sobre el botón Start. Como verás, el resultado será que se compilará y ejecutará el juego de ejemplo que trae el UDK. Como es lógico esa no es la idea, lo que queremos es compilar nuestro juego. En este punto tenemos dos variantes. Una es modificar los archivos de configuración del UDK Game para que cargue también nuestro GameType (y lo podamos seleccionar en el menú GameMode) o más directo, indicar una URL a compilar. En este caso vamos a usar la segunda variante, ya que queremos independizarnos totalmente de este juego de ejemplo y desarrollar nuestro juego desde “cero”.

Si te fijas el UFE tiene un Panel llamado Maps to Cook. En este panel podemos indicar los Mapas para “cocinar” y empaquetar con nuestro juego. Da clic en el botón Add, verás un listado de Mapas, pero… aquí no está el Mapa con el que hemos estado trabajando (la habitación sencilla). El motivo de esto es que aquí salen los Mapas que están en la carpeta .UDKGameContentMaps. Sabiendo esto lo único que tenemos que hacer es copiar nuestro Mapa aquí, pero antes debemos indicar en el Mapa un PlayerStart. Este será el punto donde aparezca el Player cuando comience el juego, cuando compilábamos y ejecutábamos desde el Editor no lo necesitábamos porque dábamos Play From Here :-).

Agregando el PlayerStart a nuestro Mapa

Hacer esto es muy sencillo, abre el UDK Editor, carga el mapa con el que hemos estado trabajando y en el punto donde quieras que aparezca el Player da clic derecho – Add Actor – Add PlayerStart. Listo, con esto tenemos, puedes aprovechar y desde aquí guardar directo en UDKGameContentMaps. En caso que los tutoriales los estés siguiendo con otro Mapa, no hay problema, solamente asegúrate que tenga un PlayerStart y esté en la carpeta UDKGameContentMaps.

Agregando nuestro Mapa al listado de mapas a cocinar por el UFE

Ahora solo nos queda agregar nuestro mapa al listado de mapas a “cocinar” por el UFE. Para esto da clic sobre el botón Add del panel Maps to Cook selecciona el Mapa en la lista y da clic en el botón Add Selected Maps (recuerda que para que aparezca aquí debes haberlo copiado en la carpeta UDKGameContentMaps).

Ahora nuestro mapa estará en el listado de Maps to Cook, solo nos queda configurar la URL. Esta URL es una forma más rápida de indicar el GameType y el mapa por defecto que queremos compilar y ejecutar. Debajo del panel Maps to Cook hay dos pestañas Launch Map y URL, selecciona la pestaña URL y en el campo de texto escribe:

MG-SampleLevel?game=SampleGame.SampleGame

Es bastante sencillo entender qué significa cada elemento de la URL, la raíz es el Mapa que queremos cocinar (cambia el MG-SampleLevel por el nombre de archivo de tu mapa) y mediante la variable game indicamos el GameInfo

Ahora da clic sobre el botón Start y el Unreal FrontEnd hará el resto ;-).

En el último panel del UFE tenemos la consola de salida ésta te va mostrando el progreso de la compilación. En caso de algún error, aquí te mostrará los detalles, si tienes algún error repasa de nuevo los pasos indicados hasta aquí, si aun así no compilas, coméntame tu error a ver si puedo ayudarte, espero no tengas problemas.

Bien!. Ya sabemos cómo compilar y ejecutar nuestro juego desde el UFE. Esto nos hará el proceso de desarrollo mucho más fácil y rápido.

Un pequeño detalle, los archivos de configuración del juego traen como resolución por defecto para el juego: 1024×768. Con los niveles de detalles que alcanza el Unreal Engine, esta resolución es mucho para los que no tenemos una tarjeta de video profesional :-S. por lo que el juego se debe ver bastante lento. Podemos bajar esta resolución a 800×600 para evitar este problema.

Configurando la resolución de pantalla inicial para nuestro juego

Para hacer esto abre el archivo UDKEngine.ini que se encuentra en .UDKGameConfig y con la funcionalidad de Buscar del Editor de Texto que estés usando busca: ResX. Es evidente lo que debes hacer ahora no? :-). Cambia el 1024 por 800 y el 768 de la siguiente línea por 600. Cierra el archivo y guarda los cambios. Ahora haces exactamente lo mismo con el archivo DefaultGame.ini. Cierra el archivo, guarda y compila de nuevo. Mucho mejor para nuestro caso verdad ? :-)

Cerrando el juego mediante el comando “exit”

Si recuerdas, al compilar nuestro juego mediante el Play From Here del Editor teníamos la ventaja que al presionar Esc se cerraba el juego. En este caso esto no va a funcionar, recuerda que esto sería lo mismo que tendría el usuario final, el proceso normal es que salga una ventana preguntando si quieres salir etc. En el proceso de desarrollo, esto es bastante tedioso.

Para salir rápido del juego podemos usar la consola. Ya con el juego abierto y corriendo, presiona la tecla Tab. Se te abrirá en la parte inferior de la pantalla la consola de comandos. Poco a poco iremos viendo varios comandos sumamente útiles, aquí está el primero. Escribe exit en la consola y presiona Enter.

Bien, ahora si podemos volver a nuestros scripts y modificarlos, ya que compilarlos y ejecutar vimos que es cosa de unos pocos clics gracias al UFE.

Continuando con el proyecto que dejamos del tutorial pasado, vamos a seguir trabajando sobre el Player. En este caso vamos a agregarle un arma con la que pueda defender el universo :-)

Agregando un arma al Player

En UnrealScript los “pasos” para adjuntarle un arma al Player son generalmente los siguientes:

1. Crear un InventoryManager y decirle al Pawn del Player que lo use. Por el nombre de esta clase se deduce su objetivo no?.

2. Crear el arma (Weapon) y adjuntarla al mesh del Player. Como es lógico, debemos crear también la clase que encerrará toda la lógica del arma. O sea, donde se procesa lo que pasa cuando se dispara, cuando el tiro le da a alguien o algo, etc.

3. Agregar el arma al InventoryManager

Bien, ya que sabemos esto vamos a comenzar a armar a nuestro Player.

Dentro de la carpeta Classes de SampleGame crea un nuevo archivo llamado SampleInventoryManager.uc y dentro cópiale el siguiente código.

class SampleInventoryManager extends InventoryManager;

defaultproperties
{
	PendingFire(0)=0
}

Como hemos hecho hasta ahora, lo primero es indicar de qué clase vamos a heredar, y para este objetivo tenemos la clase InventoryManager. Por otro lado, en cuanto a las propiedades a configurar en defaultproperties lo único que necesitamos por el momento es inicializar el PendingFire. Este atributo es un arreglo para definir el estado de fuego de nuestra arma, por lo general lo inicializaremos en 0 (más adelante cuando veamos el comando showdebug weapon podrás ver en la práctica como cambia este atributo). Seguro te llama la atención que el atributo es un arreglo, esto es para poder configurar el PendingFire en cada uno de los modos de fuego que tendrá nuestra arma. Por ejemplo, podemos tener un arma que tenga el disparo normal, pero además tenga un lanza cohetes. En este caso, el valor de PendingFire en el índice 0 es para el disparo estándar, y para el índice 1 es para el lanza cohetes. La mayoría de los atributos relacionados con nuestra arma siguen este mismo principio. En este ejemplo, tendremos un solo modo de fuego, por lo que solamente tenemos que configurar el índice 0.

Ya teniendo listo nuestro InventoryManager, debemos indicarle al Pawn del Player que lo use. Abre el archivo SamplePawn.uc y en el bloque defaultproperties agrega la siguiente línea

InventoryManagerClass=class'SampleGame.SampleInventoryManager'

Con esta línea a estas alturas no debes tener ningún problema si has seguido los tutoriales anteriores.

Bien, pero con esto no es suficiente, siguiendo la estructura lógica del UDK el GameInfo de nuestro juego llama al método AddDefaultInventory de nuestro Pawn. Si buscas este método en InventoryManager (la clase de la que hereda SampleInventoryManager) verás que no tiene implementación, por lo tanto la debemos implementar nosotros (con una línea tendremos). En esa misma clase SamplePawn agrega el siguiente método (entre las líneas var DynamicLightEnvironmentComponent … y defaultproperties):

function AddDefaultInventory()
{
	InvManager.CreateInventory(class'SampleGame.SampleWeapon');
}

Esta es la primera vez que creamos un método en una clase, pero como vez, en este caso no hay mucha explicación que dar en cuanto a sintaxis. :-). El cuerpo del método es una sola línea y lo que hace es crear un nuevo Item y agregárselo al inventario del Pawn. InvManager es una referencia al inventario. Como el ítem que queremos crear es el arma, pues lo que pasamos como parámetro es la clase que define nuestra arma, que aún no la tenemos hecha, así que no compiles todavía 😉

Creado la clase Weapon

Dentro de la carpeta Classes de SampleGame crea un nuevo archivo llamado SampleWeapon.uc y agrégale las siguientes líneas

class SampleWeapon extends Weapon;

defaultproperties
{
	Begin Object Class=SkeletalMeshComponent Name=WeaponMesh
		SkeletalMesh=SkeletalMesh'WP_LinkGun.Mesh.SK_WP_LinkGun_3P'
	End Object
	Mesh=WeaponMesh
	Components.Add(WeaponMesh)

	FiringStatesArray(0)=WeaponFiring
	WeaponFireTypes(0)=EWFT_InstantHit
	FireInterval(0)=1.0
}

Sencillamente estamos creando una clase para encerrar la lógica del arma de nuestro Player. Como siempre, el UDK nos da una clase de la que podemos heredar para facilitarnos las cosas. Como vez, en el bloque defaultproperties configuramos los aspectos básicos del arma. El primer bloque ya no te debe asustar, estamos indicando cual será el mesh del arma. Puedes darle un look en el AnimSet al kGun.Mesh.SK_WP_LinkGun_3P para que veas con detalles el arma que usaremos, o puedes usar cualquier otra que quieras.

El siguiente atributo que inicializamos es el arreglo FiringStatesArray. En este arreglo se almacenará el firing state de los distintos modos de fuego. Sé que lo que acabo de decir no es muy explicativo :-S pero espera al siguiente tutorial para comprender mejor este atributo. Tiene que ver con un tema bien importante en la programación de juegos, y que dicho sea de paso UnrealScript lo maneja muy bien, los States. Así que no te preocupes, en el siguiente tutorial veremos detalladamente los states y comprenderás a la perfección el objetivo de este atributo.

Con la línea: WeaponFireTypes(0)=EWFT_InstantHit lo que hacemos es indicar el tipo de disparo que usará esta arma. El atributo WeaponFireTypes es un arreglo de EWeaponFireType y este es una enumerada que define los distintos tipos de disparo (EWFT_InstantHit, EWFT_Projectile, EWFT_Custom, EWFT_None). Para este tutorial usaremos el primero. Este atributo define el comportamiento del disparo, por ejemplo, con EWFT_InstantHit estamos haciendo que el impacto del tiro sea al momento de presionar el clic, para nuestro primer juego con esto tenemos, Por otro lado, EWFT_Projectile nos permite trabajar con algo más parecido a la realidad o sea, con este modo de disparo nuestra arma se comportará más como en la vida real, tiene un alcance determinado, la bala va modificando su trayectoria de acuerdo a la distancia de desplazamiento etc. El tema de Proyectiles lo veremos en próximos tutoriales. Como dije, por el momento tendremos una puntería certera: “donde pongas el ojo, o mejor dicho el cursor :-) ahí pondrás la bala”.

Otro comportamiento interesante que podemos ajustar es el intervalo entre disparos, para esto constamos con el atributo FireInterval. Bueno, si nos ponemos a definir cada uno de los atributos con los que puedes trabajar no terminamos ni en un año :-) Así que te dejo a ti para que juegues con el resto, revisa la clase Weapon y verás todo lo que puedes configurar, los comentarios son bastante descriptivos. Recuerda algo, cada atributo de esto es un arreglo, para poder configurar el comportamiento en cada uno de los modos de disparo que pueda tener nuestra arma.

Ya casi estamos listos. El siguiente paso es adjuntar el arma al Player

Para esto necesitamos sobrescribir el método AttachWeaponTo, que heredamos de Weapon. Y en él debemos adjuntar el mesh del Arma al mesh del Player. Dentro de la clase SampleWeapon agrega los siguientes métodos

simulated function TimeWeaponEquipping()
{
	AttachWeaponTo( Instigator.Mesh,'WeaponPoint' );
	super.TimeWeaponEquipping();
}

simulated function AttachWeaponTo(SkeletalMeshComponent MeshCpnt, optional Name SocketName)
{

	MeshCpnt.AttachComponentToSocket(Mesh, SocketName);
}

Como vez, aquí tenemos dos métodos, uno con el que adjuntaremos el arma al Player (AttachWeaponTo) y el otro para indicar dónde se va a llamar este método, y para esto un buen lugar es en el método TimeWeaponEquipping. Por tanto, lo que hacemos es sobrescribir este método para agregarle la llamada a AttachWeaponTo pasándole como primer parámetro el mesh del player, que mediante Instigator.Mesh lo podemos obtener y el socket donde vamos a adjuntar el arma.

En AttachWeaponTo el proceso de adjuntar los mesh se hace exactamente en la línea MeshCpnt.AttachComponentToSocket(Mesh, SocketName); . Lo que se le debe indicar al método AttachComponentToSocket es el mesh del arma y el socket. Este último parámetro es el punto físico del mesh donde será adjuntada el arma. Por supuesto, este punto debe estar definido. Si revisas en el AnimSet del UDK Editor el mesh que usamos para nuestro Player, podrás ver los Sockets que tiene definido, y como es lógico, el WeaponPoint está en las manos (Por defecto los Sockets están ocultos, para verlos, selecciona en el menú principal del AnimSet View – Show Sockets).

Notarás que estos dos métodos tienen la palabra clave simulated delante. Por el momento no te preocupes por su objetivo, tiene que ver con temas de red, en su momento tocaremos detalladamente el tema.

Bien, con esto tendríamos, si en este punto ejecutas el juego, verás que ya nuestro Player tendrá el arma pero aparece muy oscura. Por tanto, vamos a arreglar esto usando el LightEnvironment que configuramos para el Pawn del Player para iluminar también el arma. Sustituye el método AttachWeaponTo que acabamos de crear por el siguiente:

simulated function AttachWeaponTo(SkeletalMeshComponent MeshCpnt, optional Name SocketName)
{
	local SamplePawn P;
	P = SamplePawn(Instigator);
	MeshCpnt.AttachComponentToSocket(Mesh, SocketName);
	Mesh.SetLightEnvironment(P.LightEnvironment);
}

Primero creamos una variable local para obtener una referencia al Pawn de nuestro Player. Para esto nos ayuda Instigator, pero recuerda que el miembro LightEnvironment está definido únicamente como miembro de SamplePawn por lo que debemos hacer una especie de casting al Instigator para poder acceder a este miembro, y precisamente eso es lo que hacemos con la línea: P = SamplePawn(Instigator); Por último, usamos el método SetLightEnvironment del mesh de nuestra arma y le pasamos el LightEnvironment de nuestro Pawn.

Listo!!, Compila y Ejecuta. Tendrás el siguiente resultado

Ya nuestro robot está armado y casi listo para salvar al mundo :-) … Digo casi listo porque si tratamos de disparar (por defecto es el clic izquierdo del mouse) no tendremos ningún resultado. Pero podemos probar lo siguiente: con el juego corriendo abre la consola de comandos (tecla Tab) y entra el siguiente comando: showdebug weapon. En las últimas líneas verás la información acerca del arma y si disparas verás cómo cambian los valores, demostrando que todo el proceso se está llevando a cabo correctamente, el único problema que tenemos es que en ningún momento hemos definido la lógica para el impacto. Que como ya mencionamos, al usar el modo EWFT_InstantHit, este momento es en cuanto presionamos el clic. Vamos a resolver este problema.

Mostrando la “mirilla” de nuestra escopeta

Con este sistema de impacto (EWFT_InstantHit) como dijimos, “donde pongamos el ojo ponemos la bala” pero si no tenemos una mirilla para nuestra arma el proceso de “poner el ojo” exactamente donde queremos será bastante difícil :-) Por tanto, antes de trabajar en el impacto del disparo, vamos a dibujar en el HUD una pequeña cruz blanca que nos servirá de mirilla, para asegurarnos de dar exactamente donde queremos.

Una de las formas que tenemos para hacer esto es sobrescribir el método DrawHUD de InventoryManager. Abre el archivo SampleInventoryManager.uc y agrega el siguiente método

simulated function DrawHUD( HUD H )
{
	local float CrosshairSize;
	super.DrawHUD(H);

	H.Canvas.SetDrawColor(255,255,255,255);

	CrosshairSize = 4;

	H.Canvas.SetPos(H.CenterX - CrosshairSize, H.CenterY);
	H.Canvas.DrawRect(2*CrosshairSize + 1, 1);

	H.Canvas.SetPos(H.CenterX, H.CenterY - CrosshairSize);
	H.Canvas.DrawRect(1, 2*CrosshairSize + 1);

}

Este método no lo explicaremos en este tutorial, para no salirnos del tema, ya que el tema del HUD y Canvas en UDK es bastante extenso, por eso también le tenemos dedicado un tutorial. Por el momento, basta con saber que lo que hace es dibujar en la pantalla una cruz blanca en la posición donde esté mirando el Player.

Bien, puedes compilar y ejecutar, tendrás el siguiente resultado.

Ahora sí podemos encargarnos de procesar el impacto del disparo.

Procesando el momento de impacto del disparo

Para indicar la lógica de impacto del disparo para el modo InstantHit debemos implementar el método ProcessInstantHit de la clase Weapon. Abre la clase SampleWeapon y agrega en ella el siguiente método

simulated function ProcessInstantHit(byte FiringMode, ImpactInfo Impact, optional int NumHits)
{
	local SamplePlayerController PC;
	PC = SamplePlayerController(Instigator.Controller);
	PC.ClientMessage("Material: "$Impact.HitInfo.Material);
}

¿Qué hemos hecho aquí?. Bueno, como aún no tenemos enemigos, vamos a definir el momento del impacto muy sencillo. Mostraremos en pantalla el nombre del material aplicado sobre el elemento donde impactó nuestro disparo. Como vez, uno de los elementos que le es pasado a este método es ImpactInfo, mediante esta variable (la puedes buscar con el UnCodeX) obtendremos muchísima información del objeto al que le dimos el tiro. Para nuestro ejemplo solo usaremos el valor Impact.HitInfo.Material. Que contiene el material de ese elemento. Ahora, para mostrar esto podemos usar un método definido en PlayerController que nos sirve para mostrar mensajes en la pantalla: ClientMessage. Por tanto, necesitamos desde el arma obtener una referencia a nuestro PlayerController y para esto nos ayuda el miembro Controller de Instigator. Ya parados en el controller llamamos a su método ClientMessage que recibe simplemente el string que se mostrará en pantalla. Notar el uso del operador $ con el objetivo de concatenar dos cadenas de caracteres.

Ya tenemos lista la lógica de procesamiento del momento del impacto de nuestros disparos. Si has estado siguiendo los tutoriales con el mapa de la habitación que hicimos en el primero, no podrás ver ningún cambio porque en esta habitación no tenemos ningún otro elemento al no ser la propia habitación. Además, a ésta no le aplicamos ningún material. Por tanto, para ver en funcionamiento el ejemplo bien claro, abre con el UDK Editor nuestro Mapa, el que está en UDKGameContentMaps y agrégale a la habitación cualquier Static Mesh. Supongo que esto lo sabes hacer, si no es así, antes de seguir con estos tutoriales, debes darle un vistazo al tema del Diseño de Niveles con UDK, de esto si hay mucha información en internet.

Hecho esto, compila, ejecuta y dispara a distintos elementos en el nivel, para que veas que en el momento del impacto, se muestra un mensaje en la pantalla con el nombre del material aplicado a ese objeto.

Este sistema de mensajes también es muy útil para depurar nuestro juego en busca de errores.

Bien, aunque con este sistema para procesar el Impacto del disparo será muy difícil eliminar a los enemigos :-) nos sirve a la perfección para comprender el proceso, y como aún no tenemos enemigos entonces podemos respirar….

Ya tenemos a nuestro robot armado y listo para atacar al enemigo :-) pero como notarás, tenemos algunos problemitas. El primero, por mucho que disparemos no se escucha nada. Vamos a solucionar esto reproduciendo un efecto de sonido en el instante del disparo para darle más vida al juego.

Reproduciendo el efecto de sonido para el disparo del arma

Reproducir un efecto de sonido cuando el arma se dispara es bastante sencillo. Solamente tenemos que indicar el sonido (SoundCue) a reproducir y reproducirlo en el momento del disparo. Abre la clase SampleWeapon y después de la línea class SampleWeapon extends Weapon; agrega lo siguiente

var	array<SoundCue>	WeaponFireSnd;

A continuación, en el bloque defaultproperties agrega:

WeaponFireSnd(0)=SoundCue'A_Weapon_Link.Cue.A_Weapon_Link_FireCue'

Como ves, lo que hacemos aquí es crear un array para almacenar el efecto de sonido (SoundCue) del disparo para los distintos modos de nuestra arma (aunque para el ejemplo recuerda que estamos usando un solo modo) y en el bloque defaultproperties indicamos que efecto vamos a usar.

Hecho esto, solo nos queda indicar en qué punto se debe reproducir el sonido. Un buen lugar para esto es el método FireAmmunition ya que aquí es donde comienza la lógica del disparo. Vamos entonces a sobrescribir este método para agregar la reproducción del efecto. En la clase SampleWeapon agrega lo siguiente:

simulated function FireAmmunition()
{
	Super.FireAmmunition();
	Instigator.PlaySound(WeaponFireSnd[0]);
}

Lo único que hacemos aquí es garantizar que el FireAmmunition de la clase base se ejecute y además se reproduzca el efecto de sonido que indicamos. Para reproducir el efecto nos ayuda el método PlaySound de Instigator. También puedes notar que para acceder al índice deseado en WeaponFireSnd indicamos directamente 0, como nuestra arma solo tiene un modo de fuego con esto no tenemos problema, pero si quisiéramos usar la misma línea para reproducir el sonido de acuerdo al modo de fuego activado podemos usar el atributo CurrentFireMode.

En este punto es válido aclarar una cosa, lo más aconsejable es separar la lógica de la reproducción del efecto en un método independiente donde además agreguemos algunas comparaciones como: if(Instigator != None), por ejemplo, para evitar errores en tiempo de ejecución. Con el objetivo de hacer estos tutoriales lo más claros y concisos posible este tipo de cosas las omitiré de vez en cuando, espero que tu si las tengas en cuenta para tu juego 😉

Compila, ejecuta y comienza a disparar… :-)

¿Qué nos va quedando?…

¿Te has fijado en el momento en el que el robot dispara su arma? Que dicho sea de paso, no se ve nada liviana. Ni se inmuta tan siquiera :-S ¿No crees que deba hacer algún movimiento por el retroceso del arma en el momento del disparo? Además, ¿No crees también, que se deba ver un pequeño efecto en la punta del cañón cuando se dispara el arma? Bien, estas y otras cosas las veremos en el siguiente tutorial 😉

Terminando por hoy…

Las clases que conforman nuestro juego van siendo cada vez más grandes, estoy buscando la manera de subirlas junto con el Mapa que estamos usando, para una referencia rápida. Por el momento, si las necesitas puedes escribir a mi correo que te las mando sin ningún problema.
Mi correo: nan2castillocoello@gmail.com

Si has seguido los tutoriales, en el pasado anuncié que para este veríamos una introducción a Actors, pero me pareció mejor armar a nuestro robot primero para que haya más acción ;-). Por tanto, el tema de Actors lo veremos después del siguiente tutorial ya que aún nos queda terminar de afinar algunos detalles en cuanto al disparo del arma de nuestro robot. También veremos que cosa son los states en la programación de juegos y cómo implementarlos en UnrealScript.

Puedes estar al tanto de los próximos tutoriales también siguiendo mi twitter: nan2cc

Hasta la próxima… chao 😉

Me gustaría mucho oír tus comentarios.

Nota: Este proyecto fue hecho con la versión del UDK de Abril de 2011 (UDKInstall-2011-04-BETA)

Introducción a Unreal Script – parte 2

En este tutorial continuamos con el proyecto que dejamos en el pasado tutorial. Ahora se comienzan a complicar un poco las cosas.

  • Configuraremos en el Pawn del player un grupo de propiedades para ajustar su comportamiento físico con el mundo y le agregaremos un mesh para que luzca mucho más cool :-).
  • Crearemos una clase para configurar la cámara principal del juego, creando un estilo de juego 3ra persona.
  • Jugaremos con las propiedades del Pawn para modificar su comportamiento físico

Comencemos!!

Agregándole un mesh al Player

Del proyecto que dejamos en el tutorial pasado, cuando ejecutábamos nuestro juego lo que teníamos era una especie de estilo de juego 1ra persona donde podíamos controlar al player pero no tenía ninguna forma. Vamos a cambiar esto dándole un cuerpo a nuestro personaje.

Evidentemente lo primero que tenemos que tener es el mesh que le vamos a aplicar al player. Para este ejemplo usaremos uno de los mesh que trae el UDK, vamos a darle un vistazo.

Abre el UDK Editor y localiza el Content Browser (View – Browser Windows – Content Browser) y en la sección Packages, en el campo para filtrar, escribe: CH_LIAM_Cathode. Al momento aparecerá este paquete en los resultados de la búsqueda, despliégalo y aparecerá un Mesh, lo seleccionas y veras el preview del SkeletalMesh SK_CH_LIAM_Cathode.

Da doble clic sobre el SK_CH_LIAM_Cathode y se abrirá el AnimSet Editor del UDK con este mesh. Puedes pasar unos instantes admirándolo (para acercar la cámara desplaza hacia arriba el mouse con el clic derecho presionado).

Bien, cierra el AnimSet Editor, ya que sabemos el nombre del mesh que vamos a usar y en que paquete está, vamos a indicárselo al Pawn de nuestro player. Abre el archivo SamplePawn.uc que dejamos en el proyecto pasado. Vamos a configurar uno de sus miembros, el Mesh.

Actualmente nuestro SamplePawn consta de una sola línea (class SamplePawn extends Pawn;) ahora vamos a agregarle un bloque defaultproperties que del tutorial pasado sabemos que es para darle valor por defecto a las variables miembro que queremos. En este caso lo que buscamos es agregarle a nuestro player el mesh que acabamos de ver. Pues para esto, lo que debemos es inicializar el objeto Mesh que tiene nuestro SamplePawn (lo hereda de Pawn) Este atributo es una instancia de SkeletalMeshComponent.

Agrega el siguiente bloque defaultproperties a SamplePawn (a continuación de: class SamplePawn extends Pawn;)

defaultproperties
{
	Begin Object Class=SkeletalMeshComponent Name=WPawnSkeletalMeshComponent
		SkeletalMesh=SkeletalMesh'CH_LIAM_Cathode.Mesh.SK_CH_LIAM_Cathode'
	End Object
	Mesh=WPawnSkeletalMeshComponent
	Components.Add(WPawnSkeletalMeshComponent)
}

Como ya hemos mencionado, la sintaxis para las inicializaciones en los bloques deafultproperties son distintas a la sintaxis normal de UnrealScript. En este caso lo que hacemos es crear una instancia de SkeletalMeshComponent, inicializar su miembro SkeletalMesh y asignársela al atributo Mesh de SamplePawn. Por último lo agregamos como componente del Pawn.

Bien, realmente con esto tenemos para lograr lo que buscamos, que es agregarle un mesh al player. Pero antes debemos configurar el atributo bDelayedStart de nuestro GameInfo. Este atributo es para demorar el inicio del juego, y es usado generalmente en juegos multiplayers, por lo que en nuestro caso no lo necesitamos.

Abre el SampleGame.uc y dentro del bloque defaultproperties agrega la siguiente línea:

bDelayedStart=false

Listo, hora de ver que hemos logrado con todo esto. Guardamos los cambios y abrimos el UDK Editor, nos preguntará, si queremos recompilar los scripts, le decimos que sí. Cuando termine cargamos el level que hicimos en el tutorial pasado y damos clic derecho – Play from Here.

Uuuufff :-( Que pasa??, sigo viendo lo mismo… Exacto, lo que pasa es lo siguiente, nuestro player ya tiene cuerpo, pero la cámara por defecto de nuestro juego es estilo 1ra persona, por lo que teóricamente estamos detrás de la cámara. Vamos a solucionar este problema configurando la cámara principal de nuestro juego para lograr un estilo 3ra persona.

Configurando la cámara principal del juego para tener un estilo 3ra persona:

Este método que usaremos para configurar la cámara no es el único, pero al hacerlo en una clase independiente nos permitirá tener toda la lógica de la cámara centralizada. Para esto, necesitamos crear una nueva clase, por suerte para nosotros el UDK nos brinda una clase llamada Camera que nos ayudará con nuestro objetivo. Si buscamos esta clase en el UnCodeX, veremos que como comentario dice: Camera: defines the Point of View of a player in world space. (UDKInstall-2011-04-BETA)(No es necesario acudir a Google Translate para saber qué significa esto no ?? :-D)

Para crear nuestra cámara agregaremos una nueva clase a nuestro SampleGame que herede de Camera. Dentro de la carpeta Classes del proyecto agrega el siguiente archivo: SamplePlayerCamera.uc y copia dentro el siguiente código.

class SamplePlayerCamera extends Camera;

var Vector CamOffset;
var float CameraZOffset;
var float CameraScale, CurrentCameraScale; /** multiplier to default camera distance */
var float CameraScaleMin, CameraScaleMax;

function UpdateViewTarget(out TViewTarget OutVT, float DeltaTime)
{
   local vector      HitLocation, HitNormal;
   local CameraActor   CamActor;
   local Pawn          TPawn;

   local vector CamStart, CamDirX, CamDirY, CamDirZ, CurrentCamOffset;
   local float DesiredCameraZOffset;

   // Don't update outgoing viewtarget during an interpolation
   if( PendingViewTarget.Target != None && OutVT == ViewTarget && BlendParams.bLockOutgoing )
   {
      return;
   }

   // Default FOV on viewtarget
   OutVT.POV.FOV = DefaultFOV;

   // Viewing through a camera actor.
   CamActor = CameraActor(OutVT.Target);
   if( CamActor != None )
   {
      CamActor.GetCameraView(DeltaTime, OutVT.POV);

      // Grab aspect ratio from the CameraActor.
      bConstrainAspectRatio   = bConstrainAspectRatio || CamActor.bConstrainAspectRatio;
      OutVT.AspectRatio      = CamActor.AspectRatio;

      // See if the CameraActor wants to override the PostProcess settings used.
      CamOverridePostProcessAlpha = CamActor.CamOverridePostProcessAlpha;
      CamPostProcessSettings = CamActor.CamOverridePostProcess;
   }
   else
   {
      TPawn = Pawn(OutVT.Target);
      // Give Pawn Viewtarget a chance to dictate the camera position.
      // If Pawn doesn't override the camera view, then we proceed with our own defaults
      if( TPawn == None || !TPawn.CalcCamera(DeltaTime, OutVT.POV.Location, OutVT.POV.Rotation, OutVT.POV.FOV) )
      {
         /**************************************
          * Calculate third-person perspective
          * Borrowed from UTPawn implementation
          **************************************/
         OutVT.POV.Rotation = PCOwner.Rotation;
         CamStart = TPawn.Location;
         CurrentCamOffset = CamOffset;

         DesiredCameraZOffset = 1.2 * TPawn.GetCollisionHeight() + TPawn.Mesh.Translation.Z;
         CameraZOffset = (DeltaTime < 0.2) ? DesiredCameraZOffset * 5 * DeltaTime + (1 - 5*DeltaTime) * CameraZOffset : DesiredCameraZOffset;

         CamStart.Z += CameraZOffset;
         GetAxes(OutVT.POV.Rotation, CamDirX, CamDirY, CamDirZ);
         CamDirX *= CurrentCameraScale;

         TPawn.FindSpot(Tpawn.GetCollisionExtent(),CamStart);
         if (CurrentCameraScale < CameraScale)          {             CurrentCameraScale = FMin(CameraScale, CurrentCameraScale + 5 * FMax(CameraScale - CurrentCameraScale, 0.3)*DeltaTime);          }          else if (CurrentCameraScale > CameraScale)
         {
            CurrentCameraScale = FMax(CameraScale, CurrentCameraScale - 5 * FMax(CameraScale - CurrentCameraScale, 0.3)*DeltaTime);
         }
         if (CamDirX.Z > TPawn.GetCollisionHeight())
         {
            CamDirX *= square(cos(OutVT.POV.Rotation.Pitch * 0.0000958738)); // 0.0000958738 = 2*PI/65536
         }
         OutVT.POV.Location = CamStart - CamDirX*CurrentCamOffset.X + CurrentCamOffset.Y*CamDirY + CurrentCamOffset.Z*CamDirZ;
         if (Trace(HitLocation, HitNormal, OutVT.POV.Location, CamStart, false, vect(12,12,12)) != None)
         {
            OutVT.POV.Location = HitLocation;
         }
      }
   }

   // Apply camera modifiers at the end (view shakes for example)
   ApplyCameraModifiers(DeltaTime, OutVT.POV);
}

defaultproperties
{
   CamOffset=(X=12.0,Y=0.0,Z=-13.0)
   CurrentCameraScale=1.0
   CameraScale=9.0
   CameraScaleMin=3.0
   CameraScaleMax=40.0
}

Tomado de: udn.epicgames.com

😮 ¡!! Como vez, para lograr la cámara que queremos no es un par de líneas solamente. El tema de cámaras en UnrealScript es bastante extenso por lo que será un tutorial completo de esta serie. Por el momento es suficiente con saber que básicamente lo que hacemos con esta clase, es sobrescribir el método UpdateViewTarget para desplazar la cámara del player un poco hacia atrás y hacia arriba logrando el estilo 3ra persona. UpdateViewTarget es llamada en cada frame de nuestro juego para actualizar la orientación y rotación de la cámara de acuerdo a la posición del Player.

Te repito, no te preocupes si no entiendes cada una de las líneas de esta clase, en el tutorial de cámaras de esta serie abarcaremos detalladamente este tema.

Solamente queda indicarle al PlayerController que use esta cámara. Abre la clase SamplePlayerController y a continuación de la línea: class SamplePlayerController extends PlayerController; agrega el siguiente bloque.

defaultproperties
{
   CameraClass=class'SampleGame.SamplePlayerCamera'
}

Ya a estas alturas, supongo que no tengas problemas con esto, no?? :-)

Listo!!!. Compila y prueba.

EEEHHH!! Ya el player tiene forma (lo que te debe dar una buena alegría) pero tenemos algunos problemas que resolver: primero, a pesar de tener una luz en la habitación, el mesh está muy oscuro, segundo, si nos tratamos de mover nos desplazamos pero sin mover ni una articulación :-S Bien…, vamos a arreglar esto.

Lo primero, es adjuntarle al Mesh del Player, además de su “esqueleto”, la animación de sus movimientos, para esto la clase SkeletalMeshComponent (de la que es instancia Mesh) tiene dos miembros AnimTreeTemplate y el array AnimSets. El primero (AnimTreeTemplate) debe hacer referencia a un AnimTree. Para nuestro ejemplo usaremos uno de los que trae el UDK en este caso CH_AnimHuman_Tree.AT_CH_Human. Puedes buscarlo en el Content Browser del UDK Editor como mismo hicimos para el esqueleto. Y el segundo es un arreglo donde podemos almacenar las distintas secuencias de animación del personaje. En este caso, usaremos CH_AnimHuman.Anims.K_AnimHuman_BaseMale, puedes buscar también ésta con el Content Browser, da doble clic y en la ventana Unreal AnimSet Editor, en el panel Browser de la izquierda, selecciona la pestaña Anim y verás un ListBox con todas las Secuencias de Animaciones para los distintos movimientos del personaje.

Por suerte para nosotros!!!, todo esto es trabajo de los diseñadores de nuestro equipo 😀 .

Entonces, en la clase SamplePawn agregamos a continuación de la línea SkeletalMesh=SkeletalMesh’CH_LIAM_Cathode.Mesh.SK_CH_LIAM_Cathode’, las siguientes líneas:

AnimTreeTemplate=AnimTree'CH_AnimHuman_Tree.AT_CH_Human'
AnimSets(0)=AnimSet'CH_AnimHuman.Anims.K_AnimHuman_BaseMale'

Aquí lo único nuevo que tenemos, en cuanto a sintaxis de UnrealScript es la inicialización de un index del arreglo AnimSets, nada raro, sencillamente entre paréntesis indicamos el index del array.

Listo!!!. Compila y prueba.

😀 Ahora sí tenemos a nuestro robot, moviéndose correctamente animado por nuestra habitación, pero aún nos queda un problemita…., la luz.

Para solucionar esto debemos agregar a nuestro Pawn una instancia de DynamicLightEnvironmentComponent, esta clase es usada para iluminar los componentes y/o los actores durante el juego.

Abre el archivo SamplePawn.uc, e inmediatamente después de la línea class SamplePawn extends Pawn; y antes del bloque defaultproperties agrega la siguiente línea:

var DynamicLightEnvironmentComponent LightEnvironment;

Con esta línea estamos agregando un nuevo miembro a SamplePawn de tipo DynamicLightEnvironmentComponent. En UnrealScript las variables de instancia de una clase son declaradas con la palabra clave var.

Bien, hecho esto ahora debemos inicializar este miembro y agregarlo como componente a nuestro Pawn. Dentro del bloque defaultproperties agrega el siguiente bloque (antes de: Begin Object Class=SkeletalMeshComponent Name=WPawnSkeletalMeshComponent)

Begin Object Class=DynamicLightEnvironmentComponent Name=MyLightEnvironment
      // Configuración de los atributos
End Object
LightEnvironment=MyLightEnvironment
Components.Add(MyLightEnvironment)

Bien, a esta altura tampoco te debe sorprender la sintaxis de aquí. Dentro del bloque Begin Object podemos poner la inicialización de los atributos de DynamicLightEnvironmentComponent, para lograr objetivos específicos con las luces. En futuros tutoriales ampliaremos el tema de las luces con UnrealScript, por el momento con los valores por defecto tenemos.

Ya configurado MyLightEnvironment debemos indicarlo en el LightEnvironment del SkeletalMeshComponent. Para esto dentro del bloque Begin Object Name=WPawnSkeletalMeshComponent agrega la siguiente línea:

LightEnvironment=MyLightEnvironment

Listo!!!. Compila y prueba.

Very cool ehh?? 😀

Jugando con las propiedades de Pawn:

Bien, ya tenemos un personaje para nuestro juego. Ahora, podemos jugar un poco con las propiedades de Pawn para modificar las características de nuestro robot, por ejemplo, si queremos modificar la velocidad máxima de desplazamiento de nuestro robot, podemos ajustar el valor del atributo GroundSpeed. Para esto, en la clase SamplePawn dentro del bloque defaultproperties agregamos la siguiente línea:

GroundSpeed=440.0

Si revisas con el UnCodeX la clase Pawn verás tooooodoooosss 😮 los atributos que podemos ajustar, te dejo a ti para que juegues un poco con ellos 😉 con los comentarios que traen es suficiente para tener una noción de lo que hacen.

Conclusión

Hasta aquí esta segunda parte (y final) de Introducción a UnrealScript. Con lo que hemos hecho hasta ahora ya estamos listos para jugar un poco con los atributos que tienen las distintas clases de las que hemos heredado.

En el próximo tutorial veremos una introducción a Actors (casi todo en el mundo UDK hereda de Actor). Veremos como nuestro robot puede interactuar con otros elementos del juego. Aprenderemos a compilar y ejecutar nuestro juego desde el Unreal FrontEnd, entre otras cosas.

Hasta entonces… chao!!! 😉

PD: Aquí te dejo el contenido de todas las clases de nuestro proyecto para una referencia rápida.

SampleGame.uc

class SampleGame extends GameInfo;

defaultproperties
{
   PlayerControllerClass=class'SampleGame.SamplePlayerController'
   DefaultPawnClass=class'SampleGame.SamplePawn'
   bDelayedStart=false
}

SamplePawn.uc

class SamplePawn extends Pawn;

var DynamicLightEnvironmentComponent LightEnvironment;

defaultproperties
{
	GroundSpeed=440.0

	Begin Object Class=DynamicLightEnvironmentComponent Name=MyLightEnvironment
		//...
	End Object
	Components.Add(MyLightEnvironment)
	LightEnvironment=MyLightEnvironment
   
	Begin Object Class=SkeletalMeshComponent Name=WPawnSkeletalMeshComponent
		SkeletalMesh=SkeletalMesh'CH_LIAM_Cathode.Mesh.SK_CH_LIAM_Cathode'
		AnimTreeTemplate=AnimTree'CH_AnimHuman_Tree.AT_CH_Human'
		AnimSets(0)=AnimSet'CH_AnimHuman.Anims.K_AnimHuman_BaseMale'
		LightEnvironment=MyLightEnvironment
	End Object
	Mesh=WPawnSkeletalMeshComponent
	Components.Add(WPawnSkeletalMeshComponent)
} 

SamplePlayerCamera.uc

class SamplePlayerCamera extends Camera;

var Vector CamOffset;
var float CameraZOffset;
var float CameraScale, CurrentCameraScale; /** multiplier to default camera distance */
var float CameraScaleMin, CameraScaleMax;

function UpdateViewTarget(out TViewTarget OutVT, float DeltaTime)
{
   local vector      HitLocation, HitNormal;
   local CameraActor   CamActor;
   local Pawn          TPawn;
   
   local vector CamStart, CamDirX, CamDirY, CamDirZ, CurrentCamOffset;
   local float DesiredCameraZOffset;

   // Don't update outgoing viewtarget during an interpolation 
   if( PendingViewTarget.Target != None && OutVT == ViewTarget && BlendParams.bLockOutgoing )
   {
      return;
   }

   // Default FOV on viewtarget
   OutVT.POV.FOV = DefaultFOV;

   // Viewing through a camera actor.
   CamActor = CameraActor(OutVT.Target);
   if( CamActor != None )
   {
      CamActor.GetCameraView(DeltaTime, OutVT.POV);

      // Grab aspect ratio from the CameraActor.
      bConstrainAspectRatio   = bConstrainAspectRatio || CamActor.bConstrainAspectRatio;
      OutVT.AspectRatio      = CamActor.AspectRatio;

      // See if the CameraActor wants to override the PostProcess settings used.
      CamOverridePostProcessAlpha = CamActor.CamOverridePostProcessAlpha;
      CamPostProcessSettings = CamActor.CamOverridePostProcess;
   }
   else
   {
      TPawn = Pawn(OutVT.Target);
      // Give Pawn Viewtarget a chance to dictate the camera position.
      // If Pawn doesn't override the camera view, then we proceed with our own defaults
      if( TPawn == None || !TPawn.CalcCamera(DeltaTime, OutVT.POV.Location, OutVT.POV.Rotation, OutVT.POV.FOV) )
      {   
         /**************************************
          * Calculate third-person perspective
          * Borrowed from UTPawn implementation
          **************************************/
         OutVT.POV.Rotation = PCOwner.Rotation;                                                   
         CamStart = TPawn.Location;
         CurrentCamOffset = CamOffset;
         
         DesiredCameraZOffset = 1.2 * TPawn.GetCollisionHeight() + TPawn.Mesh.Translation.Z;
         CameraZOffset = (DeltaTime < 0.2) ? DesiredCameraZOffset * 5 * DeltaTime + (1 - 5*DeltaTime) * CameraZOffset : DesiredCameraZOffset;
         
         CamStart.Z += CameraZOffset;
         GetAxes(OutVT.POV.Rotation, CamDirX, CamDirY, CamDirZ);
         CamDirX *= CurrentCameraScale;
      
         TPawn.FindSpot(Tpawn.GetCollisionExtent(),CamStart);
         if (CurrentCameraScale < CameraScale)
         {
            CurrentCameraScale = FMin(CameraScale, CurrentCameraScale + 5 * FMax(CameraScale - CurrentCameraScale, 0.3)*DeltaTime);
         }
         else if (CurrentCameraScale > CameraScale)
         {
            CurrentCameraScale = FMax(CameraScale, CurrentCameraScale - 5 * FMax(CameraScale - CurrentCameraScale, 0.3)*DeltaTime);
         }                              
         if (CamDirX.Z > TPawn.GetCollisionHeight())
         {
            CamDirX *= square(cos(OutVT.POV.Rotation.Pitch * 0.0000958738)); // 0.0000958738 = 2*PI/65536
         }
         OutVT.POV.Location = CamStart - CamDirX*CurrentCamOffset.X + CurrentCamOffset.Y*CamDirY + CurrentCamOffset.Z*CamDirZ;
         if (Trace(HitLocation, HitNormal, OutVT.POV.Location, CamStart, false, vect(12,12,12)) != None)
         {
            OutVT.POV.Location = HitLocation;
         }
      }
   }

   // Apply camera modifiers at the end (view shakes for example)
   ApplyCameraModifiers(DeltaTime, OutVT.POV);
}

defaultproperties
{
   CamOffset=(X=12.0,Y=0.0,Z=-13.0)
   CurrentCameraScale=1.0
   CameraScale=9.0
   CameraScaleMin=3.0
   CameraScaleMax=40.0
}

SamplePlayerController.uc

class SamplePlayerController extends PlayerController;

defaultproperties
{
   CameraClass=class'SampleGame.SamplePlayerCamera'
}

Nota: Este proyecto fue hecho con la versión del UDK de Abril de 2011 (UDKInstall-2011-04-BETA)

Introducción a UnrealScript – parte 1

Este es el primer tutorial de una serie de tutoriales relacionados con UnrealScript, el lenguaje de programación del MAGNIFÍCO motor de juegos Unreal Engine 3.

En este primer tutorial (dividido en dos partes) veremos:

  • Cómo instalar y configurar el UnCodeX para poder revisar todas las clases del UDK organizadas jerárquicamente
  • Cómo preparar un nuevo proyecto para comenzar a desarrollar nuestro juego
  • Crearemos las clases necesarias para permitirle al jugador controlar el personaje del juego mediante el teclado y el mouse.
  • Configuraremos las clases del player para indicarle un mesh y propiedades físicas que le permitan interactuar con el mundo de forma correcta.
  • Crearemos las clases necesarias para configurar la cámara principal de nuestro juego, creando un estilo de juego 3ra persona.

Instalando UnCodeX

Antes de comenzar a preparar el proyecto, vamos a instalar y configurar UnCodeX, una excelente aplicación que nos servirá para explorar de forma mucho más cómoda, toda la jerarquía de clases del UDK. Para instalar y configurar el UnCodeX , primero lo descargamos desde aquí.

Una vez descargado he instalado vamos a Tree -> Settings en su menú principal. Seleccionamos Source Paths del panel de la izquierda y agregamos la ruta:

Ruta_hacia_la instalación_del_UDKDevelopmentSrc

Damos OK, y de nuevo en el menú principal seleccionamos Tree -> Rebuild and Analyse

Listo!!!. Ya tenemos todas las clases del UDK organizadas jerárquicamente.

Ya instalado y listo el UnCodeX vamos a comenzar con nuestro proyecto. Lo primero que vamos hacer es preparar el directorio donde estarán los scripts de nuestro juego.

Nota: Tener en cuenta que Unreal Engine 3 está diseñado para trabajar con un solo proyecto a la vez.

Configurando el directorio del proyecto

Para crear nuestro primer proyecto en UnrealScript vamos a movernos hasta la raíz de la instalación del UDK, una vez aquí localizamos la carpeta DevelopmentSrc. Dentro de esta carpeta creamos una carpeta con el nombre que le querramos dar a nuestro proyecto. Para este ejemplo la nombraremos SampleGame.

Dentro de esta carpeta SampleGame creamos otra carpeta y la nombramos: Classes

Ya estamos listos para comenzar a chocar con las primeras líneas en UnrealScript. 😉

UDK tiene toda una biblioteca de clases que nos servirán para heredar las funcionalidades “básicas” para cada elemento del juego. Conociendo esto vamos a crear las primeras clases para nuestro juego, las clases que representarán al Player.

El Player es controlado por dos elementos: PlayerController y Pawn.

PlayerController

PlayerController es el responsable de determinar como el jugador controla el personaje mediante sus dispositivos de entrada (teclado, mouse o lo que sea)

Si vamos al UnCodeX y buscamos la clase Controller, veremos que de ella heredan dos clases AIController y PlayerController. Para nuestro ejemplo, por el momento, es suficiente con crear una clase que herede de PlayerController. A grandes rasgos con heredar de esta clase tendremos lo necesario para lograr que el jugador pueda controlar los movimientos del personaje usando el teclado y el mouse.

Sabiendo esto, vamos a crear nuestra primera clase UnrealScript, muy sencilla, pero por algo se empieza no? :-)

Dentro de la carpeta Classes que creamos para nuestro juego vamos a crear el archivo SamplePlayerController.uc Lo editamos con cualquier editor de texto (en futuros tutoriales veremos como configurar Visual Studio y nFringe, para tener un IDE más potente para nuestro trabajo en UnrealScript, para este ejemplo con el notepad de Windows tenemos) y le agregamos la siguiente línea

class SamplePlayerController extends PlayerController;

Bien, nuestra primera línea en UnrealScript ;-). Lo que hemos hecho aquí es crear una nueva clase llamada SamplePlayerController que hereda de la clase del UDK PlayerController. Como mencionamos anteriormente, con heredar de PlayerController es suficiente para tener lo básico para el player.

Ya teniendo listo el PlayerController para nuestro personaje vamos a pasar a Pawn.

Pawn

Mientras PlayerController es la encargada de determinar cómo las entradas (mediante el teclado, mouse o cualquier otro sistema para comunicarse con el juego) del jugador son usadas para controlar el personaje principal. La representación visual del personaje y la lógica para determinar cómo interactuar con el mundo físico es encapsulada en Pawn.

Vamos a hacer el Pawn que controlará a nuestro personaje.

Dentro de la carpeta Classes de nuestro proyecto creamos un nuevo archivo llamado SamplePawn.uc y le agregamos la siguiente línea

class SamplePawn extends Pawn;

Como vemos, lo que hacemos aquí es muy parecido a lo que hacemos para crear nuestro PlayerController, simplemente heredamos de una clase base del UDK que ya tiene implementado el principio básico de todo Pawn. Por el momento con esto tenemos.

GameInfo

Ahora sólo nos queda crear el GameInfo de nuestro juego. Esta clase, a grandes rasgos, es la que determina las reglas del juego, las condiciones bajo las cuales el juego avanza o se termina, entre otras muchas cosas que iremos viendo poco a poco. Además GameInfo es la encargada de decirle al motor cuales clases debe usar como PlayerController, Pawn.

Sabiendo esto, entonces vamos a crear el GameInfo

En la carpeta Classes del proyecto creamos un nuevo archivo llamado SampleGame.uc y le agregamos las siguiente líneas

 class SampleGame extends GameInfo;

 defaultproperties{
 	PlayerControllerClass=class'SampleGame.SamplePlayerController'
 	DefaultPawnClass=class'SampleGame.SamplePawn'
 }

Bien aquí ya tenemos nuevas cosas, la primera línea a esta altura no nos debe asustar sencillamente heredamos de la clase GameInfo que nos brinda el UDK para facilitarnos un poco las cosas. Pero en este caso tenemos que inicializar dos variables miembro de esta clase que son PlayerControllerClass y DefaultPawnClass.

En UnrealScript el bloque defaultproperties dentro de una clase se usa para especificar valores por defectos a variables miembros de dicha clase. En este bloque la sintaxis de las asignaciones es ligeramente distinta al estándar del UnrealScript. La sintaxis para inicializar objetos (como es el caso) es la siguiente:

ObjectProp=ObjectClass’ObjectName’

Listo!!! Ya tenemos preparada todas las clases necesarias para nuestro primer ejemplo.

Compilando…

Antes de compilar necesitamos informarle al motor de la existencia de un nuevo proyecto. Para hacerlo vamos al archivo DefaultEngine.ini que se encuentra en Raiz_del_UDKUDKGameConfig, abrirlo con cualquier editor de texto y buscar la línea [UnrealEd.EditorEngine] inmediatamente debajo de esta línea habrán varias líneas que comienzan con +EditPackages .. . Pues aquí es donde tenemos que indicar la existencia de nuestro proyecto. Al final de este grupo agregamos nuestro proyecto, en nuestro caso, quedaría el bloque completo [UnrealEd.EditorEngine] de la siguiente forma:

 [UnrealEd.EditorEngine]
 +EditPackages=UTGame
 +EditPackages=UTGameContent
 +EditPackages=UDNGame
 +EditPackages=SampleGame

Listo, ahora si podemos compilar. Para compilar los scripts, podemos usar el UnrealFrontend o abrir el editor. En próximos tutoriales hablaremos del UnrealFrontend, pero en este caso lo haremos mediante el UDK Editor.

Abrimos el UDK Editor y nos saldrá un ventana preguntándonos: si queremos recompilar los scripts, le decimos que sí y esperamos que termine. Finalmente nos debe salir que todo el proceso terminó sin problemas

Creando un nivel sencillo para nuestro juego

Ya compilados los scripts y abierto el UDK Editor, vamos a crear un muy simple nivel para probar nuestro juego. Antes quiero aclarar que el diseño de niveles en UDK Editor es toda una profesión, pero evidentemente ese es el trabajo de los diseñadores de nuestro equipo 😉 En el caso que quieras aprender sobre este tema, te recomiendo libros como Sams.Mastering.Unreal.Technology Volumen I y II (el Volumen III que era de UnrealScript por desgracia fue cancelado). Por suerte, información para aprender a diseñar niveles con UDK Editor si hay bastante, con un poco de Google tendrás ;-).

Para nuestro ejemplo crearemos un nivel muy simple. O si prefieres, puedes cargar cualquiera de los mapas que trae el UDK de ejemplo.

Para crear una sencilla habitación donde probar lo que tenemos hasta ahora, con un Proyecto nuevo en el UDK Editor damos clic derecho sobre el Cubo en el panel de herramientas (en la izquierda del editor) En los valores de X, Y, Z de la ventana Brush Builder – Cube que se abre indicamos 1024, 1024 y 512 respectivamente. Damos Build y Close.

Seguidamente hacemos clic sobre el botón CSG Add del mismo panel de herramientas en la sección CSG. Se crea un cubo con las dimensiones indicadas.

Ahora volvemos a dar clic derecho sobre el Cube en el panel de herramientas y en los valores de X, Y, Z indicamos 1000, 1000 y 500 respectivamente. Build y Close.

A continuación hacemos clic sobre el botón CSG Substract de la sección CSG en el panel de herramientas. Con esto sustraemos del cubo grande prácticamente todo su interior dejando solo una pequeña pared que lo hace una habitación.

Ahora nos desplazamos en el ViewPort Perspectiva hacia dentro de esta habitación (podemos usar la rueda del mouse para movernos hacia adelante y hacia atrás en el ViewPort). Una vez dentro de la habitación creamos una luz. Para esto mantenemos presionada la tecla L y hacemos clic en el centro de la habitación. Ajustamos la posición de la luz hacia el techo de la habitación y listo.

Ya tenemos nuestro nivel diseñador, extremadamente sencillo pero perfecto para probar nuestro proyecto 😉

Ejecutando nuestro primer juego con UnrealScript

Bien, ya tenemos nuestro nivel creado, en este punto tenemos dos métodos para ejecutar nuestro proyecto sobre ese nivel.

1 – Indicar nuestro SampleGame en el Game Type For PIE del World Info.

2 – Indicar nuestro SampleGame como default game por defecto para el motor en el DefaultGame.ini.

Para este ejemplo usaremos la primera variante, en otros tutoriales veremos la segunda.

Ya con el UDK Editor abierto en el nivel que acabamos de diseñar y los scripts compilados, vamos a View en el menú principal del UDK Editor y seleccionamos World Properties. En la ventana que se nos abre desplegamos la sección World Info nos movemos hasta la propiedad Game Type For PIE y del combo box de su derecha seleccionamos nuestro proyecto (SampleGame). Cerramos y listo.

En el menú principal del UDK Editor seleccionamos Build – Build All. Cuando termine la compilación en cualquiera de los viewport seleccionamos clic derecho Play from Here.

BOOOM!!! 😀 Ya tenemos corriendo nuestro primer proyecto. Como es lógico lo logrado no es muy llamativo, pero independientemente de esto…., es bastante excitante, no es así? 😉

En este punto, tenemos un estilo de juego 1ra persona, con un Player sin Mesh, ni afectado por la gravedad (mira hacia arriba y avanza y veras a lo que me refiero :-) ), pero que se mueve por todo el nivel reaccionando perfectamente a las teclas que presione el jugador o a como mueva el mouse.

Para el próximo tutorial

Aquí terminamos esta introducción a UnrealScript. En el próximo tutorial seguiremos trabajando con este proyecto, vamos a agregarle un mesh al player para darle forma, vamos a agregarle propiedades físicas, vamos a agregar una cámara a nuestro juego para crearlo estilo 3ra persona, entre otras cosas.

Hasta entonces. . . 😉

Nota: Este proyecto fue hecho con la versión del UDK de Abril de 2011 (UDKInstall-2011-04-BETA)