[XNA] Definición de trayectorias para Sprites o Modelos (II de II)

Menudas curvas!  Tranquilo, porqué no, no es lo que piensas… En este artículo completaré los anteriores que he ido escribiendo, en cuanto a parametrización de trayectorias se refiere (hemos visto ya el círculo, parábola, y curva bézier entre otros tipos de trayectorias). Lo importante aquí es la idea geométrica del asunto (más que si se implementa con XNA o cualquier otra cosa), al final el código siempre será muy parecido a este, sea cual sea el lenguaje de programación. Aquí podéis descargar el código con todos los ejemplos.

 

Parametrización de una Elipse

La elipse puede parametrizarse de la siguiente forma:

Considerando que nuestra elipse tiene el punto C como centro, entonces la parametrización se define de la siguiente forma:

Implementar esto en XNA ya es coser y cantar… (sólo habría que considerar, que aquí Y+ sería Y-, ya que las coordenadas se comienzan a contar a partir de la parte superior izquierda de la pantalla a la parte inferior derecha de la misma, es decir, en la parte inferior la Y es positiva, y más arriba de la pantalla, sería positiva). Necesitamos declarar las siguientes variables:


public
Texture2D textura;

public
Vector2 posicion;

public
Vector2 posicionInicial;

public
Vector2 dimensionesElipse;

float
t;

La textura contiene el sprite que pintaremos por pantalla, la posición el punto de la pantalla sobre el cual pintaremos en cada momento el sprite. posicionInicial contiene el centro de la elipse, y dimensionesElipse contiene el ancho y alto de la misma. Finalmente t contendrá una unidad de tiempo, que iremos incrementando para parametrizar la elipse.

A continuación, inicializamos estas variables:


// Situamos el vector posición inicial en mitad de la pantalla

this.posicionInicial
= new Vector2(this.graphics.GraphicsDevice.Viewport.Width / 2, this.graphics.GraphicsDevice.Viewport.Height / 2);

this.posicion
= new Vector2();

 

// Establecemos dimensiones (ancho y alto) de la elipse

this.dimensionesElipse
= new Vector2(300,
150);

 

// Iniciamos el tiempo

this.t
= 0.0f;

 

Cargamos el sprite que vamos a pintar por pantalla:


textura
= this.Content.Load<Texture2D>(“bola”);

 

Y esta es la parte chula, en el método Update, donde parametrizamos la elipse de sobre el vector posición, a partir de las funciones wikipedianas que hemos visto anteriormente:


// Parametrización de la elipse

this.posicion.X
= this.posicionInicial.X + this.dimensionesElipse.X * (float)Math.Cos(t);

this.posicion.Y
= this.posicionInicial.Y + this.dimensionesElipse.Y * (float)Math.Sin(t);

 

t
+= 0.015f;

 

Finalmente, en elm método draw nos limitamos a pintar el sprite en la posición calculada:


this.spriteBatch.Draw(this.textura, this.posicion,
Color.White);

 

 

Parametrización de la Espiral de Arquímedes

Para parametrizar una espiral, lo haremos a partir de:

Vamos, que vista la elipse, esto no es mucho más complicado. En la lista de variables ahora tenemos el angulo polar respecto el punto P(x,y):


public
Texture2D textura;

public
Vector2 posicion;

public
Vector2 posicionInicial;

public
float anguloPolar;

 

En el método update parametrizar la espiral nos va a resultar de lo más fácil:


// Parametrización de la espiral

this.posicion.X
= this.posicionInicial.X + this.anguloPolar * t * (float)Math.Cos(t);

this.posicion.Y
= this.posicionInicial.Y + this.anguloPolar * t * (float)Math.Sin(t);

 

t
+= 0.015f;

 

Parametrización del Cardioide

¿Qué es un Cardioide? Tendrás que seguir leyendo para verlo, porqué no pienso poner ningún dibujo. Quizá los más astutos puedan hacerse una idea con sólo ver la parametrización:

Si no has intuido la forma no pasa nada… pocos son los mortales que la conocen. El código, viendo la parametrización, no es muy distinto del anterior:


this.posicion.X
= this.posicionInicial.X + this.anguloPolar * (1 + (float)Math.Cos(t)) * (float)Math.Cos(t);

this.posicion.Y
= this.posicionInicial.Y + this.anguloPolar * (1 + (float)Math.Cos(t)) * (float)Math.Sin(t);

 

