GLBasic forum

Main forum => Beta Tests => Topic started by: MrTAToad on 2014-Jun-08

Title: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: MrTAToad on 2014-Jun-08
I'm converting Jakes Gorden Pseudo 3D track example programs to GLBasic.  This is the first part (just straight roads).

Having to convert from Javascript to a more readable text can be a bit of problem!

Here's the current code :

Code (glbasic) Select
// --------------------------------- //
// Project: Test2
// Start: Tuesday, April 01, 2014
// IDE Version: 12.001
CONSTANT PLAYER_STRAIGHT% = 0
CONSTANT PLAYER_LEFT% = 1
CONSTANT PLAYER_RIGHT% = 2
CONSTANT PLAYER_UPHILL_STRAIGHT% = 3
CONSTANT PLAYER_UPHILL_LEFT% = 4
CONSTANT PLAYER_UPHILL_RIGHT% = 5


TYPE tScreen
scale
x
y
w
ENDTYPE

TYPE tCamera
x
y
z
ENDTYPE

TYPE tWorld
x
y
z
ENDTYPE

TYPE tColour
rumble%
road%
grass%
lane%
ENDTYPE

TYPE tPart
world AS tWorld
camera AS tCamera
screen AS tScreen
ENDTYPE

TYPE tSegment
index%
colour AS tColour
p1 AS tPart
p2 AS tPart
ENDTYPE

TYPE TRoad
segments[] AS tSegment

LIGHT AS tColour
DARK AS tColour

SPRITES_SCALE

fps = 75.0
fpsStep = 0.0
segmentLength% = 200    // Length of a single segment
rumbleLength% = 3 // Number of segments per red/white rumble strip
roadWidth%      = 2000 // Actually half the roads width, easier math if the road spans from -roadWidth to +roadWidth
cameraHeight%  = 1000  // Z height of camera
cameraDepth    = 0
position      = 0.0     // Current camera Z position
playerX        = 0 // Player x offset from center of road (-1 to 1 to stay independent of roadWidth)
playerZ        = 0 // Player relative z distance from camera (computed)
lanes%          = 3       // Number of lanes
fieldOfView%    = 100     // angle (degrees) for field of view
speed          = 0.0     // current speed
maxSpeed      = 0       // top speed (ensure we can't move more than 1 segment in a single frame to make collision detection easier)
accel = 0 // acceleration rate - tuned until it 'felt' right
    breaking = 0 // deceleration rate when braking
    decel = 0 // 'natural' deceleration rate when neither accelerating, nor braking
    offRoadDecel = 0 // off road deceleration is somewhere in between
    offRoadLimit = 0 // limit when off road deceleration no longer applies (e.g. you can always go at least t
    resolution = 0.0

width%;height%
keyLeftRight%

trackLength%

drawDistance% = 100

FUNCTION Initialise%:
LOCAL tW%,tH%

GETSCREENSIZE self.width%,self.height%

DEBUG "H:"+self.height%+"\n"
self.resetRoad()

self.position=0.0

self.cameraDepth=1.0 / TAN((self.fieldOfView%/2)) // * 3.141592653/180.0)
DEBUG "Camera depth : "+self.cameraDepth+" "+self.fieldOfView%+"\n"

self.LIGHT.road%=7039851
self.LIGHT.grass%=1092112
self.LIGHT.rumble%=5592405
self.LIGHT.lane%=13421772

self.DARK.road%=6908265
self.DARK.grass%=39424
self.DARK.rumble%=12303291
self.DARK.lane%=0

self.fpsStep=1.0/self.fps
self.maxSpeed=self.segmentLength/self.fpsStep

self.accel = self.maxSpeed/5.0
    self.breaking = -self.maxSpeed
    self.decel = -self.maxSpeed/5.0
    self.offRoadDecel = -self.maxSpeed/2.0
    self.offRoadLimit = self.maxSpeed/4.0
    self.speed=0.0
    self.playerZ=self.cameraHeight * self.cameraDepth
    self.resolution=self.height%/640.0

self.keyLeftRight%=0
   
    GETSPRITESIZE PLAYER_STRAIGHT%,tW%,tH%
    self.SPRITES_SCALE= 0.3 * (1.0/tW%)
ENDFUNCTION

FUNCTION resetRoad%:
LOCAL segment AS tSegment

DIM self.segments[0]

FOR n%=0 TO 499
segment.index%=n
segment.p1.world.x=0.0
segment.p1.world.y=0.0
segment.p1.world.z=n*self.segmentLength%
//segment.p1.camera

segment.p2.world.x=0.0
segment.p2.world.y=0.0
segment.p2.world.z=(n+1)*self.segmentLength%
IF MOD(FLOOR(n%/self.rumbleLength%),2)=0
segment.colour=self.LIGHT
ELSE
segment.colour=self.DARK
ENDIF
//segment.colour=IIF(MOD(FLOOR(n%/self.rumbleLength%),2),self.LIGHT,self.DARK)
DIMPUSH self.segments[],segment
NEXT

self.trackLength% = BOUNDS(self.segments[],0)*self.segmentLength%
ENDFUNCTION

FUNCTION findSegment AS tSegment:z
RETURN self.segments[MOD(FLOOR(z/self.segmentLength%),BOUNDS(self.segments[],0))]
ENDFUNCTION

FUNCTION increase: start,increment,maxV
LOCAL result

result=start+increment
WHILE result>=maxV
DEC result,maxV
WEND

WHILE result<0
INC result,maxV
WEND

RETURN result
ENDFUNCTION

FUNCTION accelerate:v,accel,dt
RETURN v + (accel * dt)
ENDFUNCTION

FUNCTION limit:value, minV,maxV
RETURN MAX(minV,MIN(value,maxV))
ENDFUNCTION

FUNCTION fog%:x%,y%,width%,height%
ALPHAMODE -0.9
DRAWRECT x%,y%,width%,height%,RGB(64,64,64)
ALPHAMODE -1.0
ENDFUNCTION

FUNCTION update%:dt
LOCAL dx

DEBUG dt+"\n"
DEBUG "Speed:"+self.speed+"\n"
DEBUG (dt*self.speed)+"\n"

      self.position = self.increase(self.position, dt * self.speed, self.trackLength)

      dx = dt * 2 * (self.speed/self.maxSpeed); // at top speed, should be able to cross from left to right (-1 to 1) in 1 second

IF KEY(203)
DEC self.playerX,dx
self.keyLeftRight%=-1
ELSEIF KEY(205)
INC self.playerX,dx
self.keyLeftRight%=1
ELSE
self.keyLeftRight%=0
ENDIF

IF KEY(200)
self.speed=self.accelerate(self.speed,self.accel,dt)
ELSEIF KEY(208)
self.speed=self.accelerate(self.speed,self.breaking,dt)
ELSE
self.speed=self.accelerate(self.speed,self.decel,dt)
ENDIF

IF (self.playerX<-1.0 OR self.playerX>1.0) AND (self.speed>self.offRoadLimit)
self.speed=self.accelerate(self.speed,self.offRoadDecel,dt)
ENDIF

self.playerX=self.limit(self.playerX,-2,2)
self.speed=self.limit(self.speed,0.0,self.maxSpeed)

ENDFUNCTION

FUNCTION project%:p AS tPart, cameraX, cameraY, cameraZ, cameraDepth, width%, height%, roadWidth%
p.camera.x=p.world.x - cameraX
p.camera.y=p.world.y - cameraY
p.camera.z=p.world.z - cameraZ

p.screen.scale=self.cameraDepth/p.camera.z
// DEBUG "Z : "+p.camera.z+" "+cameraZ+"\n"
// DEBUG "Y : "+p.camera.y+" "+cameraY+"\n"
// DEBUG "X : "+p.camera.x+" "+cameraX+"\n"
// DEBUG "Camera depth : "+cameraDepth+" p.camera.z : "+p.camera.z+"\n"
// DEBUG "Scale : "+p.screen.scale+"\n"
// DEBUG "Width : "+width%+" Height : "+height%+"\n"
// DEBUG "Road width : "+roadWidth%+"\n"

p.screen.x=INTEGER((width%/2)+(p.screen.scale*p.camera.x*width%/2))
p.screen.y=INTEGER((height%/2)-(p.screen.scale*p.camera.y*height%/2))
p.screen.w=INTEGER(p.screen.scale*roadWidth%*width%/2)

// DEBUG p.screen.x+" "+p.screen.y+" "+p.screen.w+"\n"
//KEYWAIT
ENDFUNCTION

FUNCTION player%:width%, height%, resolution, roadWidth, sprites, speedPercent, scale, destX, destY, steer, updown
LOCAL bounce
LOCAL sprite%

bounce=1.5*(RND(2)-1)*speedPercent*resolution
IF steer<0
sprite%=IIF(updown>0,PLAYER_UPHILL_LEFT%,PLAYER_LEFT%)
ELSEIF steer>0
sprite%=IIF(updown>0,PLAYER_UPHILL_RIGHT%,PLAYER_RIGHT%)
ELSE
sprite%=IIF(updown>0,PLAYER_UPHILL_STRAIGHT%,PLAYER_STRAIGHT%)
ENDIF

DEBUG "SP:"+sprite%+"\n"

self.sprite(width%,height%,resolution,roadWidth,sprite%,scale,destX,destY+bounce,-0.5,-1)
ENDFUNCTION

