Help with "with" to affect all instances of an object.

Pkirkby

Member
Hi everyone, I have an object I call "oLighting" it's an accumulation of code that I've picked up on tutorials and forums, and it creates a light source that affects all my objects under my parent object. It works great, however I'm having troubles with the "with" function, as I already have all the code under a "with".

Here's my code:

// if `distance` is below `min_distance` then the instance is fully visible



with(oParent)
{
//variables for shadows
var min_distance = 2;
var max_distance = 150;
var distance = point_distance(oLamp.x, oLamp.y, x, y);
var shadow_alpha = 0.8 - clamp((distance - min_distance) / (max_distance - min_distance), 0, 0.8);

var sx = oLamp.x - x;
var sy = oLamp.y - y;

//Draw Shadow

gpu_set_fog(true,c_black,0,1);
draw_sprite_pos(
sprite_index,
image_index,
x - sprite_width / 2 - sx,
y-sy,
x + sprite_width / 2 - sx,
y-sy,
x + sprite_width / 3,
y,
x - sprite_width / 2,
y,
shadow_alpha + global.shadow_flicker);


//draw_sprite_ext(sprite_index,image_index,x-5,y-5,image_xscale,image_yscale,0,c_black,0.5);

//This sets fog to disable,white is default.
gpu_set_fog(false,c_white,0,0);

}

So essentially this code gives everything a shadow, it works really nice, but it only works with one light. As of right now I made the origin of the lightsource come from an object called "oLamp" but again, given that I already have a with, I cant seem to make this affect all "oLamp" objects/instances. I've read the manual on "withs" and cant seem to figure out the whole "other.object" stuff, sorry I still have alot to learn, appreciate any help.
 

YoSniper

Member
I am unsure if you can have a "with" block within another "with" block. If so, use the nested "with" block to cover all instances of oLamp, and that should do the trick.

Otherwise, you might have to create a ds_list outside of the "with" block and load it with all of the object ids of the oLamp objects. Then, within the "with" block, use a "for" loop to iterate through the ds_list and create the shadows as needed.
 

Pkirkby

Member
I am unsure if you can have a "with" block within another "with" block. If so, use the nested "with" block to cover all instances of oLamp, and that should do the trick.

Otherwise, you might have to create a ds_list outside of the "with" block and load it with all of the object ids of the oLamp objects. Then, within the "with" block, use a "for" loop to iterate through the ds_list and create the shadows as needed.
Yeah it didn't seem like nesting with blocks worked (for me anyways) but I'm going to look into the other way you mention, how would I incorporate the "for" once I've created a list? Thanks for the response.
 

YoSniper

Member
Here's what I would imagine your code to look like in the end:

Code:
// if `distance` is below `min_distance` then the instance is fully visible

var dummy_list;
dummy_list = ds_list_create();
with(oLamp) {
    ds_list_add(dummy_list, id);
}

with(oParent)
{
//variables for shadows
var min_distance = 2;
var max_distance = 150;
var distance = point_distance(oLamp.x, oLamp.y, x, y);
var shadow_alpha = 0.8 - clamp((distance - min_distance) / (max_distance - min_distance), 0, 0.8);

var ii;
for(ii = 0; ii < ds_list_size(dummy_list); ii += 1) {
    var dummy = ds_list_find_value(dummy_list, ii);
    var sx = dummy.x - x;
    var sy = dummy.y - y;
    
    //Draw Shadow
    
    gpu_set_fog(true,c_black,0,1);
    draw_sprite_pos(
        sprite_index,
        image_index,
        x - sprite_width / 2 - sx,
        y-sy,
        x + sprite_width / 2 - sx,
        y-sy,
        x + sprite_width / 3,
        y,
        x - sprite_width / 2,
        y,
        shadow_alpha + global.shadow_flicker);
    
    //draw_sprite_ext(sprite_index,image_index,x-5,y-5,image_xscale,image_yscale,0,c_black,0.5);
    
    //This sets fog to disable,white is default.
    gpu_set_fog(false,c_white,0,0);
}

ds_list_destroy(dummy_list); //Frees up memory

}
 

Pkirkby