Y e aquí el cardioide:

 

Parametrización de una Rodonea

 

Si la curva anterior te ha parecido rara… no se que pensarás cuando veas esta. Una rodonea se parametriza del siguiente modo:

Una vez implementado, obtendremos la curva más rara nunca vista:

 

Dicho código sería el siguiente:


this.posicion.X
= this.posicionInicial.X + this.anguloPolar * (float)Math.Cos(this.petalos
* t) * (float)Math.Cos(t);

this.posicion.Y
= this.posicionInicial.Y + this.anguloPolar * (float)Math.Cos(this.petalos
* t) * (float)Math.Sin(t);

 

 

Y por hoy creo que ya tenemos bastantes curvas… os dejo un adjunto este fichero RAR que contiene distintas soluciones XNA con la implementación de cada una de las curvas.

[XNA] Nace XBOX Live Indie Games! (sustituye el antiguo Xbox LIVE Community Games)

Es bien sabido que hasta ahora los desarrolladores de XNA han tenido a su disposición el sistema XBOX Live Community Live para vender sus “juegos caseros” a los usuarios de XBOX (podéis encontrar abundante información de este sistema en el número dos de la revista gratuita DOID). Resumiendo, el sistema nos permite vender nuestros juegos, y quedarnos hasta un 70% del margen de los beneficios (el resto es para Microsoft, en concepto de mantenimiento de la plataforma, gestión de trámites, etc). No obstante la comunidad de desarrolladores se “quejaba” de algunos problemas detectados, o mejoras que sería interesante introducir, para permitir obtener más ingresos al desarrollador: dar más prestigio y visibilidad al mercado de videojuegos de la comunidad. Esto podría mejorar sustancialmente con los cambios en el Creators Club (ahora llamado XBOX Live Indie Games). Parece que las novedades incluyen las reivindicaciones de la comunidad! Los cambios más importantes son:

  • Países. Se añade Japón y Alemania a la lista de países que pueden publicar videojuegos en la plataforma (no, no está abierto a todos los países del mundo, y sorprendemente no estaban estas dos “potencias”).
  • Reputación. Se genera un sistema de “reputación” en la comunidad, que dará más visibilidad a los desarrolladores más activos. Es algo parecido a lo que estamos acostubmrados a ver en los foros de MSDN. Así por ejemplo, superar un peer review de uno de nuestros juegos nos dará puntos (como nos los quitará no hacerlo), o que nos marquen una pregunta como respuesta válida en el foro también nos dará puntos.
  • Servicio de actualización de videojuegos. Las personas que tengan instalados los juegos publicados en XBOX Live recibirán una petición de autorización cada vez que aparezca una nueva versión (no habrá que ir a “buscar” actualizaciones “manualmente” -antes los updates se notificaban en los foros-).
  • Posibilidad de subir juegos hechos con XNA 3.1
  • Nuevo sistema de precios
  • Cambio de nombre. El canal Xbox LIVE Community Games pasa a llamarse Xbox LIVE Indie Games
  • Posibilidad de comprar los juegos en Xbox LIVE Indie Games o en Xbox.com (la web está pendiente de nuevos cambios en este sentido para finales de verano)

 

LO QUE TODAVÍA FALTA (esto ya es cosecha propia)

Todos los desarrolladores se quejan de lo mismo: falta promoción. Sí, se venden juegos en XBOX Live… pero no es nada teniendo en cuenta los millones de usuarios conectados al servicio en todo el mundo. De hecho, ahora mismo no es viable económicamente publicar videojuegos allí si lo que queremos es ganarnos la vida con ello (Microsoft publicó los números hace ya meses). No nos servirá para ganarnos la vida, pero sí para llamar la atención a productores de videojuegos o empresas de desarrollo de más altos vuelos.

Personalmente creo que es cuestión de tiempo que el merkatplace vaya mejorando, sobretodo cuando la comunidad de desarrolladores crezca todavía más y la calidad de los juegos vaya aumentando (ojo, que hay ya mismo juegos muy buenos disponibles). En cualquier caso es notable la voluntad de mejora que tiene el equipo de desarrollo de XNA y XBOX Live.

Los airados desarrolladores habían comenzado a aburrir la palabra “community”, que al parecer no es lo suficientemente “prestigiosa” para su marketplace.

