#!/usr/bin/perl use warnings; use strict; use SDL; use SDL::Events; use SDLx::App; my $WIDTH = 800; my $HEIGHT = 600; my $roadW = 2000; my $segL = 200; # segment length my $camD = 0.84; # camera depth sub getLines { # Preparing a list of 1600 anonymous hashes containing representations # of lines. This is done before the start of the main-loop. my @l = (); my $i; for $i (1 .. 1600) { my %line = (x => 0, y => 0, z => 0, X => 0, Y => 0, W => 0, curve => 0, spriteX => 0, clip => 0, s => undef, s_width => -1, s_height => -1); $line{z} = $i * $segL; if ($i > 300 && $i < 700) { $line{curve} = 0.5; } if ($i > 750) { $line{y} = sin($i / 30) * 1500; } if ($i % 20 == 0) { $line{spriteX} = -2.5; } push(@l, \%line); } return @l; } sub project { # Changes some values inside the anonymous hashes # from world- to screen coordinates: my ($lineref, $camX, $camY, $camZ) = @_; $lineref->{scale} = $camD / ($lineref->{z} - $camZ); $lineref->{X} = (1 + $lineref->{scale} * ($lineref->{x} - $camX)) * $WIDTH / 2; $lineref->{Y} = (1 - $lineref->{scale} * ($lineref->{y} - $camY)) * $HEIGHT / 2; $lineref->{W} = $lineref->{scale} * $roadW * $WIDTH / 2; } sub drawQuad { # Draws a polygone, filled with a color. # The polygone is defined by four points. # Landscape and road are drawn with these polygones. # So this is the central drawing routine. my ($surface, $color, $x1, $y1, $w1, $x2, $y2, $w2) = @_; my @points = ([$x1 - $w1, $y1], [$x2 - $w2, $y2], [$x2 + $w2, $y2], [$x1 + $w1, $y1]); my $pointnr = @points; my $i; my @xs = (); my @ys = (); foreach $i (@points) { my @arr = @{$i}; push(@xs, $arr[0]); push(@ys, $arr[1]); } my $xsref = \@xs; my $ysref = \@ys; SDL::GFX::Primitives::filled_polygon_color($surface, $xsref, $ysref, $pointnr, $color); } sub getColor { # Converts RGB-values to a hex-string: my ($r, $g, $b) = @_; # FF at the end for Alpha == 255: return sprintf("0x%02lX%02lX%02lXFF", $r, $g, $b); } # Initialisation: # Put the window somewhere in the middle: $ENV{SDL_VIDEO_WINDOW_POS} = "176,82"; SDL::init(SDL_INIT_VIDEO); my $app = SDLx::App->new( title => "Outrun Racing", width => $WIDTH, height => $HEIGHT, dt => 0.17, depth => 32, ); my $event = SDL::Event->new(); # Getting 1600 anonymous hashes for lines: my @lines = getLines(); # Main-Loop: my $N = @lines; my $n; my $pos = 0; my $playerX = 0; my $flying = 0; my $running = 1; while ($running) { my $x = 0; my $dx = 0; my $maxy = $HEIGHT; # A bit of delay, to not go too fast :) : SDL::delay(7); # The example in the documentation for using "SDL::Events" # is not good. Use this code instead. # It handles several keys at once, non-blocking: SDL::Events::pump_events(); my $keys_ref = SDL::Events::get_key_state(); my @keys = @{$keys_ref}; if ($keys[SDLK_LEFT] == 1) { $playerX -= 200; } if ($keys[SDLK_RIGHT] == 1) { $playerX += 200; } if ($keys[SDLK_UP] == 1) { $pos += 200; } if ($keys[SDLK_DOWN] == 1) { $pos -= 200; } if ($keys[SDLK_q] == 1) { $flying += 200; } if ($keys[SDLK_a] == 1) { $flying -= 200; } if ($keys[SDLK_ESCAPE] == 1) { $running = 0; } # Some calculations: while ($pos >= $N * $segL) { $pos -= $N * $segL; } while ($pos < 0) { $pos += $N * $segL; } my $startPos = $pos / $segL; if ($flying < 0) { $flying = 0; } my $camH = 1500 + $lines[$startPos]->{y} + $flying; # Drawing the road: foreach $n ($startPos .. $startPos + 300) { my $l = $lines[$n % $N]; if ($n >= $N) { my $n2 = $N * $segL; } else { my $n2 = 0; } project($l, $playerX - $x, $camH, $pos); $x += $dx; $dx += $l->{curve}; if ($l->{Y} >= $maxy) { next; } $maxy = $l->{Y}; my $grass = getColor(0, 154,0); my $rumble = getColor(0, 0, 0); my $road = getColor(105, 105, 105); if (($n / 3) % 2) { $grass = getColor(16, 200, 16); $rumble = getColor(255, 255, 255); $road = getColor(107, 107, 107); } my $p = $lines[($n - 1) % $N]; # previous line drawQuad($app, hex($grass), 0, $p->{Y}, $WIDTH, 0, $l->{Y}, $WIDTH); drawQuad($app, hex($rumble), $p->{X}, $p->{Y}, $p->{W} * 1.2, $l->{X}, $l->{Y}, $l->{W} * 1.2); drawQuad($app, hex($road), $p->{X}, $p->{Y}, $p->{W}, $l->{X}, $l->{Y}, $l->{W}); } # Update the screen: SDL::Video::flip($app); }