use strict; use warnings; use Box2D; use SDL; use SDL::Video; use SDLx::App; use SDLx::Sprite; use SDL::Events ':all'; use Math::Trig 'rad2deg'; my $WIDTH = 300; my $HEIGHT = 300; # record the mouse info my %mouse = ( X => 0, Y =>0, left => 0); my $app = SDLx::App->new( dt => 1.0/60, min_t => 1.0/120, width => $WIDTH, height => $HEIGHT, flags => SDL_DOUBLEBUF | SDL_HWSURFACE, eoq => 1 ); # Box sprite my $bodySurface = SDLx::Surface->new( width => 20, height => 20 ); $bodySurface->draw_rect( [2,2,18,18], [255,255,0,255]); $bodySurface->update(); # default forces 0 x and -10 y my $vec = Box2D::b2Vec2->new(0,-10); # start the world my $world = Box2D::b2World->new($vec, 1); # update the app $app->update(); # this will store the falling bodies my %bodies = (); my $bodyCount = 0; my $bodySize = 10.0; sub makeBody { my ($x, $y) = @_; # make a new body definition # this is the structure that stores the physics info my $bodyDef = Box2D::b2BodyDef->new(); $bodyDef->type(Box2D::b2_dynamicBody); # set position $bodyDef->position->Set( $x, $y ); # create the body from that definition my $body = $world->CreateBody($bodyDef); # It's a polygonal shape 16x16 # this is the shape information that the fixture holds my $dynamicBox = Box2D::b2PolygonShape->new(); $dynamicBox->SetAsBox( $bodySize, $bodySize ); # make the fixture my $fixtureDef = Box2D::b2FixtureDef->new(); # the shape $fixtureDef->shape( $dynamicBox ); # the density $fixtureDef->density(0.1 + 2*rand()); # friction $fixtureDef->friction(0.1+0.9*rand()); # attach fixture to body to give it properties and shape $body->CreateFixtureDef($fixtureDef); # record the body $bodies{ $bodyCount++ } = $body; return $body; } # simulation timestep my $timeStep = 1.0/60.0; # iterate to solve velocity my $velocityIterations = 6; # position solver my $positionIterations = 3; # if a key is pressed down, make a body under the mouse! $app->add_event_handler( sub{ my ($event, $app) = @_; return 0 unless ($event->type == SDL::Events::SDL_KEYDOWN); # note Y is flipped makeBody( $mouse{X}, $HEIGHT - $mouse{Y} ); } ); # radius of ground boxes (walls) my $groundRad = 8.0; # store the walls my @walls = (); sub makeGround { my ($x,$y) = @_; my $body_def = Box2D::b2BodyDef->new(); my ($grx, $gry) = ($x, $y); my ($grw, $grh) = ( $groundRad, $groundRad); # position of ground $body_def->position->Set( $grx, $gry ); # create body from definition my $groundBody = $world->CreateBody($body_def); # set the shape as a box my $groundBox = Box2D::b2PolygonShape->new(); $groundBox->SetAsBox( $grw, $grh ); # attach the fixture $groundBody->CreateFixture( $groundBox, 0.0 ); # record the wall push @walls, $groundBody; return $groundBody; } # $app->add_event_handler( sub{ my ($event, $app) = @_; my $type = $event->type; return 0 unless ($type == SDL::Events::SDL_MOUSEMOTION || $type == SDL::Events::SDL_MOUSEBUTTONUP || $type == SDL::Events::SDL_MOUSEBUTTONDOWN); # update mouse state my ($mask,$x,$y) = @{ SDL::Events::get_mouse_state( ) }; $mouse{X} = $x; $mouse{Y} = $y; my $left = ($mask & SDL_BUTTON_LMASK); $mouse{left} = $left; # draw walls if dragging if ($left) { push @walls, makeGround( $x, $HEIGHT - $y); } } ); my $listener = Box2D::PerlContactListener->new(); my $beginContact = 0; my $endContact = 0; my $preSolve = 0; my $postSolve = 0; # These listeners fire when a contact occurs $listener->SetBeginContactSub(sub { warn "BeginContact!"; warn @_; $beginContact++; } ); $listener->SetEndContactSub(sub { warn "EndContact!"; warn @_; $endContact++; } ); #$listener->SetPreSolveSub(sub { warn "PreSolve!"; warn @_; $preSolve++; } ); #$listener->SetPostSolveSub(sub { warn "PostSolve!"; warn @_; $postSolve++; }); $world->SetContactListener( $listener ); $app->add_show_handler( sub { # simulate $world->Step( $timeStep, $velocityIterations, $positionIterations ); $world->ClearForces(); # draw walls $app->draw_rect([0,0,$WIDTH,$HEIGHT],[0,0,0,255]); foreach my $wall (@walls) { my $pos = $wall->GetPosition(); my ($x,$y) = ($pos->x(), $pos->y()); # draw around the middle of the object $app->draw_rect( [$x - $groundRad, $HEIGHT-$y-$groundRad, $groundRad*2, $groundRad*2], [0,255,0,255] ); } # draw bodies foreach (keys %bodies) { my $body_n = $_; my $body = $bodies{$body_n}; my $position = $body->GetPosition(); my $angle = rad2deg($body->GetAngle()); # Box sprite my $bodySprite = SDLx::Sprite->new( surface => $bodySurface ); if( $HEIGHT - $position->y() > 308 ) { $world->DestroyBody($body); delete $bodies{$body_n}; } $bodySprite->rotation($angle, 1); $bodySprite->draw_xy( $app, $position->x() - $bodySize, $HEIGHT-$position->y()-$bodySize ); } $app->update(); } ); # run the application $app->run();