How to make AI advance to different instances of an object at the same time

Dany24

Member
Hello, I hope you can help me I am trying to make multiple instances of a single object (npc) go towards 4 different towers. but instead of each instance going to a different tower what it does is that they all go to a single tower I use a state machine in each example object in the tower object it has 3 states: creating, deactivated (if there is no collision with the npc), activated (if there is a collision with the npc) ... and on the part of the npc I have in its state of movement towards the towers
This code
GML:
if instance_exists (obj_ballestas) {target = instance_nearest (x, y, obj_ballestas)
} if distance_to_point (target.x, target.y) <200 and target.state! = "active" {hspeed = sing (target.x -x)*1
vspeed = sing (target.y-y) *1}
if distance_to_point (target.x, target.y) <5 {hspeed = 0; vspeed = 0}
/// with this the movement works for me but how do I make the instncia (npc) go to a different tower that has its status deactivated I appreciate your help
ps the game is top down view and is rts style in which groups of subjects are managed.
 
Last edited:

FoxyOfJungle

Kazan Games
Hi!
Please post the text in English, for the best communication for everyone and that we can help you. It's the rule of this forum, thanks for understanding. 🙂

Also use code formatting, it is difficult to read code this way.

1630483464735.png
 

FoxyOfJungle

Kazan Games
You just constantly check if the target has an activated state, then the instance will only follow if this is true. As long as it's false, it can look for a new target (the closest instance).
 

Dany24

Member
You just constantly check if the target has an activated state, then the instance will only follow if this is true. As long as it's false, it can look for a new target (the closest instance).
how can I do it ? I know that the nearest instance gets the id of the first instance that is close and unless that instance is destroyed it doesn't change its id so I don't know how to do it, could you give me an example?
 

Dany24

Member
You just constantly check if the target has an activated state, then the instance will only follow if this is true. As long as it's false, it can look for a new target (the closest instance).
no matter how hard I try I can't get it to work :c 😢
ejemplo.PNG
I'm sure I'm typing the code wrong
 

TheouAegis

Member
buggies. could use instance_nearest_list() and then loop through the list until you find an inactive tower. But you should also set the tower as targeted, not just active if it isn't going to be active until a unit attacks it.
 

Dany24

Member
[QUOTE = "Dany24, publicación: 533406, miembro: 65155"]
Hola, espero que me puedan ayudar. Estoy tratando de hacer que varias instancias de un solo objeto (npc) vayan hacia 4 torres diferentes. pero en lugar de que cada instancia vaya a una torre diferente, lo que hace es que todas van a una sola torre.Uso una máquina de estado en cada objeto de ejemplo en el objeto de la torre, tiene 3 estados: creando, desactivado (si no hay colisión con el npc), activado (si hay colisión con el npc) ... y por parte del npc tengo en su estado de movimiento hacia las torres
Este codigo
[CÓDIGO = gml] si existe_instancia (obj_ballestas) {destino = instancia_más cercana (x, y, obj_ballestas)
} if distance_to_point (target.x, target.y) <200 y target.state! = "activo" {hspeed = sing (target.x -x) * 1
vspeed = sing (target.y-y) *1}
si distancia_al_punto (objetivo.x, objetivo.y) <5 {hspeed = 0; vspeed = 0} [/ CODE]
/// con esto el movimiento me funciona pero como hago para que la instncia (npc) vaya a otra torre que tenga su estado desactivado agradezco tu ayuda
ps: el juego tiene una vista de arriba hacia abajo y es un estilo rts en el que se gestionan grupos de sujetos.
[/CITA]
después de 2 días sin parar lo obtuve con un par de alarmas y instance_find
 
I haven't much to add to this, but here is a small suggestion.

GML:
target = noone;

if instance_exists (obj_ballestas)
{target = instance_nearest (x, y, obj_ballestas)}

if target != noone
{
target_x = target.x;
target_y = target.y;
dist = distance_to_point (target_x, target_y);

if dist <200 and target.state != "active"
{
hspeed = sing (target_x -x)*1
vspeed = sing (target_y-y) *1
}
if dist < 5
{
hspeed = 0; vspeed = 0
}
}
1) I'm not 100% sure of one these changes, which is to define 'target' as a null value beforehand.

In GMS 1 if you were trying to access 'target', and it had no value because no instance was found, it would throw an error. As you have it coded: it will only check for the nearest instance of "obj_ballestas" if there is an instance currently existing.

Your code after that isn't based on this same condition, and could be looking for 'target' even when there is nothing to possibly give it a value. I'd imagine you haven't seen this happen due to there being an instance of "obj_ballestas" in the room you are currently working on. Remove it from the room, and I believe you will see your other object (as it is now) cause an error.

2) This change is about how often you call to distance_to_point, and giving you some measure of what this involves.

