Vamos a crear un arma nueva para el Unreal Tournament, utilizando recursos extraídos de un arma del Mod Tactical Ops, (Juego que por supuesto utiliza el motor Unreal ya que es un Mod que se instala sobre el Unreal Tournament), y tiene como característica, que el juego resultante es una cosa parecida al Counter Strike, (cosa que odio), pero los modelos de armas y la calidad de las texturas es magnífica.
Aclaración: No vamos a fabricar un Arma desde 0, eso llevaría todo un foro completo de tutorial y no tendría sentido. Lo que si vamos a hacer, es acortar camino utilizando recursos y conocimientos puntuales, para que puedas fabricarte una buena arma que nadie más poseé y que en sí misma, será completamente nueva y única.
Definición de lo que vamos a hacer:
Vamos a crear un arma nueva para el Unreal Tournament, utilizando recursos extraídos de un arma del Mod Tactical Ops, (Juego que por supuesto utiliza el motor Unreal ya que es un Mod que se instala sobre el Unreal Tournament), y tiene como característica, que el juego resultante es una cosa parecida al Counter Strike, (cosa que odio), pero los modelos de armas y la calidad de las texturas es magnífica.
¿Qué necesito?:
Primero tenés que conseguirte el Mod Tactical ops que pesa unos 200 Megas, bajártelo e instalarlo en cualquier disco. El instalador del Mod te va a advertir que necesita una instalación de Unreal Tournament con Patch 436 lista, pero no le hagas caso y dale Aceptar igual, que así de todos modos instalará las carpetas en donde se encuentra el archivo que nosotros necesitamos para "extraer" recursos.
El archivo que necesitamos se llama: TOModels.u, y si al Mod completo lo instalás en D:\ por ejemplo, la ruta donde podremos encontrar ese archivo instalado será algo así: D:\tacticalops\TacticalOps\System
Mapa de pasos a seguir desde aquí en adelante:
Nota: todo los programas herramientas que se nombran aquí como el UTPT, el UMake, el 3DS, se encuentran en descarga en la faccion UT con el nombre "herramientas de edicion de armas".
1º_ Vamos a extraer un modelo de Arma desde el archivo del Tactical ops, sus texturas correspondientes, sonido de disparo, etc. Todo esto lo haremos con el UTPT.
2º_ Como el UTPT no extrae correctamente los modelos que contiene un paquete, con el 3DS deberemos procesar los archivos extraídos, y así finalmente tendremos los archivos necesarios para seguir adelante.
3º_ Crearemos un proyecto de Unreal Tournament que iremos compilando hasta lograr el resultado final que es un arma utilizable en juego.
___________________________________________________________________________
Paso previo a cualqueir otra cosa, vamos a crear nuestro directorio de proyecto del Arma para Unreal Tournament, y ese directorio o carpeta debe ser creado en el directorio de una instalación de Unreal Tournament. Por ejemplo yo tengo mi UT instalado en "C:\Unrealtournament", y en ese directorio voy a crear el proyecto: "FAL_AK47_V1". Entonces creo una carpeta llamada "FAL_AK47_V1" en la instalación de Unreal Tournament y quedaría así:
"C:\Unrealtournament\FAL_AK47_V1".
Teniendo ya creado el directorio o carpeta de proyecto, en el interior de esa carpeta tengo que crear las carpetas de recursos, (en las cuales el compilador buscará los recursos para compilar el proyecto). Las carpetas que debo crear son la siguientes:
Classes
Sounds
Models
Textures
Me quedaría así:
Antes de seguir adelante, es necesario configurar el 3DS. Así que el contenido completo de la carpeta 3DS que esta al final de este post, colóquenla en un disco como por ejemplo "C:\"
Debería quedar así:
"C:\3DS"
Y en el interior de esa carpeta debe verse así:
Noten que se incluye un archivo de texto donde se explica cada comando para poder procesar archivos, así que léanlo.
Para configurar el 3DS, debemos indicarle a este programita DOS, la raíz de directorio propia, o sea, le decimos donde deberá trabajar. Para eso abrimos una ventana DOS desde Windows XP: Inicio > Todos los programas > Accesorios > Símbolo del sistema
Si lo hiciste bien te aparece la ventana de DOS:
Cambiamos el directorio de la ventana DOS a "C:\3DS", escribimos esto y damos Enter:
Escriben como se ve en la ventana anterior y dan Enter, entonces el directorio cambia y debería quedar así:
Escriben el comando para setear el directorio y le dan Enter:
Les aparece este diálogo de directorios, indican por supuesto el directorio donde pusieron el 3DS y dan Enter:
Una vez que Aceptan, queda seteado el directorio 3DS y quedamos listos para trabajar con 3DS:
Dejaron el programa 3DS listo para recibir y procesar archivos extraídos con el programa UTPT. Archivos que serán extraídos del archivo TOModels.u
Vamos a comenzar por usar el UTPT para abrir el package TOModels.u
Una vez ejecutado el UTPT, vamos al menú "File" --- > "Open" y en diálogo de archivo buscamos en donde está el archivo TOModels.u.
Lo cargamos y aparecerá en el UTPT, una lista de todo lo que tiene ese package adentro. Vean que la solapa seleccionada abajo es la de "Export Tree", la marqué con Azul así no se marean.
Uno de los tipos de exportación es el "SkeletalMesh", que es una malla que podemos exportar y es el "cuerpo" del modelo. Click con botón derecho del Mouse y "View", para ver la malla:
Ahora con el UTPT, vamos a exportar esa malla "SkeletalMesh", en un tipo de archivo que sirve para ser utilizado en softwares de modelado 3D, como por ejemplo 3D Studio Max. No es que vayamos a trabajar con 3D Studio Max, no es este caso, pero es necesario extraer los archivos en ese formato, para poder procesarlos con el 3DS, nuestra herramienta que permitirá tener unos archivos finales procesables, compilables sin errores. Es más, el UTPT te ofrece la opción de exportarlos directamete a formato del Unreal Tournament, pero como dije, los exporta mal en ese formato lamentablemente.
Simplemente vean la imagen y se van a dar cuenta de que deben hacer lo mismo que con "View Mesh", solo que esta vez indican "Extract Mesh", y que sea en formato "As 3D Studio":
Les va a aparecer una ventanita con opciones, pero en este caso no es necesario modificar nada, tan solo delen "OK"
Y cuando les pide guardar el archivo que están extrayendo, lo guardan en el directorio del programa 3DS que ya tenemos configurado desde el paso anterior. Miren la imagen:
Una vez guardado, modifican el nombre del archivo guardado, para quitarle un adornito inncesesario que el UTPT le agrega a los nombres de archivos que extrae:
Extrajeron una malla desde el archivo TOModels.u con UTPT en formato 3D Studio, y lo guardaron en el directorio donde tienen preparado el programa 3DS, en este caso, en la dirección:
"C:\3DS"
Ojo, no lo pongan en una carpeta de las que están adentro del directorio C:\3DS, sino directamente en el directorio C:\3DS.
Podrían preguntarme:
¿Porqué no podemos usar UTPT para extraer una malla directamente en formato Unreal 3D y así evitarnos el engorroso proceso extra con el 3DS?.
Respuesta:
Deberían preguntarle al autor del UTPT el porqué de que su programa extrae las mallas Unreal Tournament deformadas en formato Unreal 3D. Yo ya revisé la info, busque actualizaciones que tengan solucionado ese defecto, pero no existe nada la menos hasta hoy. No conozco otro Software que lo haga bien.
Ok, usemos el 3DS así ahorramos problemas, total es muy poco el trabajo extra que hay que hacer y las mallas quedan perfectas. Ahora bien, entonces cuando terminemos de procesar las mallas extraídas, y todo lo que deba ser extraído y procesado .... ¿Podremos compilar y tener nuestra arma lista?
Respuesta:
No, toda esta parte del trabajo solo nos dará un resultado que es tener la "carcasa" vacía del arma (como un auto sin motor y sin instalación eléctrica), pero nos faltará aún lo más importante y complejo: "darle vida". Para eso tendremos que construirle todo el script necesario, funciones, classes, variables, coordenadas, etc, etc.
Y por cierto, hasta el paso Nº 4 lo que hicimos fue preparar el directorio del proyecto, con todas sus carpetas. Luego preparamos el software en 3DS y también lo dejamos listo para trabajar. Finalmente usamos el UTPT para extraer una malla del archivo TOModels.u y guardarla en el directorio del 3DS para poder procesarla.
Pues bien, lo siguiente que haremos ahora en el paso Nº 6 es procesar esa malla y así ya podremos guardarla como recurso terminado, en la carpeta correspondiente de nuestro proyecto: FAL_AK47_V1, más precisamente en la carpeta Models.
Muy bien, ya tenemos la malla del AKA47.3DS extraída desde el archivo TOModels.u con el UTPT, pero la malla está deformada, (si no me creen compruébenlo), por lo tanto durante la extracción, la guardamos en el directorio del 3DS para poder procesarla con el mismo.
Ahora viene lo bueno, vamos a procesarla y guardarla en el directorio del proyecto FAL_AK47_V1, así vamos cerrando uno de los cientos de pasos que tenemos que dar todabía.
En Windows XP, vamos a Inicio > Todos los programas > Accesorios > Símbolo del sistema y abrimos una ventana DOS. Cambiamos el directorio al directorio del programa 3DS, en este caso: "C:\3DS". (Esto está explicado detalladamente en el paso Nº 4).
Una vez abierta la ventana DOS, y cambiado el directorio al directorio del programa 3DS, escribimos los siguiente y le damos Enter:
"C:\3DS\3ds2unr AK47 AK47.3DS" (Donde 3ds2unr es el encabezado del comando, AK47 es el nombre que tendrá el archivo nuevo en formato Unreal 3D, AK47.3DS es el archivo que será procesado, y que teníamos guardado listo para procesar).
Si no hicimos nada mal, la ventana quedará así informando que el archivo fue procesado, y que está listo para ir a buscarlo. Si es así, cerramos la ventana DOS y respiramos profundamente.
Entonces el 3DS acaba de procesar el archivo, y lo guardó en la carpeta Models en el directorio del programa 3DS. Miremos y veamos si es así:
Si, esos dos archivos que ven ahí adentro son los archivos en formato Unreal 3D que necesitábamos, copiémoslos y llevémoslos a la carpeta Models pero de nuestro poryecto FAL_AK47_V1\Models. Ahora nuestra carpeta Models en el directorio de nuestro proyecto FAL_AK47_V1 quedó asi:
Ahora cierren el 3DS y olvídenlo por un rato, porque vamos a volver al UTPT para ir en busca de las texturas.
En este paso, finalmente procesamos el archivo AK47.3DS que habíamos extraído con UTPT desde el archivo TOModels.u, y como resultado obtubimos 2 archivos en formato Unreal 3D, listos para ser agregados al proyecto en la carpeta Models del mismo.
En este paso, y ahora que ya tenemos procesada y guardada la malla del AK47 en nuestra carpeta Models del proyecto FAL_AK47_V1, vamos a ir en busca de las texturas o "Skins" del AK47.
Abrimos de nuevo el UTPT, cargamos el archivo TOModels.u, y en la misma lista del "Export Tree", en la sección "Skins", buscamos el nombre AK47 pero esta vez del tipo de exportación "Texture".
Con el botón derecho del mouse abrimos el menú y elegimos "View Texture" para que la textura se cargue en el visor. Ven que son 3 texturas:
ak47tex0
ak47tex1
ak47tex2
Una vez que vimos las texturas, ya podemos extraerlas haciendo click con el botón derechodel mouse, pero esta vez usamos "Extract as image":
Nos preguntará como guardarla y elegimos "First Map as PCX", donde .PCX es la extensión de archivo de imagen que le gusta al motor Unreal:
Y aca atención: vamos a guardar las imágenes en el directorio "Textures" de nuestro proyecto FAL_AK47_V1, y el UTPT de nuevo nos va a poner adornitos innecesarios en el nombre de archivo. Al guardar las imágenes, van a tener que modificar los nombres de los archivos a los nombres simples como aparecen originalmente en la lista de Skins, y agregarle a mano la extensión ."PCX". Asegúrense de que guardan la imagen como se ve en el ejemplo:
Luego entren al directorio Textures de nuestro proyecto y van a ver que de todos modos el UTPT le agregó adornos a los nombres y en vez de quedar por ejemplo:
ak47tex0.PCX nos quedó ak47tex0_0.PCX
Asegúrense, (modificando a mano), de que quede como ak47tex0.PCX, ak47tex1.PCX y ak47tex2.PCX. Hacer esto servirá para simplificar la modificación de código en los scripts más adelante en el proyecto. Observen como quedaron finalmente los nombres de imagen guardados en la carpeta Textures del proyecto, luego de que simplifiqué los nombres:
Extrajimos las texturas necesarias para la malla del ak47 y las guardamos en el directorio Textures del proyecto FAL_AK47_V1.
Aca hacemos una nueva detención para hacer algunas consideraciones importantes.
Ya tenemos en nuestro proyecto lo siguiente:
En la carpeta Models: las mallas en formato Unreal 3D
En la carpeta Textures: las trés imágenes .PCX para "texturizar" el ak47
En la carpeta Classes: nada
En la carpeta Sounds: nada
NOTA: Esto es solo para el arma en 1º persona, luego repetiremos procesos para agregar el arma en 3º persona más el PickUp, que son cosas separadas en Unreal Tournament.
Ahora vamos a ocuparnos de hablar de los sonidos, que iran en la carpeta Sounds:
Es importante saber que pueden poner el sonido que más les guste, tan solo teniendo en cuenta que el motor Unreal solo acepta archivos de sonido en formato .wav a 22050 KHZ y en formato mono de 8 bits. Si intentan poner cualquier otra cosa, el motor "la escupe".
Sabiendo esto, pueden utilizar cualquier archivo. wav en el formato explicado, que sea el sonido de un disparo o algo parecido, que pueden conseguir en cualquier parte y ponerlo en la carpeta Sounds del proyecto FAL_AK47_V1.
Ahora el asunto Classes:
Las classes son archivos de texto con extensión "uc", que contienen todo el código de script para darle vida a las cosas en el Unreal Tournament, y van en la carpeta Classes del proyecto. En este tutorial en especial, para evitar extenderme infinitamente, voy a entregar las classes prefabricadas para que puedan incluirlas, y me limitaré a explicar las cosas que modifiqué para que funcione. No voy a explicar cada función, cada declaración, cada variable, porque eso llevaría un manual de 560 páginas. Y así nadie terminaría de hacer nada con este tutorial.
Con esto, van a obtener un arma funcional y van a conocer puntos clave del Script para poder ustedes mismos de ahí en más, hacerse sus propias armas.
Por último aclaro algo importantísimo:
Si el directorio del proyecto no contiene las carpetas tal cual como deben llamarse, (Classes, Models, Textures, Sounds), cuando intenten usar el UMake para compliar el proyecto se les vas a hacer imposible, con miles de mensajes de error y hasta la imposibilidad total de compilar nada.
¿Ahora qué sigue?
Tenemos una malla y texturas para un arma ak47, pero bien, todo eso sirve para el arma en vista de primera persona. Ahora falta "construir" la misma arma ak47 pero será la que veamos en el piso como pickup, o en manos de otros jugadores o bots, (3º persona). Ya además tenemos que construir la caja de municiones para el ak47 y que los players y bots puedan agarrarla cuando se quedan sin municiones.
Esto vamos a comenzar a verlo en el siguiente paso, el Nº 9.
Ya tenemos los recursos necesarios para armar el AK47 que se verá en 1º persona.
Muy bien, ahora necesitamos los recursos para construir el ak47 que se verá en 3º persona, o sea tirado en el piso como PickUp, o en manos de otros players o bots durante un juego.
Para eso, repetiremos exactamente los mismos pasos que llevamos a cabo para la malla y texturas del ak47 de 1º Persona, tanto en el uso del 3DS como del UTPT. y los archivos obtenidos serán guardados en las carpetas del proyecto FAL_AK47_V1 de la misma manera también.
Primero consigamos la malla del ak47 para vista en 3º persona:
Cuando vayan a extraer esta malla del tipo "LoadMesh", aparecerá una ventana preguntando ciertas cosas. Asegúrense de dejarla así antes de extraer la malla: Desseleccionen eso de "In separate Files"
Y luego la textura para esa malla:
Recuerden, repitan los mismos pasos que con los recursos que habíamos extraído para el AK47 de vista en 1º persona, usando el UTPT y luego procesando la malla con el 3DS. Recuerden lo de simplificar los nombres de archivos tanto mallas como texturas al guardarlos desde el UTPT, que le pone adornos innecesarios.
Si se hizo todo bien, ahora las carpetas del proyecto FAL_AK47_V1 Models y Textures se ven así:
Por cierto
Faltan tres archivos .PCX o texturas que deberán ser colocados en la carpeta Textures del proyecto, y son:
La textura para la mirá telescópica o "Crosshair"
La textura para los brasos en 1º persona
La textura para la caja de municiones
Aca se las dejo a las 3 en este post. Por supuesto, estas texturas igual que todas las otras pueden editarse para darle el toque del clan propio. Ojo que las texturas deben ser en formato de 8 bits de color y en medidas en pixeles de 64, 128, 256, 512, 1024, etc etc, sino el motor Unreal se "enoja".
Para que se pueda tener una idea de qué es lo que estamos haciendo y por qué estamos rompiéndonos el coco haciendo esto, voy adelantando el resultado final del trabajo: (Si se leyó bien este tutorial, el resultante debería ser esto):
FAL_AK47_V1
Primera persona:
Y dos vistas de 3º persona:
Así que a seguir trabajando que como se ve, el resultado vale la pena.
En este paso del tutorial (Nº 11), vamos a poner el archivo de sonido de disparo en la carpeta correspondiente del proyecto FAL_AK47_V1. En este mismo post, dejo el archivo.wav para que puedan bajarlo y usarlo. Por supuesto, deben colocar este archivo en la carpeta Sounds del proyecto.
También en este post les dejo los archivos de Clases.uc, para que los coloquen en la carpeta Classes del proyecto. Estos archivos no fueron extraídos con el UTPT pues el archivo TOModels.u que estamos usando para extraer recursos, no contiene lo que necesitamos.
Nota: tanto el archivo de sonido como los archivos de Clase, están dentro del mismo archivo de Attachment.
Vayan examinando los archivos de clase.uc, así se van familiarizando antes de que comience en los próximos pasos a explicar lo necesario para implementarlos y comprenderlos al menos de una manera "rústica".
Recuerden:
Archivos.uc van en la carpeta "Classes"
Archivos.wav van en la carpeta "Sounds"
Así que las carpetas Sounds y Classes del proyecto FAL_AK47_V1 deberían quedar así:
En esta parte del tutorial, vamos a ver como se usa el compilador UMake, para compilar nuestro proyecto y dejarlo listo para usar.
Aclaración: Que éste paso muestre como usar el UMake para compilar el proyecto, no significa que el proyecto ya esté listo para ser compilado ... bueno en realidad si ... pero sólo si es que se hizo todo como se mostró en los pasos anteriores y se usan los archivos que pasé en Attachment (Clases y Sonido).
Pero veamos al UMake:
Aca vemos el UMake ejecutado y listo para que elijamos un proyecto a compilar, así que hacemos Click en el botón con forma de carpeta al costado derecho del combo:
Nos aparecerá un diálogo de directorios, para que elijamos nuestro proyecto FAL_AK47_V1:
Una vez elejido el proyecto, éste queda cargado y podemos Compilar apretando el Botón Compile:
Aca vemos al UMake compilando:
Aca vemos al UMake terminando de compilar el proyecto satisfactoriamente y sin errores. Cuando el UMake termina de compilar, deja creado un archivo.u, (Por eso se llama "U"Make el coso este), y ese archivo lo genera en el directorio System de la instalación de Unreal Tournament donde tenemos alojado nuestro proyecto FAL_AK47_V1. Por lo tanto, si miramos en la carpeta System vamos a ver un nuevo archivo.u llamado:
FAL_AK47_V1.u
Y ese archivo es nada más y nada menos que nuestra arma nueva. Tan solo falta un archivo .INT para poder cargarlo en la sección de mutatores cuando lancemos un juego multiplayer por ejemplo.
Como dije, un archivo.INT es un pequeño archivo que contiene las directivas para indicarle al motor Unreal cómo cargar un mutator. En este caso, el archivo.INT se debe llamar exactamente igual que el archivo .u, o sea:
FAL_AK47_V1.U
FAL_AK47_V1.INT
Y el contenido del archivo.INT es este:
[Public]
Object=(Name=FAL_AK47_V1.Rifle_Arena,Class=Class,MetaClass=Engine.Mutator,Description="Mi ak47 para el clan FAL"
Object=(Name=FAL_AK47_V1.Rifle,Class=Class,MetaClass=Botpack.TournamentWeapon,Description="El ak47 en persona"
Como podrán ver en la primera declaración, en color naranja está lo que indica cua es la clase dentro del FAL_AK47_V1 que el motor Unreal debe cargar como mutator. En este caso, el Rifle_Arena, o sea, un mutator que reemplaza todas las armas con el FAL_AK47_V1. (Por supuesto hay otros modos de mutator pero no viene al caso ahora explicarlos).
Luego en la segunda línea, se declara cual es la clase dentro del archivo FAL_AK47_V1, que contiene al objeto en si, o sea, al Rifle, en este caso, el FAL_AK47_V1.
También se puede ver, que al final y entre " ", van las descripciones que aparecerán en la lista de mutatores a cargar, cuando lancemos un juego multiplayer y vayamos a elegir un mutator.
NOTA: Dejo tambien en el post el archivo.INT para ya tenerlo preparado para usar.
Como el Nº 13 dicen que es de mala suerte, aca no voy a mostrar nada. Tan solo voy a dejar algunas consideraciones importantes:
Los archivos de clase que dejo para el paso Nº11, conviene coleccionarlos ya que con algunos cambios en el código de Script, pueden usarse para crear infinidad de Rifles.
Justamente en los pasos siguientes de este tutorial, voy a mostrar un poco acerca de cuales son las funciones, declaraciones y otras variables a la hora de editar un archivo de classe para hacer un arma nueva.
Como se habrán dado cuenta, (y aunque aún no se ha explicado nada acerca del tema classes y script), en este punto ya cuentan con todo lo necesario para compilar el proyecto y probarlo en el Unreal Tournament.
A partir de ahora y como parte final del tutorial, vamos a ver el asunto de las classes y conocer el significado de partes específicas del script que contienen.
Como dije antes, no voy a tratar de enseñar Script de Unreal Tournament, sino apenas dar una guía acerca de las partes del código que es necesario conocer para poder construir un Arma como el AK47 de nuestro proyecto.
Primero que nada veamos los nombres de las clases que tenemos en el proyecto, y que cosa hace cada una:
En la carpeta Classes del proyecto FAL_AK47_V1 se hallan las siguientes Classes:
mtracerbnz.uc
Rifle.uc
Rifle_arena.uc
RifleBulletsBox.uc
RifleHUDMutator.uc
mtracerbnz:
Esta clase es la que le agrega efecto "trazador" a los disparos del Rifle. Y Contiene el siguiente código Script:
class mtracerbnz expands MTracer;
defaultproperties
{
speed=19000.000000
MaxSpeed=20000.000000
LightRadius=1
}
Se puede ver que esta Clase, contiene apenas directivas para hacer que ciertas variables de la clase "nativa" del Unreal Tournament MTracer, cambien para mostrar el efecto de proyectiles trazantes, iluminados. Si modificamos los valores de estas variables, podemos hacer que el trazado del proyectil sea más o menos luminoso, o más o menos rápido.
Rifle:
Esta clase es la más compleja de todas, porque contiene al Arma en sí. Voy a hacer replyes con quotes, así voy dividiendo todo el texto en fragmentos que puedan ser explicados.
class Rifle extends TournamentWeapon;
1º #exec MESH IMPORT MESH=AK47 ANIVFILE=MODELS\AK47_a.3d DATAFILE=MODELS\AK47_d.3d X=0 Y=0 Z=0
2º #exec MESH ORIGIN MESH=AK47 X=-150 Y=0 Z=-70 Yaw=-112 Roll=0 Pitch=-13
3º #exec MESH SEQUENCE MESH=AK47 SEQ=All STARTFRAME=0 NUMFRAMES=1
4º #exec MESH SEQUENCE MESH=AK47 SEQ=MSG90 MESH STARTFRAME=0 NUMFRAMES=1
5º #exec MESH SEQUENCE MESH=AK47 SEQ=Select STARTFRAME=0 NUMFRAMES=1
6º #exec MESH SEQUENCE MESH=AK47 SEQ=Still STARTFRAME=0 NUMFRAMES=1
7º #exec MESH SEQUENCE MESH=AK47 SEQ=Fire STARTFRAME=0 NUMFRAMES=1
8º #exec MESH SEQUENCE MESH=AK47 SEQ=Down STARTFRAME=0 NUMFRAMES=1
9º #exec MESH SEQUENCE MESH=AK47 SEQ=Reloaded STARTFRAME=0 NUMFRAMES=1
#exec TEXTURE IMPORT NAME=AK470 FILE=Textures\ak47Tex0.PCX GROUP=Skins PALETTE=AK470 // SKIN000000
#exec TEXTURE IMPORT NAME=AK471 FILE=Textures\ak47Tex1.PCX GROUP=Skins PALETTE=AK471 // SKIN000001
#exec TEXTURE IMPORT NAME=AK472 FILE=Textures\ak47Tex2.PCX GROUP=Skins PALETTE=AK472 // SKIN000002
#exec TEXTURE IMPORT NAME=AK473 FILE=Textures\arms.PCX GROUP=Skins PALETTE=AK473 // SKIN000003
#exec MESHMAP NEW MESHMAP=AK47 MESH=AK47
#exec MESHMAP SCALE MESHMAP=AK47 X=0.1 Y=0.1 Z=0.2
#exec MESHMAP SETTEXTURE MESHMAP=AK47 NUM=1 TEXTURE=AK470
#exec MESHMAP SETTEXTURE MESHMAP=AK47 NUM=2 TEXTURE=AK471
#exec MESHMAP SETTEXTURE MESHMAP=AK47 NUM=3 TEXTURE=AK472
#exec MESHMAP SETTEXTURE MESHMAP=AK47 NUM=4 TEXTURE=AK473
//************************************************************************************************
#exec MESH IMPORT MESH=wak47 ANIVFILE=MODELS\wak47_a.3d DATAFILE=MODELS\wak47_d.3d X=0 Y=0 Z=0
#exec MESH ORIGIN MESH=wak47 X=-30 Y=250 Z=-40 Yaw=-64
#exec MESH SEQUENCE MESH=wak47 SEQ=All STARTFRAME=0 NUMFRAMES=1
#exec MESH SEQUENCE MESH=wak47 SEQ=ak47 STARTFRAME=0 NUMFRAMES=1
#exec TEXTURE IMPORT NAME=swAk471 FILE=Textures\swAk471.PCX GROUP=Skins FLAGS=2 // SKIN000000
#exec MESHMAP NEW MESHMAP=wak47 MESH=wak47
#exec MESHMAP SCALE MESHMAP=wak47 X=0.1 Y=0.1 Z=0.2
#exec MESHMAP SETTEXTURE MESHMAP=wak47 NUM=1 TEXTURE=swAk471
//************************************************************************************************
#exec AUDIO IMPORT FILE="Sounds\ak47fire.wav" NAME="ak47Fire" GROUP="HKG11"
#exec TEXTURE IMPORT NAME=scp FILE=TEXTURES\scp.PCX GROUP="Icons" MIPS=OFF FLAGS=2 LODSET=2
#exec TEXTURE IMPORT NAME=Fuego FILE=TEXTURES\FALbigmuzzle.PCX GROUP="Fuegos" MIPS=OFF FLAGS=2 LODSET=2
//************************************************************************************************
var int NumFire;
var vector OwnerLocation;
var float StillTime, StillStart;
var int Count;
var bool bzoom;
simulated event PostNetBeginPlay()
{
local RifleHUDMutator crosshair;
local PlayerPawn HUDOwner;
Super.PostNetBeginPlay();
// If owner is the local player, check that the HUD Mutator exists. Also check for the presense of a bbPlayer (UTPure enabled)
HUDOwner = PlayerPawn(Owner);
if (HUDOwner != None && HUDOwner.IsA('bbPlayer') && HUDOwner.myHUD != None)
{
ForEach AllActors(Class'RifleHUDMutator', crosshair)
break;
if (crosshair == None)
{
crosshair = Spawn(Class'RifleHUDMutator', Owner);
crosshair.RegisterHUDMutator();
crosshair.HUDOwner = HUDOwner;
}
}
}
simulated function PostRender( canvas Canvas )
{
local PlayerPawn P;
local float Scale;
// Super.PostRender(Canvas);
P = PlayerPawn(Owner);
if ( (P != None) && (P.DesiredFOV != P.DefaultFOV) )
{
bOwnsCrossHair = true;
Scale = Canvas.ClipX/640;
Canvas.SetPos(0.5 * Canvas.ClipX - 128 * Scale, 0.5 * Canvas.ClipY - 128 * Scale );
Canvas.Style = ERenderStyle.STY_Translucent;
Canvas.DrawIcon(Texture'scp', Scale);
Canvas.SetPos(0.5 * Canvas.ClipX + 64 * Scale, 0.5 * Canvas.ClipY + 96 * Scale);
Canvas.DrawColor = 15 * ChallengeHUD(PlayerPawn(Owner).myhud).CrosshairColor;
Scale = P.DefaultFOV/P.DesiredFOV;
Canvas.DrawText("X"$int(Scale)$"."$int(10 * Scale - 10 * int(Scale)));
}
else
bOwnsCrossHair = false;
}
function float RateSelf( out int bUseAltMode )
{
local float dist;
if ( AmmoType.AmmoAmount <=0 )
return -2;
bUseAltMode = 0;
if ( (Bot(Owner) != None) && Bot(Owner).bSniping )
return AIRating + 1.15;
if ( Pawn(Owner).Enemy != None )
{
dist = VSize(Pawn(Owner).Enemy.Location - Owner.Location);
if ( dist > 1200 )
{
return (AIRating + 0.25);
}
}
return AIRating;
}
// set which hand is holding weapon
function setHand(float Hand)
{
Super.SetHand(Hand);
if ( Hand == 1 )
Mesh = mesh(DynamicLoadObject("FAL_AK47_V1.AK47", class'Mesh'));
//Else
//Mesh = mesh(DynamicLoadObject("", class'Mesh'));
}
simulated function PlayFiring()
{
local int r;
PlayOwnedSound(FireSound, SLOT_None, Pawn(Owner).SoundDampening*3.0);
bSteadyFlash3rd = True;
if ( (Owner.Physics != PHYS_Falling && Owner.Physics != PHYS_Swimming && Pawn(Owner).bDuck != 0)
|| Owner.Velocity == 0 * Owner.Velocity )
PlayAnim('msg90mesh',0.7 + 0.7 *(FireAdjust*17.0), 0.17);
else
PlayAnim('msg90mesh',0.2 + 0.2 *(FireAdjust*3.0),1);
if ( (PlayerPawn(Owner) != None)
&& (PlayerPawn(Owner).DesiredFOV == PlayerPawn(Owner).DefaultFOV) )
bMuzzleFlash++;
}
simulated function bool ClientAltFire( float Value )
{
GotoState('Zooming');
return true;
}
function AltFire( float Value )
{
ClientAltFire(Value);
}
function Fire( float Value )
{
if ( (AmmoType == None) && (AmmoName != None) )
{
// ammocheck
GiveAmmo(Pawn(Owner));
}
if ( AmmoType.UseAmmo(1) )
{
GotoState('NormalFire');
bPointing=True;
bCanClientFire = true;
ClientFire(Value);
if ( bRapidFire || (FiringSpeed > 0) )
Pawn(Owner).PlayRecoil(FiringSpeed);
if ( bInstantHit )
{
if ( (Owner.Physics != PHYS_Falling && Owner.Physics != PHYS_Swimming && Pawn(Owner).bDuck != 0)
|| Owner.Velocity == 0 * Owner.Velocity )
TraceFire(0.0);
else
TraceFire(16);
}
else
ProjectileFire(ProjectileClass, ProjectileSpeed, bWarnTarget);
}
}
///////////////////////////////////////////////////////
state NormalFire
{
function EndState()
{
Super.EndState();
OldFlashCount = FlashCount;
}
Begin:
FlashCount++;
}
function Timer()
{
local actor targ;
local float bestAim, bestDist;
local vector FireDir;
local Pawn P;
bestAim = 0.95;
P = Pawn(Owner);
if ( P == None )
{
GotoState('');
return;
}
if ( VSize(P.Location - OwnerLocation) < 6 )
StillTime += FMin(2.0, Level.TimeSeconds - StillStart);
else
StillTime = 0;
StillStart = Level.TimeSeconds;
OwnerLocation = P.Location;
FireDir = vector(P.ViewRotation);
targ = P.PickTarget(bestAim, bestDist, FireDir, Owner.Location);
if ( Pawn(targ) != None )
{
SetTimer(1 + 4 * FRand(), false);
bPointing = true;
Pawn(targ).WarnTarget(P, 200, FireDir);
}
else
{
SetTimer(0.4 + 1.6 * FRand(), false);
if ( (P.bFire == 0) && (P.bAltFire == 0) )
bPointing = false;
}
}
function ProcessTraceHit(Actor Other, Vector HitLocation, Vector HitNormal, Vector X, Vector Y, Vector Z)
{
local UT_Shellcase s;
s = Spawn(class'UT_ShellCase',, '', Owner.Location + CalcDrawOffset() + 30 * X + (2.8 * FireOffset.Y+5.0) * Y - Z * 1);
if ( s != None )
{
s.DrawScale = 0.7;
s.Eject(((FRand()*0.3+0.4)*X + (FRand()*0.2+0.2)*Y + (FRand()*0.3+1.0) * Z)*160);
}
if (Other == Level)
Spawn(class'UT_HeavyWallHitEffect',,, HitLocation+HitNormal, Rotator(HitNormal));
else if ( (Other != self) && (Other != Owner) && (Other != None) )
{
if ( Other.bIsPawn )
{
Other.PlaySound(Sound 'ChunkHit',, 4.0,,100);
Other.PlaySound(Sound 'UnrealI.Razorjack.BladeThunk',, 4.0,,100);
PlayOwnedSound(Sound 'UnrealI.Razorjack.BladeThunk',, 4.0,,10);
}
if ( Other.bIsPawn && (HitLocation.Z - Other.Location.Z > 0.62 * Other.CollisionHeight)
&& (instigator.IsA('PlayerPawn') || (instigator.IsA('Bot') && !Bot(Instigator).bNovice))
&& ((Owner.Physics != PHYS_Falling && Owner.Physics != PHYS_Swimming && Pawn(Owner).bDuck != 0)
|| Owner.Velocity == 0 * Owner.Velocity) )
{
if ( Pawn(Other).Health > 0 )
{
Other.TakeDamage(100000, Pawn(Owner), HitLocation, 35000 * X, AltDamageType);
if ( Pawn(Other).Health < 1
&& (!Level.Game.bTeamGame || Pawn(Owner).PlayerReplicationInfo.Team != Pawn(Other).PlayerReplicationInfo.Team) )
AmmoType.AddAmmo(3);
}
else
Other.TakeDamage(100000, Pawn(Owner), HitLocation, 35000 * X, AltDamageType);
}
else
Other.TakeDamage(45, Pawn(Owner), HitLocation, 30000.0*X, MyDamageType);
if ( !Other.bIsPawn && !Other.IsA('Carcass') )
spawn(class'UT_SpriteSmokePuff',,,HitLocation+HitNormal*9);
}
}
function Finish()
{
if ( (Pawn(Owner).bFire!=0) && (FRand() < 0.6) )
Timer();
Super.Finish();
}
function TraceFire( float Accuracy )
{
local vector HitLocation, HitNormal, StartTrace, EndTrace, X,Y,Z, AimDir;
local actor Other;
Owner.MakeNoise(Pawn(Owner).SoundDampening);
GetAxes(Pawn(owner).ViewRotation,X,Y,Z);
StartTrace = Owner.Location + pawn(owner).Eyeheight * Z;
AdjustedAim = pawn(owner).AdjustAim(1000000, StartTrace, 2*AimError, False, False);
X = vector(AdjustedAim);
EndTrace = StartTrace + Accuracy * (FRand() - 0.5 )* Y * 1000
+ Accuracy * (FRand() - 0.5 ) * Z * 1000;
AimDir = vector(AdjustedAim);
EndTrace += (100000 * AimDir);
Other = Pawn(Owner).TraceShot(HitLocation,HitNormal,EndTrace,StartTrace);
Count++;
if ( Count == 2 )
{
Count = 0;
if ( VSize(HitLocation - StartTrace) > 250 )
Spawn(class'mtracerbnz',,, StartTrace + 96 * AimDir,rotator(EndTrace - StartTrace));
}
ProcessTraceHit(Other, HitLocation, HitNormal, vector(AdjustedAim),Y,Z);
}
////////////////////////////////////////////////////////////////////////////////
state Idle
{
function Fire( float Value )
{
if ( AmmoType == None )
{
// ammocheck
GiveAmmo(Pawn(Owner));
}
if (AmmoType.UseAmmo(1))
{
GotoState('NormalFire');
bCanClientFire = true;
bPointing=True;
if ( Owner.IsA('Bot') )
{
// simulate bot using zoom
if ( Bot(Owner).bSniping && (FRand() < 0.65) )
AimError = AimError/FClamp(StillTime, 1.0, 8.0);
else if ( VSize(Owner.Location - OwnerLocation) < 6 )
AimError = AimError/FClamp(0.5 * StillTime, 1.0, 3.0);
else
StillTime = 0;
}
Pawn(Owner).PlayRecoil(FiringSpeed);
if ( (Owner.Physics != PHYS_Falling && Owner.Physics != PHYS_Swimming && Pawn(Owner).bDuck != 0)
|| Owner.Velocity == 0 * Owner.Velocity )
TraceFire(0.0);
else
TraceFire(16);
AimError = Default.AimError;
ClientFire(Value);
}
}
function BeginState()
{
bPointing = false;
SetTimer(0.4 + 1.6 * FRand(), false);
Super.BeginState();
}
function EndState()
{
SetTimer(0.0, false);
Super.EndState();
}
Begin:
bPointing=False;
if ( AmmoType.AmmoAmount<=0 )
Pawn(Owner).SwitchToBestWeapon(); //Goto Weapon that has Ammo
if ( Pawn(Owner).bFire!=0 ) Fire(0.0);
Disable('AnimEnd');
PlayIdleAnim();
}
///////////////////////////////////////////////////////
state Zooming
{
simulated function Tick(float DeltaTime)
{
if ( Pawn(Owner).bAltFire == 0 )
{
bZoom = false;
SetTimer(0.0,False);
GoToState('Idle');
}
else if ( bZoom )
{
if ( PlayerPawn(Owner).DesiredFOV > 3 )
{
PlayerPawn(Owner).DesiredFOV -= PlayerPawn(Owner).DesiredFOV*DeltaTime*4.5;
}
if ( PlayerPawn(Owner).DesiredFOV <=3 )
{
PlayerPawn(Owner).DesiredFOV = 3;
bZoom = false;
SetTimer(0.0,False);
GoToState('Idle');
}
}
}
simulated function BeginState()
{
if ( Owner.IsA('PlayerPawn') )
{
if ( PlayerPawn(Owner).DesiredFOV == PlayerPawn(Owner).DefaultFOV )
{
bZoom = true;
SetTimer(0.2,True);
}
else if ( bZoom == false )
{
PlayerPawn(Owner).DesiredFOV = PlayerPawn(Owner).DefaultFOV;
Pawn(Owner).bAltFire = 0;
}
}
else
{
Pawn(Owner).bFire = 1;
Pawn(Owner).bAltFire = 0;
Global.Fire(0);
}
}
}
///////////////////////////////////////////////////////////
simulated function PlayIdleAnim()
{
if ( Mesh != PickupViewMesh )
PlayAnim('AK47',1.0, 0.2);
}
defaultproperties
{
WeaponDescription="Classification: FAL Automatic Sniper Rifle"
AmmoName=Class'FAL_AK47_V1.RifleBulletsBox'
PickupAmmoCount=250
bInstantHit=True
bAltInstantHit=True
bRapidFire=True
FiringSpeed=1.800000
FireOffset=(Y=-5.000000,Z=-2.000000)
AltDamageType=Decapitated
AIRating=0.540000
RefireRate=0.600000
AltRefireRate=0.300000
FireSound=Sound'FAL_AK47_V1.ak47Fire'
SelectSound=Sound'UnrealI.Rifle.RiflePickup'
DeathMessage="%k puso un proyectil de 7.62MM en el cuerpo de %o."
NameColor=(R=0,G=0)
bDrawMuzzleFlash=True
MuzzleScale=1.000000
FlashY=0.286000
FlashO=0.034800
FlashC=0.031000
FlashLength=0.013000
FlashS=256
MFTexture=Texture'FAL_AK47_V1.Fuegos.Fuego'
AutoSwitchPriority=10
InventoryGroup=10
bRotatingPickup=True
PickupMessage="Agarraste un FAL AK47."
ItemName="Rifle FAL"
PlayerViewOffset=(X=7.400000,Y=-4.600000,Z=-4.500000)
PlayerViewMesh=LodMesh'FAL_AK47_V1.AK47'
PlayerViewScale=0.120000
BobDamping=0.975000
PickupViewMesh=LodMesh'FAL_AK47_V1.wak47'
PickupViewScale=1.200000
ThirdPersonMesh=LodMesh'FAL_AK47_V1.wak47'
ThirdPersonScale=1.000000
StatusIcon=Texture'Botpack.Icons.UseRifle'
bMuzzleFlashParticles=True
MuzzleFlashStyle=STY_Translucent
MuzzleFlashMesh=LodMesh'Botpack.muzzsr3'
MuzzleFlashTexture=Texture'FAL_AK47_V1.Fuegos.Fuego'
PickupSound=Sound'UnrealShare.Pickups.WeaponPickup'
Icon=Texture'Botpack.Icons.UseRifle'
Rotation=(Roll=-1536)
Mesh=LodMesh'FAL_AK47_V1.wak47'
bNoSmooth=False
CollisionRadius=32.000000
CollisionHeight=8.000000
}
Aca podemos ver la primera parte de la clase Rifle, y esta primera parte del script se encarga de cargar los recursos de malla y textura del modelo para vista de 1º persona, como así de acomodar la malla en la posición que el jugador verá en 1º persona, y la manera en que se dibujarán las texturas sobre cada parte de la malla. Noten que el nombre de la Clase "Rifle", es el mismo nombre del archivo.uc que la conetiene "Rifle.uc".
class Rifle extends TournamentWeapon;
#exec MESH IMPORT MESH=AK47 ANIVFILE=MODELS\AK47_a.3d DATAFILE=MODELS\AK47_d.3d X=0 Y=0 Z=0
#exec MESH ORIGIN MESH=AK47 X=-150 Y=0 Z=-70 Yaw=-112 Roll=0 Pitch=-13
#exec MESH SEQUENCE MESH=AK47 SEQ=All STARTFRAME=0 NUMFRAMES=1
#exec MESH SEQUENCE MESH=AK47 SEQ=MSG90 MESH STARTFRAME=0 NUMFRAMES=1
#exec MESH SEQUENCE MESH=AK47 SEQ=Select STARTFRAME=0 NUMFRAMES=1
#exec MESH SEQUENCE MESH=AK47 SEQ=Still STARTFRAME=0 NUMFRAMES=1
#exec MESH SEQUENCE MESH=AK47 SEQ=Fire STARTFRAME=0 NUMFRAMES=1
#exec MESH SEQUENCE MESH=AK47 SEQ=Down STARTFRAME=0 NUMFRAMES=1
#exec MESH SEQUENCE MESH=AK47 SEQ=Reloaded STARTFRAME=0 NUMFRAMES=1
#exec TEXTURE IMPORT NAME=AK470 FILE=Textures\ak47Tex0.PCX GROUP=Skins PALETTE=AK470 // SKIN000000
#exec TEXTURE IMPORT NAME=AK471 FILE=Textures\ak47Tex1.PCX GROUP=Skins PALETTE=AK471 // SKIN000001
#exec TEXTURE IMPORT NAME=AK472 FILE=Textures\ak47Tex2.PCX GROUP=Skins PALETTE=AK472 // SKIN000002
#exec TEXTURE IMPORT NAME=AK473 FILE=Textures\arms.PCX GROUP=Skins PALETTE=AK473 // SKIN000003
#exec MESHMAP NEW MESHMAP=AK47 MESH=AK47
#exec MESHMAP SCALE MESHMAP=AK47 X=0.1 Y=0.1 Z=0.2
#exec MESHMAP SETTEXTURE MESHMAP=AK47 NUM=1 TEXTURE=AK470
#exec MESHMAP SETTEXTURE MESHMAP=AK47 NUM=2 TEXTURE=AK471
#exec MESHMAP SETTEXTURE MESHMAP=AK47 NUM=3 TEXTURE=AK472
#exec MESHMAP SETTEXTURE MESHMAP=AK47 NUM=4 TEXTURE=AK473
Pueden ver que la primera declaración #exec, llama a cargar los archivos de malla 3D que tenemos en la carpeta Models del proyecto, la segunda declaración #exec se encarga de poner en posición el modelo para la vista de 1º persona, usando parámetros de coordenadas X, Y, Z y asistentes de rotación Yaw, Pitch, Roll. Estas coordenadas de posición varían según que malla utilicemos, así que cuando usemos este código para armar un rifle pero con una malla diferente, vamos a tener que reacomodar esos valores de coordenadas y puede ser una tarea terrible. Recomiendo poner todos los valores a 0 y comenzar de esa manera, probando como va quedando y con trabajo de hormiga, ir calibrando la posición. Pueden ver que se aceptan tanto valores positivos como negativos a la hora de asignar valores a ls coordenadas.
Las siguientes líneas de declaraciones #exec, se encargan de cargar los archivos de Textura desde la carpeta Textures del proyecto, crear un nombre de importación para alojar las texturas ( Así por ejemplo el nombre AK470, se carga con la textura ak47Tex0.PCX ) . Luego se crean variables de nombre para la malla denominadas "MESHMAP", en las cuales finalmente se realiza el mapeado de las texturas sobre la malla, teniendo en cuenta valores como coordenadas y números de partes de la malla, partes que serán ocupadas según número por una de las texturas cargadas.
buen tutorial
esperamos otro ee
joder vaya curro
Tan facil como eso X'DDDD
PD: Buen tuto....
PD2: lo de los luces no se cuando te lo voy a pasar pork me voy de vacaciones y ya hasta septiembre no lo vou¡y a tocar : PP ..... al final lo abandonare o lo dejare apartado unos meses.....
pedazo texto jeje , me asombras
Dios, curraisimo, Goes a crear armas en coD2 de CoD4 y otros gaMes, GOES
Vaya tuto si señor.
Gracias, habra otros en camino asi k esperenlos.
jeje
saludos
genial
joder que curro...
un 10 tio, menudo curro