We'll dive a bit more into circles, I'll post the full code at the end.
- A point following a circle's circumference
- Field of view
- Ray casting through that field of view
A point following a circle's circumference
The math are the following simple and straightforward
The sine of an angle is Y/R and the cosine of an angle is X/R |
So for the implementation in VB.NET
AngleMidR += 1 'Increasing degrees
AngleQ = (AngleMidR * Math.PI) / 180 'AngleMidR (degrees) translated into radians
CircMidRY = CircMidY + CircRadius * Math.Sin(AngleQ)
CircMidRX = CircMidX + CircRadius * Math.Cos(AngleQ)
Where CircMid is the middle point of the circle and CircMidR is the point with R distance from it.
I cut the frames on this one to create the .gif (that's why it's so rough) |
Field of View
For calculating field of view we'll assign an angle as the middle of the vision and widen our field based on the span from it.
Let's say our Camera faces at Z degrees (where 0 is the middle right part of the horizontal axis in a clockwise direction), and our field of view has an arc vision span of a 70 degrees.
If we use the math on the first example of this post we could create the following representation of that arc.
Crude representation using drawline |
Now let's step a simple way to translate our calculations to our grid system and mark all the cells that are inside our field of view.
- Find the points on the arc horizon (furthest grid cells to show)
- Cast an arc from starting point to all the points on an arc with increasing radius (I'll explain on the next image)
- Assign a value on all these points
A different way and less crude would be to use Math.atan2, loop through all the grid cells, find their static angle from our starting point and compare it to our current camera angle.
Ray-cast visibility
To distinguish visible tiles from non visible tiles, we stop the ray-casting when we find a blocked tile while we cast our arc.
These gif files really don't do justice to the real outcome |
Code
I added a FOV integer on our gridworld array from the previous posts to hold the value for the cells inside our arc. The code is highly un-optimized and it can improve greatly.
Our drawing code (SoftRender("DRAW"))
For X = 0 To MaxGridX
For Y = 0 To MaxGridY
QRect = New Rectangle(GridWorld(X, Y).X, GridWorld(X, Y).Y, GridWidth, GridWidth)
If GridWorld(X, Y).Blocked = 0 Then
Gs.FillRectangle(Brushes.LightGray, QRect)
Else
Gs.FillRectangle(Brushes.DarkSlateGray, QRect)
End If
If GridWorld(X, Y).FOV = 1 Then
Gs.FillRectangle(Brushes.Blue, QRect)
End If
Gs.DrawRectangle(Pens.DarkGray, QRect)
Next
Next
Our calls to our new sub (Add these to a refreshing event or even inside the draw function)
CircleAlgo("PROJECT_ON_GRID")
CircleAlgo("FIELD_OF_VIEW")
CircleAlgo("DRAW_ON_CANVAS_FOV")
And the rest of the code
Public CircMidXG As Integer
Public CircMidYG As Integer
Public CircMidX As Integer = 400
Public CircMidY As Integer = 200
Public CircMidRX As Integer
Public CircMidRY As Integer
Public AngleMidR As Integer
Public CircRadius As Integer = 100
Public ArcList(70) As Point
Public Sub CircleAlgo(ByVal Func As String)
Dim AngleQ As Single
Dim FieldofViewL As Single = ((AngleMidR - 35) * Math.PI) / 180
Dim FieldofViewR As Single = ((AngleMidR + 35) * Math.PI) / 180
Dim X, Y, Z, Q As Integer
'Here I blocked some tiles for testing (the ones you see in the last image
GridWorld(37, 11).Blocked = 1
GridWorld(37, 12).Blocked = 1
GridWorld(38, 12).Blocked = 1
GridWorld(38, 13).Blocked = 1
GridWorld(39, 13).Blocked = 1
GridWorld(39, 14).Blocked = 1
GridWorld(39, 15).Blocked = 1
GridWorld(40, 15).Blocked = 1
GridWorld(41, 15).Blocked = 1
If Func = "POINT_ON_CIRCLE" Then
AngleMidR += 2 'Increasing degrees
AngleQ = (AngleMidR * Math.PI) / 180 'AngleMidR (degrees) translated into radians
CircMidRY = CircMidY + CircRadius * Math.Sin(AngleQ)
CircMidRX = CircMidX + CircRadius * Math.Cos(AngleQ)
ElseIf Func = "DRAW_ON_CANVAS" Then
QRect = New Rectangle(CircMidX - CircRadius, CircMidY - CircRadius, CircRadius * 2, CircRadius * 2)
Gs.DrawEllipse(Pens.Black, QRect)
'Draw Circle Middle point
QRect = New Rectangle(CircMidX, CircMidY, 4, 4)
Gs.FillRectangle(Brushes.Black, QRect)
'Draw Point on the circle circumference
QRect = New Rectangle(CircMidRX, CircMidRY, 4, 4)
Gs.FillRectangle(Brushes.Red, QRect)
ElseIf Func = "FIELD_OF_VIEW" Then
If AngleMidR < 360 Then
AngleMidR += 2 'Field of view middle point angle
Else
AngleMidR = 0
End If
AngleQ = (AngleMidR * Math.PI) / 180 'AngleMidR (degrees) translated into radians
FieldofViewL = ((AngleMidR - 35) * Math.PI) / 180 'Field of View Left Edge (degrees) translated into radians
FieldofViewR = ((AngleMidR + 35) * Math.PI) / 180 'Field of View Right Edge (degrees) translated into radians
CircMidRY = CircMidY + CircRadius * Math.Sin(AngleQ)
CircMidRX = CircMidX + CircRadius * Math.Cos(AngleQ)
Gs.DrawLine(Pens.DarkViolet, CircMidX, CircMidY, CircMidRX, CircMidRY)
CircMidRY = CircMidY + CircRadius * Math.Sin(FieldofViewL)
CircMidRX = CircMidX + CircRadius * Math.Cos(FieldofViewL)
Gs.DrawLine(Pens.Blue, CircMidX, CircMidY, CircMidRX, CircMidRY)
CircMidRY = CircMidY + CircRadius * Math.Sin(FieldofViewR)
CircMidRX = CircMidX + CircRadius * Math.Cos(FieldofViewR)
Gs.DrawLine(Pens.Blue, CircMidX, CircMidY, CircMidRX, CircMidRY)
ClearArc()
Dim Blockfound As Integer
For Z = 0 To 70
Blockfound = 0
For Q = 0 To CircRadius
ArcList(Z).Y = (CircMidY + Q * Math.Sin(((AngleMidR - 35 + Z) * Math.PI) / 180)) \ GridWidth
ArcList(Z).X = (CircMidX + Q * Math.Cos(((AngleMidR - 35 + Z) * Math.PI) / 180)) \ GridWidth
If Blockfound = 1 Then
GridWorld(ArcList(Z).X, ArcList(Z).Y).FOV = 1
Exit For
End If
If GridWorld(ArcList(Z).X, ArcList(Z).Y).Blocked = True Then
Blockfound = 1
Else
GridWorld(ArcList(Z).X, ArcList(Z).Y).FOV = 1
End If
Next
Next
ElseIf Func = "DRAW_ON_CANVAS_FOV" Then
QRect = New Rectangle(CircMidX - CircRadius, CircMidY - CircRadius, CircRadius * 2, CircRadius * 2)
Gs.DrawEllipse(Pens.Black, QRect)
'Draw Circle Middle point
QRect = New Rectangle(CircMidX, CircMidY, 4, 4)
Gs.FillRectangle(Brushes.Black, QRect)
ElseIf Func = "PROJECT_ON_GRID" Then
ShowGrid = 1
CircMidXG = 32
CircMidYG = 18
CircMidX = GridWorld(CircMidXG, CircMidYG).X + GridWidth / 2
CircMidY = GridWorld(CircMidXG, CircMidYG).Y + GridWidth / 2
CircRadius = GridWidth * 14
End If
End Sub
Public Sub ClearArc()
Dim X, Y As Integer
For X = 0 To MaxGridX
For Y = 0 To MaxGridY
GridWorld(X, Y).FOV = 0
Next
Next
End Sub
Ray casting can be really useful on 3d grids, when you have to lower the number of blocks drawn on large maps
Comments
Post a Comment