[bmx] Rogue-like 3D perspective effect

Started by Flanker, November 17, 2017, 11:38:28

Previous topic - Next topic

Flanker

Here is a little experiment of a perspective effect based on a 3D level data, using 2D (ASCII in the example but can be replaced with tiles). Move with keyboard arrows, zoom with mousewheel (be aware that it can be very slow when zoomed out). Idea from here : http://www.squidi.net/three/entry.php?id=251



'------------------------------------------------------------------------------------------------------------------------------------------------------------ GRAPHICS VAR
Const gfxWidth:Int = 800
Const gfxHeight:Int = 600
Const gfxDepth:Int = 0
Const gfxHertz:Int = 60

'------------------------------------------------------------------------------------------------------------------------------------------------------------ GRAPHICS INIT
Graphics gfxWidth,gfxHeight,gfxDepth,gfxHertz

'------------------------------------------------------------------------------------------------------------------------------------------------------------ LEVEL DATA
Global levelWidth:Int = 129
Global levelHeight:Int = 129
Global levelDepth:Int = 16

Global levelData:Int[levelWidth,levelHeight,levelDepth]

' >>>>>>>>>> FILL THE LEVEL WITH A GRID PATTERN
For Local x:Int = 0 To levelWidth-1
For Local y:Int = 0 To levelHeight-1

If (x/16.0) = Int(x/16.0) Or (y/16.0) = Int(y/16.0)
For Local z:Int = 0 To levelDepth-1
levelData[x,y,z] = 35 ' >>>>>>>>>> WALLS
Next
Else
levelData[x,y,0] = 46 ' >>>>>>>>>> FLOOR
EndIf

Next
Next

'------------------------------------------------------------------------------------------------------------------------------------------------------------ "CAMERA" VAR
Global cameraX:Float = 0
Global cameraY:Float = 0
Global cameraZ:Float = 20 ' >>>>>>>>>> ZOOM LEVEL
Global cameraP:Float = 20 ' >>>>>>>>>> PERSPECTIVE STRENGHT

'------------------------------------------------------------------------------------------------------------------------------------------------------------ FPS VAR
Global fps:Int = gfxHertz
Global fpsTime:Int = MilliSecs()
Global fpsCount:Int

'############################################################################################################################################################ MAIN LOOP
While Not KeyHit(KEY_ESCAPE) And Not AppTerminate()

Cls

If KeyDown(KEY_LEFT) Then cameraX = cameraX - 0.5
If KeyDown(KEY_RIGHT) Then cameraX = cameraX + 0.5
If KeyDown(KEY_UP) Then cameraY = cameraY - 0.5
If KeyDown(KEY_DOWN) Then cameraY = cameraY + 0.5

cameraZ = cameraZ + MouseZSpeed()
If cameraZ < 1 Then cameraZ = 1

DrawLevel()

SetColor 255,255,255
SetScale 5,5
DrawText GetFPS(),10,10

Flip

Wend

End

'//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FUNCTIONS
'------------------------------------------------------------------------------------------------------------------------------------------------------------ LEVEL DATA
Function GetFPS()

If MilliSecs()-fpsTime > 1000
fps = fpsCount
fpsCount = 0
fpsTime = MilliSecs()
Else
fpsCount = fpsCount + 1
EndIf

Return fps

End Function

'------------------------------------------------------------------------------------------------------------------------------------------------------------
Function DrawLevel()

' >>>>>>>>>> RESTRICTING DRAWING TO VISIBLE LEVEL DATA
Local minX:Int = cameraX - GraphicsWidth()/2/cameraZ
Local maxX:Int = cameraX + GraphicsWidth()/2/cameraZ
Local minY:Int = cameraY - GraphicsHeight()/2/cameraZ
Local maxY:Int = cameraY + GraphicsHeight()/2/cameraZ

If minX < 0 Then minX = 0
If maxX > levelWidth-1 Then maxX = levelWidth-1
If minY < 0 Then minY = 0
If maxY > levelHeight-1 Then maxY = levelHeight-1

' >>>>>>>>>> DRAWING FROM BOTTOM TO TOP
For Local z:Float = 0 To levelDepth-1

SetScale cameraZ/10*(1+z/20),cameraZ/10*(1+z/20) ' >>>>>>>>>> SCALE FUNCTION OF HEIGHT

Local color:Int = 255-224+z*14' >>>>>>>>>> COLOR FUNCTION OF HEIGHT
SetColor color,color,color

For Local y:Float = minY To maxY
For Local x:Float = minX To maxX

If levelData[x,y,z] > 0 Then DrawText Chr(levelData[x,y,z]),ProjectX(x,z),ProjectY(y,z)

Next
Next
Next

End Function

'------------------------------------------------------------------------------------------------------------------------------------------------------------
Function ProjectX:Float(x:Float,z:Float) ' >>>>>>>>>> LEVEL X-Z PROJECTION ON SCREEN
Return GraphicsWidth()/2+(x-cameraX)*cameraZ*(1+z/cameraP)
End Function

'------------------------------------------------------------------------------------------------------------------------------------------------------------
Function ProjectY:Float(y:Float,z:Float)' >>>>>>>>>> LEVEL Y-Z PROJECTION ON SCREEN
Return GraphicsHeight()/2+(y-cameraY)*cameraZ*(1+z/cameraP)
End Function
Everyone knew it was impossible, until someone who didn't know made it.

GW

Nice!
Did you try rendering the whole map to texture once and then drawing that image 'leveldepth' times?
I think you should get a speedup, but possibly some small scaling artifacts.

Flanker

You're right, this should work and it's probably faster to render. Maybe I'll try that later.
Everyone knew it was impossible, until someone who didn't know made it.