Member
Great, it's good to see how you laid it out, as I'm still learning all this stuff. I'm going to work on it this week, and see how I fare. Thanks a lot.
 
You can have a with within a, uh...with. The problem is that of scope (I think) when you are applying it to several objects within one group, that then apply it to several objects of another group.

I can't quite figure the consequences of that out in my head, but I imagine it being a problem. I can tell you of a way that I've used with within a with.

I have an object that is creating a duplicate of itself, and is storing the id. It is possible to access the parent of the creator object using with, and then have them use with to access the created object.

create event of parent object:
Code:
dupe = instance_create(etc)
event being called outside of parent object:
Code:
var whatever = whatever;
var whatever = whatever;
etc
with (parent_object)
{// can access the local variables being defined in the event above ^^^^ even though they are outside of itself
do whatever with those variables, then pass the outcome to its "dupe"
with (dupe)
{
can still refer to the local variables first defined in the original object that started this process (it's linked into it by
being called through the parent object), or the parent object being called in the with can then manipulate it in other ways
}
}
However: in this case the second with is being applied to only one id per each instance of the parent group, whereas if it wasn't then you'd have each parent object being told to access every instance of the second group and that sounds like it shouldn't work.

Based on a quick look at your code -'oLamp' is an object, so by referring to it you are only getting one instance of that object used. You want to be referring to a specific instance of 'oLamp' and that is not the same process. Something like "instance_nearest" would be a cheap way of finding a specific id, and then you could do the nested with that I described above.

But in this case it's not very precise, as the nearest lamp might be behind a wall or some other object that should obscure it's light. YoSniper' set up should work, but I think there is a small change needed - which I have altered below.

@YoSniper
Code:
var distance = point_distance(oLamp.x, oLamp.y, x, y);
This line is going to cause a problem, because oLamp is an object. The "distance" and "shadow_alpha" variables probably need to be done during the loop where they get the ids of the lamps, and should be applied to 'dummy' instead