FUNCTION sprite%:width%, height%, resolution, roadWidth, sprite%, scale, destX, destY, offsetX, offsetY, clipY=0.0
LOCAL destW,destH,clipH

GETSPRITESIZE sprite%,destW,destH
destW=destW*scale*(width%/2.0)*self.SPRITES_SCALE*roadWidth
destH=destH*scale*(width%/2.0)*self.SPRITES_SCALE*roadWidth

DEBUG "Scale : "+self.SPRITES_SCALE+" Width : "+roadWidth+"\n"
DEBUG "W:"+destW+" "+destH+"\n"

INC destX,destW*offsetX
INC destY,destH*offsetY

clipH=IIF(clipY,MAX(0,destY+destH-clipY),0)

IF clipH<destH
STRETCHSPRITE sprite%,destX,destY,destW,destH-clipH
ENDIF
ENDFUNCTION


FUNCTION render%:
LOCAL baseSegment AS tSegment
LOCAL maxy%,n%
LOCAL segment AS tSegment

// DEBUG "H2:"+self.height%+"\n"

maxy%=self.height%
// DEBUG "maxy : "+maxy%+"\n"
DEBUG "Position : "+self.position+"\n"

baseSegment = self.findSegment(self.position)

FOR n%=0 TO self.drawDistance%-1
segment=self.segments[MOD(baseSegment.index%+n%,BOUNDS(self.segments[],0))]

self.project(segment.p1,self.playerX*self.roadWidth%,self.cameraHeight%,self.position%,self.cameraDepth,self.width%,self.height%,self.roadWidth%)
self.project(segment.p2,self.playerX*self.roadWidth%,self.cameraHeight%,self.position%,self.cameraDepth,self.width%,self.height%,self.roadWidth%)

// DEBUG "Segment 1 camera Z : "+segment.p1.camera.z+"\n"
// DEBUG "Segment 2 screen Y : "+segment.p2.screen.y+"\n"
// DEBUG "maxy : "+maxy%+"\n"
IF segment.p1.camera.z<=self.cameraDepth OR segment.p2.screen.y>=maxy%
CONTINUE
ELSE
self.segment(self.width%,self.lanes%, _
segment.p1.screen.x, _
                  segment.p1.screen.y, _
                    segment.p1.screen.w, _
                    segment.p2.screen.x, _
                    segment.p2.screen.y, _
                    segment.p2.screen.w, _
                    1.0, _
                    segment.colour)
ENDIF

maxy%=segment.p2.screen.y%
NEXT

self.player(self.width%, self.height%, self.resolution, self.roadWidth% ,PLAYER_STRAIGHT%,self.speed/self.maxSpeed, _
self.cameraDepth/self.playerZ, _
self.width%/2, _
self.height, _
self.speed * SGN(self.keyLeftRight%), _
0)
//                    self.cameraDepth/self.playerZ, _
  //                  self.width%/2, _
    //                self.height, _
      //              self.speed * 1, _ // (keyLeft ? -1 : keyRight ? 1 : 0),
         //           0)
ENDFUNCTION

FUNCTION rumbleWidth:projectedRoadWidth%,lanes%
RETURN projectedRoadWidth%/MAX(6,2*lanes%)
ENDFUNCTION

FUNCTION laneMarkerWidth:projectedRoadWidth%,lanes%
RETURN projectedRoadWidth/MAX(32,8*lanes%)
ENDFUNCTION

FUNCTION polygon%:x1,y1,x2,y2,x3,y3,x4,y4,colour%
STARTPOLY 99,0
POLYVECTOR x1,y1,x2,y2,colour%
POLYVECTOR x2,y2,x3,y3,colour%
POLYVECTOR x3,y3,x4,y4,colour%
POLYVECTOR x4,y4,x1,y2,colour%
ENDPOLY

// DRAWRECT x1,y1,x2,y2,colour%
// DRAWRECT x3,y3,x4,y4,colour%

//DRAWLINE x1,y1,x2,y2,colour%
//DRAWLINE x3,y3,x4,y4,colour%
ENDFUNCTION

FUNCTION segment%:width%,lanes%,x1,y1,w1,x2,y2,w2,fog,colour AS tColour
LOCAL r1,r2,l1,l2,lanew1,lanew2,lanex1,lanex2

r1=self.rumbleWidth(w1,lanes%)
r2=self.rumbleWidth(w2,lanes%)
l1=self.laneMarkerWidth(w1,lanes%)
l2=self.laneMarkerWidth(w2,lanes%)

//DEBUG "R1 : "+r1%+" R2 : "+r2%+" l1 : "+l1%+" l2 : "+l2%+"\n"
ALPHAMODE -1.0
DRAWRECT 0,y2,width%,y1-y2,colour.grass%

    self.polygon(x1-w1-r1, y1, x1-w1, y1, x2-w2, y2, x2-w2-r2, y2, colour.rumble)
    self.polygon(x1+w1+r1, y1, x1+w1, y1, x2+w2, y2, x2+w2+r2, y2, colour.rumble)
    self.polygon(x1-w1, y1, x1+w1, y1, x2+w2, y2, x2-w2, y2, colour.road)

    IF colour.lane%
    lanew1=w1*2.0/lanes%
    lanew2=w2*2.0/lanes%

    lanex1=x1-w1+lanew1
    lanex2=x2-w2+lanew2

    FOR lane%=1 TO lanes%-1
    self.polygon(lanex1-l1/2.0,y1,lanex1+l1/2.0,y1,lanex2+l2/2.0,y2,lanex2-l2/2.0,y2,colour.lane%)
    INC lanex1,lanew1
    INC lanex2,lanew2
   
    NEXT
    ENDIF
   
    //self.fog(0,y1,width%,y2-y1)

    ENDFUNCTION
ENDTYPE

LOCAL road AS TRoad
LOCAL now,last,dt,gdt,stp=0.025

LIMITFPS -1

LOADSPRITE "Media/player_straight.png",PLAYER_STRAIGHT%
LOADSPRITE "Media/player_left.png",PLAYER_LEFT%
LOADSPRITE "Media/player_right.png",PLAYER_RIGHT%
LOADSPRITE "Media/player_uphill_straight.png",PLAYER_UPHILL_STRAIGHT%
LOADSPRITE "Media/player_uphill_left.png",PLAYER_UPHILL_LEFT%
LOADSPRITE "Media/player_uphill_right.png",PLAYER_UPHILL_RIGHT%

road.Initialise()
road.resetRoad()

now=GETTIMERALL()
last=GETTIMERALL()

WHILE TRUE
now=GETTIMERALL()
DEBUG last+" "+now+"\n"
dt=MIN(1.0,(now-last)/1000.0)
INC gdt,dt

WHILE gdt>stp
DEC gdt,stp
road.update(stp)
WEND

road.update(stp)

SMOOTHSHADING FALSE

road.render()
SHOWSCREEN

last = now
WEND


Title: Re: Something of interest : Pseudo 3D Tracks V2 - Part 1
Post by: Ian Price on 2014-Jun-08
Nice and fast - perhaps too fast! :)

I created a road system in Div many years ago (in fact it was going to be a remake of OutRun). It was my last ever Div program. Unfortunately it's all gone now due to numerous computer deaths and updates. :( I've probably got the source on a CD somewhere, but I just haven't got the time or the will to find it.

I do often think about creating a 2D racer, to harken back to the old days (especially since Foppy released a motorbike racer on OUYA that got rave reviews, despite it's limited 1 track game mode). But then I remember I might still have the code somewhere and think sod it!

Looking forward to seeing the next part(s). :)
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Part 1
Post by: MrTAToad on 2014-Jun-08
And if by magic, the next part is here.

One problem I did have was working out an acceptable easing value as Javascript uses radians.

But after a bit of experimenting, I found an acceptable value, although it's not quite the same as the original.

It is probably way too fast still - will have to look at that later.

Code (glbasic) Select
// --------------------------------- //
// Project: Test2
// Start: Tuesday, April 01, 2014
// IDE Version: 12.001
CONSTANT PLAYER_STRAIGHT% = 0
CONSTANT PLAYER_LEFT% = 1
CONSTANT PLAYER_RIGHT% = 2
CONSTANT PLAYER_UPHILL_STRAIGHT% = 3
CONSTANT PLAYER_UPHILL_LEFT% = 4
CONSTANT PLAYER_UPHILL_RIGHT% = 5

CONSTANT ROAD_LENGTH_NONE% = 0
CONSTANT ROAD_LENGTH_SHORT% = 25
CONSTANT ROAD_LENGTH_MEDIUM% = 50
CONSTANT ROAD_LENGTH_LONG% = 100

CONSTANT ROAD_CURVE_NONE% = 0
CONSTANT ROAD_CURVE_EASY% = 2
CONSTANT ROAD_CURVE_MEDIUM% = 4
CONSTANT ROAD_CURVE_HARD% = 6
   
CONSTANT PI = 3.14159

TYPE tScreen
scale
x
y
w
ENDTYPE

TYPE tCamera
x
y
z
ENDTYPE

TYPE tWorld
x
y
z
ENDTYPE

TYPE tColour
rumble%
road%
grass%
lane%
ENDTYPE

TYPE tPart
world AS tWorld
camera AS tCamera
screen AS tScreen
ENDTYPE

TYPE tSegment
index%
colour AS tColour
p1 AS tPart
p2 AS tPart
curve
ENDTYPE

TYPE TRoad
segments[] AS tSegment

LIGHT AS tColour
DARK AS tColour

