GML Problem while moving various parented instances.

N

Noizee

Guest
Hey. I'm making a grid based movement game with infinite backgound (I'm doing this by moving the player once it gets to the limits I stablished).

Once it surpasses, say, 300px before the border of the room, this is what I wrote:

Code:
if (warping=true) && (global.warp_all=false)
   {
   warping=false;
   global.warp_all=true;
   }
I turn "warping" to true every time the player is in a position in the grid where it is leaving the said parameters.
Everything normal until here.
After that wrote this in the step event of a parent object I called obj_ent (Which I'm using for every instance that isn't the player in the level so I can move every other instance with it).

Code:
if (global.warp_all=true)
{
 if (instance_exists(obj_player))
  {
  if (id.x > obj_player.x)
  {
  id.x = (obj_player.x + (id.x - obj_player.x))
  }
  if (id.x < obj_player.x)
  {
  id.x = (obj_player.x - (obj_player.x - id.x))
  }
  if (id.y > obj_player.y)
  {
  id.y = (obj_player.y + (id.y - obj_player.y))
  }
  if (id.y < obj_player.y)
  {
  id.y = (obj_player.y - (obj_player.y - id.y))
  }
  }
  global.warp_all=false
}

What this does is calculating the difference in position between the object and the player, and then adding or subtracting that difference. If done right, that would make seem as if no movement happened whatsoever.
But I've been searching in ALL the coding for an hour and a half straight and I have no idea why nothing at all is happening!
No instances besides the player are moving!

By debugging, I saw that both variables are indeed working and switching from true to false for a whole step.
I've tried to put the code in the step events of the enemy instances but the code didn't work either.
I've used event_inherited when needed, and tried with the begin step and end step events, but nothing worked.

I assumed I missunderstood the usage of "id" or "self" (which I was using), but tried everything (even storing the id in a variable and then using it) and nothing happened.

The only way I get the instances to move was by doing it in a keyboard event.

Can someone help me with this?

Thank you in advance.
 

Rob

Member
a couple of things will help you:

At the moment you're running that code in every object but the player. As soon as one instance has run its step event, the global will get set to false. I think that's the issue.

To get or set an instances x/y coordinate you can just type x and y rather than id.x

What I would recommend is having a controller object and inside that objects step event do this:

Code:
if global.warp_all == true{

with obj_ent{
   //put all your previous code here but without the warp variables
}

global.warp_all = false;

}
The with statement will cycle through all of the instances of obj_ent and the code you put inside it is treated the same as if the code were inside the step event of the obj_ent instance itself.

After every instance of obj_ent has been done in the with statement, only then will the global be set to false.
 
Last edited:
N

Noizee

Guest
a couple of things will help you:

At the moment you're running that code in every object but the player. As soon as one instance has run its step event, the global will get set to false. I think that's the issue.

To get or set an instances x/y coordinate you can just type x and y rather than id.x

What I would recommend is having a controller object and inside that objects step event do this:

Code:
if global.warp_all == true{

with obj_ent{
   //put all your previous code here but without the warp variables
}

global.warp_all = false;

}
The with statement will cycle through all of the instances of obj_ent and the code you put inside it is treated the same as if the code were inside the step event of the obj_ent instance itself.

After every instance of obj_ent has been done in the with statement, only then will the global be set to false.
Thank you bunch. I'll try this.
I figured out yesterday, though, that what was happening was that te x and y target coordinates where the same places the instances were in the first place, because I took the x and y of the player in the moment it warps and then used the same coordinates, when I needed to use the new coordinates and then calculate the difference between the last position.

Anyway, the solution you provided is more than helpful and will help to clean my code a little bit. Thank you ;).

I'll be posting updates.
 
If you go with the suggestion of a controller object, as Rob said, then this might be the best way to do it - as you have some unnecessary checking going on currently. If you have a result that will be called more than once (in this case the obj_player x / y positions) then you want to store them as a local variable. Rechecking that temporary variable is less effort than redoing it, which you are doing three times in your current IF statement. You can do the same with self x / y, as that is also something repeatedly checked.

