3D_for_GML:_Multiple_floors - hpgDesigns/hpgdesigns-dev.io GitHub Wiki

1.1 Using sprites to create levels

In previous tutorials, like ‘Platforms’, ‘Elevators’ and ‘Stairs & Ramps’, we have seen that a player can jump up to reach higher objects. Let’s see another popular example of this where the player can walk up or down stairs to reach multiple floors. The floors will be made up out of several tiles so that you can create interesting levels. To construct the levels we will use a simple method that involves placing objects with sprites in the room editor window. Anyone can do that, right? For the actual floors, three in total, we will use the following sprites: Of course, you can probably come up with more advanced ways of designing and creating levels but for the purpose of this tutorial, it will be adequate. Our three floors will be connected by stairs, two to be exact, using the following sprites: Both these stairs will use a collision mask sprite that look_s like this: And then, there’s the walls, facing in four directions (where the purple colour is used as transparent background): Last but not least the character object has a sprite. Let’s see how these sprites are put together to create a level with multiple floors. When placed in the room editor window, it will look something like this: The grey floor objects are placed in such a way that together they form a room. The yellow floor objects are placed on top to make a second floor and so on. There are open areas where the stair objects have been placed.

1.2 Drawing floors and ceilings

The floor objects have a script in their Creation event that holds the following code:

z=argument0;                                            //set floor height
tex_ceiling=background_get_texture(bac_Ceiling);        //set ceiling texture
tex_floor=background_get_texture(bac_floor);            //set floor texture
use=instance_create(x,y-16,obj_wall_s);                 //create wall facing south
use.z=z;                                                //set wall height
use=instance_create(x,y+16,obj_wall_N);                 //create wall facing north
use.z=z;                                                //set wall height
use=instance_create(x+16,y,obj_wall_w);                 //create wall facing west
use.z=z;                                                //set wall height
use=instance_create(x-16,y,obj_wall_E);                 //create wall facing east
use.z=z;                                                //set wall height

In the first line of this script, the floor height is determined by argument0that is set in the drag-and-drop action in the Object Properties window (Create event). This way each floor’s z value can be set at creation. Next, the textures to be used for drawing the ceiling and floors are set. Finally, four walls are created, each facing in a different direction. This means that each floor tile is surrounded by four walls. All walls are drawn using the parent object named par_floor, executing the following code in the Draw event:

if distance_to_object(obj_Character)>512 then {exit;}   //only draw when
near
if z>obj_Character.z+32 then                            //only draw ceilings above character
{
d3d_transform_set_identity();
d3d_transform_add_rotation_x(180);
d3d_transform_add_translation(x,y,z);
d3d_draw_floor(-32,+32,0, +32,-32,0, tex_ceiling,2,2);  //draw ceiling
d3d_transform_set_identity();
}
else
if z<obj_Character.z+32 then                            //only draw floors below character
{d3d_draw_floor(x-32,y+32,z, x+32,y-32,z, tex_floor,4,4);} //draw floor

The first line makes sure only floors are drawn that are near enough to be visible in the fog (that is set in the ini_Camerascript, by the way). The next lines make sure that only the ceilings above the character and floors below the character are drawn: no need drawing stuff you can’t see. A final note on the floors: all floors use the same sprite mask, namely: spr_floor1 so that collisions work the same, no matter what sprite image is used.

1.4 Drawing stairs

To get from one floor to the next, we will use stair objects (obj_stairs1 and obj_stairs2). They are set up in the ini_stairsscript with this code:

z=argument0;
tex=background_get_texture(bac_stairs);

Here you can see that the z value is set (in the Object Properties window’s Create event). Also, the texture of the stairs is set using a background image. In the same Object Properties, notice the sprite mask that is used is: spr_stairMask. Notice also that a parent is used: par_stairs which draws all stairs in the Draw event. The script used to draw the stairs is dra_stairs which holds this code:

if distance_to_object(obj_Character)>512 then {exit;}       //only draw when near
for (zp=0;zp<64;zp+=16;)
{
d3d_draw_block(x-64+zp*2,y+32,z+1, x-64+zp*2-8,y-32,z+zp+15, tex,1,3);
d3d_draw_block(x-64+zp*2-8,y+32,z+zp+16, x-64+32+zp*2,y-32,z+zp+12,tex,3,3);
}

Again, the first line makes sure stairs are only drawn when near enough for the player to see. Next, the wooden steps of the stairs are drawn using a loop.

1.5 Using level generation trick_s

If you take a look at the Object Properties of any wall object (like obj_wall_E), you will see that it is set to collide with other walls and also with stairs. When a wall object collides (using its sprite mask) with another wall, it executes the script scr_wallDestroy:

if z=other.z then {instance_destroy();}

This way unwanted walls will be removed, leaving us with a clear level surrounded by walls. You can see the same happens when a wall collides with a stairs object, executing the scr_stairs_open:

if z=other.z+64 then {instance_destroy();}

This way unwanted walls will be removed at the top of stairs. This enables the character to walk through the ceiling onto the next floor, without bumping into any walls that were automatically placed there. You can create or add your own level creation trick_s so that creating a level is less work, more fun, basically.

1.6 Walking on stairs and floors

So, how do we get a character to actually walk up the stairs and onto the floors? For the sake of simplicity, we will leave out the effects of gravity. If you take a look at the Object Properties of the character object (obj_Character), you will notice a lot of collision events. Let’s walk through them. When the character collides with floors, the script scr_walk_floorsis executed:

if abs(z-other.z)<32 then {z=other.z;}

Roughly, what this does is check to see what floor the character is walking on and making sure it stays on that floor. When the character collides with stairs, the script scr_walk_stairsis executed:

oldz=z;
newz=other.z+(x-other.x)/2+32;
if abs(oldz-newz)>64 then {exit;}
else
{z=newz;}

This moves the character up the stairs and eventually onto the floor above (or down the stairs onto the floor below). It makes sure the character can only walk up stairs that are actually on his floor. Finally, when the character collides with a wall, it simply bounces against it. That concludes this tutorial. As you may have noticed, this tutorial does not deal with the walking and looking around as this has been covered in previous tutorials.