Collision not working properly, don't know why

A

AiNaKa

Guest
I'm trying to work on a tetris clone, most mainly for practice, but I can't seem to get the collisions to work properly, the pieces won't fit into certain spaces properly. I can't imagine why the collisions aren't working, but I've tried many different approaches to a collision system, but each one is just worse anyways. This is as good as I can seem to get it and it still won't work.

Here is some of my collision code:
GML:
///collision
if not place_empty(x,y)
{
    if place_empty(x-8,y)
    {
        x -= 8;
    }
    if place_empty(x+8,y)
    {
        x += 8;
    }
    if place_empty (x,y-8)
    {
        y -= 8;
    }
}
And here is the code that controls the falling of the pieces and solidification into individual blocks (which is mostly managed in a seperate line of code):
GML:
///drop

if global.drop > 30
{
    if place_empty(x,y+1)
    {
        y += 8
        global.drop = 0;
    }
    else
    {
        solidify = true;
    }
}

So far, I've only coded "I" pieces and "T" pieces. The I pieces function completely as intended, but the T pieces get screwed up when trying to land them properly as shown in this screenshot. I can't seem to figure out why this is happening, nor can I fix it. I've also tried solutions using collision_rectangle, but it gets even worse and ends up glitching outside of the box.
1588457400724.png
 
A

AiNaKa

Guest
You're using precise bounding boxes, right?
I would assume that's "precise collision checking"?
If so, then yeah.
For reference, I am using GMS 1.
All alignments of objects are pixel perfect as well.
 

Nidoking

Member
Oh, I think the issue might be that you don't have elses in your collision code. It's trying to move them left, then right, then up, regardless of whether any of those succeed.
 
A

AiNaKa

Guest
Oh, I think the issue might be that you don't have elses in your collision code. It's trying to move them left, then right, then up, regardless of whether any of those succeed.
I tried this:
GML:
if not place_empty(x,y)
{
    if place_empty(x-8,y)
    {
        x -= 8;
    }
    else if place_empty(x+8,y)
    {
        x += 8;
    }
    else if place_empty(x,y-8)
    {
        y -= 8;
    }
}
The results I got were absolutely no different. Am I doing something wrong? or...?
 

Nidoking

Member
I think it's fair to say that yes, you are doing something wrong, but I can't identify what it is from the information given. What I see in the picture really looks like either the T-piece or the individual square object you're apparently splitting them into after they land isn't using precise collision checking, but you say that's the case and haven't posted any proof, so I have no further answers for you.
 

Pixel-Team

Master of Pixel-Fu
You don't need collisions for a tetris game. The collisions can be handled by examining occupied positions in a 2d array that represents your game area.
 
A

AiNaKa

Guest
I think it's fair to say that yes, you are doing something wrong, but I can't identify what it is from the information given. What I see in the picture really looks like either the T-piece or the individual square object you're apparently splitting them into after they land isn't using precise collision checking, but you say that's the case and haven't posted any proof, so I have no further answers for you.
1588511241822.png
The collision object is for the bounds on the play field. The tetrimino blocks are set as a child object. I actually didn't notice I had precise collision detecting disabled here, but upon trying to enable it, collisions stopped working entirely, and all pieces just phased through everything.
1588511477792.png1588511634412.png
With precise collision checking on, I can just do this:
1588511700782.png
have you looked into using a cell/grid based collision system instead of collision masks?
What's a cell based collision system and how would I go about doing that?
 
A

AiNaKa

Guest
You don't need collisions for a tetris game. The collisions can be handled by examining occupied positions in a 2d array that represents your game area.
Problem is I have no idea what this means or how I would do that to be honest. I'm no expert with GML here, I only understand the basics, really. I'd appreciate it if you could offer some sort of guide on something at least related to this concept though.
 
When they suggest an array or grid system, they are talking about using pure data for comparisons.

Using an mp grid / ds grid / 2d array:
The mp grid / ds grid / array has as many columns and rows as your play area. Clear the data structure before each movement step of the shapes falling, and figure out what cells they occupy, then input that info into your chosen data structure.

By performing a few checks to see if the "cells" are empty you can do collision detection just through using basic numbers

1) presumably your objects are already moving in terms of cells / grid coordinates i.e move left and it goes 1 cell left, so that its always aligned (been a while since i played tetris, so can't remember how the blocks move)
2 ) x of object div cell width = current cell x
3) y of object div cell height = current cell y
4) what is object shape? L shape? T shape? Is it rotated? etc
5) check cells at next move using however many "collision" variants you get from point 4 ^^^
6) look at the end result and apply it to your object
 
Last edited:

Sabnock

