Okay!
Here is my pseudo 3D road code. It was converted from my GLBasic code, which in turn was converted from the Javascript code by CodeIncomplete :
- Code: Select all
#PLAYER_STRAIGHT = 0
#PLAYER_LEFT = 1
#PLAYER_RIGHT = 2
#PLAYER_UPHILL_STRAIGHT = 3
#PLAYER_UPHILL_LEFT = 4
#PLAYER_UPHILL_RIGHT = 5
#ROAD_LENGTH_NONE = 0
#ROAD_LENGTH_SHORT = 25
#ROAD_LENGTH_MEDIUM = 50
#ROAD_LENGTH_LONG = 100
#ROAD_HILL_NONE = 0
#ROAD_HILL_LOW = 20
#ROAD_HILL_MEDIUM = 40
#ROAD_HILL_HIGH = 60
#ROAD_CURVE_NONE = 0
#ROAD_CURVE_EASY = 2
#ROAD_CURVE_MEDIUM = 4
#ROAD_CURVE_HARD = 6
#FRAMERATE = 75
#SCREENWIDTH = 800
#SCREENHEIGHT = 600
Structure Point
x.f
y.f
angle.f
colour.l
EndStructure
Structure tAppTime
AppTime_UPS.f
AppTime_Iterator.f
AppTime_CurrentTime.f
AppTime_PauseStart.b
AppTime_Speed.f
AppTime_DesiredLoopTime.f
AppTime_LastUpdateTime.f
AppTime_LastUPSTime.f
AppTime_DesiredFrequency.b
EndStructure
Declare TAppTime_Initialise(*appTime.tAppTime,frameRate.b=75)
Declare TAppTime_Update(*appTime.tAppTime)
Structure tGraphic
id.l
index.l
orgXSize.l
orgYSize.l
currentXSize.l
currentYSize.l
xOffset.f
yOffset.f
zOffset.f
speed.f
percent.f
moveSpeed.f
EndStructure
Structure tScreen
scale.f
x.f
y.f
w.f
EndStructure
Structure tCamera
x.f
y.f
z.f
EndStructure
Structure tWorld
x.f
y.f
z.f
EndStructure
Structure tColour
rumble.l
road.l
grass.l
lane.l
EndStructure
Structure tPart
world.tWorld
camera.tCamera
screen.tScreen
rumbleWidth.f
EndStructure
Structure tSegment
index.l
colour.tColour
p1.tPart
p2.tPart
curve.f
rumbleWidth.f
clip.f
List staticGraphics.tGraphic()
List moveGraphics.tGraphic()
EndStructure
Declare RenderFrame()
Declare render()
Declare resetRoad()
Declare addSegment(curve.f,y.f)
Declare segment(width.l,lanes.l,fog.f,*segment.tSegment)
Declare update(dt.f)
Declare.f increase(start.f,increment.f,maxV.f)
Declare.f accelerate(v.f,accel.f,dt.f)
Declare Min(a,b)
Declare Max(a,b)
Declare.f limit(value.f,minV.f,maxV.f)
Declare addRoad(enter.l,hold.l,leave.l,curve.f,y.f)
Declare addStraight(num.l=25)
Declare addCurve(num.l,curve.f)
Declare addHill(num.l, height.l)
Declare easeIn(a.f,b.f,percent.f)
Declare easeOut(a.f,b.f,percent.f)
Declare easeInOut(a.f,b.f,percent.f)
Declare.f percentRemaining(n.l,total.l)
Declare.f interpolate(a.f,b.f,percent.f)
Declare addStaticGraphic(roadIndex.l,xSize.l,ySize.l,xOffset.f,yOffset.f=0.0)
Declare renderStaticGraphic(screenWidth.l,screenHeight.l,xSize.l,ySize.l,resolution.l,roadWidth.l,scale.f,destX.f,destY.f,offsetX.f,offsetY.f,clipY.f)
Global Dim segments.tSegment(0)
Global trackLength.l
Global segmentLength.l = 200
Global rumbleLength.l = 3
Global width.l = #SCREENWIDTH
Global height.l = #SCREENHEIGHT
Global position.f = 0.0
Global drawDistance.l = 300
Global cameraDepth.f = 0.0
Global cameraHeight.f = 1500.0
Global roadWidth.f = 2000.0
Global playerX.f = 0.0
Global fieldOfView.f = 45.0
Global speed.f = 0.0
Global playerX.f = 0.0
Global lanes.l = 3
Global playerZ.f = 0.0
Global DARK_ROAD.tColour
Global LIGHT_ROAD.tColour
Global Terrain.l
Global _screen.l
Global SPRITES_SCALE.F
Define appTime.TAppTime
Define now.f,last.f,gdt.f,stp.f
; InitSprite()
; InitKeyboard()
DARK_ROAD\road=6908265
DARK_ROAD\grass=39424
DARK_ROAD\rumble=12303291
DARK_ROAD\lane=0
LIGHT_ROAD\road=7039851
LIGHT_ROAD\grass=1092112
LIGHT_ROAD\rumble=5592405
LIGHT_ROAD\lane=13421772
cameraDepth=1.0 / Tan(Radian(fieldOfView/2.0))
playerZ=cameraHeight * cameraDepth
now=0.0
last=0.0
gdt=0.0
stp=0.025
Procedure RenderFrame()
If StartDrawing(SpriteOutput(_screen))
Box(0,0,width-1,height-1,0)
render()
StopDrawing()
DisplaySprite(_screen,0,0)
FlipBuffers()
update(TAppTime_Update(appTime))
EndIf
EndProcedure
Procedure resetRoad()
Dim segments.tSegment(0)
addStraight(50)
addCurve(25,#ROAD_CURVE_MEDIUM)
addHill(25,#ROAD_HILL_HIGH)
addCurve(25,#ROAD_CURVE_EASY)
addStraight(50)
addHill(25,-#ROAD_HILL_LOW)
addHill(25,#ROAD_HILL_MEDIUM)
; addHill(25,-#ROAD_HILL_HIGH)
; addCurve(25,-#ROAD_CURVE_MEDIUM)
; addCurve(25,#ROAD_CURVE_HARD)
; addHill(25,#ROAD_HILL_HIGH)
; addHill(25,#ROAD_HILL_HIGH)
; addHill(25,-#ROAD_HILL_HIGH)
; addStraight(10)
; addHill(25,-#ROAD_HILL_HIGH)
; addStraight(100)
; addCurve(25,#ROAD_CURVE_HARD)
; addCurve(25,#ROAD_CURVE_HARD)
; addCurve(25,#ROAD_CURVE_HARD)
; addHill(100,-#ROAD_HILL_HIGH)
; addHill(200,#ROAD_HILL_HIGH)
; addHill(10,#ROAD_HILL_HIGH)
; addHill(15,#ROAD_HILL_HIGH)
; addHill(30,-#ROAD_HILL_HIGH)
; addStraight(100)
addStaticGraphic(20,32,32,-1.0,0.0)
addStaticGraphic(40,32,32,-1.0,0.0)
addStaticGraphic(60,32,32,-1.0,0.0)
addStaticGraphic(80,32,32,-1.0,0.0)
addStaticGraphic(100,32,32,-1.0,0.0)
addStaticGraphic(120,32,32,-1.0,0.0)
addStaticGraphic(140,32,32,-1.0,0.0)
addStaticGraphic(160,32,32,-1.0,0.0)
addStaticGraphic(180,32,32,-1.0,0.0)
addStaticGraphic(200,32,32,-1.0,0.0)
addStaticGraphic(220,32,32,-1.0,0.0)
addStaticGraphic(240,32,32,-1.0,0.0)
addStaticGraphic(260,32,32,-1.0,0.0)
addStaticGraphic(300,32,32,-1.0,0.0)
addStaticGraphic(400,32,32,-1.0,0.0)
addStaticGraphic(500,32,32,-1.0,0.0)
trackLength=ArraySize(segments())*segmentLength
EndProcedure
Procedure addStaticGraphic(roadIndex.l,xSize.l,ySize.l,xOffset.f,yOffset.f=0.0)
Define *graphic.tGraphic
*graphic=AddElement(segments(roadIndex)\staticGraphics())
If *graphic<>#NUL
*graphic\id=-1
*graphic\orgXSize=xSize
*graphic\orgYSize=ySize
*graphic\currentXSize=*graphic\orgXSize
*graphic\currentYSize=*graphic\orgYSize
*graphic\xOffset=xOffset
*graphic\yOffset=yOffset
*graphic\zOffset=roadIndex*segmentLength
*graphic\percent=0.0
*graphic\index=0
*graphic\moveSpeed=0.0
Debug "Added"
EndIf
EndProcedure
Procedure.f lastY()
Define index.l
index=ArraySize(segments())
If index<=0
ProcedureReturn 0.0
Else
ProcedureReturn segments(index-1)\p2\world\y
EndIf
EndProcedure
Procedure addSegment(curve.f,y.f)
Define n.l
Define prevY.f
prevY=lastY()
n=ArraySize(segments())
ReDim segments.tSegment(n+1)
Debug "Array count : "+n
NewList segments(n)\staticGraphics()
NewList segments(n)\moveGraphics()
segments(n)\curve=curve
segments(n)\index=n
segments(n)\p1\world\x=0.0
segments(n)\p1\world\y=prevY
segments(n)\p1\world\z=n*segmentLength
segments(n)\p1\camera\x=0.0
segments(n)\p1\camera\y=0.0
segments(n)\p1\camera\z=0.0
segments(n)\p2\world\x=0.0
segments(n)\p2\world\y=y
segments(n)\p2\world\z=(n+1)*segmentLength
segments(n)\p2\camera\x=0.0
segments(n)\p2\camera\y=0.0
segments(n)\p2\camera\z=0.0
segments(n)\rumbleWidth=2
If n % 2
segments(n)\colour=LIGHT_ROAD
Else
segments(n)\colour=DARK_ROAD
EndIf
segments(n)\colour\lane=RGB(255,255,255)
EndProcedure
Procedure findSegment(z.f,*store.tSegment)
Define index.l
; Debug "Z:"+StrF(z,4)+" "+Str(segmentLength)+" "+StrF(z/segmentLength,4)
index=Round(z/segmentLength,#PB_Round_Down)
CopyStructure(segments(index),*store,tSegment)
EndProcedure
Procedure project(*p.tPart,cameraX.f,cameraY.f,cameraZ.f,cameraDepth.f,width.l,height.l,roadWidth.l,rumbleWidth.l)
*p\camera\x=*p\world\x-cameraX
*p\camera\y=*p\world\y-cameraY
*p\camera\z=*p\world\z-cameraZ
If *p\camera\z<>0.0
*p\screen\scale=cameraDepth/*p\camera\z
Else
*p\screen\scale=0.0
EndIf
*p\screen\x=(width/2.0)+((*p\screen\scale* *p\camera\x*width)/2.0)
*p\screen\y=(height/2.0)-((*p\screen\scale* *p\camera\y*height)/2.0)
*p\screen\w=*p\screen\scale*roadWidth*width/2.0
*p\rumbleWidth=*p\screen\w/rumbleWidth
; Debug "Screen X : "+*p\screen\x
; Debug "Screen Y : "+*p\screen\y
EndProcedure
Procedure render()
Define baseSegment.tSegment
Define playerSegment.tSegment
Define basePercent.f
Define maxy.l,n.l,which.l,index.l
Define segment.tSegment
Define x.f,dx.f
Define playerY.f
Define playerPercent.f
Define graphicScale.f,graphicX.f,graphicY.f
maxy=height
findSegment(Int(position),@baseSegment)
basePercent=percentRemaining(position, segmentLength)
findSegment(position+playerZ,@playerSegment)
playerPercent=percentRemaining(position+playerZ,segmentLength)
playerY=interpolate(playerSegment\p1\world\y,playerSegment\p2\world\y,playerPercent)
; Debug "Player Y : "
; Debug playerY
; Debug "Player % : "
;Debug playerPercent
;Debug "P2 : "
;Debug playerSegment\p1\world\y
;Debug playerSegment\p2\world\y
;Debug playerSegment\index
Debug "P1 Y : "+StrF(playerSegment\p1\world\y,4)+" P2 Y : "+StrF(playerSegment\p2\world\y,4)
Debug "Player Percent : "+StrF(playerPercent,4)+" Y : "+StrF(playerY,4)+" Index : "+Str(playerSegment\index)
x=0.0
dx=-baseSegment\curve*basePercent
For n=0 To drawDistance-1
index=(baseSegment\index+n) % ArraySize(segments())
segment=segments(index)
;Debug "Current index : "+*segment\index
segment\clip=maxy
project(@segment\p1,(playerX*roadWidth)-x,playerY+cameraHeight,position,cameraDepth,width,height,roadWidth,segment\rumbleWidth)
project(@segment\p2,(playerX*roadWidth)-x-dx,playerY+cameraHeight,position,cameraDepth,width,height,roadWidth,segment\rumbleWidth)
segments(index)\clip=maxy
segments(index)\p1\screen=segment\p1\screen
x+dx
dx+segment\curve
If segment\p1\camera\z<=cameraDepth Or segment\p2\screen\y>=segment\p1\screen\y Or segment\p2\screen\y>=maxy
Continue
Else
segment(width,lanes,1.0,segment)
EndIf
maxy=segment\p1\screen\y
Next
; Display objects
; Static (roadside) ones
For n=drawDistance-1 To 0 Step -1
segment=segments((baseSegment\index+n) % ArraySize(segments()))
ForEach segment\staticGraphics()
graphicScale=segment\p1\screen\scale
graphicX=segment\p1\screen\x+(graphicScale * segment\staticGraphics()\xOffset * roadWidth * width / 2.0)
graphicY=segment\p1\screen\y+(graphicScale * segment\staticGraphics()\yOffset * roadWidth / 2.0)
renderStaticGraphic(width,height,segment\staticGraphics()\orgXSize,segment\staticGraphics()\orgYSize,1,roadWidth,graphicScale,graphicX,graphicY,-0.5,-1.0,segment\clip)
Next
Next
EndProcedure
Procedure renderStaticGraphic(screenWidth.l,screenHeight.l,xSize.l,ySize.l,resolution.l,roadWidth.l,scale.f,destX.f,destY.f,offsetX.f,offsetY.f,clipY.f)
Define destW.f,destH.f,clipH.f
destW=(xSize*scale*screenWidth/2.0)*(SPRITES_SCALE*roadWidth)
destH=(ySize*scale*screenWidth/2.0)*(SPRITES_SCALE*roadWidth)
destX+(destW*offsetX)
destY+(destH*offsetY)
If clipY<>0.0
clipH=Max(0,destY-destH-clipY)
Else
clipH=0.0
EndIf
destH-clipH
If destW>0.0 And destH>0
Box(destX,destY,destW,destH,RGB(0,255,0))
EndIf
EndProcedure
Procedure.f rumbleWidth(projectedRoadWidth.l,lanes.l)
ProcedureReturn projectedRoadWidth/MAX(6,2*lanes)
EndProcedure
Procedure.f laneMarkerWidth(projectedRoadWidth.l,lanes.l)
ProcedureReturn projectedRoadWidth/MAX(32,8*lanes)
EndProcedure
Procedure.f percentRemaining(n.l,total.l)
ProcedureReturn Mod(n,total)/total
EndProcedure
Procedure.f interpolate(a.f,b.f,percent.f)
ProcedureReturn a+(b-a)*percent
EndProcedure
Procedure addRoad(enter.l,hold.l,leave.l,curve.f,y.f)
Define n.l
Define startY.f,endY.f
Define total.l
startY=lastY()
endY=startY+(Int(y)*segmentLength)
total=enter+hold+leave
For n=0 To enter-1
addSegment(easeIn(0,curve,n/enter),easeInOut(startY,endY,n/total))
Next
For n=0 To hold-1
addSegment(curve,easeInOut(startY,endY,(enter+n)/total))
Next
For n=0 To leave-1
addSegment(easeInOut(curve,0,n/leave),easeInOut(startY,endY,(enter+hold+n)/total))
Next
EndProcedure
Procedure easeIn(a.f,b.f,percent.f)
ProcedureReturn a+(b-a)*Pow(percent,2)
EndProcedure
Procedure easeOut(a.f,b.f,percent.f)
ProcedureReturn a+(b-a)*(1.0-Pow(1.0-percent,2))
EndProcedure
Procedure easeInOut(a.f,b.f,percent.f)
ProcedureReturn a+(b-a)*((-Cos(percent*#PI)/2)+0.5)
EndProcedure
Procedure addStraight(num.l=25)
addRoad(num,num,num,0.0,0.0)
EndProcedure
Procedure addCurve(num.l,curve.f)
addRoad(num,num,num,curve,0.0)
EndProcedure
Procedure addHill(num.l, height.l)
addRoad(num,num,num,0,height)
EndProcedure
Procedure polygon(x1.f,y1.f,x2.f,y2.f,x3.f,y3.f,x4.f,y4.f,colour.l,type.l)
; If type = 1
;TransformSprite(Terrain, x4, y4, 1, x3, y3, 1, x2, y2, 1, x1, y1, 1)
; ElseIf type = 2
; TransformSprite(Terrain, x3, y3, 1, x4, y4, 1, x1, y1, 1, x2, y2, 1)
; EndIf
;DisplayTransparentSprite(Terrain, 0, 0, 255, colour)
LineXY(x1,y1,x2,y2,colour)
LineXY(x2,y2,x3,y3,colour)
LineXY(x3,y3,x4,y4,colour)
LineXY(x4,y4,x1,y2,colour)
; DisplaySprite(Terrain,x1,y1)
EndProcedure
Procedure segment(width.l,lanes.l,fog.f,*segment.tSegment)
polygon(*segment\p1\screen\x-*segment\p1\screen\w-*segment\p1\rumbleWidth, *segment\p1\screen\y, *segment\p1\screen\x-*segment\p1\screen\w, *segment\p1\screen\y, *segment\p2\screen\x-*segment\p2\screen\w, *segment\p2\screen\y, *segment\p2\screen\x-*segment\p2\screen\w-*segment\p2\rumbleWidth, *segment\p2\screen\y, *segment\colour\rumble, 1)
polygon(*segment\p1\screen\x+*segment\p1\screen\w+*segment\p1\rumbleWidth, *segment\p1\screen\y, *segment\p1\screen\x+*segment\p1\screen\w, *segment\p1\screen\y, *segment\p2\screen\x+*segment\p2\screen\w, *segment\p2\screen\y, *segment\p2\screen\x+*segment\p2\screen\w+*segment\p2\rumbleWidth, *segment\p2\screen\y, *segment\colour\rumble, 2)
polygon(*segment\p1\screen\x-*segment\p1\screen\w, *segment\p1\screen\y, *segment\p1\screen\x+*segment\p1\screen\w, *segment\p1\screen\y, *segment\p2\screen\x+*segment\p2\screen\w, *segment\p2\screen\y, *segment\p2\screen\x-*segment\p2\screen\w, *segment\p2\screen\y, *segment\colour\road, 1)
; Box(0,*segment\p2\screen\y,width,*segment\p1\screen\y-*segment\p2\screen\y,*segment\colour\grass)
EndProcedure
Procedure update(dt.f)
position=increase(position,speed,trackLength)
speed+accelerate(speed,45.0,dt)
speed=limit(speed,0.0,100.0)
EndProcedure
Procedure Min(a,b)
If a<b
ProcedureReturn a
Else
ProcedureReturn b
EndIf
EndProcedure
Procedure Max(a,b)
If a>b
ProcedureReturn a
Else
ProcedureReturn b
EndIf
EndProcedure
Procedure.f increase(start.f,increment.f,maxV.f)
Define result.f
result=start+increment
While result>=maxV
result-maxV
Wend
While result<0
result+maxV
Wend
ProcedureReturn result
EndProcedure
Procedure.f accelerate(v.f,accel.f,dt.f)
ProcedureReturn v+(accel*dt)
EndProcedure
Procedure.f limit(value.f,minV.f,maxV.f)
ProcedureReturn Max(minV,Min(value,maxV))
EndProcedure
Procedure TAppTime_Initialise(*appTime.tAppTime,frameRate.b=75)
Debug "Initialising frame rate to "+frameRate
*appTime\AppTime_UPS=0.0
*appTime\AppTime_Iterator=0.0
*appTime\AppTime_CurrentTime=0.0
*appTime\AppTime_PauseStart=0.0
*appTime\AppTime_Speed=1.0
*appTime\AppTime_LastUpdateTime=0.0
*appTime\AppTime_LastUPSTime=0.0
*appTime\AppTime_DesiredFrequency=frameRate
*appTime\AppTime_DesiredLoopTime=1000.0/(*appTime\AppTime_DesiredFrequency*1.0)
EndProcedure
Procedure TAppTime_Update(*appTime.tAppTime)
Define elapsed.f
Define time.f
If *appTime\AppTime_PauseStart=#True
ProcedureReturn 0.0
EndIf
time=ElapsedMilliseconds()
If *appTime\AppTime_LastUpdateTime=0.0
*appTime\AppTime_Speed=1.0
*appTime\AppTime_LastUPSTime=time
Else
elapsed=time-*appTime\AppTime_LastUpdateTime
If elapsed=0.0
elapsed=1.0
; Delay(1)
time=time+1.0
EndIf
*appTime\AppTime_Speed=elapsed/*appTime\AppTime_DesiredLoopTime
EndIf
*appTime\AppTime_LastUpdateTime=time
*appTime\AppTime_CurrentTime=time
*appTime\AppTime_Iterator=*appTime\AppTime_Iterator+1.0
If *appTime\AppTime_CurrentTime-*appTime\AppTime_LastUPSTime>=1000.0
*appTime\AppTime_UPS=*appTime\AppTime_Iterator/((*appTime\AppTime_CurrentTime-*appTime\AppTime_LastUPSTime)/1000)
*appTime\AppTime_LastUPSTime=*appTime\AppTime_CurrentTime
*appTime\AppTime_Iterator=0
EndIf
ProcedureReturn *appTime\AppTime_Speed
EndProcedure
Debug "Initialising"
TAppTime_Initialise(appTime,#FRAMERATE)
SPRITES_SCALE = 0.3 * (1/80)
SetFrameRate(#FRAMERATE)
If OpenScreen(width,height,32,"Pseudo 3D Test 1")
Terrain = CreateSprite(#PB_Any, 16, 16)
StartDrawing(SpriteOutput(Terrain))
Box(0, 0, 15, 15,RGB(255,0,0))
StopDrawing()
_screen=CreateSprite(#PB_Any,width,height)
Debug "Setting up"
resetRoad()
Debug "Finished setting up"
Debug "Binding"
BindEvent(#PB_Event_RenderFrame,@RenderFrame())
FlipBuffers()
Debug "Flipping 1"
Debug "Flipping 2"
Else
Debug "OpenScreen failed"
EndIf
Adjust the drawDistance variable value to change how far everything is drawn, and thus the overall speed - 300 is rather too much at the moment - it should be around 100