Tutorial: Sistema de selección de unidades Año: 2012 V.: AS3
[e]
How to create a typical unit selection strategy games in flash as3
Para los que estén pensando en crear un juego de estrategia, aquí les dejo a modo de referencia unos ejemplos y una pequeña explicación de cómo crear un sistema de selección de unidades.
En un juego de estrategia, el sistema de selección de unidades permite, seleccionar una o varias unidades para realizar diferentes acciones, por ejemplo, ordenar que se dirijan a una posición, que ataquen, se agrupen o se pongan en formación.
En esto ejemplos no se pretende explicar en detalle todas las acciones derivadas de la selección, únicamente se explica de manera escueta el concepto de selección básico con propósito de que sirva como referencia.
(Las descargas de los ejemplos al final del post)
En el primer ejemplo se muestra como crear el típico rectángulo de selección básico mediante el método drawRect del paquete graphics.
Ejemplo 1:
Código ejemplo 1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import flash.events.Event; //Creamos la pizarra var pizarra:Shape = new Shape(); addChild(pizarra); //Detectamos cuando se presiona con el ratón stage.addEventListener(MouseEvent.MOUSE_DOWN,_onMouseDown); //Al presionar detectamos cuando el ratón se mueve o se suelta //y recolocamos la pizarra en la misma posición que el ratón function _onMouseDown(e:Event):void { pizarra.x = mouseX; pizarra.y = mouseY; stage.addEventListener(MouseEvent.MOUSE_MOVE,_onMouseMove); stage.addEventListener(MouseEvent.MOUSE_UP,_onMouseUp); } //Al moverse dibujamos el rectángulo desde la posición del ratón function _onMouseMove(e:Event):void { pizarra.graphics.clear(); pizarra.graphics.beginFill(0x0000FF,0.5); pizarra.graphics.drawRect(0, 0,mouseX - pizarra.x,mouseY - pizarra.y); pizarra.graphics.endFill(); } //Al soltar el ratón borramos el contenido de la pizarra y eliminamos los listeners function _onMouseUp(e:Event):void { pizarra.graphics.clear(); stage.removeEventListener(MouseEvent.MOUSE_UP,_onMouseUp); stage.removeEventListener(MouseEvent.MOUSE_MOVE,_onMouseMove); } |
En este segundo ejemplo se le implementa al ejemplo anterior la función encargada de comprobar que unidades han sido seleccionadas. En primer lugar agregamos al displayList varias instancias de la clase Char de la biblioteca y las almacenamos en un array para poder tener acceso a ellas posteriormente. En segundo lugar comprobamos la polaridad de los ejes X e Y del rectángulo en el momento de soltar el ratón. En tercer lugar se comprueba basándonos en la polaridad anterior si las unidades de la escena se encuentran dentro del área del rectángulo. En cuarto lugar se seleccionan las unidades que se encuentren dentro del área. Para destacar las unidades seleccionadas del resto, la selección consiste a modo de ejemplo en pasar cada unidad (instancia Char) del fotograma uno al dos, donde existe una imagen igual que en el fotograma uno pero pintada de rojo.
Comportamiento: Si se hace clic en el escenario se deseleccionan las unidades seleccionadas, si existen unidades seleccionadas y se realiza una nueva selección se deseleccionan las unidades anteriormente seleccionadas.
Ejemplo 2:
Código ejemplo 2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
import flash.events.Event; var CHARS:Array = new Array(); /*Instanciamos 15 unidades de la clase Char de la biblioteca, guardamos una referencia de cada una en el array CHARS, las añadimos al displayList y las colocamos aleatoriamente en el escenario.*/ for (var n:int = 0; n<15; n++) { var char:Char = new Char(); CHARS.push(char); addChild(char); var stW:int = Math.random() * stage.stageWidth; var stH:int = Math.random() * stage.stageHeight; if (stW - char.width <0) { char.x = 0 + char.width / 2; } else if (stW + char.width > stW) { char.x = stW - char.width / 2; } if (stH - char.height <0) { char.y = 0 + char.height / 2; } else if (stH + char.height > stH) { char.y = stH - char.height / 2; } } //Recolocamos el texto por encima de las unidades addChild(debug); //Creamos la pizarra donde se dibujara el área de selección var pizarra:Shape = new Shape(); addChild(pizarra); /* Guardara la polaridad de los ejes X e Y dependiendo de la selección */ var polX:String; var polY:String; stage.addEventListener(MouseEvent.MOUSE_DOWN,_onMouseDown); //Al presionar posicionamos la pizarra en la misma posición que el ratón. function _onMouseDown(e:Event):void { pizarra.x = mouseX; pizarra.y = mouseY; stage.addEventListener(MouseEvent.MOUSE_MOVE,_onMouseMove); stage.addEventListener(MouseEvent.MOUSE_UP,_onMouseUp); } /* Al mover el ratón dibujamos el rectángulo de selección en la pizarra Y almacenamos la polaridad en polX y polY */ function _onMouseMove(e:Event):void { pizarra.graphics.clear(); pizarra.graphics.beginFill(0x0000FF,0.5); pizarra.graphics.drawRect(0, 0,mouseX - pizarra.x,mouseY - pizarra.y); pizarra.graphics.endFill(); if (mouseX >=pizarra.x) { polX = "+"; } else { polX = "-"; } if (mouseY >=pizarra.y) { polY = "+"; } else { polY = "-"; } } /* Al soltar el ratón comprobamos si existen unidades seleccionadas y borramos la pizarra */ function _onMouseUp(e:Event):void { checkSelectionArea(); pizarra.graphics.clear(); stage.removeEventListener(MouseEvent.MOUSE_UP,_onMouseUp); stage.removeEventListener(MouseEvent.MOUSE_MOVE,_onMouseMove); } function checkSelectionArea():void { //Si hubiera alguna unidad seleccionada la deseleccionamos for (var s:int = 0; s<CHARS.length; s++) { CHARS[s].gotoAndStop(1); } var CHARS_SELECTED:Array = new Array(); var totalChars:int = CHARS.length; for (var n:int = 0; n<totalChars; n++) { //Recorremos todas las unidades y comprobamos si alguna unidad está dentro del área de la pizarra var ch:MovieClip = CHARS[n]; if (polX == "+" && polY == "+" ) { if (ch.x >= pizarra.x && ch.x <= pizarra.x + pizarra.width && ch.y >= pizarra.y && ch.y <= pizarra.y + pizarra.height) { CHARS_SELECTED.push(ch); } } else if (polX == "-" && polY == "+" ) { if (ch.x >= pizarra.x - pizarra.width && ch.x <= pizarra.x && ch.y >= pizarra.y && ch.y <= pizarra.y + pizarra.height) { CHARS_SELECTED.push(ch); } } else if (polX == "+" && polY == "-" ) { if (ch.x >= pizarra.x && ch.x <= pizarra.x + pizarra.width && ch.y >= pizarra.y - pizarra.height && ch.y <= pizarra.y) { CHARS_SELECTED.push(ch); } } else if (polX == "-" && polY == "-" ) { if (ch.x >= pizarra.x - pizarra.width && ch.x <= pizarra.x && ch.y >= pizarra.y - pizarra.height && ch.y <= pizarra.y) { CHARS_SELECTED.push(ch); } } } //Si existen unidades dentro del área de selección las seleccionamos if (CHARS_SELECTED.length > 0) { for (var d:int = 0; d<CHARS_SELECTED.length; d++) { CHARS_SELECTED[d].gotoAndStop(2); } } debug.num.text = String(CHARS_SELECTED.length); } |
En el tercer ejemplo se extrae el funcionamiento del selector dentro de una clase llamada SelectorUnit, y se implementa la opción de seleccionar las unidades individualmente al hacer clic encima de ellas sin necesidad de arrastrar el selector. También se crea la clase Char para dotarla de movimiento y poder desplazarse a la posición indicada con el ratón.
Ejemplo 3:
Selecciona una unidad haciendo clic encima o varias unidades arrastrando el ratón, una vez seleccionadas haz clic en algún punto del mapa para que se desplacen hasta el. Para deseleccionar las unidades seleccionadas arrastra el selector en alguna zona donde no existan unidades.
Código ejemplo 3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
import flash.events.Event; //Importamos la clase SelectorUnits import SelectorUnits; /* Nota la clase SelectorUnits no se encarga de añadir ni eliminar unidades del displayList. Cada vez que se añada una nueva instancia de una unidad al displayList se debe añadir la referencia a la clase de dicha unidad, en el momento de eliminar una unidad del displayList debe eliminarse también su referencia de la clase */ //Creamos una instancia var sUni:SelectorUnits = new SelectorUnits(stage); addChild(sUni); //En este array guardaremos las referencias de las instancias de las unidades de la clase Char creadas var UNITS:Array = new Array(); /* Utilizamos la funcion addUnitsInStage para añadir varias instancias de unidades de la clase Char al escenario en posiciones aleatorias */ function addUnitsInStage(Num:int):void { for (var n:int = 0; n<Num; n++) { var unit:Char = new Char(); UNITS.push(unit); addChild(unit); randomLocation(unit); } //Posicionamos el selector por encima de las unidades addChild(sUni); } /* Utilizamos la funcion addUnitInStage para añadir una sola instancia la funcion devuelve un array con las referencia de la instancia creada */ function addUnitInStage():Char { var unit:Char = new Char(); UNITS.push(unit); addChild(unit); randomLocation(unit); //Posicionamos el selector por encima de las unidades addChild(sUni); return unit; } //Posiciona las unidades aleatoriamente en el escenario function randomLocation(unit:Char):void { var stW:int = Math.random() * stage.stageWidth; var stH:int = Math.random() * stage.stageHeight; if (stW - unit.width <0) { unit.x = 0 + unit.width / 2; } else if (stW + unit.width > stW) { unit.x = stW - unit.width / 2; } if (stH - unit.height <0) { unit.y = 0 + unit.height / 2; } else if (stH + unit.height > stH) { unit.y = stH - unit.height / 2; } } //Creamos 15 unidades addUnitsInStage(15); /* Añadimos las referencias de las instancias de las unidades creadas a la clase SelectorUnits mediante su método addUnits pasando como parámetro el array con las referencias de las unidades creadas */ sUni.addUnits(UNITS); /* Si se creara una nueva unidad es posible añadirla individualmente a las demás de la siguiente manera: sUni.addUnits([instanceRef]); */ /* Asignamos una función como referencia a la propiedad onEventsCaller del la clase SelectorUnits. Esta función será llamada cada vez que se realice una selección, una deselección o cuando existan unidades seleccionadas y se haya echo clic en alguna zona del mapa. El metodo getSelectedUnits devuelve un array con las referencias de las unidades seleccionadas o deseleccionadas */ sUni.onEventsCaller = onEventsCaller; function onEventsCaller(eType:String):void { //Recuperamos las unidades afectadas var units:Array = sUni.getSelectedUnits(); var num:int = units.length; //Recorremos todas las unidades for (var n:int = 0; n<num; n++) { if (eType == "SELECT") { //Si es select indicamos graficamente que unidades estan seleccionadas var sndYes:SoundYes = new SoundYes(); sndYes.play(); units[n].gotoAndStop(2); } else if (eType == "DESELECT") { //Si es deselect deseleccionamos la unidad units[n].gotoAndStop(1); } else if (eType == "MOVE") { /* Si se ha hecho clic en el mapa y existen unidades seleccionadas entonces se lanza el evento MOVE, recuperamos la posición del ratón en el momento del clic y le decimos a la unidad que vaya a esa posición */ units[n].moveTos(mouseX,mouseY); var sndOk:SoundOk = new SoundOk(); sndOk.play(); } } } /* Al resetear borramos todas las instancias de las unidades del escenario y sus referencias guardadas en la clase SelectorUnit, seguidamente creamos de nuevo las unidades y añadimos su referencias nuevamente a la clase SelectorUnit*/ reset_btn.addEventListener(MouseEvent.CLICK,onReset); function onReset(e:Event):void { onRemoveAll(); addUnitsInStage(15); sUni.addUnits(UNITS); } removeAll_btn.addEventListener(MouseEvent.CLICK,onRemoveAll); /* Al borrar todo eliminamos todas las instancias de las unidades del displayList y sus referencias guardadas en la claseSelectorUnit y reinicializamos el array */ function onRemoveAll(e:Event = null):void { for (var n:int = 0; n<UNITS.length; n++) { removeChild(UNITS[n]); } sUni.removeAllUnits(); UNITS = new Array(); } removeSelected_btn.addEventListener(MouseEvent.CLICK,onRemoveSelected); /* Al borrar las instancias de las unidades seleccionadas del displayList, buscamos las instancias seleccionadas en el array UNITS y las eliminamos del displaylsit y su referencia en el array, después borramos su referencia de la clase SelectorUnit*/ function onRemoveSelected(e:Event):void { var USEL:Array = sUni.getSelectedUnits(); for (var n:int = 0; n<USEL.length; n++) { for (var r:int = 0; r<UNITS.length; r++) { if (USEL[n] == UNITS[r]) { removeChild(UNITS[r]); UNITS.splice(r, 1); break; } } } sUni.removeSelected(); } addUnit_btn.addEventListener(MouseEvent.CLICK,onAddUnit); function onAddUnit(e:Event):void { sUni.addUnits([addUnitInStage()]); } |
[download id=»37″ format=»2″]
4 comments on “AS3 – Tutorial – Como crear un sistema de seleccion de unidades tipico de juegos de estrategia en flash as3”
Hola Javier, he leido practicamente casi toda tu pagina referido a AS3 y quisiera que me ayudes en un tema.
new Tween(addChild (nuevomisil), «x», None.easeInOut, centro.x, mouseX, 1, true);
new Tween(addChild (nuevomisil), «y», None.easeInOut, centro.y, mouseY, 1, true);
Con lo que puse hago que nuevomisil salga de centro y llegue a donde señalo con el raton.
pero como logro que aparte que salga de centro y llegue donde apunto con el ratón, el misil continue con su trayectoria.
Quisas ni siquiera se hace con Tween.
Hola Néstor prueba a ver con esto, aunque quizás utilizar tween no sería lo más adecuado.
Hola soy Néstor:
Realmente eres un maestro, hiciste lo que te he pedido.
Lo malo es que no lo dije bien. En realidad es un juego, un personaje tirando un misil, por eso el misil tiene que seguir su trayectoria asta donde yo se lo indique, su velocidad tiene que ser constante (la que yo le indique) y no se tiene que reiniciar el disparo al volverle hacer clic, tiene que aparecer otro disparo.
Como soy muy nuevo con el AS3 soy solo un novato y me la paso leyendo mucho, probando diferentes cosas y ahora deje el tween y estoy probando esto.
Pero aun no logro que el tiro valla a la misma velocidad sin importar en que distancia apunte entre "inicio" y donde le hice clic, tampoco logro que se deje de reiniciar el tiro con cada click que doy, tampoco logre que cada vez que se haga clic se produzca un nuevo tiro sin que desaparezca el que esta en ejecución cosa que haya mas de 1 bolaFuego a la vez en la pantalla.
Hola Néstor, para hacer lo que quieres tienes 2 opciones, una sencillita es controlar toda la inteligencia del arma desde el código principal y la otra opción quizás la más adecuada en el caso de que haya previsión de que el juego crezca pues sería la de abstraer toda la inteligencia o el código encargado de controlar el comportamiento de la munición fuera del código principal en una clase BolaFuego.
De todas maneras si el juego va a ser sencillito puedes programar toda la inteligencia de bola Fuego en el código principal sin problemas.
Para hacer que cada bola de fuego sea independiente y no se reinicie cada vez que haces click con el ratón lo que tienes que hacer es que cada vez que hagas click con el ratón es crear una instancia nueva de esa bola de fuego y guardar la instancia en un Array.
A partir de ahí necesitas un bucle (EnterFrame)que vaya recorriendo el array y vaya incrementando la posición X e Y de cada objeto bola de fuego que se encuentre en el array.
Así consigues que si disparas 5 bolas seguidas todas las bolas se muevan a la vez y de manera independiente. Si te fijas en el ejemplo del selector de unidades de arriba del esta entrada del blog utilizo una técnica parecida para desplazar las unidades, en este caso se puede extrapolar para el caso de las armas.
Como cada bola de fuego deberá tener un vida distancia predeterminada o aleatoria cuando dicho recorrido se termine, el tiempo se acabe o haya impactado con un objetivo deberás borrar o eliminar la instancia de la bola de fuego en cuestión del array y posteriormente del display List, deberás asegurarte que se borra cualquier referencia a la instancia del objeto o clase bola de fuego que vayas eliminando para que pasen a ser aptas para el recolector de basura y no se te queden ocupando memoria.
Cuando pueda mirare de hacerte un ejemplo y te lo paso que ahora me encuentro en el trabajo