Vista del nuevo “Indie Games”:

Sistema de ratings:

[XNA] Rotar un sprite en 2D y desplazarlo hacia un vector posición

Lo que vamos a hacer es dibujar un sprite que rote hacia la dirección del mouse. Es decir, que moveremos el mouse y el sprite, en mitad de la pantalla, irá girando, de forma que quede orientado hacia el puntero del ratón. Después parametrizando el vector dirección resultante, haremos que el sprite se mueva hacia donde esté el ratón en dirección recta. Una vez sacado el polvo a los viejos libros de trigonometría es sencillo de hacer. El código es fácil, pero un diagrama de clases siempre ayuda a ver las cosas mejor.

La clase Game es la encargada de instanciar a los objetos MouseInput (controla el ratón y su posición en la pantalla, así como sustituir la textura del cursor) y sprite (imagen 2D que se pintará, rotará y desplazará por la pantalla siguiendo al mouse).

A ver… si sabemos el punto orígen, y el punto destino… necesitamos saber el ángulo de un triángulo… sabemos que uno de sus ángulos es recto… (tenemos casi todos los datos!), como era? ahh sí, con el Arcotangente!

Con un poco de wikipedia conseguiremos escribir el código siguiente:

float angulo = (float)Math.Atan2(raton.posicion.Y – nave.posicion.Y, raton.posicion.X – nave.posicion.X);

Ahora sólo tenemos que utilizar el angulo obtenido para rotar el sprite -ovsérvese que en el parámetro orígen hay que pasar un nuevo vector que es el centro del sprite que vamos a dibujar, para que la rotación la haga sobre ese eje-:

spriteBatch.Draw(this.textura, this.posicion, null, Color.White, anguloRadianes + MathHelper.ToRadians(90), new Vector2(this.textura.Width / 2, this.textura.Height / 2), 1.0f, SpriteEffects.None, 1);

Con ello ya tenemos la nave que va rotando sobre si misma y que “mira” siempre al puntero del mouse a medida que lo vamos moviendo por la pantalla.

Ahora, si queremos que la nave se mueva en la dirección del ratón.. ¿como lo hacemos? Si como a mi os falla la memoria recurriremos nuevamente a la wikipedia… Finalmente podremos actualizar la posición del sprite “nave” con el siguiente código:

 float velocidadUnitaria = 3.0f;

this.velocidad.X = (float)Math.Cos(anguloRadianes) * velocidadUnitaria;
this.velocidad.Y = (float)Math.Sin(anguloRadianes) * velocidadUnitaria;

this.posicion += this.velocidad;

La velocidad en los ejes X e Y se puede calcular a partir del coseno y el seno del ángulo que habíamos calculado previamente, y multiplicándolo por un valor estático (simplemente para hacer que el sprite se mueva un poco más rápido hacia el ratón). Finalmente se modifica la posición del sprite en función de la velocidad.

Si ejecutamos la aplicación, el resultado será parecido a este:

El código se puede descargar en este enlace

[XNA] Definición de trayectorias para Sprites o Modelos (I de II)

En un videojuego nos puede interesar que los sprites (2D) o Modelos (3D) se muevan en trayectorias no lineales… Esto dará a nuestras animaciones más “naturalidad” y por consiguiente realismo. Si repasamos un poco de geometría y física de cuando eramos unos chavales podremos conseguir algunas trayectorias interesantes sin demasiado esfuerzo. Los ejemplos están realizados con XNA… la idea básica evidentemente es válida para cualquier plataforma de desarrollo.

 

Parametrización de una circunferencia (trayectoria circular)

Podemos encontrar cualquier punto de una circunferencia con radio r, con centro en el punto (x, y) tiene las coordenadas: P = (r•cos(angulo) + x0, r•sin(angulo) + y0). Esto se ha obtenido a partir de la siguiente expresión, obtenida a partir del Teorema de Pitágoras (sin entrar en mucho más detalle en el concepto matemático):

Es decir, que para trazar una trayectoria circular de cualquier objeto, podremos utilizar simplemente el siguiente código en el método Update de nuestro Game en XNA:

// (x,y) = (r•cos(angulo) + x0, r•sin(angulo) + y0)
float r = 100;

if (angulo == 360)
    angulo = 0;
else
    angulo+=0.05f;

