Sobre Plasma

Ha habido varias cosas que cambiaron desde la última entrada. No voy a dar todos los detalles pero voy a ir contando entrada a entrada las cosas que he aprendido y he descubierto últimamente.

Uno de los cambios más grandes que he hecho es probar durante este último mes el escritorio Plasma. Digo más grandes porque siempre, siempre desde el primer momento que instalé GNU/Linux preferí Gnome a KDE. Pero los tiempos cambian, y a veces hay que ver que anda pasando en la vereda de enfrente.

Lo bueno

Una de las sorpresas más gratas que me llevé es que ahora KDE… o más bien “El Escritorio antes conocido como KDE” (porque ahora es Plasma) es el escritorio más liviano que hay en GNU/Linux. Sí, es bastante increíble, pero su uso de memoria esta cercano al de mi sesión en Fluxbox (super engordado con gestores de sesion y varios demonios extra obviamente).

fit-width

Fuera de eso noto una tendencia a simplificar la interfaz, sin llegar a los extremos. Entonces quienes encontrabamos en Gnome 2 una interfaz limpia y entendible podemos estar como en casa.

Por último los escritorios más modernos me acostumbraron a pequeñas cosas como el efecto tipo “Exposé” que es muy agradable, y demás efectos visuales. Llamenme frívolo pero a mi me gusta que mi escritorio diario se vea bien .

Lo malo

Algo que si he de decir, es que en algunas ocasiones se a portado algo inestable. Nada “rompedor de sesión” pero si tiene sus crashes, aunque no son tan frecuentes.

Lo feo

Plasma ha mejorado mucho en interfáz gráfica, pero todavía tiene un camino largo para recorrer en terminos de simplicidad.


Aprendiendo ReactJS

Hace bastante que tenía pendiente ponerme a aprender algo de ReactJS, así que me puse manos a la obra y comencé a reescribir una versión online de mi Curriculum Vitae utilizando React . Hasta ahora va bastante bien, aprendí a crear componentes y un poco como trabaja el ciclo de vida de los componentes. Cargar datos asincrónicos y demás. Esto lo estoy haciendo sobre una aplicación web escrita desde 0 en Go, que puede encontrarse aquí. Eventualmente probablemente le agregue una interfáz administrativa básica, o quizás directamente lo integre con este blog.

El Blog

Como pueden ver renové un poco el look del blog, quería algo más funcional y original. Ahora tiene un toque más personalizado sin dejar de ser un diseño simple y legible. Por otra parte instalé OwnCloud para poder sincronizar los archivos desde mi PC a mi laptop, con suerte me ayudará a mantener los proyectos más ordenadamente.

Cosas personales

En este mes me tengo que mudar, por lo cual no se cual sea el tiempo que disponga para seguir publicando actualizaciones.


un vicio nuevo, eBooks

El sábado por la mañana me levanté tarde (aunque no tan tarde como de costumbre) a atender la puerta y me sorprendió que había llegado el lector de libros que encargué (en teoría debía de llegar hoy). Hace mucho quería comprar uno, la realidad es que soy un poco adicto a los libros, lo cual me esta costando un dineral y cada vez me complica más por varias cuestiones. Una es el dinero que cuestan hoy en día los libros, las ediciones impresas son realmente caras, las peores ediciones son de 120$, ni hablar de que si es un libro medianamente largo que se va de lo que es literatura clásica, los precios saltan de manera exorbitante; por otra parte ya no tengo espacio físico en mis dos bibliotecas y por último (aunque esto no puedo solucionarlo más que con organización y voluntad) se me han acumulado varios libros que no he podido terminar de leer.

fit-width

Ni hablar de que hay libros que no se consiguen por estos lados, no solo de programación, sino otros como la saga “Discworld” de Terry Pratchet (que me esta gustando mucho).

En lo que va del fin de semana, estoy a punto de terminar “El Color de la Magia” de Terry Pratchet y también el ensayo “Dios y Estado” de Mijail Bakunin, así que de momento le estoy sacando mucho provecho al aparatito. También he encontrado varios sitios donde bajar libros gratuitos, en especial recomendaría “Lectulandia” y “La biblioteca anarquista” (the anarchist library)

