Что нового

[Математика] Псевдо-3D на AutoIt. Рейкастинг

Fever

Скриптер
AutoIt: 3.3.8.1

Категория: Графика, GDI+, Математика

Описание: Пример применения рейкастинга для рендеринга псевдо-3d мира.

TODO:
  • Коллизии
  • Текстуры

Код:
Код:
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <Array.au3>
#include <GDIPlus.au3>
#include <Misc.au3>
#include <WinAPI.au3>

Opt("MustDeclareVars", 1)

Global _
	$bGameRunning = True

Global _
	$iWidth = 640, _
	$iHeight = 480, _
	$iTileSize = 64, _
	$iWallHeight = 64

;----------------------------------------------------
; GUI
;----------------------------------------------------
Global _
	$sTitle = "Ray casting", _
	$hGUI
;----------------------------------------------------

;----------------------------------------------------
; MATH CONSTANTS
;----------------------------------------------------
Global Const $MATH_PI = 3.14159265359
;----------------------------------------------------

;----------------------------------------------------
; ANGLES
;----------------------------------------------------
Global Const _
			$ANGLE_60 = $iWidth, _
			$ANGLE_30 = $ANGLE_60 / 2, _
			$ANGLE_15 = $ANGLE_30 / 2, _
			$ANGLE_90 = $ANGLE_30 * 3, _
			$ANGLE_180 = $ANGLE_90 * 2, _
			$ANGLE_270 = $ANGLE_90 * 3, _
			$ANGLE_360 = $ANGLE_60 * 6, _
			$ANGLE_0 = 0, _
			$ANGLE_5 = $ANGLE_30 / 6, _
			$ANGLE_10 = $ANGLE_5 * 2
;----------------------------------------------------

;----------------------------------------------------
; TRIGONOMETRIC TABLES
;----------------------------------------------------
Global _
		$aSinTable[$ANGLE_360 + 1], _
		$aISinTAble[$ANGLE_360 + 1], _
		$aCosTable[$ANGLE_360 + 1], _
		$aICosTable[$ANGLE_360 + 1], _
		$aTanTable[$ANGLE_360 + 1], _
		$aITanTable[$ANGLE_360 + 1], _
		$aFishTable[$ANGLE_60 + 1], _
		$aXStepTable[$ANGLE_360 + 1], _
		$aYStepTable[$ANGLE_360 + 1]
;----------------------------------------------------

Global _
	$aMap[12][12] = _
	[ _
	[1,1,1,1,1,1,1,1,1,1,1,1], _
	[1,0,0,0,0,0,0,0,0,0,0,1], _
	[1,1,1,1,1,0,0,1,0,0,0,1], _
	[1,0,0,0,0,1,0,0,0,0,0,1], _
	[1,0,0,0,1,1,1,0,0,0,0,1], _
	[1,0,0,0,1,0,1,0,0,0,0,1], _
	[1,0,0,0,0,0,0,0,0,0,0,1], _
	[1,0,0,0,0,1,0,0,0,0,0,1], _
	[1,0,0,0,0,0,0,0,0,0,0,1], _
	[1,0,0,0,0,0,0,0,0,0,0,1], _
	[1,0,0,0,0,0,0,0,0,0,0,1], _
	[1,1,1,1,1,1,1,1,1,1,1,1] _
	], _
	$iMapWidth = 12, _
	$iMapHeight = 12

Global _
	$sMapIndex_Wall = "1", _
	$sMapIndex_Empty = "0"


;----------------------------------------------------
; PLAYER
;----------------------------------------------------

Global _
	$iPlayerArc = $ANGLE_0, _
	$iPlayerPosX = 100, _
	$iPlayerPosY = 90, _
	$iPlayerDistanceToTheProjectPlane = 277, _
	$iProjectionPlaneYCenter = $iHeight / 2, _
	$iProjectionPlaneYCenterMax = Int($iHeight / 1.2), _
	$iProjectionPlaneYCenterMin = $iHeight / 4, _
	$iProjectionPlaneYCenterDef = $iProjectionPlaneYCenter, _
	$iPlayerHeight = 32, _
	$iPlayerMovementSpeed = 16, _
	$iPlayerTurnSpeed = $ANGLE_5

	;----------------------------------------------------
	; PLAYER - ANIMATION
	;----------------------------------------------------

	Global _
		$iPlayerAnimation_WalkIndex = 0

	;----------------------------------------------------

