1. Hey! Guest! The 32nd GMC Jam will take place between Feb 22nd, 12:00 UTC (Friday noon) and Feb 25th, 12:00 UTC (Monday noon). Why not join in! Click here to find out more!
    Dismiss Notice

GM:S 1.4 How to make pixel-perfect block collision with smooth movements ?

Discussion in 'Programming' started by hijong park, Feb 14, 2019 at 9:28 AM.

  1. hijong park

    hijong park Member

    Joined:
    Dec 29, 2016
    Posts:
    100
    [​IMG]

    When i use vspeed, hspeed, friction to make the player move, I want to make the players to precisely colide with walls and stop.
    Both player and walls have square hitboxes.

    I made the players to move by using motion_add(), and add friction with friction = player.speed / 10.


    and for the block collision I used this method at step event :

    Code:
    if !place_free(x+hspeed,y)
    {move_contact_solid(direction,speed); hspeed = 0;}
    if !place_free(x,y+vspeed)
    {move_contact_solid(direction,speed); vspeed = 0;}
    This method seems to work fine at first, but It's not perfect.

    Sometimes when I move and hit the wall diagonally, The player gets stuck in the wall. also, This method only works with solid objects. If possible, I want to make it to work with non solid objects for better performance.
     
  2. Lady Glitch

    Lady Glitch Member

    Joined:
    Feb 10, 2019
    Posts:
    15
    You can try something like this to collide with non-solid stuff
    Code:
    if (place_meeting(x + xSpd, y, oWall))
    {
        while(!place_meeting(x + sign(xSpd), y, oWall))
        {
            x += sign(xSpd);
        }
        xSpd = 0;
    }
    x += xSpd;
     
  3. NeZvers

    NeZvers Member

    Joined:
    Mar 24, 2018
    Posts:
    217
    Just NO! That works for those that start coding and it still leaves gaps.

    If you are using GM:S2 than you can look into tile collision system, but I'm using more 1.4 so I go with bbox or ds_grid collision systems.
    Not at home and can't remember if needed to do 1 pixel shift like in comments
    Code:
    hitbox_r  = bbox_right - x;
    hitbox_l  = bbox_left - x;
    hitbox_t  = bbox_top - y;
    hitbox_b  = bbox_bottom - y;
    
    Code:
    var hin = keyboard_check(vk_right) - keyboard_check(vk_left);
    var vin = keyboard_check(vk_down) - keyboard_check(vk_up);
    
    if(hin!=0){
        hspd += acc * hin;
        hspd = clamp(hspd, -spd, spd);
    }else{
        hspd = 0;   
    }
    if(vin!=0){
        vspd += acc * vin;
        vspd = clamp(vspd, -spd, spd);
    }else{
        vspd = 0;   
    }
    
    var hsp = floor(hspd); //round down
    if(place_meeting(x+hsp, y, oSolid)){ // check if collide
        hspd = 0;
        var block = instance_place(x+hsp, y, oSolid); // get ID from it
        if(hsp>0){ // move right
            x = block.bbox_left -hitbox_r -1;
        }
        if(hsp<0){ // move right
            x = block.bbox_right -hitbox_l +1;
        }
    }
    else{
        x+=hsp;  
    }
    
    var vsp = floor(vspd); //round down
    if(place_meeting(x, y+vsp, oSolid)){
        vspd = 0;
        var block = instance_place(x, y+vsp, oSolid); // get ID from it
        if(vsp>0){ // move down
            y = block.bbox_top -hitbox_b -1;
        }
        if(vsp<0){ // move up
            y = block.bbox_bottom -hitbox_t +1;
        }
    }
    else{
        y+=vsp;  
    }
    

    To get more optimized game it's suggested to go with ds_grid collision which is similar to this bbox collision.

    EDIT: forgot to put last bracket after if and add hspd/ vspd to x & y.
     
    Last edited: Feb 14, 2019 at 5:17 PM
  4. Bayesian

    Bayesian Member

    Joined:
    Sep 13, 2016
    Posts:
    363
    When does this leave gaps? The only issue with it is that its not flexible when you want to add literally and thing else to the game. For basic collisions I like to use this.
     
  5. NeZvers

    NeZvers Member

    Joined:
    Mar 24, 2018
    Posts:
    217
    @Bayesian it leaves gaps when you don't move rounded numbers (for example have acceleration 0.2) and so you can end up 0.8 away from wall. So after t,hat you are checking whole pixels and move by whole pixels. Meaning that if you are 3.8 pixels away from wall and hspd is 4 - after while loop system, you end up 0.8 fra om wall.
    Sure you can the do same rounding I showed, but still, you are doing several unnecessary place_meeting checks that impact processing. In bbox collision system, you get ID and snap to its side without any loop.
    And I'm assuming @hijong park is already using while loop collision, hence asking for a solution.

    Don't get me wrong, Zack has good code, but there's a flaw as previously explained (you can't use decimal acceleration or need clever rounding) but it also require repeats and call place_meeting repeatedly (meaning it it's far from optimized).
     
    Last edited: Feb 14, 2019 at 5:19 PM
  6. Bentley

    Bentley Member

    Joined:
    Jun 18, 2017
    Posts:
    624
    Edit: ignore my post. I just realized my code will still move you to the wall at fractions so it won't be px perfect. Maybe snapping to the wall is the best wall to go.

    You could collide with the wall, backtrack, and then move 1 pixel at a time until you are next to it.
    I think this should work. Uncheck solid if it's checked.

    Collision event with obj_solid (or w/e you name it)
    Code:
    x = xprevious;
    y = yprevious;
    
    var dir, dx, dy;
    dir = point_direction(0, 0, hspeed, vspeed);
    dx = lengthdir_x(1, dir);
    dy = lengthdir_y(1, dir);
    
    while (!place_meeting(x + dx, y + dy, other))
    {
        x += dx;
        y += dy;
    }
    
    if ((bbox_left > other.bbox_right) || (bbox_right < other.bbox_left))
    {
        hspeed = 0;
    }
    if ((bbox_top > other.bbox_bottom) || (bbox_bottom < other.bbox_top))
    {
        vspeed = 0;
    }
    If you have two different blocks, like obj_floor and obj_wall, then you can skip the bottom part of the code and set vspeed to 0 when you collide with the floor and hspeed to 0 when you collide with the wall.

    Also, this may not be perfect because you are moving at subpixels. A simple solution would be to round your hspeed and vspeed so you always move at integers.

    hspeed = round(hspeed). // Same for vspeed
     
    Last edited: Feb 14, 2019 at 10:01 PM
  7. NightFrost

    NightFrost Member

    Joined:
    Jun 24, 2016
    Posts:
    1,514
    The while-looping collision method works well and brings the instance next to a wall. The reason for this being, collision checking commands round coordinates before doing the testing. The same holds true to bbox_* values, they are rounded positions (which is why it is a bad idea to combine x + vspd and bbox_* testing in collisions, you can end with one-pixel errors when used values are not integers). The exception, I recall, are the commands that look at single pixels; it's been a while since I tested, but if memory serves they floor the coordinates instead before testing (so it is bad idea to combine bounding box collision checking with pixel collision checking, you can get mixed signals).

    Edit - oops, I forgot to add one critical point to why the while-loop collision works: because draw routines also round the coordinates in the same manner. So, while the collisions leave fractions to the coordinates (instance might be 0.40000 away from a wall or 0.21425 inside it, etc), everything aligns because of rounding performed at all stages.
     
    Last edited: Feb 15, 2019 at 1:07 AM
  8. hijong park

    hijong park Member

    Joined:
    Dec 29, 2016
    Posts:
    100

    Thanks for the help! It works well.
     
  9. NeZvers

    NeZvers Member

    Joined:
    Mar 24, 2018
    Posts:
    217
    @hijong park I just figured out a minor bug with rounding.
    Code:
    var hsp = floor(abs(hspd)) * sign(hspd);
    
    Floor will round down and in negative numbers, it will give the wrong value.
     

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