SPRITES_SCALE

fps = 75.0
fpsStep = 0.0
segmentLength% = 200    // Length of a single segment
rumbleLength% = 3 // Number of segments per red/white rumble strip
roadWidth%      = 2000 // Actually half the roads width, easier math if the road spans from -roadWidth to +roadWidth
cameraHeight%  = 1000  // Z height of camera
cameraDepth    = 0
position      = 0.0     // Current camera Z position
playerX        = 0 // Player x offset from center of road (-1 to 1 to stay independent of roadWidth)
playerZ        = 0 // Player relative z distance from camera (computed)
lanes%          = 3       // Number of lanes
fieldOfView%    = 100     // angle (degrees) for field of view
speed          = 0.0     // current speed
maxSpeed      = 0       // top speed (ensure we can't move more than 1 segment in a single frame to make collision detection easier)
accel = 0 // acceleration rate - tuned until it 'felt' right
    breaking = 0 // deceleration rate when braking
    decel = 0 // 'natural' deceleration rate when neither accelerating, nor braking
    offRoadDecel = 0 // off road deceleration is somewhere in between
    offRoadLimit = 0 // limit when off road deceleration no longer applies (e.g. you can always go at least t
    resolution = 0.0

width%;height%
keyLeftRight%

trackLength%

drawDistance% = 100

FUNCTION Initialise%:
LOCAL tW%,tH%

GETSCREENSIZE self.width%,self.height%

DEBUG "H:"+self.height%+"\n"
self.resetRoad()

self.position=0.0

self.cameraDepth=1.0 / TAN((self.fieldOfView%/2)) // * 3.141592653/180.0)
DEBUG "Camera depth : "+self.cameraDepth+" "+self.fieldOfView%+"\n"

self.LIGHT.road%=7039851
self.LIGHT.grass%=1092112
self.LIGHT.rumble%=5592405
self.LIGHT.lane%=13421772

self.DARK.road%=6908265
self.DARK.grass%=39424
self.DARK.rumble%=12303291
self.DARK.lane%=0

self.fpsStep=1.0/self.fps
self.maxSpeed=self.segmentLength/self.fpsStep

self.accel = self.maxSpeed/5.0
    self.breaking = -self.maxSpeed
    self.decel = -self.maxSpeed/5.0
    self.offRoadDecel = -self.maxSpeed/2.0
    self.offRoadLimit = self.maxSpeed/4.0
    self.speed=0.0
    self.playerZ=self.cameraHeight * self.cameraDepth
    self.resolution=self.height%/640.0

self.keyLeftRight%=0

    GETSPRITESIZE PLAYER_STRAIGHT%,tW%,tH%
    self.SPRITES_SCALE= 0.3 * (1.0/tW%)
ENDFUNCTION

FUNCTION addSegment%:curve
LOCAL size%
LOCAL segment AS tSegment

size%=BOUNDS(self.segments[],0)

// DEBUG "Curve : "+curve+"\n"

segment.index%=size%
segment.curve=curve
segment.p1.world.x=0.0
segment.p1.world.y=0.0
segment.p1.world.z=size%*self.segmentLength%
segment.p1.camera.x=0.0
segment.p1.camera.y=0.0
segment.p1.camera.z=0.0
segment.p1.screen.x=0.0
segment.p1.screen.y=0.0
segment.p1.screen.w=0.0
segment.p1.screen.scale=0.0

segment.p2.world.x=0.0
segment.p2.world.y=0.0
segment.p2.world.z=(size%+1)*self.segmentLength%
segment.p2.camera.x=0.0
segment.p2.camera.y=0.0
segment.p2.camera.z=0.0
segment.p2.screen.x=0.0
segment.p2.screen.y=0.0
segment.p2.screen.w=0.0
segment.p2.screen.scale=0.0

IF FLOOR(MOD(size%/self.rumbleLength%,2))
segment.colour.road%=RGB(64,64,64)
segment.colour.grass%=RGB(0,64,0)
segment.colour.rumble%=RGB(128,128,128)
ELSE
segment.colour.road%=RGB(200,200,200)
segment.colour.grass%=RGB(0,200,0)
segment.colour.rumble%=RGB(220,220,220)
ENDIF

segment.colour.lane%=RGB(255,255,0)

DIMPUSH self.segments[],segment
ENDFUNCTION

FUNCTION addRoad%:enter, hold, leave, curve
LOCAL n%

FOR n%=0 TO enter-1;
self.addSegment(self.easeIn(0,curve,n%/enter));
NEXT

FOR n%=0 TO hold-1; self.addSegment(curve); NEXT

FOR n%=0 TO leave-1; self.addSegment(self.easeInOut(curve,0,n%/leave)); NEXT
ENDFUNCTION
   
FUNCTION addStraight%:num=25
self.addRoad(num,num,num,0)
ENDFUNCTION

    FUNCTION addCurve%:num, curve
    self.addRoad(num,num,num,curve)
    ENDFUNCTION
               
    FUNCTION addSCurves%:
      self.addRoad(ROAD_LENGTH_MEDIUM%, ROAD_LENGTH_MEDIUM%, ROAD_LENGTH_MEDIUM%, -ROAD_CURVE_EASY%)
      self.addRoad(ROAD_LENGTH_MEDIUM%, ROAD_LENGTH_MEDIUM%, ROAD_LENGTH_MEDIUM%, ROAD_CURVE_MEDIUM%)
//      self.addRoad(ROAD_LENGTH_MEDIUM%, ROAD_LENGTH_MEDIUM%, ROAD_LENGTH_MEDIUM%, ROAD_CURVE_EASY%)
  //    self.addRoad(ROAD_LENGTH_MEDIUM%, ROAD_LENGTH_MEDIUM%, ROAD_LENGTH_MEDIUM%, -ROAD_CURVE_EASY%)
    //  self.addRoad(ROAD_LENGTH_MEDIUM%, ROAD_LENGTH_MEDIUM%, ROAD_LENGTH_MEDIUM%, -ROAD_CURVE_MEDIUM%)
    ENDFUNCTION
   
FUNCTION resetRoad%:
LOCAL segment AS tSegment

DIM self.segments[0]

self.addStraight(ROAD_LENGTH_SHORT%/4.0)
      self.addSCurves()
      self.addStraight(ROAD_LENGTH_LONG%)
      self.addCurve(ROAD_LENGTH_MEDIUM%, ROAD_CURVE_MEDIUM%)
      self.addCurve(ROAD_LENGTH_LONG%, ROAD_CURVE_MEDIUM%)
      self.addStraight()
      self.addSCurves()
      self.addCurve(ROAD_LENGTH_LONG%, -ROAD_CURVE_MEDIUM%)
      self.addCurve(ROAD_LENGTH_LONG%, ROAD_CURVE_MEDIUM%)
      self.addStraight()
      self.addSCurves()
      self.addCurve(ROAD_LENGTH_LONG%, -ROAD_CURVE_EASY%)

segment=self.findSegment(self.playerZ)
      self.segments[segment.index% + 2].colour.road% = RGB(255,255,255)
      self.segments[segment.index% + 2].colour.lane% = RGB(255,255,255)
      self.segments[segment.index% + 3].colour.road% = RGB(255,255,255)
      self.segments[segment.index% + 3].colour.lane% = RGB(255,255,255)

     
      FOR n%=0 TO self.rumbleLength%-1
      self.segments[BOUNDS(self.segments[],0)-1-n%].colour.road% = RGB(255,255,255)
      self.segments[BOUNDS(self.segments[],0)-1-n%].colour.lane% = RGB(255,255,255)
      NEXT

self.trackLength% = BOUNDS(self.segments[],0)*self.segmentLength%
ENDFUNCTION

FUNCTION findSegment AS tSegment:z
RETURN self.segments[MOD(FLOOR(z/self.segmentLength%),BOUNDS(self.segments[],0))]
ENDFUNCTION

FUNCTION increase: start,increment,maxV
LOCAL result

result=start+increment
WHILE result>=maxV
DEC result,maxV
WEND

WHILE result<0
INC result,maxV
WEND

RETURN result
ENDFUNCTION

FUNCTION accelerate:v,accel,dt
RETURN v + (accel * dt)
ENDFUNCTION

FUNCTION limit:value, minV,maxV
RETURN MAX(minV,MIN(value,maxV))
ENDFUNCTION

FUNCTION easeIn:a,b,percent
RETURN a + (b-a)*POW(percent,2)
ENDFUNCTION

  FUNCTION easeOut:a,b,percent
  RETURN a + (b-a)*(1.0-POW(1-percent,2))
  ENDFUNCTION
 
  FUNCTION easeInOut:a,b,percent
  RETURN a + (b-a)*((-COS(percent*180.0)/2.0) + 0.5)
  ENDFUNCTION
 
FUNCTION percentageRemaining:n,total
RETURN MOD(n,total)/total
ENDFUNCTION

FUNCTION fog%:x%,y%,width%,height%
ALPHAMODE -0.9
DRAWRECT x%,y%,width%,height%,RGB(64,64,64)
ALPHAMODE -1.0
ENDFUNCTION

FUNCTION update%:dt
LOCAL dx

// DEBUG dt+"\n"
// DEBUG "Speed:"+self.speed+"\n"
// DEBUG (dt*self.speed)+"\n"

      self.position = self.increase(self.position, dt * self.speed, self.trackLength)

      dx = dt * 2 * (self.speed/self.maxSpeed); // at top speed, should be able to cross from left to right (-1 to 1) in 1 second