;----------------------------------------------------

Global _
	$fPlayerDirX, _
	$fPlayerDirY

Global _
	$iTime = 0, _
	$iOldTime = 0

Global _
	$hGraphic, _
	$iFrameTime, _
	$aGraphic

Global $hSystemStartTimer

;----------------------------------------------------
; LOCAL
;----------------------------------------------------

;----------------------------------------------------
; _ENGINE_RENDER
;----------------------------------------------------

Global _
	$iGridVertical, $iGridHorizontal, _
	$iDistToNextVerticalGrid, $iDistToNextHorizontalGrid, _
	$fXIntersection, $fYIntersection, _
	$fDistToNextXIntersection, $fDistToNextYIntersection, _
	$iGridIndexX, $iGridIndexY, _
	$fDistToVerticalGridBeingHit, $fDistToHorizontalGridBeingHit, _
	$iCastArc, $iCastColumn, _
	$fScaleFactor, $fDist, _
	$iTopOfWall, $iBottomOfWall, _
	$iProjectedWallHeight

Global _
	$iXTemp, $iYTemp

;----------------------------------------------------

;----------------------------------------------------

$hGUI = GUICreate($sTitle, $iWidth, $iHeight, -1, -1)

GUICtrlSetColor(-1, 0xFF0000)

GUISetState()

Global $hLoopTimer = TimerInit()
Global $iNextTick = TimerDiff($hLoopTimer)

_Init()

While ($bGameRunning)
	_GUILoop()
	_KeysLoop()

	_Engine_Render()

	$iOldTime = $iTime
	$iTime = TimerDiff($hSystemStartTimer)

	$iFrameTime = ($iTime - $iOldTime) / 1000

	WinSetTitle($hGUI, "", $sTitle & " - FPS: " & Round(1 / $iFrameTime) & " X: " & $iPlayerPosX & " Y: " & $iPlayerPosY & " ARC: " & $iPlayerArc)
WEnd

_Shutdown()

Func _GUILoop()
	Switch GUIGetMsg()
		Case $GUI_EVENT_CLOSE
			_Shutdown()
	EndSwitch
EndFunc

Func _KeysLoop()
	If _IsPressed("25") Then
		$iPlayerArc -= $iPlayerTurnSpeed

		If $iPlayerArc < $ANGLE_0 Then
			$iPlayerArc += $ANGLE_360
		EndIf
	ElseIf _IsPressed("27") Then
		$iPlayerArc += $iPlayerTurnSpeed

		If $iPlayerArc >= $ANGLE_360 Then
			$iPlayerArc -= $ANGLE_360
		EndIf
	EndIf

	$fPlayerDirX = $aCosTable[$iPlayerArc]
	$fPlayerDirY = $aSinTable[$iPlayerArc]

	If _IsPressed("26") Then
		$iPlayerPosX += Int($fPlayerDirX * $iPlayerMovementSpeed)
		$iPlayerPosY += Int($fPlayerDirY * $iPlayerMovementSpeed)

		If $iPlayerAnimation_WalkIndex == 0 Then
			$iProjectionPlaneYCenter += 2

			If $iProjectionPlaneYCenter >= $iProjectionPlaneYCenterDef + 10 Then
				$iPlayerAnimation_WalkIndex = 1
			EndIf
		Else
			$iProjectionPlaneYCenter -= 2

			If $iProjectionPlaneYCenter <= $iProjectionPlaneYCenterDef - 10 Then
				$iPlayerAnimation_WalkIndex = 0
			EndIf
		EndIf

	ElseIf _IsPressed("28") Then
		$iPlayerPosX -= Int($fPlayerDirX * $iPlayerMovementSpeed / 2)
		$iPlayerPosY -= Int($fPlayerDirY * $iPlayerMovementSpeed / 2)

		If $iPlayerAnimation_WalkIndex == 0 Then
			$iProjectionPlaneYCenter += 1

			If $iProjectionPlaneYCenter >= $iProjectionPlaneYCenterDef + 10 Then
				$iPlayerAnimation_WalkIndex = 1
			EndIf
		Else
			$iProjectionPlaneYCenter -= 1

			If $iProjectionPlaneYCenter <= $iProjectionPlaneYCenterDef - 10 Then
				$iPlayerAnimation_WalkIndex = 0
			EndIf
		EndIf
	EndIf

	#cs
	If _IsPressed("57") Then
		If $iProjectionPlaneYCenter < $iProjectionPlaneYCenterMax Then
			$iProjectionPlaneYCenter += 10
		EndIf
	ElseIf _IsPressed("53") Then
		If $iProjectionPlaneYCenter > $iProjectionPlaneYCenterMin Then
			$iProjectionPlaneYCenter -= 10
		EndIf
	Else
		;;If $iProjectionPlaneYCenter > $iProjectionPlaneYCenterDef Then
		;;	$iProjectionPlaneYCenter = $iProjectionPlaneYCenterDef
		;;Else
		;;	$iProjectionPlaneYCenter = $iProjectionPlaneYCenterDef
		;;EndIf
	EndIf
	#ce
