Advertisement
Scroll to top
Read Time: 18 min
This post is part of a series called You Do The Math.
Creating Generative Art with HYPE
Euclidean Vectors in Flash

() translation by (you can also view the original English article)

En este tutorial crearemos un juego donde el objetivo es evitar que otros objetos colisionen con el cursor. No usaremos los métodos hitTestObject() integrados de Flash; en cambio, escribiremos nuestras propias rutinas de detección de colisiones.

Tutorial republicado

Cada pocas semanas, volvemos a visitar algunas de las publicaciones favoritas de nuestros lectores a lo largo de la historia del sitio. Este tutorial se publicó por primera vez en febrero de 2011.


Vista previa del resultado final

Echemos un vistazo al resultado final para el que trabajaremos:


Paso 1: Comienza

Crear un nuevo archivo Flash (ActionScript 3.0)

Flash collision game tutorial

Establezca las dimensiones del escenario a 500x500 px y FPS a 32.

Flash collision game tutorialFlash collision game tutorialFlash collision game tutorial

Paso 2: la clase Ball

Esta clase contendrá todos los datos relacionados con una pelota. Una pelota tiene _mass, _radius, _xSpeed ​​y _ySpeed. Así que haremos una propiedad para cada uno. En el constructor pasamos la masa, el ángulo y la velocidad de la pelota. Debido a que la clase se vinculará a un objeto de visualización, podemos recuperar el radio de nuestra bola dividiendo el ancho del objeto de visualización por 2. La _xSpeed ​​y _ySpeed ​​pueden calcularse utilizando funciones simples de seno y coseno.

1
2
package
3
{
4
  import flash.display.Stage
5
  import flash.display.Sprite
6
  import flash.events.Event
7
8
  public class Ball extends Sprite
9
  {
10
    private var _radius:Number = 0
11
    private var _mass:Number = 0
12
    private var _xSpeed:Number = 0
13
    private var _ySpeed:Number = 0
14
15
    public function Ball(mass:Number = 10.0, angle:Number = Math.PI, speed:Number = 10.0):void
16
    {
17
      this.mass = mass
18
      this._radius = this.width/2
19
      this.xSpeed = speed*Math.sin(angle)
20
      this.ySpeed = speed*Math.cos(angle)
21
    }
22
  }
23
}

Para obtener más información sobre estas funciones trigonométricas Math.sin() y Math.cos(), consulte esta sugerencia rápida.


Paso 3: Proporcionando Getters y Setters

En nuestra clase Ball proporcionamos getters y setters para nuestras propiedades.

1
2
public function get radius():Number
3
{
4
  return this._radius
5
}
6
7
public function set mass(mass:Number):void
8
{
9
  this._mass = mass
10
}
11
12
public function get mass():Number
13
{
14
  return this._mass
15
}
16
17
public function set xSpeed(xSpeed:Number):void
18
{
19
  this._xSpeed = xSpeed
20
}
21
22
public function get xSpeed():Number
23
{
24
  return this._xSpeed
25
}
26
27
public function set ySpeed(ySpeed:Number):void
28
{
29
  this._ySpeed = ySpeed
30
}
31
32
public function get ySpeed():Number
33
{
34
  return this._ySpeed
35
}

Paso 4: Función de actualización

Esta función actualiza las propiedades xey de nuestra bola de acuerdo con _xSpeed y _ySpeed. Implementaremos esta función en nuestra clase Ball.

1
2
public function update():void
3
{
4
  this.x += _xSpeed
5
  this.y += _ySpeed
6
}

Paso 5: la clase completada

Terminaremos nuestra clase Ball en este paso.