IF KEY(203)
DEC self.playerX,dx
self.keyLeftRight%=-1
ELSEIF KEY(205)
INC self.playerX,dx
self.keyLeftRight%=1
ELSE
self.keyLeftRight%=0
ENDIF

IF KEY(200)
self.speed=self.accelerate(self.speed,self.accel,dt)
ELSEIF KEY(208)
self.speed=self.accelerate(self.speed,self.breaking,dt)
ELSE
self.speed=self.accelerate(self.speed,self.decel,dt)
ENDIF

IF (self.playerX<-1.0 OR self.playerX>1.0) AND (self.speed>self.offRoadLimit)
self.speed=self.accelerate(self.speed,self.offRoadDecel,dt)
ENDIF

self.playerX=self.limit(self.playerX,-2,2)
self.speed=self.limit(self.speed,0.0,self.maxSpeed)

ENDFUNCTION

FUNCTION project%:p AS tPart, cameraX, cameraY, cameraZ, cameraDepth, width%, height%, roadWidth%
p.camera.x=p.world.x - cameraX
p.camera.y=p.world.y - cameraY
p.camera.z=p.world.z - cameraZ

p.screen.scale=self.cameraDepth/p.camera.z
// DEBUG "Z : "+p.camera.z+" "+cameraZ+"\n"
// DEBUG "Y : "+p.camera.y+" "+cameraY+"\n"
// DEBUG "X : "+p.camera.x+" "+cameraX+"\n"
// DEBUG "Camera depth : "+cameraDepth+" p.camera.z : "+p.camera.z+"\n"
// DEBUG "Scale : "+p.screen.scale+"\n"
// DEBUG "Width : "+width%+" Height : "+height%+"\n"
// DEBUG "Road width : "+roadWidth%+"\n"

p.screen.x=INTEGER((width%/2)+(p.screen.scale*p.camera.x*width%/2))
p.screen.y=INTEGER((height%/2)-(p.screen.scale*p.camera.y*height%/2))
p.screen.w=INTEGER(p.screen.scale*roadWidth%*width%/2)

// DEBUG p.screen.x+" "+p.screen.y+" "+p.screen.w+"\n"
//KEYWAIT
ENDFUNCTION

FUNCTION player%:width%, height%, resolution, roadWidth, sprites, speedPercent, scale, destX, destY, steer, updown
LOCAL bounce
LOCAL sprite%

bounce=1.5*(RND(2)-1)*speedPercent*resolution
IF steer<0
sprite%=IIF(updown>0,PLAYER_UPHILL_LEFT%,PLAYER_LEFT%)
ELSEIF steer>0
sprite%=IIF(updown>0,PLAYER_UPHILL_RIGHT%,PLAYER_RIGHT%)
ELSE
sprite%=IIF(updown>0,PLAYER_UPHILL_STRAIGHT%,PLAYER_STRAIGHT%)
ENDIF

DEBUG "SP:"+sprite%+"\n"

self.sprite(width%,height%,resolution,roadWidth,sprite%,scale,destX,destY+bounce,-0.5,-1)
ENDFUNCTION

FUNCTION sprite%:width%, height%, resolution, roadWidth, sprite%, scale, destX, destY, offsetX, offsetY, clipY=0.0
LOCAL destW,destH,clipH

GETSPRITESIZE sprite%,destW,destH
destW=destW*scale*(width%/2.0)*self.SPRITES_SCALE*roadWidth
destH=destH*scale*(width%/2.0)*self.SPRITES_SCALE*roadWidth

DEBUG "Scale : "+self.SPRITES_SCALE+" Width : "+roadWidth+"\n"
DEBUG "W:"+destW+" "+destH+"\n"

INC destX,destW*offsetX
INC destY,destH*offsetY

clipH=IIF(clipY,MAX(0,destY+destH-clipY),0)

IF clipH<destH
STRETCHSPRITE sprite%,destX,destY,destW,destH-clipH
ENDIF
ENDFUNCTION


FUNCTION render%:
LOCAL baseSegment AS tSegment
LOCAL basePercentage
LOCAL maxy%,n%
LOCAL segment AS tSegment
LOCAL x,dx

// DEBUG "H2:"+self.height%+"\n"

maxy%=self.height%
// DEBUG "maxy : "+maxy%+"\n"
DEBUG "Position : "+self.position+"\n"

baseSegment = self.findSegment(self.position)
basePercentage=self.percentageRemaining(self.position,self.segmentLength%)

x=0.0
dx=-(baseSegment.curve*basePercentage)

PRINT FORMAT$(4,4,dx),0,0
//DEBUG "Base Percentage : "+basePercentage+" DX : "+dx+"\n"
// KEYWAIT

FOR n%=0 TO self.drawDistance%-1
segment=self.segments[MOD(baseSegment.index%+n%,BOUNDS(self.segments[],0))]

self.project(segment.p1,(self.playerX*self.roadWidth%*1.0)-x,self.cameraHeight%,self.position%,self.cameraDepth,self.width%,self.height%,self.roadWidth%)
self.project(segment.p2,(self.playerX*self.roadWidth%*1.0)-x-dx,self.cameraHeight%,self.position%,self.cameraDepth,self.width%,self.height%,self.roadWidth%)

// DEBUG "Segment 1 camera Z : "+segment.p1.camera.z+"\n"
// DEBUG "Segment 2 screen Y : "+segment.p2.screen.y+"\n"
// DEBUG "maxy : "+maxy%+"\n"

INC x,dx
INC dx,baseSegment.curve

IF segment.p1.camera.z<=self.cameraDepth OR segment.p2.screen.y>=maxy%
CONTINUE
ELSE
self.segment(self.width%,self.lanes%, _
segment.p1.screen.x, _
                  segment.p1.screen.y, _
                    segment.p1.screen.w, _
                    segment.p2.screen.x, _
                    segment.p2.screen.y, _
                    segment.p2.screen.w, _
                    1.0, _
                    segment.colour)
ENDIF

maxy%=segment.p2.screen.y%
NEXT

self.player(self.width%, self.height%, self.resolution, self.roadWidth% ,PLAYER_STRAIGHT%,self.speed/self.maxSpeed, _
self.cameraDepth/self.playerZ, _
self.width%/2, _
self.height, _
self.speed * SGN(self.keyLeftRight%), _
0)
//                    self.cameraDepth/self.playerZ, _
  //                  self.width%/2, _
    //                self.height, _
      //              self.speed * 1, _ // (keyLeft ? -1 : keyRight ? 1 : 0),
         //           0)
ENDFUNCTION

FUNCTION rumbleWidth:projectedRoadWidth%,lanes%
RETURN projectedRoadWidth%/MAX(6,2*lanes%)
ENDFUNCTION

FUNCTION laneMarkerWidth:projectedRoadWidth%,lanes%
RETURN projectedRoadWidth/MAX(32,8*lanes%)
ENDFUNCTION

FUNCTION polygon%:x1,y1,x2,y2,x3,y3,x4,y4,colour%
STARTPOLY 99,0
POLYVECTOR x1,y1,x2,y2,colour%
POLYVECTOR x2,y2,x3,y3,colour%
POLYVECTOR x3,y3,x4,y4,colour%
POLYVECTOR x4,y4,x1,y2,colour%
ENDPOLY

// DRAWRECT x1,y1,x2,y2,colour%
// DRAWRECT x3,y3,x4,y4,colour%

//DRAWLINE x1,y1,x2,y2,colour%
//DRAWLINE x3,y3,x4,y4,colour%
ENDFUNCTION

FUNCTION segment%:width%,lanes%,x1,y1,w1,x2,y2,w2,fog,colour AS tColour
LOCAL r1,r2,l1,l2,lanew1,lanew2,lanex1,lanex2

r1=self.rumbleWidth(w1,lanes%)
r2=self.rumbleWidth(w2,lanes%)
l1=self.laneMarkerWidth(w1,lanes%)
l2=self.laneMarkerWidth(w2,lanes%)

//DEBUG "R1 : "+r1%+" R2 : "+r2%+" l1 : "+l1%+" l2 : "+l2%+"\n"
ALPHAMODE -1.0
DRAWRECT 0,y2,width%,y1-y2,colour.grass%

    self.polygon(x1-w1-r1, y1, x1-w1, y1, x2-w2, y2, x2-w2-r2, y2, colour.rumble)
    self.polygon(x1+w1+r1, y1, x1+w1, y1, x2+w2, y2, x2+w2+r2, y2, colour.rumble)
    self.polygon(x1-w1, y1, x1+w1, y1, x2+w2, y2, x2-w2, y2, colour.road)

    IF colour.lane%
    lanew1=w1*2.0/lanes%
    lanew2=w2*2.0/lanes%

    lanex1=x1-w1+lanew1
    lanex2=x2-w2+lanew2

    FOR lane%=1 TO lanes%-1
    self.polygon(lanex1-l1/2.0,y1,lanex1+l1/2.0,y1,lanex2+l2/2.0,y2,lanex2-l2/2.0,y2,colour.lane%)
    INC lanex1,lanew1
    INC lanex2,lanew2

    NEXT
    ENDIF

    //self.fog(0,y1,width%,y2-y1)

    ENDFUNCTION
ENDTYPE

LOCAL road AS TRoad
LOCAL now,last,dt,gdt,stp=0.025

LIMITFPS -1