It may also be the case that, even though these are a parented object (which is meant to cut down on effort), by calling those obj_player results in one object, and then using the WITH statement to pass the local variable through to them, it is better. I'm certain my first suggestion will optimize your code, but with the second suggestion I'm a little unsure.

Code:
if global.warp_all
{
 if (instance_exists(obj_player))
 {
 
var xx = obj_player.x;  // define this once - as it's better than recalling it at each stage in the code block. Whether you have it here.......
var yy = obj_player.y; // define this once - as it's better than recalling it at each stage in the code block. Whether you have it here.......

with (obj_ent)
{// ^^^^ or place var xx etc here - either way the result should be better than the current way you have
// ^^^^ or place var yy etc here - either way the result should be better than the current way you have
var is_x = x;
var is_y = y;

if  is_x > xx
{
x = (xx + (is_x - xx))
}
 
if  is_x < xx
{
x = (xx - (xx - is_x))
}

if is_y > yy
{
y = (yy + (is_y - yy))
}
 
if is_y < yy
{
y = (yy - (yy - is_y))
}
}
}
  global.warp_all=false
}
 
N

Noizee

Guest
If you go with the suggestion of a controller object, as Rob said, then this might be the best way to do it - as you have some unnecessary checking going on currently. If you have a result that will be called more than once (in this case the obj_player x / y positions) then you want to store them as a local variable. Rechecking that temporary variable is less effort than redoing it, which you are doing three times in your current IF statement. You can do the same with self x / y, as that is also something repeatedly checked.

It may also be the case that, even though these are a parented object (which is meant to cut down on effort), by calling those obj_player results in one object, and then using the WITH statement to pass the local variable through to them, it is better. I'm certain my first suggestion will optimize your code, but with the second suggestion I'm a little unsure.

Code:
if global.warp_all
{
 if (instance_exists(obj_player))
 {
 
var xx = obj_player.x;  // define this once - as it's better than recalling it at each stage in the code block. Whether you have it here.......
var yy = obj_player.y; // define this once - as it's better than recalling it at each stage in the code block. Whether you have it here.......

with (obj_ent)
{// ^^^^ or place var xx etc here - either way the result should be better than the current way you have
// ^^^^ or place var yy etc here - either way the result should be better than the current way you have
var is_x = x;
var is_y = y;

if  is_x > xx
{
x = (xx + (is_x - xx))
}
 
if  is_x < xx
{
x = (xx - (xx - is_x))
}

if is_y > yy
{
y = (yy + (is_y - yy))
}
 
if is_y < yy
{
y = (yy - (yy - is_y))
}
}
}
  global.warp_all=false
}
Thank you dude (also Big Lebowski is the best movie in history).

I think I got the mechanic working. Here's a gif of the product:



As you can see, the simplified coordinates are changing, and everything is working fine.

As dude said, I simplified my coding and added the local variable with the player position (I already had a control object so I putted the code there):

Code:
/// Warping
var xwarp,ywarp,pla_exi;
xwarp = obj_player.xwarp
ywarp = obj_player.ywarp

if (!global.warp_all == "noone") {

with obj_ent
{
   if (instance_exists(obj_player))
{
  if (global.warp_all=="left")
  {
   x += xwarp
   if variable_instance_exists(id,"xtar") {xtar+=xwarp}
  }
 
  if (global.warp_all=="right")
  {
   x -= xwarp
   if variable_instance_exists(id,"xtar") {xtar-=xwarp}
  }
 
  if (global.warp_all=="up")
  {
   y += ywarp
   if variable_instance_exists(id,"ytar") {ytar+=ywarp}
  }
 
  if (global.warp_all=="down")
  {
   y -= ywarp
   if variable_instance_exists(id,"ytar") {ytar-=ywarp}
  }
}
}
global.warp_all = "noone"
}
Here the event checks where the player is warping and simply moves every other instance the same ammount of pixels, and moves the target position of the enemies the same ammount so they don't try to get to an old one. I don't know why I made the old code so tangled.
Though I think the same could be applied to "if variable..." isn't it?
Like, assigning that function to a variable to clean the code even more.
 
