How to avoid the respawn of the enemies when I re-enter the room

Game maker 1.4:

I put an enemy in the room, but even when I kill him, when I enter again in that same room, he spawn again with the full life. How can I avoid the enemies that had already dead of spawn again?
 

NightFrost

Member
However, persisting rooms just to save enemy states is like applying a wrecking ball to hammer down a nail. You could use a global variable to track it. You'd start by defining a global scope ds map at the start of your game, make sure you run this only once:
GML:
global.dead_enemies = ds_map_create();
Whenever an enemy dies, you add a unique identifier to the map. When you enter the room again later, the enemy checks the map and if the identifier is on the list, it knows it should be dead and destroys itself. For the unique identifiers, if you have just a few enemies in each room you could right-click on them in the room and add room creation code that gives each a unique name. If there's so many this becomes a chore, you can generate an identifier from the enemy's room and starting coordinates. You would do this checking in enemy create event:
GML:
// Form a unique identifier.
Identifier = string(room) + "_" + string(x) + "_" + string(y);
// Check if identifier is on the map.
if(!is_undefined(global.dead_enemies[? Identifier])){
    // Identifier was found, this enemy is already dead.
    instance_destroy();
}
When enemy dies it adds itself to the ds map:
GML:
// When dying.
global.dead_enemies[? Identifier] = true;
 
However, persisting rooms just to save enemy states is like applying a wrecking ball to hammer down a nail. You could use a global variable to track it. You'd start by defining a global scope ds map at the start of your game, make sure you run this only once:
GML:
global.dead_enemies = ds_map_create();
Whenever an enemy dies, you add a unique identifier to the map. When you enter the room again later, the enemy checks the map and if the identifier is on the list, it knows it should be dead and destroys itself. For the unique identifiers, if you have just a few enemies in each room you could right-click on them in the room and add room creation code that gives each a unique name. If there's so many this becomes a chore, you can generate an identifier from the enemy's room and starting coordinates. You would do this checking in enemy create event:
GML:
// Form a unique identifier.
Identifier = string(room) + "_" + string(x) + "_" + string(y);
// Check if identifier is on the map.
if(!is_undefined(global.dead_enemies[? Identifier])){
    // Identifier was found, this enemy is already dead.
    instance_destroy();
}
When enemy dies it adds itself to the ds map:
GML:
// When dying.
global.dead_enemies[? Identifier] = true;
Just a doubt, I'm beginner on it, This "?" means what exactly? Because it's not working
And another doubt, can I use this code to solve the problem that my healthbar is also reseting when I change the room.
 

curato

Member
if it is just a few rooms persistence will work just fine. If you are doing something with a large number of levels at a time then you really need a data structure to keep track of what you did or didn't do on a level.
 
K

KiD_Rager

Guest
If you don't want the Room Persistence approach, you could also do enums. @NightFrost explained this already, but if you don't know how it works exactly, I've also written out an expanded version of it and how it might look once the code is fully written out.

Have an enum called "state" and specify "alive" and "dead". Mark a key for the object that the game will save along with what state the object is at all times. In the beginning, the object (enemy) will be alive. If you kill the enemy instance, the state changes from "alive" to "dead" and saves the key so that when you return from a different room, it remembers the state the instance was last in and doesn't respawn it. And yes, it does use ds_maps as @NightFrost stated.

First, create your ds_map and create a enum called "States" with the different types of states you want in the game. Best to place this in some controller object that stays persistent throughout the game.

obj_game_manager: Create
GML:
///Game Data
//Save data
save_data = ds_map_create();

//Check enum states
enum States{
    //Enemy
    alive,
    dead
}
Next, we need to write some scripts telling the game to save the state information of any object in question into the ds_map. We use our controller object to save the object based the room, the name of the object, and its coordinates in said room. There are other ways we could change it up (like object ID) but for now this is the more common route.

Script - save_data_get_value
GML:
///save_data_get_value(key)
with(obj_game_manager) //WHATEVER YOUR CONTROLLER OBJECT NAME IS
  return save_data[? argument[0]];
Script - save_data_generate_key
GML:
///save_data_generate_key()
//Creates a key for the current item to use for save data.
return room_get_name(room)+object_get_name(object_index)+string(x)+string(y);
Script - save_data_set_value
GML:
///save_data_set_value(key,value)
with(obj_game_manager) //CHANGE TO YOUR CONTROLLER OBJECT
  ds_map_replace(save_data,argument[0],argument[1]);
Then we specify in our enemy code what state it begins in. If we have some save data for that object already, it'll keep the enemy dead (imitates room persistence). Otherwise, it starts it off alive.

