Categories
Game Development

Oracle’s Eye Development: The Room 2

Last night’s Oracle’s Eye programming session was still productive, although not as much as I would have liked. I last had a Room that would get created, and the Player would be able to walk over it.

I wanted the Player and the Walls to be on the same hiearchical level, and the Floors should be underneath. That way, Floors can be added to Kyra‘s KrImageTree as much as I want without the Player getting covered.

I already knew that I could setup the Tree by creating KrImNodes to act as the root of subtrees. Normally if you just add nodes to the tree, the later nodes will cover the earlier ones. I could instead add nodes to the child nodes instead to control the hiearchy. For instance, I could create a background and a foreground by adding background and foreground KrImNodes:

KrImNode* background = new KrImNode;
KrImNode* foreground = new KrImNode;
engine_->Tree()->AddNode( 0, background );
engine_->Tree()->AddNode( 0, foreground );

AddNode() takes as arguments the parent node and the node you want to add. In the above example, I am adding two new nodes to the root node. Now I can put as many items in the background as I want, and I won’t have to worry about covering up anything in the foreground tree. I already used this technique before with my Game in a Day in June.

What I learned was that I could also name a node. Kyra doesn’t care about the name, but I found that I could name a node and than search for a node with that name.

KrImNode* background = new KrImNode;
KrImNode* foreground = new KrImNode;
background->SetNodeName( "background" );
foreground->SetNodeName( "foreground" );
engine_->Tree()->AddNode( 0, background );
engine_->Tree()->AddNode( 0, foreground );

Now if another section of my code doesn’t know the specific pointers to the nodes in question, I can still do a search and get the information I need. For instance, in my GameWorldFactory’s createRoom() function:

engine_->Tree()->AddNode(
engine_->Tree()->FindNodeByName( "background" ),
floor->getSprite()
);

The result is that the Player can walk over the FloorTiles and gets covered by the WallTiles if the Player sprite was added to the foreground before the Room’s Tiles were.

The cool thing is that the separation of the Tiles into the hierarchy also makes it a lot easier to do collision detection. My previous code to move the player:

void GameWorld::moveY( const double distance )
{
player_->moveY( distance );
}

My current code to move the player:

void GameWorld::moveY( const double distance )
{
engine_->Tree()->Walk();
//! Check if moving Player sprite would intersect a wall.
//! If so, do not move the Player.
player_->moveY( distance );
if ( engine_->Tree()->CheckSiblingCollision (
player_->getSprite(),
&spritesHit,
0
) )
{
//! Reverse direction to restore position.
player_->moveY( -distance );
}
}

Since the Player’s sprite and the WallTiles’ sprites are siblings, and the FloorTiles’ sprites aren’t, I can simply check for collision with the siblings. CheckSiblingCollision() returns true if there was a collision, and so what I do is move the Player back the same distance, which should place it where it was before the move.

Unfortunately, it doesn’t quite seem to work. The Player does obey the WallTiles, but it then gets stuck. I think the problem is the double precision used for the distance. The sprite makes use of integers to move, and so I am probably losing precision. When I move the player back, it might be just short of where it should be.

Before this week, I was looking at a single Tile and a Player that moved around it. Now I have an actual Room and simple, although flawed, collision detection to keep the Player in the Room.

I’m also wondering if PlayState should still own the Kyra engine. I think that maybe GameWorld should own it instead since it makes use of it more, but for now I at least know that the project is working. I can always optimize later, and in the meantime I am making steady progress.