Simple Raycaster

Übersicht BlitzMax, BlitzMax NG Codearchiv & Module

Neue Antwort erstellen

Trust

Betreff: Simple Raycaster

BeitragDo, Okt 17, 2019 21:00
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo Community,

ich bin über meinen alten Code eines Raycasters gestoßen und dachte mir, ich teile ihn mal mit euch.

user posted image

Das war damals nur ein Testprojekt, da ich verstehen wollte wie das Raycasten funktioniert.

Hier ein Post von damals: https://www.blitzforum.de/foru...=raycaster Laughing

Was geht alles?

Arrow verschiedene Wandtexturen
Arrow Boden und Decke
Arrow Kollisionen
Arrow Schattierte Seiten

Der Code ist alles am Stück geschrieben, keine Klassen/Funktionen etc. da es nur ein Test war.

BlitzMax: [AUSKLAPPEN]
SuperStrict

Const SCREEN_W:Int = 1200
Const SCREEN_H:Int = 720
Const TEX_W:Int = 128
Const TEX_H:Int = 128
Const MAP_W:Int = 24
Const MAP_H:Int = 24
Const FOV:Float = Pi*0.4


' Load wall textures
Local texWall1:TPixmap = LoadPixmap("assets/wall_1.png")
Local texWall2:TPixmap = LoadPixmap("assets/wall_2.png")
Local texWall3:TPixmap = LoadPixmap("assets/wall_3.png")
Local texWall4:TPixmap = LoadPixmap("assets/wall_4.png")

' Load ground and ceiling texture
Local texFloor1:TPixmap = LoadPixmap("assets/floor_1.png")
Local texCeiling1:TPixmap = LoadPixmap("assets/ceiling_1.png")

' Load player weapon
Local player:TImage = LoadImage("assets/weapon.png")

' The buffer that all calculated pixels go into
Local drawBuffer:TPixmap = CreatePixmap(SCREEN_W, SCREEN_H, PF_RGBA8888)


' x and y start position
Local posX:Float = 32
Local posY:Float = 12
' Initial direction vector
Local dirX:Float = -1
Local dirY:Float = 0
' 2d raycaster version of camera plane
Local planeX:Float = 0
Local planeY:Float = 0.66

' Time of current frame
Local time:Float = 0
' Time of previous frame
Local oldTime:Float = 0
Local timer:TTimer = CreateTimer(60)



' Worldmap (everything above 0 is considered to be a wall)
Local worldMap:Int[] = [ ..
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, ..
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,0,0,0,0,0,2,2,2,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1, ..
1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,3,0,0,0,3,0,0,0,1, ..
1,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,0,0,0,0,0,2,2,0,2,2,0,0,0,0,3,0,3,0,3,0,0,0,1, ..
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,4,0,0,0,0,5,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,4,0,4,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,4,0,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, ..
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]



Graphics(SCREEN_W, SCREEN_H)

' Hide the mouse pointer and move it to the middle of the screen
HideMouse()
MoveMouse(SCREEN_W / 2, SCREEN_H / 2)

' Main loop
While Not (AppTerminate() Or KeyDown(KEY_ESCAPE))

Local mouseSpeedX:Int = MouseXSpeed()

' Lock the mouse to the middle of the screen
MoveMouse(SCREEN_W / 2, SCREEN_H / 2)
' Flush the buffer
MouseXSpeed()

For Local x:Int = 0 To SCREEN_W-1

' Calculate ray position and direction
Local camX:Float = 2 * x / Float(SCREEN_W) -1
Local rayDirX:Float = dirX + planeX * camX
Local rayDirY:Float = dirY + planeY * camX

' Which box of the map we are in
Local mapX:Int = Int(posX)
Local mapY:Int = Int(posY)

' Lenght of ray from current position to next x or y-side
Local sideDistX:Float
Local sideDistY:Float

' Length of ray frlom one x or y-side to next x or y-side
Local deltaDistX:Float = Abs(1 / rayDirX)
Local deltaDistY:Float = Abs(1 / rayDirY)
Local perpWallDist:Float

' What direction to step in x or y-direction (either +1 or -1)
Local stepX:Int
Local stepY:Int

' Was there a wall hit?
Local hit:Int
' Was a NS or a EW wall hit?
Local side:Int

' Calculate step and initial sideDist
If (rayDirX < 0)
stepX = -1
sideDistX = (posX - mapX) * deltaDistX
Else
stepX = 1
sideDistX = (mapX + 1.0 - posX) * deltaDistX
EndIf

