1. Hey! Guest! The 31st GMC Jam will take place between Nov 16th, 12:00 UTC (Friday noon) and Nov 26th, 12:00 UTC (Monday noon). Why not join in! Click here to find out more!
    Dismiss Notice

Discussion [GUIDE] Getting Started with Room Layers (mostly new GML functions)

Discussion in 'GameMaker Studio 2 Community Tech Support' started by MaddeMichael, Nov 18, 2016.

  1. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    REMEMBER WHAT I SAID ABOUT TAKING OVER THE FORUM WITH MY GUIDES?

    ***NOTE: THIS GUIDE IS FOR THE BETA VERSION OF GAMEMAKER STUDIO 2. AS THE BETA CHANGES/UPDATES, SO MAY THIS GUIDE***

    Luckily, I will leave a (dd/mm/yy) formatted date here, so you know when it was last updated: 28/11/16 - Added layer position and speed functions
    Updated layer_create as layers can be created with names after all!
    Updated "depth = -y alternatives" with a link to @Ariak's faster BinaryList depth sorting!
    I'll also update the guide if there a better methods that I haven't thought of/found out about yet, or if it turns out I'm using some function totally inappropriately.

    GM Version: IDE: 2.0.2.44, RUNTIME: 2.0.1.27
    Target Platform: ALL
    Download
    : N/A
    Links: N/A

    As always, I recommend that you get @YellowAfterlife's plugin for forum code syntax highlighting. It makes reading code here way easier: https://yellowafterlife.itch.io/syntax-highlighting-for-gamemaker-forum

    If you get stuck, ask a question and check the documentation!


    Summary:
    Okay, here is my third guide for GMS2. This time I’ll be covering one of GameMaker Studio 2’s biggest features, and I believe last major change that I have not covered – room layers
    I asked if anyone wanted/needed this and I got a load of "Yes!"s, so here we are!

    Now, there are a few different types of layer introduced, so I’ll be breaking this guide down into the following parts:

    1. Layer Basics – The Room Editor
    2. General GML Layer Functions
    3. Instances and Layers – update “instance_create”
    4. Tile Layers – The Big Subject
    5. Assets and Backgrounds
    6. New Particle System Functions
    7. Layers and “depth = -y”

    I’ll basically be going over the most important GML functions that can be used with layers and their usage, as well as linking to the relevant documentation pages. There are Drag n’ Drop equivalents to some of the functions, but not all of them.

    I am not including a downloadable project for this guide – this is because you can see more advanced implementations of most of these features by looking at the demos included with GMS2, so there’s little point in having me writing a whole project. For example, tilemap collision is shown in both “YoYo Dungeon Lite” and “YoYo Platform Lite” – the latter shows examples of both using tiles for one-way ground collision and using tiles (from the same tileset) as ladders.

    Relevant Documentation Links:

    Tutorial:

    Layer Basics – The Room Editor
    It’s possible you may not need to read all this section. Some of this stuff explains itself, but I’ll cover enough stuff to minimise confusion and be as beginner friendly as possible. If you use the tutorials included with GMS2, you probably won’t even need this section.

    Layers, in their most basic form, are a new way of organizing content within a room, though they provide much more useful functionality which will make certain aspects of development easier and faster both in the IDE and at runtime.

    First, a brief coverage of the 5 different types of layers that you’ll see in the room editor:

    • Instance Layers
      • These layers contain instances and, in a way, replace depth.
    • Asset Layers
      • These layers a used to render sprites without having to create an object. This is handy if you have an animation that isn’t compatible with a tilemap (non-power of 2 frame count, can’t be snapped to a rigid grid), for example. Assets can also be colour blended and transformed in ways tiles can’t.
    • Tile Layers
      • One of the biggest changes for GMS2, the tile layers – these completely replace what you may have previously known tiles to be from previous iterations of GameMaker. These layers can be used for faster collision and much faster word building.
    • Background Layers
      • Backgrounds no longer exist as a resource in GMS2, but we still have background layers. These allow a sprite to be used in much the same way as backgrounds used to be used – but they can be animated now, as well as stretched AND tiled at the same time – which is good for moving some moving backgrounds.
    • Path Layers
      • Path layers are used to view and edit a path within a room. A path layer may only use one path at a time, and are only particularly useful when you are editing a path (as you get an in-room preview)
      • Path layers are only available in the IDE and cannot be used or referenced at runtime.
      • I don’t think I’ll cover these layers in detail, since they are basically the same as the path editor.

    So, that’s a summary of the layer types. Note that at runtime, layers do not have a specific type and can have any number of different elements dynamically added to a single layer. Now for some basic usage of layers in general. We’ll focus more specifically on each individual layer type later.

    Creating and deleting layers in the IDE is pretty straightforward

    You can simply open the room you wish to edit, and use these convenient buttons to create, delete and structure layers

    editorInfo3.png

    In order, from left to right, these buttons perform the following operations:

    • Create a background layer
    • Create an instance layer
    • Create a tilemap layer
    • Create a path layer
    • Create an asset layer
    • Create a layer folder
    • Delete the currently selected layer/folder
    • Toggle inheritence for all layers in the room

    When a layer is created, it is automatically placed above the currently selected layer.

    Layers can be reorganized just by dragging them to a new desired position in the list.

    Right clicking an existing layer can allow you to add “Sub Layers”, which seem to exist purely for organization purposes, as they do not effect layer usage at runtime.

    As another cool thing to note, while layers in the IDE can only contain one type of element, a single layer can contain many different types of element at runtime, assuming the elements are added dynamically through code. This means a single layer could contain backgrounds, tiles, sprites and instances all at once.

    Here’s a little image of what you’ll see in layer properties. I’ve labelled and outlined settings that appear in all different layer properties:

    editorInfo2.png

    Here’s what each thing means and does:

    1. Depth – like the previous iterations of GameMaker, this is the draw depth and z position of the layer. If the lock icon is highlighted, the IDE will automatically space layers 100 units apart, to try and reduce unexpected behaviour. To change the depth, you must first uncheck the lock.
    2. Layer depth can be inherited from parent rooms. If you change the depth of a parent, the child will change too. If you change the depth of the child, it will automatically stop inheriting parent values.
    3. This second inherit button works in much the same way as the depth inherit button, applies to all properties of the layer EXCEPT depth. Sometimes this button is at the top of layer properties (like here), though sometimes it appears at the bottom (e.g instance layer properties)

    Other properties can be edited within the layer list:

    editorInfo1.png
    1. A slow double click can be used to edit the layer name
    2. Clicking this toggles layer visibility for both the runtime and the IDE. The layer still exists, but it does not draw
    3. Clicking this can be used to lock layer properties – this can prevent accidental editing of layers, as you must manually unlock the layer to perform any edits.
    4. Right-clicking a layer brings up a drop-down list of other options. These include:
      • Renaming the layer
      • Deleting the layer
      • Duplicating the layer
      • Adding sub-layers
      • Tweaking inheritence

    General GML Layer Functions
    Let’s get on with some GML, shall we? A lot of these functions are covered within the documentation. Here’s a link to a list of general layer functions from the documentation: http://docs2.yoyogames.com/source/_build/3_scripting/4_gml_reference/rooms/layers/index.html

    Now, there are a lot of layer functions – and I mean a LOT, so I’ll I’m going to cover what I consider the most vital functions – Once you know these, you should be able to explore the rest without too many problems.

    The function you’ll probably find the most useful is this one:
    Code:
    layer_get_id(Layer Name);
    
    It takes the name of the layer as an argument and returns an ID for use in other functions.

    In general, I’d say it’s best to call this at room start and store the ID in a global variable to save time accessing the layer later.

    Sometimes, you may need to create or destroy a layer. This can be done with these respective functions:
    Code:
    layer_create(Depth, name [optional]);
    
    layer_destroy(Layer ID);
    
    “layer_create” does allow you to specify a layer name, however it returns the ID directly, so there is no need to call “layer_get_id”. Calling “layer_get_name” on an unnamed layer returns an empty string. If you try to call “layer_get_id” with an empty string, it will return -1. There is no “layer_set_name” currently.

    “layer_destroy” Removes a layer from the room and all its contents – tiles, instances, sprites etc. This is handy for removing whole groups of things you no longer need – just be careful to not accidentally delete a layer that contains important stuff!

    Another important function is
    Code:
    layer_exists(layer)
    
    This function takes either the layer name as a string, or the layer ID as a real number and returns true if the layer exists, and false if it does not. This is useful as a way of checking if a layer even exists before attempting to manipulate it in any way.

    If you need to show or hide a layer at runtime, perhaps for debug purposes, you can use this simple function:
    Code:
    layer_set_visible(layer ID, Visible?)
    
    This function takes the layer ID and either “true” or “false” as arguments, where “true” makes the layer visible and ”false” hides the layer. Hidden layers can still be used in the same way as visible layers; they just don’t perform any draw operations. If

    Moving layer offset and changing their speed is pretty straightforward too:
    Code:
    layer_x(Layer ID, x offset);
    layer_y(Layer ID, y offset);
    layer_hspeed(Layer ID, horizontal speed);
    layer_vspeed(Layer ID, vertical speed);
    
    //Respective getter functions
    layer_get_x(Layer ID);
    layer_get_y(Layer ID);
    layer_get_hspeed(Layer ID);
    layer_get_vspeed(Layer ID);
    
    Apparently, these functions work on all layers except for instance layers. Good examples of usage include creating parallax backgrounds.

    These are a few other general layer functions that I believe are worth mentioning
    Code:
    layer_script_begin(Target Layer ID, Script ID);
    
    layer_script_end(Target Layer ID, Script ID);
    
    layer_shader(Target Layer ID, Shader ID);
    
    //Respective getter functions
    
    layer_get_script_begin(Target Layer ID);
    
    layer_get_script_end(Target Layer ID);
    
    layer_get_shader(Target Layer ID);
    
    These functions provide some quite useful options. Each of this functions run for each element on the target layer and for each individual draw event of an instance. A simple use for this would be setting a shader and passing some constant uniforms to it, though you could theoretically do much more.


    There are still quite a few more general layer functions, though they aren’t necessary to explain right now, as they aren’t essential in creating a basic game. Check the documentation if you want to read up on them!

    Instances and Layers – update “instance_create”
    I was initially going to cover tiles here since they are the BIG thing, but I reckon people would probably find instance-related stuff slightly more useful to begin with – say goodbye to instance_create! …Unless you import a 1.4 project, in which case compatibility scripts are automatically generated.

    So, let’s get straight to it – “instance_create” is gone. If you’re a DnD user, the change won’t be too harsh as the input box for the function plainly lists usage. As for the GML side of things, here’s the 2 functions you’ll need to know about:
    Code:
    instance_create_layer(x, y, layer ID (or name as a "string"), Object index);
    
    instance_create_depth(x, y, depth, Object index);
    
    Out of the 2, it’s preferred to use “instance_create_layer” as it follows the new layering rules properly. Depth apparently creates “pseudo-layers” for objects at irregular depths, which is slightly less efficient. At this point, depth mainly exists for compatibility with 1.X projects and the old “depth = -y” trick for pseudo-3D instance draw ordering for Isometric or “Legend of Zelda”-alike games.

    As per previous iterations of GameMaker, both of these functions return the unique ID of the created instance.

    If you need to move instances between layers, or check if a layer contains an instance or any instance of an object, we can use these functions:
    Code:
    layer_has_instance(Layer ID, Instance ID or object index);
    
    layer_add_instance(Layer ID, Unique Instance ID);
    
    layer_has_instance returns true if the layer contains the instance passed, or any object or child of the object index passed.

    layer_add_instance does not create an instance, but instead allows you to change which layer an instance is on at runtime. This is handy for layered collision environments, for example.

    The last functions related to instances and layers that I feel I should mention are these:
    Code:
    instance_activate_layer(Layer ID or name);
    
    instance_deactivate_layer(Layer ID or name);
    
    These functions activate or deactivate all instances on a specific layer. This finally provides us with a way to isolate certain groups of instances without needing using a with statement. The only problem with this is it cannot be restricted to a specific object index, though this shouldn’t matter too much if your layers are well organized.

    Tile Layers – The Big Subject
    The thing we’ve all been waiting for! Proper tilemaps!

    Now, I’ll be focusing on GML usage, rather than creating tiles and populating a room.

    “Why?”, you may ask. Well, because GMS2 has built-in tutorials and a huge manual http://docs2.yoyogames.com/source/_build/3_scripting/4_gml_reference/rooms/index.html to teach you these bits! Coding is my specialty, so I’d rather focus on that.

    At runtime, there’s a few things you may want to do with tiles – set some, update them, or check if a tile is present for tile based collisions. The latter is fairly likely, since the new tile system permits collisions much, much faster and more accurate than instance based collisions.

    But, before you can do all this, you’ll need to get the tilemap ID. Since layers can contain multiple different things, we can’t just use the layer ID and need to specifically grab the tile layer assigned to the layer. We use this function to get the tilemap ID:
    Code:
    layer_tilemap_get_id(layer_id);
    
    We pass the layer ID (either received through layer_get_id or layer_create) and the function returns the tilemap ID, assuming it exists, otherwise returning -1.

    We can also check if a layer actually has a tilemap using “layer_tilemap_exists” which returns either “true” or “false”, depending on whether or not the layer has a tilemap – building upon this, while a layer can have multiple instances or sprites, a layer cannot have more than one tilemap.

    If you need to create or destroy a tilemap at runtime, we have these handy functions:
    Code:
    layer_tilemap_create(layer_id, x in room, y in room, tileset resource ID, width in cells, height in cells);
    
    layer_tilemap_destroy(Tilemap ID)
     
    “layer_tilemap_create” returns the tilemap ID so you don’t need to call “layer_tilemap_get_id”.

    Sometimes, you may just want to start fresh with your tilemap, without deleting and creating a new one. You can do this by just calling:
    Code:
    tilemap_clear(Tilemap ID);
    

    Okay, now you should have some way if acquiring the ID of a tilemap – now you can get onto manipulating it! First, we’ll cover some handy tilemap functions. Once we’ve been through that, I’ll demo a simple implementation of tilemap-based collisions.

    There are more tile functions than the ones I'll go through, like finding cell size, position in room, getting the current frame (for animated tiles), but they aren’t necessary for understanding and using tiles in general – they’re more like “highly useful extras”. Check out the manual for more on these!

    First, let’s get on with getting and setting tiles.

    When we “get” a tile in the room, we get a kind of number “blob” of information about the tile. Assuming your tile is untransformed, it will simply represent the index of the tile from the tileset. This is because the first 19-bits of information represent the tile index, with the remaining bits representing mirror, flip and rotation. Tiledata does not store information about the tile size or location in the room.

    Moving on, here's how to get some tiledata from a tilemap:
    Code:
    tilemap_get(tilemap, cell x, cell y);
    
    tilemap_get_at_pixel(tilemap, x, y);
    
    The first function, ”tilemap_get”, returns the tile data found at the specific cell of a tilemap. You’d probably find this function more useful when looping through a tilemap to populate a world with instances.

    The second function, “tilemap_get_at_pixel”, returns the tiledata found at a specific coordinate in the room. This function is more useful in systems such as tile based collision, as you don’t have to figure out which cells to check manually. If you do need to check cells surrounding a pixel, perhaps these functions would interest you:
    Code:
    tilemap_get_cell_x_at_pixel(Tilemap ID, x);
    
    tilemap_get_cell_y_at_pixel(Tilemap ID, y);
    
    These functions return the x and y of the cell at a pixel, which can then be used with functions such as “tilemap_get”. They are also a little faster than rounding a coordinate to tile cell size manually.

    Now that you’ve retrieved the tile data, you can now read it.

    Assuming none of your tiles are transformed in any way, the number represented by the tiledata retrieved will just be the tile index – which you can check using the room editor. Tile ID’s start from 0 in the top left corner and increase in value from left-to-right.

    If the value is 0, then the tile is the empty tile, where nothing is drawn.

    If you do transform tiles in any way, either at runtime or in the editor, you’ll need to use another function:
    Code:
    tile_get_index(tiledata);
    
    This function returns the actual tile index, stripping off the extra data. If you need to find out if a tile has any other transform data, you can use these functions:
    Code:
    tile_get_mirror(tiledata);
    
    tile_get_flip(tiledata);
    
    tile_get_rotate(tiledata);
    
    These functions return “true” if the transform is applied to the data and each have appropriate setter functions so you can apply transforms to tiledata.
    Code:
    tile_set_mirror(tiledata, True or False);
    
    tile_set_flip(tiledata, True or False);
    
    tile_set_rotate(tiledata, True or False);
    
    Okay, this may raise the question “why is rotate only true or false?”. Well, the rotate flag only rotates the tile 90 degrees. To rotate a tile 180 degrees, rotate must be false and the tile must be both flipped and mirrored. To rotate 270 degrees, the tile must be mirrored, flipped and rotated.

    Additionally, I’m just going to point out that mirroring a tile reflects it horizontally, while flipping reflects vertically.

    There are also read-only variable equivalents to these functions which can be used with bitwise operations to apply/remove transforms (tile_mirror, tile_flip, tile_rotate, tile_index_mask) for example, “tiledata & tile_index_mask” returns the same result as “tile_get_index(tiledata);”.

    There is a “tile_set_empty” function too, though I’m fairly sure it has no real use, since it should always return 0.

    If you need to add a tile to the tilemap (or update a tile with transformed tile data), you’ll need to use one of these:
    Code:
    tilemap_set(tilemap ID, new tiledata, cell_x, cell_y);
    
    tilemap_set_at_pixel(tilemap ID, new tiledata, x in room, y in room);
    
    Passing 0 as the new tiledata will clear the tile. Otherwise, it will update and replace the tile in the targeted cell.

    For a simple demo of the functions, here’s a sample of code which will mirror a tile horizontally when clicked on with the mouse, assuming the tile is not empty:
    Code:
    //Get the tile layer ID. Better to run this at create or room start and store in a global, but for demoing, it's here
    var layer_id = layer_get_id("Tiles_1");
    var tilemap = layer_tilemap_get_id(layer_id);
    
    //If clicking
    if(mouse_check_button_pressed(mb_left))
    {
                    //Get tiledata at mouse
                    var tiledata = tilemap_get_at_pixel(tilemap, mouse_x, mouse_y);
    
                    //Check if tile is not empty
                    if(tiledata != 0)
                    {
                                    //get mirror state and toggle it
                                    var mirror_state = tile_get_mirror(tiledata);
                                    tiledata = tile_set_mirror(tiledata, !mirror_state);
                         
                                    //Update the tile in the map
                                    tilemap_set_at_pixel(tilemap, tiledata, mouse_x, mouse_y);
                    }
    }
    

    Okay, so that’s how to get and set tiles. Now let’s look at changing the tileset a tilemap uses at runtime. This could be useful in games that feature seasons, for example. Note that you cannot change the tilemap for an individual tile without creating a whole new layer.

    Well, that’s nice and simple. We have two functions for this:
    Code:
    tilemap_get_tileset(Tilemap ID);
    
    tilemap_tileset(Tilemap ID, Tileset ID);
    
    The first function returns the ID of the tileset currently in use. The second allows you to set the tileset the tilemap should use, automatically updating tiles visually.

    Okay, that’s about all there is to the basics of tiles. Like I said earlier, there are more functions, but these are the ones I think you will find most useful for making a game.

    Assets and Backgrounds
    Asset layers and background layers are basically ways of drawing sprites without using objects.

    We’ll quickly cover background layers, then move on to asset layers.

    Backgrounds

    Background layers replace the backgrounds you may know from GM: S 1.X and now use sprite resources and hence can even be animated.

    The old background array has now been replaced with layers and functions. Here is a list of all background functions within GMS2, so you can find out how to transform and edit backgrounds: http://docs2.yoyogames.com/source/_build/3_scripting/4_gml_reference/rooms/backgrounds/index.html

    For the most part, you’re probably going to just create and edit backgrounds in the room editor, but here’s a few handy functions to know about.

    Creating, Destroying and checking the existence of background layers
    Code:
    layer_background_create(Target Layer ID, Sprite to use);
    
    layer_background_destroy(Background ID);
    
    layer_background_exists(Layer ID, Background ID);
    
    Getting the background ID that was attached to a background layer in the IDE, so you can manipulate it at runtime
    Code:
    layer_background_get_id(layer_id);
    
    Changing the layer a background is bound to
    Code:
    layer_element_move(Background ID, Target Layer ID);
    
    You need to use “layer_element_move” as there isn’t a specific function for background layers to change the layer. Just pass the background ID as normal.

    Changing the background image:
    Code:
    layer_background_change(Background ID, New Sprite Index);
    
    Like I said, very quickly going over that, since the basic use is very similar to the usage in 1.X.

    Moving on to asset layer manipulation

    To start with, despite the name of Asset layers, the only assets you can currently put on them in the IDE are sprites. Basically, this section will be more about sprite manipulation on an asset layer. Should more asset types appear, I will update the guide with relevant information.

    Here’s a link to the relevant documentation page on sprites in layers: http://docs2.yoyogames.com/source/_build/3_scripting/4_gml_reference/rooms/sprites/index.html

    Okay, basic stuff first.

    Creating, destroying and checking the existence of a sprite asset at runtime:
    Code:
    layer_sprite_create(Target Layer ID, x in room, y in room, sprite index);
    
    layer_sprite_destroy(Sprite element ID);
    
    layer_sprite_exists(Target Layer ID, Sprite element ID)
    
    “layer_sprite_create” returns the sprite element ID for use in other functions.


    Retrieving the ID of an asset placed in the room editor:
    Code:
    layer_sprite_get_id(Layer ID, Asset Name);
    
    For the “Asset Name” argument, you need to provide the name that was generated or that you gave the asset when placing it in the room – this can be found by looking at the layer properties of an asset layer or by double clicking on the desired asset in the room editor.


    Changing the drawn sprite of the asset:
    Code:
    layer_sprite_change(Sprite element ID, New sprite_index)
    
    Changing the layer a sprite asset is on:
    Code:
    layer_element_move(Sprite Element ID, Target Layer ID);
    
    You can transform all the aspects of the sprite as usual (x, y, scale, color, angle etc) by using the relevant, named functions as shown in the link to the documentation. They basically explain themselves, so there is little reason for me to rewrite the manual here.

    New Particle System Functions
    This bit isn’t very long, it’s just a quick update on how particle systems work with layers. All other particle related functionality (emitters, particles, drawing etc.) still seem to be the same.

    Firstly, I will point out that “part_system_create” still exists, and creates a managed layer for the system.

    However, you can now create particle systems that are attached to layers with:

    Code:
    
    part_system_create_layer(Layer ID);
    
    
    This functions still returns the ID of the particle system.

    To get and change the current layer of a particle system, we have these functions:

    Code:
    
    part_system_get_layer(Particle system ID);
    
    part_system_layer(Particle system ID, target layer ID);
    
    
    “part_system_get_layer” returns the ID of the layer, unless the layer is an internally managed one, in which case 0 is returned.

    “part_system_layer” attaches the particle system to the specified layer.

    Particle system visibility/existence is tied to the layer it is bound to.

    Layers and “depth = -y”
    Okay, here’s one thing that did change, in a big way. If you’ve ever made an isometric game, or a Zelda-alike where closer entities are drawn on top of further entities, you may have used “depth = -y”.

    Well, you can still use this as it exists for compatibility with 1.X projects, however, if you’re developing purely from a GMS2 standpoint, you may come across some issues. For example, here is an instance sorted with “depth = -y”, on a layer with depth at -100, as well as 2 other layers – one at -50 depth, the other at -150. Here is the result:

    BadOrdering.gif

    Not so desirable. The purple should always be below the red circle, while the green should always be on top.

    Obviously, you can work around this with spacing the layers in different ways, but layers would need separating by the height of the room to work around this. If you have a lot of layers, that could cause problems.

    So, for a single layer, here is some basic code:
    Code:
    ///Create of draw controller
    draw_queue = ds_priority_create();
    
    ///Draw event of draw controller
    with(obj_drawParent)//Parent objects of all ordered instances
    {
                    ds_priority_add(other.draw_queue, id, y);
    }
    
    //Loop n' draw
    while(!ds_priority_empty(draw_queue))
    {
                    with(ds_priority_delete_min(draw_queue))
                    {
                                    draw_self();
                    }
    }
    
    The result is this:

    GoodOrdering.gif

    Much more satisfying. As you can see, it still sorts depth within the same layer properly too:
    [​IMG]
    The instance drawn is set to be invisible for this, just to make it a little easier to code. I wouldn’t recommend using disabling visibility for things you want to draw as that’s a little counter-intuitive. You’d also want to test that the object is on the same layer, in some situations.

    One other option is to use a ds_priority data structure. The problem with this is it gets very slow when dealing with 1000s of instances – you must keep the number of drawn instances for every frame low to keep the framerate reasonable (though if you’re drawing 1000s of instances every frame, you may want to consider optimising anyway).

    You’ll need a priority queue for every single layer that instances are drawn on, so this can get a little too much if you’re dealing with complex environments.

    Fprum user @Ariak has proposed an alternate to priority lists, which run consistently twice as fast by using "BinaryLists" - please check out their thread!

    Another possible suggestion is to use 3D techniques and rendering, but with an orthographic perspective. You could, for example, keep the depth constant, but draw the top 2 vertices of the sprite a little higher up in the z-axis, so it looks like the sprites are correctly layered. This removed the need for the queue, but adds the slight graphical overhead of the z-test and write.

    That should be everything you need.

    If you have any more suggestions on how I could expand on/improve this guide, or if you have a suggestion for a new guide (it could be ANYTHING!), let me know!
     
    Last edited: Nov 28, 2016
  2. squarebit

    squarebit Member

    Joined:
    Jun 20, 2016
    Posts:
    25
    This will be highly useful when making the transition to GMS2 on a future project. Thanks!

    Bookmarked :)
     
    MaddeMichael likes this.
  3. Dr Greb

    Dr Greb Member

    Joined:
    Nov 20, 2016
    Posts:
    19
    One comment about instance_create_layer
    It's written like this in the help file, but it's missing the ""quotes for layer_id. It took me ( a very fustrating) 10 minutes to realize I had to name it "Instances" instead of just Instances.
    instance_create_layer(x, y, layer_id, obj); it should be instance_create_layer(x, y, "layer_id", obj);

    Maybe if you edited your post a little to make sure people realize this right away, could save some peoples hair from being pulled out :p
     
  4. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    I've adjusted the line to say:
    Code:
    instance_create_layer(x, y, layer ID (or name as a "string"), Object index);
    
    Is that any better/clearer? I don't really want to write "layer_id", as the actual ID is a real number found by using "layer_get_id()" for example, whereas the name is a string
     
  5. BloodCorn

    BloodCorn Member

    Joined:
    Oct 8, 2016
    Posts:
    51
    Hey,
    Just wondering how you would suggest moving a background layer in code to another x position (I am making a parallax effect)
    Thanks!
     
  6. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    Ooh, this ones a little tricky since there are no background position/speed functions. May be worth making a suggestion for those. EDIT: Okay, there are functions (layer_x/y/hspeed/vspeed), but they weren't in the public documentation

    Okay, here's a suggestion. I've only done horizontal parallax here, but expanding shouldn't be too difficult. It requires usage of matrices and layer update scripts and may need a few tweaks, but it's not too bad for now:

    background_begin script:
    Code:
    var mat = matrix_build_identity();
    
    //A multiplying factor to stop the BG moving at the same speed as the foreground
    var factor = 0.25;
    
    //Set x translation based on the global shift (this could just be the camera x in the room.
    //Modulo by background sprite width in order to keep horizontal tiling happy (though may need something more for negative BG locations)
    mat[12] = (global.shift_x * factor) % sprite_get_width(bg_sprite);
    
    //Set the transform matrix
    matrix_set(matrix_world, mat);
    
    background_end script:
    Code:
    //Reset transforms
    matrix_set(matrix_world, matrix_build_identity());
    
    Some kind of create event which sets the scripts to the background:
    Code:
    //Get the layer the background is one
    bgID = layer_get_id("Background");
    layer_script_begin(bgID, background_begin);
    layer_script_end(bgID, background_end);
    
    global.shift_x is a position in the room. Possibly the camera x, for example.
    It worked in a quick test, but may need some tweaks depending on how you use it. Other option is to use asset layers, but then you lose tiling.
     
    Last edited: Nov 28, 2016
    IndianaBones likes this.
  7. BloodCorn

    BloodCorn Member

    Joined:
    Oct 8, 2016
    Posts:
    51
    Does that work to follow the player when it is moving?
     
  8. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    I assume you mean when the player moves? Assuming you set global.shift_x to the player x every step, it should follow the player. I've not tested it in a practical situation, but it worked when it was just connected to mouse position :p
     
  9. BloodCorn

    BloodCorn Member

    Joined:
    Oct 8, 2016
    Posts:
    51
    The kind of thing I want to do is make it like an endless runner, I am trying to follow the first part of this tutorial:
     
  10. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    Like this, yeah?
    EditoTest.gif

    I made a couple of little tweaks to the script - follows the camera now. When factor is 0, bg does not move, at 1 - moves at same speed as screen/foreground
    Code:
    var mat = matrix_build_identity();
    
    var factor = 0.5;
    
    var cx = camera_get_view_x(camera_get_active());
    
    
    mat[12] = (cx * (1 - factor)) % sprite_get_width(bg);
    
    matrix_set(matrix_world, mat);
    
    I did have to make a separate script for the further back background - I'm not sure if there is a way to get the ID of the calling background/layer yet

    If your endless runner does not use camera position but some kind of distance, just set "cx" to the distance instead, it should still work.

    EDIT Just saw a little further into the video - this code shouldn't even need adapting if you need zooming views. It should just work. Matrices are very nice. Maybe I should make some tutorial videos for GMS2
     
    Last edited: Nov 24, 2016
    IndianaBones likes this.
  11. BloodCorn

    BloodCorn Member

    Joined:
    Oct 8, 2016
    Posts:
    51
    Thanks so much for this, I am unfortunately lost, could you please tell me which script everything needs to go into and what I need to do in the room settings and other things to set it up..

    Thanks in advance,
    BloodCorn
     
  12. BloodCorn

    BloodCorn Member

    Joined:
    Oct 8, 2016
    Posts:
    51
    Th

    Thank you so much, unfortunately I am lost, could you please tell me what scripts to put everything in and what I need to do beforehand when preparing the room.

    Thanks,
    BloodCorn
     
  13. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    Hello!
    I'll not be able to help out much more right away - it's really late in the world where I am and I'll end up writing nonsense because I'm tired :p. I'll sort out a more in-depth step-by-step solution/guide tomorrow! It's worth me doing anyway, since parallax is pretty popular.
     
  14. Daniel Jackson

    Daniel Jackson Member

    Joined:
    Jun 21, 2016
    Posts:
    109
    Excellent guide, lots of info!

    But I don't exactly get the part about depth = -y. I've never used it in a game before, so I'm trying to learn about it now.
    I'm confused about that first animated gif. It's described as 'not so desirable'. But isn't that exactly what it is 'supposed' to do?
    If the instance is constantly updated to depth = -y, doesn't it make perfect sense that it suddenly jumps behind purple and is always in front of green?
    It says that the instance is on a layer having a depth exactly between the two blocks. But if you're constantly updating its depth, isn't it not on that layer anymore, but instead is on a dynamic layer?
     
  15. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    On the whole "depth = -y" thing - Yes, in a GMS2 context, the jumping between layers is actually what you'd expect - and for anyone starting off with GMS2 may never need to worry about it

    HOWEVER

    In previous iterations of GameMaker layers didn't exist, and if we wanted to make an isometric game or a LoZ-alike where closer things are drawn on top of further things (allowing you to walk the full way around a pillar, be hidden behind it and visible in front of it, for example), we'd use "depth = -y" to order the rendering correctly - hence why the first result is no longer desirable.
    So, basically, the whole reason that bit of the guide exists is to help people update from previous usage of "depth = -y", as it's a habit we now need to break.

    I'm more of a platformer (or full 3D) guy anyway, so I don't use this much, but it felt pertinent to point out.

    EDIT
    I spent some time writing up a guide, but soon realised it was going to be too big to just do quickly. So, until I write a full guide on parallax, here's what you can do:
    This applies to simple parallax scrolling in the x-direction, following a camera. If you need to learn how to use cameras, check out my other guide on cameras.
    If you want to expand into y-scrolling as well, it shouldn't be too difficult to use this code as a base, as long as you understand it.

    So, you'll need some scripts - one for each parallax layer. Name them something useful like "background_parallax_1"
    Code:
    h
    var mat = matrix_build_identity();
    
    var factor = 0.5;
    
    var cx = camera_get_view_x(camera_get_active());
    
    mat[12] = (cx * (1 - factor)) % sprite_get_width(bg_sprite);
    
    matrix_set(matrix_world, mat);
    [code]
    Replace "bg_sprite" with whatever your background sprite name is. Factor is the ammount of parallax (I guess) 1 means the background stays in its rest position (in the room), so appears as normal. 0 means the background moves 100% with the view. Anything in between varies the background "speed".
    
    You'll need a "transform_reset" script as well, to stop the background transforms after the layer is drawn:
    [code]
    matrix_set(matrix_world, matrix_build_identity());
    
    Then, in one of either room creation code, room start or create:
    Code:
    var bgID = layer_get_id("Background");
    
    layer_script_begin(bgID, background_parallax_1);
    layer_script_end(bgID, transform_reset);
    
    This binds the scripts to the layers. You'll need more copies of this code for more layers, with the "script_begin" and background names edited to suit.

    Okay, that a basic run-down. I'll do a more in-depth full guide soon
     
    Last edited: Nov 25, 2016
  16. Kezz

    Kezz Member

    Joined:
    Nov 27, 2016
    Posts:
    3
    Wow that's an epic write up, must have taken you ages!

    You mentioned tile layers can be used for collision. Would physics based collision be possible?
     
  17. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    Honestly, I couldn't tell you! Physics and box2D is probably the only area I haven't explored in GameMaker.

    I think that physics collisions are all done separately to main collision stuff, so I guess you could check for a tile at the position, but then have to apply force and calculate direction separately. Or perhaps rigid collision fixtures could be generated with a loop on a tilemap.

    Again, I don't really know for sure, I'm just throwing around ideas, but my first guess would be that you can't use the same system for physics collisions.
     
  18. Kezz

    Kezz Member

    Joined:
    Nov 27, 2016
    Posts:
    3
    Yeah cool I suspected that might be the case but I thought I'd see if you knew something I didn't. Thanks for your help!
     
    MaddeMichael likes this.
  19. BloodCorn

    BloodCorn Member

    Joined:
    Oct 8, 2016
    Posts:
    51
    Thank you! Will test it when I have a chance!
     
    MaddeMichael likes this.
  20. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    *Minor Update*
    Added link to @Ariak's faster priority list method using "BinaryLists" to deal with "depth = -y"
    I also updated the section on "layer_create", as you can now specify layer name on create at runtime!
     
  21. BloodCorn

    BloodCorn Member

    Joined:
    Oct 8, 2016
    Posts:
    51
    Sorry to throw a spanner into the works, but my guy doesn't actually move, the game just moves behind him, do you suggest any way to go around this?
     
  22. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    If you have some kind of distance counter (or can add one), I think that you'd just need to change
    Code:
    var cx = camera_get_view_x(camera_get_active());
    
    From the layer scripts to whatever your counter variable is and it should work. e.g.
    Code:
    var cx = global.distanceCounter;
    
     
  23. BloodCorn

    BloodCorn Member

    Joined:
    Oct 8, 2016
    Posts:
    51
    What would you suggest to do for the distance counter - add 5 every step?
     
  24. BloodCorn

    BloodCorn Member

    Joined:
    Oct 8, 2016
    Posts:
    51
    Is there a way to change speed for all the different layers, the factor doesn't change anything.
    Edit: Never mind, I referenced the same script all those times.
     
    Last edited: Nov 28, 2016
  25. MaddeMichael

    MaddeMichael Member

    Joined:
    Jun 20, 2016
    Posts:
    171
    Sorry about the super late reply! My power cut out due to bad weather.

    Glad you've got factor (and seemingly everything else) working!
    As for the distance counter thing, at this point, I couldn't really tell you - it's something you'd have to try different values for until it looks and feels right. Basically depends on the speed you want it to look like you are moving at (e.g. the same value as you would have used for hspeed normally)

    EDIT: Just to add, @rwkay pointed out some extra functions that weren't listed in the public documentation but in the runtime release notes (which pretty much make the layer scripts void) -layer_x, layer_y, layer_hspeed, layer_vspeed and related getter functions (consider them replacements for background_* functions when working with background layers). I̶'̶m̶ ̶g̶o̶i̶n̶g̶ ̶t̶o̶ ̶u̶p̶d̶a̶t̶e̶ ̶t̶h̶e̶ ̶g̶u̶i̶d̶e̶ ̶w̶i̶t̶h̶ ̶t̶h̶e̶s̶e̶ ̶f̶u̶n̶c̶t̶i̶o̶n̶s̶ ̶t̶o̶o̶. DONE
     
    Last edited: Nov 28, 2016
  26. BloodCorn

    BloodCorn Member

    Joined:
    Oct 8, 2016
    Posts:
    51
    Thank you so much! Such a huge help!
     
    MaddeMichael likes this.
  27. breakmt

    breakmt Member

    Joined:
    Sep 13, 2016
    Posts:
    147
    Thank you for depth = -y alternative @MaddeMichael ! A little worried about fact that we need override Draw event and use draw_self(). But what if I need to draw instance with some additions? For example, my character draw self and weapon in his hands.
     
    Last edited: Dec 28, 2017
  28. wroberts1014

    wroberts1014 Member

    Joined:
    Jan 23, 2018
    Posts:
    2
    Hey there, thanks for the guide! It solved my problem with my flickering sprite, but now has broken my lighting system. I was wondering if you could help?

    I believe the issue is that these sprites are sorted and drawn after the lighting has been drawn, is there any way to make sure this is ran before my lighting system is ran?
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice