as3 enemy generation and level scripts

8 posts

Flag Post

so maybe I’m just not searching for the right stuff, but I can’t seem to find a tutorial on google (or anywhere) that talks about how to create a level script (ie create 4 of x enemy in location y at 30 seconds and then 15 seconds later create 4 of y enemy in location x, etc). I know there has to be a standard way of doing this, I’m just not sure what to search for to figure it out. Any help? If anyone has done anything like this in the past, I would love to see your code so I can adapt it to what I’m trying to do. I’m thinking something like ZunderFury where what appeared on each level was scripted and never changed.

Thanks.

 
Flag Post

Well, you don’t exactly need a script if you’re only going to do one type of thing with the data in it. You just need to decide on a structure to store the data in and write a function to handle it.

If you only want to start creating enemies at set intervals, you could do something like this:

class Data{
    static public var LevelData = [[false, [enemyX, locationY, 4], [enemyY, locationX, 4]], //level 1
                                   [[powerfulenemyZ, locationQ, 8], false, false, [bigenemyW, locationX, 3]], //level 2
                                   [[smallenemyE, locationY, 22], false, [ultimateenemyA, locationO, 1]]]; //level 3
}

class EnemySpawner{
    var data;
    var count = 0;

    public function EnemySpawner(data:Array, delay:int) {
        this.data = data;
        var spawnTimer:Timer = new Timer(delay, data.length);
        spawnTimer.addEventListener("timer", this.loop);
        spawnTimer.start();
    }

    public function loop(evt:TimerEvent):void {
        var nextenemy = data[count];
        count++;
        if(nextenemy){
            var myTimer = new Timer(10, nextenemy[2]);
            myTimer.addEventListener("timer", makeEnemyCreationFunction(nextenemy[0], nextenemy[1]));
            myTimer.start();
        }
    }
}

And use it with this sort of code:

new EnemySpawner(Data.LevelData[levelnumber], 15000); //note the first level is 0 here, not 1

All the data for the enemies in each level is stored in the big LevelData array. When you call the level initialization function, it pulls out the data for the current level – for instance, the first level would be [false, [enemyX, locationY, 4], [enemyY, locationX, 4]]. Then it creates an instance of EnemySpawner with that data and a fifteen-second delay. The EnemySpawner instance sets up its loop function to be called every fifteen seconds, for as many times as its data array has elements.

Each time EnemySpawner.loop is called, it’ll pull the next element of the data array and put that into the nextenemy variable. The first time (T+15sec), this value will be false, so it’ll skip over the rest of the function and exit, doing nothing. The second time (T+30sec), this value will be [enemyX, locationY, 4] – so it sets up a timer to create 4 of enemyX at locationY. Similarly, the third time (T+45sec), it’ll set up a timer to create 4 of enemyY at locationX.

This exact method does have one limitation – you can only create enemies on a multiple of the interval which you set for spawnTimer. Thus, if you set it to call the loop every 15 seconds, you can create enemies at 15, 30, and 45 seconds, and so on, but not at 10 or 32 seconds. If this is a problem, and you’d rather write the level data in a format such as [[enemyX, locationY, 4, 30], [enemyY, locationX, 4, 15]] – where you can specify arbitrary delays – that’s not too hard to do. You’ll just have to have EnemySpawner.loop read the delay and set up a one-use timer each time to call itself again at the right time, instead of just using one timer for all the calls.

Does that help you?


Now! The one thing I don’t know how to do… what sort of data should things like “enemyX” and “enemyY” be? Perhaps you already have something set up for this – if so, I’m curious what it is.

My instinct would be to have something like this:

class EnemyX extends Enemy{
    //whatever
}

var enemytype:Class = EnemyX; //simplified version of the code to determine the type of enemy that's next up

...

newenemy = new enemytype(somelocation); //code that would actually create the enemy

…but, as far as I know, Actionscript doesn’t support first-class types or first-class classes, making code like this impossible to write so simply.

 
Flag Post

Holy cow… I haven’t had time to try this yet, but I think that’s exactly what I need. Once again, an elegant, explainable solution. Thanks!

 
Flag Post

:)

Does anyone have thoughts about what I asked at the end of that last post? What the best data type for storing the type of enemy would be?

 
Flag Post

I got my flash trail but i doubt i can do diddly with it. I tried this but i got way to confused

 
Flag Post