Create
GML:
//Identify state
key = save_data_generate_key();
var _save_data = save_data_get_value(key);
if(!is_undefined(_save_data) && _save_data==1)
{ //Enemy is dead
  state=States.dead;
}
else { //Enemy starts off alive
  state=States.alive;
}
Finally, we change the state of the object based on certain triggers. If I want the enemy to execute this script when it dies from a bullet, for example, this would go in my Collision event (although I'd highly recommend it going in the Step event if possible). In my conditional statement, I would say something like if hp <= 0 or whatever to make it change into the dead state. I can tell it what I want to do right as it's entering the dead state. Maybe increase my score, drop an item, create an explosion effect, whatever. Lastly, I'd have the instance destroyed after I've declared the enemy dead. THIS IS IMPORTANT because whenever you return to the room, the game will come back to find this state event. Even though the game knows what state the enemy is in, it'll want to know what to do if the enemy data returns "dead". Hence why I did all my dead code during the transition (middle of this code) and just have it destroying the instance if enemy is saved in dead state.

Event Action (Step, Collision with weapon, etc.)
GML:
///Define states
switch(state)
{
  case States.alive:
  {
    //State what happens right as enemy dies
      if (SET CONDITION TO WHAT MAKES YOUR ENEMY DEAD) {
          state = States.dead;
          save_data_set_value(key,true);
      }
    }
  }break;
  case States.dead:
  {
    instance_destroy(); //Note: this may change depending where code is being called from
  } break;
}
Also....

Just a doubt, I'm beginner on it, This "?" means what exactly? Because it's not working
And another doubt, can I use this code to solve the problem that my healthbar is also reseting when I change the room.
The ? symbol in the code is an accessor. For data structures (ds_), each type has their own accessor. Since we used ds_maps, that accessor is a ?. Think of it as a shortcut for writing ds code. You can find an example of how to use it easily here.

Hope that explains is more. :)
 
If you don't want the Room Persistence approach, you could also do enums. @NightFrost explained this already, but if you don't know how it works exactly, I've also written out an expanded version of it and how it might look once the code is fully written out.

Have an enum called "state" and specify "alive" and "dead". Mark a key for the object that the game will save along with what state the object is at all times. In the beginning, the object (enemy) will be alive. If you kill the enemy instance, the state changes from "alive" to "dead" and saves the key so that when you return from a different room, it remembers the state the instance was last in and doesn't respawn it. And yes, it does use ds_maps as @NightFrost stated.

First, create your ds_map and create a enum called "States" with the different types of states you want in the game. Best to place this in some controller object that stays persistent throughout the game.

obj_game_manager: Create
GML:
///Game Data
//Save data
save_data = ds_map_create();

//Check enum states
enum States{
    //Enemy
    alive,
    dead
}
Next, we need to write some scripts telling the game to save the state information of any object in question into the ds_map. We use our controller object to save the object based the room, the name of the object, and its coordinates in said room. There are other ways we could change it up (like object ID) but for now this is the more common route.

Script - save_data_get_value
GML:
///save_data_get_value(key)
with(obj_game_manager) //WHATEVER YOUR CONTROLLER OBJECT NAME IS
  return save_data[? argument[0]];
Script - save_data_generate_key
GML:
///save_data_generate_key()
//Creates a key for the current item to use for save data.
return room_get_name(room)+object_get_name(object_index)+string(x)+string(y);
Script - save_data_set_value
GML:
///save_data_set_value(key,value)
with(obj_game_manager) //CHANGE TO YOUR CONTROLLER OBJECT
  ds_map_replace(save_data,argument[0],argument[1]);
Then we specify in our enemy code what state it begins in. If we have some save data for that object already, it'll keep the enemy dead (imitates room persistence). Otherwise, it starts it off alive.

Create
GML:
//Identify state
key = save_data_generate_key();
var _save_data = save_data_get_value(key);
if(!is_undefined(_save_data) && _save_data==1)
{ //Enemy is dead
  state=States.dead;
}
else { //Enemy starts off alive
  state=States.alive;
}
Finally, we change the state of the object based on certain triggers. If I want the enemy to execute this script when it dies from a bullet, for example, this would go in my Collision event (although I'd highly recommend it going in the Step event if possible). In my conditional statement, I would say something like if hp <= 0 or whatever to make it change into the dead state. I can tell it what I want to do right as it's entering the dead state. Maybe increase my score, drop an item, create an explosion effect, whatever. Lastly, I'd have the instance destroyed after I've declared the enemy dead. THIS IS IMPORTANT because whenever you return to the room, the game will come back to find this state event. Even though the game knows what state the enemy is in, it'll want to know what to do if the enemy data returns "dead". Hence why I did all my dead code during the transition (middle of this code) and just have it destroying the instance if enemy is saved in dead state.

Event Action (Step, Collision with weapon, etc.)
GML:
///Define states
switch(state)
{
  case States.alive:
  {
    //State what happens right as enemy dies
      if (SET CONDITION TO WHAT MAKES YOUR ENEMY DEAD) {
          state = States.dead;
          save_data_set_value(key,true);
      }
    }
  }break;
  case States.dead:
  {
    instance_destroy(); //Note: this may change depending where code is being called from
  } break;
}
Also....



The ? symbol in the code is an accessor. For data structures (ds_), each type has their own accessor. Since we used ds_maps, that accessor is a ?. Think of it as a shortcut for writing ds code. You can find an example of how to use it easily here.

Hope that explains is more. :)
Man, that's not working, the enemies are still respawning when I change the room, I did everything you said.
 
Top