RGBSurface.Pixel Reporting Wrong Colour?

Wow loads of help. Thank you!

TLDR - I’ve solved it.

As @MarkusR suggested, I decided to try to use maths to solve the problem rather than relying on a special tile. The advantage is that I can change the tile dimensions in my graphics editor and don’t have to remember to create a new “mouse map” image.

I think the suggestion by @Eric_Williams would likely work and is smart. Thanks @anon20074439 for the explanation of what was going wrong.

For those that are interested in hex maps what I did, is as follows:

First the screen is internally carved up into MouseCells (drawn in red):

The coordinates in red at the top of the red cells are the “mouse map” (row, column). The black coordinates in the centre of the hexes are actual in-world (row, column) coordinates.

We can see from the picture that if we know the row and column of a MouseCell we can compute the in-world tile index by adding a delta value to the MouseCell.Row and MouseCell.Column. The delta to add depends which part of the MouseCell the cursor is in:

First thing we need to do is convert the X,Y coordinates in the mouse map to local coordinates within a cell. We do this by using Mod with either column * cellWidth (for the X) or row * cellHeight (for Y). Incidentally, Mod is a great tool for wrapping a value into a range. Once we have X and Y for the cell (localX and localY) we can start determine which region of the cell that point is in.

Each MouseCell can be thought of as a hexagon with four surrounding regions. We need to determine which area (1 to 4) each point in the cell is in. There are four triangles and two rectangles to check:

In order to check if our local point is within one of the triangles or lower rectangles, we need the vertices (points):

Fortunately the vertices are easy to compute:

Var v1 As New Point(cellWidth / 2, 0)
Var v2 As New Point(cellWidth, tileHeight * 0.25)
Var v3 As New Point(cellWidth, tileHeight * 0.75)
Var v4 As New Point(cellWidth / 2, tileHeight)
Var v5 As New Point(0, tileHeight * 0.75)
Var v6 As New Point(0, tileHeight * 0.25)
Var va As New Point(0, 0)
Var vb As New Point(cellWidth, 0)
Var vc As New Point(cellWidth, tileHeight)
Var vd As New Point(0, tileHeight)

Now we can loop through every point in the mouse cell and determine which region it is and, therefore, what the delta to apply is:

// Loop through every point within the mouse map cell and determine
// which region it's in.
For x As Integer = 0 To cellWidth - 1
  For y As Integer = 0 To cellHeight - 1
    Var p As New Point(x, y)
    // We must check the triangles first, then the centre and then 
    // the rectangles.
    If PointInTriangle(p, va, v1, v6) Then
      // Top left triangle (4).
      mMouseMap(x, y) = New Delta(-1, -1)
    ElseIf PointInTriangle(p, v1, vb, v2) Then
      // Top right triangle (1).
      mMouseMap(x, y) = New Delta(-1, 0)
    ElseIf PointInTriangle(p, v4, v3, vc) Then
      // Bottom right triangle (2).
      mMouseMap(x, y) = New Delta(1, 0)
    ElseIf PointInTriangle(p, v4, v5, vd) Then
      // Bottom left triangle (3).
      mMouseMap(x, y) = New Delta(1, -1)
    ElseIf y <= tileHeight Then
      // Centre.
      mMouseMap(x, y) = New Delta(0, 0)
    ElseIf x <= cellWidth / 2 Then
      // Bottom left rectangle (3).
      mMouseMap(x, y) = New Delta(1, -1)
    Else
      // It's in the bottom right rectangle (2).
      mMouseMap(x, y) = New Delta(1, 0)
    End If
  Next y
Next x

Before you ask, mMouseMap() is a two dimensional array (mMouseMap(localX, localY)) where the value of each element is the delta to apply for both the row and the column. This is stored in the class Delta:

Class Delta
  Property Row As Integer
  Property Column As Integer

  Sub Constructor(row As Integer, column As Integer)
    Self.Row = row
    Self.Column = column
  End Sub
End Class

The PointInTriangle method determines if a point is within a triangle by checking which side of the half-plane created by the edges the point is:

Function PointInTriangle() As Boolean
  Var d1, d2, d3 As Double
  Var has_neg, has_pos As Boolean

  d1 = Sign(p, v1, v2)
  d2 = Sign(p, v2, v3)
  d3 = Sign(p, v3, v1)

  has_neg = (d1 < 0) Or (d2 < 0) Or (d3 < 0)
  has_pos = (d1 > 0) Or (d2 > 0) Or (d3 > 0)

  Return Not (has_neg And has_pos)
End Function

The Sign() function is a little helper:

Function Sign(p1 As Point, p2 As Point, p3 As Point) As Double
  Return (p1.X - p3.X) * (p2.Y - p3.Y) - (p2.X - p3.X) * (p1.Y - p3.Y)
End Function

At this point, we now have an array that we can quickly look up a local mouse cell coordinate and get the delta to apply. I create the mouse map array once when the game loads and that saves loads of maths on every mouse move.

1 Like