EndFunc

Func _Init()
	Global $hGraphic, $aGraphic, $hPen, $hBitmap, $hBuffer

	$hSystemStartTimer = TimerInit()

	_GDIPlus_Startup()

	$hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI)
	$hBitmap = _GDIPlus_BitmapCreateFromGraphics($iWidth, $iHeight, $hGraphic)
	$hBuffer = _GDIPlus_ImageGetGraphicsContext($hBitmap)
	_GDIPlus_GraphicsSetSmoothingMode($hBuffer, 0)

	$hPen = _GDIPlus_BrushCreateSolid()

	_GDIPlus_GraphicsClear($hBuffer, 0xFFFFFFFF)

	_CreateOptTables()
EndFunc

Func _Shutdown()
	_GDIPlus_GraphicsDispose($hGraphic)
	_GDIPlus_BrushDispose($hPen)
	_GDIPlus_Shutdown()

	Exit
EndFunc

;----------------------------------------------------
; HELPER
;----------------------------------------------------

Func _ArcToRad($iArcAngle)
	Return $iArcAngle * $MATH_PI / $ANGLE_180
EndFunc

Func _CreateOptTables()
	Local $i, $fRadian

	For $i = 0 To $ANGLE_360
		$fRadian = _ArcToRad($i) + 0.0001

		$aSinTable[$i] = Sin($fRadian)
        $aISinTable[$i] = 1 / $aSinTable[$i]
		$aCosTable[$i] = Cos($fRadian)
		$aICosTable[$i] = 1 / $aCosTable[$i]
		$aTanTable[$i] = Tan($fRadian)
		$aITanTable[$i] = 1 / $aTanTable[$i]

		If $i >= $ANGLE_90 And $i < $ANGLE_270 Then
			$aXStepTable[$i] = Round($iTileSize / $aTanTable[$i], 5)

			If $aXStepTable[$i] > 0 Then
				$aXStepTable[$i] = -$aXStepTable[$i]
			EndIf
		Else
			$aXStepTable[$i] = Round($iTileSize / $aTanTable[$i], 5)

			If $aXStepTable[$i] < 0 Then
				$aXStepTable[$i] = -$aXStepTable[$i]
			EndIf
		EndIf

		If $i >= $ANGLE_0 And $i < $ANGLE_180 Then
			$aYStepTable[$i] = Round($iTileSize * $aTanTable[$i], 5)

			If $aYStepTable[$i] < 0 Then
				$aYStepTable[$i] = -$aYStepTable[$i]
			EndIf
		Else
			$aYStepTable[$i] = Round($iTileSize * $aTanTable[$i], 5)

			If $aYStepTable[$i] > 0 Then
				$aYStepTable[$i] = -$aYStepTable[$i]
			EndIf
		EndIf
	Next

	For $i = -$ANGLE_30 To $ANGLE_30
		$fRadian = _ArcToRad($i)

		$aFishTable[$i + $ANGLE_30] = Round(1 / Cos($fRadian), 7)
	Next