Member
00000000000000
00000001000000
00000011100000
00000000000000
11111100011111
11111111111111
11111111111111

for instance the T shape would fit in the gap in this example

but not in this example


00000000000000
00000010000000
00000111000000
00000000000000
11111100011111
11111111111111
11111111111111
 
A

AiNaKa

Guest
When they suggest an array or grid system, they are talking about using pure data for comparisons.

Using an mp grid / ds grid / 2d array:
The mp grid / ds grid / array has as many columns and rows as your play area. Clear the data structure before each movement step of the shapes falling, and figure out what cells they occupy, then input that info into your chosen data structure.

By performing a few checks to see if the "cells" are empty you can do collision detection just through using basic numbers

1) presumably your objects are already moving in terms of cells / grid coordinates i.e move left and it goes 1 cell left, so that its always aligned (been a while since i played tetris, so can't remember how the blocks move)
2 ) x of object div cell width = current cell x
3) y of object div cell height = current cell y
4) what is object shape? L shape? T shape? Is it rotated? etc
5) check cells at next move using however many "collision" variants you get from point 4 ^^^
6) look at the end result and apply it to your object
ok, I've figured out how to set up a ds_grid, but I'm a little stumped right now. How could I go about moving the pieces? I can do it with one point, but doing it for all 4 points in the tetrimino doesn't seem to be working.
 

Nidoking

Member
The tetrimino blocks are set as a child object.
Is precise collision checking turned on for the tetromino block sprite? It appears that what you've posted here is the sprite for the T-shape, but you indicated that you split it into individual instances for the squares once it lands. I assume you'd have to, in order to handle eliminating parts of the blocks. Is that sprite set to precise collision checking even though it's a square? If not, then that would cause what you're seeing.
 

vdweller

Member
In the draw event of each block have it also draw the collision mask. This will give you all the answers you need.

Also how are you going to make lines disappear by using objects and collisions? Tetris has simple code, it can even run on pocket calculators, where objects and precise collisions aren't even a thing. Sometimes using the advertised points of game maker can fire backwards, and can cause even more confusion than a more traditional programming approach.
 
A

AiNaKa

Guest
Is precise collision checking turned on for the tetromino block sprite? It appears that what you've posted here is the sprite for the T-shape, but you indicated that you split it into individual instances for the squares once it lands. I assume you'd have to, in order to handle eliminating parts of the blocks. Is that sprite set to precise collision checking even though it's a square? If not, then that would cause what you're seeing.
They're set as a child object of the collision object.
Both the block's sprite and the collision's sprite were set to either precise collision checking or not together. I didn't make on precise and the other not.[
Eitherway, setting either of them to precise in any combination makes the collisions much worse.

In the draw event of each block have it also draw the collision mask. This will give you all the answers you need.

Also how are you going to make lines disappear by using objects and collisions? Tetris has simple code, it can even run on pocket calculators, where objects and precise collisions aren't even a thing. Sometimes using the advertised points of game maker can fire backwards, and can cause even more confusion than a more traditional programming approach.
I intend to make the lines disappear as usual. If it detects that there are 10 block objects in a row, it will get rid of them and move all the ones above it downwards.
Also, I can't seem to find anything that directly draws the collision mask. I just assumed the collision mask was automatic regardless when drawing the sprite.
 
I need to remind myself how tetris pieces move, and where they rotate from.

Currently I have my shapes for testing, and the origin is 0, 0: though I am unsure of where they're originally meant to be rotated around. I add or reduce the image_angle by 90 degrees when they are rotated, and use that for a switch statement. It looks like this :

(note: it is only done for objects that have hit the bottom of the play area, or have "settled" against another block. i.e not moving anymore, and have a variable of the same name now set to true)

GML:
case small_l_shape:
start_x = x div 32;
start_y = y div 32;

switch (image_angle)
{case 0:
mp_grid_add_cell(is_grid, start_x, start_y);
mp_grid_add_cell(is_grid, start_x, start_y + 1);
mp_grid_add_cell(is_grid, start_x + 1, start_y);
break;

case 90:
mp_grid_add_cell(is_grid, start_x, start_y); // this is just copy pasted from above, so is incorrect :)
mp_grid_add_cell(is_grid, start_x, start_y + 1); // this is just copy pasted from above, so is incorrect :)
mp_grid_add_cell(is_grid, start_x + 1, start_y); // this is just copy pasted from above, so is incorrect :)
break;

// repeat for 180 degrees and 270 degrees
}
break;
Basically, for each object type I am creating responses for each of the four possible current image angles it could be, and then setting the relevant mp grid cells to occupied.

For moving my objects:

I am just moving the object as per normal (bear in mind that I don't know how they are meant to move) and this isn't fully figured out yet :)