1
2
package
3
{
4
  import flash.display.Stage
5
  import flash.display.Sprite
6
  import flash.events.Event
7
8
  public class Ball extends Sprite
9
  {
10
    private var _radius:Number = 0
11
    private var _mass:Number = 0
12
    private var _xSpeed:Number = 0
13
    private var _ySpeed:Number = 0
14
15
    public function Ball(mass:Number = 10.0, angle:Number = Math.PI, speed:Number = 10.0):void
16
    {
17
      this.mass = mass
18
      this._radius = this.width/2
19
      this.xSpeed = speed*Math.sin(angle)
20
      this.ySpeed = speed*Math.cos(angle)
21
    }
22
23
    public function get radius():Number
24
    {
25
      return this._radius
26
    }
27
28
    public function set mass(mass:Number):void
29
    {
30
      this._mass = mass
31
    }
32
33
    public function get mass():Number
34
    {
35
      return this._mass
36
    }
37
38
    public function set xSpeed(xSpeed:Number):void
39
    {
40
      this._xSpeed = xSpeed
41
    }
42
43
    public function get xSpeed():Number
44
    {
45
      return this._xSpeed
46
    }
47
48
    public function set ySpeed(ySpeed:Number):void
49
    {
50
      this._ySpeed = ySpeed
51
    }
52
53
    public function get ySpeed():Number
54
    {
55
      return this._ySpeed
56
    }
57
58
    public function update():void
59
    {
60
      this.x += _xSpeed
61
      this.y += _ySpeed
62
    }
63
  }
64
}

Paso 6: mostrar objetos para nuestra clase Ball

En los archivos fuente incluí un inicio FLA que contiene todos los elementos de la biblioteca que necesita. Puedes dibujarlos tú mismo si quieres, por supuesto. Asegúrese de que su FLA tenga los siguientes objetos de visualización:

Flash collision game tutorial

(Nota del editor: es un error tipográfico: "ennemyball" debería decir "enemyball").


Paso 7: Vinculación de nuestros objetos de biblioteca

La clase Ball que acabamos de crear tiene que estar vinculada al Sprite enemyball en la biblioteca.

Flash collision game tutorialFlash collision game tutorialFlash collision game tutorial

El Sprite playerball debe tener Ball como clase base y PlayerBall como clase.

Flash collision game tutorialFlash collision game tutorialFlash collision game tutorial

El clip de película score debe tener una clase Score.

Flash collision game tutorialFlash collision game tutorialFlash collision game tutorial

Paso 8: la clase de la aplicación (clase de documento)

La clase Application contendrá toda la lógica del juego. Importamos todas las clases que necesitamos. Como puede ver, usaremos TweenMax.

A continuación definimos nuestras variables de campo. La primera variable de campo es ballPlayer.

Debido a que la clase base de nuestro Playerball Sprite es Ball, podemos almacenar esta Clase en la variable ballPlayer. Esto hace que sea más fácil verificar las colisiones entre el ballPlayer y las pelotas enemigas.

La segunda variable de campo es una matriz que contendrá todas nuestras bolas enemigas. La tercera variable es el temporizador que se usará para realizar el ciclo principal del juego. El cuarto y último campo es una instancia de nuestro objeto de la biblioteca Score que se usará para mostrar el tiempo transcurrido del juego. En el constructor llamamos a la función init() que explicaré en el siguiente paso.

1
2
package
3
{
4
  import flash.display.Sprite
5
  import flash.display.Graphics
6
  import flash.events.Event
7
  import flash.events.TimerEvent
8
  import flash.events.MouseEvent
9
  import flash.geom.Matrix
10
  import flash.utils.Timer
11
  import flash.ui.Mouse
12
  import com.greensock.TweenMax
13
  import com.greensock.easing.*
14
15
  public class Application extends Sprite
16
  {
17
18
    private var ballPlayer:Ball
19
    private var eballs:Array
20
    private var tmr:Timer
21
    private var score:Score
22
23
    public function Application():void
24
    {
25
      init()
26
    }
27
  }
28
}

¡No olvides vincular la clase document!.

Flash collision game tutorial

Paso 9: La función init()

Eche un vistazo a este código:

