Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Game Development
  2. Game Mechanics
Gamedevelopment

Cómo codificar Monster Loot Drops

by
Difficulty:IntermediateLength:LongLanguages:

Spanish (Español) translation by Elías Nicolás (you can also view the original English article)

Un mecanismo común en los juegos de acción es que los enemigos arrojen algún tipo de objeto o recompensa al morir. El personaje puede recoger este botín para obtener alguna ventaja. Es un mecanismo que se espera en muchos juegos, como los juegos de rol, ya que le da al jugador un incentivo para deshacerse de los enemigos, así como una pequeña explosión de endorfinas al descubrir cuál es la recompensa inmediata por hacerlo.

En este tutorial, revisaremos el funcionamiento interno de dicha mecánica y veremos cómo implementarla, sin importar el tipo de juego y la herramienta / lenguaje de codificación que pueda estar usando.

Los ejemplos que uso para demostrar esto se hicieron usando Construct 2, una herramienta de creación de juegos HTML5, pero no son de ninguna manera específicos para él. Debería poder implementar la misma mecánica sea cual sea su lenguaje o herramienta de codificación.

Los ejemplos se hicieron en r167.2 y se pueden abrir y editar en la versión gratuita del software. Puede descargar la última versión de Construct 2 aquí (desde que comencé a escribir este artículo, se han lanzado al menos dos versiones más nuevas) y perder el tiempo con los ejemplos a su gusto. Los ejemplos de los archivos fuente CAPX se adjuntan a este tutorial en el archivo zip.

La mecánica básica

A la muerte de un enemigo (entonces, cuando su HP es menor o igual a cero) se llama a una función. La función de esta función es determinar si hay una caída o no, y si es así, el tipo de caída debería ser.

La función también puede manejar la creación de la representación visual de la gota, generándola en las antiguas coordenadas de pantalla del enemigo.

Considere el siguiente ejemplo:

Haga clic en el botón Slay 100 Beasts. Esto ejecutará un proceso por lotes que crea 100 bestias aleatorias, las mata y muestra el resultado para cada bestia (es decir, si la bestia arroja un objeto y, de ser así, qué tipo de elemento). Las estadísticas en la parte inferior de la pantalla muestran cuántas bestias arrojaron elementos y cuántos de cada tipo de elemento se eliminaron.

Este ejemplo es estrictamente de texto para mostrar la lógica detrás de la función, y para mostrar que esta mecánica se puede aplicar a cualquier tipo de juego, ya sea un juego de plataformas en el que pise a los enemigos, o un juego de disparos de vista de arriba hacia abajo, o un juego de roles

Veamos cómo funciona esta demostración. Primero, las bestias y las gotas están contenidas en matrices. Aquí está la matriz beast:

Índice (X)
Nombre (Y-0)
Tasa de caída (Y-1)
Artículo rareza (Y-2)
0 Jabali 100 100
1 Duende 75 75
2 Escudero 65 55
3 ZogZog 45 100
4 Búho 15 15
5 Mastodonte 35 50

Y aquí está la matriz drops:

Índice (X)
Nombre (Y-0)
Raramente del artículo (Y-1)
0 Chupete 75
1 Oro 50
2 Rocas 95
3 Joya 25
4 Incienso 35
5 Equipo 15

El valor X (la columna Index) de la matriz actúa como un identificador único para la bestia o el tipo de elemento. Por ejemplo, la bestia del índice 0 es un Boar. El ítem del índice 3 es una joya Jewel.

Estas matrices actúan como tablas de búsqueda para nosotros, que contienen el nombre o tipo de cada bestia o elemento, así como otros valores que nos permitirán determinar la rareza o la tasa de abandono. En la matriz de bestias, hay dos columnas más después del nombre:

La Drop rate tasa de caída es la probabilidad de que la bestia caiga un objeto cuando muere. Por ejemplo, el jabalí tendrá un 100% de probabilidad de soltar un objeto cuando muere, mientras que el búho tendrá un 15% de posibilidades de hacer lo mismo.

Rarity define cuán poco comunes son los elementos que puede arrojar esta bestia. Por ejemplo, es probable que un jabalí suelte objetos de un valor de rareza de 100. Ahora, si comprobamos el array drops, podemos ver que las rocas son el elemento con mayor rareza (95). (A pesar de que el valor de rareza es alto, debido a la forma en que programé la función, cuanto más grande es el número de rareza, más común es el objeto. Tiene más posibilidades de soltar las rocas que un elemento con un valor de rareza más bajo).

Y eso es interesante para nosotros desde la perspectiva del diseño del juego. Para el resto del juego, no queremos que el jugador tenga acceso a demasiados equipos o demasiados artículos de alta gama demasiado pronto; de lo contrario, el personaje podría verse dominado demasiado pronto, y el juego será menos interesante para jugar.

Estas tablas y valores son solo ejemplos, y puedes y debes jugar con ellos y adaptarlos a tu propio sistema de juego y universo. Todo depende del equilibrio de tu sistema. Si desea obtener más información sobre el tema del equilibrio, le recomiendo consultar esta serie de tutoriales: Equilibrar los juegos de rol basados ​​en turnos.