posicion = new Vector2(r * (float)Math.Cos(angulo) + posicionInicial.X, r * (float)Math.Sin(angulo) + posicionInicial.Y);

Donde la variable posicion es un Vector2 (la posición en cada momento del juego del elemento formando una trayectoria circular), y la variable posicionInicial es el centro del círculo (también de tipo Vector2). Tenéis el código completo disponible en el fichero adjunto Trayectoria_Circular.rar, que lo que hace es mover un sprite trazando precisamente una trayectoria circular.

Por el mismo precio, dejo otra versión del código que además de mover un sprite simple, lo va rotando a medida que va siguiendo la trayectoria de la circunferencia. Esto es muy útil, en nuestro caso hemos puesto un bitmap que es un coche visto desde arriba, dando la sensación de que este está moviéndose pero girando su dirección (no simplemente dando vueltas orientado siempre en la misma dirección). Para ello rotaremos el bitmap en el mismo ángulo en cada momento en que estemos obteniendo la posición, así tendremos un bitmap en ángulo recto respecto este ángulo (esto se ve mejor con un dibujo):

Esto lo haremos con el código siguiente:

spriteBatch.Draw(this.sprite, this.posicion, null, Color.White, angulo, new Vector2(this.sprite.Width / 2, this.sprite.Height / 2), 1.0f, SpriteEffects.None, 0f);

En el adjunto Trayectoria_Circular_RotacionSprite.rar he dejado el ejemplo completo, en el que se ve un coche dando vueltas de forma circular, hasta el infinito…

 

Trayectoria de un proyectil

Esto es física de hace ya años… aunque reconozco que he tenido que rascarme la cabeza para volver a encontrar las fórmulas para parametrizar la trayectoria de un proyectil 😛

Se consigue con la fórmula siguiente:

x = v0 • cos(angulo) • t
y = v0 • sin(angulo) • t – (1/2) • g • t^2   

Observad que no se ha considerado ninguna fricción con el aire, por lo que la velocidad en el eje X sería constante. Esto, en código, viene a suponer lo siguiente:

this.posicion.X = this.posicionInicial.X + v0 * (float)Math.Cos(angulo) * t;
this.posicion.Y = this.posicionInicial.Y + v0 * (float)Math.Sin(angulo) * t – (float)(.5 * g * (t * t));

Esto teniendo en cuenta que hemos declarado las siguientes constantes generales:

const float v0 = 50.0f;
const float angulo = 80.0f;
const float g = -4.0f;

Velocidad inicial, el ángulo del disparo y la gravedad. Si ejecutamos el código Trayectoria_DisparoParabolico.rar veremos el movimiento aproximadamente parabólico del proyectil.

Trayectoria de una curva lineal Bézier

Esta quizá sea la más interesante. La idea de una curva Bézier no es compleja. Conocida la posición de dos puntos (a los que se les llama nodos), definiremos unos puntos “invisibles” llamados puntos de control. Dicho esto el Sr. Pierre Bézier definió una expresión matemática para definir esta curva ya en 1960 (que bonita es la historia).

Función de una curva lineal (no se porqué se le llama curva si acaba dibujando una recta, pero bueno…):

Función de una curva cuadrática. Esta es la que nos interesa. Dibuja una curva entre dos nodos mediante dos puntos de control. Se obtiene mediante la función:

 

Este dibujo ilustra la curva bézier cuadrática (como me gusta el nombre jeje):

La expresión que ya hemos visto se traduce del siguiente modo en XNA:

this.posicion = (float)Math.Pow((1 – t), 2) * pos0 + 2*t*(1-t) * pos1 + (float)Math.Pow(t, 2) * pos2;
t += 0.005f;

¿Fácil? 😛 Nuestro amigo Pierre era un crack, todo hay que decirlo. Ala, aquí os dejo el código por si queréis ejecutarlo y ver como queda: Trayectoria_Beizer.rar

 

Nota: Varias de las imágenes y fórmulas han sido obtenidas de la Wikipedia

[GameDev] Visual Studio como entorno de desarrollo de videojuegos

Para desarrollar videojuegos con Visual Studio hoy por hoy tenemos varias opciones. Las hay para todos los gustos, y al contrario de lo que piensa mucha gente, no sólo está XNA. Aquí enumero algunos de los más utilizados y explico como configurarlos.