1
2
private function init():void
3
{
4
  ballPlayer = new PlayerBall()
5
  eballs = new Array()
6
  tmr = new Timer(10)
7
  score = new Score()
8
9
  stage.align = "TL"
10
  stage.scaleMode = "noScale"
11
  Mouse.hide()
12
13
  setBackground()
14
15
  score.x = stage.stageWidth/2
16
  score.y = stage.stageHeight/2
17
  stage.addChild(score)
18
19
  stage.addEventListener(MouseEvent.MOUSE_MOVE, updatePlayerBall)
20
  stage.addChild(ballPlayer)
21
22
        tmr.addEventListener(TimerEvent.TIMER, updateTime)
23
24
  stage.addEventListener(MouseEvent.CLICK, startGame)
25
}

En las primeras cuatro líneas, inicializamos nuestras variables de campo.

A continuación, nos aseguramos de que nuestra etapa esté alineada con la esquina superior izquierda y no se escale.

Ocultamos el cursor del mouse Nuestro cursor será reemplazado por el Sprite playerball . Luego llamamos a la función setBackground (explicada en el siguiente paso).

Centramos nuestro score en la pantalla y lo agregamos a la lista de visualización. Para actualizar la posición de ballPlayer, adjuntamos un evento MouseEvent.MOUSE_MOVE al escenario.

La función updatePlayerBall (explicada en el paso 11) manejará este evento MouseEvent. A continuación, agregamos ballPlayer a la lista de visualización.

El temporizador se usará para mostrar el tiempo del juego. Adjuntamos un oyente TimerEvent.TIMER a nuestro temporizador, que activará la función updateTime() (explicada en el Paso 12) cada 10 milisegundos.

Finalmente, agregamos un MouseEvent.CLICK a nuestro escenario. La función startGame (explicada en el paso 13) comenzará nuestro juego.


Paso 10: función setBackground()

Esta función agrega un fondo degradado radial a la lista de visualización. Para dibujar un degradado en un Sprite debe definir el tipo de degradado, los colores que desea usar, los valores alfa de los colores, las relaciones (esto define la distribución de los colores) y el método de dispersión.

Para obtener más información, consulte esta sugerencia rápida sobre degradados.

1
2
private function setBackground():void
3
{
4
  var type:String = "radial"
5
  var colors:Array = [0xffffff,0xcccccc]
6
  var alphas:Array = [ 1, 1 ]
7
  var ratios:Array = [ 0, 255 ]
8
  var matr:Matrix = new Matrix()
9
  matr.createGradientBox(stage.stageWidth, stage.stageHeight, Math.PI / 2, 0, 0 )
10
  //SpreadMethod will define how the gradient is spread. Note!!! Flash uses CONSTANTS to represent String literals

11
  var sprMethod:String = "pad"
12
  //Start the Gradietn and pass our variables to it

13
  var sprite:Sprite = new Sprite()
14
  //Save typing + increase performance through local reference to a Graphics object

15
  var g:Graphics = sprite.graphics
16
  g.beginGradientFill( type, colors, alphas, ratios, matr, sprMethod )
17
  g.drawRect(0,0,stage.stageWidth,stage.stageHeight)
18
19
  stage.addChild(sprite)
20
}

Paso 11: Función updatePlayerBall()

Esta función actualiza la posición de ballPlayer según la posición del mouse.

1
2
private function updatePlayerBall(e:MouseEvent):void
3
{
4
  ballPlayer.x = mouseX
5
  ballPlayer.y = mouseY
6
}

Paso 12: función updateTime()

Calculamos el tiempo en segundos y lo colocamos dentro del cuadro de texto de nuestro Sprite score. Cada 5000 ms (cinco segundos) agregamos una nueva pelota al juego.

1
2
private function updateTime(e:TimerEvent):void
3
{
4
  score.txtScore.text = String(((tmr.currentCount*tmr.delay)/1000).toFixed(2));
5
  if((tmr.currentCount*tmr.delay) % 5000 == 0)
6
  {
7
    addBall();
8
  }
9
}

Paso 13: función startGame()

El juego se inicia haciendo clic en el escenario. Primero eliminamos el oyente para el clic de escenario, de modo que no podemos comenzar el juego varias veces. Agregamos tres bolas al juego llamando a la función addBall() (explicada en el siguiente paso) tres veces. Comenzamos nuestro temporizador que actualizará nuestro tiempo de juego.

