The ruby-processing sketch
require 'pbox2d' require_relative 'lib/particle_system' attr_reader :box2d, :boundaries, :systems def setup size(400,300) @box2d = Box2D.new(self) box2d.create_world # We are setting a custom gravity box2d.gravity(0, -20) # Create Arrays @systems = [] @boundaries = [] # Add a bunch of fixed boundaries boundaries << Boundary.new(box2d, 50, 100, 300, 5, -0.3) boundaries << Boundary.new(box2d, 250, 175, 300, 5, 0.5) end def draw background(255) # Run all the particle systems if systems.size > 0 systems.each do |system| system.run system.add_particles(box2d, rand(0..2)) end end # Display all the boundaries boundaries.each(&:display) end def mouse_pressed # Add a new Particle System whenever the mouse is clicked systems << ParticleSystem.new(box2d, 0, mouse_x, mouse_y) end
The associated (local) library
require 'forwardable' module Runnable def run reject! { |item| item.done } each { |item| item.display } end end class ParticleSystem include Enumerable, Runnable extend Forwardable def_delegators(:@particles, :each, :reject!, :<<, :empty?) def_delegator(:@particles, :empty?, :dead?) attr_reader :x, :y def initialize(bd, num, x, y) @particles = [] # Initialize the Array @x, @y = x, y # Store the origin point num.times do self << Particle.new(bd, x, y) end end def add_particles(bd, n) n.times do self << Particle.new(bd, x, y) end end end # A Particle require 'pbox2d' class Particle include Processing::Proxy, PB TRAIL_SIZE = 6 # We need to keep track of a Body attr_reader :trail, :body, :box2d # Constructor def initialize(b2d, x, y) @box2d = b2d @trail = Array.new(TRAIL_SIZE, [x, y]) # Add the box to the box2d world # Here's a little trick, let's make a tiny tiny radius # This way we have collisions, but they don't overwhelm the system make_body(PB::Vec2.new(x, y), 0.2) end # This function removes the particle from the box2d world def kill_body box2d.destroy_body(body) end # Is the particle ready for deletion? def done # Let's find the screen position of the particle pos = box2d.body_coord(body) # Is it off the bottom of the screen? return false unless (pos.y > $app.height + 20) kill_body true end # Drawing the box def display # We look at each body and get its screen position pos = box2d.body_coord(body) # Keep track of a history of screen positions in an array (TRAIL_SIZE - 1).times do |i| trail[i] = trail[i + 1] end trail[TRAIL_SIZE - 1] = [pos.x, pos.y] # Draw particle as a trail begin_shape no_fill stroke_weight(2) stroke(0, 150) trail.each do |v| vertex(v[0], v[1]) end end_shape end # This function adds the rectangle to the box2d world def make_body(center, r) # Define and create the body bd = PB::BodyDef.new bd.type = PB::BodyType::DYNAMIC bd.position.set(box2d.processing_to_world(center)) @body = box2d.create_body(bd) # Give it some initial random velocity body.set_linear_velocity(PB::Vec2.new(rand(-1.0..1), rand(-1.0..1))) # Make the body's shape a circle cs = PB::CircleShape.new cs.m_radius = box2d.scale_to_world(r) fd = PB::FixtureDef.new fd.shape = cs fd.density = 1 fd.friction = 0 # Slippery when wet! fd.restitution = 0.5 # We could use this if we want to turn collisions off # cd.filter.groupIndex = -10 # Attach fixture to body body.create_fixture(fd) end end class Boundary include Processing::Proxy, PB attr_reader :box2d, :b, :x, :y, :w, :h def initialize(b2d, x, y, w, h, a) @box2d, @x, @y, @w, @h = b2d, x, y, w, h # Define the polygon sd = PB::PolygonShape.new # Figure out the box2d coordinates box2d_w = box2d.scale_to_world(w / 2) box2d_h = box2d.scale_to_world(h / 2) # We're just a box sd.set_as_box(box2d_w, box2d_h) # Create the body bd = PB::BodyDef.new bd.type = PB::BodyType::STATIC bd.angle = a bd.position.set(box2d.processing_to_world(x, y)) @b = box2d.create_body(bd) # Attached the shape to the body using a Fixture b.create_fixture(sd, 1) end # Draw the boundary, it doesn't move so we don't have to ask the Body for location def display fill(0) stroke(0) stroke_weight(1) rect_mode(CENTER) a = b.get_angle push_matrix translate(x, y) rotate(-a) rect(0, 0, w, h) pop_matrix end end
This comment has been removed by a blog administrator.
ReplyDelete