Code:
distance_to_point (target.x, target.y)
You call this function twice, but it also means calling 3 further things: what is 'target', what is 'target' x, what is 'target' y. It is inevitable that 'target' will be a cost, as it must look up that value twice to find target.x and target.y. Once you have those x and y values, if you need them later you should store them.

When they're stored think of it as it looking inside itself, when they're not stored: it has to make that extra effort in looking outside of itself

Code:
hspeed = sing (target.x -x)*1
vspeed = sing (target.y-y) *1
Above, you again end up calling what is 'target', what is 'target' x, what is 'target' y.

Any time that you want to call up a value more than once in the same step, and you are accessing "outside" of the current instance, it is best to store that value.

Code:
target_x = target.x;

target_y = target.y;

dist = distance_to_point (target_x, target_y);


hspeed = sing (target_x -x)*1

vspeed = sing (target_y-y) *1
The cost of looking up the stored variables will be less than the 6 times you call targets x and y, and the 2 times you use distance_to_point. It's probably not a lot less by itself, but don't forget this cost grows with every instance you have performing this check....and why encourage wastefulness anyway.

Who knows: one day you might find yourself wondering what you can do to optimize your game, and little things like this could slip under the radar. They all add up :)

Hope that helps.
 

Dany24

Member
I haven't much to add to this, but here is a small suggestion.

GML:
target = noone;

if instance_exists (obj_ballestas)
{target = instance_nearest (x, y, obj_ballestas)}

if target != noone
{
target_x = target.x;
target_y = target.y;
dist = distance_to_point (target_x, target_y);

if dist <200 and target.state != "active"
{
hspeed = sing (target_x -x)*1
vspeed = sing (target_y-y) *1
}
if dist < 5
{
hspeed = 0; vspeed = 0
}
}
1) I'm not 100% sure of one these changes, which is to define 'target' as a null value beforehand.

In GMS 1 if you were trying to access 'target', and it had no value because no instance was found, it would throw an error. As you have it coded: it will only check for the nearest instance of "obj_ballestas" if there is an instance currently existing.

Your code after that isn't based on this same condition, and could be looking for 'target' even when there is nothing to possibly give it a value. I'd imagine you haven't seen this happen due to there being an instance of "obj_ballestas" in the room you are currently working on. Remove it from the room, and I believe you will see your other object (as it is now) cause an error.

2) This change is about how often you call to distance_to_point, and giving you some measure of what this involves.

Code:
distance_to_point (target.x, target.y)
You call this function twice, but it also means calling 3 further things: what is 'target', what is 'target' x, what is 'target' y. It is inevitable that 'target' will be a cost, as it must look up that value twice to find target.x and target.y. Once you have those x and y values, if you need them later you should store them.

When they're stored think of it as it looking inside itself, when they're not stored: it has to make that extra effort in looking outside of itself

Code:
hspeed = sing (target.x -x)*1
vspeed = sing (target.y-y) *1
Above, you again end up calling what is 'target', what is 'target' x, what is 'target' y.

Siempre que desee recuperar un valor más de una vez en el mismo paso y esté accediendo "fuera" de la instancia actual, es mejor almacenar ese valor.

[CÓDIGO] target_x = target.x;

target_y = target.y;

dist = distancia_al_punto (objetivo_x, objetivo_y);


hspeed = sing (target_x -x) * 1

vspeed = sing (target_y-y) * 1 [/ CODE]

El costo de buscar las variables almacenadas será menor que las 6 veces que llamas a los objetivos xey, y las 2 veces que usas distance_to_point. Probablemente no sea mucho menos por sí solo, pero no olvide que este costo aumenta con cada instancia que tiene al realizar esta verificación ... y por qué fomentar el despilfarro de todos modos.

Quién sabe: un día puede que te preguntes qué puedes hacer para optimizar tu juego, y pequeñas cosas como esta podrían pasar desapercibidas. Todos se suman:)

Espero que ayude.
[/CITA]
Muchas gracias por la ayuda lo que buscaba conseguir era 1. Las torres tienen máquina de estado (construida, desactivada, activada) cuando aparecen enemigos los minions que buscan las torres y cuando están cerca de ellas se van a activar modo y luego comenzar a atacar a los enemigos.

I have already managed to do it but I still have a question, but this time is with the gui version 1.4 as I can print on screen the state machine of the same object without overlapping each other and do not repeat the same let's say I want to show what profession has each (state machine) example lumberjack, farmer, miner etc. ... is there any way?
 
A state machine is just a variable set to a value, and variables can be drawn onscreen.

In a draw event of the objects with states:
GML:
draw_text(x, bbox_top - 20, whatver the variable is to show)
Would be the most basic way to show the information, with it being drawn at the instances x location, and 20 pixels above it's bbox_top so it's not drawn over any area of the sprite. However: you would only see this whilst they are onscreen, as it is drawn specifically to their location, and not based on the GUI area.

