Actionscript 3 help - Random spawn movieclip

Started by
2 comments, last by Patrick B 10 years, 4 months ago

Hi guys, I've tried getting help on other forums but not getting much help so I hope I've came to the right place smile.png I'm doing a project for college in games design, it's a 2D First Person Shooter (Top down) game, and I've run into a problem..... I want to get multiple instances of my 'Enemy' movieclip to spawn anywhere off stage, I'd say no further than 20px from all edges of the stage, I have a quick nice(... or not) annotated image below to help explain visually. You should also know that I'm still learning AS3 so yes I am a newbie at it, and I used a online tutorial to help get bullets working. Player movement works as well, it's just the Enemys I'm having trouble with, both getting multiple instances of them at anyone point in time and random spawning positions off the viewable area of the stage.

[sharedmedia=core:attachments:19186]
I hope this is possible to do, thanks for the help.

Software is only as good as the person that made it.

Advertisement

I don't know ActionScript so I can't give you specific examples. Since this is a homework assignment, that's probably a good thing. :)

If you're having trouble getting more than one enemy on the screen, I'm guessing it's because you only have one variable (or set of variables) for your enemy. If you're creating multiple enemies and storing them in the same variable, you're basically throwing away the previous instances of your enemy to hold the enemy you just created. If you haven't done so already, you'll want to Encapsulate the data for an enemy into a class. And then you'll want a list(or array) of enemies.

Once you have your list of enemies, you'll want to adapt your original logic that dealt with 1 enemy, to deal with a list instead. Instead of updating/drawing/hit-testing 1 enemy, loop through the list and update/draw/hit-test each one. You might find it useful to group these functions into an EnemyManager or EnemyController class. You'll want the ability to add a randomly spawned enemy, and remove dead enemies as well.

For spawning random enemies just off-screen, first I'd get enemies randomly spawning on the screen working (so you can see it). And then adapt it to spawning off the screen. Once you have a random x/y for your enemy, you can get a random number 1-4 (0-3) and assign a logical value to each number sometimes called an enumeration or enum. 0 could mean left, 1 right, 2 up, 3 down. So then if you rolled a 0(left), you could just set the x component of that enemy to just off the left edge.

I'm assuming you'll also have random direction/velocity for these guys. Make sure you either have a bounding box that reflects the enemies or you pick a direction that moves it into the view area. Otherwise enemies spawning left, might go left and float off into the ether. :)

- Eck

EckTech Games - Games and Unity Assets I'm working on
Still Flying - My GameDev journal
The Shilwulf Dynasty - Campaign notes for my Rogue Trader RPG

I'm sure someone else would have something more elegant but this is some code I dug up out of an old project of my own for what sounds like a similar game, so it does work :p


// This rectangle defines your screen size. Replace 640x480 with the size of your Flash app.
// We will spawn enemies just outside this rectangle.
var _screenRect:Rectangle = new Rectangle(0, 0, 640, 480);

// This rectangle defines the range of positions enemies should spawn in. It will be adjusted
// to match the size of the enemy being spawned during the set position method.
var _spawnRect:Rectangle = new Rectangle();

// Return a random number between min and max.
private function RandomWithinRange(min:Number, max:Number):Number
{
	return Math.random() * (max - min) + min;
}

// Send a sprite class to this to move it off the screen.
private function MoveOffscreen(enemy:Sprite):void
{
	var spawnX:Number;
	var spawnY:Number;
	
	// We set the spawn rectangle to the size of the screen but
	// expand it out by the size of the object being spawned.
	// This will allow enemies of any size to always be moved off screen.
	var padX:Number = enemy.width;
	var padY:Number = enemy.height;
	
	_spawnRect.x = _screenRect.x - padX;
	_spawnRect.width = _screenRect.width + padX;
	_spawnRect.y = _screenRect.y - padY;
	_spawnRect.height = _screenRect.height + padY;
	
	// Pick randomly between X and Y, and move the spawn position to the full extent on
	// that axis. Then randomise the other axis.
	if (Math.random() > 0.5)
	{
		// We are setting the X to its full distance on the left or right, randomly pick which
		if (Math.random() > 0.5)
		{
			// left
			spawnX = _spawnRect.x;
		}
		else
		{
			// right
			spawnX = _spawnRect.width;
		}
		// Randomise the other axis.
		spawnY = RandomWithinRange(_spawnRect.y, _spawnRect.height);
	}
	else
	{
		// We are setting the Y to its full distance up or down, randomly pick which
		if (Math.random() > 0.5)
		{
			// Up
			spawnY = _spawnRect.y;
		}
		else
		{
			// down
			spawnY = _spawnRect.height;
		}
		// Randomise the other axis.
		spawnX = RandomWithinRange(_spawnRect.x, _spawnRect.width);
	}
	
	// Move the enemy to the new position
	enemy.x = spawnX;
	enemy.y = spawnY;
}

If you're using a generic Sprite instance for creating instance of enemy, you're limiting what they'll be able to do (not to mention making your code unnecessarily complex). So if you're not already doing so I'd recommend making the enemy a class of its own...something like this (note that you'll have to update the package, and maybe the class name if you can't use or don't like "Enemy"):

