AS3 How to find current location of mc? - actionscript-3

Thanks for the startdrag() suggestions, but I'm trying to avoid that atm
I'm trying to create a drag motion by using mouse_down then the mc = mouseX. Here is an image of the situation
But when I click, the mc always jumps to its registration point, which is the top left corner atm.
I can't work my head around how to grab the current location of the mc. Note that the mc (all_mc) is wider than the stage.
Can someone please help me out?
this.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
function mouseDownHandler(e:MouseEvent) {
this.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
}
function mouseMoveHandler(e:MouseEvent) {
all_mc.x = mouseX;
}
Edit:
Ok I kind of worked out the x location of the mouse in relation to the registration point of the mc (the registration is at the top left):
Math.abs(stage.x - all_mc.x) + mouseX
But how to I select that point of on the mc?

Modified Marty Wallace's answer to handle the difference between where you click and the registration point:
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
public class DummyTest extends Sprite {
private var mDeltaX:Number = 0;
private var mDeltaY:Number = 0;
private var mGfx:Sprite;
public function DummyTest() {
mGfx = new Sprite();
with(mGfx.graphics) {
beginFill(0x00FF00);
drawRect(0, 0, 200, 200);
endFill();
}
addChild(mGfx);
addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
}
private function onMouseDown(e:MouseEvent) : void {
mDeltaX = mGfx.x - mouseX;
mDeltaY = mGfx.y - mouseY;
addEventListener(Event.ENTER_FRAME, onEnterFrame);
addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
private function onMouseUp(e:MouseEvent) : void {
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
private function onEnterFrame(e:Event) : void {
mGfx.x = parent.mouseX + mDeltaX;
mGfx.y = parent.mouseY + mDeltaY;
}
}
}

import flash.events.MouseEvent;
import flash.display.Sprite;
var mc:MovieClip;
var sprite:Sprite = new Sprite ;
sprite.graphics.beginFill(0x000000);
sprite.graphics.drawRect(0,0,100,100);
sprite.graphics.endFill();
this.addChild(sprite);
sprite.addEventListener(MouseEvent.MOUSE_DOWN,function(){
sprite.startDrag();}
);
sprite.addEventListener(MouseEvent.MOUSE_UP,function(){
sprite.stopDrag();}
);

startDrag() has a lockCenter argument which you could try playing with.
lockCenter:Boolean (default =
false) — Specifies whether the
draggable sprite is locked to the
center of the mouse position (true),
or locked to the point where the user
first clicked the sprite (false).
Without startDrag as per comment
Make this the base class of an object that you want to drag:
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.Event;
public class Draggable extends Sprite
{
// vars
private var _hx:Number = 0;
private var _hy:Number = 0;
/**
* Constructor
*/
public function Draggable()
{
addEventListener(MouseEvent.MOUSE_DOWN, _mouseDown);
}
/**
* MouseEvent.MOUSE_DOWN
*/
private function _mouseDown(e:MouseEvent):void
{
_hx = mouseX;
_hy = mouseY;
addEventListener(Event.ENTER_FRAME, _handle);
addEventListener(MouseEvent.MOUSE_UP, _mouseUp);
}
/**
* MouseEvent.MOUSE_UP
*/
private function _mouseUp(e:MouseEvent):void
{
removeEventListener(Event.ENTER_FRAME, _handle);
removeEventListener(MouseEvent.MOUSE_UP, _mouseUp);
}
/**
* Event.ENTER_FRAME
*/
private function _handle(e:Event):void
{
x = parent.mouseX - _hx;
y = parent.mouseY - _hy;
}
}
}

stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
var positionX:Number = 0;
var positionY:Number = 0;
function mouseDownHandler(e:MouseEvent) {
positionX = all_mc.mouseX;// save the x position for future reference
positionY = all_mc.mouseY;// save the y position for future reference
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
}
function mouseMoveHandler(e:MouseEvent) {
all_mc.x = stage.mouseX - positionX ;
all_mc.y = stage.mouseY - positionY ;
}
function mouseUpHandler(e:MouseEvent) {
stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
}

Related

AS3 - Space Shooter Enemy Shots