If you wanted to list every instance onscreen, and show this variable, it would still be a draw function but you'd have to figure out a few more things.

You might gather the info, such as the instance id, or object name, and the state, and then merge them all into a string. You then add that string to a ds_list, then draw the contents of the list to the screen.

Say you have a control object to do this, and only one instance of it exists in a room.

It's create event:
Code:
draw_list = ds_list_create();
begin_point = 0;
Destroy event:
Code:
ds_list_destroy(draw_list)
Step event:
Code:
ds_list_clear(draw_list)
var list = draw_list;
with (objects that have states)
{
str = ""
str += object_get_name(object_index) + " \ ";
str += string(id) + " \ ";
str += whatever state variable is;
ds_list_add(list, str);
}
draw GUI event:
Code:
var size = ds_list_size(draw_list)
for (var a = begin_point; a < size; a++)
{
result = draw_list[| a];
draw_text(20, 20 + (20 * (a - begin_point)), result);
}
mouse wheel up:
Code:
if begin_point < ds_list_size(draw_list) -1
{
begin_point += 1;
}
mouse wheel down:
Code:
if begin_point > 0
{
begin_point -= 1;
}
That's a rough way of showing the information.

1) You access those objects you wish to get info from: if they share a parent then you could just access that
2) They add their info to a ds_list stored in the control object
3) the control object draws using the GUI event, meaning the text it draws will always be within the view if the X and Y values of draw_text are less than the view width and height
4) I have set a variable called 'begin_point' which is used as the starting number for where in the ds list (draw_list) the first value is taken from. You can see this in the FOR loop, and the loop is set to the size of the ds list, so it cannot go out of the lists bounds
5)
Code:
draw_text(20, 20 + (20 * (a - begin_point)), result);
In a loop, this code will basically keep adding to itself.

This is the y component of when it is drawn unmodified in the view
Code:
20 + (20 * a )
The starting point is always 20 pixels from the top, and then as 'a' is added too, it draws 20 pixels further down, then another 20, and so on.
Code:
20 + (20 * (a - begin_point))
By doing this it should allow for 'a' to cancel out 'begin_point' so that the difference starts at zero, and so the first piece of text is drawn at the top, then the second bit has a difference of one, as 'a' increases but 'begin_point' remains static, and so on.

By changing begin_point you can change where in the ds_list you get the first value from, and can then essentially scroll up and down through all the content of the list.
 

Dany24

Member
A state machine is just a variable set to a value, and variables can be drawn onscreen.

In a draw event of the objects with states:
GML:
draw_text(x, bbox_top - 20, whatver the variable is to show)
Would be the most basic way to show the information, with it being drawn at the instances x location, and 20 pixels above it's bbox_top so it's not drawn over any area of the sprite. However: you would only see this whilst they are onscreen, as it is drawn specifically to their location, and not based on the GUI area.

If you wanted to list every instance onscreen, and show this variable, it would still be a draw function but you'd have to figure out a few more things.

You might gather the info, such as the instance id, or object name, and the state, and then merge them all into a string. You then add that string to a ds_list, then draw the contents of the list to the screen.

Say you have a control object to do this, and only one instance of it exists in a room.

It's create event:
Code:
draw_list = ds_list_create();
begin_point = 0;
Destroy event:
Code:
ds_list_destroy(draw_list)
Step event:
Code:
ds_list_clear(draw_list)
var list = draw_list;
with (objects that have states)
{
str = ""
str += object_get_name(object_index) + " \ ";
str += string(id) + " \ ";
str += whatever state variable is;
ds_list_add(list, str);
}
draw GUI event:
Code:
var size = ds_list_size(draw_list)
for (var a = begin_point; a < size; a++)
{
result = draw_list[| a];
draw_text(20, 20 + (20 * (a - begin_point)), result);
}
mouse wheel up:
[CÓDIGO] si begin_point <ds_list_size (draw_list) -1
{
punto_inicio + = 1;
}[/CÓDIGO]

rueda del mouse hacia abajo:
[CÓDIGO] si begin_point> 0
{
punto_inicio - = 1;
}[/CÓDIGO]

Esa es una forma aproximada de mostrar la información.

1) Accede a los objetos de los que desea obtener información: si comparten un padre, entonces puede acceder a eso
2) Añaden su información a una ds_list almacenada en el objeto de control
3) el objeto de control dibuja usando el evento GUI, lo que significa que el texto que dibuja siempre estará dentro de la vista si los valores X e Y de draw_text son menores que el ancho y alto de la vista
4) He configurado una variable llamada 'begin_point' que se usa como el número inicial para el lugar de la lista ds (draw_list) de donde se toma el primer valor. Puede ver esto en el bucle FOR, y el bucle se establece en el tamaño de la lista ds, por lo que no puede salir de los límites de la lista.
5)
Code:
 draw_text (20, 20 + (20 * (a - begin_point)), resultado); [/ CODE]