Finalmente, agregamos un evento ENTER_FRAME a nuestra etapa. La función gameLoop() (explicada en el Paso 15) actualizará la posición de nuestras bolas enemigas.

1
2
private function startGame(e:MouseEvent):void
3
{
4
  stage.removeEventListener(MouseEvent.CLICK, startGame)
5
  addBall()
6
  addBall()
7
  addBall()
8
  tmr.start()
9
  stage.addEventListener(Event.ENTER_FRAME, gameLoop)
10
}

Paso 14: Función addBall()

Primero hacemos una nueva instancia de nuestra clase Ball. Posicionamos la ball aleatoriamente en el escenario con un alfa de 0 y lo agregamos a la lista de visualización.

A continuación, volvemos a conectar el alfa a 1. (Utilizo TweenMax, está incluido en los archivos de origen. También puede usar el motor de interpolación Flash incorporado). La segunda interpolación no es realmente una interpolación. Simplemente espera un segundo y la función onComplete empuja la ball hacia nuestra matriz de eballs. De esta forma, la función gameLoop() (explicada en el siguiente paso) puede manejar el resto.

1
2
private function addBall():void
3
{
4
  var ball:Ball = new Ball(10, Math.random()*Math.PI*2, 5)
5
  ball.x = Math.random()*stage.stageWidth
6
  ball.y = Math.random()*stage.stageHeight
7
  ball.alpha = 0
8
  stage.addChild(ball)
9
  TweenMax.to(ball, 0.5, {alpha:1})
10
  TweenMax.to(ball, 0, {delay: 1, onComplete:function():void{eballs.push(ball)}})
11
}

Paso 15: Función gameLoop()

Cada cuadro pasará por esta función.

1
2
private function gameLoop(e:Event):void
3
{
4
  for (var i:uint = 0; i < eballs.length; i++)
5
  {
6
    for (var j:uint = i + 1; j < eballs.length; j++)
7
    {
8
      if (collision(eballs[i], eballs[j]))
9
      {
10
        doCollision(eballs[i], eballs[j])
11
      }
12
    }
13
14
    if(collision(eballs[i], ballPlayer))
15
    {
16
      endOfGame()
17
      break
18
    }
19
20
    eballs[i].update()
21
    checkBounds(eballs[i])
22
  }
23
}

Comenzamos iterando a través de todas nuestras bolas enemigas.

El segundo for-loop comprueba las colisiones entre las bolas enemigas. El ciclo comienza en 'i + 1'. De esta forma no verificamos las colisiones.

A continuación, verificamos si ballPlayer golpea la bola enemiga. Si es así, el juego está terminado. Luego actualizamos la posición de nuestra bola enemiga.

Nos aseguramos de que las bolas permanezcan en la pantalla del juego llamando a la función checkBounds() (explicada más adelante).


Paso 16: función collision()

Esta función verifica si un par determinado de bolas está colisionando.

Primero calculamos la distancia xy la distancia y entre las dos bolas. Usando el Teorema de Pitágoras (ver el siguiente diagrama) calculamos la distancia absoluta entre ellos. Si la distancia es menor o igual a la suma de los radios de las bolas, tenemos una colisión.

Flash collision game tutorial
1
2
private function collision(ball1:Ball, ball2:Ball):Boolean
3
{
4
  var xDist:Number = ball1.x - ball2.x
5
  var yDist:Number = ball1.y - ball2.y
6
  var Dist:Number = Math.sqrt(xDist * xDist + yDist * yDist)
7
8
  return Dist <= ball1.radius + ball2.radius
9
}

Paso 17: Función doCollision()

Esta función calculará las nuevas velocidades x e y de las bolas según la velocidad y el ángulo de la colisión. Advertencia: matemática;)

Primero calculamos la distancia horizontal entre las dos bolas y luego la distancia vertical entre las bolas. Con estas distancias (y un poco más de trigonometría) podemos calcular el ángulo entre las bolas (ver diagrama).

Flash collision game tutorial