Para quienes sean fanáticos de la lectura, les recomiendo considerar adquirir uno, no son tan caros y con el valor actual de los libros impresos seguramente recuperaran la inversión en poco tiempo.


Varias cosas

En las ultimas semanas hice varias cosas:

Juego

Tengo que hacer una entrada sobre las ultimas funcionalidades añadidas al juego. En las últimas iteraciones le he agregado animaciones, proyectiles, detección de daño, y soporte para varios jugadores. Igualmente falta mucho por hacer (menues, IA básico, scrolling de pantalla), aquí un video de como esta quedando:

Extensión de Nemo

Una de las cosas que me gustan del open source es que cuando encontramos cosas que no se adaptan exactamente a nuestro flujo de trabajo podemos ensuciarnos un poco las manos y tomar el código existente para lidiar con eso. En este caso encontré que Nemo estaba utilizando File-Roller de Gnome, mientras que yo suelo utilizar engrampa de MATE. Luego de buscar y ver que había gente que pedía esta funcionalidad decidí hacer un fork muy básico, ya que ambas herramientas utilizan los mismos flags en la linea de comandos (solo tuve que cambiár el nombre del binario)


sobre el #NiUnaMenos y el #NadieMenos

El dia de ayer fue un día bastante reflexivo debido a la protesta contra la violencia de género, me llego mucho al alma el reclamo, por lo espontáneo y la repercusión que tuvo (aunque para mi una hora de paro es medio flojito, pero bueno yo soy medio criticón por naturaleza) y sobre todo la causa que lo disparó.

fit-width

También hubo una contracara, un #NadieMenos que me puso a pensar. Entiendo en parte el fundamento de querer hacer una protesta más abarcativa, pero eso no desacredita el derecho a hacer una protesta por causas específicas, no dudo que la intención de la gente posteando #NadieMenos no sea sincera. Pero creo que nos falta entender que si bien, todos queremos que la violencia se acabe, tenemos que tomar cartas en el asunto por nuestras causas y no esperar que otros adapten sus lemas para cambiarlas.

Algunas cifras

Datos sacados de acá, basados en datos del INDEC

Los homicidios en Argentina se se cuentan en 2746 victimas del genero masculino por año (7 hombres x día) y 524 mujeres asesinadas al año . De esa cifra 277 mujeres son asesinadas por violencia de genero(1 cada 30 horas) mientras que de la altisima cifra de hombres asesinados al año solo una cifra de 30 al año son por violencia de género (lo cual es igualmente trágico, pero decididamente menor en cantidad). Entonces claramente tenemos un sesgo , claramente las mujeres tienen una causa identificable de muerte predominante , mientras que los hombres (si bien esos 30 importan mucho) somos minoría en violencia de género.

La marcha, y protesta fue en contra de esas muertes. Una causa clara e identificada. Entonces los invito a hacer una reflexión ¿cada vez que haya una marcha en contra de los asesinatos violentos, vemos un #TambiénNiUnaMenos ? Cuando se protesta por las muertes por accidentes viales ¿vamos a reclamar que también se pida por la gente que muere de sobredosis o asesinada?

Entonces invitate a pensar ¿por qué una contraprotresta? contra algo que realmente no te perjudica. A mi me encantaría que se reduzcan todas las muertes, pero ayer se peleo por esto ¿para que lo voy a boicotear? ¿en que parte estas en desacuerdo?

¿Qué hacer?

Si realmente nos calienta el #NadieMenos, y no queremos simplemente “hacer callar a las de #NiUnaMenos”, tal vez en realidad deberíamos seguir el ejemplo y protestar, tenemos todo el resto de los días del año para organizarnos. Para protestar contra los pibes asesinados por la policia en las villas, protestar contra las muertes políticas, contra la represión, la desigualdad social detras de muchos casos de violencia. Como hombre, tengo que decir que que hasta me da un poquito de verguenza ver que las mujeres se animan a protestar y nosotros temerosos (adoctrinados como el supuesto “sexo fuerte”) les estamos diciendo “no se quejen, no protesten”, mientras no hacemos nada por nuestros muertos ni nuestras tragedias. Nos volvemos un instrumento del status quo que no beneficia a nadie, ni a nosotros mismos.

