Different class for different projectiles


(David Williams) #1

Wasn’t really sure on how to describe my problem in the title.
Anyways, I want to be able to store the “class” of my projectile at weapon creation, and then create new objects of the same class every time I shoot.

In my current code, every time I shoot it just repositions the same entity projectile rather than create a new one.

	public var bullet:Projectile;
	public function WpnBase(_gfx:Image, _bullet:Projectile) 
	{
		graphic = _gfx;
		bullet = _bullet;
	}
	public function fire(fromX:int, fromY:int, toX:int, toY:int):void
	{
		bullet.x = fromX;
		bullet.y = fromY;
		FP.world.add(bullet);
		bullet.shoot(toX, toY);
	}

I tried doing like, var prj:bullet to see if I could create a variable of the same type as an existing one, but it isn’t a run time constant.


(Justin Wolf) #2

Forgive me if I’m misunderstanding, but can’t you just use FP.world.create()?

(FP.world.create(Bullet) as Bullet).init(x, y, toX, toY);

And then in your Bullet class, create an init function.

public function init(x:int, y:int, toX:int, toY:int):void
{
     this.x = x, this.y =y;
     this.toX = toX, this.toY = toY;
}

(David Williams) #3

That won’t work because ‘bullet’ isn’t a class, it’s a variable of type Projectile. I want to be able to create a custom projectile variable, then pass it to the gun, where it will ‘save’ it and use that type of projectile.

‘gun’ code:

    package  
{
	import net.flashpunk.Entity;
	import net.flashpunk.FP;
	import net.flashpunk.graphics.Image;
	
	/**
	 * ...
	 * @author David Williams
	 */
	public class WpnBase extends Entity 
	{
		public var bullet:Projectile;
		private var gfx:Image;
		private var ma:Number;
		public function WpnBase(_gfx:Image, _bullet:Projectile) 
		{
			graphic = gfx =  _gfx;
			bullet = _bullet;
		}
		public function fire():void
		{
			var angAdj:Number = ma * Math.PI / -180;
			var xAdj:Number = Math.round(13.375 + 15.63 * Math.cos(angAdj) - 9.29 * Math.sin(angAdj));
			var yAdj:Number = Math.round(14.25 + 15.63 * Math.sin(angAdj) + 9.29 * Math.cos(angAdj));
			bullet.x = this.x - 18 + xAdj;
			bullet.y = this.y - 18 + yAdj;
			FP.world.add(bullet);
			bullet.shoot(ma);
		}
		override public function update():void
		{
			ma = FP.angle(this.x, this.y, world.mouseX, world.mouseY);
			gfx.angle = ma;
		}
	}

}

(Zachary Lewis) #4

What you’re describing is a textbook use of polymorphism.

Say you have a projectile class, Projectile. This class describes the behaviors of any projectile.

Let’s take look at this simple class:

public class Projectile extends Entity
{
	// Set default values for the base projectile.
	protected var _speed:Number = 100;
	protected var _size:int = 10;
	protected var _velocity:Point;

	public function Projectile() { }

	/**
	 * Initializes the projectile with a position and an angle of fire.
	 * @param	xLocation The x-location of the projectile.
	 * @param	yLocation The y-location of the projectile.
	 * @param	angle The angle, in degrees, of fire.
	 */
	public function init(xLocation:int, yLocation:int, angle:Number):void
	{
		// Children can set their graphics, size and speed before final setup is done.

		x = xLocation;
		y = yLocation;

		// Save our projectile velocity based on angle and speed.
		// Doing this once prevents the need to recalculate sines and cosines constantly.
		_velocity = new Point(_speed * Math.cos(FP.RAD * angle), _speed * Math.sin(FP.RAD * angle));

		// Create our projectile's hitbox based on size.
		setHitbox(_size, _size);

		
	}

	override public function update():void
	{
		// During update, move the projectile.
		x += FP.elapsed * _velocity.x;
		y += FP.elapsed * _velocity.y;
	}
}

Notice how it does the rough setup for any projectile. Now, we want a small, fast projectile, Bullet, and a large, slow projectile, Missle. If we inherrit from Projectile, we need to make minimal changes.

public class Bullet extends Projectile
{
	public function Bullet() { }

	override public function init(xLocation:int, yLocation:int, angle:Number):void
	{
		_speed = 500;
		_size = 2;

		graphic = new Image(BULLET_IMAGE);

		super.init(xLocation, yLocation, angle);
	}
}
public class Missle extends Projectile
{
	public function Bullet() { }

	override public function init(xLocation:int, yLocation:int, angle:Number):void
	{
		_speed = 50;
		_size = 25;

		graphic = new Image(MISSLE_IMAGE);

		super.init(xLocation, yLocation, angle);
	}
}

Now, we can use the fact that both Bullet and Missle are children of Projectile when creating Weapon. Here’s the important bits:

public class Weapon
{
	/** The type of Projectile to fire. */
	public var equippedProjectile:Class;

	...

	public function fire():void
	{
		// Error check to make sure the equippedProjectile is a Projectile.
		if (Projectile(equippedProjectile))
		{
			// Determine angle and location...

			var xLocation:int = ...
			var yLocation:int = ...
			var angle:Number = ...

			// ...and FIRE!
			Projectile(FP.world.create(equippedProjectile)).init(xLocation, yLocation, angle);
		}
	}
}

Now, your weapon will fire anything that extends Projectile. If you want to change what you fire, you just set your equippedProjectile property on Weapon.

// Equip missles!
myWeapon.equippedProjectile = Missles;

// Too close for rockets, switching to guns!
myWeapon.equippedProjectile = Bullet;

This is all well and good, but if you want Weapon to fire something that isn’t a projectile (like a shield gun…), you’ll want to look at creating an interface


(David Williams) #5

Is there any way to do that (which is indeed what I’m trying) without actually making a physical bullet class? Kind of like:

var proj:Projectile = new Projectile();
proj.x  = 5;
proj.y = 60;
proj.graphic = bulletGFX;
proj.damage = 5;

and the create multiple instances of ‘proj’?


(Zachary Lewis) #6

Sure. You can write a clone() function that creates a new Projectile with the same values.


(David Williams) #7

How so exactly? I tried this, but it’s still only creating 1 at a time/removing the previous one:

	public function copy(_bul:Projectile):Projectile
	{
		var p:Projectile = _bul;
		return p;
	}

(Zachary Lewis) #8

Nope. You’re simply returning a reference of the same Projectile. You’ll want to make a new Projectile, then give it the same properties as the source.

public function copy(source:Projectile):Projectile
{
  // Create a new Projectile.
  var p:Projectile = new Projectile();

  // Copy the properties needed.
  p.size = source.size;
  p.speed = source.speed;
  p.damage = source.damage;

  // Return the copy.
  return p;
}

(David Williams) #9

Alright, thanks, that’s the next thing I was going to try. I’d say solved.


(Zachary Lewis) #10