AutoIt: 3.3.8.1
Категория: Графика, GDI+, Математика
Описание: Пример применения рейкастинга для рендеринга псевдо-3d мира.
TODO:
Код:
Снимок:
Автор(ы): Fever
Использованные статьи и части кода:
http://lodev.org/cgtutor/raycasting.html
http://www.permadi.com/java/rayc/Rayc.java
Категория: Графика, 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