GMS 2 Multiple collisions: getting a value from one instance

G

Grimbel

Guest
Hello everyone!

In my game, the player object may happen to collide with multiple instances at the same time. All of those instances come from a same object, but some of them may carry different values for the same variable. What I need to do is to take the highest of all those values and to ignore or discard the rest. How can I do that?

I can give an example if necessary:
There are 3 different instances called "o_wall", all of them with the variable "z_ground_". In one of them the variable "z_ground_" is equal to 0, in the second one it is equal to 16, and in the last one it is equal to 32. I need to take in consideration the value of the last one (32) and ignore the other 2.

At the moment, with my current code, the player object just takes one of those three values, but I can't choose which one. This is what it looks like:

Code:
var _block;
if place_meeting(x+horizontal_speed_, y, o_wall_) {
    _block = (instance_place(x+horizontal_speed_, y, o_wall_));
    if _block.z_ground_ > z_ {
        while !place_meeting(x + sign(horizontal_speed_), y, o_wall_) {
            x += sign(horizontal_speed_);
        }
        horizontal_speed_ = 0;
    }
}

Thank you in advance for helping. I hope what I am asking to do is clear enough.
 

samspade

Member
Hello everyone!

In my game, the player object may happen to collide with multiple instances at the same time. All of those instances come from a same object, but some of them may carry different values for the same variable. What I need to do is to take the highest of all those values and to ignore or discard the rest. How can I do that?

I can give an example if necessary:
There are 3 different instances called "o_wall", all of them with the variable "z_ground_". In one of them the variable "z_ground_" is equal to 0, in the second one it is equal to 16, and in the last one it is equal to 32. I need to take in consideration the value of the last one (32) and ignore the other 2.

At the moment, with my current code, the player object just takes one of those three values, but I can't choose which one. This is what it looks like:

Code:
var _block;
if place_meeting(x+horizontal_speed_, y, o_wall_) {
    _block = (instance_place(x+horizontal_speed_, y, o_wall_));
    if _block.z_ground_ > z_ {
        while !place_meeting(x + sign(horizontal_speed_), y, o_wall_) {
            x += sign(horizontal_speed_);
        }
        horizontal_speed_ = 0;
    }
}

Thank you in advance for helping. I hope what I am asking to do is clear enough.
The basic process is either:

Get a list of all instances the player is colliding with
loop through those instances and select the highest value

or

loop through all instances of the type the player could collide with
for any instance the player is colliding with
check the important value
if that value is higher than the last value, store that value and the instance id if necessary
at the end of the loop use the value/instance id as you want

That's in English of course and I'm generalizing some of the steps, but if that doesn't make sense, both methods are laid out in this:

with Block Recipe Cards

The collision list function (which you can read about in the manual) might be helpful but is overall unnecessary.
 
G

Grimbel

Guest
Thank you very much for your reply, samspade!

I read it yesterday night, and I didn't want to answer yet because I wanted to try and see if I would manage to make it work by myself (I am still pretty new at coding). Unfortunately, after hours reading and researching I haven't succeded (yet), but I have learnt a lot of useful things thanks to the information you gave me.

I guess once I get the value from the different instances I need to somehow use this code from the page you gave me to figure out what is the highest value:
Code:
var val, highest, highest_val;
highest = noone;
highest_val = -1;
with (object_or_all) {
  val = property;
  if (highest == noone || val > highest_val) {
   highest = id;
   highest_val = val;
  }
}
if (highest != noone) {
  /* Use highest here */
}
I am not quite there yet, though.

I understand that if I use the next code:
Code:
var _list = ds_list_create();
var _num = instance_place_list(x, y, block_, _list, false);
I can know how many blocks I am colliding with, but I don't know how to get any value from them. Any help?
 

samspade

Member
The problem isn't the easiest. As I understand it, you want pretty standard collision code but you want it to take into account the z value.

