• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Graphics Ideal variety of slopes?

C

Canamla

Guest
In a typical retro style pixel art game, what are the ideal angles of slopes? i.e. 45 degree inclines, 75 degrees, etc.
It's an arduous task to create a graphic for each degree in a gradual curve, but what are the best bases to cover when creating a few varieties of slope inclines? I'm going to say that 45 is probably a given, but what does the community have to offer?
 

Yal

šŸ§ *penguin noises*
GMC Elder
I'd say 2x1 and 1x1 tile slopes work for most games, perhaps 1x2 slopes as well if you want the player to run up a lot of walls. But usually you're fine with 2x1 and 1x1 slopes for most situations, since they have the most noticeable gameplay changes (2x1 slopes slow you down and inconveniences combat with uphill enemies, 1x1 slopes makes it completely impossible to fight uphill enemies and forces you to dodge them, and in some games are so slippery you either need to constantly jump up them or can only slide down them). I wouldn't say adding 3x1 slopes would have any effect on gameplay 2x1 ones wouldn't have.
 

NeoShade

Member
I wouldn't say adding 3x1 slopes would have any effect on gameplay 2x1 ones wouldn't have.
While that's true, 3x1 (and even 4x1) slopes can be nice simply to help create more realistic and flowy levels. I think the answer here comes down to personal opinion a great deal. If you're only looking at it from a game mechanics point of view, then yeah, maybe 1x1 and 2x1 is enough, but you have to decide if you think it's worth the extra effort to help make things look a bit nicer by adding just a couple more tiles.
 
Ideal angles would be 1 x 1 (45) and 1 x 2 (75) as you know.

I've done 1 x 1, 1 x 2, 1 x 3, 1 x 4 with overhangs. It was easy to make the code, tougher to make the actual graphics for the slopes. The code turned out to allow any sort of slope incline. By tougher to make the actual graphics, its more just an issue of tedium. I never look forward to making those graphics just because its taking a slice and moving it.

The only challenge with the code was still small. Weight to roll and going up hill will still get you moving up hill as long as you don't stop. Its probably easier to do with the physics system.



This next one probably isn't the best considering I jump down the hill rather than run down it.

 
W

Wayfarer

Guest
In the game I'm currently working on, I use a shader to create slopes from wall tiles, and then the game automatically adds in the grass. That way slopes can be dragged to any size in the room editor.

Most games probably don't need this (and the art style has to work in with this approach) but thought I'd mention it anyway.
 
Last edited:
W

Wayfarer

Guest
@NeoShade: Sure thing!

Slope Clipping Shader
I'm using GMS1 but I imagine the same code works in GMS2. Here are the steps:

  1. Add a shader, titled "shaderSlopeClipping".

  2. Here's the Vertex code:
    Code:
    attribute vec3 in_Position; // (x,y,z)
    attribute vec4 in_Colour; // (r,g,b,a)
    attribute vec2 in_TextureCoord; // (u,v)
    
    varying vec2 v_vTexcoord;
    varying vec4 v_vColour;
    varying vec3 vvPos;
    
    // Main
    void main()
    {
        v_vTexcoord = in_TextureCoord;
        v_vColour = in_Colour;
        vvPos = in_Position;
       
        gl_Position = (
            gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] *
            vec4(in_Position.x * 1.0, in_Position.y * 1.0, in_Position.z * 1.0, 1.0)
        );
    }

  3. Here's the Fragment code:
    Code:
    varying vec2 v_vTexcoord;
    varying vec4 v_vColour;
    varying vec3 vvPos;
    
    // Uniforms
    uniform vec4 uPos;
    uniform vec2 uSlopeInfo;
    
    // Main
    void main() {
        // 1. Grab pixel
        vec4 col = v_vColour * texture2D(gm_BaseTexture, v_vTexcoord);
       
        // 2. Determine if slope is sloping left or right (in the upwards direction)
        if (uSlopeInfo[0] == -1.0) {
            col.a *= float(vvPos.y - uPos[1] >= (vvPos.x - uPos[0]) * uSlopeInfo[1]);
        } else{
            col.a *= float(uPos[2] - (vvPos.y - uPos[1]) <= (vvPos.x - uPos[0]) * uSlopeInfo[1]);
        }
       
        // 3. Return pixel
        gl_FragColor = col;
    }

  4. Make two sprites to represent a left and right slope (these are just so you have something to place in the room editor). So, say your grid size is 16x16, each sprite would be 16x16. The left slope should slope left upwards, and the right slope should slope right upwards.

  5. Make a matching object for each of those sprites. These are what you will place in the room and drag to size. In the left slope object create event have: "dir = -1;". In the right slope create event have "dir = 1;"

  6. Finally, in the draw event of each:
    Code:
    // 1. Start slope clipping shader
    shader_set(shaderSlopeClipping);
    var uPos = shader_get_uniform(shaderSlopeClipping, "uPos");
    var uSlopeInfo = shader_get_uniform(shaderSlopeClipping, "uSlopeInfo");
    shader_set_uniform_f(uPos, x, y, sprite_height);
    shader_set_uniform_f(uSlopeInfo, dir, sprite_height/sprite_width);
    
    // 2. Draw things
    var xx, yy;
    for (xx = 0; xx < sprite_width; xx += 16) {
        for (yy = 0; yy < sprite_height; yy += 16) {
            draw_sprite(sTile, 0, x + xx, y + yy);
        }
    }
    
    // 3. End shader
    shader_reset();

  7. Replace the "sTile" with whatever you need to be the tile that the slope uses, or it even can be a variable etc. In my current game, it places different tiles depending on how close the tile is to the top, but I've left that out as that complicates this example. You will also need to change "16" to your grid size.

Tell me if it doesn't work as I may've missed something. I had to change parts of the code to make it more generic and therefore may've missed things. In my game, rather than placing the draw code in both left and right slopes, the draw code is placed once in a parent slope object. I left that out of this example to keep things simple. I based the shader off a clipping mask shader by @YellowAfterlife.



Bonus Example: Adding grass on top
  1. Add the following below the supplied draw event code (in the first example) to add grass, or whatever, on top of the slope.
    Code:
    var xx = 0, yy = 0;
    var yOff = sprite_get_yoffset(sGrass);
    var xStep = max(sprite_width/sprite_height, 1);
    var yStep = max(sprite_height/sprite_width, 1);
    
    for (xx = 0; xx < sprite_width; xx += xStep) {
        draw_sprite_part(sGrass, 0,
            xx mod 16, 0, xStep, 16,
            x + xx,
            y + ((dir == 1) * sprite_height) + (yy*-dir) - yOff
        );
        yy += yStep;
    }

  2. Replace "sGrass" with your grass sprite. Replace "16" with your grid size. When making your grass sprite, set the y origin to the part you want to sit along the slope. If your grid size is 16, you might have grass that is 8 or so pixels tall (tip: adding some darker shadow pixels at the base of the sprite helps with the effect). You might then set the y origin to 4 pixels (so the grass follows the slope along the middle point).

  3. Quick Edit: Just realised, that for loop should be made to only iterate if the xx/yy is in view (otherwise it's quite a lot of processing!). I'll fix that a bit later (it's not hard to fix). It will still work as is, it's just a lot of iterating each step.
 
Last edited:

NightFrost

Member
That's an interesting shader. I'm not sure what vec3 in_Position is but judging by the code it must be the pixel's actual coordinates, since it is used with instance position and slope aspect ratio to calculate a truthiness multipier for the alpha?
 
Top