XNA
Basado en C# y las librerías gráficas XNA desarrolladas por Microsoft. Ideal para introducirse en el mundo del desarrollo de los videojuegos 2D y 3D. Esos pueden ser desplegados en los siguientes dispositivos: PC, XBOX 360, y ZUNE. Además incluye la ventaja de que puedes publicar tus juegos en XBOX Live y ganar algun dinero vendiéndolos, algo que sería bastante más difícil con una distribución própia si eres un desarrollador “amateur”.

Todo el software necesario para desarrollar con XNA es gratuito:

Visual C# 2008 Express Edition

XNA Framework

GDK

Otra plataforma de desarrollo de videojuegos 2D y 3D creada por Microsoft. Esta vez funciona con código C++, con el enorme potencial que ello ofrece (en cuanto a performance y compatibilidad). Existe una gran comunidad de GDK que ofrece herramientas y material gratuito para el desarrollo de videojuegos, como modelos3D, sonidos, tutoriales, y modeladores 3D, etc. Se podría considerar un entorno de desarrollo profesional. También existe una versión para .NET, que puede funcionar con C# y VB.NET entre otros. También pueden encontrarse frameworks específicos que funcionan como una capa superior a este (engines) de Física, AI, etc de gran calidad, alguno de ellos desarrollado por NVIDIA.

De nuevo, el software es gratuito:

Visual Studio 2008 Express Edition

Dark GDK

 

OPENGL/GLUT

GLUT podría decirse que es la versión para windows de OPENGL. ¿Qué decir de OPENGL? Es multiplataforma, open source, y lleva ya años de carrera… Los desarrolladores nos aseguran que sus aplicaciones son portables a cualquiera de las plataformas que soporte OPENGL (son muchas).

Visual Studio 2008 Express Edition

GLUT

Tutorial de instalación y uso en Visual Studio C++

 

¿Otros entornos potentes para desarrollar juegos con Visual Studio? Si crees que hay otras opciones interesantes comentalo y las iré añadiendo 🙂

[XNA] Detección de colisiones entre una Cámara y un Terreno

En mi anterior blog escribí un artículo en el que se explicaba como generar un terreno en XNA a partir de un mapa de alturas (catalán). Vamos a partir de ese ejemplo para este nuevo artículo.

Una problematica común en un juego es que movamos directamente la cámara (en primera persona) sin “atravesar las paredes”. Sería el caso típico de juegos como Quake, Unreal, y también de coches, entre otros. Aquí vemos un ejemplo de juego con la cámara en primera persona:

Normalmente las colisiones entre modelos 3D dentro del juego se detectarían mediante BoundingSpheres, lo cual de entrada tiene que ser relativamente fácil. Es fácil porque de entrada todos los modelos tienen o pueden tener un BoundingSphere que los “rodee”, como se puede ver en la imagen siguiente. Posteriormente utilizando el método “Intersects” de una instancia de la clase BoundingSphere de XNA podemos saber si hay colisión o no entre estos dos objetos…

Con el terreno generado mediante un mapa de alturas el problema hay que afrontarlo de otro modo… no se puede utilizar la clase BoundingSphere ni BoundingBox por motivos evidentes: la detección de colisiones sería enormemente imprecisa. Por lo tanto esta solución es difícil que nos sirva para los terrenos. ¿Qué hacemos entonces? En internet se pueden encontrar multitud de aproximaciones/soluciones. A mi lo que me parece más sencillo es lo siguiente: en el momento de generar el terreno a partir del mapa de alturas, estamos creando vértices para nuestro nuevo modelo 3D. Miremos de almacenar esa información de forma fácilmente accesible, por ejemplo en una lista de vértices. Cuando movamos la cámara… sólo deberemos buscar el valor de Y para el terreno, en relación a la posición Y del vector posición de la cámara. Miremos de ilustrarlo con un fantástico dibujo para que se entienda mejor… :

La idea es simplemente que al desplazar la cámara por cualquiera de sus ejes, se compruebe el elemento Y del vértice del terreno más cercano para la posición X-Z de la cámara. Si el usuario mueve la cámara a una posición en la que hay colisión, incrementamos la posición de la camara para evitar la misma. De este modo obtenemos la sensación de que la cámara “camina” por encima del terreno generado. El resultado es el siguiente:

Os dejo además el enlace al código, para que podáis probarlo. Recordad que para ejecutarlo es necesario tener instalado el XNA Framework Redistributable 3.0 o superior.

