Game Development Community

Astral Objects Spawning Help

by Robert Carroll · in Torque Game Builder · 12/05/2009 (3:44 pm) · 13 replies

Ok, I'm trying to make a spawner that spawns enemys, Im using the astral objects tutorial codes to do this. But Im trying to make it spawn single enemys like the big asteroids but NOT have it spawn smaller asteroids. So I just put the spawn traker on the enemy. I get the message "Level 1" the enemys spawn like there suppose to but then after there all gone it dosn't spawn again, or add any asteroids to the scene.


The codes for the spawner

if (!isObject(SpawnAreaBehavior))
{
   %template = new BehaviorTemplate(SpawnAreaBehavior);
   
   %template.friendlyName = "Spawn Area";
   %template.behaviorType = "AI";
   %template.description  = "Spawns objects inside the area of this object";

   %template.addBehaviorField(object, "The object to clone", object, "", t2dSceneObject);
   %template.addBehaviorField(count, "The number of objects to clone (-1 for infinite)", int, 50);
   %template.addBehaviorField(addOnRespawn, "The number of objects to add in each wave of respawns", int, 1);
   %template.addBehaviorField(spawnTime, "The time between spawns (seconds)", float, 2.0);
   %template.addBehaviorField(spawnVariance, "The variance in the spawn time (seconds)", float, 1.0);
   %template.addBehaviorField(spawnStart, "Time before the first spawn (seconds)", float, 2.0);
   
   %spawnLocations = "Area" TAB "Edges" TAB "Center" TAB "Top" TAB "Bottom" TAB "Left" TAB "Right";
   %template.addBehaviorField(spawnLocation, "The area in which objects can be spawned", enum, "Area", %spawnLocations);
   %template.addBehaviorField(addOnRespawn, "The number of objects to add in each wave of respawns", int, 1);
   %template.addBehaviorField(textObject, "The object which displays level text", object, "", t2dSceneObject);


}

function SpawnAreaBehavior::onAddToScene(%this, %scenegraph)
{
   %this.ZombiesCount = 0;
   %this.spawnCount = 0;
   %this.schedule(%this.spawnTime * 100, "spawn");
   %this.schedule(%this.spawnTime * 100, "messageTextObject");

}

function SpawnAreaBehavior::spawn(%this)
{
   if (!isObject(%this.object) || !%this.owner.enabled)
      return;
   
   %clone = %this.object.cloneWithBehaviors();
   %xPos = 0;
   %yPos = 0;
   %spawnLocation = %this.spawnLocation;
   %edges = "Top" TAB "Bottom" TAB "Left" TAB "Right";
   if (%spawnLocation $= "Edges")
      %spawnLocation = getField(%edges, getRandom(0, 3));
   
   switch$ (%spawnLocation)
   {
      case "Area":
         %xPos = getRandom(getWord(%this.owner.getAreaMin(), 0), getWord(%this.owner.getAreaMax(), 0));
         %yPos = getRandom(getWord(%this.owner.getAreaMin(), 1), getWord(%this.owner.getAreaMax(), 1));
      case "Center":
         %xPos = %this.owner.position.x;
         %yPos = %this.owner.position.y;
      case "Top":
         %xPos = getRandom(getWord(%this.owner.getAreaMin(), 0), getWord(%this.owner.getAreaMax(), 0));
         %yPos = getWord(%this.owner.getAreaMin(), 1);
      case "Bottom":
         %xPos = getRandom(getWord(%this.owner.getAreaMin(), 0), getWord(%this.owner.getAreaMax(), 0));
         %yPos = getWord(%this.owner.getAreaMax(), 1);
      case "Left":
         %xPos = getWord(%this.owner.getAreaMin(), 0);
         %yPos = getRandom(getWord(%this.owner.getAreaMin(), 1), getWord(%this.owner.getAreaMax(), 1));
      case "Right":
         %xPos = getWord(%this.owner.getAreaMax(), 0);
         %yPos = getRandom(getWord(%this.owner.getAreaMin(), 1), getWord(%this.owner.getAreaMax(), 1));
    

%clone.onAddToScene(%this.owner.scenegraph);
}
   
   %clone.position = %xPos SPC %yPos;
   
   %this.spawnCount++;
   if (%this.spawnCount < %this.count || %this.count == -1)
   {
      %minTime = (%this.spawnTime - %this.spawnVariance) * 1000;
      %maxTime = (%this.spawnTime + %this.spawnVariance) * 1000;
      %spawnTime = getRandom(%minTime, %maxTime);
      if( %spawnTime < 55 ) 
         %spawnTime = 55; 
      
      %this.schedule(%spawnTime, "spawn");
   }
   //might want to think about deleting the spawners if we're spawning spawners...
}
function SpawnAreaBehavior::update(%this, %object)
{
   %this.AsteroidsRemoved = %this.AsteroidsRemoved + 1;
   %totalSpawns = %object.spawnOnRemoveCount * %this.count;
   
   if (%this.AsteroidsRemoved == %totalSpawns)
   {
      %this.AsteroidsRemoved = 0;
      %this.count = %this.count + %this.addOnRespawn;
      %this.spawnCount = 0;
      %this.schedule(%this.spawnTime * 1000, "spawn");
      %this.schedule(%this.spawnTime * 1000, "messageTextObject");

    }
}
function SpawnAreaBehavior::messageTextObject(%this)
{
   %this.textObject.displayLevelText();
}