Veamos ahora el (pseudo) código para la demostración:

Primero, la acción del usuario: hacer clic en el botón Slay 100 Beasts. Este botón llama a una función con un parámetro de 100, solo porque 100 se siente como un buen número de enemigos para matar. En un juego real, es más probable que mates animales uno por uno, por supuesto.

A partir de esto, se llama a la función SlainBeast. Su propósito es mostrar un texto para dar a los usuarios su opinión sobre lo que sucedió. Primero, limpia la variable BeastDrops y una matriz aStats, que se usan para las estadísticas. En un juego real, es poco probable que los necesites. También limpia Text, de modo que se mostrarán 100 nuevas líneas para ver los resultados de este lote. En la función en sí, se crean tres variables numéricas: BeastType, DropChance y Rarity.

BeastType será el índice que usemos para referirnos a una fila específica en la matriz aBeast; es básicamente el tipo de bestia que el jugador tuvo que enfrentar y matar. La rareza Rarity también se toma de la matriz aBeast; es la rareza del elemento que esta bestia debería caer, el valor del campo Item rarity en la matriz aBeast.

Finalmente, DropChance es un número que seleccionamos aleatoriamente entre 0 y 100. (La mayoría de los lenguajes de codificación tendrán una función para obtener un número aleatorio de un rango, o al menos para obtener un número aleatorio entre 0 y 1, que luego podría simplemente multiplicarse por 100.)

En este punto, podemos mostrar nuestro primer bit de información en el objeto Text: ya sabemos qué tipo de bestia generó y fue asesinada. Por lo tanto, concatenamos el valor actual de Text.text BEAST_NAME del BeastType actual que elegimos al azar, fuera de la matriz aBeast.

A continuación, tenemos que determinar si un artículo se descartará. Lo hacemos comparando el valor DropChance con el valor BEAST_DROPRATE de la matriz aBeast. Si DropChance es menor o igual que este valor, soltamos un elemento.

(Decidí optar por el enfoque "menor o igual que", después de haber sido influenciado por estos jugadores en vivo usando el conjunto de reglas de D & D King Arthur: Pendragon con respecto a tiras de dados, pero podrías codificar la función al revés , decidiendo que las caídas solo ocurrirán cuando sea "mayor o igual". Es solo una cuestión de valores numéricos y lógica. Sin embargo, manténgase constante a lo largo de su algoritmo y no cambie la lógica a la mitad, de lo contrario, podría terminar con problemas al intentar depurarlo o mantenerlo).

Por lo tanto, dos líneas determinan si un elemento se descarta o no. Primero:

Aquí, DropChance es mayor que DropRate, y consideramos que esto significa que no se descarta ningún elemento. A partir de ahí, lo único que se muestra es un cierre "." (punto) que termina la oración, "[BeastType] fue asesinado.", antes de pasar al siguiente enemigo en nuestro lote.

Por otra parte:

Aquí, DropChance es menor o igual que el DropRate para el BeastType actual, por lo que consideramos que significa que un elemento se descarta. Para hacerlo, haremos una comparación entre la rareza Rarity de los ítems que el "BeastType" actual está "permitido" y los varios valores raros que hemos configurado en la tabla aDrop.

Recorrimos la tabla aDrop, verificando cada índice para ver si su DROP_RATE es mayor o igual a Rarity. (Recuerde, en contra de la intuición, cuanto mayor es el valor de Rarity, más común es el elemento) Para cada índice que coincida con la comparación, colocamos ese índice en una matriz temporal, aTemp.

Al final del ciclo, deberíamos tener al menos un índice en la matriz aTemp. (Si no, tenemos que rediseñar nuestras tablas aDrop y aEast). Luego creamos una nueva variable numérica DropType que selecciona aleatoriamente uno de los índices de la matriz aTemp; este será el artículo que soltamos.

Agregamos el nombre del elemento a nuestro objeto de Texto, haciendo que la oración sea algo así como "BeastType fue asesinado, dejando caer un DROP_NAME". Luego, por el bien de este ejemplo, agregamos algunos números a nuestras diversas estadísticas (en la matriz aStats y en BeastDrops).

Finalmente, después de las 100 repeticiones, mostramos esas estadísticas, el número de bestias (de 100) que arrojaron elementos y el número de cada elemento que se eliminó.

Otro ejemplo: dejar caer objetos visualmente

Consideremos otro ejemplo:

Presiona Espacio para crear una bola de fuego que matará al enemigo.

Como puede ver, se crea un enemigo aleatorio (de un bestiario de 11). El personaje jugador (a la izquierda) puede crear un ataque de proyectil. Cuando el proyectil golpea al enemigo, el enemigo muere.

A partir de ahí, una función similar a la que hemos visto en el ejemplo anterior determina si el enemigo está eliminando algún elemento o no, y determina cuál es el elemento. Esta vez, también crea la representación visual del artículo eliminado y actualiza las estadísticas en la parte inferior de la pantalla.