If (rayDirY < 0)
stepY = -1
sideDistY = (posY - mapY) * deltaDistY
Else
stepY = 1
sideDistY = (mapY + 1.0 - posY) * deltaDistY
EndIf

' Perform DDA
While (hit = 0)
' Jump to next map square, or in x- or in y-direction
If (sideDistX < sideDistY)
sideDistX :+ deltaDistX
mapX :+ stepX
side = 0
Else
sideDistY :+ deltaDistY
mapY :+ stepY
side = 1
EndIf

' Check if ray has hit a wall
If(worldMap[mapY * MAP_W + mapX] > 0) hit = 1
Wend

' Calculate distance projected on camera direction
If (side = 0)
perpWallDist = (mapX - posX + (1 - stepX) / 2) / rayDirX
Else
perpWallDist = (mapY - posY + (1 - stepY) / 2) / rayDirY
EndIf

' Calculate height of line to draw on sceen
Local lineH:Int = Int(SCREEN_W / perpWallDist)

' Calculate lowest and highest pixel to fill in current stripe
Local drawStart:Int = -lineH / 2 + SCREEN_H / 2
If(drawStart < 0) drawStart = 0
Local drawEnd:Int = lineH / 2 + SCREEN_H / 2
If(drawEnd >= SCREEN_H) drawEnd = SCREEN_H - 1

' Calculate value of wallX
Local wallX:Float
If (side = 0)
wallX = posY + perpWallDist * rayDirY
Else
wallX = posX + perpWallDist * rayDirX
EndIf
wallX :- Floor(wallX)

' X coordinate on the texture
Local texX:Int = Int(wallX * Float(TEX_W))
If(side = 0 And rayDirX > 0) texX = TEX_W - texX - 1
If(side = 1 And rayDirY < 0) texX = TEX_W - texX - 1

For Local y:Int = drawStart To drawEnd-1
Local d:Int = y * 256 - SCREEN_H * 128 + lineH * 128
Local texY:Int = ((d * TEX_H) / lineH) / 256

If texY < 0 Then texY = 0
If texY > TEX_H Then texY = TEX_H

Local argb:Int

Select (worldMap[mapY * MAP_W + mapX])
Case 1
argb = ReadPixel(texWall1, texX, texY)
Case 2
argb = ReadPixel(texWall2, texX, texY)
Case 3
argb = ReadPixel(texWall3, texX, texY)
Case 4
argb = ReadPixel(texWall4, texX, texY)
Default
argb = ReadPixel(texWall1, texX, texY)
End Select
' Make color darker for y-sides: R, G and B byte each divided through two with a "shift" and an "and"
If (side = 1) argb = (argb Shr 1) & 8355711
WritePixel(drawBuffer, x, y, argb)
Next


' FLOOR CASTING

' x, y position of the floor texel at the bottom of the wall
Local floorXWall:Float
Local floorYWall:Float

' 4 different wall directions possible
If (side = 0 And rayDirX > 0)
floorXWall = mapX
floorYWall = mapY + wallX
ElseIf (side = 0 And rayDirX < 0)
floorXWall = mapX + 1.0
floorYWall = mapY + wallX
ElseIf (side = 1 And rayDirY > 0)
floorXWall = mapX + wallX
floorYWall = mapY
Else
floorXWall = mapX + wallX
floorYWall = mapY + 1.0
EndIf

Local distWall:Float
Local distPlayer:Float
Local currentDist:Float

distWall = perpWallDist
distPlayer = 0.0

If (drawEnd < 0) Then drawEnd = SCREEN_H ' #######################################



' Write the floor/ceiling from drawEnd to the bottom of the screen
For Local y:Int = drawEnd+1 To SCREEN_H-1
currentDist = SCREEN_H / (2.0 * y - SCREEN_H)
Local weight:Float = (currentDist - distPlayer) / (distWall - distPlayer)

Local currentFloorX:Float = weight * floorXWall + (1.0 - weight) * posX
Local currentFloorY:Float = weight * floorYWall + (1.0 - weight) * posY

Local texX:Int
Local texY:Int
texX = Int(currentFloorX * TEX_W) Mod TEX_W
texY = Int(currentFloorY * TEX_H) Mod TEX_H

' Write imageBuffer
' Floor
Local argb:Int = ReadPixel(texFloor1, texX , texY)
WritePixel(drawBuffer, x, y, argb)

' Ceiling (mirror of floor)
argb:Int = ReadPixel(texCeiling1, texX, texY)
WritePixel(drawBuffer, x, SCREEN_H-y, argb)
Next

