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();
}

Multi-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  => '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;
}