• Hey! Guest! The 40th (!!!) GMC Jam will take place between February 25th, 12:00 UTC to March 1st 12:00 UTC. Why not join in this very special anniversary jam! Click here to find out more!

GMS 2.3+ [Solved] How to create multiple instances with different coordinates ?

Flagolet

Member
Hi everybody !

I'm a beginner and I'm currently making a brick-breaker to practice coding. I have one specific problem and a little consideration.

The specific problem

In my brick-breaker, I want a powerup to add a "sight line" in the direction of the ball, so the player can anticipate where the ball is gonna go. So far, here is what I have being able to do : https://vimeo.com/user130013288/review/494842250/c56be2336e

To do this, I created an object called "O_Trajectoire", drawed a sprite (an arrow) for it, and I used the chunk of code below in the "step event" of the "O_Trajectoire", so the object is always right in front of my ball.

GML:
with (O_Balle) {
    var _x,_y; //coordonnates of the line right in front of the ball
    _x = x + lengthdir_x(32, direction);
    _y = y + lengthdir_y(32, direction);
}

x = _x;
y = _y;
But what I would like, is having many "O_Trajectoire" objects placed next to each other, like this : Capture d’écran 2020-12-26 à 20.55.06.png

I know I could use a "draw_line" function, or draw a very long sprite for one single object, which would surely be FAR LESS ressources consumming. But as simple curiosity, I would like to know how to code so I could create like 10 instances of the O_Trajectoire object when i get the PowerUp, and assign the coordinates of each of these instances so they form a lign in front of my ball ?

My little consideration.

I'm worring about peformances and space memory. I don't really realize how important it is to use local variables (which look a bit more difficult to handle) instead of normal instances variables, in terms of optimisation. Manual says that variables take space in memory, but how much ? I would like to take good habits as soon as possible, but should I focus more on learning functions and making things work first ?

I hope I have been clear, don't hesitate to ask more, if needed. Thanks in advance for taking time to respond. :)
 

kburkhart84

Firehammer Games
The best way to do this actually is to draw the sprites, not actually create object instances of them. This will take two steps, the first of which you need even if you did create objects instead of just draw sprites.

1. You have to calculate the path of the ball. I don't know if you are accounting for collisions and doing predictions, or how far you are taking that. But that's the hard part. Once you figure that out, you will have a way to get future positions based on an amount of time. So you would store say 10 of those positions in an array or similar.

2. Then, I suggest you simply draw your trajectory sprite in a single draw event of the trajectory object. The catch is that in the draw event, you directly draw the sprite using draw_sprite() function calls. And you do that 10 times, using the position you got in step 1.

If you really wanted to make 10 instances instead, you could simply create those instances using the positions...but this is much less effcient due to the overhead of those object instances...and you would then have to mess with destroying them and recreating them as the ball moves around, which seems like a pain. If you simply draw the sprites in a single object's event you won't have that issue.

Your consideration about variables...it is important that you figure out stuff on learning functions and making things work. But there is nothing wrong with learning the difference between local and instance variables right now as part of that. Local variables are best used where you only need that variable in that piece of code you are running. They are incredibly efficient, and are cleaned up as soon as the code is finished, whether its in a function, an event, or even a loop(like do/until or for() loops). I use them constantly when doing formulas for things. Like if you do a formula or code involving half the distance between objects... I calculate that into a local variable, and then use that in the multiple places it goes, instead of recalculating it in the code over and over.

Local variables are not actually any more difficult to handle either. You just have to understand that they belong only in the code where they are declared. Besides that, they work basically the same as any other variable. In fact, they have a certain handy thing about them. Certain things you do in code change the "scope" you are in. Normally, in some event, you are in the scope of the instance of that event. If you use the with() statement, the scope changes to whatever you set in the with() statement. However, if you make a local variable, that variable is accessible in AND out of the with() statement.

Code:
instVariable = 2;
var localVariable = 5;
with(otherObjectInstance)
{
    //I can only access the instance variable here using the other statement since I'm now in the scope of the other instance
    myVariable += other.instVariable;

    //note that I can directly access the local variable though, because it belongs to ALL the code in this event, not the instance
    myOtherVariable += localVariable;
}
I apologize if I confused you there, but code scope is a very important concept to understand so its a good time to get started on that.
 

Flagolet

Member
The best way to do this actually is to draw the sprites, not actually create object instances of them. This will take two steps, the first of which you need even if you did create objects instead of just draw sprites.

1. You have to calculate the path of the ball. I don't know if you are accounting for collisions and doing predictions, or how far you are taking that. But that's the hard part. Once you figure that out, you will have a way to get future positions based on an amount of time. So you would store say 10 of those positions in an array or similar.

2. Then, I suggest you simply draw your trajectory sprite in a single draw event of the trajectory object. The catch is that in the draw event, you directly draw the sprite using draw_sprite() function calls. And you do that 10 times, using the position you got in step 1.

If you really wanted to make 10 instances instead, you could simply create those instances using the positions...but this is much less effcient due to the overhead of those object instances...and you would then have to mess with destroying them and recreating them as the ball moves around, which seems like a pain. If you simply draw the sprites in a single object's event you won't have that issue.

Your consideration about variables...it is important that you figure out stuff on learning functions and making things work. But there is nothing wrong with learning the difference between local and instance variables right now as part of that. Local variables are best used where you only need that variable in that piece of code you are running. They are incredibly efficient, and are cleaned up as soon as the code is finished, whether its in a function, an event, or even a loop(like do/until or for() loops). I use them constantly when doing formulas for things. Like if you do a formula or code involving half the distance between objects... I calculate that into a local variable, and then use that in the multiple places it goes, instead of recalculating it in the code over and over.

