Sistema de events editor de nivel, cómo traducir evento a acción de juego

He estado ocupado intentando crear un editor de niveles para un juego basado en fichas en el que estoy trabajando. Todo va bastante bien, lo único con lo que estoy teniendo problemas es crear un sistema de events simple.

Digamos que el jugador pisa una ficha particular que tuvo la acción "telepuerto" asignada en el editor. La cadena de teletransportación se guarda en el object de mosaico como una variable. Al crear el tilegrid, una class de actionmanager escanea la variable de acción y asigna acciones a la variable.

public static class ActionManager { public static function ParseTileAction(tile:Tile) { switch(tile.action) { case "TELEPORT": //assign action here break; } } } 

Ahora este es un evento de colisión, así que supongo que también debería proporcionar un object para colidar con el mosaico. Pero, ¿y si tuviera que contar para la colisión con todos los objects en el mundo? Además, verificar las colisiones en la class de administrador de acciones no parece ser muy eficiente.

¿Estoy incluso en el path correcto aquí? Soy nuevo en el layout del juego, así que podría estar completamente fuera de la pista. Todos los consejos sobre cómo gestionar y crear events utilizando un editor suelen ser geniales. El principal problema que tengo es una forma eficiente de leer y reactjsr ante las acciones dadas en el editor.

Gracias por adelantado.

No estoy muy familiarizado con el guión de acción, pero parece bastante familiar para C # y creo que entiendo su pregunta / dilema.

Podría ser beneficioso tener ciertos factores desencadenantes (como cuando un jugador pisa el azulejo o cuando algo pisa el azulejo). Estos desencadenantes podrían ser parte de un mosaico o posiblemente parte de un map.

Luego asigna acciones para hacer cuando estos desencadenantes se disparan / ocurren.

Básicamente, este sistema de events se divide en dos partes. Una parte (el disparador) alerta la acción que se ejecutará. La segunda parte realmente ejecuta la acción y posiblemente comtesting si hay condiciones adicionales.

Si hiciste esto, tu editor de maps podría asignar activadores a un mosaico (o un map). Luego, podría especificar una acción para ejecutar para ese disparador específico. Por lo tanto, el activador de la ficha de teletransporte haría la comprobación de colisión (o se conectaría a algún sistema de física / colisión para recibir una notificación cuando ocurriera una colisión con la ficha) y luego activaría la acción de teletransporte.

PD: Consideraría que la acción se especifica con más que solo una cadena (o al less una cadena que también puede tener arguments en ella). ¿Cómo se especifica dónde esa ficha teletransporta al jugador si la cadena tiene que ser "TELEPORT"? Solo algo de reflexión.

Además, parece que falta una parte de tu pregunta (casi al final). "El problema principal que estoy teniendo es la [pieza faltante?]"

Acción / Resultado es una function de un grupo de variables, por ejemplo, f (x, y, z …)

Primero, establezca qué son x, y, z, etc. y cuál será la acción resultante. Piense en x, y, z como mosaico (su position, estado, etc.), nivel actual, variables de entorno actuales, etc. Luego, decida quién actuará en la acción. ¿Será el azulejo o un controller O ambos? Una vez que haya respondido las dos primeras preguntas, sabrá qué variables / valores necesita enviar de vuelta a la casilla o enviar a un controller.

En su map debe haber "Zonas" / "Disparadores". En mi juego, mis desencadenantes tienen un campo de acción y un campo de parámetro que especifica cómo se debe hacer la acción.

Ejemplo: El jugador, cuando ingresa un teleprotractor, solo debe teletransportarse cuando hay un button activado. La condición debe ser probada dentro del motor. Su activador podría verse así: Tipo: Región de activación: … Acción: Condiciones de teletransportación: Botón1.Activo == verdadero Parámetro: 100, 200 (las coorderadas del objective)

Por lo tanto, tu juego debe tener funciones de condición y Acciones (con parameters). Esa es la forma exacta en que se maneja en todos los juegos principales. Motor Warcraft3, motor SourceEngine, Unreal … Warcraft3 es un ejemplo perfecto. Su editor de scripts consiste en Eventos, Condiciones y Acciones. Búscalo, tal vez puedas get inspiración de él.

Consejo: No conozco las habilidades de actionscript, pero trato de analizar los datos de nivel en matrices estáticas. itera sobre ellos cada cuadro para ver si se activa algo. En mi juego también hay un campo que describe con qué frecuencia se debe actualizar la lógica de esta entidad.

Pregunte si algo no está claro, estoy feliz si puedo ayudar.

El truco para esto es simplemente encontrar la manera de equilibrar la cantidad de análisis de datos y el comportamiento codificado. Estoy seguro de que un teleprotractor no es el único tipo de comportamiento que te gustaría agregar a tu ficha. Idealmente, podrá agregar nuevos comportamientos rápidamente y tener una forma obvia de describirlos en un formatting de datos.

No proporcionó muchos detalles sobre su class de fichas, así que voy a hacer un poco de mierda. Supongamos que tiene algún tipo de class GameObject base, que tiene algunas variables básicas como position y grupo. Supongo que GameObject s son cosas que son generalmente dinámicas en tu juego. Cosas como el escenario estático no entrarían en esta categoría. Group es solo algo que agregué para ilustrar el efecto de verificar solo para el jugador, todos los NPC, etc.

