Spanish (Español) translation by Juliana Carvajal (you can also view the original English article)
En este tutorial me gustaría mostrarte lo fácil que es crear un juego clásico de "Serpiente" en Flash. ¡Intentaré explicarte todo fácilmente, paso a paso, para que puedas desarrollar el juego según tus necesidades! El juego se desarrollará en AS3 y usaré el IDE de FlashDevelop.
Introducción
El juego no será complejo. Siempre que golpeemos una pared, se reiniciará el juego. Después de comer una manzana, la serpiente crecerá y aparecerá una "nueva" manzana. (En realidad, será la misma manzana, pero lo explicaré más adelante).
Uno de los aspectos más importantes del juego es la reacción del código a los eventos KEY_DOWN. La serpiente solo cambiará su dirección después de que haya pasado una marca, no inmediatamente después de presionar una tecla. Esto significa que, si la serpiente va hacia la derecha y tu presionas hacia abajo y hacia la izquierda muy rápido, la serpiente bajará, no bajará Y se irá a la izquierda. Sin esta 'característica', la serpiente nos permitiría ir a la izquierda mientras vamos a la derecha, lo que significaría que se golpea a sí misma.
¡Veamos ya el Juego!
Echemos un vistazo al resultado final hacia el que trabajaremos:
Paso 1: Creación del Proyecto
En FlashDevelop, crea un nuevo proyecto y, dentro de la carpeta 'src', crea una carpeta 'com'. En la carpeta 'com' crea una nueva clase y llámala 'Element.as'.
Establece las dimensiones del proyecto en 600x600px.