EndFunc

;----------------------------------------------------

;----------------------------------------------------
; ENGINE
;----------------------------------------------------

;----------------------------------------------------
; ENGINE -> GRAPHIC
;----------------------------------------------------
Func _Engine_Graphic_DrawBackground()
	Local _
		$iC = 25, _
		$iR, $iF, _
		$iSkyLength = $iHeight - $iProjectionPlaneYCenter

	For $iR = 0 To $iSkyLength - 1 Step 10
		_GDIPlus_BrushSetSolidColor($hPen, "0xFF" & Hex($iC, 2) & Hex(125, 2) & Hex(225, 2))
		_GDIPlus_GraphicsFillRect($hBuffer, 0, $iR, $iWidth, 10, $hPen)

		$iC += 5
	Next

	$iC = 5

	For $iF = $iR To $iHeight - 1 Step 15
		_GDIPlus_BrushSetSolidColor($hPen, "0xFF" & Hex($iC, 2) & Hex(10, 2) & Hex(10, 2))
		_GDIPlus_GraphicsFillRect($hBuffer, 0, $iF, $iWidth, 15, $hPen)

		$iC += 5
	Next
EndFunc
;----------------------------------------------------

Func _Engine_Render()
	_GDIPlus_GraphicsClear($hBuffer, 0xFFFFFFFF)
	_Engine_Graphic_DrawBackground()

	$iCastArc = $iPlayerArc
	$iCastArc -= $ANGLE_30

	If $iCastArc < 0 Then
		$iCastArc = $ANGLE_360 + $iCastArc
	EndIf

	For $iCastColumn = 0 To $iWidth - 1 Step 5
		If $iCastArc > $ANGLE_0 And $iCastArc < $ANGLE_180 Then
			$iGridHorizontal = Int($iPlayerPosY / $iTileSize) * $iTileSize + $iTileSize

			$iDistToNextHorizontalGrid = $iTileSize

			$iXTemp = $aITanTable[$iCastArc] * ($iGridHorizontal - $iPlayerPosY)

			$fXIntersection = $iXTemp + $iPlayerPosX
		Else
			$iGridHorizontal = Int($iPlayerPosY / $iTileSize) * $iTileSize
			$iDistToNextHorizontalGrid = -$iTileSize

			$iXTemp = $aITanTable[$iCastArc] * ($iGridHorizontal - $iPlayerPosY)
			$fXIntersection = $iXTemp + $iPlayerPosX

			$iGridHorizontal -= 1
		EndIf

		If $iCastArc == $ANGLE_0 Or $iCastArc == $ANGLE_180 Then
			$fDistToHorizontalGridBeingHit = 9999999
		Else
			$fDistToNextXIntersection = $aXStepTable[$iCastArc]

			While True
				$iGridIndexX = Int($fXIntersection / $iTileSize)
				$iGridIndexY = Int($iGridHorizontal / $iTileSize)

				If $iGridIndexX >= $iMapWidth Or $iGridIndexY >= $iMapHeight Or $iGridIndexX < 0 Or $iGridIndexY < 0 Then
					$fDistToHorizontalGridBeingHit = 9999999

					ExitLoop
				ElseIf $aMap[$iGridIndexY][$iGridIndexX] <> $sMapIndex_Empty Then
					$fDistToHorizontalGridBeingHit = ($fXIntersection - $iPlayerPosX) * $aICosTable[$iCastArc]

					ExitLoop
				Else
					$fXIntersection += $fDistToNextXIntersection
					$iGridHorizontal += $iDistToNextHorizontalGrid
				EndIf
			WEnd
		EndIf

		If $iCastArc < $ANGLE_90 Or $iCastArc > $ANGLE_270 Then
			$iGridVertical = $iTileSize + Int($iPlayerPosX / $iTileSize) * $iTileSize
			$iDistToNextVerticalGrid = $iTileSize

			$iYTemp = $aTanTable[$iCastArc] * ($iGridVertical - $iPlayerPosX)
			$fYIntersection = $iYTemp + $iPlayerPosY
		Else
			$iGridVertical = Int($iPlayerPosX / $iTileSize) * $iTileSize
			$iDistToNextVerticalGrid = -$iTileSize

			$iYTemp = $aTanTable[$iCastArc] * ($iGridVertical - $iPlayerPosX)
			$fYIntersection = $iYTemp + $iPlayerPosY

			$iGridVertical -= 1
		EndIf

		If $iCastArc == $ANGLE_90 Or $iCastArc == $ANGLE_270 Then
			$fDistToVerticalGridBeingHit = 9999999
		Else
			$fDistToNextYIntersection = $aYStepTable[$iCastArc]

			While True
				$iGridIndexX = Int($iGridVertical / $iTileSize)
				$iGridIndexY = Int($fYIntersection / $iTileSize)

				If $iGridIndexX >= $iMapWidth Or $iGridIndexY >= $iMapHeight Or $iGridIndexX < 0 Or $iGridIndexY < 0 Then
					$fDistToVerticalGridBeingHit = 9999999

					ExitLoop
				ElseIf $aMap[$iGridIndexY][$iGridIndexX] <> $sMapIndex_Empty Then
					$fDistToVerticalGridBeingHit = ($fYIntersection - $iPlayerPosY) * $aISinTable[$iCastArc]

					ExitLoop
				Else
					$fYIntersection += $fDistToNextYIntersection
					$iGridVertical += $iDistToNextVerticalGrid
				EndIf
			WEnd
		EndIf

		If $fDistToHorizontalGridBeingHit < $fDistToVerticalGridBeingHit Then
			$fDist = $fDistToHorizontalGridBeingHit
			;_GDIPlus_BrushSetSolidColor($hPen, 0xFF444444)
		Else
			$fDist = $fDistToVerticalGridBeingHit
			;_GDIPlus_BrushSetSolidColor($hPen, 0xFF343434)
		EndIf

		$fDist /= $aFishTable[$iCastColumn]
		$iProjectedWallHeight = Int($iWallHeight * $iPlayerDistanceToTheProjectPlane / $fDist)
		$iBottomOfWall = $iProjectionPlaneYCenter + Int($iProjectedWallHeight * 0.5)
		$iTopOfWall = $iHeight - $iBottomOfWall

		If $iBottomOfWall >= $iHeight Then
			$iBottomOfWall = $iHeight - 1
		EndIf

		_GDIPlus_BrushSetSolidColor($hPen, "0xFF" & Hex(Int($iProjectedWallHeight / 4), 2) & Hex(Int($iProjectedWallHeight / 4), 2) & Hex(Int($iProjectedWallHeight / 4), 2))
		_GDIPlus_GraphicsFillRect($hBuffer, $iCastColumn, $iTopOfWall, 5, $iProjectedWallHeight, $hPen)

		$iCastArc += 5
		If $iCastArc >= $ANGLE_360 Then
			$iCastArc -= $ANGLE_360
		EndIf
	Next

	$iPlayerMovementSpeed = Round(1 / $iFrameTime) / 3
	$iPlayerTurnSpeed = Round(1 / $iFrameTime) * 2

	_GDIPlus_GraphicsDrawImageRect($hGraphic, $hBitmap, 0, 0, $iWidth, $iHeight)
EndFunc

;----------------------------------------------------

Снимок:




Автор(ы): Fever
Использованные статьи и части кода:
http://lodev.org/cgtutor/raycasting.html
http://www.permadi.com/java/rayc/Rayc.java
 

winstan

Эксплотатор)
damien2008 [?]
по теме:зачет! побольше бы таких примеров!
Ждём! кто-нибудь да возьмётся да и сделает но будет это уже не контра а новая игра.

P.S. Ранее хотел сделать подобное но не осилил, а тут даже игра цветов
 
Верх