Code:
var _block = noone;
if (place_meeting(x + horizontal_speed_, y, o_wall_)) {
    _block = scr_find_highest_wall();
    if (_block != noone) {
        if (_block.z_ground > z_) {
            while (!place_meeting(x + sign(horizontal_speed_), y, _block)) {
                x += sign(horizontal_speed_);
            }
            horizontal_speed_ = 0;
        }
    }
}


///scr_find_highest_wall
var val, highest, highest_val;
highest = noone;
highest_val = -1;
with (o_wall_) {
  if (!place_meeting(x + horizontal_speed_, y, other)) {
    continue;
  }
  val = z_ground;
  if (highest == noone) || (val > highest_val) {
   highest = id;
   highest_val = val;
  }
}
return highest;
In English
If there is a collision at x + hsp
then, find the highest wall
if there is a highest wall (and there always should be)
compare the walls z to the players z and if the wall is higher
do your standard collision code

find the highest wall
with every wall
if it doesn't meet the qualification, check the next wall
otherwise, check to see if this is the highest value,
if so, save it and keep checking
return the instance id with the highest value

I might have missed something there or gotten a variable wrong. You also don't need the script, and you shouldn't need the _block != noone check as its position in the loop should guarantee that _block never equals noone. But I used a script to keep it more readable, and because you might want to use this for other objects, and I did the check for a similar reason.

This version is inefficient as well as it basically does two identical collision checks, but its a lot more understandable in this form (at least in my mind) and isn't that much slower. The better version would be to make a collision list, loop through just that list to find the highest instance, and then use that instance id in your collision code instead of the object id.
 
G

Grimbel

Guest
We are almost there! Thank you so much again.

I think the script to get the highest value is not working properly, though, and I think I know why, but I don't know how to solve it.

The thing is that since we are using a "with", the "place_meeting" is working from the point of view of the o_wall instead of the o_player, which of course has no horizontal_speed_ and even if it had, it wouldn't solve much.
Is it possible to easily change the "place_meeting" back to the point of view of the o_player?

I am really sorry I am still such a noob. Thank you for your patience, really.

As I understand it, you want pretty standard collision code but you want it to take into account the z value.
You are totally right. I am working on a top down platformer, and I want the player to be able to walk on "blocks" only if their "surface" is at the level of the player object or below. Otherwise, they should act as a wall.
I solved the problem of meeting several blocks at once by changing the collision mask to only 1 pixel, and it worked pretty well for the most part, but it had some really ugly consequences for the depth of the o_player and I thought it would all be better if I could find a way of working with a bigger collision mask.
If you are interested, you can check a little video I made when I wanted to post the problem here (although I decided not to show it at the end).

 

samspade

Member
You are right. I missed the other for the collision check inside the with statement.

Code:
with (o_wall_) {
  if (!place_meeting(other.x + other.horizontal_speed_, other.y, other)) {
   continue;
  }
I might have missed other things as well.
 
G

Grimbel

Guest
Yeah, I tried that too. Still doesn't work.

Anyway, maybe this is not the moment for me yet to do this. I will keep working on other things, and whenever I feel my knowledge of coding is better I can try again.

You have been really useful, anyway. I have learnt more than a couple of things thanks to you.
 

immortalx

Member
This won't probably solve your problem, but I'm just posting it here as a use of the instance_place_list function that was mentioned:
Code:
// oPlayer create
wall_with_highest_val = noone;
highest_val = -1;

// oPlayer step
var _list = ds_list_create();
var _num = instance_place_list(x, y, oWall, _list, false);

if (_num > 0)
{
    for (var i = 0; i < _num; ++i)
    {
        cur_inst = _list[| i];

        if (cur_inst.z_ground > highest_val)
        {
            highest_val = cur_inst.z_ground;
            wall_with_highest_val = cur_inst;
        }
    }  
}
This will get the id of the wall instance with the higher z_ground and its z_ground value;
 
Top