left = move one column left
right = move one column right
drop speed (y) = 4 // rough guess

Code:
if !settled
{new_y = y + 4;
if new_y + height <= grid_bot // 'height' is a variable set to the unrotated sprite height & 'grid_bot' is the bottom of the play area
{
y = new_y;
}
else
{
settled = true;
}
}
So far that activates them when they hit the bottom of the grid, but it does not have
(a) an account for if they are rotated
(b) any grid cell checks included, to see if moves ahead are possible

But in the case of (b) that is pretty much some more tedious repetition like when setting the grid, only instead of setting cells you will instead be using mp_grid_get_cell to see if where its "moving to" has any collisions based on its current shape.

Once these collisions (or not) are figured then the outcome is another control element added to the check:

Code:
if !settled
{new_y = y + 4;
no_collision = false;


repeat (however many times)

{if mp_grid_get_cell(grid, whatever, whatever) != -1
{no_collision = true;
}
)

if new_y + height <= grid_bot && !no_collision
{
y = new_y;
x = wherever;//
}
else
{
settled = true;
}
}
EDIT: This is hypothetical pseudo code above :)

Where it references 'grid' above, that will be the variable assigned to the mp grid in a separate object. The shape objects will use it to be able to access the grid, from outisde of the object that has it.

When it comes to setting the grid, the object that has created it does this in a step event:

Code:
var is_grid = grid;
mp_grid_clear_all(grid);
with (obj_parent)
{if settled
{
switch (object_index)
{case square_shape:
start_x = x div 32;
start_y = y div 32;
mp_grid_add_cell(is_grid, start_x, start_y);
mp_grid_add_cell(is_grid, start_x, start_y + 1);
mp_grid_add_cell(is_grid, start_x + 1, start_y);
mp_grid_add_cell(is_grid, start_x + 1, start_y + 1);
break;

case small_l_shape:
switch (image_angle)
{case 0:
start_x = x div 32;
start_y = y div 32;
mp_grid_add_cell(is_grid, start_x, start_y);
mp_grid_add_cell(is_grid, start_x, start_y + 1);
mp_grid_add_cell(is_grid, start_x + 1, start_y);
break;
}
break;

case large_l_shape:
switch (image_angle)
{case 0:
start_x = x div 32;
start_y = y div 32;
mp_grid_add_cell(is_grid, start_x, start_y);
mp_grid_add_cell(is_grid, start_x, start_y + 1);
mp_grid_add_cell(is_grid, start_x + 1, start_y);
mp_grid_add_cell(is_grid, start_x + 2, start_y);
break;
}
break;
case t_shape:
switch (image_angle)
{case 0:
start_x = x div 32;
start_y = y div 32;
mp_grid_add_cell(is_grid, start_x, start_y);
mp_grid_add_cell(is_grid, start_x, start_y + 1);
mp_grid_add_cell(is_grid, start_x + 1, start_y);
mp_grid_add_cell(is_grid, start_x + 2, start_y);
break;
}
break;
}}}
By setting its own permanent variable to a local one (var is_grid = grid) it can use a 'with' upon the parent of the shape objects, and call them all. If 'settled' is true, then they fill in the grid accordingly.
 
Last edited:
A

AiNaKa

Guest
I need to remind myself how tetris pieces move, and where they rotate from.

Currently I have my shapes for testing, and the origin is 0, 0: though I am unsure of where they're originally meant to be rotated around. I add or reduce the image_angle by 90 degrees when they are rotated, and use that for a switch statement. It looks like this :

(note: it is only done for objects that have hit the bottom of the play area, or have "settled" against another block. i.e not moving anymore, and have a variable of the same name now set to true).....
Ok so I'm a little confused here. are the tetriminos still supposed to be individual objects and have their own sprites? If so, that might explain why I'm having trouble with the grid, I was trying to do it all within a single object with one grid. Am I supposed to use the grid for the tetriminos themselves or just for keeping track of where the tetriminos and blocks are and handling collision detection?
 
Ok so I'm a little confused here. are the tetriminos still supposed to be individual objects and have their own sprites? If so, that might explain why I'm having trouble with the grid, I was trying to do it all within a single object with one grid. Am I supposed to use the grid for the tetriminos themselves or just for keeping track of where the tetriminos and blocks are and handling collision detection?
As of now I am using objects, but am making this up as I go along :)

I do know how to use this for collision checking, but haven't yet boiled it down to pure maths, so still have objects featured.

The grid is used for the collision detection.

The objects exist mainly for me to see how things are currently working, but also for an x / y position to keep track of them individually, and have individual handles to define what they are.
 
A

AiNaKa