LOADSPRITE "Media/player_straight.png",PLAYER_STRAIGHT%
LOADSPRITE "Media/player_left.png",PLAYER_LEFT%
LOADSPRITE "Media/player_right.png",PLAYER_RIGHT%
LOADSPRITE "Media/player_uphill_straight.png",PLAYER_UPHILL_STRAIGHT%
LOADSPRITE "Media/player_uphill_left.png",PLAYER_UPHILL_LEFT%
LOADSPRITE "Media/player_uphill_right.png",PLAYER_UPHILL_RIGHT%

road.Initialise()
road.resetRoad()

now=GETTIMERALL()
last=GETTIMERALL()

WHILE TRUE
now=GETTIMERALL()
DEBUG last+" "+now+"\n"
dt=MIN(1.0,(now-last)/1000.0)
INC gdt,dt

WHILE gdt>stp
DEC gdt,stp
road.update(stp)
WEND

road.update(stp)

SMOOTHSHADING FALSE

road.render()
SHOWSCREEN

last = now
WEND


I've always wanted to do a game like Outrun/Lotus Esprit, but the main problem has been graphics...
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Part 1 & 2
Post by: mentalthink on 2014-Jun-09
Very very cool, and very very usefull,... Thanks a lot for the code....
<3 <3
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Part 1 & 2
Post by: MrTAToad on 2014-Jun-09
No problem!

This is the third part : Hills & trough's.  I had to sort the easing calculation for this - and worked out why it wasn't working properly : I was multiplying the result of COS by 180/PI rather than the initial value :)

Code (glbasic) Select
// --------------------------------- //
// Project: Test2
// Start: Tuesday, April 01, 2014
// IDE Version: 12.001
CONSTANT PLAYER_STRAIGHT% = 0
CONSTANT PLAYER_LEFT% = 1
CONSTANT PLAYER_RIGHT% = 2
CONSTANT PLAYER_UPHILL_STRAIGHT% = 3
CONSTANT PLAYER_UPHILL_LEFT% = 4
CONSTANT PLAYER_UPHILL_RIGHT% = 5

CONSTANT ROAD_LENGTH_NONE% = 0
CONSTANT ROAD_LENGTH_SHORT% = 25
CONSTANT ROAD_LENGTH_MEDIUM% = 50
CONSTANT ROAD_LENGTH_LONG% = 100

CONSTANT ROAD_HILL_NONE% = 0
CONSTANT ROAD_HILL_LOW% = 20
CONSTANT ROAD_HILL_MEDIUM% = 40
CONSTANT ROAD_HILL_HIGH% = 60

CONSTANT ROAD_CURVE_NONE% = 0
CONSTANT ROAD_CURVE_EASY% = 2
CONSTANT ROAD_CURVE_MEDIUM% = 4
CONSTANT ROAD_CURVE_HARD% = 6
   
CONSTANT PI = 3.14159

CONSTANT COLOURS_SKY% = 7526382
CONSTANT COLOURS_TREE% = 20744
CONSTANT COLOURS_FOG% = 20744

TYPE tScreen
scale
x
y
w
ENDTYPE

TYPE tCamera
x
y
z
ENDTYPE

TYPE tWorld
x
y
z
ENDTYPE

TYPE tColour
rumble%
road%
grass%
lane%
ENDTYPE

TYPE tPart
world AS tWorld
camera AS tCamera
screen AS tScreen
ENDTYPE

TYPE tSegment
index%
colour AS tColour
p1 AS tPart
p2 AS tPart
curve
ENDTYPE

TYPE TRoad
segments[] AS tSegment

LIGHT AS tColour
DARK AS tColour

SPRITES_SCALE

fps = 75.0
fpsStep = 0.0
segmentLength% = 200    // Length of a single segment
rumbleLength% = 3 // Number of segments per red/white rumble strip
roadWidth%      = 2000 // Actually half the roads width, easier math if the road spans from -roadWidth to +roadWidth
cameraHeight%  = 1000  // Z height of camera
cameraDepth    = 0
position      = 0.0     // Current camera Z position
playerX        = 0 // Player x offset from center of road (-1 to 1 to stay independent of roadWidth)
playerZ        = 0 // Player relative z distance from camera (computed)
lanes%          = 3       // Number of lanes
fieldOfView%    = 100     // angle (degrees) for field of view
speed          = 0.0     // current speed
maxSpeed      = 0       // top speed (ensure we can't move more than 1 segment in a single frame to make collision detection easier)
accel = 0 // acceleration rate - tuned until it 'felt' right
    breaking = 0 // deceleration rate when braking
    decel = 0 // 'natural' deceleration rate when neither accelerating, nor braking
    offRoadDecel = 0 // off road deceleration is somewhere in between
    offRoadLimit = 0 // limit when off road deceleration no longer applies (e.g. you can always go at least t
    resolution = 0.0

width%;height%
keyLeftRight%

trackLength%

drawDistance% = 100

FUNCTION Initialise%:
LOCAL tW%,tH%

GETSCREENSIZE self.width%,self.height%

self.position=0.0

self.cameraDepth=1.0 / TAN((self.fieldOfView%/2)) // * 3.141592653/180.0)
DEBUG "Camera depth : "+self.cameraDepth+" "+self.fieldOfView%+"\n"

self.LIGHT.road%=7039851
self.LIGHT.grass%=1092112
self.LIGHT.rumble%=5592405
self.LIGHT.lane%=13421772

self.DARK.road%=6908265
self.DARK.grass%=39424
self.DARK.rumble%=12303291
self.DARK.lane%=0

self.fpsStep=1.0/self.fps
self.maxSpeed=self.segmentLength/self.fpsStep

self.accel = self.maxSpeed/5.0
    self.breaking = -self.maxSpeed
    self.decel = -self.maxSpeed/5.0
    self.offRoadDecel = -self.maxSpeed/2.0
    self.offRoadLimit = self.maxSpeed/4.0
    self.speed=0.0
    self.playerZ=self.cameraHeight * self.cameraDepth
    self.resolution=self.height%/640.0

self.keyLeftRight%=0

    GETSPRITESIZE PLAYER_STRAIGHT%,tW%,tH%
    self.SPRITES_SCALE= 0.3 * (1.0/tW%)
ENDFUNCTION

FUNCTION addSegment%:curve,y
LOCAL size%
LOCAL segment AS tSegment

size%=BOUNDS(self.segments[],0)

// DEBUG "Curve : "+curve+"\n"

segment.index%=size%
segment.curve=curve
segment.p1.world.x=0.0
segment.p1.world.y=self.lastY()
segment.p1.world.z=size%*self.segmentLength%
segment.p1.camera.x=0.0
segment.p1.camera.y=0.0
segment.p1.camera.z=0.0
segment.p1.screen.x=0.0
segment.p1.screen.y=0.0
segment.p1.screen.w=0.0
segment.p1.screen.scale=0.0

segment.p2.world.x=0.0
segment.p2.world.y=y
segment.p2.world.z=(size%+1)*self.segmentLength%
segment.p2.camera.x=0.0
segment.p2.camera.y=0.0
segment.p2.camera.z=0.0
segment.p2.screen.x=0.0
segment.p2.screen.y=0.0
segment.p2.screen.w=0.0
segment.p2.screen.scale=0.0

IF FLOOR(MOD(size%/self.rumbleLength%,2))
segment.colour=self.LIGHT
ELSE
segment.colour=self.DARK
ENDIF

segment.colour.lane%=RGB(255,255,0)

// DEBUG "> : "+segment.p1.world.y+" "+segment.p2.world.y+"\n"

DIMPUSH self.segments[],segment
ENDFUNCTION

FUNCTION addRoad%:enter, hold, leave, curve, y
LOCAL startY,endY,total
LOCAL n%

startY=self.lastY()
endY=startY+(INTEGER(y)*self.segmentLength%)
total=enter+hold+leave

// DEBUG "Start Y : "+startY+" endY : "+endY+"\n"
// KEYWAIT
FOR n%=0 TO enter-1;
self.addSegment(self.easeIn(0,curve,n%/enter),easeInOut(startY,endY,n%/total));
NEXT
// DEBUG "Holding ---\n"
FOR n%=0 TO hold-1
self.addSegment(curve,self.easeInOut(startY,endY,(enter+n%)/total));
NEXT
// DEBUG "---\n"

FOR n%=0 TO leave-1
self.addSegment(self.easeInOut(curve,0,n%/leave),self.easeInOut(startY,endY,(enter+hold+n)/total))
NEXT
ENDFUNCTION
   
FUNCTION addStraight%:num=25
self.addRoad(num,num,num,0,0)
ENDFUNCTION

