Now that we finished our basic drawing calls and our map creation, let's add some units on the map and program some basic game moving concepts. (It will be a long post since we'll cover multiple steps together)
Redesigning our units
Since I redesigned the UI couple of posts back, and got one step closer to actually continue with the game programming I thought maybe I should re-draw my units too. So I got to it and came up with the following result.
Old static unit sheet
New animated unit sheet
As you can see I decided to draw moving animations when our units move through the map, so now we will adjust our unit drawing call to get frames too. (I added here the updated unit sheet in case you want to use it.)
Creating our basic module (This will change heavily probably..)
I created a new module in which I made a structure to hold our factions data. (which on it's own is pretty much another structure, it's done like that so it's easier to work on than just having variables thrown here and there).
Public Structure FactionStruct
Dim Name As String
Dim Nation As String
Dim Color As Color
Dim Unit() As UnitStruct
'StrategyLibrary is a reference to something we'll implement on another post
'Dim Resources As StrategyLibrary
'Dim References As StrategyLibrary
'Dim Modifiers_A As StrategyLibrary
'Dim Modifiers_B As StrategyLibrary
End Structure
Public Structure UnitStruct
Dim X As Integer
Dim Y As Integer
Dim TargetX As Integer
Dim TargetY As Integer
Dim TileX As Integer
Dim TileY As Integer
Dim ID As Integer 'To specify unit ID from a unit library
Dim TileToM() As Point 'Here we'll store all the available unit target tiles at any time
Dim TileToMC As Integer 'Tile To Move Clicked
Dim Frame As Integer 'For animation reasons
Dim Health As Integer
Dim Attack As Integer
Dim Defense As Integer
Dim OnTheMove As Int16 'To specify if the units is moving at any time
Dim TurnPoints As Int16 'To specify how many times the unit can move
End Structure
Public MaxUnitsPF As Integer = 50 'Unit cap (For each faction)
Public UnitSelected As Integer = 0 'Selected Unit
Public UnitSelectF As Integer = 0 'Selected Unit Tile <<Frame>>
Public UnitSelectedMouseHover As Integer 'Direction Mouse Hovers over
Public MaxFactions As Int16 = 2 'Number of factions
Public Faction(MaxFactions) As FactionStruct 'Faction structure
Spawning our first unit
New page on our strategy game! So now that we have our basic faction structure ready, let's spawn our main unit! Our settlers!
In order to do so, we'll have to find a suitable block (non-ocean block), and a Do Until loop will fit perfectly. After we find that tile we'll need to assign to our unit values for Tile X,Y and Screen X,Y. Lastly we'll center (kinda) our camera on our settler.
Public Sub Faction_F(ByVal func As String)
Dim I, Q As Int16
Dim X, Y As Integer
If func = "CREATE_FACTIONS" Then
For I = 0 To MaxFactions
ReDim Faction(I).Unit(MaxUnitsPF)
For Q = 0 To MaxUnitsPF
ReDim Faction(I).Unit(Q).TileToM(7)
Next
Next
For I = 0 To MaxFactions 'Going through all the factions
Faction(I).Unit(0).ID = 1 'Assigning settler unit
Faction(I).Unit(0).TileX = Int(Rnd() * MaxGridX) 'Random tile X
Faction(I).Unit(0).TileY = Int(Rnd() * MaxGridY) 'Random tile Y
Do Until GridWorld(Faction(I).Unit(0).TileX, Faction(I).Unit(0).TileY).ID >= 4 'Make sure no unit is spawned on an ocean tile
Faction(I).Unit(0).TileX = Int(Rnd() * MaxGridX)
Faction(I).Unit(0).TileY = Int(Rnd() * MaxGridY)
Loop
X = Faction(I).Unit(0).TileX
Y = Faction(I).Unit(0).TileY
Faction(I).Unit(0).X = ((X - Y) * GridWidth / 2) * ZoomVar 'Making sure it gets the proper X,Y based on resizing
Faction(I).Unit(0).Y = ((X + Y) * GridHeight / 2) * ZoomVar 'Making sure it gets the proper X,Y based on resizing
Faction(I).Unit(0).TargetX = Faction(I).Unit(0).X
Faction(I).Unit(0).TargetY = Faction(I).Unit(0).Y
Faction(I).Unit(0).TurnPoints = 20 'For testing reasons we'll give 20 moving points to our settler
Next
UnitSelected = 0 'There is only our settler, so we select it
Unit_F("MOVE_CALCULATE", UnitSelected)
'Center the camera on our settler
OffSetX = -Faction(0).Unit(0).X + ScreenWidth / 2 - 140
OffSetY = -Faction(0).Unit(0).Y + ScreenHeight / 2 - 60
End If
End Sub
Testing our above algorithm
The first step was done (if you include this Faction_F("CREATE_FACTIONS")) at the end of our map creation algorithm and call a draw_textured for our unit (I will include the code as a whole at the next steps) and run the application you should get the result as shown below.
Our first unit is on the map!
To explain it a bit better. (Our TileToM() array will hold the data of the 8 tiles around our unit whenever our unit moves through our map grid. Now on our move move event we'll check if our mouse is over one of those 8 tiles and if clicked it will move to that tile)
More unit algorithms
I'll include large chunks of code since it will be a long post and explain them as it goes. You will understand how they are all connected at the end. If you have questions just post a response at the article!
'On our Unit_F sub we control our mouse events (hover over the available tiles our unit can move to, clicking on one and of course lastly Calculating which are the available tiles our unit can move to!)
Public Sub Unit_F(ByVal Func As String, ByVal ID As Integer)
Dim Q As Int16
If Func = "MOUSE_HOVER_UNIT" Then
Dim HoveringOver As Int16 = 0
For Q = 0 To 7
If GridPX = Faction(0).Unit(ID).TileToM(Q).X And GridPY = Faction(0).Unit(ID).TileToM(Q).Y Then
UnitSelectedMouseHover = Q
HoveringOver = 1
Exit For
End If
Next
If HoveringOver = 0 Then
UnitSelectedMouseHover = 10
End If
ElseIf Func = "MOUSE_MOVE_UNIT" Then
'We check if the mouse clicked on an (can move to) tile, then if the unit is not already on the move and lastly if it has remaining points to move
If UnitSelectedMouseHover <> 10 And Faction(0).Unit(UnitSelected).OnTheMove = 0 And Faction(0).Unit(UnitSelected).TurnPoints > 0 Then
Faction(0).Unit(UnitSelected).TargetX = GridWorld(Faction(0).Unit(UnitSelected).TileToM(UnitSelectedMouseHover).X, Faction(0).Unit(UnitSelected).TileToM(UnitSelectedMouseHover).Y).SX
Faction(0).Unit(UnitSelected).TargetY = GridWorld(Faction(0).Unit(UnitSelected).TileToM(UnitSelectedMouseHover).X, Faction(0).Unit(UnitSelected).TileToM(UnitSelectedMouseHover).Y).SY
Faction(0).Unit(UnitSelected).OnTheMove = 1
Faction(0).Unit(UnitSelected).TileToMC = UnitSelectedMouseHover
'As you can see we assigned the target X,Y variables the point we want our unit to move to and we declared it as "On the move"
End If
ElseIf Func = "MOVE_CALCULATE" Then 'Which tiles can we move to?
Q = 0
Faction(0).Unit(ID).TileToM(Q).X = Faction(0).Unit(ID).TileX + 1
Faction(0).Unit(ID).TileToM(Q).Y = Faction(0).Unit(ID).TileY
Q = 1
Faction(0).Unit(ID).TileToM(Q).X = Faction(0).Unit(ID).TileX + 1
Faction(0).Unit(ID).TileToM(Q).Y = Faction(0).Unit(ID).TileY + 1
Q = 2
Faction(0).Unit(ID).TileToM(Q).X = Faction(0).Unit(ID).TileX
Faction(0).Unit(ID).TileToM(Q).Y = Faction(0).Unit(ID).TileY + 1
Q = 3
Faction(0).Unit(ID).TileToM(Q).X = Faction(0).Unit(ID).TileX - 1
Faction(0).Unit(ID).TileToM(Q).Y = Faction(0).Unit(ID).TileY + 1
Q = 4
Faction(0).Unit(ID).TileToM(Q).X = Faction(0).Unit(ID).TileX - 1
Faction(0).Unit(ID).TileToM(Q).Y = Faction(0).Unit(ID).TileY
Q = 5
Faction(0).Unit(ID).TileToM(Q).X = Faction(0).Unit(ID).TileX - 1
Faction(0).Unit(ID).TileToM(Q).Y = Faction(0).Unit(ID).TileY - 1
Q = 6
Faction(0).Unit(ID).TileToM(Q).X = Faction(0).Unit(ID).TileX
Faction(0).Unit(ID).TileToM(Q).Y = Faction(0).Unit(ID).TileY - 1
Q = 7
Faction(0).Unit(ID).TileToM(Q).X = Faction(0).Unit(ID).TileX + 1
Faction(0).Unit(ID).TileToM(Q).Y = Faction(0).Unit(ID).TileY - 1
End If
End Sub
'On our Unit_F sub we control the movement of our unit when a target tile is clicked. It's a simple if X<>TargetX and Y<>TargetY then move till reach & change frame while moving.
Public Sub Unit_M(ByVal X As Integer, ByVal Y As Integer)
If Faction(Y).Unit(X).X <> Faction(Y).Unit(X).TargetX Then
If Faction(Y).Unit(X).X > Faction(Y).Unit(X).TargetX Then
Faction(Y).Unit(X).X -= 1
ElseIf Faction(Y).Unit(X).X < Faction(Y).Unit(X).TargetX Then
Faction(Y).Unit(X).X += 1
End If
End If
If Faction(Y).Unit(X).Y <> Faction(Y).Unit(X).TargetY Then
If Faction(Y).Unit(X).Y > Faction(Y).Unit(X).TargetY Then
Faction(Y).Unit(X).Y -= 1
ElseIf Faction(Y).Unit(X).Y < Faction(Y).Unit(X).TargetY Then
Faction(Y).Unit(X).Y += 1
End If
End If
If Faction(Y).Unit(X).X = Faction(Y).Unit(X).TargetX And Faction(Y).Unit(X).Y = Faction(Y).Unit(X).TargetY Then
If Faction(Y).Unit(X).OnTheMove = 1 Then
Faction(Y).Unit(X).TileX = Faction(Y).Unit(X).TileToM(Faction(Y).Unit(X).TileToMC).X
Faction(Y).Unit(X).TileY = Faction(Y).Unit(X).TileToM(Faction(Y).Unit(X).TileToMC).Y
Faction(Y).Unit(X).OnTheMove = 0
Unit_F("MOVE_CALCULATE", X)
End If
End If
If Faction(Y).Unit(X).OnTheMove = 1 Then
If Faction(Y).Unit(X).Frame < 27 Then
Faction(Y).Unit(X).Frame += 1
Else
Faction(Y).Unit(X).Frame = 4
End If
Else
Faction(Y).Unit(X).Frame = 0
End If
End Sub
'Now we'll update a bit our old Grid algorithms (Mouse move and click, resizing and changing Field of view and field of war. You'll see there are additions to resizing since Units are drawn directly with they pixel X,Y without passing through our grid X,Y reference.
Public Sub GLMouseMove(ByVal X1 As Integer, ByVal Y1 As Integer)
Dim X, Y As Integer
X = X1 - OffSetX
Y = Y1 - OffSetY
GridPX = (Y + X / 2) / (GridHeight * ZoomVar)
GridPY = GridPX - (X) / (GridHeight * ZoomVar)
Unit_F("MOUSE_HOVER_UNIT", 0)
End Sub
Public Sub GLMouseDown(ByVal Btn As MouseEventArgs) 'Just call this from you clicking on screen event
'If you run this from a console openGL application then you'll need to change it a bit since there is a different event call
If Btn.Button = MouseButtons.Left Then
Unit_F("MOUSE_MOVE_UNIT", 0)
End If
End Sub
Public Sub FOV_FOW_Grid()
Dim X, Y, Q As Integer
Q = 1
For Y = 0 To MaxGridY
For X = 0 To MaxGridX
GridWorld(X, Y).FOW = 0
Next
Next
For Q = 0 To MaxUnitsPF
If Faction(0).Unit(Q).ID <> 0 Then
For Y = (Faction(0).Unit(Q).TileY - 2) To (Faction(0).Unit(Q).TileY + 2)
For X = (Faction(0).Unit(Q).TileX - 2) To (Faction(0).Unit(Q).TileX + 2)
If GridWorld(X, Y).FOV = 0 Then GridWorld(X, Y).FOV = 1
GridWorld(X, Y).FOW = 1
Next
Next
End If
Next
End Sub
Public Sub Grid_Resize(ByVal Func As String)
Dim X, Y As Integer
Dim Q, I As Integer
If Func = "Minus" Then
If ZoomVar > 0.6F Then
ZoomVar -= 0.2F
'Tweaking Offset
OffSetX += 50
OffSetY += 220
End If
ElseIf Func = "Plus" Then
If ZoomVar < 2.4F Then
ZoomVar += 0.2F
OffSetX -= 50
OffSetY -= 220
End If
End If
For X = 0 To MaxGridX
For Y = 0 To MaxGridY
GridWorld(X, Y).SX = ((X - Y) * GridWidth / 2) * ZoomVar
GridWorld(X, Y).SY = ((X + Y) * GridHeight / 2) * ZoomVar
Next
Next
For I = 0 To MaxFactions
For Q = 0 To MaxUnitsPF
X = Faction(I).Unit(Q).TileX
Y = Faction(I).Unit(Q).TileY
Faction(I).Unit(Q).X = ((X - Y) * GridWidth / 2) * ZoomVar
Faction(I).Unit(Q).Y = ((X + Y) * GridHeight / 2) * ZoomVar
Faction(I).Unit(Q).TargetX = Faction(I).Unit(Q).X
Faction(I).Unit(Q).TargetY = Faction(I).Unit(Q).Y
Next
Next
End Sub
Almost done! Our additions and changes to our rendering algorithms
'Drawing Units
For Y = 0 To MaxFactions
For X = 0 To MaxUnitsPF
If Faction(Y).Unit(X).ID <> 0 Then
If X = UnitSelected And Y = 0 Then
'Here we draw the points around the unit where it can move (if mouse is over one of them it's drawn with a different color
For Z = 0 To 7
sRect = New Rectangle((11 + Z) * 64, 392, 64, 56) 'Y * 56
dRect = New Rectangle(GridWorld(Faction(Y).Unit(X).TileToM(Z).X, Faction(Y).Unit(X).TileToM(Z).Y).SX + OffSetX, GridWorld(Faction(Y).Unit(X).TileToM(Z).X, Faction(Y).Unit(X).TileToM(Z).Y).SY + OffSetY - 24 * ZoomVar, 64 * ZoomVar, 56 * ZoomVar)
If Z = UnitSelectedMouseHover Then
DrawTexturedQuad(sRect, dRect, 1472, 448, 1, 1, 1, 0, 0, 0.6)
Else
DrawTexturedQuad(sRect, dRect, 1472, 448, 1, 1, 1, 1, 1, 0.6)
End If
Next
'Here we draw tile animation underneath our selected unit
sRect = New Rectangle((UnitSelectF \ 4) * 64, 392, 64, 56) 'Y * 56
dRect = New Rectangle(Faction(Y).Unit(X).X + OffSetX, Faction(Y).Unit(X).Y + OffSetY - 24 * ZoomVar, 64 * ZoomVar, 56 * ZoomVar)
DrawTexturedQuad(sRect, dRect, 1472, 448, 1, 1, 1, 1, 1, 0.6) 'GridWorld(Faction(Y).Unit(X).TileX, Faction(Y).Unit(X).TIleY).Z
End If
'Drawing units
sRect = New Rectangle(Faction(Y).Unit(X).ID * 64, (Faction(Y).Unit(X).Frame \ 4) * 56, 64, 56) 'Y * 56
dRect = New Rectangle(Faction(Y).Unit(X).X + OffSetX, Faction(Y).Unit(X).Y + OffSetY - 24 * ZoomVar, 64 * ZoomVar, 56 * ZoomVar)
DrawTexturedQuad(sRect, dRect, 1472, 448, 1, 1, 1, 1, 1, 1) 'GridWorld(Faction(Y).Unit(X).TileX, Faction(Y).Unit(X).TIleY).Z
End If
Unit_M(X, Y)
Next
Next
If UnitSelectF < 40 Then 'Frames for tile underneath (These our divided by 4 to get our actual frame since we have 10 frames)
UnitSelectF += 1
Else
UnitSelectF = 3
End If
That was it, a long post! You should have the following result when you implement the above to our strategy game.
On our Strategy Struct we created the code for spawning units, checking their nearby available to move tiles, handlers for mouse moving and mouse clicking and a simple algorithm to move our unit to the target tile. (this one gets called in our render code at this line : Unit_M(X, Y))
We updated our Grid module for calculation for Field of view and fog of war, added handlers that connect mouse move and click between modules.
And lastly we updated our rendering code to draw our unit with it's frames and the tile overlays for selecting target.
I know it was a long post with lots of coding lines so if you have questions about something just comment below!
Comments
Post a Comment