Mientras tanto incluso si no querés marchar o protestar, simplemente no obstaculizes, porque si realmente te interesa que no muera nadie más, entonces por definición también te interesa que ellas dejen de morir y deberías estar a favor de la protesta, no en contra.


Paveando con elixir

Este fin de semana estuve jugando un poco con Elixir. Elixir es un lenguaje de programación funcional muy similar a Ruby implementado sobre la VM de Erlang. Es un lenguaje interesante y aunque la sintaxis es algo similar a Ruby apenas rascamos un poco la superficie y las diferencias se notan muchísimo.

Elixir es puramente funcional, esta diseñado para manejar de manera eficiente la programación concurrente.

Entre las cosas que me llamaron la atención son:

  • la utilidad mix

  • los guards (guardias?)

  • pattern matching

mix

Mix es una utilidad de linea de comandos, al parecer extensible, permite instalar nuevas funcionalidades, crear proyectos y demás. Parece ser muy flexible y potente, y me llamo la atención que sea incluído por defecto en el lenguaje. Debo admitir que no estoy todavía interiorizado con la mayoría de sus comandos (solo lo utilize para instalar hex y subsecuentemente Phoenix un framework web que aún no he probado).

El comando mix new por ejemplo nos dejará hacer un nuevo proyecto ya listo con sus carpetas de test y configuración


 elixir $ mix new example2
* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/example2.ex
* creating test
* creating test/test_helper.exs
* creating test/example2_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd example2
    mix test

Run "mix help" for more commands.

Guards

Al definir las funciones en Elixir podemos utilizar guards, que básicamente hacen que ese método solo sea utilizado en caso de cumplirse x condición (is_list, is_integer). También podemos definir nuevos guards a traves de macros ( no se pueden llamar funciones externas/remotas) desde un guard. He aquí un ejemplo bastante trivial y probablemente demasiado complicado para lo que hace, que toma edades desde la linea de comandos y determina si son mayores de edad o no.


defmodule Example do
  def main(args) do
    Enum.each(args, fn(arg) -> determine( elem( Integer.parse( arg ),0)  ) end ) 
  end

  defmacro is_adult(age) do
    quote do: 18 <= unquote(age) 
  end

  defmacro is_minor(age) do
    quote do: 18 > unquote(age)
  end

  def determine(arg) when is_adult(arg) do
    IO.puts "Mayor de edad"
    IO.puts "Edad ingresada: #{arg}"
  end


  def determine(arg) when is_minor(arg) do
    IO.puts "Menor de edad"
    IO.puts "Edad ingresada: #{arg}"
  end

  
end

Pattern Matching

En elixir la expresion 5 = x por ejemplo realiza una operación de matching, es por eso que si hacemos esto:


iex(1)> x = 5 
5
iex(2)> 5 = x
5
iex(3)> 3 = x
** (MatchError) no match of right hand side value: 5

Arroja un error del tipo MatchError. Raro no?

En fin. He visto muy poquito aún de Elixir, pero sin dudas parece un bicho lo suficientemente extraño como para analizarlo, eventualmente me gustaría ver bien como trabaja con multiples hilos y probar el framework Phoenix.


Agregando un Popover a una aplicación Ruby Gtk

La aplicación Pomarola es un poquito distinta a los demás relojes pomodoro que he visto, básicamente la idea era generar un diario de trabajo basado en la cantidad de pomodoros trabajados por día. Es por eso que al fin me puse manos a la obra para poder guardarlos en un formato JSON. Para ello quería añadir un menu a mi aplicación. Resulta que los nuevos menues gtk son algo poco intuitivo de cara al desarrollo y por eso me costo bastante encontrarle la vuelta. Es por eso que aquí hay un pequeño tutorial de como agregar un menú tipo popover a nuestra aplicación.

fit-width

El boton de menu

Es conveniente utilizar el botón de menu provisto por GTK (GtkMenuButton), esto nos permitirá definir si lo que queremos es un menu popover. Para ello en las opciones del MenuButton seleccionamos “Ventana Emergente” y haciendo click en el pequeño icono de la entrada de texto creamos una nueva ventana emergente (nuestro popover). Este nos servirá en el código, más no podremos editarlo directamente desde aquí.

