Platformer using advanced objects not working

WimpyLlama

Member
I have no idea if the title to this post is anywhere near what my problem is, but I'm trying my best. So I've been working on this one problem for a good eight hours now (according to Steam), and I decided to just throw in the towel and ask here.

I'm working on a platformer survival game, similar to Terraria, that uses an ID system for all of its blocks and items. I made it so all I have to do to add a new block or item is create the sprite then run a small script I made. It simply adds the values I put into the script into an array, where all blocks and item data is stored. I made it so there is an object named oBLIT (object block item (I use BLIT a lot in the code as a way of saying block and item together)) and all I have to do is set its sprite in the creation code of the instance. Once I do that it auto sets all of its values (such as if it takes a certain amount of left-clicks to break, or if it uses gravity (blocks like sand do)) using the ID system I talked about earlier.

This is all fine and dandy. Yeah there's probably a better way, and when putting blocks in a room from the editor they are all the same sprite at first, but the game works and runs just fine. It even makes a collision with the player easier. I only have to test the oBLIT object instead of all types of block objects, which is what I thought at first. Turns out that works fine, but if I want to test if a certain block has values like if it's passable (I use this on blocks like leaves or doors) that's where trouble happens. First of all, I have to find what block I'm colliding with. I can't seem to find an easy way to do this. The only method I've found so far is testing with instance_place twice, doing something like this: block = [instance_place(x+moveX,y,oBLIT),instance_place(x,y+moveY,oBLIT)]; (moveX and moveY are what some people may call vSpeed and hSpeed. Later when I show the code you'll see what I mean.) I then can test something like if (block[0].sprite_index == sLeaves) don't do collision. Or something along those lines (Obviously I have this more optimized where every time I add in a new passable block I don't have to go in and add a new if statement.)

What I have done in my code is I loop through the block array and test if each one is passable, or a liquid, or whatever I need to test, then I set a variable called canCollide to true if the block is simply a normal block you'd need collision for. I then test after the loop to see if canCollide is true, and if so I do collision. The thing though with all this is that if one of the two blocks we're testing is true and the other is false (such as if there is a tree with leaves on top and the player jumps up and to the right and hits the leaves and tree trunk at the same time), or something along those lines, the player will often clip into half of a block and get stuck. That's why I made canCollide an array, then set its indexes to be their own, matching with the block array. Now when I test for x collision I can test for the part of canCollide that is testing the part of block which is using x.

At this point, it finally started working (there was a lot more trouble leading up to this point, but I'm not gonna explain it all.) But then comes in the matter of jumping. You see, jumping tests if there is a block under it, but I don't have a way to test if that block is passable or not, so it uses one of the canCollide indexes, and often doesn't allow jumping if you are in a passable block. So I add another part to the block array, this time testing the block under the player. Then I did the same with canCollide and gave jumping its own little section. And guess what, it worked, for the most part.

Now, for some reason, when you're in the air and you land, it sometimes sets you down a few pixels above the ground, then slowly drops you onto it. Movement and everything works fine, so it's all okay, except for the fact that it looks really strange. But it's small, and not a big deal, so I decided to leave it for now. I thought that if and when the game got better sprites and stuff (currently I'm just using solid colors) it wouldn't be all that noticeable, so I left it alone.

Then, of course, another problem came along (I swear this is the last one.) If you press yourself against a block and jump, you get shot off into the air. From just assumption, I assume this is going about double the speed that normal jumping is. I honestly don't know why it's doing this.

At this point, it was four in the morning and I decided to go to bed. I woke up a little bit ago and tried to fix it, but couldn't. I am so tired of this at this point, and I just want to move on to working on a different part of the game already, that I chose to come here and rant.

So yeah, that's the problem. I'm really sorry about this long post, and I hope I explained things well. Any help is greatly appreciated. There is a lot more code in this game then just what I'm gonna put here, so if you think you need more of it from anywhere to help, I'll be glad to post it. Thanks for the help again!

Code:
Code:
CREATE EVENT

//Move variables
moveX = 0
moveY = 0

//Walk speed variable
wSpeed = 4;

//Sprint power variable
sPower = 1.5;

//Jump power variable
jPower = 6.5;

//Player gravity
playerGravity = grv;

STEP EVENT:

//Keys array
keys = [keyboard_check(ord("A")),keyboard_check(ord("D")),
        keyboard_check(vk_space),keyboard_check(vk_shift),];

//Test if we are pressing shift, and if so up the speed by sprint power
if (keys[3]) var tSpeed = wSpeed*sPower; else var tSpeed = wSpeed;

//Set moveX with keys and walk speed
moveX = (keys[1]-keys[0])*tSpeed;

//Set moveY with gravity speed
moveY += playerGravity;

//Set block array
var block = [instance_place(x+moveX,y,oBLIT),instance_place(x,y+moveY,oBLIT),
             instance_place(x,y+1,oBLIT)];
    
//Set the can collide variable
var canCollide = [];
    
//Loop through the blocks we have selected
for (i = 0; i < array_length_1d(block); i++) {   
    //Set can can collide to true
    canCollide[i] = true;
    
    //Test if there is a block
    if (block[i] != noone) {
        //Test if it's a drop or something else and if so set can collide to false
        if (block[i].image_xscale == dropSize
        //Test if it's passable
        or block[i].ourBLIT[3] == "passable") canCollide[i] = false; //Our BLIT is an array in the oBLIT objects that store the objects BLIT ID stuff. So index 3 is the block type, that's why it tests if the block's ourBLIT 3 is "passable".
        //If all above fails then set can collide to true
        else canCollide[i] = true;
    }
}
        
//Test for collision on the y axis if we can collide
if (canCollide[1]) {   
    //Vertical
    if (place_meeting(x,y+moveY,oBLIT)) {
        while (!place_meeting(x,y+sign(moveY),oBLIT)) y += sign(moveY);
        moveY = 0;
    }
}

//Jump if there is a block below
if (canCollide[2] and place_meeting(x,y+1,oBLIT) and keys[2]) moveY -= jPower;
            
//Move the player on the y axis
y += moveY;
    
//Test for collision on the x axis if we can collide
if (canCollide[0]) {
    //Horizontal
    if (place_meeting(x+moveX,y,oBLIT)) {
        while (!place_meeting(x+sign(moveX),y,oBLIT)) x += sign(moveX);
        moveX = 0;
    }
}
    
//Move the player on the x axis
x += moveX;
 

Simon Gust

Member
So, I've been trying to replicate your system and came to the conclusion that (sorry, I have to say it) it is not optimal so to say. Either that or I didn't get it the same way as you did.
->
https://gyazo.com/0b9c09ac9bf7f990d84aa586e1c9a474

I've changed and simplified the code so it works in GM:S 1.4 which I'am using.
Code:
//Set moveX with keys and walk speed
moveX = keyboard_check(ord("D")) - keyboard_check(ord("A"));
moveY += playerGravity;

//Set block array
var block = lookup_add
(
instance_place(x + moveX, y, obj_block),
instance_place(x, y + moveY, obj_block),
instance_place(x, y + 1, obj_block)
);
  
//Set the can collide variable
var canCollide = lookup_add(true, true, true);
  
//Loop through the blocks we have selected
for (var i = 0; i < 3; i++)
{  
    //Set can can collide to true
    canCollide[i] = true;
    if (block[i] != noone)
    {
        //Test if it's a drop or something else and if so set can collide to false
        if (block[i].passable)
        {
            canCollide[i] = false;
            // Our BLIT is an array in the oBLIT objects that store the objects BLIT ID stuff.
            // So index 3 is the block type, that's why it tests if the block's ourBLIT 3 is "passable".
        }
    }
}
      
//Test for collision on the y axis if we can collide
if (canCollide[1])
{  
    //Vertical
    if (place_meeting(x,y+moveY,obj_block))
    {
        while (!place_meeting(x,y+sign(moveY),obj_block)) {
            y += sign(moveY);
        }
        moveY = 0;
    }
}

//Jump if there is a block below
if (canCollide[2] and place_meeting(x,y+1,obj_block) and keyboard_check(vk_space)) {
    moveY -= jPower;
}
y += moveY;
  
//Test for collision on the x axis if we can collide
if (canCollide[0])
{
    if (place_meeting(x+moveX,y,obj_block))
    {
        while (!place_meeting(x+sign(moveX),y,obj_block)) x += sign(moveX);
        moveX = 0;
    }
}
x += moveX;

I suggest trying to move the logic inside the while-statements so it's absolutely sure every block 1 pixel ahead or below you has those traits.
I also experienced this hard stop + slow drop when falling. I don't know what's causing it but it happens with normal collision code as well.

There are better object-collision systems on here such as the one by CardinalCoder, they have a tutorial.
https://forum.yoyogames.com/index.php?threads/pixel-perfect-object-based-collision.30739/
You could work with that and see if the results change.

What you can also do is scrap objects and go for an array as your collision "grid". This would mean you would have to get rid of your block object considering you're creating a Terraria-style game (which also uses a grid).

I personally had much greater success so far (without a platform and slope system yet) using a grid collision system.
Your choice.
 
Last edited:

TheouAegis

Member
Even with slopes, a grid system is fine.

If every block is going to be an instance
anyway, then you may as well just make separate objects for each one. I mean, you're changing more than just the sprites; you are changing their physics properties as well.

//Jump if there is a block below if (canCollide[2] and place_meeting(x,y+1,oBLIT) and keys[2]) moveY -= jPower;
This section should be higher up in your code, not after collision handling. And by using -=, that's probably what's making you slingshot upwards. Use =- instead.
 

WimpyLlama

Member
So, I've been trying to replicate your system and came to the conclusion that (sorry, I have to say it) it is not optimal so to say. Either that or I didn't get it the same way as you did.
->
https://gyazo.com/0b9c09ac9bf7f990d84aa586e1c9a474

I've changed and simplified the code so it works in GM:S 1.4 which I'am using.
Code:
//Set moveX with keys and walk speed
moveX = keyboard_check(ord("D")) - keyboard_check(ord("A"));
moveY += playerGravity;

//Set block array
var block = lookup_add
(
instance_place(x + moveX, y, obj_block),
instance_place(x, y + moveY, obj_block),
instance_place(x, y + 1, obj_block)
);
 
//Set the can collide variable
var canCollide = lookup_add(true, true, true);
 
//Loop through the blocks we have selected
for (var i = 0; i < 3; i++)
{ 
    //Set can can collide to true
    canCollide[i] = true;
    if (block[i] != noone)
    {
        //Test if it's a drop or something else and if so set can collide to false
        if (block[i].passable)
        {
            canCollide[i] = false;
            // Our BLIT is an array in the oBLIT objects that store the objects BLIT ID stuff.
            // So index 3 is the block type, that's why it tests if the block's ourBLIT 3 is "passable".
        }
    }
}
     
//Test for collision on the y axis if we can collide
if (canCollide[1])
{ 
    //Vertical
    if (place_meeting(x,y+moveY,obj_block))
    {
        while (!place_meeting(x,y+sign(moveY),obj_block)) {
            y += sign(moveY);
        }
        moveY = 0;
    }
}

//Jump if there is a block below
if (canCollide[2] and place_meeting(x,y+1,obj_block) and keyboard_check(vk_space)) {
    moveY -= jPower;
}
y += moveY;
 
//Test for collision on the x axis if we can collide
if (canCollide[0])
{
    if (place_meeting(x+moveX,y,obj_block))
    {
        while (!place_meeting(x+sign(moveX),y,obj_block)) x += sign(moveX);
        moveX = 0;
    }
}
x += moveX;

I suggest trying to move the logic inside the while-statements so it's absolutely sure every block 1 pixel ahead or below you has those traits.
I also experienced this hard stop + slow drop when falling. I don't know what's causing it but it happens with normal collision code as well.

There are better object-collision systems on here such as the one by CardinalCoder, they have a tutorial.
https://forum.yoyogames.com/index.php?threads/pixel-perfect-object-based-collision.30739/
You could work with that and see if the results change.

What you can also do is scrap objects and go for an array as your collision "grid". This would mean you would have to get rid of your block object considering you're creating a Terraria-style game (which also uses a grid).

I personally had much greater success so far (without a platform and slope system yet) using a grid collision system.
Your choice.
Yes! It finally works! Thank you so much. I used the link you provided and did some adjustments and now it all works. Thanks a lot!
 
Top