Code:
var ii;
for(ii = 0; ii < ds_list_size(dummy_list); ii += 1) {
    var dummy = ds_list_find_value(dummy_list, ii);
var distance = point_distance(dummy.x, dummy.y, x, y);
var shadow_alpha = 0.8 - clamp((distance - min_distance) / (max_distance - min_distance), 0, 0.8);
Dunno if I figured that correctly, but they definitely want a specific id rather than accessing the object id.
 

Pkirkby

Member
You can have a with within a, uh...with. The problem is that of scope (I think) when you are applying it to several objects within one group, that then apply it to several objects of another group.

I can't quite figure the consequences of that out in my head, but I imagine it being a problem. I can tell you of a way that I've used with within a with.

I have an object that is creating a duplicate of itself, and is storing the id. It is possible to access the parent of the creator object using with, and then have them use with to access the created object.

create event of parent object:
Code:
dupe = instance_create(etc)
event being called outside of parent object:
Code:
var whatever = whatever;
var whatever = whatever;
etc
with (parent_object)
{// can access the local variables being defined in the event above ^^^^ even though they are outside of itself
do whatever with those variables, then pass the outcome to its "dupe"
with (dupe)
{
can still refer to the local variables first defined in the original object that started this process (it's linked into it by
being called through the parent object), or the parent object being called in the with can then manipulate it in other ways
}
}
However: in this case the second with is being applied to only one id per each instance of the parent group, whereas if it wasn't then you'd have each parent object being told to access every instance of the second group and that sounds like it shouldn't work.

Based on a quick look at your code -'oLamp' is an object, so by referring to it you are only getting one instance of that object used. You want to be referring to a specific instance of 'oLamp' and that is not the same process. Something like "instance_nearest" would be a cheap way of finding a specific id, and then you could do the nested with that I described above.

But in this case it's not very precise, as the nearest lamp might be behind a wall or some other object that should obscure it's light. YoSniper' set up should work, but I think there is a small change needed - which I have altered below.

@YoSniper
Code:
var distance = point_distance(oLamp.x, oLamp.y, x, y);
This line is going to cause a problem, because oLamp is an object. The "distance" and "shadow_alpha" variables probably need to be done during the loop where they get the ids of the lamps, and should be applied to 'dummy' instead

Code:
var ii;
for(ii = 0; ii < ds_list_size(dummy_list); ii += 1) {
    var dummy = ds_list_find_value(dummy_list, ii);
var distance = point_distance(dummy.x, dummy.y, x, y);
var shadow_alpha = 0.8 - clamp((distance - min_distance) / (max_distance - min_distance), 0, 0.8);
Dunno if I figured that correctly, but they definitely want a specific id rather than accessing the object id.
Ok, when. That's a lot to take in, I'm going to work away and hopefully see something happen soon and get back to you. Much appreciation!
 

YoSniper

Member
@YoSniper
Code:
var distance = point_distance(oLamp.x, oLamp.y, x, y);
This line is going to cause a problem, because oLamp is an object. The "distance" and "shadow_alpha" variables probably need to be done during the loop where they get the ids of the lamps, and should be applied to 'dummy' instead
Code:
var ii;
for(ii = 0; ii < ds_list_size(dummy_list); ii += 1) {
    var dummy = ds_list_find_value(dummy_list, ii);
var distance = point_distance(dummy.x, dummy.y, x, y);
var shadow_alpha = 0.8 - clamp((distance - min_distance) / (max_distance - min_distance), 0, 0.8);
Dunno if I figured that correctly, but they definitely want a specific id rather than accessing the object id.
Yeah, I missed that one line. I meant to replace every occurrence of "oLamp" within the for loop with "dummy".
And yeah, I was under the impression that nesting a with block within another with block would cause more headaches than needed, so I created a list of all instance IDs of oLamp, and then iterated through that list to apply the appropriate collision detection. It was basically the same thing as using another "with" block, except that the reference of "self" and "other" would not be altered.
 

Pkirkby

Member
Well, I tried implementing these ideas, and it could just be me missing something, but no go as of yet. Anyone have any other ideas to try? Thanks alot.
 

TheouAegis

Member
with inside with is simple for GM to handle. It seems to just be a normal stack operation and as such causes stack overflows as well. The only tricky part is calling back to any instances farther down the stack than the last one to call with(). But yeah, people have been using with recursively for a long time until Studio's drastic stack limits were implemented.
 

FrostyCat

Redemption Seeker
For people who have never seen it before, yes, it's 100% valid to nest with blocks. And like how a nested for block gives you i-j pairings, a nested with block gives you instance-instance pairings.
Code:
with (oParent) {
  with (oLamp) {
    /* This runs once for every pairing of an oParent instance (other) and an oLamp instance (self) */
  }
}
Or if you want to reverse the relationship:
Code:
with (oParent) {
  with (oLamp) {
    with (other) {
      /* This runs once for every pairing of an oParent instance (self) and an oLamp instance (other) */
    }
  }
}
With the second form, you can more or less just plug in your current code, but with oLamp replaced by other.

It's easy to validate my claim if you treat each with block as a loop through instances and trace through the role reversals:
Code:
/* self: Calling instance; other: noone */
with (oParent) {
  /* self: Outer loop's current oParent instance; other: Calling instance */
  with (oLamp) {
    /* self: Inner loop's current oLamp instance; other: Outer loop's current oParent instance */
    with (other) {
      /* self: Outer loop's current oParent instance; other: Inner loop's current oLamp instance */
    }
  }
}
 

Pkirkby

Member
For people who have never seen it before, yes, it's 100% valid to nest with blocks. And like how a nested for block gives you i-j pairings, a nested with block gives you instance-instance pairings.
Code:
with (oParent) {
  with (oLamp) {
    /* This runs once for every pairing of an oParent instance (other) and an oLamp instance (self) */
  }
}
Or if you want to reverse the relationship:
Code:
with (oParent) {
  with (oLamp) {
    with (other) {
      /* This runs once for every pairing of an oParent instance (self) and an oLamp instance (other) */
    }
  }
}
With the second form, you can more or less just plug in your current code, but with oLamp replaced by other.

It's easy to validate my claim if you treat each with block as a loop through instances and trace through the role reversals:
Code:
/* self: Calling instance; other: noone */
with (oParent) {
  /* self: Outer loop's current oParent instance; other: Calling instance */
  with (oLamp) {
    /* self: Inner loop's current oLamp instance; other: Outer loop's current oParent instance */
    with (other) {
      /* self: Outer loop's current oParent instance; other: Inner loop's current oLamp instance */
    }
  }
}
I appreciate the response and explanation, I'm obviously still a noob, so I'm trying to wrap my head around it. I tried something like this:

if (instance_exists(oMoon))
{
with(oParent)
{
with (oLight)
{
with (other)
{
//variables for shadows
var min_distance = 2;
var max_distance = 150;
var distance = point_distance(oLight.x, oLight.y, x, y);
var shadow_alpha = 0.6 - clamp((distance - min_distance) / (max_distance - min_distance), 0, 0.6);

var sx = oLight.x - x;
var sy = oLight.y - y;

//Draw Shadow
gpu_set_fog(true,c_black,0,1);
draw_sprite_pos(
sprite_index,
image_index,
x - sprite_width / 1.5 - sx,
y-sy,
x + sprite_width / 1.5 - sx,
y-sy,
x + sprite_width / 2,
y,
x - sprite_width / 2,
y,
shadow_alpha + global.shadow_flicker);

//draw_sprite_ext(sprite_index,image_index,x-5,y-5,image_xscale,image_yscale,0,c_black,0.5);

//This sets fog to disable,white is default.
gpu_set_fog(false,c_white,0,0);
}
}
}
}
 

FrostyCat

Redemption Seeker
Never refer to instances by the object ID when there are multiple instances of the object around. Learn the difference between an object and an instance. You don't talk about "human's eye colour" in a room full of people, or "cat's fur colour" in an animal shelter at peak kitten season.

This is the kind of code that you must learn to instinctively recognize and call out as wrong:
Code:
var distance = point_distance(oLight.x, oLight.y, x, y);
Code:
var sx = oLight.x - x;
var sy = oLight.y - y;
Before you put an object ID on the left side of a variable reference dot again, ask yourself, "Is there one and only one instance of this?" If you cannot answer yes with complete certainty, the code cannot be right. You must stay your hand and find the correct instance ID to use instead.

I have already said in my previous post that the 3-fold version repeats the innermost code once for every pairing of oParent instances (as self) and oLight instances (as other). So follow the instructions and use other instead of oLight. That gives you an instance-specific reference, oLight doesn't.
Code:
with(oParent)
{
  with (oLight)
  {
    with (other)
    {
      //variables for shadows
      var min_distance = 2;
      var max_distance = 150;
      var distance = point_distance(other.x, other.y, x, y);
      var shadow_alpha = 0.6 - clamp((distance - min_distance) / (max_distance - min_distance), 0, 0.6);

      var sx = other.x - x;
      var sy = other.y - y;
      ...
    }
  }
}
 

Pkirkby

Member
Never refer to instances by the object ID when there are multiple instances of the object around. Learn the difference between an object and an instance. You don't talk about "human's eye colour" in a room full of people, or "cat's fur colour" in an animal shelter at peak kitten season.

This is the kind of code that you must learn to instinctively recognize and call out as wrong:
Code:
var distance = point_distance(oLight.x, oLight.y, x, y);
Code:
var sx = oLight.x - x;
var sy = oLight.y - y;
Before you put an object ID on the left side of a variable reference dot again, ask yourself, "Is there one and only one instance of this?" If you cannot answer yes with complete certainty, the code cannot be right. You must stay your hand and find the correct instance ID to use instead.

I have already said in my previous post that the 3-fold version repeats the innermost code once for every pairing of oParent instances (as self) and oLight instances (as other). So follow the instructions and use other instead of oLight. That gives you an instance-specific reference, oLight doesn't.
Code:
with(oParent)
{
  with (oLight)
  {
    with (other)
    {
      //variables for shadows
      var min_distance = 2;
      var max_distance = 150;
      var distance = point_distance(other.x, other.y, x, y);
      var shadow_alpha = 0.6 - clamp((distance - min_distance) / (max_distance - min_distance), 0, 0.6);

      var sx = other.x - x;
      var sy = other.y - y;
      ...
    }
  }
}
 

Pkirkby

Member
Oh, I get it a bit better, thanks for expanding. I just had no experience with "with." I'm going to give it a shot and I'll let you know, thanks
 
Top