Next

DrawPixmap(drawBuffer, 0, 0)
DrawImage(player, SCREEN_W / 2 - ImageWidth(player) / 2, SCREEN_H - ImageHeight(player)+2)









' Update timing
oldTime = time
time = TimerTicks(timer)
Local frameTime:Float = (time - oldTime) / 1000.0
DrawText(1.0 / frameTime, 5, 5)

' Speed modifiers
Local moveSpeed:Float = frameTime * 50.0
Local rotSpeed:Float = frameTime * 30.0
Local viewSpeed:Float = frameTime * mouseSpeedX * 150.0



' Controls

' Forward
If (KeyDown(KEY_W))
' Move the player
posX :+ dirX * moveSpeed
posY :+ dirY * moveSpeed

' If he lands in a wall -> undo movement
If(worldMap[Int(posY) * MAP_W + Int(posX + dirX * moveSpeed)] <> 0) Or (worldMap[Int(posY + dirY * moveSpeed) * MAP_W + Int(posX)] <> 0)
posX :- dirX * moveSpeed
posY :- dirY * moveSpeed
EndIf

EndIf

' Backward
If (KeyDown(KEY_S))
' Move the player
posX :- dirX * moveSpeed
posY :- dirY * moveSpeed

' If he lands in a wall -> undo movement
If(worldMap[Int(posY) * MAP_W + Int(posX - dirX * moveSpeed)] <> 0) Or (worldMap[Int(posY - dirY * moveSpeed) * MAP_W + Int(posX)] <> 0)
posX :+ dirX * moveSpeed
posY :+ dirY * moveSpeed
EndIf
EndIf

' Left
If (KeyDown(KEY_A))
' Move the player
posX :+ -dirY * moveSpeed
posY :+ dirX * moveSpeed

' If he lands in a wall -> undo movement
If(worldMap[Int(posY) * MAP_W + Int(posX + -dirY * moveSpeed)] <> 0) Or (worldMap[Int(posY + dirX * moveSpeed) * MAP_W + Int(posX)] <> 0)
posX :- -dirY * moveSpeed
posY :- dirX * moveSpeed
EndIf
EndIf

' Right
If (KeyDown(KEY_D))
' Move the player
posX :- -dirY * moveSpeed
posY :- dirX * moveSpeed

' If he lands in a wall -> undo movement
If(worldMap[Int(posY) * MAP_W + Int(posX + -dirY * moveSpeed)] <> 0) Or (worldMap[Int(posY + dirX * moveSpeed) * MAP_W + Int(posX)] <> 0)
posX :+ -dirY * moveSpeed
posY :+ dirX * moveSpeed
EndIf
EndIf

' Rotate based on mouse movement
If (mouseSpeedX <> 0)
' both camera direction and camera plane must be rotated
Local oldDirX:Float = dirX;
dirX = dirX * Cos(-viewSpeed) - dirY * Sin(-viewSpeed);
dirY = oldDirX * Sin(-viewSpeed) + dirY * Cos(-viewSpeed);
Local oldPlaneX:Float = planeX;
planeX = planeX * Cos(-viewSpeed) - planeY * Sin(-viewSpeed);
planeY = oldPlaneX * Sin(-viewSpeed) + planeY * Cos(-viewSpeed);
EndIf


Flip;Cls;Wend;End


Damit ihr es ausprobieren könnt, benötigt ihr die assets ebenso.
Erstellt euch einen Ordner mit dem Namen "assets" im Rootverzeichnis (wo sich die bmx-Datei befindet) und kopiert folgende Bilder hinein:

wall_1.png
user posted image

wall_2.png
user posted image

wall_3.png
user posted image

wall_4.png
user posted image

floor_1.png
user posted image

ceiling_1.png
user posted image

weapon.png
user posted image

Die Texturen sind nicht von mir. Verwendet sie daher nicht in Projekten die ihr veröffentlichen möchtet.

Die Berechnung für Boden/Decke ist leicht fehlerhaft. Wer es hinbekommt, darf die Lösung hier gerne posten.

PS: nur im Release-Modus kompilieren, beim Debug-Modus ruckelt es.

Gruß
Trust
Es gibt 10 Gruppen von Menschen: diejenigen, die das Binärsystem verstehen, und die anderen.

DAK

BeitragMi, Okt 23, 2019 11:37
Antworten mit Zitat
Benutzer-Profile anzeigen
Sehr coole Sache! Schaut hübsch aus!
Gewinner der 6. und der 68. BlitzCodeCompo

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Codearchiv & Module

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group