FUNCTION addHill%:num,height
self.addRoad(num,num,num,0,height)
ENDFUNCTION

    FUNCTION addCurve%:num, curve,height
    self.addRoad(num,num,num,curve,height)
    ENDFUNCTION
   
    FUNCTION addLowRollingHills%:num,height
    self.addRoad(num, num, num, 0, height/2)
      self.addRoad(num, num, num, 0, -height)
      self.addRoad(num, num, num, 0, height)
      self.addRoad(num, num, num, 0, 0)
      self.addRoad(num, num, num, 0, height/2)
      self.addRoad(num, num, num, 0, 0)
    ENDFUNCTION
   
    FUNCTION addSCurves%:
      self.addRoad(ROAD_LENGTH_MEDIUM, ROAD_LENGTH_MEDIUM, ROAD_LENGTH_MEDIUM, -ROAD_CURVE_EASY, ROAD_HILL_NONE)
      self.addRoad(ROAD_LENGTH_MEDIUM, ROAD_LENGTH_MEDIUM, ROAD_LENGTH_MEDIUM, ROAD_CURVE_MEDIUM, ROAD_HILL_MEDIUM)
      self.addRoad(ROAD_LENGTH_MEDIUM, ROAD_LENGTH_MEDIUM, ROAD_LENGTH_MEDIUM, ROAD_CURVE_EASY, -ROAD_HILL_LOW)
      self.addRoad(ROAD_LENGTH_MEDIUM, ROAD_LENGTH_MEDIUM, ROAD_LENGTH_MEDIUM, -ROAD_CURVE_EASY, ROAD_HILL_MEDIUM)
      self.addRoad(ROAD_LENGTH_MEDIUM, ROAD_LENGTH_MEDIUM, ROAD_LENGTH_MEDIUM, -ROAD_CURVE_MEDIUM, -ROAD_HILL_MEDIUM)
    ENDFUNCTION
   
    FUNCTION addDownhillToEnd%:num=200
      self.addRoad(num, num, num, -ROAD_CURVE_EASY, -lastY()/self.segmentLength);
    ENDFUNCTION
   
FUNCTION resetRoad%:
LOCAL segment AS tSegment

DIM self.segments[0]

self.addStraight(ROAD_LENGTH_SHORT/2)
      self.addHill(ROAD_LENGTH_SHORT, ROAD_HILL_LOW%)
      self.addLowRollingHills(ROAD_LENGTH_SHORT,ROAD_HILL_LOW%)
      self.addCurve(ROAD_LENGTH_MEDIUM, ROAD_CURVE_MEDIUM, ROAD_HILL_LOW)
      self.addLowRollingHills(ROAD_LENGTH_SHORT,ROAD_HILL_LOW%)
      self.addCurve(ROAD_LENGTH_LONG, ROAD_CURVE_MEDIUM, ROAD_HILL_MEDIUM)
      self.addStraight()
      self.addCurve(ROAD_LENGTH_LONG, -ROAD_CURVE_MEDIUM, ROAD_HILL_MEDIUM)
      self.addHill(ROAD_LENGTH_LONG, ROAD_HILL_HIGH)
      self.addCurve(ROAD_LENGTH_LONG, ROAD_CURVE_MEDIUM, -ROAD_HILL_LOW)
      self.addHill(ROAD_LENGTH_LONG, -ROAD_HILL_MEDIUM)
      self.addStraight()
      self.addDownhillToEnd()

segment=self.findSegment(self.playerZ)
      self.segments[segment.index% + 2].colour.road% = RGB(255,255,255)
      self.segments[segment.index% + 2].colour.lane% = RGB(255,255,255)
      self.segments[segment.index% + 3].colour.road% = RGB(255,255,255)
      self.segments[segment.index% + 3].colour.lane% = RGB(255,255,255)
     
      FOR n%=0 TO self.rumbleLength%-1
      self.segments[BOUNDS(self.segments[],0)-1-n%].colour.road% = RGB(255,255,255)
      self.segments[BOUNDS(self.segments[],0)-1-n%].colour.lane% = RGB(255,255,255)
      NEXT

self.trackLength% = BOUNDS(self.segments[],0)*self.segmentLength%
ENDFUNCTION

FUNCTION lastY:
LOCAL size%

size%=BOUNDS(self.segments[],0)
IF size%=0
RETURN 0
ELSE
RETURN self.segments[size%-1].p2.world.y
ENDIF
ENDFUNCTION

FUNCTION findSegment AS tSegment:z
RETURN self.segments[MOD(FLOOR(z/self.segmentLength%),BOUNDS(self.segments[],0))]
ENDFUNCTION

FUNCTION increase: start,increment,maxV
LOCAL result

result=start+increment
WHILE result>=maxV
DEC result,maxV
WEND

WHILE result<0
INC result,maxV
WEND

RETURN result
ENDFUNCTION

FUNCTION accelerate:v,accel,dt
RETURN v + (accel * dt)
ENDFUNCTION

FUNCTION limit:value, minV,maxV
RETURN MAX(minV,MIN(value,maxV))
ENDFUNCTION

FUNCTION easeIn:a,b,percent
RETURN a + (b-a)*POW(percent,2)
ENDFUNCTION

  FUNCTION easeOut:a,b,percent
  RETURN a + (b-a)*(1.0-POW(1-percent,2))
  ENDFUNCTION
 
  FUNCTION easeInOut:a,b,percent
  LOCAL cosV,v
 
  // DEBUG "A:"+a+" B:"+b+" Percent:"+percent+"\n"
  cosV=(percent*PI)*(180.0/PI)
  v=a + (b-a)*((-COS(cosV)/2.0) + 0.5)
//  DEBUG "Value : "+v+"\n"
  RETURN v
  ENDFUNCTION
 
FUNCTION percentageRemaining:n,total
RETURN MOD(n,total)/total
ENDFUNCTION

FUNCTION interpolate:a,b,percent
RETURN a+(b-a)*percent
ENDFUNCTION

FUNCTION fog%:x%,y%,width%,height%
ALPHAMODE -0.9
DRAWRECT x%,y%,width%,height%,RGB(64,64,64)
ALPHAMODE -1.0
ENDFUNCTION

FUNCTION update%:dt
LOCAL dx

// DEBUG dt+"\n"
// DEBUG "Speed:"+self.speed+"\n"
// DEBUG (dt*self.speed)+"\n"

      self.position = self.increase(self.position, dt * self.speed, self.trackLength)

      dx = dt * 2 * (self.speed/self.maxSpeed); // at top speed, should be able to cross from left to right (-1 to 1) in 1 second

IF KEY(203)
DEC self.playerX,dx
self.keyLeftRight%=-1
ELSEIF KEY(205)
INC self.playerX,dx
self.keyLeftRight%=1
ELSE
self.keyLeftRight%=0
ENDIF

IF KEY(200)
self.speed=self.accelerate(self.speed,self.accel,dt)
ELSEIF KEY(208)
self.speed=self.accelerate(self.speed,self.breaking,dt)
ELSE
self.speed=self.accelerate(self.speed,self.decel,dt)
ENDIF

IF (self.playerX<-1.0 OR self.playerX>1.0) AND (self.speed>self.offRoadLimit)
self.speed=self.accelerate(self.speed,self.offRoadDecel,dt)
ENDIF

self.playerX=self.limit(self.playerX,-2,2)
self.speed=self.limit(self.speed,0.0,self.maxSpeed)

ENDFUNCTION

FUNCTION project%:p AS tPart, cameraX, cameraY, cameraZ, cameraDepth, width%, height%, roadWidth%
p.camera.x=p.world.x - cameraX
p.camera.y=p.world.y - cameraY
p.camera.z=p.world.z - cameraZ

p.screen.scale=self.cameraDepth/p.camera.z
// DEBUG "Z : "+p.camera.z+" "+cameraZ+"\n"
// DEBUG "Y : "+p.camera.y+" "+cameraY+"\n"
// DEBUG "X : "+p.camera.x+" "+cameraX+"\n"
// DEBUG "Camera depth : "+cameraDepth+" p.camera.z : "+p.camera.z+"\n"
// DEBUG "Scale : "+p.screen.scale+"\n"
// DEBUG "Width : "+width%+" Height : "+height%+"\n"
// DEBUG "Road width : "+roadWidth%+"\n"

p.screen.x=INTEGER((width%/2)+(p.screen.scale*p.camera.x*width%/2))
p.screen.y=INTEGER((height%/2)-(p.screen.scale*p.camera.y*height%/2))
p.screen.w=INTEGER(p.screen.scale*roadWidth%*width%/2)

// DEBUG p.screen.x+" "+p.screen.y+" "+p.screen.w+"\n"
//KEYWAIT
ENDFUNCTION

FUNCTION player%:width%, height%, resolution, roadWidth, sprites, speedPercent, scale, destX, destY, steer, updown
LOCAL bounce
LOCAL sprite%

bounce=1.5*(RND(2)-1)*speedPercent*resolution
IF steer<0
sprite%=IIF(updown>0,PLAYER_UPHILL_LEFT%,PLAYER_LEFT%)
ELSEIF steer>0
sprite%=IIF(updown>0,PLAYER_UPHILL_RIGHT%,PLAYER_RIGHT%)
ELSE
sprite%=IIF(updown>0,PLAYER_UPHILL_STRAIGHT%,PLAYER_STRAIGHT%)
ENDIF

DEBUG "SP:"+sprite%+"\n"

self.sprite(width%,height%,resolution,roadWidth,sprite%,scale,destX,destY+bounce,-0.5,-1)
ENDFUNCTION

FUNCTION sprite%:width%, height%, resolution, roadWidth, sprite%, scale, destX, destY, offsetX, offsetY, clipY=0.0
LOCAL destW,destH,clipH

GETSPRITESIZE sprite%,destW,destH
destW=destW*scale*(width%/2.0)*self.SPRITES_SCALE*roadWidth
destH=destH*scale*(width%/2.0)*self.SPRITES_SCALE*roadWidth

DEBUG "Scale : "+self.SPRITES_SCALE+" Width : "+roadWidth+"\n"
DEBUG "W:"+destW+" "+destH+"\n"