Local variables are not actually any more difficult to handle either. You just have to understand that they belong only in the code where they are declared. Besides that, they work basically the same as any other variable. In fact, they have a certain handy thing about them. Certain things you do in code change the "scope" you are in. Normally, in some event, you are in the scope of the instance of that event. If you use the with() statement, the scope changes to whatever you set in the with() statement. However, if you make a local variable, that variable is accessible in AND out of the with() statement.

Code:
instVariable = 2;
var localVariable = 5;
with(otherObjectInstance)
{
    //I can only access the instance variable here using the other statement since I'm now in the scope of the other instance
    myVariable += other.instVariable;

    //note that I can directly access the local variable though, because it belongs to ALL the code in this event, not the instance
    myOtherVariable += localVariable;
}
I apologize if I confused you there, but code scope is a very important concept to understand so its a good time to get started on that.
Thanks a lot for your exhaustive response. I will try to code it the way you proposed me. :) And thank you also for the explanations about "scope" and local variables. It get things clearer to me !
 

Flagolet

Member
Hi again. I messed around the whole day to make this works and I guess I'm pretty close. I still have a last trouble about it.

Following kburkhart84 advices, I tried to make an array of the future coordinates of my ball. I use these coordinates to draw sprites of O_Trajectoire, in the draw event of O_Trajectoire. It looks like it's working :


But as you can see, when I pick the PowerUp, a small problem occurs. It looks like the first step after picking the PowerUp, the sprites are drawn at other coordinates.

Capture d’écran 2020-12-27 à 17.10.47.png

I think it comes from the way I coded my array.

1) first, I declared my array with the futures coordinates of the ball in the create event of my ball.

GML:
for (var i = 9 ; i > -1 ; i -= 1) {
    for (var j = 1 ; j > -1 ; j += -1){
        if (j == 1){
            loc[i, j] = y + lengthdir_y((i + 1) * 16,direction);}
        else {
            loc[i, j] = x + lengthdir_x((i + 1) * 16,direction);}
    }
}

// The for loops above are supposed to emulate the code below. First trials with "for" loops...

//loc[9, 1] = y + lengthdir_y(160,direction);
//loc[9, 0] = x + lengthdir_x(160,direction);
//loc[8, 1] = y + lengthdir_y(144,direction);
//loc[8, 0] = x + lengthdir_x(144,direction);
//loc[7, 1] = y + lengthdir_y(128,direction);
//loc[7, 0] = x + lengthdir_x(128,direction);
//loc[6, 1] = y + lengthdir_y(112,direction);
//loc[6, 0] = x + lengthdir_x(112,direction);
//loc[5, 1] = y + lengthdir_y(96,direction);
//loc[5, 0] = x + lengthdir_x(96,direction);
//loc[4, 1] = y + lengthdir_y(80,direction);
//loc[4, 0] = x + lengthdir_x(80,direction);
//loc[3, 1] = y + lengthdir_y(64,direction);
//loc[3, 0] = x + lengthdir_x(64,direction);
//loc[2, 1] = y + lengthdir_y(48,direction);
//loc[2, 0] = x + lengthdir_x(48,direction);
//loc[1, 1] = y + lengthdir_y(32,direction);
//loc[1, 0] = x + lengthdir_x(32,direction);
//loc[0, 1] = y + lengthdir_y(16,direction);
2) When the PowerUp collides with the Bat, it creates an instance of O_Trajectoire (that will draw it sprites) and I update the future coordinates of my ball in my O_Balle step event with this code :

GML:
if instance_exists(O_Trajectoire){
    for (var i = 9 ; i > -1 ; i -= 1 ;)
    {
        for (var j = 1 ; j > -1 ; j -= 1;)
        {
            if (j == 1){
                loc[i, j] = y + (lengthdir_y((i+1)*16,direction));
            }
            else{
                loc[i, j] = x + (lengthdir_x((i+1)*16,direction));
            }
        }
    }
}
3) My O_Trajectoire draw its sprites, in its draw event, with the code below :

GML:
var _i;
_i = 0;

repeat (10) {
draw_sprite_ext(Spr_Trajectoire,image_index,O_Balle.loc[_i, 0],O_Balle.loc[_i, 1],1,1,O_Balle.direction,c_white,1);
_i += 1;
}
I assume I made an error somewhere. It looks like when I pick the PowerUp and the O_trajectoire object is created (and is supposed to be drawn at the coordinates), it uses (at the very first step) the "future" coordinates of the Ball that are set in the O_Ball create event, which are not yet updated.

So, shall I update the coordinates of the Ball when the PowerUp is on the screen, instead of when the O_Trajectoire object is created ? so the coordinates would be already updated. Or is there a way to manage the sequence of the events so the O_Trajectoire object will directly use the updated coordinates ?

If you have any comments about how I could improve my "for" loops and my arrays, I obviously take them.

Cheers, sorry for this giant question.
 

kburkhart84

Firehammer Games
If this is something that changes every step, you are going to have to update it every step. The ball is constantly moving, so that qualifies. Its good to create the initial array in the create event, but if you don't update them you are not going to get updated visuals on it. They will keep the same values you had originally put. This is perfectly normal with code and is expected. Variables don't track other variables when you set them, you have to constantly tell them the new values.
 

Flagolet

Member
Thanks again for your help. 🙏

So I just threw away the " if instance_exist(O_Trajectoire) " condition in my code, in order to update the coordinates everytime. It works fine. :)
 
Top