On the last post before the sprite sheet overhaul we spawned our settlers on the map, so it's only logical for this post to be about creating a city, addressing some custom resolution issues and creating buttons for our game.
Moving from fixed resolution to custom resolution
While I'm writing this post, I realize that I created my UI sheets having in mind my monitor's resolution which is 1920 x 1080, so I didn't actually calculate different monitor sizes. What does that mean? Well let's say I run the application on my laptop which has a resolution of 1280 x 720 then our UI will look all weird, or a part of it will probably be drawn out the screen. We need to fix that if we want other things to work proper (like our buttons. Our tiles and mouse clicking will run proper though since we calculate everything based on camera zoom in/out - or a scaling float that doesn't care about screen resolution).
The problem can be seen on the image above (UI is off screen). So let's create a public float which will hold our screen to drawing ratio or just add the equation straight to the code, and we go from there to changes and implementing buttons.
Public ScreenWidth As Integer = F_New.GLHandler.Width 'Where F_New is the form my GLControl is on
Public ScreenHeight As Integer = F_New.GLHandler.Height 'If you are running a console application these are Screen.PrimaryScreen.Bounds .Width and .Height
Let’s take for example the sidebar to create our custom ration.
It was created for a screen of 960 x 540 (real size) and we double it’s projection size on our screen since we are running our game on a 1920 x 1080 size screen. Till now we use a ratio of W = 1920/960 and H = 1080/540 which is x2 and x2 for width and height.
Now, what if our target screen is let’s say 1220 x 700? I know there is no such screen resolution but maybe someone runs it on a custom GLControl size.
We need a custom ration of W = 1220/960 and H = 700/540 so instead of multiplying by 2 we'll multiply by roughly 1.27 for width and 1.29 for height.
Public UISizeW As Integer = 960
Public UISizeH As Integer = 540
So wherever we have *2 on our drawing UI code we change it to the following for width and height
'Top Border
sRect = New Rectangle(5, 8, 140, 5)
dRect = New Rectangle(0, 0, ScreenWidth - 140 * (ScreenWidth / UISizeW), sRect.Height * (ScreenHeight / UISizeH))
DrawTexturedQuad(sRect, dRect, 800, 548, 3, 1.0F, 1, 1, 1, 1)
'Bottom Border
sRect = New Rectangle(5, 8, 140, 5)
dRect = New Rectangle(0, (ScreenHeight - sRect.Height * (ScreenHeight / UISizeH)), ScreenWidth - 140 * (ScreenWidth / UISizeW), sRect.Height * (ScreenHeight / UISizeH))
DrawTexturedQuad(sRect, dRect, 800, 548, 3, 1.0F, 1, 1, 1, 1)
'Main UI Screen
sRect = New Rectangle(140, 8, 140, 540)
dRect = New Rectangle(ScreenWidth - sRect.Width * (ScreenWidth / UISizeW), 0, sRect.Width * (ScreenWidth / UISizeW), sRect.Height * (ScreenHeight / UISizeH))
DrawTexturedQuad(sRect, dRect, 800, 548, 3, 1.0F, 1, 1, 1, 1)
'Left border
sRect = New Rectangle(0, 8, 5, 540)
dRect = New Rectangle(0, 0, sRect.Width * (ScreenWidth / UISizeW), sRect.Height * (ScreenHeight / UISizeH))
DrawTexturedQuad(sRect, dRect, 800, 548, 3, 1.0F, 1, 1, 1, 1)
'Here is the minimap drawing code
'Main UI Overlap
sRect = New Rectangle(0, 8, 140, 540)
dRect = New Rectangle(ScreenWidth - sRect.Width * (ScreenWidth / UISizeW), 0, sRect.Width * (ScreenWidth / UISizeW), sRect.Height * (ScreenHeight / UISizeH))
DrawTexturedQuad(sRect, dRect, 800, 548, 3, 1.0F, 1, 1, 1, 1)
Here you'll notice that the sidebar and the borders are drawn proper but the minimap is not.
Public GridMiniSizeW As Single = 1Public GridMiniSizeH As Single = 1
We split the GridMiniSize variable into W and H ones and on our Public Sub GridCalls(ByVal Func As String) we change these to the following
GridMiniSizeW = (132 * (ScreenWidth / UISizeW)) / MaxGridX
GridMiniSizeW = (132 * (ScreenHeight / UISizeH)) / MaxGridX
We also change the code for drawing the mini-map to the following.
'DRAWING MINI-MAP
For X = 0 To MaxGridX
For Y = 0 To MaxGridY
If GridWorld(X, Y).FOV = 1 Then
sRect = New Rectangle(GridWorld(X, Y).ID * 8, 0, 8, 8)
'dRect = New Rectangle(ScreenWidth - 140 * 2 + 4 * 2 + X * GridMiniSize, 5 * 2 + Y * GridMiniSize, GridMiniSize, GridMiniSize)
dRect = New Rectangle(ScreenWidth - 140 * (ScreenWidth / UISizeW) + 4 * (ScreenWidth / UISizeW) + X * GridMiniSizeW, 5 * (ScreenHeight / UISizeH) + Y * GridMiniSizeH, GridMiniSizeW, GridMiniSizeH)
If (GridWorld(X, Y).SX + OffSetX) > -GridWidth And (GridWorld(X, Y).SX + OffSetX) < (ScreenWidth - 120) And (GridWorld(X, Y).SY + OffSetY) >= -GridHeight And (GridWorld(X, Y).SY + OffSetY) < ScreenHeight + 40 Then
DrawTexturedQuad(sRect, dRect, 800, 548, 3, 1.0F, 1, 1, 1, 1)
Else
DrawTexturedQuad(sRect, dRect, 800, 548, 3, 1.0F, 0.4, 0.4, 0.4, 1)
End If
End If
If X = GridPX And Y = GridPY Then
DrawQuad(ScreenWidth - 140 * 2 + 4 * 2 + X * GridMiniSizeW, 5 * 2 + Y * GridMiniSizeH, GridMiniSizeW, GridMiniSizeH, 1.0F, 1.0F, 1, 0.4, 1)
End If
Next
Next
Creating a button and making it build a city from our settler
Before we jump into creating a city we'll need to have some buttons in order to tell our unit what to do (that's why I didn't go into changing UI again - this post will have lots of additions to it). So I'll implement code that will handle buttons on the screen, and I'll create buttons for disposing our unit, passing it's turn, special unit function (if it has one) and get into a state of defence.
I'm
starting with creating buttons cause we'll have a lot of them from now
on. Now what's the logic behind a button in an openGL window?
- We need to create a sub to handle a call to it from a mouse_down event
- That sub will check where the mouse clicked and accordingly pass it to a proper result
- Since we'll have cities and settings windows or whatever else we want we'll have to create a variable which will hold which window state our game is in (example : diplomacy menu is open so the rest are closed)
How will our button structure work?
Now in order for our buttons to work proper we'll use the 0-1 float system of openGL. What does that mean? Since I created my UI in a 1:1 resolution of 960x540 as I explained on the resolution above, I'll calculate button positions based on that scale and automatically transform them into my actual screen resolution based on that float.
Let's create our special function button (It will spawn a city from a settler)
- You'll see on our button sub which we'll call at application start up we create our buttons based on our 960x540 and it translates them to floats right after.
- On our hover we find which of the buttons we created we hover over which we'll call on our mouse_hover event
- And on our click what happens when each button is clicked, for now it checks if we are over our build city button and we have a settler as current unit on the map
Dim Text As String
Dim X As Single
Dim Y As Single
Dim Width As Single
Dim Height As Single
Dim Enabled As Int16 'Specifies if it's enabled
Dim UIState As Integer 'Specifies in which UI windows state it's enabled
End Structure
Public SBtnNumber As Integer = 20 'Maximum number of buttons
Public SButton(SBtnNumber) As NullButtons 'Our buttons array
Public SUIWindow As Integer 'Which UI window is open
Public SButtonHover As Integer 'Which button is our mouse hovering over
Public Sub GameButtons(ByVal Func As String)
Dim I, Q As Integer
If Func = "CREATE" Then
SButton(1).Text = "Special unit function" 'This will be for a button description which we'll create later on
SButton(1).Enabled = 1
SButton(1).X = 857 : SButton(1).Y = 467
SButton(1).Width = 15 : SButton(1).Height = 15
For I = 0 To SBtnNumber
SButton(I).X /= 960 : SButton(I).Width /= 960
SButton(I).Y /= 540 : SButton(I).Height /= 540
Next
ElseIf Func = "HOVER" Then
Q = 0
For I = 0 To SBtnNumber 'Looping through our button array
'Now checking if our Mouse X,Y (translated also unto floats) are inside any button's borders (X,Width - Y,Height)
If (MouseX / ScreenWidth) >= SButton(I).X And (MouseX / ScreenWidth) <= (SButton(I).X + SButton(I).Width) And (MouseY / ScreenHeight) >= SButton(I).Y And (MouseY / ScreenHeight) <= (SButton(I).Y + SButton(I).Height) Then
If SButton(I).Enabled = 1 Then
Q = I 'Found that X,Y is over a button
End If
End If
If Q <> 0 Then 'A button was found - 0 will assign to no button and our array (0) will remain empty
SButtonHover = Q
Exit For
Else
SButtonHover = 0
End If
Next
ElseIf Func = "CLICK" Then
If SButtonHover = 1 Then 'If the mouse is over Special unit function
If Faction(0).Unit(UnitSelected).ID = 1 Then 'If that unit is a settler -> create a city
For I = 0 To MaxCitiesPF
If Faction(0).City(I).Enabled = 0 Then
City_M("CREATE", I, 0, Faction(0).Unit(UnitSelected).TileX, Faction(0).Unit(UnitSelected).TileY)
Exit For
End If
Next
End If
End If
End If
End Sub
If func = "CREATE" Then 'Create a simple city
Faction(FactionN).City(ID).Enabled = 1
Faction(FactionN).City(ID).Name = "Test City"
Faction(FactionN).City(ID).TileX = X
Faction(FactionN).City(ID).TileY = Y
Faction(FactionN).City(ID).Population = 50
Faction(FactionN).City(ID).Prosperity = 1
Faction(FactionN).City(ID).Luxuries = 0
Faction(FactionN).City(ID).Unemployment = 0
Faction(FactionN).City(ID).Civility = 100
Faction(FactionN).Unit(UnitSelected).ID = 0
End If
End Sub
If SButton(SButtonHover).UIState = SUIWindow Then 'If the button we hover over is at the same UI window
sRect = New Rectangle(SButton(SButtonHover).X * ScreenWidth, SButton(SButtonHover).Y * ScreenHeight, SButton(SButtonHover).Width * ScreenWidth, SButton(SButtonHover).Height * ScreenHeight)
DrawQuad(sRect.X, sRect.Y, sRect.Width, sRect.Height, 1.0F, 0.5F, 0.35F, 0.4F, 0.65F)
End If
End If
Dim Enabled As Int16
Dim TileX As Integer
Dim TileY As Integer
Dim Name As String
Dim Population As Integer
Dim Prosperity As Integer
Dim Luxuries As Integer
Dim Unemployment As Integer
Dim Civility As Integer
End Structure
Somewhere on our module we declare Public MaxCitiesPF As Integer = 50 'Cities cap (For each faction)
All the above are done the exact same way we created our units in our faction, so we'll redim our cities inside our Faction_F sub just after we redim our unit we continue to ReDim Faction(I).City(MaxCitiesPF)
The following code goes into our Fog of war calculation right after calculating it for our units
For Q = 0 To MaxCitiesPF
If Faction(0).City(Q).Enabled = 1 Then
For Y = (Faction(0).City(Q).TileY - 3) To (Faction(0).City(Q).TileY + 3)
For X = (Faction(0).Unit(Q).TileX - 3) To (Faction(0).Unit(Q).TileX + 3)
If X >= 0 And X <= MaxGridX And Y >= 0 And Y <= MaxGridY Then
If GridWorld(X, Y).FOV = 0 Then GridWorld(X, Y).FOV = 1
GridWorld(X, Y).FOW = 1
End If
Next
Next
End If
Next
And lastly our city rendering code (Faction colors is an array I created for testing some variations on the RGB float you can just remove it for now)
'Drawing Cities
For Y = 0 To MaxFactions
For X = 0 To MaxCitiesPF
If Faction(Y).City(X).Enabled = 1 Then
DrawText(Faction(Y).City(X).Name, GridWorld(Faction(Y).City(X).TileX, Faction(Y).City(X).TileY).SX + OffSetX - 16 * ZoomVar, GridWorld(Faction(Y).City(X).TileX, Faction(Y).City(X).TileY).SY + OffSetY - 20 * ZoomVar, 1.1 * ZoomVar, 1, 0.21 - FactionColors(Y).R, 1 - FactionColors(Y).G, 0 + FactionColors(Y).B, 1)
Dim CityPopulation As Integer = (51 + (Faction(Y).City(X).Population \ 20000)) '51 refers to our first city tile on our tilesheet, and every 20000 population it will become bigger
TexY = CityPopulation \ 17
TexX = CityPopulation - TexY * 17
sRect = New Rectangle(TexX * 64, TexY * 40, 64, 40)
dRect = New Rectangle(GridWorld(Faction(Y).City(X).TileX, Faction(Y).City(X).TileY).SX + OffSetX, GridWorld(Faction(Y).City(X).TileX, Faction(Y).City(X).TileY).SY + OffSetY - 8 * ZoomVar, 64 * ZoomVar, 40 * ZoomVar)
DrawTexturedQuad(sRect, dRect, 1088, 160, 0, 1.0F, 1, 1, 1, 1)
End If
Next
Next
That was it! Now we have buttons and our first city.
Comments
Post a Comment