INC destX,destW*offsetX
INC destY,destH*offsetY

clipH=IIF(clipY,MAX(0,destY+destH-clipY),0)

IF clipH<destH
STRETCHSPRITE sprite%,destX,destY,destW,destH-clipH
ENDIF
ENDFUNCTION


FUNCTION render%:
LOCAL baseSegment AS tSegment,playerSegment AS tSegment
LOCAL basePercentage,playerY,playerPercent
LOCAL maxy%,n%
LOCAL segment AS tSegment
LOCAL x,dx

// DEBUG "H2:"+self.height%+"\n"

maxy%=self.height%
// DEBUG "maxy : "+maxy%+"\n"
DEBUG "Position : "+self.position+"\n"


baseSegment = self.findSegment(self.position)
playerSegment=self.findSegment(self.position+self.playerZ)
basePercentage=self.percentageRemaining(self.position,self.segmentLength%)
playerPercent=self.percentageRemaining(self.position+self.playerZ,self.segmentLength%)
playerY= self.interpolate(playerSegment.p1.world.y, playerSegment.p2.world.y, playerPercent);

x=0.0
dx=-(baseSegment.curve*basePercentage)

PRINT FORMAT$(4,4,dx),0,0
//DEBUG "Base Percentage : "+basePercentage+" DX : "+dx+"\n"
// KEYWAIT

FOR n%=0 TO self.drawDistance%-1
segment=self.segments[MOD(baseSegment.index%+n%,BOUNDS(self.segments[],0))]

self.project(segment.p1,(self.playerX*self.roadWidth%*1.0)-x,playerY+self.cameraHeight%,self.position%,self.cameraDepth,self.width%,self.height%,self.roadWidth%)
self.project(segment.p2,(self.playerX*self.roadWidth%*1.0)-x-dx,playerY+self.cameraHeight%,self.position%,self.cameraDepth,self.width%,self.height%,self.roadWidth%)

// DEBUG "Segment 1 camera Z : "+segment.p1.camera.z+"\n"
// DEBUG "Segment 2 screen Y : "+segment.p2.screen.y+"\n"
// DEBUG "maxy : "+maxy%+"\n"

INC x,dx
INC dx,baseSegment.curve

IF segment.p1.camera.z<=self.cameraDepth OR segment.p2.screen.y>=maxy%
CONTINUE
ELSE
self.segment(self.width%,self.lanes%, _
segment.p1.screen.x, _
                  segment.p1.screen.y, _
                    segment.p1.screen.w, _
                    segment.p2.screen.x, _
                    segment.p2.screen.y, _
                    segment.p2.screen.w, _
                    1.0, _
                    segment.colour)
ENDIF

maxy%=segment.p2.screen.y%
NEXT

self.player(self.width%, self.height%, self.resolution, self.roadWidth% ,PLAYER_STRAIGHT%,self.speed/self.maxSpeed, _
self.cameraDepth/self.playerZ, _
self.width%/2, _
self.height- (self.cameraDepth/self.playerZ * interpolate(playerSegment.p1.camera.y, playerSegment.p2.camera.y, playerPercent) * self.height/2.0), _
self.speed * SGN(self.keyLeftRight%), _
playerSegment.p2.world.y - playerSegment.p1.world.y)
//                    self.cameraDepth/self.playerZ, _
  //                  self.width%/2, _
    //                self.height, _
      //              self.speed * 1, _ // (keyLeft ? -1 : keyRight ? 1 : 0),
         //           0)
ENDFUNCTION

FUNCTION rumbleWidth:projectedRoadWidth%,lanes%
RETURN projectedRoadWidth%/MAX(6,2*lanes%)
ENDFUNCTION

FUNCTION laneMarkerWidth:projectedRoadWidth%,lanes%
RETURN projectedRoadWidth/MAX(32,8*lanes%)
ENDFUNCTION

FUNCTION polygon%:x1,y1,x2,y2,x3,y3,x4,y4,colour%
STARTPOLY 99,0
POLYVECTOR x1,y1,x2,y2,colour%
POLYVECTOR x2,y2,x3,y3,colour%
POLYVECTOR x3,y3,x4,y4,colour%
POLYVECTOR x4,y4,x1,y2,colour%
ENDPOLY

// DRAWRECT x1,y1,x2,y2,colour%
// DRAWRECT x3,y3,x4,y4,colour%

//DRAWLINE x1,y1,x2,y2,colour%
//DRAWLINE x3,y3,x4,y4,colour%
ENDFUNCTION

FUNCTION segment%:width%,lanes%,x1,y1,w1,x2,y2,w2,fog,colour AS tColour
LOCAL r1,r2,l1,l2,lanew1,lanew2,lanex1,lanex2

r1=self.rumbleWidth(w1,lanes%)
r2=self.rumbleWidth(w2,lanes%)
l1=self.laneMarkerWidth(w1,lanes%)
l2=self.laneMarkerWidth(w2,lanes%)

//DEBUG "R1 : "+r1%+" R2 : "+r2%+" l1 : "+l1%+" l2 : "+l2%+"\n"
ALPHAMODE -1.0
DRAWRECT 0,y2,width%,y1-y2,colour.grass%

    self.polygon(x1-w1-r1, y1, x1-w1, y1, x2-w2, y2, x2-w2-r2, y2, colour.rumble)
    self.polygon(x1+w1+r1, y1, x1+w1, y1, x2+w2, y2, x2+w2+r2, y2, colour.rumble)
    self.polygon(x1-w1, y1, x1+w1, y1, x2+w2, y2, x2-w2, y2, colour.road)

    IF colour.lane%
    lanew1=w1*2.0/lanes%
    lanew2=w2*2.0/lanes%

    lanex1=x1-w1+lanew1
    lanex2=x2-w2+lanew2

    FOR lane%=1 TO lanes%-1
    self.polygon(lanex1-l1/2.0,y1,lanex1+l1/2.0,y1,lanex2+l2/2.0,y2,lanex2-l2/2.0,y2,colour.lane%)
    INC lanex1,lanew1
    INC lanex2,lanew2

    NEXT
    ENDIF

    //self.fog(0,y1,width%,y2-y1)

    ENDFUNCTION
ENDTYPE

LOCAL road AS TRoad
LOCAL now,last,dt,gdt,stp=0.025

LIMITFPS -1

LOADSPRITE "Media/player_straight.png",PLAYER_STRAIGHT%
LOADSPRITE "Media/player_left.png",PLAYER_LEFT%
LOADSPRITE "Media/player_right.png",PLAYER_RIGHT%
LOADSPRITE "Media/player_uphill_straight.png",PLAYER_UPHILL_STRAIGHT%
LOADSPRITE "Media/player_uphill_left.png",PLAYER_UPHILL_LEFT%
LOADSPRITE "Media/player_uphill_right.png",PLAYER_UPHILL_RIGHT%

road.Initialise()
road.resetRoad()

now=GETTIMERALL()
last=GETTIMERALL()

WHILE TRUE
now=GETTIMERALL()
DEBUG last+" "+now+"\n"
dt=MIN(1.0,(now-last)/1000.0)
INC gdt,dt

WHILE gdt>stp
DEC gdt,stp
road.update(stp)
WEND

road.update(stp)

SMOOTHSHADING FALSE

road.render()
SHOWSCREEN

last = now
WEND


Youtube link : https://www.youtube.com/watch?v=Zlpll6c03-Y (https://www.youtube.com/watch?v=Zlpll6c03-Y)
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Part 1, 2 and 3
Post by: matchy on 2014-Jun-09
Good stuff. :good: For inspiration, check out: https://www.youtube.com/channel/UCy9fx8XnsX_hP9UsPt4k5IA
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Part 1, 2 and 3
Post by: Ian Price on 2014-Jun-09
Excellent stuffage :)
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Part 1, 2 and 3
Post by: mentalthink on 2014-Jun-09
Thanks Mr-T just yesterday I want comment this point... I think whit this we can done a great game... Thanks a lot, very very usefully.
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Part 1, 2 and 3
Post by: MrTAToad on 2014-Jun-09
No problem!
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Part 1, 2 and 3
Post by: MrTAToad on 2014-Jun-09
A quick preview of the next part : Scenery (and I'll try and get the background graphics in as well later) :

Title: Re: Something of interest : Pseudo 3D Tracks V2 - Part 1, 2 and 3
Post by: Marmor on 2014-Jun-09
great , i like this stuff !
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Part 1, 2, 3 and 4
Post by: MrTAToad on 2014-Jun-10
Good to hear :)

This is the next stage, and has road-side objects and a background.  The background warps unfortunately - must be mis-reading the Javascript code for that, and will have to look at it later.

What could be done in future is "rent" billboard space :)
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Part 1, 2, 3 and 4
Post by: spacefractal on 2014-Jun-10
This screenshot looks nice. There is a cool "Outrun" game with named "Final Freeway" for iOS which could been get some inspiration from. That is a 2d Outrun style game. But scene here also looks nice.
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Part 1, 2, 3 and 4
Post by: MrTAToad on 2014-Jun-11
I'm using the graphics that Code inComplete used for his example programs - for a proper game, you would need a decent/proper/professional artist.

The last part will have the vehicles in!
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3 and 4
Post by: mentalthink on 2014-Jun-11
Very impressive, I think from here can see the light some interesting projects, not it's necessary a car game.... somethink like F-Zero can be cool too!!!