I'm very new to AS3 and programming in general and I've been developing a space shooter game in AS3 and have run into trouble regarding the enemy shots. The enemies fly vertically down from the top of the screen and should fire two shots (one going left and one going right) once their y coordinates equal that of the player. This works fine, except after anything from 30 seconds to 2 minutes, the following errors occur.
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at EnemyShip2/fireWeapon()[G:\Games Related\1942\src\EnemyShip2.as:78]
at EnemyShip2/loop2()[G:\Games Related\1942\src\EnemyShip2.as:65]
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at EnemyShot/removeSelf()[G:\Games Related\1942\src\EnemyShot.as:43]
at EnemyShot/loop()[G:\Games Related\1942\src\EnemyShot.as:36]
Below is the relevant code, for the enemy ship class as well as the enemy shot class.
Enemy Ship
package
{
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.Stage;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.utils.Timer;
/**
* ...
* #author D Nelson
*/
[Embed(source = "../assets/enemyship2.png")]
//This enemy moves relatively slowly and fires horizontal shots
public class EnemyShip2 extends Bitmap
{
private var vy:Number = 3;
//private var ay:Number = .2;
private var target:HeroShip;
private var enemyShip2:EnemyShip2;
private var enemyfireTimer:Timer;
private var enemycanFire:Boolean = true;
public function EnemyShip2(target:HeroShip):void
{
this.target = target;
scaleX = 0.3;
scaleY = 0.3;
x = Math.floor(Math.random() * 550);
y = -105;
addEventListener(Event.ENTER_FRAME, loop2, false, 0, true);
enemyfireTimer = new Timer(1000, 1);
enemyfireTimer.addEventListener(TimerEvent.TIMER, handleenemyfireTimer, false, 0, true);
}
private function handleenemyfireTimer(e:TimerEvent) : void
{
//the timer runs, so a shot can be fired again
enemycanFire = true;
}
private function removeSelf2():void
{
removeEventListener(Event.ENTER_FRAME, loop2);
if (stage.contains(this))
{
stage.removeChild(this);
}
}
private function loop2 (e:Event):void
{
//vy += ay;
y += vy;
if (y > stage.stageHeight)
{
removeSelf2();
}
if (y >= target.y && (enemycanFire))
{
fireWeapon();
enemycanFire = false;
enemyfireTimer.start();
}
if (this.x > 540 || this.x < 10)
{
removeSelf2();
}
}
private function fireWeapon():void
{
stage.addChild(new EnemyShot(target, x, y, -4));
stage.addChild(new EnemyShot(target, x, y, +4));
}
}
}
Enemy Shot
package
{
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.events.Event;
import flash.display.Stage;
/**
* ...
* #author D Nelson
*/
[Embed(source="../assets/enemyshot.png")]
public class EnemyShot extends Bitmap
{
private var speed:Number;
private var target:HeroShip;
private var vx:Number;
public function EnemyShot(target:HeroShip, x:Number, y:Number, vx:Number)
{
this.target = target;
this.x = x;
this.y = y;
this.vx = vx;
scaleX = 0.3;
scaleY = 0.3;
addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
}
private function loop(e:Event) : void
{
x += vx;
if (x >= 600 || x <= -50)
{
removeSelf();
}
}
private function removeSelf():void
{
removeEventListener(Event.ENTER_FRAME, loop);
if (stage.contains(this))
{
stage.removeChild(this);
}
}
}
}
Also provided is the main class, in case that is of any help.
package
{
import flash.display.Sprite;
import flash.display.MovieClip;
import flash.display.DisplayObject;
import flash.display.Stage;
import flash.events.*;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.KeyboardEvent;
import flash.events.TimerEvent;
import flash.ui.Mouse;
import flash.utils.*;
import flash.utils.ByteArray;
import flash.utils.Timer;
import flash.text.*;
import flash.media.Sound;
import flash.media.SoundChannel;
/**
* ...
* #author D Nelson
*/
public class Main extends MovieClip
{
[Embed(source = "snd/gamemusic2.mp3")]
private var MySound : Class;
private var mainsound : Sound;
private var shootsound: Sound = new ShootSound;
private var sndChannel:SoundChannel = new SoundChannel;
public var heroShip:HeroShip;
public var enemyShip1:EnemyShip1
public var enemyShip2:EnemyShip2
public var enemyShip3:EnemyShip3
public var enemyShip4:EnemyShip4
public var bossShip: BossShip
public var enemyShot: EnemyShot;
public var playerShot: PlayerShot;
private var background1:Background1;
private var background2:Background2;
public static const scrollspeed:Number = 2;
public var playerShotArray:Array = new Array()
public var enemyShotArray:Array = new Array()
public var enemies1Array:Array = new Array()
public var enemies2Array:Array = new Array()
private var fireTimer:Timer; //this creates a delay between each shot
private var canFire:Boolean = true; //this checks if a shot can be fired
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
mainsound = (new MySound) as Sound;
shootsound = (new ShootSound) as Sound;
mainsound.play();
background1 = new Background1();
background2 = new Background2();
//setting the backgrounds one below another
background1.y = 0;
background2.y = background1.height;
//add background at the lowest depth level
stage.addChildAt(background1,0);
stage.addChildAt(background2, 0);
//sets up the timer and its listener
fireTimer = new Timer(250, 1);
fireTimer.addEventListener(TimerEvent.TIMER, handlefireTimer, false, 0, true);
stage.addEventListener(KeyboardEvent.KEY_DOWN, handleCharacterShoot);
//background scrolling effect
stage.addEventListener(Event.ENTER_FRAME, backgroundScroll);
//loop for enemy1 variety
stage.addEventListener(Event.ENTER_FRAME, loop1, false, 0, true);
//loop for enemy2 variety
stage.addEventListener(Event.ENTER_FRAME, loop2, false, 0, true);
//speed of player shots
setInterval(playerShootMovement, 10);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
//entry point
heroShip = new HeroShip();
stage.addChild(heroShip);
//test values for enemies
/*enemyShip1 = new EnemyShip1();
stage.addChildAt(enemyShip1, 2);
enemyShip1.y = stage.stageWidth * 0.3;
enemyShip1.x = 300;
enemyShip2 = new EnemyShip2();
stage.addChildAt(enemyShip2, 2);
enemyShip2.y = stage.stageWidth * 0.3;
enemyShip2.x = 200;
enemyShip3 = new EnemyShip3();
stage.addChildAt(enemyShip3, 2);
enemyShip3.y = stage.stageWidth * 0.3;
enemyShip3.x = 100;
enemyShip4 = new EnemyShip4();
stage.addChildAt(enemyShip4, 2);
enemyShip4.y = stage.stageWidth * 0.3;
enemyShip4.x = 400;
bossShip = new BossShip();
stage.addChildAt(bossShip, 1);
bossShip.y = 10;
bossShip.x = 130;*/
Mouse.hide();
}
private function handlefireTimer(e:TimerEvent) : void
{
//the timer runs, so a shot can be fired again
canFire = true;
}
public function playerShoot():void
{
//if canFire is true, allow a shot to be fired, then set canFire to false and start the timer again
//else, do nothing
if (canFire)
{
//Add new line to the array
playerShotArray.push(playerShot = new PlayerShot);
//Spawn missile in ship
playerShot.y = heroShip.y;
playerShot.x = heroShip.x+11;
addChild(playerShot);
canFire = false;
fireTimer.start();
sndChannel = shootsound.play();
}
}
public function playerShootMovement():void
{
//code adapted from the Pie Throw tutorial
for (var i:int = 0; i < playerShotArray.length; i++)
{
playerShotArray[i].y -= 10;
if (playerShotArray[i].y == 850)
{
playerShotArray.shift();
}
}
}
public function handleCharacterShoot(e:KeyboardEvent):void
{
/**
* SpaceBar = 32
*/
if (e.keyCode == 32)
{
playerShoot();
}
}
public function backgroundScroll (evt:Event):void
{
background1.y += scrollspeed;
background2.y += scrollspeed;
if (background1.y >= stage.stageHeight)
{
//the background is below the visible stage area, put it above the other background
background1.y = background2.y - background2.height;
}
else if (background2.y >= stage.stageHeight)
{
background2.y = background1.y - background2.height;
}
}
//EnemyShip 1 spawning
private function loop1(e:Event):void
{
//generates probability for the ship to spawn (lower number to increase odds, decrease it to decrease odds)
if (Math.floor(Math.random() * 55) == 5)
{
var enemyShip1:EnemyShip1 = new EnemyShip1(heroShip);
enemyShip1.addEventListener(Event.REMOVED_FROM_STAGE, removeEnemy1, false, 0, true);
enemies1Array.push(enemyShip1);
stage.addChild(enemyShip1);
}
}
private function removeEnemy1(e:Event):void
{
//removes the enemy that most recently left the screen from the array
enemies1Array.splice(enemies1Array.indexOf(e.currentTarget), 1);
}
//EnemyShip2 spawning
private function loop2(e:Event):void
{
if (Math.floor(Math.random() * 40) == 5)
{
var enemyShip2:EnemyShip2 = new EnemyShip2(heroShip);
enemyShip2.addEventListener(Event.REMOVED_FROM_STAGE, removeEnemy2, false, 0, true);
enemies2Array.push(enemyShip2);
stage.addChild(enemyShip2);
}
}
private function removeEnemy2(e:Event):void
{
enemies2Array.splice(enemies2Array.indexOf(e.currentTarget), 1);
}
}
}
Initially I thought it was to do with enemies firing shots while being too far to either side of the screen, but that doesn't seem to be the case. Any help would be greatly appreciated.
You need to make sure you remove the ENTER_FRAME listeners whenever you remove the object.
Actually, my advise is to not add ENTER_FRAME handlers throughout your game objects. This gets hard to manage and leads to bugs like you've encountered. Instead, add a single ENTER_FRAME as your core game loop, and update objects by calling an update() function on a list of game objects you maintain in your main game class. When you remove an object you simply won't call update() anymore. It also becomes easy to pause the game by just removing the ENTER_FRAME handler.
For example, here's a pattern I like to use for simple games:
interface IGameObject {
update():void;
}
class Enemy extends Sprite implements IGameObject {
public update():void {
// move, fire, etc
}
}
class Player extends Sprite implements IGameObject {
public update():void {
// move, fire, etc
}
}
class Bullet extends Bitmap implements IGameObject {
public update():void {
// move, collide, etc
}
}
class Main extends Sprite {
private objects:Vector.<IGameObject> = new <IGameObject>[];
public start():void {
addEventListener(Event.ENTER_FRAME, update);
}
public stopGame():void {
removeEventListener(Event.ENTER_FRAME, update);
}
private function update(e:Event):void {
for each (var object:IGameObject in objects) {
object.update();
}
}
public addObject(object:IGameObject):void {
objects.push(object);
addChild(object as DisplayObject);
}
public removeObject(object:IGameObject):void {
objects.splice(objects.indexOf(object), 1);
removeChild(object as DisplayObject);
}
}
Add and remove objects using addObject and removeObject. You can invoke them through a reference to Main or through event handlers you dispatch from your objects.

