Spring 效果
#!/usr/bin/env perl
use strict;
use warnings;
use SDL;
use SDL::Event;
use SDLx::App;
my $app = SDLx::App->new(
width => 800,
height => 600,
title => 'Spring - Chained && Bounced!');
my $spring = 0.1;
my $friction = 0.4;
my $gravity = {
x => 0,
y => 8,
};
my $target = {
x => $app->w/2,
y => 0,
};
my @balls = ();
my $balls = 6;
for (1..$balls) {
push @balls, {
x => rand $app->w,
y => rand $app->h,
r => rand(10) + 10,
vx => 0,
vy => 0,
ax => 0,
ay => 0,
};
}
$app->add_event_handler(\&event);
$app->add_move_handler (\&calc);
$app->add_show_handler (\&render);
$app->run();
sub event {
my $event = shift;
my $controller = shift;
$controller->stop if $event->type == SDL_QUIT;
if ($event->type == SDL_MOUSEMOTION) {
$target->{x} = $event->button_x;
$target->{y} = $event->button_y;
}
}
sub collide {
my $ball1 = shift;
my $ball2 = shift;
# not collide...
if (($ball1->{x}-$ball2->{x})**2 + ($ball1->{y}-$ball2->{y})**2
>= ($ball1->{r}+$ball2->{r})**2) {
return;
}
# neg velocity
$ball1->{vx} = -$ball1->{vx};
$ball1->{vy} = -$ball1->{vy};
$ball2->{vx} = -$ball2->{vx};
$ball2->{vy} = -$ball2->{vy};
# place to correct place
my $cx = ($ball1->{x} + $ball2->{x}) / 2;
my $cy = ($ball1->{y} + $ball2->{y}) / 2;
my $dx = $ball1->{x} - $ball2->{x};
my $dy = $ball1->{y} - $ball2->{y};
my $dlen = ($dx**2 + $dy**2) ** 0.5;
$dx /= $dlen;
$dy /= $dlen;
$ball1->{x} = $cx + $dx*$ball1->{r};
$ball1->{y} = $cy + $dy*$ball1->{r};
$ball2->{x} = $cx - $dx*$ball2->{r};
$ball2->{y} = $cy - $dy*$ball2->{r};
}
sub move_springly {
my $ball = shift;
my $target = shift;
my $step = shift;
# apply physics
$ball->{ax} = ($target->{x} - $ball->{x}) * $spring + $gravity->{x};
$ball->{ay} = ($target->{y} - $ball->{y}) * $spring + $gravity->{y};
$ball->{vx} += $ball->{ax} * $step;
$ball->{vy} += $ball->{ay} * $step;
$ball->{vx} *= 1 - $friction * $step;
$ball->{vy} *= 1 - $friction * $step;
$ball->{x} += $ball->{vx} * $step;
$ball->{y} += $ball->{vy} * $step;
# bouncing
if ($ball->{x}-$ball->{r} < 0) {
$ball->{x} = $ball->{r};
if ($ball->{vx} < 0) {
$ball->{vx} = -$ball->{vx};
}
} elsif ($ball->{x}+$ball->{r} > $app->w) {
$ball->{x} = $app->w - $ball->{r};
if ($ball->{vx} > 0) {
$ball->{vx} = -$ball->{vx};
}
}
if ($ball->{y}-$ball->{r} < 0) {
$ball->{y} = $ball->{r};
if ($ball->{vy} < 0) {
$ball->{vy} = -$ball->{vy};
}
} elsif ($ball->{y}+$ball->{r} > $app->h) {
$ball->{y} = $app->h - $ball->{r};
if ($ball->{vy} > 0) {
$ball->{vy} = -$ball->{vy};
}
}
}
sub calc {
my ($step, $app) = @_;
move_springly $balls[0], $target, $step;
foreach my $i (1..$#balls) {
move_springly $balls[$i],
{x=>$balls[$i-1]->{x}, y=>$balls[$i-1]->{y}}, $step;
}
foreach my $i (0..$#balls) {
foreach my $j (($i+1)..$#balls) {
collide $balls[$i], $balls[$j];
}
}
}
sub draw_ball {
my $ball = shift;
my $target = shift;
$app->draw_line([$target->{x}, $target->{y}], [$ball->{x}, $ball->{y}],
[0, 0, 255, 255]);
$app->draw_circle_filled([$ball->{x}, $ball->{y}], $ball->{r},
[255, 0, 0, 255]);
}
sub render {
my ($delta, $app) = @_;
$app->draw_rect([0, 0, $app->w, $app->h], 0); # clear screen
draw_ball $balls[0], $target;
foreach my $i (1..$#balls) {
draw_ball $balls[$i], $balls[$i-1];
}
$app->update();
}
#!/usr/bin/env perl
use strict;
use warnings;
use SDL;
use SDL::Event;
use SDLx::App;
my $app = SDLx::App->new(
width => 800,
height => 600,
title => 'Multi-Spring - Handles!');
my $spring = 0.02;
my $friction = 0.04;
my $gravity = {
x => 0,
y => 0,
};
my $ball = {
x => $app->w/2,
y => $app->h/2,
r => 10,
vx => 0,
vy => 0,
ax => 0,
ay => 0,
};
my @hands = ();
my $hands = 4;
my $hand_current = undef;
for (1..$hands) {
push @hands, {
x => rand $app->w,
y => rand $app->h,
};
}
$app->add_event_handler(\&event);
$app->add_move_handler (\&calc);
$app->add_show_handler (\&render);
$app->run();
sub event {
my $event = shift;
my $controller = shift;
$controller->stop if $event->type == SDL_QUIT;
if ($event->type == SDL_MOUSEBUTTONDOWN) {
foreach my $i (0..$#hands) {
if (($event->button_x-$hands[$i]->{x})**2
+ ($event->button_y-$hands[$i]->{y})**2 < 100) {
$hand_current = $hands[$i];
return;
}
}
}
if ($event->type == SDL_MOUSEBUTTONUP) {
$hand_current = undef;
}
if ($event->type == SDL_MOUSEMOTION && $hand_current) {
$hand_current->{x} = $event->button_x;
$hand_current->{y} = $event->button_y;
}
}
sub move_springly {
my $ball = shift;
my $target = shift;
my $step = shift;
# apply physics
$ball->{ax} = ($target->{x} - $ball->{x}) * $spring + $gravity->{x};
$ball->{ay} = ($target->{y} - $ball->{y}) * $spring + $gravity->{y};
$ball->{vx} += $ball->{ax} * $step;
$ball->{vy} += $ball->{ay} * $step;
$ball->{vx} *= 1 - $friction * $step;
$ball->{vy} *= 1 - $friction * $step;
$ball->{x} += $ball->{vx} * $step;
$ball->{y} += $ball->{vy} * $step;
# bouncing
if ($ball->{x}-$ball->{r} < 0) {
$ball->{x} = $ball->{r};
if ($ball->{vx} < 0) {
$ball->{vx} = -$ball->{vx};
}
} elsif ($ball->{x}+$ball->{r} > $app->w) {
$ball->{x} = $app->w - $ball->{r};
if ($ball->{vx} > 0) {
$ball->{vx} = -$ball->{vx};
}
}
if ($ball->{y}-$ball->{r} < 0) {
$ball->{y} = $ball->{r};
if ($ball->{vy} < 0) {
$ball->{vy} = -$ball->{vy};
}
} elsif ($ball->{y}+$ball->{r} > $app->h) {
$ball->{y} = $app->h - $ball->{r};
if ($ball->{vy} > 0) {
$ball->{vy} = -$ball->{vy};
}
}
}
sub calc {
my ($step, $app) = @_;
foreach my $i (0..$#hands) {
move_springly $ball, $hands[$i], $step;
}
}
sub draw_ball {
my $ball = shift;
my $target = shift;
$app->draw_line([$target->{x}, $target->{y}], [$ball->{x}, $ball->{y}],
[0, 0, 255, 255]);
$app->draw_circle_filled([$ball->{x}, $ball->{y}], $ball->{r},
[255, 0, 0, 255]);
$app->draw_circle_filled([$target->{x}, $target->{y}], 10,
[0, 128, 255, 255]);
}
sub render {
my ($delta, $app) = @_;
$app->draw_rect([0, 0, $app->w, $app->h], 0); # clear screen
foreach my $i (0..$#hands) {
draw_ball $ball, $hands[$i];
}
$app->update;
}