Thanks MrT this stuff have a lot of quality..  :booze:
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3 and 4
Post by: MrTAToad on 2014-Jun-11
Things that would be good to add (aside from more efficient code) :

A)  Different sides having different coloured background (and possibly emulate height even more by being able to set the height for that)
B) Tunnels
C) Split tracks
D) Track sections having different road and line widths

Got it all up and running now with vehicles moving and over taking.

The main problem (aside from the shuddering - I think that is due to how GLBasic scales stuff), is that collision against objects at the side are based on the size of the original graphic and not what it should be...  It would require a fair bit of code to deal with that unfortunately (Javascript just fetches the current sprite size).

As you'll see in the video, I've modified the track somewhat :)

F-Zero I think works on a slightly different principle - namely its tracks are 3D transformed bitmaps

Video : http://www.dailymotion.com/video/x1z3e9u_racing-game-demo-with-glbasic_tech
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: matchy on 2014-Jun-11
Smooth video.  :) I guess you should be able to easily flip the traffic sprites from right to left lane side view, at least for now.

How about some Road Rash sprites?  :D
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: Ian Price on 2014-Jun-11
Not had a chance to test (or respond before now), but I'm really liking what I'm seeing :)
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: HamishTPB on 2014-Jun-11
This is such a good thread, I think perhaps it would be good to put it in the tutorials section for posterity as well.  :nw:
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: MrTAToad on 2014-Jun-11
Quote from: matchy on 2014-Jun-11
Smooth video.  :) I guess you should be able to easily flip the traffic sprites from right to left lane side view, at least for now.

How about some Road Rash sprites?  :D
If you draw some for me :)

Each vehicle needs 9 graphics : straight, left,right ,up+left,up+right,down+left,down+right - shouldn't take you long to do 5 or so vehicles :)
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: matchy on 2014-Jun-11
Quote from: MrTAToad on 2014-Jun-11
Quote from: matchy on 2014-Jun-11
How about some Road Rash sprites?  :D
If you draw some for me :)

Hang on (no pun intended), where did you get those Outrun sprites because I meant rips?
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: MrTAToad on 2014-Jun-11
I didn't get them anywhere - I "borrowed" them from the original authors website...  He, in turn, "borrowed" them from http://pixel.garoux.net/game/44 (http://pixel.garoux.net/game/44)
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: matchy on 2014-Jun-11
Sure but I'm more inclined to automate it than manual pixel draw. I'd would approach it by rendering a 3D vehicle to 2D as opposed to pixel art.  :P Perhaps I'll get a better chance to try out the engine but for now, who's got some vehicles?  ;)  :D
As for, say, billboards, they can be easily produced internally also.
A more import and dynamic example, also something that I need, is implementing configurable plants/trees/fauna types.  :sick:

Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: erico on 2014-Jun-11
This looks great, quite way too fast here.
Yep Matchy, render the car and get the output to pixel art style would be best

I wonder maybe if the system could be up gradable to, let´s say, spline curves instead of just horizontal lines.
Would it be good for gaming?

Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: MrTAToad on 2014-Jun-12
It is way too fast - at the moment I'm upgrading the engine so that the left and right sides can have different coloured backgrounds, track width and number of lines, and well as variable "rumble" widths.

I'll release a more playable version later on...
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: erico on 2014-Jun-12
The extra speed is not bad, you could probably split screen 4x easily on pc :good:
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: MrTAToad on 2014-Jun-12
Certainly could!

I've got a more playable version now.  Need to correct the collision detection with the sides next - its another area that needs optimising.

Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: Slydog on 2014-Jun-12
Wow, this is really coming along!
Multi-layered parallax background scrolling too?
Plays so smooth.
Possible to have forks in the road?
Jumps? Rivers and bridges?
Can't wait to see where this goes.
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: erico on 2014-Jun-12
Jumps, this is begging for jumps! :good:
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: MrTAToad on 2014-Jun-12
I think the background needs improving - the layers don't moving terribly convincingly at the moment.

Got an example of multiple tracks : http://www.dailymotion.com/video/x1z9z10_pseudo-3d-split-tracks_tech (http://www.dailymotion.com/video/x1z9z10_pseudo-3d-split-tracks_tech)

To fork/join them it would be just a matter of changing the distance between them and decide on a rendering order.  Dealing with the animated grass would be harder and I can see that being a dealt with as an array of quads.

Experimenting with tunnels - it turns out that the left and right sides of the tracks will need to be done back to front (like vehicles)

Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: erico on 2014-Jun-13
video is looking good on split roads.
Other then the background, you might want to overlay some degradee to create an aerial perspective.
Lotus 2 is the best example of this game type I have seen so far. maybe a closer to that perspective?
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: MrTAToad on 2014-Jun-13
It would preferably be user-definable.  In the previous program I had it that as the road got wider, the perspective slowly changed.

Tunnels are being a pain, especially with rendering vehicles in a tunnel.

If anyone has any ideas on how to sort that, I would be most greatful :)

By the looks of it, the tunnel face (and possibly exit) and vehicles will need to be sorted by the Z value.  If a  tunnel face is present then no objects before this will be displayed...

Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: MrTAToad on 2014-Jun-13
I think the thing to do first is to allow vertical positioning - this would allow clouds as well...

Anyway, list of features needed :

Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: MrPlow on 2014-Jun-13
Looking very nice indeed!  :good:
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: erico on 2014-Jun-13
I would not know how to do the tunnel sorting, I´d have to try understand the code first. ;/
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: MrTAToad on 2014-Jun-13
My idea for tunnels seems to be fine - see my previous post for more details!
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: Slydog on 2014-Jun-13
This just seems so tedious!  So much work to recreate that retro driving style.
In pure 3D mode, it would be so much easier to create a simple driving game.

I read an article a few years back on this retro 3d technique and was intrigued . . . but not enough to actually do it!
Kudos, you have great patience wise one.   :nw:

What type of game play are you planning, just the usual driving / racing?
I wonder what other driving types could be done with this? 
Bikes riding down a path?
Boats / Jet Skis on a river?
Flying / hovering, but then why the road?  Clouds?

This could be used to make the next viral runner game - but only driving.
Move over Flappy Birds. Touch screen to drive left, let go and it drives right by itself (one touch movement control, like flappy birds!)
Kind of only kidding, that would be hard, as you would always have to be turning one way or the other. But hey, people like hard.
Better maybe to have touch left and right side of screen to turn.  Or tilt.

Place obstacles in your path, make the road width get smaller, increase driving speed - see how far you can drive!
Flappy was a hit - and for no reason!  This at least sounds much more fun.

Edit: Call it "DriRun"! Well - it sounded better in my head.
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: MrTAToad on 2014-Jun-13
Whilst doing it in proper 3D may seem easier, but you've got the trouble of designing the tracks, camera positioning and collision detection.    Plus, of course, you are hoping that the machine is fast enough...

I would like to do something along the lines of Lotus Turbo Challenge 2 or a Trailblazer type game.  Aside from the graphics, another problem will be how to create levels....

This type of thing was used in quite a few arcade games - Space Harrier (just need to modify the ground), Outrun, Line Of Fire, Afterburner/G-Loc or even 3D Deathchase...
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: erico on 2014-Jun-13
yep, I like lotus 2, that would be a great feat!

Other then harrier, this was also used on the more obscure 8-bit games. I like this Technic.
I´m specially found of FEEDBACK on the MSX, though a lot simpler.
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: matchy on 2014-Jun-14
Lotus 2 four players with two Amiga linked. Those were the days.  :)

So how does it currently run on other platforms, line Android/win32?
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: mentalthink on 2014-Jun-15
Yes in 3D it's easy I'm doing testings in Shiva and its really easy, about make the traks really you need only 2 or 3, then you can make any track.

But the good point it's how says Erico it's like lotus, I think if MrT make a game more far than only run the tracks the game will be explendid, something like you have to buy wheels, tires, can buy cars, really make a strategy, the game can be awesome...

I try yesterday to play a bit and I like it a lot, very faster and very playable.
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: MrTAToad on 2014-Jun-16
I'm updating it to be a bit more efficient at the moment - its a multy-pass rendering system now (mainly so that tunnels and what-not could be displayed in the correct Z order).

I think I've spent enough time on this now - there is one fairly serious problem (that I mentioned originally) is that the car bounces around on hills/slopes - I think its due to the segments Y positions being incorrect for some reason.  I have no idea why it happens or how to fix it unfortunately.
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: MrTAToad on 2014-Jun-20
I've finally found out what was causing the jitter!  Only taken several days :)

Need to re-do the rendering routine again now :)
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: SBlectric on 2014-Jun-24
Looks awesome, seriously it brings that retro racer feel right back.
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: MrTAToad on 2014-Jun-26
Yes, these types of games are usually pretty fun.

Glad you like it!
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: Kitty Hello on 2014-Sep-04
Wow, that's cool.
For the sprites, you might use a 3D model and render it from different angles?
Or do photos of a real car and reduce it to 16 colours...
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: MrTAToad on 2014-Sep-04
Thanks!  For the graphics side I would need someone to design them.
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: Hemlos on 2014-Sep-06
i see multiple test app and a gbas in this thread...

Could you put them together in a package and update it to the top of the thread?
Title: Re: Something of interest : Pseudo 3D Tracks V2 - Parts 1, 2, 3, 4 and 5
Post by: MrTAToad on 2014-Sep-07
Better yet, its in the Showcase - its a slightly earlier version (before I fixed the positioning problem), but everything should be there.