This enemy has the following behaviors on it:

TakesDamageAdv: from shooter demo
Template: Shooter Demo
Move Towards:Behavior Playground

and most important

Spawn Traker: From Astral Objects Tut.

if (!isObject(SpawnTrackerBehavior))
{
   %template = new BehaviorTemplate(SpawnTrackerBehavior);
   
   %template.friendlyName = "Spawn Tracker";
   %template.behaviorType = "Game";
   %template.description  = "Tags object and reports scene removal to spawner";

   %template.addBehaviorField(spawner, "The spawn area object", object, "", t2dSceneObject);
   %newObject.spawnOnRemoveCount = %this.count;

}

function SpawnTrackerBehavior::onRemoveFromScene(%this, %scenegraph)
{
   %spawner = %this.spawner.getBehavior("SpawnAreaBehavior");
   if (!isObject(%spawner))
      return;
      
   %spawner.update(%this.owner);
}

#1
12/05/2009 (7:16 pm)
I thought this was purely an issue with following the tutorial but I see you are basing your own game around some of the behaviors. Nothing wrong with that, you just need to make sure the behaviors play nicely with each other.

The root cause of the problem is that your counter is not working correctly. The goal is to have a counter that knows how many enemies you spawned and keeps track of how many you have killed. When the number killed equals the total spawned (i.e. no enemies left on the screen) then the next level or wave should spawn.

For the Astral Objects tutorial, if I had 10 big asteroids and each big asteroid makes 3 small asteroids - then I have 30 small asteroids to destroy in total. The counter is set up to take this into account and ignores that I had 10 big asteroids to begin with. Since you do not want enemies spawning from other enemies you need to change the update function to the following:

function SpawnAreaBehavior::update(%this, %object)  
 {  
    %this.AsteroidsRemoved = %this.AsteroidsRemoved + 1;  
    %totalSpawns = %this.count;  
      
    if (%this.AsteroidsRemoved == %totalSpawns)  
    {  
       %this.AsteroidsRemoved = 0;  
       %this.count = %this.count + %this.addOnRespawn;  
       %this.spawnCount = 0;  
       %this.schedule(%this.spawnTime * 1000, "spawn");  
       %this.schedule(%this.spawnTime * 1000, "messageTextObject");  
  
     }  
 }

I've changed the %totalSpawns line so it only refers to the count field in the behavior. You can get rid of the
%newObject.spawnOnRemoveCount = %this.count;
line in the spawn tracker behavior - it doesn't belong there anyway. You also have
%template.addBehaviorField(addOnRespawn, "The number of objects to add in each wave of respawns", int, 1);
listed twice in the spawn area behavior, best to remove one of them.
#2
12/05/2009 (7:38 pm)
Ok, Im trying it now I havent used the scene for a few months.
#3
12/05/2009 (7:52 pm)
Sorry I kinda lost you but is this rihgt?
It wont spawn anything even the asteroid.