En un bucle, este código básicamente seguirá agregándose a sí mismo.

Este es el componente y de cuando se dibuja sin modificar en la vista [CÓDIGO] 20 + (20 * a) [/ CÓDIGO] El punto de partida siempre está a 20 píxeles de la parte superior, y luego, cuando se agrega 'a' también, dibuja 20 píxeles más abajo, luego otros 20, y así sucesivamente.
[CÓDIGO] 20 + (20 * (a - punto_inicio)) [/ CÓDIGO]
Al hacer esto, debería permitir que 'a' cancele 'begin_point' para que la diferencia comience en cero, y así la primera parte del texto se dibuje en la parte superior, luego el segundo bit tenga una diferencia de uno, como 'a 'aumenta pero' begin_point 'permanece estático, y así sucesivamente.

Al cambiar begin_point, puede cambiar en qué parte de ds_list obtiene el primer valor y luego, esencialmente, puede desplazarse hacia arriba y hacia abajo a través de todo el contenido de la lista.
[/CITA]
Muchas gracias me ha funcionado estoy seguro que si leo el manual para ver como funciona la lista ds no entendería nada jaja.
 

Dany24

Member
and if I want to add more objects do I have to repeat the same thing that goes in the step event?
 
Last edited:
and if I want to add more objects do I have to repeat the same thing that goes in the step event?
When I made an example to test my code, there was multiple objects but they shared a parent.

This means that if you access the parent object, you gain access to all of the children.

By using WITH on the parent object, you essentially begin a loop that will go through every single instance of it's children.

Ds lists are dynamic data structures, and can be grown without too much concern. Meaning you can add several new instances of objects and the list will expand to fit the data (up to a point - something like 130,000 entries? maybe more, I don't recall exactly)

It's two dynamic applications: 1) the WITH loop always expands to the size of how many instances there are, and (2) the ds list can scale in size to fit them (it is cleared every step to zero in size, and then refilled).

So, other than the finite size of ds lists, it will always be updating itself without you having to do anything :)

The rest of the code is set so that the various conditions which would break GMS (out of data structure bounds / accessing instances that don't exist / and a few others) cannot happen.

Explaining those would take a bit longer, but once you understand how useful WITH is, you should be able to figure out why it will still work even if you have no instances of the children objects, or if you had a million and they outnumbered more than a list can hold.

EDIT: Technically I suppose it might still crash through the sheer size of a ds list with too many entries, but that would be an engine error, rather than a coding one.....And I only mention it to be thorough, since you most likely won't have that many instances!
 
Last edited:

Dany24

Member
When I made an example to test my code, there was multiple objects but they shared a parent.

This means that if you access the parent object, you gain access to all of the children.

By using WITH on the parent object, you essentially begin a loop that will go through every single instance of it's children.

Ds lists are dynamic data structures, and can be grown without too much concern. Meaning you can add several new instances of objects and the list will expand to fit the data (up to a point - something like 130,000 entries? maybe more, I don't recall exactly)

It's two dynamic applications: 1) the WITH loop always expands to the size of how many instances there are, and (2) the ds list can scale in size to fit them (it is cleared every step to zero in size, and then refilled).

So, other than the finite size of ds lists, it will always be updating itself without you having to do anything :)

The rest of the code is set so that the various conditions which would break GMS (out of data structure bounds / accessing instances that don't exist / and a few others) cannot happen.

Explaining those would take a bit longer, but once you understand how useful WITH is, you should be able to figure out why it will still work even if you have no instances of the children objects, or if you had a million and they outnumbered more than a list can hold.

EDIT: Technically I suppose it might still crash through the sheer size of a ds list with too many entries, but that would be an engine error, rather than a coding one.....And I only mention it to be thorough, since you most likely won't have that many instances!
thank you very much for the explanation
my native language is Spanish
I have created a game resvientemente with english translation I would like you to try it is free on my page ichio Dany24m
 

Dany24

Member
Happy to help :)
Hello again, can you help me to create a script that works so that an object (enemy) has several targets depending on its proximity?
has several targets depending on the proximity of the object, I have a code but it is not working very well and I can't advance in the development.
 
Hello again, can you help me to create a script that works so that an object (enemy) has several targets depending on its proximity?
has several targets depending on the proximity of the object, I have a code but it is not working very well and I can't advance in the development.
How much of this have you developed? I'd be happy to look over what you have but am somewhat short of time these days, due to work. Which is why I didn't see this message until now.

I guess you could post it here, if you wanted the possibility of other users seeing it too. Otherwise send it in a message to me, I guess?

Tell me what you intend for it to do, what problems you're having with it, and I'll see if I can figure out what are the issues with your code.
 
Top