Paso 2: Espera ... ¿Qué es un Elemento?
La serpiente está formada por cuadrados azules, a los que llamo elementos. Crearemos un Element Class, que dibuja el elemento. La manzana roja también será un elemento, así que ampliaremos el código con algunas líneas más.
Por lo tanto, no crearemos una nueva clase para la manzana. (Pero si realmente lo deseas, puede hacerlo).
Paso 3: Escribir el Element Class
El Element class crea un cuadrado. No lo dibuja en el escenario, simplemente lo crea. El punto de registro del elemento, la posición a la que se refieren sus -coordenadas x y y-, está en la parte superior izquierda.
Después de abrir Element.as, verás algo como esto:
1 |
package com |
2 |
{
|
3 |
/**
|
4 |
* ...
|
5 |
* @author Fuszenecker Zsombor
|
6 |
*/
|
7 |
public class Element |
8 |
{
|
9 |
|
10 |
public function Element() |
11 |
{
|
12 |
|
13 |
}
|
14 |
|
15 |
}
|
16 |
}
|
Primero, necesitamos esto para extender la clase Shape, por lo que podemos usar el objeto graphics
para dibujar el cuadrado. Después de esto, crea dos variables: una para la dirección (si es parte de la serpiente) y otra para el valor de puntuación (si es una manzana), y luego cambia los parámetros de la función constructora:
1 |
package com |
2 |
{
|
3 |
import flash.display.Shape; |
4 |
|
5 |
public class Element extends Shape |
6 |
{
|
7 |
protected var _direction:String; |
8 |
//IF IT IS AN APPLE ->
|
9 |
protected var _catchValue:Number; |
10 |
|
11 |
//color,alpha,width,height
|
12 |
public function Element(_c:uint,_a:Number,_w:Number,_h:Number) |
13 |
{
|
14 |
|
15 |
}
|
16 |
}
|
17 |
}
|
Ahora completa la función con algo de código:
1 |
package com |
2 |
{
|
3 |
import flash.display.Shape; |
4 |
|
5 |
public class Element extends Shape |
6 |
{
|
7 |
protected var _direction:String; |
8 |
//IF IT IS AN APPLE ->
|
9 |
protected var _catchValue:Number; |
10 |
|
11 |
//color,alpha,width,height
|
12 |
public function Element(_c:uint,_a:Number,_w:Number,_h:Number) |
13 |
{
|
14 |
graphics.lineStyle(0, _c, _a); |
15 |
graphics.beginFill(_c, _a); |
16 |
graphics.drawRect(0, 0, _w, _h); |
17 |
graphics.endFill(); |
18 |
|
19 |
_catchValue = 0; |
20 |
}
|
21 |
}
|
22 |
}
|
Ahora, cada vez que creamos un elemento, dibujará un rectángulo y establecerá el valor de puntuación del elemento en 0 de forma predeterminada. (No pondrá el rectángulo en el escenario, solo lo dibujará dentro de sí mismo. Observa que no hemos llamado a la función addChild ()
).
Terminemos esta clase y luego finalmente podemos probar cuánto hemos hecho ya:
1 |
package com |
2 |
{
|
3 |
import flash.display.Shape; |
4 |
|
5 |
public class Element extends Shape |
6 |
{
|
7 |
protected var _direction:String; |
8 |
//IF IT IS AN APPLE ->
|
9 |
protected var _catchValue:Number; |
10 |
|
11 |
//color,alpha,width,height
|
12 |
public function Element(_c:uint,_a:Number,_w:Number,_h:Number) |
13 |
{
|
14 |
graphics.lineStyle(0, _c, _a); |
15 |
graphics.beginFill(_c, _a); |
16 |
graphics.drawRect(0, 0, _w, _h); |
17 |
graphics.endFill(); |
18 |
|
19 |
_catchValue = 0; |
20 |
}
|
21 |
|
22 |
//ONLY USED IN CASE OF A PART OF THE SNAKE
|
23 |
public function set direction(value:String):void |
24 |
{
|
25 |
_direction = value; |
26 |
}
|
27 |
public function get direction():String |
28 |
{
|
29 |
return _direction; |
30 |
}
|
31 |
|
32 |
//ONLY USED IN CASE OF AN APPLE
|
33 |
public function set catchValue(value:Number):void |
34 |
{
|
35 |
_catchValue = value; |
36 |
}
|
37 |
public function get catchValue():Number |
38 |
{
|
39 |
return _catchValue; |
40 |
}
|
41 |
}
|
42 |
|
43 |
}
|
Creamos cuatro funciones para cambiar las direcciones y el valor de la manzana. Lo logramos usando setters y getters. ¡Más sobre Setters/Getters en este artículo!
Paso 4: Probar el Element Class
Abra Main.as ahora.
Importe la clase com.Element
y crea un elemento en la función init ()
:
1 |
package
|
2 |
{
|
3 |
import flash.display.Sprite; |
4 |
import flash.events.Event; |
5 |
import com.Element; |
6 |
|
7 |
public class Main extends Sprite |
8 |
{
|
9 |
public function Main() |
10 |
{
|
11 |
if(stage) |
12 |
addEventListener(Event.ADDED_TO_STAGE, init); |
13 |
else
|
14 |
init(); |
15 |
}
|
16 |
|
17 |
private function init(e:Event = null):void |
18 |
{
|
19 |
var testElement:Element = new Element(0x00AAFF, 1, 10, 10); |
20 |
testElement.x = 50; |
21 |
testElement.y = 50; |
22 |
this.addChild(testElement); |
23 |
|
24 |
}
|
25 |
|
26 |
}
|
27 |
}
|
Primero creamos la variable testElement
que contiene nuestro elemento. Creamos un nuevo elemento y lo asignamos a nuestra variable testElement
. Ten en cuenta los argumentos que te pasamos: primero le damos un color, luego el alpha, el ancho y el alto. Si observas la función Element en el Element class, puedes ver cómo usa estos datos para dibujar el rectángulo.
Después de crear el elemento, lo posicionamos y lo colocamos en el escenario!
Paso 5: Configurar las Variables
Mira el siguiente código. Escribí las funciones de las variables junto a ellas (observa que también importamos las clases necesarias):
1 |
package
|
2 |
{
|
3 |
import flash.display.Sprite; |
4 |
import flash.text.TextField; |
5 |
import flash.utils.Timer; |
6 |
import flash.events.TimerEvent; |
7 |
import flash.ui.Keyboard; |
8 |
import flash.events.KeyboardEvent; |
9 |
import flash.events.MouseEvent; |
10 |
import flash.events.Event; |
11 |
|
12 |
import com.Element; |
13 |
|
14 |
public class Main extends Sprite |
15 |
{
|
16 |
|
17 |
//DO NOT GIVE THESE VARS A VALUE HERE!
|
18 |
//Give them their values in the init() function.
|
19 |
private var snake_vector:Vector.<Element>; //the snake's parts are held in here |
20 |
private var markers_vector:Vector.<Object>; //the markers are held in here |
21 |
private var timer:Timer; |
22 |
private var dead:Boolean; |
23 |
private var min_elements:int; //holds how many parts the snake should have at the beginning |
24 |
private var apple:Element; //Our apple |
25 |
private var space_value:Number; //space between the snake's parts |
26 |
private var last_button_down:uint; //the keyCode of the last button pressed |
27 |
private var flag:Boolean; //is it allowed to change direction? |
28 |
private var score:Number; |
29 |
private var score_tf:TextField; //the Textfield showing the score |
30 |
|
31 |
public function Main() |
32 |
{
|
33 |
if(stage) |
34 |
addEventListener(Event.ADDED_TO_STAGE, init); |
35 |
else
|
36 |
init(); |
37 |
}
|
38 |
|
39 |
private function init(e:Event = null):void |
40 |
{
|
41 |
snake_vector = new Vector.<Element>; |
42 |
markers_vector = new Vector.<Object>; |
43 |
space_value = 2; //There will be 2px space between every Element |
44 |
timer = new Timer(50); //Every 50th millisecond, the moveIt() function will be fired! This will set the SPEED of the snake |
45 |
dead = false; |
46 |
min_elements = 10; //We will begin with 10 elements. |
47 |
apple = new Element(0xFF0000, 1, 10, 10); //red, not transparent, width:10, height: 10; |
48 |
apple.catchValue = 0; //pretty obvious - the score of the apple |
49 |
last_button_down = Keyboard.RIGHT; //The first direction of the snake is set in this variable |
50 |
score = 0; |
51 |
score_tf = new TextField(); //this is the TextField which shows our score. |
52 |
this.addChild(score_tf); |
53 |
}
|
54 |
}
|
55 |
}
|
La variable más importante es snake_vector
. Pondremos cada Elemento de la serpiente en este Vector.
Luego está el markers_vector
. Usaremos marcadores para establecer la dirección de las partes de la serpiente. Cada objeto de este Vector tendrá una posición y un tipo. El tipo nos dirá si la serpiente debe ir a la derecha, izquierda, arriba o abajo después de "golpear" el objeto. (No chocarán, solo se comprobará la posición de los marcadores y las partes de la serpiente).
Como ejemplo, si presionamos ABAJO, se creará un objeto. La x y y de este objeto serán las coordenadas x y y de la cabeza de la serpiente, y el tipo será "Down". Siempre que la posición de uno de los elementos de la serpiente sea la misma que la de este objeto, la dirección de los elementos de la serpiente se establecerá en "Down".
¡Lee los comentarios junto a las variables para comprender qué hacen las otras variables!
Paso 6: Escribir la Función attachElement ()
La función attachElement()
tomará cuatro parámetros: el nuevo elemento serpiente, las coordenadas x y y, y la dirección de la última parte de la serpiente.
1 |
private function attachElement(who:Element,lastXPos:Number = 0,lastYPos:Number = 0,dirOfLast:String = "R"):void |
2 |
{
|
3 |
|
4 |
}
|
Antes de poner el elemento en el escenario debemos colocarlo. Pero para esto necesitamos la dirección del último elemento de la serpiente, para saber si el nuevo elemento tiene que estar encima, debajo o al lado de este.
Después de verificar la dirección y establecer la posición, podemos agregarlo al escenario.
1 |
private function attachElement(who:Element,lastXPos:Number = 0,lastYPos:Number = 0,dirOfLast:String = "R"):void |
2 |
{
|
3 |
if (dirOfLast == "R") |
4 |
{
|
5 |
who.x = lastXPos - snake_vector[0].width - space_value; |
6 |
who.y = lastYPos; |
7 |
}
|
8 |
else if(dirOfLast == "L") |
9 |
{
|
10 |
who.x = lastXPos + snake_vector[0].width + space_value; |
11 |
who.y = lastYPos; |
12 |
}
|
13 |
else if(dirOfLast == "U") |
14 |
{
|
15 |
who.x = lastXPos; |
16 |
who.y = lastYPos + snake_vector[0].height + space_value; |
17 |
}
|
18 |
else if(dirOfLast == "D") |
19 |
{
|
20 |
who.x = lastXPos; |
21 |
who.y = lastYPos - snake_vector[0].height - space_value; |
22 |
}
|
23 |
this.addChild(who); |
24 |
}
|
Ahora podemos usar esta función en la función init()
:
1 |
for(var i:int=0;i<min_elements;++i) |
2 |
{
|
3 |
snake_vector[i] = new Element(0x00AAFF,1,10,10); |
4 |
snake_vector[i].direction = "R"; //The starting direction of the snake |
5 |
if (i == 0)//first snake element |
6 |
{
|
7 |
//you have to place the first element on a GRID. (now: 0,0)
|
8 |
//[possible x positions: (snake_vector[0].width+space_value)*<UINT> ]
|
9 |
attachElement(snake_vector[i],0,0,snake_vector[i].direction) |
10 |
snake_vector[0].alpha = 0.7; |
11 |
}
|
12 |
else
|
13 |
{
|
14 |
attachElement(snake_vector[i], snake_vector[i - 1].x, snake_vector[i - 1].y, snake_vector[i - 1].direction); |
15 |
}
|
16 |
}
|
Creamos los primeros 10 elementos y establecemos la dirección de ellos en 'R' (derecha). Si es el primer elemento, llamamos attachElement()
y cambiamos un poco su alpha (por lo que la "cabeza" es de un color ligeramente más claro).
Si deseas establecer la posición en otro lugar, ten en cuenta lo siguiente: la serpiente debe colocarse en una cuadrícula, de lo contrario se vería mal y no funcionaría. Si deseas cambiar la posición x y y puedes hacerlo de la siguiente manera:
Estableciendo la posición x:
(snake_vector[0].width+space_value)*[UINT]
, donde debes reemplazar [UINT] con un número entero positivo.
Estableciendo la posición y:
(snake_vector[0].height+space_value)*[UINT]
, donde debes reemplazar [UINT] con un número entero positivo.
Vamos a cambiarlo a esto:
1 |
if (i == 0)//first snake element |
2 |
{
|
3 |
//you have to place the first element on a GRID. (now: 0,0)
|
4 |
//[possible x positions: (snake_vector[0].width+space_value)*<UINT>]
|
5 |
attachElement( |
6 |
snake_vector[i], |
7 |
(snake_vector[0].width+space_value)*20, |
8 |
(snake_vector[0].height+space_value)*10, |
9 |
snake_vector[i].direction |
10 |
); |
11 |
snake_vector[0].alpha = 0.7; |
12 |
}
|
Y el primer elemento de la serpiente se coloca en el espacio 20 en la cuadrícula x- y en el décimo espacio en la cuadrícula y.
Esto es lo que tenemos hasta ahora:
1 |
package
|
2 |
{
|
3 |
import flash.display.Sprite; |
4 |
import flash.text.TextField; |
5 |
import flash.utils.Timer; |
6 |
import flash.events.TimerEvent; |
7 |
import flash.ui.Keyboard; |
8 |
import flash.events.KeyboardEvent; |
9 |
import flash.events.MouseEvent; |
10 |
import flash.events.Event; |
11 |
|
12 |
import com.Element; |
13 |
|
14 |
public class Main extends Sprite |
15 |
{
|
16 |
|
17 |
//DO NOT GIVE THEM A VALUE HERE! Give them a value in the init() function
|
18 |
private var snake_vector:Vector.<Element>; //the snake's parts are held in here |
19 |
private var markers_vector:Vector.<Object>; //the markers are held in here |
20 |
private var timer:Timer; |
21 |
private var dead:Boolean; |
22 |
private var min_elements:int; //holds how many parts should the snake have at the beginning |
23 |
private var apple:Element; //Our apple |
24 |
private var space_value:Number; //space between the snake parts |
25 |
private var last_button_down:uint; //the keyCode of the last button pressed |
26 |
private var flag:Boolean; //is it allowed to change direction? |
27 |
private var score:Number; |
28 |
private var score_tf:TextField; //the Textfield showing the score |
29 |
|
30 |
public function Main() |
31 |
{
|
32 |
if(stage) |
33 |
addEventListener(Event.ADDED_TO_STAGE, init); |
34 |
else
|
35 |
init(); |
36 |
}
|
37 |
|
38 |
private function init(e:Event = null):void |
39 |
{
|
40 |
snake_vector = new Vector.<Element>; |
41 |
markers_vector = new Vector.<Object>; |
42 |
space_value = 2; |
43 |
timer = new Timer(50); //Every 50th millisecond, the moveIt() function will be fired! |
44 |
dead = false; |
45 |
min_elements = 10; //We will begin with 10 elements. |
46 |
apple = new Element(0xFF0000, 1,10, 10); //red, not transparent, width:10, height: 10; |
47 |
apple.catchValue = 0; //pretty obvious |
48 |
last_button_down = Keyboard.RIGHT; //The first direction of the snake is set in this variable |
49 |
score = 0; |
50 |
score_tf = new TextField(); //this is the TextField which shows our score. |
51 |
this.addChild(score_tf); |
52 |
|
53 |
for(var i:int=0;i<min_elements;++i) |
54 |
{
|
55 |
snake_vector[i] = new Element(0x00AAFF,1,10,10); |
56 |
snake_vector[i].direction = "R"; //The starting direction of the snake |
57 |
if (i == 0)//first snake element |
58 |
{
|
59 |
//you have to place the first element on a GRID. (now: 0,0) [possible x positions: (snake_vector[0].width+space_value)*<UINT> ]
|
60 |
attachElement(snake_vector[i], (snake_vector[0].width + space_value) * 20, (snake_vector[0].height + space_value) * 10, snake_vector[i].direction); |
61 |
snake_vector[0].alpha = 0.7; |
62 |
}
|
63 |
else
|
64 |
{
|
65 |
attachElement(snake_vector[i], snake_vector[i - 1].x, snake_vector[i - 1].y, snake_vector[i - 1].direction); |
66 |
}
|
67 |
}
|
68 |
}
|
69 |
|
70 |
private function attachElement(who:Element,lastXPos:Number = 0,lastYPos:Number = 0,dirOfLast:String = "R"):void |
71 |
{
|
72 |
if (dirOfLast == "R") |
73 |
{
|
74 |
who.x = lastXPos - snake_vector[0].width - space_value; |
75 |
who.y = lastYPos; |
76 |
}
|
77 |
else if(dirOfLast == "L") |
78 |
{
|
79 |
who.x = lastXPos + snake_vector[0].width + space_value; |
80 |
who.y = lastYPos; |
81 |
}
|
82 |
else if(dirOfLast == "U") |
83 |
{
|
84 |
who.x = lastXPos; |
85 |
who.y = lastYPos + snake_vector[0].height + space_value; |
86 |
}
|
87 |
else if(dirOfLast == "D") |
88 |
{
|
89 |
who.x = lastXPos; |
90 |
who.y = lastYPos - snake_vector[0].height - space_value; |
91 |
}
|
92 |
this.addChild(who); |
93 |
}
|
94 |
}
|
95 |
}
|
Paso 7: Escribir la Función placeApple()
Esta función hace lo siguiente:
- Comprueba si la manzana fue atrapada. Para esto usaremos el parámetro
caught
y estableceremos su valor predeterminado entrue
, en caso de que no pasemos ningún valor como parámetros en el futuro. Si fue detectado, agrega 10 al valor de puntuación de la manzana (por lo que la siguiente manzana vale más). - Después de esto, la manzana debe reposicionarse (no creamos manzanas nuevas) en una posición de cuadrícula aleatoria.
- Si se coloca sobre la serpiente, debemos colocarla en otro lugar.
- Si aún no está en el escenario, la colocamos allí.
1 |
private function placeApple(caught:Boolean = true):void |
2 |
{
|
3 |
if (caught) |
4 |
apple.catchValue += 10; |
5 |
|
6 |
var boundsX:int = (Math.floor(stage.stageWidth / (snake_vector[0].width + space_value)))-1; |
7 |
var randomX:Number = Math.floor(Math.random()*boundsX); |
8 |
|
9 |
var boundsY:int = (Math.floor(stage.stageHeight/(snake_vector[0].height + space_value)))-1; |
10 |
var randomY:Number = Math.floor(Math.random()*boundsY); |
11 |
|
12 |
apple.x = randomX * (apple.width + space_value); |
13 |
apple.y = randomY * (apple.height + space_value); |
14 |
|
15 |
for(var i:uint=0;i<snake_vector.length-1;i++) |
16 |
{
|
17 |
if(snake_vector[i].x == apple.x && snake_vector[i].y == apple.y) |
18 |
placeApple(false); |
19 |
}
|
20 |
if (!apple.stage) |
21 |
this.addChild(apple); |
22 |
}
|
Habrá algunas matemáticas aquí, pero si lo piensas bien, debes entender por qué es así. Simplemente dibújalo en un papel si es necesario.
-
boundsX
contendrá cuántos elementos se pueden dibujar en una fila. -
randomX
toma esteboundsX
, lo multiplica por un Número entre cero y uno, y lo reduce. SiboundsX
es 12 y el número aleatorio es 0.356, entonces elfloor(12*0.356)
es 4, por lo que la manzana se colocará en el cuarto lugar de la cuadrícula x. -
boundsY
contendrá cuántos elementos se pueden dibujar en una columna. -
randomY
toma esteboundsY
, lo multiplica por un Número entre cero y uno, y lo reduce. - Luego establecemos la posición x y y en estos números.
En el ciclo for, verificamos si las nuevas posiciones x y y de la manzana son idénticas a cualquiera de los elementos snake_vectors
. Si es así, volvemos a llamar a la función placeApple()
(función recursiva) y establecemos el parámetro en false
. (Lo que significa que la manzana no fue atrapada, solo necesitamos reposicionarla)
(apple.stage)
devuelve verdadero si la manzana está en el escenario. Usamos el '!' operador para invertir ese valor, por lo que si NO está en el escenario, lo colocamos allí.
Lo último que debemos hacer es llamar a la función placeApple()
al final de la función init()
.
1 |
private function init(e:Event = null):void |
2 |
{
|
3 |
/*
|
4 |
.
|
5 |
.
|
6 |
.
|
7 |
*/
|
8 |
|
9 |
placeApple(false); |
10 |
}
|
Observe que pasamos false
como parámetro. Es lógico, porque todavía no capturamos la manzana en la función init()
. Solo lo capturaremos en la función moveIt()
.
Ahora solo quedan tres funciones más para escribir: las funciones directionChanged()
, moveIt()
y gameOver()
.
Paso 8: Iniciar la función moveIt()
La función moveIt()
es responsable de todo el movimiento. Esta función comprobará los límites y comprobará si hay un objeto en la posición x y y de la cabeza de la serpiente. También buscará la manzana en esta posición.
Para todo esto, usaremos nuestra variable de temporizador.
Agrega dos líneas más al final de la función init()
:
1 |
timer.addEventListener(TimerEvent.TIMER,moveIt); |
2 |
timer.start(); |
Mira los comentarios en el código fuente para ver qué bloque de código hace qué.
1 |
private function moveIt(e:TimerEvent):void |
2 |
{
|
3 |
if (snake_vector[0].x == apple.x && snake_vector[0].y == apple.y) |
4 |
{
|
5 |
//This code runs if the snakes heads position and the apples position are the same
|
6 |
}
|
7 |
|
8 |
if (snake_vector[0].x > stage.stageWidth-snake_vector[0].width || snake_vector[0].x < 0 || snake_vector[0].y > stage.stageHeight-snake_vector[0].height || snake_vector[0].y < 0) |
9 |
{
|
10 |
//This block runs if the snakes head is out of the stage (hitting the walls)
|
11 |
}
|
12 |
|
13 |
|
14 |
for (var i:int = 0; i < snake_vector.length; i++) |
15 |
{
|
16 |
/*
|
17 |
START OF FOR BLOCK
|
18 |
This whole 'for' block will run as many times, as many elements the snake has.
|
19 |
If there are four snake parts, this whole for cycle will run four times.
|
20 |
*/
|
21 |
|
22 |
if (snake_vector[i] != snake_vector[0] && (snake_vector[0].x == snake_vector[i].x && snake_vector[0].y == snake_vector[i].y)) |
23 |
{
|
24 |
//If the snakes heads position is the same as any of the snake parts, this block will run (Checking the collision with itself).
|
25 |
}
|
26 |
|
27 |
if (markers_vector.length > 0) |
28 |
{
|
29 |
//if there are direction markers, this code runs
|
30 |
}
|
31 |
|
32 |
|
33 |
var DIRECTION:String = snake_vector[i].direction; //getting the direction of the current snake element. |
34 |
switch (DIRECTION) |
35 |
{
|
36 |
//Sets the new position of the snakes part
|
37 |
}
|
38 |
|
39 |
/*
|
40 |
END OF FOR BLOCK
|
41 |
*/
|
42 |
}
|
43 |
|
44 |
}
|
Ahora necesitamos codificar el movimiento. Para esto, saltamos al bloque de switch, que se ejecutará en cada parte de la serpiente, debido al ciclo for.
Primero necesitamos verificar la dirección del elemento actual.
1 |
switch (DIRECTION) |
2 |
{
|
3 |
case "R" : |
4 |
//Here we need to set the new x position for the current part
|
5 |
break; |
6 |
case "L" : |
7 |
//Here we need to set the new x position for the current part
|
8 |
break; |
9 |
case "D" : |
10 |
//Here we need to set the new y position for the current part
|
11 |
break; |
12 |
case "U" : |
13 |
//Here we need to set the new y position for the current part
|
14 |
break; |
15 |
}
|
Cuando la dirección de la parte se establece en "R", por ejemplo, necesitamos agregar algo a su posición X actual (el space_value
más el ancho de la parte de la serpiente).
Con esto en mente, podemos completarlo:
1 |
switch (DIRECTION) |
2 |
{
|
3 |
case "R" : |
4 |
snake_vector[i].x += snake_vector[i].width + space_value; |
5 |
break; |
6 |
case "L" : |
7 |
snake_vector[i].x -= snake_vector[i].width + space_value; |
8 |
break; |
9 |
case "D" : |
10 |
snake_vector[i].y += snake_vector[i].height + space_value; |
11 |
break; |
12 |
case "U" : |
13 |
snake_vector[i].y -= snake_vector[i].width + space_value; |
14 |
break; |
15 |
}
|
Después de probar el código, deberías ver que la serpiente se mueve, sale del escenario y nunca se detiene. (Es posible que debas actualizar la página, o simplemente hagas clic aquí para cargarla en una nueva ventana).
Entonces tenemos que detener a la serpiente
Paso 9: Escribir la Función gameOver()
Esta función será la más corta. Simplemente limpiamos el escenario y lo reiniciamos:
1 |
private function gameOver():void |
2 |
{
|
3 |
dead = true; |
4 |
timer.stop(); |
5 |
while (this.numChildren) |
6 |
this.removeChildAt(0); |
7 |
timer.removeEventListener(TimerEvent.TIMER,moveIt); |
8 |
init(); |
9 |
}
|
Eso es. Establecemos la variable dead
en verdadera, detenemos el movimiento con el temporizador, eliminamos todos los elementos secundarios de la clase y llamamos a la función init()
, como si recién comenzáramos el juego.
Ahora, volvamos a la función moveIt()
.
Paso 10: Continuación de la función moveIt()
Usaremos la función gameOver()
en dos lugares. La primera es cuando comprobamos si la cabeza está fuera de límites, y la segunda es cuando la serpiente se golpea a sí misma:
1 |
private function moveIt(e:TimerEvent):void |
2 |
{
|
3 |
if (snake_vector[0].x == apple.x && snake_vector[0].y == apple.y) |
4 |
{
|
5 |
//This code runs if the snakes heads position and the apples position are the same
|
6 |
}
|
7 |
|
8 |
if (snake_vector[0].x > stage.stageWidth-snake_vector[0].width || snake_vector[0].x < 0 || snake_vector[0].y > stage.stageHeight-snake_vector[0].height || snake_vector[0].y < 0) |
9 |
{
|
10 |
gameOver(); |
11 |
}
|
12 |
|
13 |
|
14 |
for (var i:int = 0; i < snake_vector.length; i++) |
15 |
{
|
16 |
/*
|
17 |
START OF FOR BLOCK
|
18 |
This whole 'for' block will run as many times, as many elements the snake has.
|
19 |
If there are four snake parts, this whole for cycle will run four times.
|
20 |
*/
|
21 |
|
22 |
if (snake_vector[i] != snake_vector[0] && (snake_vector[0].x == snake_vector[i].x && snake_vector[0].y == snake_vector[i].y)) |
23 |
{
|
24 |
//If the snakes heads position is the same as any of the snake parts, this block will run
|
25 |
gameOver(); |
26 |
}
|
27 |
|
28 |
if (markers_vector.length > 0) |
29 |
{
|
30 |
//if there are direction markers, this code runs
|
31 |
}
|
32 |
|
33 |
|
34 |
var DIRECTION:String = snake_vector[i].direction; //getting the direction of the current snake element. |
35 |
switch (DIRECTION) |
36 |
{
|
37 |
case "R" : |
38 |
snake_vector[i].x += snake_vector[i].width + space_value; |
39 |
break; |
40 |
case "L" : |
41 |
snake_vector[i].x -= snake_vector[i].width + space_value; |
42 |
break; |
43 |
case "D" : |
44 |
snake_vector[i].y += snake_vector[i].height + space_value; |
45 |
break; |
46 |
case "U" : |
47 |
snake_vector[i].y -= snake_vector[i].width + space_value; |
48 |
break; |
49 |
}
|
50 |
|
51 |
/*
|
52 |
END OF FOR BLOCK
|
53 |
*/
|
54 |
}
|
55 |
|
56 |
}
|
Este es el código que tenemos ahora:
1 |
package
|
2 |
{
|
3 |
import flash.display.Sprite; |
4 |
import flash.text.TextField; |
5 |
import flash.utils.Timer; |
6 |
import flash.events.TimerEvent; |
7 |
import flash.ui.Keyboard; |
8 |
import flash.events.KeyboardEvent; |
9 |
import flash.events.MouseEvent; |
10 |
import flash.events.Event; |
11 |
|
12 |
import com.Element; |
13 |
|
14 |
public class Main extends Sprite |
15 |
{
|
16 |
|
17 |
//DO NOT GIVE THEM A VALUE HERE! Give them a value in the init() function
|
18 |
private var snake_vector:Vector.<Element>; //the snake's parts are held in here |
19 |
private var markers_vector:Vector.<Object>; //the markers are held in here |
20 |
private var timer:Timer; |
21 |
private var dead:Boolean; |
22 |
private var min_elements:int; //holds how many parts should the snake have at the beginning |
23 |
private var apple:Element; //Our apple |
24 |
private var space_value:Number; //space between the snake parts |
25 |
private var last_button_down:uint; //the keyCode of the last button pressed |
26 |
private var flag:Boolean; //is it allowed to change direction? |
27 |
private var score:Number; |
28 |
private var score_tf:TextField; //the Textfield showing the score |
29 |
|
30 |
public function Main() |
31 |
{
|
32 |
if(stage) |
33 |
addEventListener(Event.ADDED_TO_STAGE, init); |
34 |
else
|
35 |
init(); |
36 |
}
|
37 |
|
38 |
private function init(e:Event = null):void |
39 |
{
|
40 |
snake_vector = new Vector.<Element>; |
41 |
markers_vector = new Vector.<Object>; |
42 |
space_value = 2; |
43 |
timer = new Timer(50); //Every 50th millisecond, the moveIt() function will be fired! |
44 |
dead = false; |
45 |
min_elements = 10; //We will begin with 10 elements. |
46 |
apple = new Element(0xFF0000, 1,10, 10); //red, not transparent, width:10, height: 10; |
47 |
apple.catchValue = 0; //pretty obvious |
48 |
last_button_down = Keyboard.RIGHT; //The first direction of the snake is set in this variable |
49 |
score = 0; |
50 |
score_tf = new TextField(); //this is the TextField which shows our score. |
51 |
this.addChild(score_tf); |
52 |
|
53 |
for(var i:int=0;i<min_elements;++i) |
54 |
{
|
55 |
snake_vector[i] = new Element(0x00AAFF,1,10,10); |
56 |
snake_vector[i].direction = "R"; //The starting direction of the snake |
57 |
if (i == 0)//first snake element |
58 |
{
|
59 |
//you have to place the first element on a GRID. (now: 0,0) [possible x positions: (snake_vector[0].width+space_value)*<UINT> ]
|
60 |
attachElement(snake_vector[i], (snake_vector[0].width + space_value) * 20, (snake_vector[0].height + space_value) * 10, snake_vector[i].direction); |
61 |
snake_vector[0].alpha = 0.7; |
62 |
}
|
63 |
else
|
64 |
{
|
65 |
attachElement(snake_vector[i], snake_vector[i - 1].x, snake_vector[i - 1].y, snake_vector[i - 1].direction); |
66 |
}
|
67 |
}
|
68 |
|
69 |
placeApple(false); |
70 |
timer.addEventListener(TimerEvent.TIMER, moveIt); |
71 |
timer.start(); |
72 |
}
|
73 |
|
74 |
private function attachElement(who:Element,lastXPos:Number = 0,lastYPos:Number = 0,dirOfLast:String = "R"):void |
75 |
{
|
76 |
if (dirOfLast == "R") |
77 |
{
|
78 |
who.x = lastXPos - snake_vector[0].width - space_value; |
79 |
who.y = lastYPos; |
80 |
}
|
81 |
else if(dirOfLast == "L") |
82 |
{
|
83 |
who.x = lastXPos + snake_vector[0].width + space_value; |
84 |
who.y = lastYPos; |
85 |
}
|
86 |
else if(dirOfLast == "U") |
87 |
{
|
88 |
who.x = lastXPos; |
89 |
who.y = lastYPos + snake_vector[0].height + space_value; |
90 |
}
|
91 |
else if(dirOfLast == "D") |
92 |
{
|
93 |
who.x = lastXPos; |
94 |
who.y = lastYPos - snake_vector[0].height - space_value; |
95 |
}
|
96 |
this.addChild(who); |
97 |
}
|
98 |
|
99 |
private function placeApple(caught:Boolean = true):void |
100 |
{
|
101 |
if (caught) |
102 |
apple.catchValue += 10; |
103 |
|
104 |
var boundsX:int = (Math.floor(stage.stageWidth / (snake_vector[0].width + space_value)))-1; |
105 |
var randomX:Number = Math.floor(Math.random()*boundsX); |
106 |
|
107 |
var boundsY:int = (Math.floor(stage.stageHeight/(snake_vector[0].height + space_value)))-1; |
108 |
var randomY:Number = Math.floor(Math.random()*boundsY); |
109 |
|
110 |
apple.x = randomX * (apple.width + space_value); |
111 |
apple.y = randomY * (apple.height + space_value); |
112 |
|
113 |
for(var i:uint=0;i<snake_vector.length-1;i++) |
114 |
{
|
115 |
if(snake_vector[i].x == apple.x && snake_vector[i].y == apple.y) |
116 |
placeApple(false); |
117 |
}
|
118 |
if (!apple.stage) |
119 |
this.addChild(apple); |
120 |
}
|
121 |
|
122 |
private function moveIt(e:TimerEvent):void |
123 |
{
|
124 |
if (snake_vector[0].x == apple.x && snake_vector[0].y == apple.y) |
125 |
{
|
126 |
//This code runs if the snakes heads position and the apples position are the same
|
127 |
}
|
128 |
|
129 |
if (snake_vector[0].x > stage.stageWidth-snake_vector[0].width || snake_vector[0].x < 0 || snake_vector[0].y > stage.stageHeight-snake_vector[0].height || snake_vector[0].y < 0) |
130 |
{
|
131 |
gameOver(); |
132 |
}
|
133 |
|
134 |
|
135 |
for (var i:int = 0; i < snake_vector.length; i++) |
136 |
{
|
137 |
/*
|
138 |
START OF FOR BLOCK
|
139 |
This whole 'for' block will run as many times, as many elements the snake has.
|
140 |
If there are four snake parts, this whole for cycle will run four times.
|
141 |
*/
|
142 |
|
143 |
if (snake_vector[i] != snake_vector[0] && (snake_vector[0].x == snake_vector[i].x && snake_vector[0].y == snake_vector[i].y)) |
144 |
{
|
145 |
//If the snakes heads position is the same as any of the snake parts, this block will run
|
146 |
gameOver(); |
147 |
}
|
148 |
|
149 |
if (markers_vector.length > 0) |
150 |
{
|
151 |
//if there are direction markers, this code runs
|
152 |
}
|
153 |
|
154 |
|
155 |
var DIRECTION:String = snake_vector[i].direction; //getting the direction of the current snake element. |
156 |
switch (DIRECTION) |
157 |
{
|
158 |
case "R" : |
159 |
snake_vector[i].x += snake_vector[i].width + space_value; |
160 |
break; |
161 |
case "L" : |
162 |
snake_vector[i].x -= snake_vector[i].width + space_value; |
163 |
break; |
164 |
case "D" : |
165 |
snake_vector[i].y += snake_vector[i].height + space_value; |
166 |
break; |
167 |
case "U" : |
168 |
snake_vector[i].y -= snake_vector[i].width + space_value; |
169 |
break; |
170 |
}
|
171 |
|
172 |
/*
|
173 |
END OF FOR BLOCK
|
174 |
*/
|
175 |
}
|
176 |
|
177 |
}
|
178 |
|
179 |
private function gameOver():void |
180 |
{
|
181 |
dead = true; |
182 |
timer.stop(); |
183 |
while (this.numChildren) |
184 |
this.removeChildAt(0); |
185 |
timer.removeEventListener(TimerEvent.TIMER,moveIt); |
186 |
//stage.removeEventListener(KeyboardEvent.KEY_DOWN,directionChanged);
|
187 |
init(); |
188 |
}
|
189 |
|
190 |
}
|
191 |
}
|
Paso 11: La Función directionChanged()
Queremos escuchar el teclado para poder controlar a la serpiente. Para esto, necesitamos poner algo de código en la función init()
y la función gameOver()
.
Pon esto al final de la función init()
(configurando la función de escucha):
1 |
stage.addEventListener(KeyboardEvent.KEY_DOWN,directionChanged); |
Y esto al final de la función gameOver()
:
1 |
stage.removeEventListener(KeyboardEvent.KEY_DOWN,directionChanged); |
Ahora crea una nueva función:
1 |
private function directionChanged(e:KeyboardEvent):void |
2 |
{
|
3 |
var m:Object = new Object(); //MARKER OBJECT |
4 |
//this will be added to the markers_vector, and have the properties x,y, and type
|
5 |
//the type property will show us the direction. if it is set to right, whenever a snake's part hits it,
|
6 |
//the direction of that snake's part will be set to right also
|
7 |
|
8 |
if (e.keyCode == Keyboard.LEFT && last_button_down != e.keyCode && last_button_down != Keyboard.RIGHT) |
9 |
{
|
10 |
//If we pressed the LEFT arrow,
|
11 |
//and it was not the last key we pressed,
|
12 |
//and the last key pressed was not the RIGHT arrow either...
|
13 |
//Then this block of code will run
|
14 |
}
|
15 |
markers_vector.push(m); //we push the object into a vector, so we can acces to it later (in the moveIt() function) |
16 |
}
|
¿Qué entra en el bloque if?
- La dirección de la cabeza debe reescribirse.
- El objeto marker debe configurarse correctamente.
- La variable last_button debe establecerse en el último botón presionado.
1 |
if (e.keyCode == Keyboard.LEFT && last_button_down != e.keyCode && last_button_down != Keyboard.RIGHT && flag) |
2 |
{
|
3 |
snake_vector[0].direction = "L"; |
4 |
m = {x:snake_vector[0].x, y:snake_vector[0].y, type:"L"}; |
5 |
last_button_down = Keyboard.LEFT; |
6 |
}
|
Repite esto tres veces más, y tendremos esto:
1 |
private function directionChanged(e:KeyboardEvent):void |
2 |
{
|
3 |
var m:Object = new Object(); //MARKER OBJECT |
4 |
|
5 |
if (e.keyCode == Keyboard.LEFT && last_button_down != e.keyCode && last_button_down != Keyboard.RIGHT) |
6 |
{
|
7 |
snake_vector[0].direction = "L"; |
8 |
m = {x:snake_vector[0].x, y:snake_vector[0].y, type:"L"}; |
9 |
last_button_down = Keyboard.LEFT; |
10 |
}
|
11 |
else if (e.keyCode == Keyboard.RIGHT && last_button_down != e.keyCode && last_button_down != Keyboard.LEFT) |
12 |
{
|
13 |
snake_vector[0].direction = "R"; |
14 |
m = {x:snake_vector[0].x, y:snake_vector[0].y, type:"R"}; |
15 |
last_button_down = Keyboard.RIGHT; |
16 |
}
|
17 |
else if (e.keyCode == Keyboard.UP && last_button_down != e.keyCode && last_button_down != Keyboard.DOWN) |
18 |
{
|
19 |
snake_vector[0].direction = "U"; |
20 |
m = {x:snake_vector[0].x, y:snake_vector[0].y, type:"U"}; |
21 |
last_button_down = Keyboard.UP; |
22 |
}
|
23 |
else if (e.keyCode == Keyboard.DOWN && last_button_down != e.keyCode && last_button_down != Keyboard.UP) |
24 |
{
|
25 |
snake_vector[0].direction = "D"; |
26 |
m = {x:snake_vector[0].x, y:snake_vector[0].y, type:"D"}; |
27 |
last_button_down = Keyboard.DOWN; |
28 |
}
|
29 |
markers_vector.push(m); |
30 |
}
|
Necesitamos una cosa más para probarlo. En la función moveIt() tenemos algo como esto:
1 |
if (markers_vector.length > 0) |
2 |
{
|
3 |
//if there are direction markers, this code runs
|
4 |
}
|
Aquí necesitamos otro ciclo for, para verificar la parte de cada serpiente con cada marcador en el escenario y verificar si chocan. Si lo hacen, debemos establecer la dirección de la parte de la serpiente según el tipo de marcador. Si es la última parte de la serpiente que choca con el marcador, también debemos quitar el marcador del vector markers_vector
, para que las partes de la serpiente no choquen más con él.
1 |
if (markers_vector.length > 0) |
2 |
{
|
3 |
for(var j:uint=0;j < markers_vector.length;j++) |
4 |
{
|
5 |
if(snake_vector[i].x == markers_vector[j].x && snake_vector[i].y == markers_vector[j].y) |
6 |
{
|
7 |
//setting the direction
|
8 |
snake_vector[i].direction = markers_vector[j].type; |
9 |
if(i == snake_vector.length-1) |
10 |
{
|
11 |
//if its the last snake_part
|
12 |
markers_vector.splice(j, 1); |
13 |
}
|
14 |
}
|
15 |
}
|
16 |
}
|
Ahora, si juegas con él, se ve bien, pero hay un error allí. ¿Recuerdas lo que dije al principio del tutorial?
Por ejemplo, si la serpiente va hacia la derecha y presionas el combo abajo-izquierda muy rápido, se golpeará a sí misma y reiniciará el juego.
¿Cómo corregimos esto? Bueno, es fácil. Tenemos nuestra variable de flag
y la usaremos para esto. Solo podremos cambiar las direcciones de la serpiente cuando se establezca en verdadero (el valor predeterminado es falso, verifica la función init()
para eso).
Entonces necesitamos cambiar un poco la función directionChanged()
. Las cabezas de los bloques if deben cambiarse: agrega una cláusula de && flag
al final de cada 'if'.
1 |
private function directionChanged(e:KeyboardEvent):void |
2 |
{
|
3 |
var m:Object = new Object(); //MARKER OBJECT |
4 |
|
5 |
if (e.keyCode == Keyboard.LEFT && last_button_down != e.keyCode && last_button_down != Keyboard.RIGHT && flag) |
6 |
{
|
7 |
snake_vector[0].direction = "L"; |
8 |
m = {x:snake_vector[0].x, y:snake_vector[0].y, type:"L"}; |
9 |
last_button_down = Keyboard.LEFT; |
10 |
flag = false; |
11 |
}
|
12 |
else if (e.keyCode == Keyboard.RIGHT && last_button_down != e.keyCode && last_button_down != Keyboard.LEFT && flag) |
13 |
{
|
14 |
snake_vector[0].direction = "R"; |
15 |
m = {x:snake_vector[0].x, y:snake_vector[0].y, type:"R"}; |
16 |
last_button_down = Keyboard.RIGHT; |
17 |
flag = false; |
18 |
}
|
19 |
else if (e.keyCode == Keyboard.UP && last_button_down != e.keyCode && last_button_down != Keyboard.DOWN && flag) |
20 |
{
|
21 |
snake_vector[0].direction = "U"; |
22 |
m = {x:snake_vector[0].x, y:snake_vector[0].y, type:"U"}; |
23 |
last_button_down = Keyboard.UP; |
24 |
flag = false; |
25 |
}
|
26 |
else if (e.keyCode == Keyboard.DOWN && last_button_down != e.keyCode && last_button_down != Keyboard.UP && flag) |
27 |
{
|
28 |
snake_vector[0].direction = "D"; |
29 |
m = {x:snake_vector[0].x, y:snake_vector[0].y, type:"D"}; |
30 |
last_button_down = Keyboard.DOWN; |
31 |
flag = false; |
32 |
}
|
33 |
markers_vector.push(m); |
34 |
}
|
Si lo pruebas ahora, no funcionará porque la bandera siempre es falsa.
Entonces, ¿Cuándo debemos configurarlo como verdadero?
Después de un movimiento/marca, podemos permitir que los usuarios cambien de dirección, pero no queremos cambiarlo dos veces en una marca. Así que pon esto al final de la función moveIt()
:
1 |
flag = true; |
Ahora pruébalo y ya no habrá ningún error.
Paso 12: Finalización del Juego
Ahora lo único que tenemos que hacer es el 'apple-check'
¿Recuerdas esto al principio de la función moveIt()
?
1 |
if (snake_vector[0].x == apple.x && snake_vector[0].y == apple.y) |
2 |
{
|
3 |
//This code runs if the snake's head's position and the apple's position are the same
|
4 |
}
|
Esto es lo que debemos hacer allí:
- Llama la función placeApple(). (No establecemos el parámetro en falso; lo dejamos como está. El valor predeterminado es verdadero).
- Mostrar la puntuación actual
- Adjunta un nuevo elemento a la última parte de la serpiente.
1 |
if (snake_vector[0].x == apple.x && snake_vector[0].y == apple.y) |
2 |
{
|
3 |
//calling the placeApple() function
|
4 |
placeApple(); |
5 |
//show the current Score
|
6 |
score += apple.catchValue; |
7 |
score_tf.text = "Score:" + String(score); |
8 |
//Attach a new snake Element
|
9 |
snake_vector.push(new Element(0x00AAFF,1,10,10)); |
10 |
snake_vector[snake_vector.length-1].direction = snake_vector[snake_vector.length-2].direction; //lastOneRichtung |
11 |
//attachElement(who,lastXPos,lastYPos,lastDirection)
|
12 |
attachElement(snake_vector[snake_vector.length-1], |
13 |
(snake_vector[snake_vector.length-2].x), |
14 |
snake_vector[snake_vector.length-2].y, |
15 |
snake_vector[snake_vector.length-2].direction); |
16 |
}
|
Ahora todo debería funcionar bien. Pruébalo:
Aquí está toda la clase principal nuevamente:
1 |
package
|
2 |
{
|
3 |
import flash.display.Sprite; |
4 |
import flash.text.TextField; |
5 |
import flash.utils.Timer; |
6 |
import flash.events.TimerEvent; |
7 |
import flash.ui.Keyboard; |
8 |
import flash.events.KeyboardEvent; |
9 |
import flash.events.MouseEvent; |
10 |
import flash.events.Event; |
11 |
|
12 |
import com.Element; |
13 |
|
14 |
public class Main extends Sprite |
15 |
{
|
16 |
//DO NOT GIVE THEM A VALUE HERE! Give them a value in the init() function
|
17 |
private var snake_vector:Vector.<Element>; //the snake's parts are held in here |
18 |
private var markers_vector:Vector.<Object>; //the markers are held in here |
19 |
private var timer:Timer; |
20 |
private var dead:Boolean; |
21 |
private var min_elements:int; //holds how many parts should the snake have at the beginning |
22 |
private var apple:Element; //Our apple |
23 |
private var space_value:Number; //space between the snake parts |
24 |
private var last_button_down:uint; //the keyCode of the last button pressed |
25 |
private var flag:Boolean; //is it allowed to change direction? |
26 |
private var score:Number; |
27 |
private var score_tf:TextField; //the Textfield showing the score |
28 |
|
29 |
public function Main() |
30 |
{
|
31 |
if(stage) |
32 |
addEventListener(Event.ADDED_TO_STAGE, init); |
33 |
else
|
34 |
init(); |
35 |
}
|
36 |
|
37 |
private function init(e:Event = null):void |
38 |
{
|
39 |
snake_vector = new Vector.<Element>; |
40 |
markers_vector = new Vector.<Object>; |
41 |
space_value = 2; |
42 |
timer = new Timer(50); //Every 50th millisecond, the moveIt() function will be fired! |
43 |
dead = false; |
44 |
min_elements = 1; |
45 |
apple = new Element(0xFF0000, 1,10, 10); //red, not transparent, width:10, height: 10; |
46 |
apple.catchValue = 0; //pretty obvious |
47 |
last_button_down = Keyboard.RIGHT; //The starting direction of the snake (only change it if you change the 'for cycle' too.) |
48 |
score = 0; |
49 |
score_tf = new TextField(); |
50 |
this.addChild(score_tf); |
51 |
|
52 |
//Create the first <min_elements> Snake parts
|
53 |
for(var i:int=0;i<min_elements;++i) |
54 |
{
|
55 |
snake_vector[i] = new Element(0x00AAFF,1,10,10); |
56 |
snake_vector[i].direction = "R"; //The starting direction of the snake |
57 |
if (i == 0) |
58 |
{
|
59 |
//you have to place the first element on a GRID. (now: 0,0) [possible x positions: (snake_vector[0].width+space_value)*<UINT> ]
|
60 |
attachElement(snake_vector[i],0,0,snake_vector[i].direction) |
61 |
snake_vector[0].alpha = 0.7; |
62 |
}
|
63 |
else
|
64 |
{
|
65 |
attachElement(snake_vector[i], snake_vector[i - 1].x, snake_vector[i - 1].y, snake_vector[i - 1].direction); |
66 |
}
|
67 |
}
|
68 |
|
69 |
placeApple(false); |
70 |
timer.addEventListener(TimerEvent.TIMER,moveIt); |
71 |
stage.addEventListener(KeyboardEvent.KEY_DOWN,directionChanged); |
72 |
timer.start(); |
73 |
}
|
74 |
|
75 |
private function attachElement(who:Element,lastXPos:Number = 0,lastYPos:Number = 0,dirOfLast:String = "R"):void |
76 |
{
|
77 |
if (dirOfLast == "R") |
78 |
{
|
79 |
who.x = lastXPos - snake_vector[0].width - space_value; |
80 |
who.y = lastYPos; |
81 |
}
|
82 |
else if(dirOfLast == "L") |
83 |
{
|
84 |
who.x = lastXPos + snake_vector[0].width + space_value; |
85 |
who.y = lastYPos; |
86 |
}
|
87 |
else if(dirOfLast == "U") |
88 |
{
|
89 |
who.x = lastXPos; |
90 |
who.y = lastYPos + snake_vector[0].height + space_value; |
91 |
}
|
92 |
else if(dirOfLast == "D") |
93 |
{
|
94 |
who.x = lastXPos; |
95 |
who.y = lastYPos - snake_vector[0].height - space_value; |
96 |
}
|
97 |
this.addChild(who); |
98 |
}
|
99 |
|
100 |
private function placeApple(caught:Boolean = true):void |
101 |
{
|
102 |
if (caught) |
103 |
apple.catchValue += 10; |
104 |
|
105 |
var boundsX:int = (Math.floor(stage.stageWidth / (snake_vector[0].width + space_value)))-1; |
106 |
var randomX:Number = Math.floor(Math.random()*boundsX); |
107 |
|
108 |
var boundsY:int = (Math.floor(stage.stageHeight/(snake_vector[0].height + space_value)))-1; |
109 |
var randomY:Number = Math.floor(Math.random()*boundsY); |
110 |
|
111 |
apple.x = randomX * (apple.width + space_value); |
112 |
apple.y = randomY * (apple.height + space_value); |
113 |
|
114 |
for(var i:uint=0;i<snake_vector.length-1;i++) |
115 |
{
|
116 |
if(snake_vector[i].x == apple.x && snake_vector[i].y == apple.y) |
117 |
placeApple(false); |
118 |
}
|
119 |
if (!apple.stage) |
120 |
this.addChild(apple); |
121 |
}
|
122 |
|
123 |
private function moveIt(e:TimerEvent):void |
124 |
{
|
125 |
if (snake_vector[0].x == apple.x && snake_vector[0].y == apple.y) |
126 |
{
|
127 |
placeApple(); |
128 |
//show the current Score
|
129 |
score += apple.catchValue; |
130 |
score_tf.text = "Score:" + String(score); |
131 |
//Attach a new snake Element
|
132 |
snake_vector.push(new Element(0x00AAFF,1,10,10)); |
133 |
snake_vector[snake_vector.length-1].direction = snake_vector[snake_vector.length-2].direction; //lastOneRichtung |
134 |
attachElement(snake_vector[snake_vector.length-1], |
135 |
(snake_vector[snake_vector.length-2].x), |
136 |
snake_vector[snake_vector.length-2].y, |
137 |
snake_vector[snake_vector.length-2].direction); |
138 |
}
|
139 |
if (snake_vector[0].x > stage.stageWidth-snake_vector[0].width || snake_vector[0].x < 0 || snake_vector[0].y > stage.stageHeight-snake_vector[0].height || snake_vector[0].y < 0) |
140 |
{
|
141 |
gameOver(); |
142 |
}
|
143 |
|
144 |
for (var i:int = 0; i < snake_vector.length; i++) |
145 |
{
|
146 |
if (markers_vector.length > 0) |
147 |
{
|
148 |
for(var j:uint=0;j < markers_vector.length;j++) |
149 |
{
|
150 |
if(snake_vector[i].x == markers_vector[j].x && snake_vector[i].y == markers_vector[j].y) |
151 |
{
|
152 |
snake_vector[i].direction = markers_vector[j].type; |
153 |
if(i == snake_vector.length-1) |
154 |
{
|
155 |
markers_vector.splice(j, 1); |
156 |
}
|
157 |
}
|
158 |
}
|
159 |
}
|
160 |
if (snake_vector[i] != snake_vector[0] && (snake_vector[0].x == snake_vector[i].x && snake_vector[0].y == snake_vector[i].y)) |
161 |
{
|
162 |
gameOver(); |
163 |
}
|
164 |
|
165 |
//Move the boy
|
166 |
var DIRECTION:String = snake_vector[i].direction; |
167 |
switch (DIRECTION) |
168 |
{
|
169 |
case "R" : |
170 |
snake_vector[i].x += snake_vector[i].width + space_value; |
171 |
break; |
172 |
case "L" : |
173 |
snake_vector[i].x -= snake_vector[i].width + space_value; |
174 |
break; |
175 |
case "D" : |
176 |
snake_vector[i].y += snake_vector[i].height + space_value; |
177 |
break; |
178 |
case "U" : |
179 |
snake_vector[i].y -= snake_vector[i].width + space_value; |
180 |
break; |
181 |
}
|
182 |
|
183 |
}
|
184 |
|
185 |
flag = true; |
186 |
}
|
187 |
|
188 |
private function gameOver():void |
189 |
{
|
190 |
dead = true; |
191 |
timer.stop(); |
192 |
while (this.numChildren) |
193 |
this.removeChildAt(0); |
194 |
timer.removeEventListener(TimerEvent.TIMER,moveIt); |
195 |
stage.removeEventListener(KeyboardEvent.KEY_DOWN,directionChanged); |
196 |
init(); |
197 |
}
|
198 |
|
199 |
private function directionChanged(e:KeyboardEvent):void |
200 |
{
|
201 |
var m:Object = new Object(); //MARKER OBJECT |
202 |
|
203 |
if (e.keyCode == Keyboard.LEFT && last_button_down != e.keyCode && last_button_down != Keyboard.RIGHT && flag) |
204 |
{
|
205 |
snake_vector[0].direction = "L"; |
206 |
m = {x:snake_vector[0].x, y:snake_vector[0].y, type:"L"}; |
207 |
last_button_down = Keyboard.LEFT; |
208 |
flag = false; |
209 |
}
|
210 |
else if (e.keyCode == Keyboard.RIGHT && last_button_down != e.keyCode && last_button_down != Keyboard.LEFT && flag) |
211 |
{
|
212 |
snake_vector[0].direction = "R"; |
213 |
m = {x:snake_vector[0].x, y:snake_vector[0].y, type:"R"}; |
214 |
last_button_down = Keyboard.RIGHT; |
215 |
flag = false; |
216 |
}
|
217 |
else if (e.keyCode == Keyboard.UP && last_button_down != e.keyCode && last_button_down != Keyboard.DOWN && flag) |
218 |
{
|
219 |
snake_vector[0].direction = "U"; |
220 |
m = {x:snake_vector[0].x, y:snake_vector[0].y, type:"U"}; |
221 |
last_button_down = Keyboard.UP; |
222 |
flag = false; |
223 |
}
|
224 |
else if (e.keyCode == Keyboard.DOWN && last_button_down != e.keyCode && last_button_down != Keyboard.UP && flag) |
225 |
{
|
226 |
snake_vector[0].direction = "D"; |
227 |
m = {x:snake_vector[0].x, y:snake_vector[0].y, type:"D"}; |
228 |
last_button_down = Keyboard.DOWN; |
229 |
flag = false; |
230 |
}
|
231 |
markers_vector.push(m); |
232 |
}
|
233 |
|
234 |
}
|
235 |
|
236 |
}
|
Paso 13: Resumiendo Todo
¡Felicidades! Acabas de crear un buen juego. Ahora puedes desarrollarlo más y crear una súper manzana o algo así. Para eso recomiendo usar otra función llamada placeSuperApple()
y una nueva clase llamada SuperApple
. Siempre que atrapes una súper manzana, las partes de las serpientes podrían alargarse en tres elementos, tal vez. Esto podría establecerse con setters/getters en la clase SuperApple
.
Si deseas hacer esto y te quedas atascado en algún lugar, déjame un comentario aquí.
¡Gracias por tu tiempo!