Luego calculamos lo que llamo la magnitud de cada bola. (Tenemos un vector xspeed y un vector yspeed, la magnitud es la suma de esos). Luego calculamos el ángulo de cada bola (similar al cálculo del ángulo anterior).

Luego rotamos las nuevas velocidades x e y de cada bola. Lo que realmente estamos haciendo es rotar el sistema de coordenadas. Al rotar nuestros ejes tenemos una colisión 1D. (Ver el siguiente diagrama).

Flash collision game tutorial

Newton dice que la cantidad total de energía cinética en un sistema cerrado es constante. Ahora usamos estas fórmulas:

  • v1 = (u1*(m1-m2) + 2*m2*u2)/(m1+m2)
  • v2 = (u2*(m2-m1) + 2*m1*u1)/(m1+m2)

dónde:

v1 = final xSpeedBall 1

v2 = final xSpeedBall 2

m1 = masa de bola 1

m2 = masa de bola 2

u1 = velocidad inicial de bola 1

u2 = velocidad inicial de bola 2

Las velocidades y no cambian ya que es una colisión 1D.

Con estas fórmulas podemos calcular el xSpeed ​​y el ySpeed ​​de cada bola.

Ahora tenemos las nuevas velocidades x e y en nuestro sistema de coordenadas giradas. El último paso es convertir todo a un sistema de coordenadas normal. Usamos Math.PI/2 porque el ángulo entre xSpeed ​​y ySpeed ​​debe ser siempre de 90 grados (pi/2 radianes).

1
2
private function doCollision(ball1:Ball, ball2:Ball):void
3
{
4
  var xDist:Number = ball1.x - ball2.x
5
  var yDist:Number = ball1.y - ball2.y
6
  var collisionAngle:Number = Math.atan2(yDist, xDist)
7
8
  var magBall1:Number = Math.sqrt(ball1.xSpeed*ball1.xSpeed+ball1.ySpeed*ball1.ySpeed)
9
  var magBall2:Number = Math.sqrt(ball2.xSpeed*ball2.xSpeed+ball2.ySpeed*ball2.ySpeed)
10
11
  var angleBall1:Number = Math.atan2(ball1.ySpeed, ball1.xSpeed)
12
  var angleBall2:Number = Math.atan2(ball2.ySpeed, ball2.xSpeed)
13
14
  var xSpeedBall1:Number = magBall1 * Math.cos(angleBall1-collisionAngle)
15
  var ySpeedBall1:Number = magBall1 * Math.sin(angleBall1-collisionAngle)
16
  var xSpeedBall2:Number = magBall2 * Math.cos(angleBall2-collisionAngle)
17
  var ySpeedBall2:Number = magBall2 * Math.sin(angleBall2-collisionAngle)
18
19
  var finalxSpeedBall1:Number = ((ball1.mass-ball2.mass)*xSpeedBall1+(ball2.mass+ball2.mass)*xSpeedBall2)/(ball1.mass+ball2.mass)
20
  var finalxSpeedBall2:Number = ((ball1.mass+ball1.mass)*xSpeedBall1+(ball2.mass-ball1.mass)*xSpeedBall2)/(ball1.mass+ball2.mass)
21
  var finalySpeedBall1:Number = ySpeedBall1
22
  var finalySpeedBall2:Number = ySpeedBall2
23
24
  ball1.xSpeed = Math.cos(collisionAngle)*finalxSpeedBall1+Math.cos(collisionAngle+Math.PI/2)*finalySpeedBall1
25
  ball1.ySpeed = Math.sin(collisionAngle)*finalxSpeedBall1+Math.sin(collisionAngle+Math.PI/2)*finalySpeedBall1
26
  ball2.xSpeed = Math.cos(collisionAngle)*finalxSpeedBall2+Math.cos(collisionAngle+Math.PI/2)*finalySpeedBall2
27
  ball2.ySpeed = Math.sin(collisionAngle)*finalxSpeedBall2+Math.sin(collisionAngle+Math.PI/2)*finalySpeedBall2
28
}

Para encontrar más información sobre colisiones elásticas, visite hoomanr.com.


Paso 18: Función endOfGame()

