Strategy game, isometric maps (textured) with OpenGL (Post #11 / Strategy game #2)

In order to create a 2.5D feel we'll transform our grid top-down view to an isometric projection.

Isometric grid

My second post on the strategy game will be a bit long since, I'll post the isometric map creation (with random elements), tile picking and drawing textured tiles with openGL.

Let's test our sprites, I've loaded my two sprite sheets on my project resources. I've added them here if you want to use them. (I used .PNG files with no background)

We'll create a diamond iso grid since it's more relatable to our square grid and easier to calculate movements in the long run. (But how do we create an isometric grid?)

We just project our square grid differently.


For Y = 0 To MaxGridY
                For X = 0 To MaxGridX
                    GridWorld(X, Y).X = (X) * GridWidth
                    GridWorld(X, Y).Y = (Y) * GridHeight
                    GridWorld(X, Y).SX = (X - Y) * GridWidth / 2
                    GridWorld(X, Y).SY = (X + Y) * GridHeight / 2
                Next
            Next

That means that our Square grid positions will be X * Width and Y * Height but our Iso grid positions are (X-Y) * Width/2 and (X+Y) * Height /2, explanation in the image below.

And our backwards to normal X,Y calculations will be as follows.

    Public GridPX As Integer 'Refers to Tile X not pixel X
    Public GridPY As Integer 'Refers to Tile Y not pixel Y
    Public Sub GLMouseMove(ByVal X As Integer, ByVal Y As Integer)

        GridPX = (X * 2 / GridWidth + Y * 2 / GridHeight) / 2
        GridPY = Y * 2 / GridHeight - GridPX
    End Sub

The calculations are simple backwards solving math for our two equations, explained in the next image.


Now we have our projection to isometric and mouse grid point detection ready, and in order to draw these tiles we'll use the following code. (We will add an offset X,Y value so we can move our map around) (I will post the full code in parts, if you can't understand these small adds between images).

Loading textures from images in our resources

   Public ImgTex(10) As Integer
    Sub LoadImgTex()
        GL.GenTextures(ImgTex.Length, ImgTex)
        Dim TextureLoadingBmp As Bitmap
        TextureLoadingBmp = My.Resources.TileSheetN
        LoadTexture(ImgTex(0), TextureLoadingBmp)
        TextureLoadingBmp = My.Resources.UnitSheet
        LoadTexture(ImgTex(1), TextureLoadingBmp)
    End Sub
     Sub LoadTexture(ByVal TexID As Integer, ByVal SFileName As Bitmap)
        Dim BitmapB As New Bitmap(SFileName)

        Dim ImagingData As Imaging.BitmapData = BitmapB.LockBits(New Rectangle(0, 0, BitmapB.Width, BitmapB.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly,
                                                System.Drawing.Imaging.PixelFormat.Format32bppArgb)
        GL.BindTexture(TextureTarget.Texture2D, TexID)
        GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, BitmapB.Width, BitmapB.Height, 0, PixelFormat.Bgra,
                      PixelType.UnsignedByte, ImagingData.Scan0)
        BitmapB.UnlockBits(ImagingData)
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, TextureMinFilter.Nearest)
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, TextureMagFilter.Nearest)
        GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, TextureWrapMode.Repeat)
        GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, TextureWrapMode.Repeat)
    End Sub

    Sub DrawTexturedQuad(ByVal sRct As Rectangle, ByVal dRct As Rectangle, ByVal W As Integer, ByVal H As Integer, ByVal Texture As Integer, ByVal Z As Double, ByVal R As Single, ByVal G As Single, ByVal B As Single)
        GL.Enable(EnableCap.Texture2D)
        GL.AlphaFunc(AlphaFunction.Gequal, 0.7F)
        GL.BindTexture(TextureTarget.Texture2D, ImgTex(Texture))
        GL.Begin(PrimitiveType.Quads)
        Dim Fx As Single = sRct.X / W
        Dim Fy As Single = sRct.Y / H
        Dim Fw As Single = (sRct.X + sRct.Width) / W
        Dim Fh As Single = (sRct.Y + sRct.Height) / H
        Dim Tx As Integer = dRct.X
        Dim Ty As Integer = dRct.Y
        Dim Tw As Integer = dRct.X + dRct.Width
        Dim Th As Integer = dRct.Y + dRct.Height
        GL.Color4(R, G, B, 1.0F)
        GL.TexCoord2(Fx, Fy) : GL.Vertex3(Tx, Ty, Z)
        GL.TexCoord2(Fw, Fy) : GL.Vertex3(Tw, Ty, Z)
        GL.TexCoord2(Fw, Fh) : GL.Vertex3(Tw, Th, Z)
        GL.TexCoord2(Fx, Fh) : GL.Vertex3(Tx, Th, Z)
        GL.End()
    End Sub

Drawing a textured quad (Quad since it's 4 vertices), and we follow the same technique with DrawColorQuad (transform screen X,Y to 0-1 float, now since we have a texture to apply we do the same transformation with our spritesheet)

Map Generation

Now we'll change our map generation algorithm (see previous posts), and create an algorithm designed around our tile and resource sheet.

Our algorithm will work like this
  • Clearing our map with 0 and 1
  • Creating worms with 4, 7 and 15 (These are our main tiles - snow, grass, desert) 
  • Creating some coast line (14
  • Changing water next to these tiles to 2 (shallow water) 
  • Pass through all our tiles and randomize depending on tile (example grass tiles might change to forest or mountain)
  • Add coastline finishing, husbandry, and other over-tiles
  • Generate resources

At the end of these steps we should get a result like the above image, random island generation with coasts  (we added a coastline which will have a changing alpha so it seems like a wave kinda) and resources (if the player wants them to show on map).

You can optimize your map algorithm, and maybe create more tiles (like corner tiles) so the map doesn't look so squared, depending on relations between nearby grids, or rivers creation. Just remember to add extra changes to base tile map before randomizing or resource adding.

Comments