Though I think the same could be applied to "if variable..." isn't it?
Like, assigning that function to a variable to clean the code even more.
You are correct - that can be assigned once, and then rechecked later. However, you have the condition now being set to global.warp_all, and so all those variable_instance_exists will not be run. Only one of them will be done, as global.warp_all can only be one result. If you want to condense it a little further: global.warp_all is now in a form that would fit a switch statement.

Code:
var xwarp,ywarp,pla_exi;
xwarp = obj_player.xwarp
ywarp = obj_player.ywarp

if (!global.warp_all == "noone") {

with obj_ent
{
   if (instance_exists(obj_player))
{
switch (global.warp_all)
  {case "left":
   x += xwarp
   if variable_instance_exists(id,"xtar") {xtar+=xwarp}
  break;
 
  case "right":
  x -= xwarp
   if variable_instance_exists(id,"xtar") {xtar-=xwarp}
  break;
 
case "up":
 y += ywarp
 if variable_instance_exists(id,"ytar") {ytar+=ywarp}
 break;
 
 case "down":
 y -= ywarp
 if variable_instance_exists(id,"ytar") {ytar-=ywarp}
 break;
}
}
global.warp_all = "noone"
}
Hmm! I've somehow added something unnecessary to your code - will have to edit it. Or it might not be a mistake, as you have it here:
Code:
if (!global.warp_all == "noone") {
and it seems it should be this:
Code:
if (global.warp_all != "noone") {
noone is a recognized value by GMS, so actually using it there instead of a string might be better (reading strings is slower, I think)
Code:
if (global.warp_all != noone) {
and you might find the same is true for the other strings - turning them into a real value (1 / 2 / 3 / 4 etc) would be a bit faster than comparing strings.
 
Last edited:
N

Noizee

Guest
You are correct - that can be assigned once, and then rechecked later. However, you have the condition now being set to global.warp_all, and so all those variable_instance_exists will not be run. Only one of them will be done, as global.warp_all can only be one result. If you want to condense it a little further: global.warp_all is now in a form that would fit a switch statement.

Code:
var xwarp,ywarp,pla_exi;
xwarp = obj_player.xwarp
ywarp = obj_player.ywarp

if (!global.warp_all == "noone") {

with obj_ent
{
   if (instance_exists(obj_player))
{
switch (global.warp_all)
  {case "left":
   x += xwarp
   if variable_instance_exists(id,"xtar") {xtar+=xwarp}
  break;
 
  case "right":
  x -= xwarp
   if variable_instance_exists(id,"xtar") {xtar-=xwarp}
  break;
 
case "up":
 y += ywarp
 if variable_instance_exists(id,"ytar") {ytar+=ywarp}
 break;
 
 case "down":
 y -= ywarp
 if variable_instance_exists(id,"ytar") {ytar-=ywarp}
 break;
}
}
global.warp_all = "noone"
}
Hmm! I've somehow added something unnecessary to your code - will have to edit it. Or it might not be a mistake, as you have it here:
Code:
if (!global.warp_all == "noone") {
and it seems it should be this:
Code:
if (global.warp_all != "noone") {
noone is a recognized value by GMS, so actually using it there instead of a string might be better (reading strings is slower, I think)
Code:
if (global.warp_all != noone) {
and you might find the same is true for the other strings - turning them into a real value (1 / 2 / 3 / 4 etc) would be a bit faster than comparing strings.
Thank you dude! That helps me a lot.
Also, I tend to use strings so I don't confuse myself later (though I know I can make that clear by comment lines). While I understand it is slower, I'm making a small scoped game I think it won't be a problem. Anyway, thank you for that tip too.

However, I didn't understood this particular part of what you wrote (maybe my english is a bit rusty):

However, you have the condition now being set to global.warp_all, and so all those variable_instance_exists will not be run. Only one of them will be done, as global.warp_all can only be one result.
 
However, I didn't understood this particular part of what you wrote (maybe my english is a bit rusty):

However, you have the condition now being set to global.warp_all, and so all those variable_instance_exists will not be run. Only one of them will be done, as global.warp_all can only be one result.
Because you have a controlling condition, only one of those 4 possibilities will ever be true. So there won't be any gain from setting variable_instance_exists to a variable, because it is only called once in the code block. If you were calling it twice, or more, then it is worth holding it in memory. But the way this is now set up makes that unnecessary.

As far as I know: even though it is present in the switch statement, that code is not actually checked if the case is not true. If that is incorrect, then yeah - saving it to a variable would cut down some of the effort. I'd be interested to know how the unused code in a switch statement works, but it's a bit beyond my understanding :)

EDIT: I'm not the best person to qualify this, but in my experience the setting of repeatedly used values to variables makes a big difference (even when they're inbuilt ones like false / true / noone / numbers etc). If you end up doing something more taxing / advanced it can give a big boost in performance.

There also seems to be some quirk in how local variables are looked up by the engine, where some circumstances get a boost from doing it a certain way. This is the forum page where it is discussed. I can't comment on the technical stuff behind it, but having used it myself saw almost 20 fps improvement after implementing it in about a fifth of my code so far. Doesn't sound a lot (a bit over 33% of a boost, as it would go as low as 45 or so), but it struggled to hit 60 fps so this is helpful.

https://forum.yoyogames.com/index.php?threads/optimizing-code-for-yyc.62937/
 
Last edited:
N

Noizee

Guest
Because you have a controlling condition, only one of those 4 possibilities will ever be true. So there won't be any gain from setting variable_instance_exists to a variable, because it is only called once in the code block. If you were calling it twice, or more, then it is worth holding it in memory. But the way this is now set up makes that unnecessary.

As far as I know: even though it is present in the switch statement, that code is not actually checked if the case is not true. If that is incorrect, then yeah - saving it to a variable would cut down some of the effort. I'd be interested to know how the unused code in a switch statement works, but it's a bit beyond my understanding :)

EDIT: I'm not the best person to qualify this, but in my experience the setting of repeatedly used values to variables makes a big difference (even when they're inbuilt ones like false / true / noone / numbers etc). If you end up doing something more taxing / advanced it can give a big boost in performance.

There also seems to be some quirk in how local variables are looked up by the engine, where some circumstances get a boost from doing it a certain way. This is the forum page where it is discussed. I can't comment on the technical stuff behind it, but having used it myself saw almost 20 fps improvement after implementing it in about a fifth of my code so far. Doesn't sound a lot (a bit over 33% of a boost, as it would go as low as 45 or so), but it struggled to hit 60 fps so this is helpful.

https://forum.yoyogames.com/index.php?threads/optimizing-code-for-yyc.62937/
I'll definitely check that link. Thank you bunch.

But I think I might not understand what you explained yet. I'm sorry.
The xtar and ytar of the id are checked because that parent object is asigned to various instances, and some of them aren't enemies with target positions, so that's what's being checked there.
 
The code you have here is fine. My trying to explain this seems to be muddying the waters, so I'll just leave it at that. Other than changing the value of "global.warp_all" from strings to a real value, as far as I can see this is as optimized as it can be for performance.
Code:
/// Warping
var xwarp,ywarp,pla_exi;
xwarp = obj_player.xwarp
ywarp = obj_player.ywarp

if (!global.warp_all == "noone") {

with obj_ent
{
   if (instance_exists(obj_player))
{
  if (global.warp_all=="left")
  {
   x += xwarp
   if variable_instance_exists(id,"xtar") {xtar+=xwarp}
  }
 
  if (global.warp_all=="right")
  {
   x -= xwarp
   if variable_instance_exists(id,"xtar") {xtar-=xwarp}
  }
 
  if (global.warp_all=="up")
  {
   y += ywarp
   if variable_instance_exists(id,"ytar") {ytar+=ywarp}
  }
 
  if (global.warp_all=="down")
  {
   y -= ywarp
   if variable_instance_exists(id,"ytar") {ytar-=ywarp}
  }
}
}
global.warp_all = "noone"
}
 
Top