Esto se ejecuta cuando el juego termina.

1
2
private function endOfGame():void
3
{
4
  tmr.stop()
5
  Mouse.show()
6
  stage.removeEventListener(MouseEvent.MOUSE_MOVE, updatePlayerBall)
7
  stage.removeEventListener(Event.ENTER_FRAME, gameLoop)
8
9
  while(eballs.length > 0)
10
  {
11
    TweenMax.to(eballs[0], 0.5, {scaleX:0, scaleY:0, ease:Bounce.easeOut})
12
    eballs.splice(0,1)
13
  }
14
15
  TweenMax.to(ballPlayer, 0.5, {scaleX:0, scaleY:0, ease:Bounce.easeOut})
16
}

Primero, detengamos el temporizador. Mostramos el mouse nuevamente A continuación, eliminamos los detectores de eventos MOUSE_MOVE y ENTER_FRAME. Finalmente hacemos que todas las bolas en el escenario sean invisibles.


Paso 19: función checkBounds()

Esta función asegura que las bolas permanezcan dentro de la pantalla del juego. Entonces, si la pelota golpea el lado superior o inferior, invertimos la velocidad y ySpeed. si la pelota golpea el lado izquierdo o el derecho de la pantalla invertimos la xSpeed. Utiliza una lógica similar a la función de detección de colisión de bolas para comprobar si los bordes de la pelota golpean un borde de la pantalla.

1
2
private function checkBounds(ball:Ball):void
3
{
4
  if((ball.x + ball.radius) > stage.stageWidth)
5
  {
6
    ball.x = stage.stageWidth - ball.radius
7
    ball.xSpeed *= -1
8
  }
9
  if((ball.x - ball.radius) < 0)
10
  {
11
    ball.x = 0 + ball.radius
12
    ball.xSpeed *= -1
13
  }
14
  if((ball.y + ball.radius) > stage.stageHeight)
15
  {
16
    ball.y = stage.stageHeight - ball.radius
17
    ball.ySpeed *= - 1
18
  }
19
  if((ball.y - ball.radius) < 0)
20
  {
21
    ball.y = 0 + ball.radius
22
    ball.ySpeed *= - 1
23
  }
24
}

Paso 20: la completa clase de la aplicación

Hemos completado nuestra clase de Aplicación. ¡Ahora tenemos un juego funcionando!