AS3 How to call a button from another class?

I'm trying to create a button that when you click on it, the ship fires a laser, but the button isn't working. I mean I didn't get any error when debugging, however it won't allow me to click on the button, but instead it allows me to click on my ship to fire. Any help is greatly appreciated, thanks!
My Fire.as
package control {
import flash.events.Event;
import flash.display.Stage;
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.events.Event;
import objects.Ship;
public class Fire extends MovieClip {
private var my_x: Number;
private var my_y: Number;
private var ourShip: Ship;
var mouseDown: Boolean;
public function Fire(margin_left: Number, margin_bottom: Number, ourShip_mc: Ship) {
my_x = margin_left;
my_y = margin_bottom;
ourShip = ourShip_mc;
if (stage) {
init();
} else {
addEventListener(Event.ADDED_TO_STAGE, init);
}
}
private function init(e: Event = null): void {
if (hasEventListener(Event.ADDED_TO_STAGE)) {
removeEventListener(Event.ADDED_TO_STAGE, init);
}
this.x = my_x + this.width / 2;
this.y = stage.stageHeight - my_y - this.height / 2;
this.addEventListener(MouseEvent.CLICK, onClick);
}
private function onClick(event: MouseEvent): void {
//EVENT DISPATCHER
dispatchEvent(new Event("eventshoot", true));
trace("Fire clicked");
}
}
}
My Ship.as
package objects {
import flash.display.MovieClip;
import flash.display.Stage;
import flash.ui.Keyboard;
import flash.ui.Mouse;
import flash.utils.Timer;
import flash.display.JointStyle;
import control.Controller;
import control.Joystick;
import control.Fire;
public class Ship extends MovieClip {
var mouseDown: Boolean;
private var stageRef: Stage;
private var key: Controller;
private var speed: Number = 2.5;
private var vx: Number = 0;
private var vy: Number = 0;
private var friction: Number = 0.93;
private var maxspeed: Number = 8;
//fire related variables
private var fireTimer: Timer; //causes delay between fires
private var canFire: Boolean = true; //can you fire a laser
public var move_left: Boolean = false;
public var move_up: Boolean = false;
public var move_right: Boolean = false;
public var move_down: Boolean = false;
public function Ship(stageRef: Stage): void {
this.stageRef = stageRef;
key = new Controller(stageRef);
this.addEventListener(Event.ENTER_FRAME, ShipMove);
stage.addEventListener("eventshoot", firenow);
//setup your fireTimer and attach a listener to it.
fireTimer = new Timer(250, 1);
fireTimer.addEventListener(TimerEvent.TIMER, fireTimerHandler, false, 0, true);
addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
}
private function ShipMove(event: Event): void {
if (move_left)
vx -= speed;
else if (move_right)
vx += speed;
else
vx *= friction;
if (move_up)
vy -= speed;
else if (move_down)
vy += speed;
else
vy *= friction;
}
public function firenow(event: Event) {
fireLaser();
}
public function loop(e: Event): void {
//update position
x += vx;
y += vy;
//speed adjustment
if (vx > maxspeed)
vx = maxspeed;
else if (vx < -maxspeed)
vx = -maxspeed;
if (vy > maxspeed)
vy = maxspeed;
else if (vy < -maxspeed)
vy = -maxspeed;
//ship appearance
rotation = vx;
scaleX = (maxspeed - Math.abs(vx)) / (maxspeed * 4) + 0.75;
//stay inside screen
if (x > stageRef.stageWidth - 30) {
x = stageRef.stageWidth - 30;
vx = -vx;
} else if (x < 30) {
x = 30;
vx = -vx;
}
if (y > stageRef.stageHeight) {
y = stageRef.stageHeight;
vy = -vy;
} else if (y < 0) {
y = 0;
vy = -vy;
}
}
private function fireLaser(): void {
//if canFire is true, fire a laser
//set canFire to false and start our timer
//else do nothing.
if (canFire) {
stageRef.addChild(new LaserGreen(stageRef, x + vx, y - 10));
canFire = false;
fireTimer.start();
}
}
//HANDLERS
private function fireTimerHandler(e: TimerEvent): void {
//Timer ran, fire again.
canFire = true;
}
public function takeHit(): void {
dispatchEvent(new Event("hit"));
}
}
}
Updated, here's the Engine.as, sorry for not replying it to your comment below, the structure is messed up if I do so.
package objects {
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
import flash.display.StageScaleMode;
import flash.display.StageAlign;
import control.Joystick;
import control.Fire;
public class Engine extends MovieClip {
private var preloader: ThePreloader;
public function Engine() {
preloader = new ThePreloader(474, this.loaderInfo);
stage.addChild(preloader);
preloader.addEventListener("loadComplete", loadAssets);
preloader.addEventListener("preloaderFinished", showSponsors);
stage.addEventListener("gameSTART", fGameStart);
}
private function loadAssets(e: Event): void {
this.play();
}
private function showSponsors(e: Event): void {
stage.removeChild(preloader);
var ps: PrerollSponsors = new PrerollSponsors(stage);
ps.addEventListener("prerollComplete", showMenu);
ps.preroll();
}
private function showMenu(e: Event): void {
new MainMenu(stage).load();
}
public static var enemyList: Array = new Array();
private var ourShip: Ship;
private var joystick: Joystick;
private var fire: Fire;
private var scoreHUD: ScoreHUD;
public function fGameStart(evt: Event): void {
ourShip = new Ship(stage);
ourShip.x = stage.stageWidth / 2;
ourShip.y = stage.stageHeight / 2;
ourShip.addEventListener("hit", shipHit, false, 0, true);
stage.addChild(ourShip);
joystick = new Joystick(120, 70, ourShip);
addChild(joystick);
fire = new Fire(420, 70, ourShip);
addChild(fire);
scoreHUD = new ScoreHUD(stage);
stage.addChild(scoreHUD);
addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
}
private function loop(e: Event): void {
if (Math.floor(Math.random() * 20) == 5) {
var enemy: E1 = new E1(stage, ourShip);
enemy.addEventListener(Event.REMOVED_FROM_STAGE, removeEnemy, false, 0, true);
enemy.addEventListener("killed", enemyKilled, false, 0, true);
enemyList.push(enemy);
stage.addChild(enemy);
} else if (Math.floor(Math.random() * 80) == 5) {
var enemy2: E2 = new E2(stage, ourShip);
enemy2.addEventListener(Event.REMOVED_FROM_STAGE, removeEnemy, false, 0, true);
enemy2.addEventListener("killed", enemyKilled, false, 0, true);
enemyList.push(enemy2);
stage.addChild(enemy2);
}
}
private function enemyKilled(e: Event) {
scoreHUD.updateKills(1);
scoreHUD.updateScore(e.currentTarget.points);
}
private function removeEnemy(e: Event) {
enemyList.splice(enemyList.indexOf(e.currentTarget), 1);
}
private function shipHit(e: Event) {
scoreHUD.updateHits(1);
}
}
}
So every time the fire button (Fire.as) is clicked, it dispatched an event "eventshoot", and the ship (Ship.as) pick it up. And when the ship receive it, the ship itself fires a laser, that's the idea. But since there are prerolls, menus...stuff like that will loaded before starting the game, I can't just simply drag the fire button to the stage. The engine will load the ship, fire button, enemy, score... to the stage when game started. And I got a error 1009 when debugging "TypeError: Error #1009: Cannot access a property or method of a null object reference.", it is from:
stage.addEventListener("eventshoot", fire now);
in Ship.as
I understand that I'm getting this error because there is no fire button on stage, so my ship can't pickup the "eventshoot" event, is there a way I can make the ship only pickup that event after making sure the button is loaded to the stage to avoid the error?
While you certainly can create a class for your button, the functionality to make the ship object fire a laser should not be in the button.
Given a Ship class that looks like this:
package
{
import flash.display.MovieClip;
public class Ship extends MovieClip
{
public function fireLaser():void
{
trace("pew pew");
}
}
}
You can instantiate this class and add it to your main timeline with this code:
var ship:Ship = new Ship();
addChild(ship);
If you placed the symbol by hand you do not need to do this and instead only need to give it an instance name of ship.
To make something clickable, add an event listener to it. For example, to make the ship itself clickable:
var ship:Ship = new Ship();
addChild(ship);
ship.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
function onMouseDown(event:MouseEvent):void
{
trace("ship clicked");
}
If you have a button on the main time line with an instance name of fire, you can as easily add the listener to that button:
var ship:Ship = new Ship();
addChild(ship);
// v---this changed
fire.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
function onMouseDown(event:MouseEvent):void
{
trace("fire button clicked");
}
Last but not least, if you want to call a method on an object instead of using trace(), you can do that, too:
var ship:Ship = new Ship();
addChild(ship);
fire.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
function onMouseDown(event:MouseEvent):void
{
ship.fireLaser(); // this changed, laser fired "pew pew"
}
tl, dr;
The button itself shouldn't do anything. If the button did anything with the ship directly, it would have to know the ship. The button shouldn't know the ship. All the button does is say "I got clicked" by dispatching an event, everything else should be handled outside.
You know, just like when you wrote your question here, it's not the entire internet (including me) sitting in the keyboard buttons of your computer listening to your input. All your keyboard buttons did was saying "I got clicked". Everything else got handled outside, by your operating system, browser, etc.