[XNA] Jugando con las Leyes de Newton con XNA (I de II)

Las maravillosas leyes de newton… ¿quién no las recuerda? No nos engañemos, son amadas y “odiadas” por igual. Pero vayamos a ver un ejemplo basado en XNA, que seguro que lo hará más ameno (lástima que no estaba XNA cuando estudié esto :-P).

No voy a dar la vara recordando las leyes de newton… pero sí que enumeraré, a modo de recordatorio, lo que podemos calcular con ellas:

  • I = m · v (I= Inercia, m = masa, v = velocidad)
  • A = F/m (F = Fuerza, a = aceleración, m = masa)
  • Para cada acción, existe una reacción opuesta (esto lo veremos en la segunda parte del artículo con mayor detalle)

A partir de estas leyes, conociendo muy poco XNA, y paciencia, queremos hacer una pantalla en la que aparezcan objetos, los cuales al ser “clicados” con el ratón, se desplacen en base a unas leyes de newton.

En la segunda parte del artículo, utilizaremos la tercera ley de newton para detectar las colisiones entre unos objetos y otros (en la primera parte sólo se detectará la colisión entre el puntero del ratón y el propio objeto).

Lo que vamos a obtener con el código de esta primera parte es algo parecido a lo siguiente:
Lo primero que haremos es crear nuestro proyecto XNA y añadir una clase, la llamaremos “ObjetoFisico”. Muestro los atributos privados más destacados (son bastante autodescriptivos):

  • public Vector2 posicion
  • public Vector2 acceleracion
  • public Vector2 velocidad
  • public Vector2 friccion: indica la fuerza de fricción en un momento determinado
  • public float masa: masa del objeto
  • const float coeficienteFriccion: coeficiente de fricción (es un porcentaje sobre la velocidad)
  • public Texture2D textura: representa la textura que se “pintará” en pantalla

Básicamente lo que haremos será construir una lista de objetos de tipo “ObjetoFisico” y repartirlos por la pantalla. Con el constructor (public ObjetoFisico(Vector2 inPosicion, Texture2D inTextura)) de esta clase podremos inicializar la posición al valor deseado, así como la textura. La aceleración y velocidad son de uso interno, y las inicializaremos a cero (Vector2.Zero).

Ahora vamos a construir la lógica del objeto. Crearemos un método público: Update(). Este calculará la fricción, aceleración, velocidad, y finalmente posición. Además leerá el “input” del usuario. Pulsando el botón izquierdo del ratón sobre el objeto alteraremos su posición. Cuanto más cerca del centro del objeto pinchemos, con mayor aceleración este será “despedido”. Esto lo hacemos con las líneas de código siguientes:

// Establecemos el vector fuerza, cuanto más cerca del centro “impacte” el puntero del objeto
// más suave será la fuerza

Vector2 fuerza = new Vector2(posicion.X – Mouse.GetState().X + this.textura.Width / 2, posicion.Y – Mouse.GetState().Y + this.textura.Height / 2);

// Ahora aplicaremos la fuerza a la aceleración del objeto (según newton aceleración = fuerza / masa), así pues…

this.acceleracion.X += (fuerza.X * 10) / masa;
this.acceleracion.Y += (fuerza.Y * 10) / masa;

Para terminar con la clase, añadiremos un método que dibuje el objeto en pantalla “Draw()”.

La clase principal de la aplicación -Game.cs- inicializará una lista de “ObjetoFisico”, con valores más o menos aleatorios, y una textura predeterminada. El método update() y draw() de esta clase se limitarán a recorrer la lista de objetoFisico y llamar a los respectivos métodos: Update() y Draw(). Esto es así de sencillo:
Para el método Update():

// Actualizamos todos los objetos de la lista

foreach (ObjetoFisico objeto in objetos)
{

    objeto.Update(gameTime);

}

Finalmente ejecutamos y pulsamos sobre los “objetos” que hay por la pantalla. Veremos como los objetos se mueven con una aceleración proporcional a la distancia del centro de la textura respecto la cual hagamos click. Los objetos llegarán al estado de reposo (1a ley de newton) gracias a la fricción.

En el próximo “episodio” los objetos podrá interactuar unos con otros (rebotando básicamente), completando este experimento.

Podéis encontrar el código adjunto a este artículo