[source]

package your.package.path {

import flash.display.Sprite;

import flash.geom.Rectangle;

import flash.events.Event;

public class Enemy extends Sprite {

private var _excArea:Rectangle;

public function Enemy(spawnExclusionArea:Rectangle) {

this._excArea=spawnExclusionArea;

//Ensure that enemy is on the stage before trying to update its position...

this.addEventListener(Event.ADDED_TO_STAGE, this.placeEnemy);

}

private function placeEnemy(eventObj:Event):void {

this.removeEventListener(Event.ADDED_TO_STAGE, this.placeEnemy);

this.x=this.getExcludedPoint("x");

this.y=this.getExcludedPoint("y");

}

function getExcludedPoint(axis:String):Number {
if (Math.random()<=0.5) {
if (axis=="x") {

//get point left of defined area...
return (Math.random()*(_excArea.x-this.width));
} else {
//get point above defined area...
return (Math.random()*(_excArea.y-this.height));
}
} else {
if (axis=="x") {
//get point to right of defined area...
return ((_excArea.width+_excArea.x)+(Math.random()*(stage.stageWidth-(_excArea.width+_excArea.x))));
} else {
//get point below defined area...
return ((_excArea.height+_excArea.y)+(Math.random()*(stage.stageHeight-(_excArea.height+_excArea.y))));
}
}
}

}

[/source]

Using an Enemy class will allow you to extend your code to create custom enemies (with custom behaviours, graphics, etc.):

[source]

package your.package.path {

import your.package.path.Enemy;

public class Boss extends Enemy {

...

[/source]

Note that in the exclusion area calculations we have to use stageWidth and stageHeight instead of the stage's width/height properties (these tell us the dimensions of all of the content on the stage, which typically aren't the dimensions of the whole stage area). If you want a larger or smaller display area (for example, your game doesn't take up the entire stage), change the stageWidth and stageHeight values to your maximum viewable area.

In the class that spawns your enemies, creating a randomly placed enemy is very simple:

[source]

import your.package.path.Enemy;
public function createEnemy():Enemy {
var exclusionArea:Rectangle=new Rectangle (100,100,300,200); //Don't generate enemies in a rectangle 300 wide by 200 high at position 100x100
var newEnemy:Enemy=new Enemy(exclusionArea);
this.addChild(newEnemy);
return (newEnemy);
}

[/source]

Because the random placement is done in the Enemy class constructor, this is literally all that's needed. The createEnemy function returns the new Enemy instance, but that's entirely optional.

If you wanted to keep track of all active enemies, you could extend the Enemy class to track itself using a static variable:

[source]

public class Enemy extends Sprite {

private static var _enemies:Vector.<Enemy>=new Vector.<Enemy>();

private var _excArea:Rectangle;

public function Enemy(spawnExclusionArea:Rectangle) {

this._excArea=spawnExclusionArea;

//Ensure that enemy is on the stage before trying to update its position...

this.addEventListener(Event.ADDED_TO_STAGE, this.placeEnemy);

this.addEventListener(Event.REMOVED_FROM_STAGE, this.removeEnemy);

}

private function placeEnemy(eventObj:Event):void {

this.removeEventListener(Event.ADDED_TO_STAGE, this.placeEnemy);

this.x=this.getExcludedPoint("x");

this.y=this.getExcludedPoint("y");

this.addEnemyToList();

}

private function removeEnemy(eventObj:Event):void {

this.removeEventListener(Event.ADDED_TO_STAGE, this.removeEnemy);

this.removeEnemyFromList();

}

private function addEnemyToList():void {

_enemies.push(this);

}

private function removeEnemyFromList():void {

var compactVector:Vector.<Enemy>=new Vector.<Enemy>();

for (var count:uint=0; count<_enemies.length; count++) {

var currentEnemy:Enemy=_enemies[count];

if (currentEnemy!=this) {

compactVector.push(currentEnemy)

}

}

_enemies=compactVector;

}

public static function get enemies():Vector.<Enemy> {

return (_enemies);

}

function getExcludedPoint(axis:String):Number {
if (Math.random()<=0.5) {

if (axis=="x") {
//get point left of defined area...
return (Math.random()*(_excArea.x-this.width));
} else {
//get point above defined area...
return (Math.random()*(_excArea.y-this.height));
}
} else {
if (axis=="x") {
//get point to right of defined area...
return ((_excArea.width+_excArea.x)+(Math.random()*(stage.stageWidth-(_excArea.width+_excArea.x))));
} else {
//get point below defined area...
return ((_excArea.height+_excArea.y)+(Math.random()*(stage.stageHeight-(_excArea.height+_excArea.y))));
}
}
}

}

[/source]

From outside the Enemy class, all currently active enemies would be available as Enemy.enemies (a vector array). I use a Vector since it's more efficient, but making _enemies a standard Array instance would work equally well.
I've added the list management to happen automatically -- a new Enemy is added when added to the stage display list, and it's automatically removed when removed from the stage. If you wanted to maintain the list manually (for example, an Enemy isn't considered to be a part of the list until something else happens), just make the relevant functions public and call them from outside the Enemy class.
Hope this helps.

This topic is closed to new replies.

Advertisement