fit-width

Los archivos UI

Un popover no es simplemente un menú tradicional (en cuyo caso simplemente utilizariamos un TreeModel y lo poblaríamos. Sino más bien un widget contenedor con widgets hijos. Es por eso que debemos diseñar y guardar en un archivo ui aparte el contenedor de la siguiente manera:

fit-width

El código

Creamos nuestro popover en Glade, así como el archivo UI necesario, es por eso que ahora deberemos indicar en nuestro código que queremos utilizar este archivo para poblar los contenidos de nuestro popover:


  def generate_popover()
    @popover = @ui.get_object "popover1"
    @popover_ui = Gtk::Builder.new(:file => "./ui/menu.ui")
    @popover_contents = @popover_ui["popover"]
    
    @popover_ui.connect_signals do |handler|
        method(handler)
    end

   
    @popover.add @popover_contents
  end

Paso a paso

Obtenemos de nuestro archivo UI principal el objeto con el ID “popover1”, este es el widget que mostrará el GtkMenuButton al pulsarlo, pero ahora mismo se encuentra vacío

    @popover = @ui.get_object "popover1"

Cargamos nuestro archivo de menu, y seleccionamos el widget con el ID “popover” dentro de el (quizás debería haber utilizado nombres distintos para evitar confusión)

    @popover_ui = Gtk::Builder.new(:file => "./ui/menu.ui")
    @popover_contents = @popover_ui["popover"]

Conectamos las señales de los widgets del popover (no es necesario hacerlo de esta forma)

    @popover_ui.connect_signals do |handler|
        method(handler)
    end

Añadimos los contenidos de este nuevo archivo UI a nuestro popover.

    @popover.add @popover_contents

Y listo! Como dije anteriormente el contenido de los popover es básicamente un Container Gtk (creo que más especificamente un Gtk::bin), por lo tanto podremos añadir dentro de el grillas, cajas, barras de progreso y botones esto posibilita menus complejos como los nuevos menues de gEdit y los de otras nuevas aplicaciones de Gnome.


Pomarola, evil-mode y otras cosas

Hola!

Despues de tanto tiempo vuelvo a bloguear. Sinceramente extrañaba mucho escribir por acá (ahora en nuevo dominio), ya que es una herramienta que me mantiene motivado a seguir aprendiendo cosas y mantiene un buen registro de las cosas que he aprendido previamente (lo cual me ha servido más de una vez).

Emacs, I3, RSI y Evil

Tanto joder con atajos de teclado, hace un tiempo me empezó a molestar la mano izquierda y me di cuenta que entre Tmux, i3 y Emacs estaba haciendo mucho uso de atajos de teclado con esta misma. Así que lamentablemente (a pesar de que me encontraba muy a gusto con ese manejador de ventanas) tuve que abandonar i3 y volví al tradicional escritorio MATE. No pensaba dejar Emacs tan fácilmente, pero si tengo que admitir que el lado oscuro (VIM) tiene atajos que no cansan tanto las muñecas, y que los modos, una vez que te acostumbrás son más fáciles. Es por eso que ahora mismo me encuentro utilizando evil-mode y god-mode que me emulan bastante el workflow de Vim sin perder todas las ventajas de Emacs (la indentación, paquetes, org-mode, etc)

Pomarola

Hace un tiempo que quiero implementar la técnica pomodoro y vi que la app para linux solo soporta el escritorio GNOME. Es así que me decidi a hacer una aplicación en Ruby para aplicar esta técnica a mi trabajo (inicialmente iba a ser en Go, pero la librería esta rota para gtk3.20). No es la gran cosa, pero lo bueno es que al finalizar un pomodoro, guarda la entrada debajo así podemos editar el nombre. La idea es eventualmente poder exportarla a diversos formatos y hacer una suerte de work log. Ahora mismo está muy verde (no se puede guardar permanentemente), pero como timer pomodoro básico funciona bastante bien (con notificaciones de escritorio y toda la cosa).

fit-width

Libros

Estoy leyendo la Trilogía del Asesino de Robin Hobb, y debo decir que me gusta bastante. Es muy similar (pero anterior) a Canción de Hielo y Fuego de GRRM, aunque bastante más centrado en un solo personaje. También comencé la saga de la Torre Obscura, pero realmente debo admitir que me cuesta muchísimo seguir la escritura de Stephen King.

Una canción

Esta canción la escuche en el CD Incompleto de Callejeros y la verdad me encantó:

Un Minuto


Frase #3

Que es un buen hombre sino un maestro para el hombre malo?

Que es un hombre malo sino un trabajo para el hombre bueno?

Si no entiendes esto te perderás, sin importar lo inteligente que seas

–Tao Te Ching, ch. 27


Haciendo un juego de plataformas con Ruby y Gosu (Parte II )

En el post anterior comente como comenzar a hacer juegos con la librería Gosu y Ruby. El ejemplo mostraba como implementar una función muy primitiva para simular la gravedad, pero dejaba a resolver cuestiones como cálculo de colisiones lo cual es muy importante. Para simplificar el trabajo encontré la librería Chipmunk que básicamente se encarga de hacer el trabajo pesado de calcular la física de nuestro juego, y además nos permite detectar y responder a distintas colisiones de una manera muy sencilla.

En este tutorial voy a mostrar como añadir Chipmunk al ejemplo anterior ( y ya que estamos cambie el tileset del juego con la ayuda de opengameart )

Conceptos de Chipmunk

Chipmunk añade varias abstracciones a nuestro juego en este ejempo hacemos uso de:

  • Cuerpos
  • Figuras de colisión
  • Espacios
  • Vectores

Cuerpos

El cuerpo es una representación de la estructura física de una entidad o actor de nuestro juego, básicamente contiene información como la posicion, fuerza y velocidad de un objeto, así como otras propiedades como masa, momento, elasticidad, fricción y demás.

Figuras de colisión

Las figuras de colisión básicamente manejan la forma en la que chipmunk representará nuestra figura estas pueden ser

  • Circulos
  • Segmentos
  • Poligonos

Espacio

El espacio cumple una función muy similar a nuestra clase mundo, básicamente se encarga de hacer interactuar las distintas figuras y cuerpos entre si, también permite manipular las colisiones y activar callbacks o bloques en caso de producirse colisiones específicas.

Revisando lo que ya se hizo

Estos son los cambios a realizar:

La clase Actor pierde gran parte de sus propiedades que estarán ahora manejadas por CP::Body y CP::Shape, he escrito también algunos metodos de conveniencia como vec_from_size que permite establecer una forma (CP::Shape) CP::Vec2 a partir de un tamaño arbitrario o bien del tamaño del sprite . Le agregamos además la función draw, que dibuja el sprite a partir de un cuerpo, warp también ha cambiado, directamente alterando la posición del cuerpo.

require 'chipmunk'

class Actor
  attr_accessor :sprite, :angle, :mass, :falling, :mid_air, :height
  attr_reader :shape, :body

  def vec_from_size
    @width = @width ? width : @sprite.width
    @height = @height ? height : @sprite.height
    half_width = @width / 2
    half_height = @height / 2
    
    [CP::Vec2.new(-half_width,-half_height), CP::Vec2.new(-half_width, half_height), CP::Vec2.new(half_width, half_height), CP::Vec2.new(half_width,-half_height)]

  end

  def width
    @width ? @width : @sprite.width 
  end

  def height
    @height ? @height: @sprite.height
  end  
  

  def draw
    @sprite.draw_rot(@body.p.x , @body.p.y  , 1, @shape.body.a)
  end
  
  def mid_air
    @body.v.y.abs > 0
  end
  
  def warp(x,y)
    @body.p.x = x
    @body.p.y = y
  end
  
end

La clase player

La clase player cambia bastante, el constructor se encarga de establecer un cuerpo y una forma a partir de nuestro sprite ( que ha cambiado por este simpatico amigo por cierto!). El método accelerate ahora solo incrementa un poco la velocidad del cuerpo hacia la izquierda o derecha. Y saltar hace lo mismo detectando que el actor no este en el aire (para evitar el doble salto)

require_relative "./actor"
require 'chipmunk'
require 'pp'

class Player < Actor

  def initialize
    @sprite = Gosu::Image.new("assets/images/player.png")    

 # agregamos un cuerpo dandole masa y
 # momento le damos CP::INFINITY ya que no queremos que gire

    @body = CP::Body.new(10, CP::INFINITY)  

# Creamos la forma
    @shape = CP::Shape::Poly.new(@body,vec_from_size,CP::Vec2.new(0,0) )
    @shape.collision_type = :player #el tipo de colisión servirá para determinar que accion tomar ante distintas colisiones
    @shape.e = 0.0 # Le quitamos elasticidad así nuestro personaje no rebota por todos lados
    @shape.u = 1 # Le damos friccion
    @shape.surface_v  = CP::Vec2.new(1.0,1.0) #Velocidad de superficie

    @body.w_limit = 0.5

  end


  def accelerate(angle)
     case angle
     when :right
       @body.v.x = 3 * 0.85
     when :left
       @body.v.x = -3 * 0.85
     end
  end

  def jump
    if !mid_air
      @body.v.y = -20 * 0.95
    end
  end  
  

end


Mundo

El mundo ahora tiene menos atributos, conserva los actores, y añade uno nuevo, :space, lo inicializa determinando el damping ( una fuerza global de desaceleración, que evitara que nuestros objetos se aceleren indefinidamente ) y la gravedad

El método add actor ahora agrega la capacidad de añadir “rogue bodies”, básicamente cuerpos que no serán manipulados por el espacio, esto es util para hacer cosas como el suelo o plataformas fijas

require "chipmunk"
class World
  attr_reader :actors, :space

  def initialize
    @space = CP::Space.new()
    @actors = []

    @space.damping = 0.9
    @space.gravity.y = 0.5
  end


  
  def add_actor(actor, rogue = false)
    @actors << actor
    if rogue #adds static shape to have a rogue body
      @space.add_static_shape(actor.shape) 
    else
      @space.add_body(actor.body)      
      @space.add_shape(actor.shape)
    end
  end

  def show
    @actors.each { |actor|
      actor.draw
    }


  end

end

Clase Platform

Esta clase la cree para crear plataformas donde nuestro personaje se pueda subir, básicamente es igual a las demas solo que cuenta con 3 sprites para definir inicio, medio y final. También es una de las únicas done definimos arbitrariamente el tamaño en vez de tomarlo del tamaño del sprite, es por ello que sobrecargamos luego el metodo draw para poder dibujar correctamente la plataforma completa.

require_relative "./actor.rb"
require "chipmunk"

class Platform < Actor
  attr_accessor :height
  
  def initialize(width, height, angle = nil)
    @body = CP::Body.new_static()
    @width = width
    @height = height
    @sprite_start = Gosu::Image.new("assets/images/platform_start.png")
    @sprite = Gosu::Image.new("assets/images/platform_body.png")
    @sprite_end = Gosu::Image.new("assets/images/platform_end.png")

    @shape = CP::Shape::Poly.new(@body,vec_from_size,CP::Vec2.new(0,0) )

    if angle
      @body.a = angle
    end
    
    @shape.collision_type = :platform

  end

  def draw
     tiles = (@width / @sprite.width) / 2 
     (-tiles..tiles).each do |i|
       if i == -tiles
         @sprite_start.draw_rot(@body.p.x + (@sprite.width  * i  ) + 32 ,@body.p.y    , 1, @body.a)
       elsif i > -tiles && i < tiles -1
         @sprite.draw_rot(@body.p.x + (@sprite.width * i ) + 32  ,@body.p.y    , 1, @body.a)
       elsif i == tiles -1
         @sprite_end.draw_rot(@body.p.x + (@sprite.width * i ) + 32 ,@body.p.y    , 1, @body.a)
       end
     end
   end
  
end

Clase Ground

Ahora que tenemos física necesitamos un lugar a donde caer. La clase ground es muy similar a platform aunque un poco más simple. (quizas platform la hace obsoleta)

require_relative "./actor.rb"
require "chipmunk"

class Ground < Actor
  attr_accessor :height
  
  def initialize
    @body = CP::Body.new_static()
    @sprite = Gosu::Image.new("assets/images/ground.png")
    @width = 1200
    @height = 84
    @shape = CP::Shape::Poly.new(@body,vec_from_size,CP::Vec2.new(0,0) )
    
    @shape.collision_type = :ground

  end


  def draw
    tiles = (@width / @sprite.width) / 2
    (-tiles..tiles).each do |i|
      @sprite.draw_rot(@body.p.x + (@sprite.width * i ) ,@body.p.y    , 1, @body.a)
    end
  end

  
end

Actualizando nuestro juego

Ahora es momento de editar nuestro archivo principal game.rb y hacer que las cosas interactuen entre sí. Afortunadamente ahora esto es muy sencillo ya que la mayoría de nuestras clases manejan todo lo necesario, lo único que cambia es que ahora en vez de llamar a distintos metodos de World para la gravedad y demás, simplemente llamamos al método step de @world.space con parametro 1, lo cual avanzara la simulación una unidad de tiempo.

Ah dado que cambiamos el tileset, la funcion de dibujar el fondo también cambia un poquito.

require "gosu"
require_relative "./lib/player"
require_relative "./lib/crate"
require_relative "./lib/platform"
require_relative "./lib/world"
require_relative "./lib/ground"

class GameWindow < Gosu::Window
  
  def initialize
    super 1024, 768
    self.caption =  "Game test"

    @world = World.new()

    
    @player = Player.new
    @player.warp(200,128) #position the player
    @world.add_actor(@player)

    
    @ground = Ground.new
    @ground.warp(600,726) #position the ground
    @world.add_actor(@ground,true)    

    @platform = Platform.new(256,64)
    @platform.warp(256,128)
    @world.add_actor(@platform,true)

    @platform = Platform.new(256,64)
    @platform.warp(640,128)
    @world.add_actor(@platform,true)        

    @platform = Platform.new(256,64)
    @platform.warp(512,256)
    @world.add_actor(@platform,true)    

    @platform = Platform.new(256,64)
    @platform.warp(256,512)
    @world.add_actor(@platform,true)    

    @platform = Platform.new(256,64)
    @platform.warp(512,640)
    @world.add_actor(@platform,true)    

    
    @crate = Crate.new
    @crate.warp(640,128)
    @world.add_actor(@crate)


    @crate = Crate.new 3
    @crate.warp(256,128)
    @world.add_actor(@crate)

    

    @crate = Crate.new 2
    @crate.warp(600,350)
    @world.add_actor(@crate)        
    
    @background_image = Gosu::Image.new("assets/images/bg.png", :tileable => true)
  end

  def update
    if Gosu::button_down? Gosu::KbLeft #or Gosu::button_down? Gosu::GpLeft then
      @player.accelerate :left
    end
    
    if Gosu::button_down? Gosu::KbRight #or Gosu::button_down? Gosu::GpRight then
      @player.accelerate :right
    end

    if Gosu::button_down? Gosu::KbUp #or Gosu::button_down? Gosu::GpRight then

      @player.jump        
    

    end

    @world.space.step 1
  end

  def draw
    @world.show
    tiles_x = 1024 / @background_image.width
    tiles_y = 768 / @background_image.height
    tiles_x.times { |i|
      tiles_y.times {|j|
              @background_image.draw(i * @background_image.width, j * @background_image.height, 0)
      }

    }

  end
end


window = GameWindow.new

window.show

El resultado un simpático robot en una fábrica que puede empujar cajas y otros objetos. También subi un video de etapas más tempranas del desarrollo usando el antiguo tileset.

fit-width

Consideraciones

Hay ciertas cosas a recordar trabajando con chipmunk:

  • Chipmunk y gosu expresan los angulos y vectores de manera distinta, chipmunk simplemente indica puntos en un eje relativo al cuerpo, gosu lo expresa en función de un angulo y distancia.
  • CP::INFINITY es un valor que representa infinito, y es útil en algunos casos como por ejemplo cuando no queremos que un actor gire sobre si mismo.
  • Chipmunk no maneja fricción con objetos en rotación, eso hace que sea más importante CP::INFINITY
  • Si añadis un cuerpo al espacio simulado este va a ser afectado por la simulación, si solo añadís la forma, esta va a afectar a los demás pero el propio cuerpo no se vera afectado ( a menos que arbitrariamente se modifique como en caso de ascensores y demás) esto sería un “rogue body”

Recuerden que pueden descargar el juego aquí