Although it is possible, with what was shown until now, create scenarios like mazes, it would be complicated to create a side-scrolling game
in which the scenario follows the movements of the character we control just like it were using a camera.
It this tutorial we will discuss some extra features of the Scene class that: allow the use of scenarios that exceed the limits of the screen and
control the movement within the same.
1 - Tiles
As was described in the class tutorial, the scenarios are built with equal dimensions rectangles,
better know as tiles. The whole scenario is described basically in function of which tiles to use in what positions.
Note.: it is not recommended to use very small tiles, because they will make more difficult to treat collisions. In the example used to build this tutorial, the tile used had 30 x 32 pixels.
2 - A new scenario file
Following the same model of the previous tutorial, we will use a larger matrix.
The file is organized as follows:
Line 1 - number of images used to construct the scenario (the static images).
Line 2 - path of the first image and its extension
Line 3 - path of the second image and its extension
Line 4 - path of the third image and its extension
Line n+1 - path of the n-th image and its extension
Line n+2 - start of the construction of the matrix
Line m - end of the construction of the matrix that is representedby the '%' character.
Line m+1 - path of the backdrop and its extension.
It is important to notice that if, for example, the tile represented by the number 1 has 30 pixels of width by
32 pixels of height and the scenario file has a 44 columns and 30 lines matrix, we will have a scenario of 1320 pixels of width and 960 pixels of height.
Example of a '.scn' file.
2
tile.png
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
%
background.jpg
3 - Movement functions
To move a scenario according to a reference point and apply that offset to all dynamic objects we use the following functions:
//Draws the scenario tiles that are around the GameObject
//passed and in the limits of the window's dimensions.
public void moveScene(GameObject object);
//Returns the last scenario offset in the x axis.
public double getXOffset();
//Returns the last scenario offset in the y axis.
public double getYOffset();
4 - Centering an object.
To move the scenario as to centralize a determined object and pass that movement for all the dinamic objects, we use the previous funcionts as follows:
////Draws the scenario tiles that are around the player,
//and the updates its position with the scenario "offset".
scene.moveScene(player);
player.x += scene.getXOffset();
player.y += scene.getYOffset();
window.update();
Through this sequece of functions it is possible to create the illusion that there is a camera following the player, when in fact only the scenario is moving.
Obs.: As alredy said, it is necessary to apply the positioning update to each dinamic object, although in this case this was done only for the player.
5 - Defining traversable areas.
To make, for example, a platform game we have to deal with the collision that will happen between the character and the wall tiles:
We will continue following the same steps of the scenarios tutorial, but now with a new collision processing:
//for all tiles
for(int i = 0 ; i < tiles.size() ; i++)
{
TileInfo tile = (TileInfo)tiles.elementAt(i);
//if the tile is a wall and the player collided with him
if((tile.id == Constante.TILE_WALL) && player.collided(tile))
{
//The collision with the player was in the vertical?
if (VerticalCollision (player, tile))
{
if(tile.y + tile.height + player.getVelocityY() - player.getMovementSpeed() - 1 < player.y)
{
//the player is below the wall
player.y = tile.y + tile.height;//Repositions the player
player.setVelocityY(0.0);
}
else if (tile.y > player.y + player.height - player.getVelocityY() - player.getMovementSpeed() - 1)
{
//the player is above the wall
player.setVelocityY(0.0);
player.setFloor((int)tile.y);
player.y = tile.y - player.height ;//Repositions the player
}
}
//The player is moving in the horizontal? The collision was in the horizontal?
if((player.moveuNaHorizontal()) && (HorizontalCollision(player, tile))){
//the player is behind the wall?
if(player.x <= tile.x - player.getMovementSpeed()- 1)
player.x = tile.x - player.width;//Repositions the player
else
//the player is in front of the wall?
player.x = tile.x + tile.width;//Repositions the player
}
}
}
boolean VerticalCollision (GameObject object, TileInfo tile)
{
if (tile.x + tile.width <= object.x)
return false;
if (object.x + object.width <= tile.x)
return false;
return true;
}
boolean HorizontalCollision (GameObject object, TileInfo tile)
{
if (tile.y + tile.height <= object.y)
return false;
if (object.y + object.height <= tile.y)
return false;
return true;
}
The function getVelocityY() returns the vertical speed with which the player is moving, caused by the jump function (jump()).
The function getMovementSpeed() is not in the Sprite class and was implemented in this case to return how much the player moves by the use of controls.
The functions VerticalCollision() and HorizontalCollision() alone do not return the fact that a collision occurred or not in the vertical and horizontal.
First the collided() function must be used, that will return true if a collision has occurred and then use that functions to know from where the collision occurred.