how to reference a hit test object between .as files [AS3]

i'm trying to make a top down shooter game, and have been following tutorials here: http://gamedev.michaeljameswilliams.com/2008/09/17/avoider-game-tutorial-1/
and here: as3gametuts.com/2013/07/10/top-down-rpg-shooter-4-shooting/
i've managed to get shooting and movement, but i need to get a hit test object to register when the bullet (defined in its own seperate as class file) and the enemy (also defined in seperate file) come into contact. code below:
Enemy code:
package
{
import flash.display.MovieClip;
public class Enemy extends MovieClip
{
public function Enemy()
{
x = 100;
y = -15;
}
public function moveDownABit():void
{
y = y + 3;
}
}
}
Bullet code:
package
{
import flash.display.Stage;
import flash.display.MovieClip;
import flash.events.Event;
import flash.utils.Timer;
import flash.events.TimerEvent;
public class Bullet extends MovieClip
{
private var stageRef:Stage;
private var speed:Number = 10;
private var xVel:Number = 0;
private var yVel:Number = 0;
private var rotationInRadians = 0;
public var enemy:Enemy;
public function Bullet(stageRef:Stage, X:int, Y:int, rotationInDegrees:Number):void
{
this.stageRef = stageRef;
this.x = X;
this.y = Y;
this.rotation = rotationInDegrees;
this.rotationInRadians = rotationInDegrees * Math.PI / 180;
}
public function bullethit():void{
if (Bullet.hitTestObject(enemy)){
gameTimer.stop();
}
}
public function loop():void
{
xVel = Math.cos(rotationInRadians) * speed;
yVel = Math.sin(rotationInRadians) * speed;
x += xVel;
y += yVel;
if(x > stageRef.stageWidth || x < 0 || y > stageRef.stageHeight || y < 0)
{
this.parent.removeChild(this);
}
}
}
}
Main.as document class code:
package
{
import flash.display.Stage;
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.utils.Timer;
import flash.events.TimerEvent;
public class Main extends MovieClip
{
public var player:Player;
public var bulletList:Array = []; //new array for the bullets
public var enemy:Enemy;
public var gameTimer:Timer;
public function Main():void
{
player = new Player(stage, 320, 240);
stage.addChild(player);
enemy = new Enemy();
addChild( enemy );
gameTimer = new Timer( 25 );
gameTimer.addEventListener( TimerEvent.TIMER, moveEnemy );
gameTimer.start();
stage.addEventListener(MouseEvent.CLICK, shootBullet, false, 0, true);
stage.addEventListener(Event.ENTER_FRAME, loop, false, 0, true); //add an EventListener for the loop
}
public function moveEnemy( timerEvent:TimerEvent ):void
{
enemy.moveDownABit();
}
public function loop(e:Event):void //create the loop function
{
if(bulletList.length > 0) //if there are any bullets in the bullet list
{
for(var i:int = bulletList.length-1; i >= 0; i--) //for each one
{
bulletList[i].loop(); //call its loop() function
}
}
}
public function shootBullet(e:MouseEvent):void
{
var bullet:Bullet = new Bullet(stage, player.x, player.y, player.rotation);
bullet.addEventListener(Event.REMOVED_FROM_STAGE, bulletRemoved, false, 0, true); //triggers the "bulletRemoved()" function whenever this bullet is removed from the stage
bulletList.push(bullet); //add this bullet to the bulletList array
stage.addChild(bullet);
}
public function bulletRemoved(e:Event):void
{
e.currentTarget.removeEventListener(Event.REMOVED_FROM_STAGE, bulletRemoved); //remove the event listener so we don't get any errors
bulletList.splice(bulletList.indexOf(e.currentTarget),1); //remove this bullet from the bulletList array
}
}
}
As Vesper said, you'll want to do your checks in the Main class. You've already got a game loop set up, so you can just add the check in there:
public function loop(e:Event):void //create the loop function
{
if(bulletList.length > 0) //if there are any bullets in the bullet list
{
for(var i:int = bulletList.length-1; i >= 0; i--) //for each one
{
bulletList[i].loop(); //call its loop() function
// check to see if the enemy has been hit
if(enemy.hitTestObject(bulletList[i]))
{
// the enemy has been hit by the bullet at index i
}
}
}
}
Since you currently only have a single enemy, you're just testing each bullet against that one enemy. If you had more enemies, you'd want to keep an array of references to those enemies and do a nested loop, checking to see if any of the enemies were hit by any of the bullets.

