Wie der Titel schon sagt, stelle ich hier den Raycaster vor, der innerhalb der letzten Wochen entstanden ist.
 Wer nicht weiss, was Raycasting ist, der kann sich hier darüber informieren.
 Kurz gefasst ist es eine alternative 3D - Renderart, die in den ersten 3D - Spielen eingesetzt wurde (Wolfenstein 3D und eine fortgeschrittene Version in Doom).
 Meine Version ist nicht besonders komplex, beherrscht jedoch das Rendern von Boden, Decke, texturierten Wänden und Sprites, was eigentlich schon genug ist für eine sehr simple 3D - Simulation (beispielsweise der Labyrinth - Bildschirmschoner früherer Windowseditionen).
 
 Ich habe versucht, den Algorithmus so weit wie möglich zu optimieren, jedoch frisst das Berechnen der Sprites leider (zu) viel Rechenleistung, da mir unter anderem kein schneller Sichtbarkeitscheck engefallen ist.
 Man kann auch das WaitTimer rausnehmen und schauen, wie schnell es bei ihm läuft - auf meinem Intel Celeron läuft es (mit ausgeschaltetem Debugger!) zwischen 100 - 200 FPS, was noch genug Freiraum für anderweitige Berechnungen zulässt..
 Achtung: Das ist kein Aufruf, seine FPS hier zu posten!
 
 Da sich das Programm auf Metadaten verlässt, habe ich das ganze gezippt hier hochgeladen.
 Die Texturen und das Sprite sind auch bei den B3D - Samples beigelegt und sind schnell gefunden, sollte der Link tot sein.
 
 
 EDIT: Fehler wegen dem WritePixelFast fixed!
 
 BlitzBasic:  [AUSKLAPPEN]  [EINKLAPPEN] Const MAPSIZE = 16 Const GRIDSIZE = 64 Const GRIDSHIFTS = 6 Const GWIDTH = 320 Const GHEIGHT = 200 Const ANGLEOFVIEW = 60 Const CASTINGSTEP# = ANGLEOFVIEW/Float( GWIDTH ) Const TILES = 2 Const TEXTURESIZE = 64 Const SCALEFACTOR# = TEXTURESIZE/GRIDSIZE Const FLOORTILE = 2 Const CEILINGTILE = 2 Const HD = ( SCALEFACTOR# <> 1 ) Const CEILING = True
  Graphics GWIDTH, GHEIGHT, 32, 2
  Global CamX# = 5*GRIDSIZE + GRIDSIZE/2, CamY# = 5*GRIDSIZE + GRIDSIZE/2, ProjectionDistance# = ( GWIDTH/2 )/Tan( ANGLEOFVIEW/2 ) Global CameraAngle = 270, PlayerHeight = 32, PlaneDisplacement
  Dim MapData( MAPSIZE - 1, MAPSIZE - 1 ) Dim TextureData( TILES*( TEXTURESIZE)*( TEXTURESIZE ) ) Dim Distances#( GWIDTH - 1 )
  For i = 0 To Tiles - 1 	Texture = LoadImage( "Tile" + ( i + 1 ) + ".png" ) 	SetBuffer ImageBuffer( Texture ) 	LockBuffer ImageBuffer( Texture ) 	For x = 0 To TEXTURESIZE - 1 		For y = 0 To TEXTURESIZE - 1 			TextureData( i*TEXTURESIZE*TEXTURESIZE + ( x*TEXTURESIZE + y ) ) = ReadPixelFast( x, y ) 		Next 	Next 	 	FreeImage Texture Next
  Type Sprite 	Field X# 	Field Y# 	Field Height 	Field Width 	Field ImageData[ 256*256 ] End Type
  Dummy = LoadImage( "Alien.png" ) LockBuffer ImageBuffer( Dummy ) For i = 1 To 10 	Drink.Sprite = New Sprite 		Drink\X# = Rnd( 0, MAPSIZE*GRIDSIZE ) 		Drink\Y# = Rnd( 0, MAPSIZE*GRIDSIZE ) 		Drink\Height = ImageHeight( Dummy ) 		Drink\Width = ImageWidth( Dummy ) 		 		For x = 0 To Drink\Width - 1 			For y = 0 To Drink\Height - 1 				Drink\ImageData[ x*Drink\Width + y ] = ReadPixelFast( x, y, ImageBuffer( Dummy ) ) 			Next 		Next 	 	If MapData( Drink\X# Shr GRIDSHIFTS, Drink\Y# Shr GRIDSHIFTS ) Then i = i - 1 Next FreeImage( Dummy )
  SetBuffer BackBuffer()
  Restore MapData For i = 0 To MAPSIZE - 1 	For t = 0 To MAPSIZE - 1 		Read MapData( t, i ) 	Next Next
  Timer = CreateTimer( 60 )
  DisplayFPS = 0 FPS = 0 Counter = MilliSecs()
  While Not KeyHit( 1 ) 	Cls 	 	LockBuffer BackBuffer() 	RenderScreen() 	RenderSprites() 	UnlockBuffer BackBuffer() 	 	MoveCamera() 	 	If MilliSecs() - Counter > 1000 Then 		DisplayFPS = FPS 		Counter = MilliSecs() 		FPS = 0 	EndIf 	 	Text 0, 0, DisplayFPS 	 	Flip 0 	WaitTimer Timer 	 	FPS = FPS + 1 Wend
  End
  Function MoveCamera() 	XSpeed# = -Sgn( KeyDown( 208 ) - KeyDown( 200 ) )*Cos( CameraAngle ) 	YSpeed# = Sgn( KeyDown( 208 ) - KeyDown( 200 ) )*Sin( CameraAngle ) 	 	TileX = ( CamX# + XSpeed# + Sgn( XSpeed# ) ) Shr GRIDSHIFTS 	TileY = ( CamY# + YSpeed# + Sgn( YSpeed# ) ) Shr GRIDSHIFTS 	OldTileX = CamX# Shr GRIDSHIFTS 	OldTileY = CamY# Shr GRIDSHIFTS 	 	If Not MapData( OldTileX, TileY ) Then CamY# = CamY# + YSpeed# 	If Not MapData( TileX, OldTileY ) Then CamX# = CamX# + XSpeed# 	 	CameraAngle = CameraAngle - KeyDown( 203 ) + KeyDown( 205 ) 	 	PlaneDisplacement = PlaneDisplacement - KeyDown( 74 ) + KeyDown( 78 ) End Function
  Function RenderScreen() 	For i = 0 To GWIDTH - 2 		Angle# = ( CameraAngle - ANGLEOFVIEW/2 + CASTINGSTEP#*i ) Mod 360 		Angle# = ( 360 + Angle# ) Mod 360 		 		 		YDirection = 1 - ( Angle# < 180 )*2 		 		 		XDirection = -1 + ( Angle# > 270 Or Angle# < 90 )*2 		 		 		Ya# = CamY# Mod GRIDSIZE 		If YDirection = 1 Then Ya# = GRIDSIZE - Ya# 		Xa# = Ya#/Tan( Angle# ) 		 		IntersectionY# = CamY# + YDirection*Ya# 		IntersectionX# = CamX# - YDirection*Xa# 		 		TileY = ( IntersectionY# + YDirection ) Shr GRIDSHIFTS 		TileX = IntersectionX# Shr GRIDSHIFTS 		 		If TileX >= 0 And TileX < MAPSIZE And TileY >= 0 And TileY < MAPSIZE Then 			If MapData( TileX, TileY ) Then  				WallTile = MapData( TileX, TileY ) 			Else 				Ya# = YDirection Shl GRIDSHIFTS 				Xa# = Ya#/Tan( Angle# ) 				Repeat 					IntersectionX# = IntersectionX# - Xa# 					IntersectionY# = IntersectionY# + Ya# 					TileY = ( IntersectionY# + YDirection ) Shr GRIDSHIFTS 					TileX = IntersectionX# Shr GRIDSHIFTS 					If TileX >= 0 And TileX < MAPSIZE And TileY >= 0 And TileY < MAPSIZE Then 						If MapData( TileX, TileY ) Then 							WallTile = MapData( TileX, TileY ) 							Exit 						EndIf 					Else 						Exit 					EndIf 				Forever 			EndIf 		EndIf 		 		WallX# = IntersectionX# 		WallY# = IntersectionY# 		 		 		Xa# = CamX# Mod GRIDSIZE 		If XDirection = 1 Then Xa# = GRIDSIZE - Xa# 		Ya# = Xa#*Tan( Angle# ) 		 		IntersectionX# = CamX# + XDirection*Xa# 		IntersectionY# = CamY# - XDirection*Ya# 		 		TileY = IntersectionY# Shr GRIDSHIFTS 		TileX = ( IntersectionX# + XDirection ) Shr GRIDSHIFTS 		 		If TileX >= 0 And TileX < MAPSIZE And TileY >= 0 And TileY < MAPSIZE Then 			If MapData( TileX, TileY ) Then 				IntersectionTile = MapData( TileX, TileY ) 			Else 				Xa# = XDirection Shl GRIDSHIFTS 				Ya# = Xa#*Tan( Angle# ) 				Repeat 					IntersectionX# = IntersectionX# + Xa# 					IntersectionY# = IntersectionY# - Ya# 					TileY = IntersectionY# Shr GRIDSHIFTS 					TileX = ( IntersectionX# + XDirection ) Shr GRIDSHIFTS 					If TileX >= 0 And TileX < MAPSIZE And TileY >= 0 And TileY < MAPSIZE Then 						If MapData( TileX, TileY ) Then 							IntersectionTile = MapData( TileX, TileY ) 							Exit 						EndIf 					Else 						Exit 					EndIf 				Forever 			EndIf 		EndIf 		 		DistanceVertically# = Abs( ( WallY# - CamY# )/Sin( Angle# ) ) 		DistanceHorizontally# = Abs( ( IntersectionX# - CamX# )/Cos( Angle# ) ) 		 		If DistanceHorizontally# < DistanceVertically# Then  			SmallerDistance# = DistanceHorizontally# 			If WallTile > 0 Then 				If HD Then  					TextureRow = Floor( ( WallX# Mod GRIDSIZE )*SCALEFACTOR# ) 				Else 					TextureRow = Floor( WallX# ) Mod GRIDSIZE 				EndIf 			EndIf 			If HD Then 				TextureRow = Floor( ( IntersectionY# Mod GRIDSIZE )*SCALEFACTOR# ) 			Else 				TextureRow = Floor( IntersectionY# ) Mod GRIDSIZE 			EndIf 		Else 			SmallerDistance# = DistanceVertically# 			If IntersectionTile > 0 Then 				If HD Then 					TextureRow = Floor( ( IntersectionY# Mod GRIDSIZE )*SCALEFACTOR# ) 				Else 					TextureRow = Floor( IntersectionY# ) Mod GRIDSIZE 				EndIf 			EndIf 			If HD Then 				TextureRow = Floor( ( WallX# Mod GRIDSIZE )*SCALEFACTOR# ) 			Else 				TextureRow = Floor( WallX# ) Mod GRIDSIZE 			EndIf 		EndIf 		 		WallHeight = ( GRIDSIZE/( SmallerDistance#*Cos( CASTINGSTEP#*i - ANGLEOFVIEW/2 ) ) )*ProjectionDistance# 		If WallTile = 0 And IntersectionTile > 0 Then WallTile = IntersectionTile 		 		TStart = ( GHEIGHT/2 + PlaneDisplacement ) - WallHeight/2 		TEnd = ( GHEIGHT/2 + PlaneDisplacement ) + WallHeight/2 		If TStart < 0 Then YCorrection = Abs( TStart ) 		If TEnd >= GHEIGHT Then YCorrection2 = TEnd - GHEIGHT + 1 				 		For t = TStart + YCorrection To TEnd - YCorrection2 			If WallTile = 1 Then 				WritePixelFast i, t, $FFFFFFFF  			Else 				ARGB = TextureData( ( WallTile - 2 )*TEXTURESIZE*TEXTURESIZE + ( TextureRow*TEXTURESIZE + ( t - GHEIGHT/2 + WallHeight/2 - PlaneDisplacement )*( TEXTURESIZE/Float( WallHeight ) ) ) ) 				WritePixelFast i, t, ARGB 			EndIf 		Next 		 		 		TStart = ( GHEIGHT/2 + PlaneDisplacement ) + WallHeight/2 		TStart = TStart*( TStart >= 0 ) 		TEnd = GHEIGHT - 1 		For t = TStart To TEnd 			Length# = ( ( GRIDSIZE/2 )/( ( t - GHEIGHT/2 - PlaneDisplacement )/ProjectionDistance# ) )/Cos( CASTINGSTEP#*i - ANGLEOFVIEW/2 ) 			FloorX = CamX# + Cos( Angle# )*Length# 			FloorY = CamY# - Sin( Angle# )*Length# 			TextureColumn = FloorY Mod TEXTURESIZE 			TextureRow = FloorX Mod TEXTURESIZE 			ARGB = TextureData( ( FLOORTILE - 1 )*TEXTURESIZE*TEXTURESIZE + ( TextureRow*TEXTURESIZE + TextureColumn ) ) 			WritePixelFast i, t, ARGB 		Next 		 		If CEILING Then 			 			TStart = 0 			TEnd = ( GHEIGHT/2 + PlaneDisplacement ) - WallHeight/2 			TEnd = TEnd*( TEnd < GHEIGHT ) + ( GHEIGHT - 1 )*( TEnd >= GHEIGHT ) 			For t = TStart To TEnd 				Length# = ( ( GRIDSIZE/2 )/( ( GHEIGHT/2 - t + PlaneDisplacement )/ProjectionDistance# ) )/Cos( CASTINGSTEP#*i - ANGLEOFVIEW/2 ) 				CeilingX = CamX# + Cos( Angle# )*Length# 				CeilingY = CamY# - Sin( Angle# )*Length# 				TextureRow = CeilingX Mod TEXTURESIZE 				TextureColumn = CeilingY Mod TEXTURESIZE 				ARGB = TextureData( ( CEILINGTILE - 1 )*TEXTURESIZE*TEXTURESIZE + ( TextureRow*TEXTURESIZE + TextureColumn ) ) 				WritePixelFast i, t, ARGB 			Next 		EndIf 		 		Distances#( i ) = SmallerDistance# 		 		TextureRow = 0 		IntersectionTile = 0 		WallTile = 0 		Saviour = 0 		YCorrection = 0 		YCorrection2 = 0 	Next End Function
  Function RenderSprites() 	 	For Drink.Sprite = Each Sprite 		Distance# = Sqr( ( CamX# - Drink\X# )*( CamX# - Drink\X# ) + ( CamY# - Drink\Y# )*( CamY# - Drink\Y# ) ) 		Height = Drink\Height - 1 		Width = Drink\Width 		SpriteHeight = ( Height/Distance# )*ProjectionDistance# 		SpriteWidth = Width*( SpriteHeight/Float( Height ) ) 		 		Difference# = ATan( ( Drink\Y# - CamY# )/( Drink\X# - CamX# ) ) 		If Drink\X# - CamX# < 0 Then Difference# = 180 + Difference# 		Angle# = ( -Difference# - CameraAngle + 360 ) Mod 360 		StartSlice = ( Angle# + ANGLEOFVIEW/2 )/CASTINGSTEP# - SpriteWidth/2 		If ( StartSlice < 0 Or StartSlice > GWIDTH ) And Abs( Angle# ) > 360 - ANGLEOFVIEW/2 - SpriteWidth/2 Then 			Angle# = Angle# - Sgn( Angle# )*360 			StartSlice = ( Angle# + ANGLEOFVIEW/2 )/CASTINGSTEP# - SpriteWidth/2 		EndIf 		 		XStart = StartSlice*( StartSlice >= 0 ) 		XEnd = StartSlice + SpriteWidth - 1 		XEnd = XEnd*( XEnd < GWIDTH ) + ( GWIDTH - 1 )*( XEnd > GWIDTH ) 		YStart = ( GHEIGHT/2 - Spriteheight/2 + PlaneDisplacement )*( GHEIGHT/2 - SpriteHeight/2 + PlaneDisplacement >= 0 )  		YEnd = GHEIGHT/2 + SpriteHeight/2 - 1 + PlaneDisplacement 		YEnd = YEnd*( YEnd < GHEIGHT ) + ( GHEIGHT - 1)*( YEnd >= GHEIGHT ) 		 		For x = XStart To XEnd 			For y = YStart To YEnd 				If Distances#( x ) > Distance# Then 					ARGB = Drink\ImageData[ Floor( ( x - StartSlice )*( Width/Float( SpriteWidth ) ) )*Drink\Width + ( y - GHEIGHT/2 + SpriteHeight/2 - PlaneDisplacement )*( Height/Float( SpriteHeight ) ) ] 					If ARGB - $FF000000 <> 0 Then 						WritePixelFast x, y, ARGB 					EndIf 				EndIf 			Next 		Next 	Next End Function
  .MapData Data 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 Data 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2 Data 1, 0, 2, 0, 2, 0, 2, 0, 2, 2, 2, 2, 2, 2, 0, 2 Data 2, 2, 2, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 2 Data 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2 Data 2, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 2, 0, 2 Data 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 0, 2 Data 2, 2, 2, 0, 2, 2, 0, 2, 2, 2, 2, 2, 0, 2, 0, 2 Data 2, 2, 2, 0, 2, 2, 0, 2, 0, 0, 0, 2, 0, 2, 0, 2 Data 2, 2, 2, 0, 0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 0, 2 Data 2, 0, 2, 0, 0, 0, 0, 2, 0, 2, 0, 2, 2, 2, 0, 2 Data 2, 0, 2, 0, 0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 0, 2 Data 2, 0, 2, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 2, 0, 2 Data 2, 0, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 0, 2 Data 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 Data 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2  
 
 Edit:
 Jetzt habe ich doch tatsächlich vergessen, die bekannten Bugs aufzuzählen   
 Es kann manchmal vorkommen, dass Strahlen durch die Wände 'durchsausen'.
 Das macht sich dadurch bemerkbar, dass Streifen der Textur einer dahinterliegenden Wand auf der vorderen sichtbar werden, oder dass an einer Ecke plötzlich der Eckpunkt fehlt und man das dahinter sieht.
 Sollte ich den Fehler finden, werde ich ihn nachbessern - euch steht es selbstverständlich frei, den Code selbst auszubessern.
							 |