 public class GameObject { public static const PLAYER_ONLY:int = 1; public static const ALL_CHARACTERS:int = 2; public static const ALL_ENEMIES:int = 3; public static const ALL_FRIENDLY_NPCS:int = 4; public static const INTERACTABLES:int = 42; public static const BLOCKS:int = 99; public static const TREASURE_CHESTS:int = 20384293; public var Group:int; // assuming you have some Point class encapsulating X/Y coordinates public var position:Point; } 

Ahora para una class de fichas. Estoy usando una biblioteca llamada as3signals (biblioteca alternativa: TurboSignals). Básicamente, le permite crear delegates de estilo C # para evitar la molestia de tener que usar el sistema de events estándar de Flash con cadenas y EventDispatcher. Puede lograr lo mismo utilizando el método EventDispatcher, pero es mucho less sexy.

La forma en que funciona es que tiene campos en su class que son del tipo Signal. Cada señal tiene una function add (), que se comporta como eventDispatcher.addEventListener, excepto que el oyente que está agregando es para esa señal específica, y nada más. Hace que sus events sean seguros para types, por lo que es bueno para events donde sabe que los necesitará absolutamente, como OnObjectEnter y OnObjectLeave. Claro, no tienes la flexibilidad de definir arbitrariamente literales de cadenas de events para disparar al éter, pero estas cosas son bastante fáciles de usar. Y nada le impide usar EventDispatcher junto con señales.

Una cosa que es agradable acerca de as3signals es que puedes definir el tipo de argumento que envías con el despacho. En el caso del mosaico, enviará un parámetro de GameObject, para que quien esté interesado en el evento sepa qué object del juego ingresó en el mosaico.

Acabo de crear dos ranuras de señal: OnObjectEnter y OnObjectLeave . Estos sirven para notificar a otros objects cuando un GameObject ha ingresado / dejado el mosaico. Esto es lo que usaremos para implementar el teleprotractor.

 public class Tile { // Useful to know when GameObjects enter and leave this tile public var OnObjectLeave:Signal = new Signal(GameObject); public var OnObjectEnter:Signal = new Signal(GameObject); public var position:Point; // Use Vector.<T>s, they're faster than plain Arrays private var properties:Vector.<TileProperty> = new Vector.<TileProperty>(); public function addProperty(property:TileProperty):void { properties.push(property); property.attachToTile(this); // Because we have no double dispatch :( } public function Update(deltaTime:Number):void { // This update function is kinda dumb, it just serves the purpose of // having a starting point for reacting to an object entering the tile if (someIndicationThatThisTileWasEntenetworking) { OnObjectEnter.dispatch(getObjectThatJustEntenetworking()); } if (someIndicationThatThisTileWasLeft) { OnObjectLeave.dispatch(getObjectThatJustLeft()); } for (var property in Properties) { if (property.NeedsUpdating) { property.Update(deltaTime); } } } } 

También verá el campo de properties allí, un vector de TileProperty s. Si desea codificar un comportamiento especial y poder duplicarlo en múltiples áreas (como un comportamiento de teletransportación), entonces subclasifica esto. Proporciona una function de Update(deltaTime) , información sobre el mosaico padre y una function reemplazable que le notifica cuando el padre cambia (bueno para grupos de objects) y un bool para alternar si recibe Update() llamado en cada fotogtwig o no.

Puse una function de actualización en el Mosaico, pero es solo para tener algún sentido en este ejemplo donde puedes seguir la cadena de detección de colisión -> activar la teletransportación. En un juego real, dejaría la responsabilidad de disparar las señales a una class de sistema de colisión más grande.

