To bring life into the sample code I added a small function that provides random movement for the units. You don't have to understand the code but it's really easy. Basically it moves all units in their current direction and changes the direction from time to time. Here's the code:
Public Sub MoveRandomly() Dim A as Long Dim MoveX as Long Dim MoveY as Long For A = 2 To UnitCount 'Reset MoveX = 0 MoveY = 0 With Unit( A ) 'Set random direction If 100 * Rnd < 10 Then: .Direction = CLng( 3 * Rnd ) 'Get movement Select Case .Direction Case 0: MoveY = -1 'Up Case 1: MoveX = 1 'Right Case 2: MoveY = 1 'Down Case 3: MoveX = -1 'Left End Select 'Set new position MoveUnit A, ( .X + MoveX ), ( .Y + MoveY ) End With Next End Sub
Now that we have some movement it's time to implement the collision checks. Since we already made a MoveUnit function we can just extend it to check the requested position before moving the unit. Note that we have to do 2 different collision checks, unit-to-map and unit-to-unit. The first one checks if the tile is walkable and the second checks if there's already a unit registered. If one of the checks fails the function simply exits without moving the unit at all.
Here again I'll first show you the code, then go through it step by step:
Public Sub MoveUnit( Index as Long, NewX as Long, NewY as Long ) Dim TempIndex as Long 'Check boundaries If NewX < 0 Then: NewX = 0 If NewY < 0 Then: NewY = 0 If NewX > ( ( Map.W - 1 ) * TileSize ) Then: NewX = ( ( Map.W - 1 ) * TileSize ) If NewY > ( ( Map.H - 1 ) * TileSize ) Then: NewY = ( ( Map.H - 1 ) * TileSize ) 'Get new map index TempIndex = ( CLng( NewY / TileSize ) * Map.W ) + CLng( NewX / TileSize ) With Unit( Index ) If Not TempIndex = .MapIndex Then 'Unit-to-map collision check If Not Tile( Map.Floor( TempIndex ) ).Walkable Then: Exit Sub 'Unit-to-unit collision check If Map.Units( TempIndex ) > 0 Then: Exit Sub End If 'Unregister from map If .MapIndex > -1 Then: Map.Units( .MapIndex ) = 0 'Apply new position .X = NewX .Y = NewY .MapIndex = TempIndex 'Register to map Map.Units( .MapIndex ) = Index End With End Sub
Here we go.
Public Sub MoveUnit( Index as Long, NewX as Long, NewY as Long ) Dim TempIndex as Long 'Check boundaries If NewX < 0 Then: NewX = 0 If NewY < 0 Then: NewY = 0 If NewX > ( ( Map.W - 1 ) * TileSize ) Then: NewX = ( ( Map.W - 1 ) * TileSize ) If NewY > ( ( Map.H - 1 ) * TileSize ) Then: NewY = ( ( Map.H - 1 ) * TileSize )
This all stayed the same except that we need a tempoary index this time. Explanation following below.
'Get new map index TempIndex = ( CLng( NewY / TileSize ) * Map.W ) + CLng( NewX / TileSize )
Instead of just moving the unit and setting the new index we get the requested map index here. This is because we need to check that position first before unregistering or moving anything.
With Unit( Index ) If Not TempIndex = .MapIndex Then
This line checks if the unit moved to another map position (tile). Othewise we don't have to double-check the tile and can skip the following collision tests.
'Unit-to-map collision check If Not Tile( Map.Floor( TempIndex ) ).Walkable Then: Exit Sub
As the comment says this line checks the tile if it's walkable or not. If not we just exit the code without moving the unit at all.
'Unit-to-unit collision check If Map.Units( TempIndex ) > 0 Then: Exit Sub
This second check uses the units map to see if the new position already contains another unit. This is exactly the line what we made this map for.
If the unit didn't yet leave it's current tile, this check would fail returning the unit's index because at this point it's still registered to that tile! This is another reason for checking if the unit moved on to another tile or not.
End If 'Unregister from map If .MapIndex > -1 Then: Map.Units( .MapIndex ) = 0 'Apply new position .X = NewX .Y = NewY .MapIndex = TempIndex
Now that the new position is confirmed we can unregister the unit from the current position and set the new coodinates. Also we can now store the new map index and use it in the following line to register the unit at the new position:
'Register to map Map.Units( .MapIndex ) = Index End With End Sub
Finally the unit is re-registered at the new position.
Now all the work gets paid and we can use only 1 line to perform the whole unit-to-unit collision check. And since the tile map has the same size as the unit map we can do the unit-to-map collision check without even re-calculating the map index. At this point your engine provides more or less all the collisions you need so run the sample code to see it working.