Multitouch as3 problems

I have a little problem with multitouch in as3.
Recently started a project in connection with my student practice.
This is to be a small game for android.
Are used to control two virtual joysticks and that's the problem with them.
As long as I use one of them or the other separately all works very well. However, when I try to use two at the same time, one of them is blocked and the object which moves begin to move in a random way and I can not control it.
Here is my code:
joystick.as:
package com.controls {
import flash.events.TouchEvent;
import flash.ui.Multitouch;
import flash.ui.MultitouchInputMode;
import flash.display.Sprite;
import flash.events.Event;
import flash.display.MovieClip;
import flash.geom.Rectangle;
import com.controls.JoystickKnob;
import com.Hero;
import com.Fire;
import com.greensock.*;
import com.greensock.easing.*;
public class Joystick extends MovieClip {
Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
private var my_x:Number;
private var my_y:Number;
private var knob:JoystickKnob;
private var hero:Hero;
private var fire:Fire;
private var knob_tween:TweenLite;
public function Joystick(margin_left, margin_bottom, hero_mc) {
var circle:Sprite = new Sprite();
circle.graphics.beginFill(0x696969);
circle.graphics.drawCircle(50, 50, 60);
circle.graphics.endFill();
addChild(circle);
my_x = margin_left;
my_y = margin_bottom;
hero = hero_mc;
if (stage) {
init();
} else {
addEventListener(Event.ADDED_TO_STAGE,init);
}
}
private function init(e:Event = null):void {
if (hasEventListener(Event.ADDED_TO_STAGE)) {
removeEventListener(Event.ADDED_TO_STAGE,init);
}
this.x = my_x + this.width / 2;
this.y = stage.stageHeight - my_y - this.height / 2;
knob = new JoystickKnob();
knob.x = 0;
knob.y = 0;
knob.origin_x = 0;
knob.origin_y = 0;
addChild(knob);
this.addEventListener(TouchEvent.TOUCH_BEGIN, onTouchBegin, true);
knob.addEventListener(TouchEvent.TOUCH_MOVE, onTouchMove, true);
stage.addEventListener(TouchEvent.TOUCH_END, onTouchEnd, true);
knob.buttonMode = true;
}
private function onTouchBegin(event:TouchEvent):void {
knob.x = this.mouseX;
knob.y = this.mouseY;
onTouchMove(null);
}
private function onTouchMove(event:TouchEvent):void {
if (knob_tween) {
knob_tween.kill();
}
this.addEventListener(Event.ENTER_FRAME, knobMoved);
knob.startDrag(false, new Rectangle( - this.width /2, - this.height /2, this.width, this.height));
}
private function knobMoved(event:Event):void {
// LEFT OR RIGHT
if (knob.x > 15) {
hero.move_right = true;
hero.move_left = false;
} else if (knob.x < -15) {
hero.move_right = false;
hero.move_left = true;
} else {
hero.move_right = false;
hero.move_left = false;
}
// UP OR DOWN
if (knob.y > 15) {
hero.move_down = true;
hero.move_up = false;
} else if (knob.y < -15) {
hero.move_down = false;
hero.move_up = true;
} else {
hero.move_down = false;
hero.move_up = false;
}
}
private function onTouchEnd(event:TouchEvent):void {
knob.stopDrag();
hero.move_left = false;
hero.move_up = false;
hero.move_right = false;
hero.move_down = false;
if (this.hasEventListener(Event.ENTER_FRAME)) {
this.removeEventListener(Event.ENTER_FRAME, knobMoved);
}
mover();
}
private function mover():void {
knob_tween = new TweenLite(knob, 0.5, {x: knob.origin_x, y:knob.origin_y, ease:Bounce.easeOut});
}
}
}
joystickKnob.as:
package com.controls {
import flash.display.Sprite;
import flash.display.MovieClip;
public class JoystickKnob extends MovieClip {
private var _origin_x:Number;
private var _origin_y:Number;
private var knob:Class;
public function JoystickKnob() {
var circle:Sprite = new Sprite();
circle.graphics.beginFill(0x32CD32);
circle.graphics.drawCircle(50, 50, 35);
circle.graphics.endFill();
addChild(circle);
}
public function get origin_x():Number {
return _origin_x;
}
public function set origin_x(o_x:Number):void {
_origin_x = o_x;
}
public function get origin_y():Number {
return _origin_x;
}
public function set origin_y(o_y:Number):void {
_origin_y = o_y;
}
}
}
Second joystick code looks the same, except that it is stored in files joystick2.as, joystickKnob2.as.
This is the main class of my program:
package com {
import flash.events.TouchEvent;
import flash.display.MovieClip;
import flash.events.MouseEvent;
import com.controls.Joystick;
import com.controls.Joystick2;
import com.Hero;
import com.Fire;
public class MyApp extends MovieClip {
private var joystick:Joystick;
private var hero:Hero;
private var joystick2:Joystick2;
private var fire:Fire;
public function MyApp() {
hero = new Hero();
hero.x = stage.stageWidth/1.7;
hero.y = stage.stageHeight/1.7;
addChild(hero);
fire = new Fire();
fire.x = stage.stageWidth/1.7;
fire.y = stage.stageHeight/1.7;
addChild(fire);
joystick = new Joystick(-350, 100, hero);
addChild(joystick);
joystick2 = new Joystick2(600, 100, fire);
addChild(joystick2);
}
}
}
When using the two joysticks at the same time, the problem also occurs with the knob graphics - instead move in a specific area, one of them almost always moves to the other end of the screen, near the area of the second joystick.
Has anyone of you already encountered such a problem and knows how to remedy it?
Best regards and thank you in advance for your help
Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT; does not support multiple fingers as written in de documentation:
For user interaction with multiple points of contact (such as several fingers moving across a touch screen at the same time) use the related GestureEvent, PressAndTapGestureEvent, and TransformGestureEvent classes. And, use the properties and methods of these classes to construct event handlers that respond to the user touching the device.
so you want Multitouch.inputMode = MultitouchInputMode.GESTURE;
This post shows up when searching for info on touch_point so I want to make sure it's correct.
TOUCH_POINT does support multiple touch points. Each touch is unique and given a unique id. You can have 100s of simultaneous touches.
TOUCH_POINT is what you use if you want to code your own touch handlers. GESTURE is TOUCH_POINTs handled for you. GESTURE decides if the touchpoints are swiping, tapping, etc. TOUCH_POINT is like a mouse event - the raw event.