Aquí hay una implementación en pseudocódigo:

Eche un vistazo al contenido de las tablas aEnemy y aDrop, respectivamente:

Índice (X)
Nombre (Y-0)
Tasa de caída (Y-1)
Rarasidad del artículo (Y-2)
Nombre de la animación (Y-3)
0 Sanadora Hembra 100 100 Healer_F
1 Sanador Masculino 75 75 Healer_M
2 Mago Hembra 65 55 Mage_F
3 Mago macho 45 100 Mage_M
4 Ninja Mujer 15 15 Ninja_F
5 Ninja macho 35 50 Ninja_M
6 guardabosques masculino 75 80 Ranger_M
7 Gente del pueblo femenina 75 15 Townfolk_F
8 Gente del pueblo masculino 95 95 Townfolk_M
9 Guerrero femenino 70 70 Warrior_F
10 Guerrero Hombre 45 55 Warrior_M
Índice (X)
Nombre (Y-0)
Raramente del artículo (Y-1)
0 Manzana 75
1 Banana 50
2 Zanahoria 95
3 Uva 95
4 Poción vacía 80
5 Poción azul 75
6 Poción roja 70
7 Poción verde 60
8 Corazón rosado 65
9 Perla azul 15
10 Rock 100
11 Guante 25
12 Armadura 30
13 Joya 35
14 Sombrero de mago 65
15 Escudo de madera 85
16 Hacha de hierro 65

A diferencia del ejemplo anterior, la matriz que contiene los datos del enemigo se llama aEnemy y contiene una fila más de datos, ENEMY_ANIM, que tiene el nombre de la animación del enemigo. De esta forma, cuando generamos al enemigo, podemos buscarlo y automatizar la visualización gráfica.

En la misma línea, aDrop ahora contiene 16 elementos, en lugar de seis, y cada índice se refiere al marco de la animación del objeto, pero también podría haber tenido varias animaciones, como para los enemigos, si los elementos abandonados fueran animados.

Esta vez, hay muchos más enemigos y elementos que en el ejemplo anterior. Sin embargo, puede ver que los datos relativos a las tasas de abandono y los valores de rareza todavía existen. Una diferencia notable es que hemos separado el desove de los enemigos de la función que calcula si hay una caída o no. Esto se debe a que, en un juego real, los enemigos probablemente harían algo más que esperar en la pantalla para ser asesinados.

Entonces ahora tenemos una función SpawnEnemy y otra función Drop. Drop es muy similar a la forma en que manejamos la "tirada de dados" de nuestro elemento cae en el ejemplo anterior, pero toma varios parámetros esta vez: dos de estas son las coordenadas X e Y del enemigo en la pantalla, ya que ese es el lugar donde querrá engendrar el artículo cuando haya una caída; los otros parámetros son EnemyType, por lo que podemos buscar el nombre del enemigo en la tabla aEnemy, y el nombre del personaje como una cadena, para que sea más rápido escribir los comentarios que queremos darle al jugador.

La lógica de la función Drop es similar a la del ejemplo anterior; lo que más cambia es la forma en que mostramos los comentarios. Esta vez, en lugar de simplemente mostrar texto, también generamos un objeto en la pantalla para darle una representación visual al jugador.

(Nota: para engendrar a los enemigos en varias posiciones en la pantalla, usé un objeto invisible, Spawn, como referencia, que se mueve continuamente hacia la izquierda y la derecha. Siempre que se llame a la función SpawnEnemy, crea el enemigo en las coordenadas actuales del objeto Spawn, para que aparezcan los enemigos y una variedad de ubicaciones horizontales.)

Una última cosa para discutir es cuándo se llama exactamente la función Drop. No lo disparo directamente después de la muerte de un enemigo, pero después de que el enemigo se haya desvanecido (la animación de la muerte del enemigo). Por supuesto, puede solicitar la caída cuando el enemigo aún esté visible en la pantalla, si lo prefiere; una vez más, eso depende del diseño de tu juego.

Conclusión

A nivel de diseño, tener enemigos que tiren un botín incentiva al jugador para enfrentarlos y destruirlos. Los elementos eliminados te permiten otorgar poderes, estadísticas o incluso objetivos al jugador, ya sea de forma directa o indirecta.

En un nivel de implementación, la eliminación de elementos se gestiona a través de una función que el programador decide cuándo llamar. La función hace el trabajo de verificar la rareza de los elementos que se deben eliminar según el tipo de enemigo muerto, y también puede determinar dónde generarlo en la pantalla si es necesario. Los datos para los elementos y enemigos se pueden guardar en estructuras de datos como matrices, y la función los puede consultar.

La función utiliza números aleatorios para determinar la frecuencia y el tipo de las caídas, y el codificador tiene control sobre esas tiradas aleatorias y los datos que busca, para adaptar la sensación de esas caídas en el juego.

Espero que hayas disfrutado este artículo y entiendas mejor cómo hacer que tus monstruos caigan en el juego. Estoy deseando ver tus propios juegos usando esa mecánica.

Referencias

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.