ok, so in theory, your code makes sense phanta, but I’m getting some odd errors. I’m not using classes just yet, i’m just writing it out in regular functions, because that’s what’s familiar to me, but here’s what i’ve got:

var LevelData:Array = [[[0, "right", 5],false, [0, "left", 10], [0, "top", 10]],
			       [[0, "right", 5],false,[0, "left", 10], [0, "top", 10]]];

	var count:Number = 0;
	var mydata:Array;
	function EnemySpawner(thedata:Array, delay:int)
	{
		count = 0;
		trace (thedata);
		mydata = thedata;
		trace (mydata);
		var spawnTimer:Timer = new Timer(delay, thedata.length);
		spawnTimer.addEventListener("timer", loop);
		spawnTimer.start();
	}
	
	function loop(e:TimerEvent):void
	{
		trace(mydata + count);
		trace(mydata[count]);
		var nextenemy;
		nextenemy = mydata[count];
		trace(nextenemy[2]);
		count++;
		if (nextenemy)
		{
			var myTimer = new myTimer(25, nextenemy[2]);
			myTimer.addEventListener("timer", makeEnemyCreationFunction(nextenemy[0], nextenemy[1]));
			myTimer.start();
		}
	}

function makeEnemyCreationFunction(type, dir):Function
{
	return function(event:TimerEvent):void	
		{
			createBadGuy(type, dir);
		}
} 


EnemySpawner(LevelData[0], 2000);

The trace statements all come out as expected, but then I get this:


0,right,5,false,0,left,10,0,top,10
0,right,5,false,0,left,10,0,top,10
0,right,5,false,0,left,10,0,top,100
0,right,5
5
TypeError: Error #1007: Instantiation attempted on a non-constructor.
at stv_fla::MainTimeline/loop()
at flash.utils::Timer/flash.utils:Timer::_timerDispatch()
at flash.utils::Timer/flash.utils:Timer::tick()

If I force nextenemy to be an Array (var nextenemy:Array), I get this:


0,right,5,false,0,left,10,0,top,10
0,right,5,false,0,left,10,0,top,10
0,right,5,false,0,left,10,0,top,100
0,right,5
5
TypeError: Error #1007: Instantiation attempted on a non-constructor.
at stv_fla::MainTimeline/loop()
at flash.utils::Timer/flash.utils:Timer::timerDispatch()
at flash.utils::Timer/flash.utils:Timer::tick()
0,right,5,false,0,left,10,0,top,101
false
TypeError: Error #1034: Type Coercion failed: cannot convert false to Array.
at stv_fla::MainTimeline/loop()
at flash.utils::Timer/flash.utils:Timer::timerDispatch()
at flash.utils::Timer/flash.utils:Timer::tick()
0,right,5,false,0,left,10,0,top,101
false
TypeError: Error #1034: Type Coercion failed: cannot convert false to Array.
at stv
fla::MainTimeline/loop()
at flash.utils::Timer/flash.utils:Timer::
timerDispatch()
at flash.utils::Timer/flash.utils:Timer::tick()

Thoughts?

If I take out the implicit Array designation on the two arrays, it seems to fix the false issue, but i still get the non-constructor message. It seems to be a problem with the myTimer instantiation in the loop function.

 
Flag Post

>It seems to be a problem with the myTimer instantiation in the loop function.

Indeed, that’s where the problem lies. Sometimes, errors in code are hard to track down… but in this case, it’s so obvious that you’re looking right past it. ;)

Note the error message:

TypeError: Error #1007: Instantiation attempted on a non-constructor.

And take a good look at this line:

var myTimer = new myTimer(25, nextenemy[2]);

See it yet?

Also, yeah, you’ll get errors if you declare nextenemy to be an array. The reason is simple – sometimes it’ll be an array, and sometimes it’ll be a boolean (false). Just as the error message says…

TypeError: Error #1034: Type Coercion failed: cannot convert false to Array.

Oh, and you probably shouldn’t put “trace(nextenemy[2]);” before the code that checks if nextenemy is an array. Flash is generous in its runtime error handling, so trying to perform array access on a boolean value doesn’t blow up your program, but it’s still kind of sloppy.

 
Flag Post

wow… I’m an idiot… just walking away for a while it was obvious… new Timer instead of new myTimer… >_> shouldn’t have gone for help so fast. It’s all working as intended now.