function SpawnAreaBehavior::onAddToScene(%this, %scenegraph)
{
   %this.AsteroidsCount = 0;
   %this.spawnCount = 0;
   %this.schedule(%this.spawnTime * 100, "spawn");
   %this.schedule(%this.spawnTime * 100, "messageTextObject");

}

function SpawnAreaBehavior::spawn(%this)
{
   if (!isObject(%this.object) || !%this.owner.enabled)
      return;
   
   %clone = %this.object.cloneWithBehaviors();
   %xPos = 0;
   %yPos = 0;
   %spawnLocation = %this.spawnLocation;
   %edges = "Top" TAB "Bottom" TAB "Left" TAB "Right";
   if (%spawnLocation $= "Edges")
      %spawnLocation = getField(%edges, getRandom(0, 3));
   
   switch$ (%spawnLocation)
   {
      case "Area":
         %xPos = getRandom(getWord(%this.owner.getAreaMin(), 0), getWord(%this.owner.getAreaMax(), 0));
         %yPos = getRandom(getWord(%this.owner.getAreaMin(), 1), getWord(%this.owner.getAreaMax(), 1));
      case "Center":
         %xPos = %this.owner.position.x;
         %yPos = %this.owner.position.y;
      case "Top":
         %xPos = getRandom(getWord(%this.owner.getAreaMin(), 0), getWord(%this.owner.getAreaMax(), 0));
         %yPos = getWord(%this.owner.getAreaMin(), 1);
      case "Bottom":
         %xPos = getRandom(getWord(%this.owner.getAreaMin(), 0), getWord(%this.owner.getAreaMax(), 0));
         %yPos = getWord(%this.owner.getAreaMax(), 1);
      case "Left":
         %xPos = getWord(%this.owner.getAreaMin(), 0);
         %yPos = getRandom(getWord(%this.owner.getAreaMin(), 1), getWord(%this.owner.getAreaMax(), 1));
      case "Right":
         %xPos = getWord(%this.owner.getAreaMax(), 0);
         %yPos = getRandom(getWord(%this.owner.getAreaMin(), 1), getWord(%this.owner.getAreaMax(), 1));
    

%clone.onAddToScene(%this.owner.scenegraph);
}
   
   %clone.position = %xPos SPC %yPos;
   
   %this.spawnCount++;
   if (%this.spawnCount < %this.count || %this.count == -1)
   {
      %minTime = (%this.spawnTime - %this.spawnVariance) * 1000;
      %maxTime = (%this.spawnTime + %this.spawnVariance) * 1000;
      %spawnTime = getRandom(%minTime, %maxTime);
      if( %spawnTime < 55 ) 
         %spawnTime = 55; 
      
      %this.schedule(%spawnTime, "spawn");
   }
}
function SpawnAreaBehavior::update(%this, %object)     
 {     
    %this.AsteroidsRemoved = %this.AsteroidsRemoved + 1;     
    %totalSpawns = %this.count;     
         
    if (%this.AsteroidsRemoved == %totalSpawns)     
    {     
       %this.AsteroidsRemoved = 0;     
       %this.count = %this.count + %this.addOnRespawn;     
       %this.spawnCount = 0;     
       %this.schedule(%this.spawnTime * 1000, "spawn");     
       %this.schedule(%this.spawnTime * 1000, "messageTextObject");     
     
     }     
 }  

function SpawnAreaBehavior::messageTextObject(%this)
{
   %this.textObject.displayLevelText();
}

#4
12/05/2009 (8:32 pm)
Is that everything in your script? Did you somehow delete the behavior template? If so add it back in before the rest of the functions you posted:

if (!isObject(SpawnAreaBehavior))
{
   %template = new BehaviorTemplate(SpawnAreaBehavior);
   
   %template.friendlyName = "Spawn Area";
   %template.behaviorType = "AI";
   %template.description  = "Spawns objects inside the area of this object";

   %template.addBehaviorField(object, "The object to clone", object, "", t2dSceneObject);
   %template.addBehaviorField(count, "The number of objects to clone (-1 for infinite)", int, 50);
   %template.addBehaviorField(addOnRespawn, "The number of objects to add in each wave of respawns", int, 1);
   %template.addBehaviorField(spawnTime, "The time between spawns (seconds)", float, 2.0);
   %template.addBehaviorField(spawnVariance, "The variance in the spawn time (seconds)", float, 1.0);
   %template.addBehaviorField(spawnStart, "Time before the first spawn (seconds)", float, 2.0);
   
   %spawnLocations = "Area" TAB "Edges" TAB "Center" TAB "Top" TAB "Bottom" TAB "Left" TAB "Right";
   %template.addBehaviorField(spawnLocation, "The area in which objects can be spawned", enum, "Area", %spawnLocations);
   %template.addBehaviorField(textObject, "The object which displays level text", object, "", t2dSceneObject);


}
#5
12/05/2009 (8:34 pm)
Oh I didn't delete It I just thought I wasn't needed in this post It seemed like too much code ;)
#6
12/05/2009 (8:43 pm)
At first glance everything looks okay. Is the behavior properly attached to the object that controls the spawning and "object" field filled in with the name of the object you want cloned?

If you're still having trouble I'll have a look tomorrow after a bit of sleep.
#7
12/05/2009 (9:46 pm)
Ya I named the thing asteroids Ill try with another object. BTW thanks for all your help :)
#8
12/06/2009 (5:23 am)
Ok, I fooled around a little bu=it and now I've managed to make it spawn but it just spawns and never stops :?. I taking a noob guess but it seems like it disn't keeping trak of the spawned object. Just like going through instantly. I have the SpawnTraker behavior attached to it, and have the it set to the spawner.
#9
12/06/2009 (7:26 am)
Wow. I started the Astral Objects tutorial from scratch with TGB 1.7.4 and am seeing the same issue with endless spawns. I've been putting in echo statements to debug the scripts a bit - my first guess is that something has changed with how the template behavior and cloneWithBehaviors interact from previous versions of TGB. My console log looks like this:

t2dSceneGraph::addToScene() - Object '1370' is already in a SceneGraph!.
removing asteroid
asteroid count 1
total spawns 3
t2dSceneGraph::addToScene() - Object '1377' is already in a SceneGraph!.
removing asteroid
asteroid count 2
total spawns 3
t2dSceneGraph::addToScene() - Object '1384' is already in a SceneGraph!.
removing asteroid
asteroid count 3
total spawns 3
t2dSceneGraph::addToScene() - Object '1391' is already in a SceneGraph!.
removing asteroid
asteroid count 1
total spawns 4

Going to need a bit more time to figure this out.
#10
12/06/2009 (8:29 am)
Thats what mine does too! I though maybe I made an embarassing mistake or somthing, but I guess somthing in TGB changed for it to do that. And I havn't done the real tut but i've came close so that just might mean that it's because Im not doing exactly what the tut said.
#11
12/06/2009 (12:21 pm)
Solved!

After doing a bit of forum searching and playing with scripts, the issue is with the onRemoveFromScene callback found in the spawn tracker behavior. TGB has a bug using cloneWithBehaviors() - it triggers the onRemoveFromScene callback at the point of creating the clone.

I fixed this by changing onRemoveFromScene to onRemove. Here is how the script looks now:

if (!isObject(SpawnTrackerBehavior))
{
   %template = new BehaviorTemplate(SpawnTrackerBehavior);
   
   %template.friendlyName = "Spawn Tracker";
   %template.behaviorType = "Game";
   %template.description  = "Tags object and reports scene removal to spawner";

   %template.addBehaviorField(spawner, "The spawn area object", object, "", t2dSceneObject);
}

function SpawnTrackerBehavior::onRemove(%this)
{
   %spawner = %this.spawner.getBehavior("SpawnAreaBehavior");
   if (!isObject(%spawner))
      return;
   
   %spawner.update(%this.owner);
}

I'll update the TDN tutorial with this change.
#12
12/06/2009 (4:25 pm)
I still can't get it to work. It still spawns uncontrollably :? I replaced the the old code with the new.
#13
12/06/2009 (5:39 pm)
Robert, just sent an email to you. If you want to try to debug it yourself, try putting in echo statements to figure out what is causing the SpawnAreaBehavior::update function to trigger. Ideally it should only be when you kill an enemy, and not when an object is cloned.