A loop in enterframe?

I'm animating a bunch of words in AS3. Because I'm going to be using this on a mobile device, I want to use bitmaps rather than Sprites. So I've created WordObjects, which have a .bitmap property that I can access.
I have the following code, which fires on the click event and loops through an array inside an enterframe event. This is probably a bad idea, but I'm not sure how to do it better. (What is surprising is that it runs just fine in Flashbuilder, but slows to a crawl in Flash CS5.)
Is there some better way to do this? I just want an efficient way to animate the array of bitmaps.
private function clickhandler (e:MouseEvent){
this.addEventListener(Event.ENTER_FRAME, blowemup);
}
private function blowemup(e:Event){
var newPosition:Number;
for(var i:int=0; i<arrWordObjects.length; i++)
{
newPosition = updatePosition(arrWordObjects[i].bitmap);
arrWordObjects[i].bitmap.x += newPosition;
arrWordObjects[i].bitmap.y += getRandomNumber();
}
}
Something that will make a huge difference is using for each(Object in Array) rather than the standard for loop.
private function blowemup(e:Event):void
{
var newPosition:Number;
var i:ArrWordsObjectClass; // <-- don't know what the class for this is, just replace
for each(i in arrWordObjects)
{
newPosition = updatePosition(i.bitmap);
i.bitmap.x += newPosition;
i.bitmap.y += getRandomNumber();
}
}
A for each loop is typed, meaning a lot of time is saved where normally it'd be trying to work out what arrWordObjects[i] is every iteration.
Also, side note: using one ENTER_FRAME driven function and looping through everything in your application that you want to handle each frame is much more efficient than applying hundreds of listeners for objects.
I normally create a handler class that contains the ENTER_FRAME and an array storing my objects, like so:
package
{
import flash.events.Event;
import flash.display.Sprite;
public class Handler extends Sprite
{
// vars
public var elements:Array = [];
/**
* Constructor
*/
public function Handler()
{
addEventListener(Event.ENTER_FRAME, _handle);
}
/**
* Called on each dispatch of Event.ENTER_FRAME
*/
private function _handle(e:Event):void
{
var i:Element;
for each(i in elements)
{
i.step();
}
}
}
}
Then I create a base class for all the objects that I want to handle, containing the step() function called above.
package
{
import flash.display.DisplayObject;
public class Element extends Object
{
// vars
public var skin:DisplayObject;
/**
* Called on each dispatch of Event.ENTER_FRAME at Handler
*/
public function step():void
{
// override me
}
}
}
Now just extend Element with your objects:
package
{
import flash.display.Sprite;
public class MyThing extends Element
{
/**
* Constructor
*/
public function MyThing()
{
skin = new Sprite();
skin.graphics.beginFill(0);
skin.graphics.drawCircle(0,0,40);
skin.graphics.endFill();
}
/**
* Override step
*/
override public function step():void
{
skin.x += 4;
}
}
}
And get it all going!:
var handler:Handler = new Handler();
var m:MyThing;
var i:uint = 0;
for(i; i<10; i++)
{
m = new MyThing();
m.y = Math.random()*stage.stageHeight;
handler.elements.push(m);
addChild(m.skin);
}
How many bitmaps do you plan to have on the stage at a time?
I have had 40 900x16px bitmaps animating on the stage at full speed running on my iphone using air 2.6.
I used a foreach loop in an enterframe event which i added on mouseclick and removed once the animation was finished.
Remember to compile it for the mobile with gpu rendering enabled. (gpu in your app.xml if you are using air 2.6)
This is worth a read too, it explains a lot about performance for mobile devices
http://help.adobe.com/en_US/as3/mobile/WS901d38e593cd1bac-3d719af412b2b394529-8000.html
Here is a basic example of what I had...
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
[SWF(frameRate="30", backgroundColor="#FF00FF")]
public class Test extends Sprite
{
private var fields:Vector.<Bitmap> = new Vector.<Bitmap>();
public function Test()
{
this.stage.scaleMode = StageScaleMode.NO_SCALE;
this.stage.align = StageAlign.TOP_LEFT;
for(var i:int = 0; i< 37; i++){
var bd:BitmapData = new BitmapData(960, 16, true, 0x000000);
bd.fillRect(new Rectangle(0, 0, 900, 16), Math.round( Math.random()*0xFFFFFFFF ));
var b:Bitmap = new Bitmap(bd);
b.x = 0;
b.y = i*16;
stage.addChild(b);
fields.push(b);
}
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
}
private var inertia:Boolean = false;
private var yCurrent:Number;
private var ySpeed:Number;
private var startY:Number;
private var cy:Number = 0;
private function onEnterFrame(e:Event):void{
if(!inertia){
ySpeed = (startY - yCurrent) ; // / 16;
startY = yCurrent
} else {
ySpeed *= 0.8;
if(ySpeed < 0.01 && ySpeed > -0.01){
inertia = false;
stage.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
}
}
cy += ySpeed;
if(cy > 640)
cy -= 640;
var ty:Number = cy;
for each(var tf:Bitmap in fields){
tf.y = ty;
ty += 16;
if(ty > 640)
ty -= 640;
}
}
private function onMouseDown(e:MouseEvent):void{
inertia = false;
startY = e.stageY;
yCurrent = e.stageY;
stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
private function onMouseMove(e:MouseEvent):void{
yCurrent = e.stageY;
}
private function onMouseUp(e:Event):void{
inertia = true;
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
}
}
I would suggest looking at writing a custom effect on Adobe's website over registering for ENTER_FRAME event. What you've put up there means this code will forever run as long as the program is running. If you wanted to stop the effect or run for 10 frames and stop then you'll have to write more code. It gets even more complex if you want to apply this to several instances. You're going to have to resolve problems that custom effects framework solves.
I'd read how to write custom effects here:
http://livedocs.adobe.com/flex/3/html/help.html?content=createeffects_1.html