Guest
As of now I am using objects, but am making this up as I go along :)

I do know how to use this for collision checking, but haven't yet boiled it down to pure maths, so still have objects featured.

The grid is used for the collision detection.

The objects exist mainly for me to see how things are currently working, but also for an x / y position to keep track of them individually, and have individual handles to define what they are.
So I've managed to set up a function grid but it seems to have worsened the problem and I'm not sure why. I was testing out the collision for the left side (I made it so that when you push left, it won't let you move left if there is something there.) What it's doing instead though is allowing for me to lodge the piece into the side of the wall (but not go any further than one block) and I'm not sure why. I've set up a display for the grid in the bottom right corner. The red border is also part of the grid, and also has a value of 1 like the white tiles.
1588536080465.png
Here's the code for mapping the tetriminos onto the grid which seems to work perfectly fine:
GML:
if angle = 0
{
    ds_grid_clear(global.grid,0);
    ds_grid_set(global.grid,((x-4-96)/8)+1,((y-12-48)/8)+1,1);
    ds_grid_set(global.grid,((x-12-96)/8)+1,((y-4-48)/8)+1,1);
    ds_grid_set(global.grid,((x-4-96)/8)+1,((y-4-48)/8)+1,1);
    ds_grid_set(global.grid,((x+4-96)/8)+1,((y-4-48)/8)+1,1);
}
if angle = 90
{
    ds_grid_clear(global.grid,0);
    ds_grid_set(global.grid,((x-4-96)/8)+1,((y-12-48)/8)+1,1);
    ds_grid_set(global.grid,((x-12-96)/8)+1,((y-4-48)/8)+1,1);
    ds_grid_set(global.grid,((x-4-96)/8)+1,((y-4-48)/8)+1,1);
    ds_grid_set(global.grid,((x-4-96)/8)+1,((y+4-48)/8)+1,1);
}
if angle = 180
{
    ds_grid_clear(global.grid,0);
    ds_grid_set(global.grid,((x-4-96)/8)+1,((y+4-48)/8)+1,1);
    ds_grid_set(global.grid,((x-12-96)/8)+1,((y-4-48)/8)+1,1);
    ds_grid_set(global.grid,((x-4-96)/8)+1,((y-4-48)/8)+1,1);
    ds_grid_set(global.grid,((x+4-96)/8)+1,((y-4-48)/8)+1,1);
}
if angle = 270
{
    ds_grid_clear(global.grid,0);
    ds_grid_set(global.grid,((x-4-96)/8)+1,((y-12-48)/8)+1,1);
    ds_grid_set(global.grid,((x-4-96)/8)+1,((y+4-48)/8)+1,1);
    ds_grid_set(global.grid,((x-4-96)/8)+1,((y-4-48)/8)+1,1);
    ds_grid_set(global.grid,((x+4-96)/8)+1,((y-4-48)/8)+1,1);
}

and here's my code for moving left (it tests the leftmost square of the tetrimino at angle 0 right now):

GML:
if ds_grid_get(global.grid,((x-12-96)/8),((y-4-48)/8)) = 0
{
    x -= 8;
}
 
Although I have currently got some of the collisions figured out, I haven't quite figured out how to do movement.

Your problem where it lodges in the wall is most likely from miscalculating where to stop it

// going left
new_x = x - 8;
width = sprite_width/ 2 // I've done squares as 64 x 64, whereas the others are all 96 x 96. The origin is always centred
if (new_x - width) >= playing side left // whatever is the left boundary of the playing area i.e the beginning of grid zero as a full x value
{x = new_x;}
// else does nothing

But then you have the problem of this not taking into account grid collisions, as you don't want to use a coordinate divided by the cell size (row / column) , that is less than 0. Because that returns a value of - 1 (out of grid boundaries), which is the same as what it returns when a cell is occupied.

So when testing movement / collisions you have to find a way to account for the playing area, and possible movement, but without using the grid check for the playing area, and then using grid checks for collisions with other objects.

// going left
new_x = x - 8;
new_y = y + drop_speed;
width = sprite_width/ 2;
if (new_x - width) >= playing side left
{
// check for collisions on the grid using new_x / new_y as starting point, instead of current x / y
// functions like sprite_get_xoffset / yoffset and image_angle can help figure out what a rotated object occupies from these new_x / y coordinates
if no collisions
{x = new_x;
y = new_y;
}
else
{
//does nothing for movement.
}
}

Something like that, but I haven't fully figured it out yet....

As my project currently stands: If it tries to move into a occupied cell it automatically stops, so it has to be refined to know the difference between being "stuck" (settled) and a predicted collision from predicted movement. As is, it just treats them the same, so I'm going to carry on looking into it.
 
Top