 public class TileProperty { public var NeedsUpdating:Boolean; private var parentTile:Tile; protected function get ParentTile():Tile { return parentTile; } public final function attachToTile(tile:Tile):void { var oldTile:Tile = parentTile; parentTile = tile; onNewParent(oldTile); } protected function onNewParent():void { // Override this } public function Update(deltaTime:Number):void { // Override this } } 

Así que ahora tenemos una class TeleportTriggerProperty, y aquí es donde realmente ocurre el teletransporte. Cuando se llama a OnNewParent (), agrega un oyente ( teleportObject ) a la señal OnObjectEnter. De esta forma, cada vez que un object ingresa en el mosaico al que está vinculado este TileProperty, puede teletransportar ese object.

 public class TeleportTriggerProperty extends TileProperty { private finalDestination:Point; public function TeleportTriggerProperty(destination:Point, group:int) { finalDestination = destination; } override protected function OnNewParent(oldTile:Tile):void { // I'm not sure if trying to remove a null listener, or // a non-existent listener throws exceptions // If it does, then wrap it in try-catch. // Or better yet, modify the library so it DOESN'T :P oldTile.OnObjectEnter.remove(teleportObject); parentTile.OnObjectEnter.add(teleportObject); } private function teleportObject(thing:GameObject):void { // Maybe in some scenario this OnObjectEnter could get called if a // chest gets placed on the tile, so it's good to confirm the Group. // And maybe you want to be able to configure it to only teleport // the player, or only enemies. if (thing.Group == GameObject.ALL_CHARACTERS) { // BAM! Teleported. Insert special effects as necessary. thing.position = finalDestination; } } } 

Entonces ahora tienes toda esta infraestructura. ¿Cómo se aplica esto al editor de niveles?

Bueno, fíjate que realmente lo único que necesita una TeleportTriggerProperty es una identificación de punto y de grupo. Estos son los dos campos que puede exponer en su editor de niveles. Entonces, presumiblemente, puede exportar su estructura de nivel en XML u otro formatting de datos para que se vea así (utilicé XML, ya que su naturaleza jerárquica tiene sentido en este escenario, y como 3 lo analiza de forma nativa):

 <Tile> <Position>24,42</Position> <Properties> <TeleportTriggerProperty destination="42,24" group="2" /> <TriggerMusicProperty track="NeverGonnaGiveYouUp.mp3" /> <BrutallyMurderPlayerInFutureProperty time="3m30s" /> </Properties> </Tile> 

Probablemente no sea ideal almacenar una gran jerarquía XML para cada mosaico en su juego, pero este fue el ejemplo más directo que se me ocurrió.

Luego, en su rutina de analizador, puede iterar y build sus TileProperties según sea necesario. Posiblemente podría automatizar este process con serialization. Aunque no he hecho la serialization en as3, así que eso es tarea para ti si estás interesado. Si no utiliza la serialization, le recomiendo tener unas pocas classs separadas responsables de analizar types específicos de elementos (un analizador de GameObject, un analizador de Tile, y tal vez incluso un analizador TileProperty por separado).

Llamé a esto como TileProperty con la esperanza de que pueda acomodar tanto acciones directas como tu problema de teleprotractor, o efectos de mosaico, como disminuir la salud de cualquier personaje que aparezca en él cada pocos segundos. Pero, puede ser mejor implementar un par de sistemas diferentes para hacer lo que desea. Depende de tu juego