1
2
package
3
{
4
  import flash.display.Sprite;
5
  import flash.display.Graphics;
6
  import flash.events.Event;
7
  import flash.events.TimerEvent;
8
  import flash.events.MouseEvent;
9
  import flash.geom.Matrix;
10
  import flash.utils.Timer;
11
  import flash.ui.Mouse;
12
  import com.greensock.TweenMax;
13
  import com.greensock.easing.*;
14
  public class Application extends Sprite
15
  {
16
17
    private var ballPlayer:Ball;
18
    private var eballs:Array;
19
    private var tmr:Timer;
20
    private var score:Score;
21
    public function Application():void
22
    {
23
      init();
24
    }
25
26
    private function init():void
27
    {
28
      ballPlayer = new PlayerBall();
29
      eballs = new Array();
30
      tmr = new Timer(10);
31
      score = new Score();
32
33
      stage.align = "TL";
34
      stage.scaleMode = "noScale";
35
      Mouse.hide();
36
37
      setBackground();
38
39
      score.x = stage.stageWidth / 2;
40
      score.y = stage.stageHeight / 2;
41
      stage.addChild(score);
42
43
      stage.addEventListener(MouseEvent.MOUSE_MOVE, updatePlayerBall);
44
      stage.addChild(ballPlayer);
45
46
      tmr.addEventListener(TimerEvent.TIMER, updateTime);
47
48
      stage.addEventListener(MouseEvent.CLICK, startGame);
49
    }
50
51
    private function setBackground():void
52
    {
53
      var type:String = "radial";
54
      var colors:Array = [0xffffff,0xcccccc];
55
      var alphas:Array = [1,1];
56
      var ratios:Array = [0,255];
57
      var matr:Matrix = new Matrix();
58
      matr.createGradientBox(stage.stageWidth, stage.stageHeight, Math.PI / 2, 0, 0 );
59
      //SpreadMethod will define how the gradient is spread. Note!!! Flash uses CONSTANTS to represent String literals

60
      var sprMethod:String = "pad";
61
      //Start the Gradietn and pass our variables to it

62
      var sprite:Sprite = new Sprite();
63
      //Save typing + increase performance through local reference to a Graphics object

64
      var g:Graphics = sprite.graphics;
65
      g.beginGradientFill( type, colors, alphas, ratios, matr, sprMethod );
66
      g.drawRect(0,0,stage.stageWidth,stage.stageHeight);
67
      stage.addChild(sprite);
68
    }
69
70
    private function updatePlayerBall(e:MouseEvent):void
71
    {
72
      ballPlayer.x = mouseX;
73
      ballPlayer.y = mouseY;
74
    }
75
76
    private function updateTime(e:TimerEvent):void
77
    {
78
      score.txtScore.text = String(((tmr.currentCount*tmr.delay)/1000).toFixed(2));
79
      if ((tmr.currentCount*tmr.delay) % 5000 == 0)
80
      {
81
        addBall();
82
      }
83
    }
84
85
    private function startGame(e:MouseEvent):void
86
    {
87
      stage.removeEventListener(MouseEvent.CLICK, startGame);
88
      addBall();
89
      addBall();
90
      addBall();
91
      tmr.start();
92
      stage.addEventListener(Event.ENTER_FRAME, gameLoop);
93
    }
94
95
    private function addBall():void
96
    {
97
      var ball:Ball = new Ball(10,Math.random() * Math.PI * 2,5);
98
      ball.x = Math.random() * stage.stageWidth;
99
      ball.y = Math.random() * stage.stageHeight;
100
      ball.alpha = 0;
101
      stage.addChild(ball);
102
      TweenMax.to(ball, 0.5, {alpha:1});
103
      TweenMax.to(ball, 0, {delay: 1, onComplete:function():void{eballs.push(ball)}});
104
    }
105
106
    private function gameLoop(e:Event):void
107
    {
108
      for (var i:uint = 0; i < eballs.length; i++)
109
      {
110
        for (var j:uint = i + 1; j < eballs.length; j++)
111
        {
112
          if (collision(eballs[i],eballs[j]))
113
          {
114
            doCollision(eballs[i], eballs[j]);
115
          }
116
        }
117
118
        if (collision(eballs[i],ballPlayer))
119
        {
120
          endOfGame();
121
          break;
122
        }
123
124
        eballs[i].update();
125
        checkBounds(eballs[i]);
126
      }
127
    }
128
129
    private function collision(ball1:Ball, ball2:Ball):Boolean
130
    {
131
      var xDist:Number = ball1.x - ball2.x;
132
      var yDist:Number = ball1.y - ball2.y;
133
      var Dist:Number = Math.sqrt(xDist * xDist + yDist * yDist);
134
135
      if (Dist <= ball1.radius + ball2.radius)
136
      {
137
        if (ball1.x < ball2.x)
138
        {
139
          ball1.x -=  2;
140
          ball2.x +=  2;
141
        }
142
        else
143
        {
144
          ball1.x +=  2;
145
          ball2.x -=  2;
146
        }
147
148
        if (ball1.y < ball2.y)
149
        {
150
          ball1.y -=  2;
151
          ball2.y +=  2;
152
        }
153
        else
154
        {
155
          ball1.y +=  2;
156
          ball2.y -=  2;
157
        }
158
      }
159
160
161
      return Dist <= ball1.radius + ball2.radius;
162
    }
163
164
    private function doCollision(ball1:Ball, ball2:Ball):void
165
    {
166
      var xDist:Number = ball1.x - ball2.x;
167
      var yDist:Number = ball1.y - ball2.y;
168
      var collisionAngle:Number = Math.atan2(yDist,xDist);
169
      var magBall1:Number = Math.sqrt(ball1.xSpeed * ball1.xSpeed + ball1.ySpeed * ball1.ySpeed);
170
      var magBall2:Number = Math.sqrt(ball2.xSpeed * ball2.xSpeed + ball2.ySpeed * ball2.ySpeed);
171
      var angleBall1:Number = Math.atan2(ball1.ySpeed,ball1.xSpeed);
172
      var angleBall2:Number = Math.atan2(ball2.ySpeed,ball2.xSpeed);
173
      var xSpeedBall1:Number = magBall1 * Math.cos(angleBall1 - collisionAngle);
174
      var ySpeedBall1:Number = magBall1 * Math.sin(angleBall1 - collisionAngle);
175
      var xSpeedBall2:Number = magBall2 * Math.cos(angleBall2 - collisionAngle);
176
      var ySpeedBall2:Number = magBall2 * Math.sin(angleBall2 - collisionAngle);
177
      var finalxSpeedBall1:Number = ((ball1.mass-ball2.mass)*xSpeedBall1+(ball2.mass+ball2.mass)*xSpeedBall2)/(ball1.mass+ball2.mass);
178
      var finalxSpeedBall2:Number = ((ball1.mass+ball1.mass)*xSpeedBall1+(ball2.mass-ball1.mass)*xSpeedBall2)/(ball1.mass+ball2.mass);
179
      var finalySpeedBall1:Number = ySpeedBall1;
180
      var finalySpeedBall2:Number = ySpeedBall2;
181
      ball1.xSpeed = Math.cos(collisionAngle) * finalxSpeedBall1 + Math.cos(collisionAngle + Math.PI / 2) * finalySpeedBall1;
182
      ball1.ySpeed = Math.sin(collisionAngle) * finalxSpeedBall1 + Math.sin(collisionAngle + Math.PI / 2) * finalySpeedBall1;
183
      ball2.xSpeed = Math.cos(collisionAngle) * finalxSpeedBall2 + Math.cos(collisionAngle + Math.PI / 2) * finalySpeedBall2;
184
      ball2.ySpeed = Math.sin(collisionAngle) * finalxSpeedBall2 + Math.sin(collisionAngle + Math.PI / 2) * finalySpeedBall2;
185
    }
186
187
    private function endOfGame():void
188
    {
189
      tmr.stop();
190
      Mouse.show();
191
      stage.removeEventListener(MouseEvent.MOUSE_MOVE, updatePlayerBall);
192
      stage.removeEventListener(Event.ENTER_FRAME, gameLoop);
193
194
      while (eballs.length > 0)
195
      {
196
        TweenMax.to(eballs[0], 0.5, {scaleX:0, scaleY:0, ease:Bounce.easeOut});
197
        eballs.splice(0,1);
198
      }
199
200
      TweenMax.to(ballPlayer, 0.5, {scaleX:0, scaleY:0, ease:Bounce.easeOut});
201
    }
202
203
    private function checkBounds(ball:Ball):void
204
    {
205
      if ((ball.x + ball.radius) > stage.stageWidth)
206
      {
207
        ball.x = stage.stageWidth - ball.radius;
208
        ball.xSpeed *=  -1;
209
      }
210
      if ((ball.x - ball.radius) < 0)
211
      {
212
        ball.x = 0 + ball.radius;
213
        ball.xSpeed *=  -1;
214
      }
215
      if ((ball.y + ball.radius) > stage.stageHeight)
216
      {
217
        ball.y = stage.stageHeight - ball.radius;
218
        ball.ySpeed *=  -1;
219
      }
220
      if ((ball.y - ball.radius) < 0)
221
      {
222
        ball.y = 0 + ball.radius;
223
        ball.ySpeed *=  -1;
224
      }
225
    }
226
  }
227
}

Conclusión

Eso es todo por este tutorial. Por supuesto, podrías agregar la posibilidad de reiniciar el juego, pero eso no debería ser demasiado difícil. Este ejemplo básico de colisiones elásticas se puede utilizar para juegos más grandes como un juego de billar o similar.

Espero que les haya gustado este tutorial, ¡gracias por leer!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
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.