#12 (Sticking in between rooms.) – HoverRace

Ticket #12 (assigned defect)

Opened 2 years ago

Last modified 13 months ago

Sticking in between rooms.

Reported by: Austin Owned by: Austin
Priority: trivial Milestone: HoverRace 1.24
Component: Client Version:
Keywords: sticking rooms Cc:
Blocking: Blocked By:

Description

One issue with HoverRace is the ability to get stuck at the exact point where rooms join; the only way to get out is normally to change direction or brake.

Change History

Changed 2 years ago by zoogie

Is there a specific track where this happens a lot?

Changed 2 years ago by Austin

It generally happens in Puck You[1-1666] or NHL Hockey around the goal area's.

Its hard to re-emulate, but its where the rooms join at the corner (between 4 rooms).

Changed 21 months ago by ryan

  • version changed from 1.23 to 1.24

Changed 21 months ago by anonymous

  • version 1.24 deleted
  • milestone set to HoverRace 1.24

Changed 13 months ago by Austin

I believe this is caused by the way a player is deemed to be able to leave a room.

If a player is in room 1, and adjoining rooms are 2(north)/3(east), if 2/3 are joined by 4 (north-east from 1), when the player crosses into 4 directly over the edge of 2/3, he is judged to pass into one of 2/3 and so quick is the way he enters 4, that he is deemed to be in 2/3 still and is then judged to be in a dead void area.

He has to then aim back into the room he is supposedly in in order to become unstuck.

Might be completely wrong, but that is my take on this :).

Changed 13 months ago by Austin

After inserting many breakpoints to see how HoverRace goes through room switching and collision, I have finally found the point at which sticking between rooms happens.

Level.cpp at line 598

int MR_Level::FindRoomForPoint(const MR_2DCoordinate & pPosition, int pStartingRoom) const
{
	int lReturnValue = -1;

	// Verify if the position is included in the current section
	if(MR_GetPolygonInclusion(SectionShape(&mRoomList[pStartingRoom]), pPosition)) {
		lReturnValue = pStartingRoom;
	}
	else {
		// Verify neighbor rooms
		for(int lCounter = 0; lCounter < mRoomList[pStartingRoom].mNbVertex; lCounter++) {
			int lNeighbor = mRoomList[pStartingRoom].mNeighborList[lCounter];

			if(lNeighbor != -1) {
				if(MR_GetPolygonInclusion(SectionShape(&mRoomList[lNeighbor]), pPosition)) {
					lReturnValue = lNeighbor;
					break;
				}
			}
		}
	}

	/*
	   if( lReturnValue == -1 )
	   {
	   lReturnValue = pStartingRoom;
	   }
	 */

	return lReturnValue;
}

If you manage to enter a room that is NOT considered a neighbouring room, it will return -1.

Changed 13 months ago by Austin

  • status changed from new to assigned
  • owner set to Austin

Changeset [642] see's the implementation of checking the players location in comparison to the neighbours neighbouring rooms. If he is deemed to be in one of them, that is what it returns.

Changed 13 months ago by Austin

Evan suggested the best way to do this: it would be to consider any room sharing a node to be a neighbor. Rather than relying on neighbors neighbors.

I'll have a look how neighbors are assigned and attempt to figure this out for myself. If not I will unassign myself.

Changed 13 months ago by Austin

The neighbors lists are created upon compiling of the track. No further calculation is made when the track is loaded into the client and there is no list of connecting nodes, therefore the neighbors neighbor fix is something that will have to do for now.

The scenario in which this would not work goes like this....

_____________
|   \ 2  /   |
| 1  \  / 3  |
|_____\/_____|
|     /\     |
| 4  /  \ 6  |
|___/_5__\___|

If a player were to cross from 1 into 6, he would get stuck, in comparison to pre-[642] where travelling into 3, 5 or 6 would get you stuck.

I suppose in a way that this is a very small chance, and I don't really know many people who design levels like this anyway.

Changed 13 months ago by Evan

I wrote a pseudo-code c# implementation for this method. (My C++ is a bit rusty) I tried to maintain performance, while still accounting for every possible boundary-crossing scenario.

-E 1-940

public static Room GetRoomForPoint(Point point, Room startingRoom, List<Room> searched)
{
    // If the craft is in this room, just return it.
    if (startingRoom.ContainsPoint(point))
    {
        return startingRoom;
    }

    // Check the neighboring rooms
    foreach (Room neighbor in startingRoom.Neighbors)
    {
        // If searched is null, this is the first room, so we'll put off memory allocation until after we've searched the neighbors.
        // If searched is not null, this is not the first room we've checked, so make sure we haven't already checked each neighbor.
        if ((searched == null || !searched.Contains(neighbor)) && neighbor.ContainsPoint(point))
        {
            // We found it!  Make sure to deallocate the searched list if we had one.
            if (searched != null)
            {
                //De-allocate searched list (sucks to be writing C++ and not have a garbage collector)
            }
            return neighbor;
        }
    }

    // If this is the first room checked, we'll need a searched list.
    if (searched == null)
    {
        searched = new List<Room>();
    }
    // We already checked the neighbors of this room, so add them right away
    searched.AddRange(startingRoom.Neighbors);

    // Recursively search each neighbor room
    foreach (Room neighbor in startingRoom.Neighbors)
    {
        Room room = GetRoomForPoint(point, neighbor, searched);
        if (room != null)
        {
            //De-allocate searched list (still sucks to be writing C++)
            return room;
        }
    }
    return null;
}

Changed 13 months ago by Evan

I wrote a pseudo-code c# implementation for this method. (My C++ is a bit rusty) I tried to maintain performance, while still accounting for every possible boundary-crossing scenario.

(This is refactored from the above code to fix a bug, improve performance and readability, and to fit in the narrow code field. Please remove the previous post if possible.)

public static Room GetRoomForPoint(Point point, Room startingRoom)
{
    // If the craft is in this room, just return it.
    if (startingRoom.ContainsPoint(point))
    {
        return startingRoom;
    }

    // Search the neighboring rooms
    foreach (Room neighbor in startingRoom.Neighbors)
    {
        if (neighbor.ContainsPoint(point))
        {
            return neighbor;
        }
    }

    // We didn't find it yet.  Allocate a list of searched rooms.
    List<Room> searched = new List<Room>();

    //Recursively search the neighbors.
    Room room = 
        GetRoomForPointRecursive(point, startingRoom, searched, 0);

    // TODO: De-allocate memory for searched list
    return room;
}

private static Room GetRoomForPointRecursive(
    Point point, Room startingRoom, List<Room> searched, int depth)
{
    // If depth is < 2, the non-recursive method already checked this room.
    // If the craft is in this room, just return it.
    if (depth > 1 && startingRoom.ContainsPoint(point))
    {
        return startingRoom;
    }

    searched.Add(startingRoom);

    // Search the neighboring rooms recursively
    foreach (Room neighbor in startingRoom.Neighbors)
    {
        // Make sure we haven't searched it already
        if (!searched.Contains(neighbor))
        {
            Room room = GetRoomForPointRecursive(
                point, neighbor, searched, depth + 1);
            if (room != null)
            {
                // Found it!
                return room;
            }
        }
    }
    return null;
}

//-E 1-940
Note: See TracTickets for help on using tickets.