Drag and Drop allow only one object per target - actionscript-3

I'm sure this is simple but I have the drag and drop code that works wonderfully...however, it allows me to drop muliple objects per target. What I want is to allow only one object per target and I've looked and can't seem to find how to test to see if an object had already been dropt. Here's the code I have:
package com {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.geom.Point;
import com.greensock.*;
public class DraggableObject extends MovieClip
{
protected var origPos:Point;
public function DraggableObject()
{
origPos = new Point(x, y);
buttonMode = true; // changes cursor to hand
addEventListener(MouseEvent.MOUSE_DOWN, down);
}
protected function down (event:MouseEvent): void
{
parent.addChild(this); // adds object to the top of the display list to keep the object on top
startDrag(); // built in Flash method
stage.addEventListener(MouseEvent.MOUSE_UP, stageUp);
var upPosX = x + 5;
var upPosY = y - 5;
TweenMax.to(this, .2, {dropShadowFilter:{color:0x666666, alpha:1, blurX:12, blurY:12, distance:12}});
TweenLite.to(this, .2, {x: upPosX, y: upPosY});
mouseEnabled = false;
}
protected function stageUp (event:MouseEvent): void
{
stage.removeEventListener(MouseEvent.MOUSE_UP, stageUp); // good coding
stopDrag(); // built in Flash method
TweenMax.to(this, .5, {dropShadowFilter:{color:0x666666, alpha:0, blurX:12, blurY:12, distance:12}});
if (dropTarget.parent.name == "root1"){
returnToOrigPos();
}
else if(dropTarget)// dropTarget is a built in property this means if dropTarget != null
{
trace("dropt on "+dropTarget.parent.name);
x = dropTarget.parent.x;
y = dropTarget.parent.y;
//buttonMode = false; //only use these to keep the user's selection...can't change if these are active.
//removeEventListener(MouseEvent.MOUSE_DOWN, down);
}
mouseEnabled = true;
}
protected function returnToOrigPos(): void
{
x = origPos.x;
y = origPos.y;
}
}
I was thinking that hitTestObject or dropTarget would work but can't figure out how to use them.
Thanks for any help!
Mike

Add a public property (eg: _hasDroppedObject) to whatever kind of object you're dropping objects into and set it to true when an object is dropped into it:
dropTarget.hasDroppedObject = true;
Then change your "if (dropTarget.parent.name == "root1")" line to:
if (dropTarget.parent.name == "root1" || dropTarget.hasDroppedObject) {
(This assumes you have access to the drop-receiving Class).

Related

Test collision between two movie clips in two different classes AS3

I need to test the collision between 2 movie clips, using air for android action script 3.
Its a collision between an object and several obstacles.
My structure is the following :
The base FLA file, is linked to Action Script file called baseCode.as.
In this AS file, i create the obsctacles, using the following code :
baseCode.as :
package {
import flash.display.MovieClip;
import flash.events.Event;
import flash.geom.Point;
import Mc_MC; // Not strictly needed
public class baseCode extends flash.display.MovieClip
{
//private static var SYMBOLS:Array = new Array(MySymbol1, MySymbol2);
public var t:int = 0;
public function baseCode()
{
// Create five symbols:
for (var i:int = 0; i < 5; i++) {
trace(i);
makeSymbol();
}
}
function randomRange(minNum:Number, maxNum:Number):Number
{
return (Math.floor(Math.random() * (maxNum - minNum + 1)) + minNum);
}
public function makeSymbol():void
{
trace("IT entered makeSymbol");
// Pick a random symbol from the array:
// var symType:Class = SYMBOLS[0];
//trace(SYMBOLS[Math.random() * SYMBOLS.length]);
// Construct the new symbol:
//var Positi : Number = new Number(Math.random);
var loc:Point = new Point(randomRange(100,stage.stage.height),0);
var loc2:Point = new Point(randomRange(110,stage.stage.height),0);
//var loc:Point = new Point(10*randomRange(15, stage.width),100*randomRange(10 , stage.width));
trace("this is the starting point" , loc);
var sym:Mc_MC = new Mc_MC(1 + Math.random() *10, loc);
if( t % 2 == 0 ){
var sym2:Mc_MC2 = new Mc_MC2(15 + Math.random() *10, loc);
// Listen for the object hitting the left edge:
//sym2.addEventListener(Event.COMPLETE, remakeObject);
this.addChild(sym2);
}
sym.addEventListener(Event.COMPLETE, remakeObject);
this.addChild(sym);
t ++;
}
public function remakeObject(e:Event):void
{
e.target.removeEventListener(Event.COMPLETE, remakeObject);
//e.removeChild(sym);
//e.parent.removeChild(this.child);
// removeChild(this);
// this.removeChild(sym);
// Replace the dead symbol:
makeSymbol();
}
}
}
Mc_MC and Mc_MC2 are two Action Script file in which the obstacles are called :
Mc_MC.as :
package {
import flash.display.MovieClip;
import flash.events.Event;
import flash.geom.*;
import flash.display.Screen;
import flash.system.Capabilities;
public class Mc_MC extends MovieClip
{
public var speed:Number; // Pixels moved per frame
var valuee:baseCode;
public function Mc_MC(speed:Number, startPosition:Point)
{
this.speed = speed;
this.addEventListener(Event.ENTER_FRAME, update);
this.x = startPosition.x;
this.y = startPosition.y;
}
public function update(speed:Number)
{
var screenWidth:Number = Capabilities.screenResolutionX;
var screenHeight:Number = Capabilities.screenResolutionY;
trace("this.y" , this.y);
trace("this is the stage height" , screenHeight);
trace("this.speed" , this.speed);
if (this.y >= screenHeight - 100) { // We're at the left edge
trace("Entered if");
trace("new Starting Pos" , this.y);
this.y = stage.height;
parent.removeChild(this);
this.removeEventListener(Event.ENTER_FRAME, update);
this.dispatchEvent(new Event(Event.COMPLETE));
}
else this.y += this.speed;
}
}
}
In the base FLA file, i create the main object that will collide with all the obstacles created using Mc_MC and Mc_MC2. I create it using the following code :
Home.fla
import flash.events.*
//import flash.events.EventDispatcher.addEventListener()
import flash.display.DisplayObject;
//import flash.events.MouseEvent;
import flashx.textLayout.events.UpdateCompleteEvent;
import flash.display.MovieClip;
var offsetX:Number;
var offsetY:Number;
//var draggedObject:DisplayObject;
var my_obj:OriginalObject = new OriginalObject();
//left.addEventListener(MouseEvent.MOUSE_MOVE, drag);
//The speed of the scroll movement.
var scrollSpeed:uint = 2;
//This adds two instances of the movie clip onto the stage.
var s1:ScrollBg = new ScrollBg();
var s2:ScrollBg = new ScrollBg();
left.addEventListener(MouseEvent.MOUSE_DOWN,mouseDown);
function mouseDown(e:MouseEvent):void {
stage.addEventListener(MouseEvent.MOUSE_UP,mouseUp); //listen for mouse up on the stage, in case the finger/mouse moved off of the button accidentally when they release.
addEventListener(Event.ENTER_FRAME,myButtonClick); //while the mouse is down, run the tick function once every frame as per the project frame rate
}
function mouseUp(e:MouseEvent):void {
removeEventListener(Event.ENTER_FRAME,myButtonClick); //stop running the tick function every frame now that the mouse is up
stage.removeEventListener(MouseEvent.MOUSE_UP,mouseUp); //remove the listener for mouse up
}
right.addEventListener(MouseEvent.MOUSE_DOWN,mouseDown2);
function mouseDown2(e:MouseEvent):void {
stage.addEventListener(MouseEvent.MOUSE_UP,mouseUp2); //listen for mouse up on the stage, in case the finger/mouse moved off of the button accidentally when they release.
addEventListener(Event.ENTER_FRAME,stopDragging); //while the mouse is down, run the tick function once every frame as per the project frame rate
}
function mouseUp2(e:MouseEvent):void {
removeEventListener(Event.ENTER_FRAME,stopDragging); //stop running the tick function every frame now that the mouse is up
stage.removeEventListener(MouseEvent.MOUSE_UP,mouseUp2); //remove the listener for mouse up
}
my_obj.x = stage.width / 2;
my_obj.y = stage.height - (stage.height / 3 );
stage.addChild(my_obj);
function myButtonClick(ev:Event):void
{
trace("UPPP");
if(my_obj.x > (my_obj.width*2)){
my_obj.x = my_obj.x - 10;
trace("In the limit");
}
else {
trace("out of bounds");
}
trace("myButton has been clicked.");
}
//// This function is called when the mouse button is released.
function stopDragging(ev2:Event):void
{
trace("Down");
if(my_obj.x <= right.x){
my_obj.x = my_obj.x + 10;
}
}
How can I test the collision of the moving Mc_MC / Mc_MC2 with my_obj considering that the come from different AS files?
I am new to AS, so any help would be appreciated!
If you want to be able to collision test objects from different classes / parentage, then you need to set up your project in a way that you can gain a reference to said objects.
From the looks of it, your two objects are actually in the same class already (as your main timeline code and document class code share the same scope, so what you declare in one should be available in the other).
The only thing you are missing, is a top-level reference to your obstacle/Mc_MC. As currently you assign it to a var that is scoped to the makeSymbol function (so you only have a reference to it inside that function).
Solution
In your document class (baseCode.as) , create a top-level var to hold a reference to that obstacle: (for reference, you have a top level var called t, put this line above or below that)
private var obstacle:Mc_MC;
later in your function that instantiates the new Mc_MC (makeSymbol), assign the instance to that top level var:
obstacle = new Mc_MC(1 + Math.random() *10, loc);
addChild(obstacle);
Now you can access that obstacle var anywhere else in the main timeline or document class.
if(my_obj.hitTest(obstacle)){
}
As an aside, if you have a document class, there is no point in having code on the first frame of your main timeline as that code would work the same in your document class (though it has to be contained in a function). Here is an example of where to move main timeline code:
public class Main extends MovieClip {
public function Main():void {
//listen for the added to stage event prior to do anything display oriented
this.addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event):void {
//this is the best place to put the equivalent of timeline code (not including vars or functions, put those in the class root NOT nested here)
}
}

How can I solve this error in Flash game?

I have a problem in Flash puzzle game. If I create the game in the first frame of my timeline it's working, but if the game has been created (for example) in 5th frame it does'nt work!
It send me this error:
TypeError: Error #1009: Cannot access a property or method of a null
object reference.
at Map() TypeError: Error #2007: Parameter hitTestObject must be non-null.
at flash.display::DisplayObject/_hitTest()
at flash.display::DisplayObject/hitTestObject()
at DragDrop/drop()
dragdrop class
package
{
import flash.display.*;
import flash.events.*;
public class DragDrop extends Sprite
{
var origX:Number;
var origY:Number;
var target:DisplayObject ;
public function DragDrop()
{
// constructor code
origX = x;
origY = y;
addEventListener(MouseEvent.MOUSE_DOWN, drag);
buttonMode = true;
}
function drag(evt:MouseEvent):void
{
stage.addEventListener(MouseEvent.MOUSE_UP, drop);
startDrag();
parent.addChild(this);
}
function drop(evt:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_UP, drop);
stopDrag();
if(hitTestObject(target))
{
visible = false;
target.alpha = 1;
Object(parent).match();
}
x = origX;
y = origY;
}
}
}
I think the problem is in var target! and I don't know how to solve it.
Map.as
enter code here package
{
import flash.display.*;
import flash.events.*;
public class Map extends MovieClip
{
var dragdrops:Array;
public function Map()
{
// constructor code
dragdrops = [tt1];
var currentObject:DragDrop;
for(var i:uint = 0; i < dragdrops.length; i++)
{
currentObject = dragdrops[i];
currentObject.target = getChildByName(currentObject.name + "_target");
}
}
public function match():void
{
}
}
}
Edit:
There are multiple problems with the code. Too many to list, I'm afraid, but the biggest one is:
You're declaring a map, and trying to add your object to it, before your object exists. It doesn't exist until frame 5, so this won't work. I've re-written the code below, but honestly, there is so much wrong with the code that it's just not possible to fix without re-writing significant portions of it.
package
{
import flash.display.*;
import flash.events.*;
public class Map extends MovieClip
{
var dragdrops:Array;
public function Map()
{
// constructor code
dragdrops = new Array();
}
public function addElement(gamepiece:DragDrop):void {
dragdrops.push(gamepiece);
}
public function addChildElements():void {
var currentObject:Object;
for(var i:uint = 0; i < dragdrops.length; i++)
{
currentObject = dragdrops[i];
currentObject.test();
currentObject.target = (currentObject.name + "_target"); // this should work now, but doesn't. Why?
currentObject.target.test();
}
}
public function match():void
{
}
}
}
Then, on frame one, I added:
var map:Map = new Map();
Then, on frame five, I added:
map.addElement(tt1);
map.addChildElements();
This got tt1 added to map, at least, but that's as far as I got. Your problem now is;
currentObject.target = (currentObject.name + "_target");
It's the correct name, now, but it won't add it to target. That's as much as I can do.
It's because your hitTestObject method isn't correctly invoked. This method must be invoked in a Display Object instance to test if another instance of a Display Object hits it:
if (myDisplayObject.hitTestObject(anotherDisplayObject))
{
// do stuff
}
Adobe help about hitTestObject method.
Edit
So you should write you class like that:
package
{
import flash.display.*;
import flash.events.*;
public class DragDrop extends Sprite
{
var origX:Number;
var origY:Number;
var target:DisplayObject;
public function DragDrop()
{
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event):void {
origX = x;
origY = y;
stage.addEventListener(MouseEvent.MOUSE_DOWN, drag);
buttonMode = true;
}
private function drag(evt:MouseEvent):void
{
stage.addEventListener(MouseEvent.MOUSE_UP, drop);
startDrag();
parent.addChild(this);
}
private function drop(evt:MouseEvent):void
{
target = (evt.target as DisplayObject);
stage.removeEventListener(MouseEvent.MOUSE_UP, drop);
stopDrag();
if(target.hitTestObject(target))
{
visible = false;
target.alpha = 1;
Object(parent).match();
}
x = origX;
y = origY;
}
}
}
Remark
You shouldn't call your variable target, because its the name of a Flash native variable. Rename it targ for example.

AS3 MouseEvent.CLICK Interaction on Different Indexes

I'm currently working through a AS3 game tutorial on Lynda.com and am coming across a problem with the MouseEvent.CLICK and child indexes. The game is a simple point and shoot, where the player must shoot all of the approaching enemies before they get too close. It works initially, however the custom cursor I added displays behind the enemies. However when I try and adjust the index (I've used the addChildAt function and moving the addChild(cursor) line of code below the enemy container initializer) the on click interaction, which is supposed to remove the enemy when clicked on, doesn't work.
My document class:
package {
import flash.display.*;
import flash.utils.*;
import flash.events.*;
import flash.ui.*;
public class Game extends MovieClip {
public var cursor:Cursor;
public var enemy:Enemy;
public var numberOfEnemies:uint;
public var enemyContainer:MovieClip;
public var enemyTimer:Timer;
public function Game() {
addEventListener(Event.ADDED_TO_STAGE, init);
Mouse.hide();
}
public function init(event:Event):void {
cursor = new Cursor;
addChild(cursor);
stage.addEventListener(MouseEvent.MOUSE_MOVE, dragCursor);
numberOfEnemies = 10;
enemyTimer = new Timer(1000, numberOfEnemies);
enemyContainer = new MovieClip();
addChild(enemyContainer);
enemyTimer.addEventListener(TimerEvent.TIMER, createEnemies);
enemyTimer.start();
}
public function dragCursor(event:MouseEvent) {
cursor.x = this.mouseX;
cursor.y = this.mouseY;
}
public function createEnemies(event:TimerEvent):void {
enemy = new Enemy();
enemy.x = 25 + Math.random() * (stage.stageWidth - 75);
enemy.y = 25 + Math.random() * (stage.stageHeight - 75);
enemyContainer.addChild(enemy);
enemy.timerStart();
}
}
}
My enemy class:
package {
import flash.display.MovieClip;
import flash.utils.Timer;
import flash.ui.Mouse;
import flash.events.*;
public class Enemy extends MovieClip {
public var scaleObj:Number = 0.50;
public var growTimer:Timer;
public function Enemy() {
scaleX = scaleObj;
scaleY = scaleObj;
addEventListener(MouseEvent.CLICK, shootEnemy);
}
public function timerStart() {
growTimer = new Timer(50);
growTimer.addEventListener(TimerEvent.TIMER, objectGrow);
growTimer.start();
}
public function objectGrow(event:TimerEvent):void {
if(scaleObj <= 1.0) {
scaleObj += 0.01;
scaleX = scaleObj;
scaleY = scaleObj;
}
else {
killEnemy();
}
}
public function killEnemy():void {
this.parent.removeChild(this);
growTimer.stop();
}
public function shootEnemy(event:MouseEvent):void {
killEnemy();
}
}
}
There also is a cursor class, however there is no code beyond the package and class definers. Please let me know of any questions or comments you might have, thanks.
Most likely the Cursor object is intercepting the mouse click since it is above the Enemy object.
You can stop the Cursor from intercepting mouse events by setting in the cursor class:
this.mouseEnabled = false;
this.mouseChildren = false;
Also, you should ideally be using a native mouse cursor instead of manually creating your own. Check out this Adobe tutorial for an example.
Set your Cursor instance to not receive mouse events itself as it would block the click events from getting to the objects behind it. Code would be something like
cursor = new Cursor;
cursor.mouseEnabled = false;
cursor.mouseChildren = false;

Actionscript: Am I deleting this class instance correctly?

Okay by deleting correctly I mean am I actually getting rid of the instance or is it just not being drawn anymore? I should mention that I'm trying to delete the instance from within its own class, that is it deletes itself. It 'works' in that the square it draws no longer appears on the screen but again I'm not sure if it's really gone or just not being drawn. Anyway here's the class:
package
{
import flash.display.*;
import flash.events.*;
public class OBJECT_bullet_1 extends Sprite
{
public var X:int = 0; public var Y:int = 0;
public var Y_SPEED:int = 5;
public var DEPTH:int = 9;
public var CONTAINER:Sprite = new Sprite();
public function CREATE(CONTAINER:Sprite,X:int,Y:int):void
{
this.CONTAINER = CONTAINER;
CONTAINER.stage.addEventListener(Event.ENTER_FRAME,STEP);
this.X = X; this.Y = Y;
DRAW();
}
public function STEP(event:Event):void
{
this.graphics.clear();
Y -= Y_SPEED;
if (Y < 20) {Y = 300; CONTAINER.removeChild(this); CONTAINER.stage.removeEventListener(Event.ENTER_FRAME,STEP); CONTAINER.(delete this); CONTAINER = null; return;}
DRAW();
}
public function DRAW():void
{
this.graphics.beginFill(0xCCCC00,1);
this.graphics.drawRect(X - 2,Y - 2,4,4);
this.graphics.endFill();
CONTAINER.addChild(this);
}
}
}
The part I'm concerned about is in the STEP function when it checks to see if Y < 20. You'll notice that it does several things afterwords. Am I deleting it correctly? If so is there anything I am doing to delete it that I don't need to?
Yes to both questions. To ensure an object is deleted, all you have to do is remove all references to it. The child reference and event callback are the only ones the above code is aware of, and you have taken care to remove them both. Nullifying your own container reference is unnecessary, as is whatever you think CONTAINER.(delete this) does.
There are some other significant problems with your supplied code. I made some improvements and heavily commented all changes to explain why I made them.
// You should avoid using the default package. Using the default package
// can make it difficult later on if you start having naming conflicts.
package com.stackoverflow.example {
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
import flash.utils.getTimer;
// Class names are spelled in CamelCase by convention. Also, note
// that "Object" has a special meaning in AS3 so you should avoid
// using it to refer to anything else. I used here "Entity" instead.
public class EntityBullet1 extends Sprite {
// ALLCAPS when used are reserved for static const names.
// A good use of static consts is to store "magic numbers".
public static const DEFAULT_COLOR:uint = 0xCCCC00;
public static const DEFAULT_SPEED_X:Number = 0;
public static const DEFAULT_SPEED_Y:Number = -100;
public static const DEFAULT_SIZE:Number = 4;
// I'm calculating the time between frames for smoother movement.
public var lastTime:int;
public var color:uint = DEFAULT_COLOR;
public var size:int = DEFAULT_SIZE;
// Instead of separate x and y vars, you can use the Point class.
public var pos:Point;
public var speed:Point;
// Instead of a "create" method do all creation inside the constructor!
public function EntityBullet1(x:Number = 0, y:Number = 0) {
pos = new Point(x, y);
speed = new Point(DEFAULT_SPEED_X, DEFAULT_SPEED_Y);
// You don't need the parent container to access the ENTER_FRAME
// event. Every DisplayObject has its own. Much simpler.
addEventListener(Event.ENTER_FRAME, firstStep);
}
public function draw():void {
// Keep all drawing inside the draw function. Previously,
// clear() was being called inside the step method.
graphics.clear();
graphics.beginFill(color);
graphics.drawRect(pos.x - size/2, pos.y - size/2, size, size);
graphics.endFill();
}
// On the first frame, the field "lastTime" is still uninitialized.
// This method initializes it to the current time and hands off
// future events to the proper step() method.
public function firstStep(event:Event):void {
removeEventListener(Event.ENTER_FRAME, firstStep);
addEventListener(Event.ENTER_FRAME, step);
lastTime = getTimer();
step(event);
}
public function step(event:Event):void {
// To move at a fixed rate regardless of how fast the framerate is,
// you need to calculate the time delta.
var cur:int = getTimer();
var delta:Number = (cur - lastTime) / 1000.0;
lastTime = cur;
// Position equals velocity times time.
pos.x += speed.x * delta;
pos.y += speed.y * delta;
draw();
// Note that all DisplayObjects already have references to their
// parent containers called "parent"!
if (pos.y < 20) {
if (parent != null) parent.removeChild(this);
removeEventListener(Event.ENTER_FRAME, step);
}
}
}
}

What is the most effective way to test for combined keyboard arrow direction in ActionScript 3.0?

I need to monitor the direction a user is indicating using the four directional arrow keys on a keyboard in ActionScript 3.0 and I want to know the most efficient and effective way to do this.
I've got several ideas of how to do it, and I'm not sure which would be best. I've found that when tracking Keyboard.KEY_DOWN events, the event repeats as long as the key is down, so the event function is repeated as well. This broke the method I had originally chosen to use, and the methods I've been able to think of require a lot of comparison operators.
The best way I've been able to think of would be to use bitwise operators on a uint variable. Here's what I'm thinking
var _direction:uint = 0x0; // The Current Direction
That's the current direction variable. In the Keyboard.KEY_DOWN event handler I'll have it check what key is down, and use a bitwise AND operation to see if it's already toggled on, and if it's not, I'll add it in using basic addition. So, up would be 0x1 and down would be 0x2 and both up and down would be 0x3, for example. It would look something like this:
private function keyDownHandler(e:KeyboardEvent):void
{
switch(e.keyCode)
{
case Keyboard.UP:
if(!(_direction & 0x1)) _direction += 0x1;
break;
case Keyboard.DOWN:
if(!(_direction & 0x2)) _direction += 0x2;
break;
// And So On...
}
}
The keyUpHandler wouldn't need the if operation since it only triggers once when the key goes up, instead of repeating. I'll be able to test the current direction by using a switch statement labeled with numbers from 0 to 15 for the sixteen possible combinations. That should work, but it doesn't seem terribly elegant to me, given all of the if statements in the repeating keyDown event handler, and the huge switch.
private function checkDirection():void
{
switch(_direction)
{
case 0:
// Center
break;
case 1:
// Up
break;
case 2:
// Down
break;
case 3:
// Up and Down
break;
case 4:
// Left
break;
// And So On...
}
}
Is there a better way to do this?
You can keep track of whether each key is down or not by listening for all KEY_DOWN and KEY_UP events, and storing each key state in an array. I wrote a class a while ago to do just that (included at the end of my answer).
Then you are no longer tied to the event model to know if a certain key is down or not; you can periodically check every frame (or every timer interval). So you could have a function like:
function enterFrameCallback(e:Event):void
{
var speed:Number = 1.0; // net pixels per frame movement
thing.x += (
-(int)Input.isKeyDown(Keyboard.LEFT)
+(int)Input.isKeyDown(Keyboard.RIGHT)
) * speed;
thing.y += (
-(int)Input.isKeyDown(Keyboard.UP)
+(int)Input.isKeyDown(Keyboard.DOWN)
) * speed;
}
which would take into account all possible combinations of arrow key presses. If you want the net displacement to be constant (e.g. when going right and down at same time, the object moves X pixels diagonally, as opposed to X pixels in both horizontal and vertical directions), the code becomes:
function enterFrameCallback(e:Event):void
{
var speed:Number = 1.0; // net pixels per frame movement
var displacement:Point = new Point();
displacement.x = (
-(int)Input.isKeyDown(Keyboard.LEFT)
+(int)Input.isKeyDown(Keyboard.RIGHT)
);
displacement.y = (
-(int)Input.isKeyDown(Keyboard.UP)
+(int)Input.isKeyDown(Keyboard.DOWN)
);
displacement.normalize(speed);
thing.x += displacement.x;
thing.y += displacement.y;
}
Here is the Input class I wrote (don't forget to call init from the document class). Note that it also keeps track of mouse stuff; you can delete that if you don't need it:
/*******************************************************************************
* DESCRIPTION: Defines a simple input class that allows the programmer to
* determine at any instant whether a specific key is down or not,
* or if the mouse button is down or not (and where the cursor
* is respective to a certain DisplayObject).
* USAGE: Call init once before using any other methods, and pass a reference to
* the stage. Use the public methods commented below to query input states.
*******************************************************************************/
package
{
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.display.Stage;
import flash.geom.Point;
import flash.display.DisplayObject;
public class Input
{
private static var keyState:Array = new Array();
private static var _mouseDown:Boolean = false;
private static var mouseLoc:Point = new Point();
private static var mouseDownLoc:Point = new Point();
// Call before any other functions in this class:
public static function init(stage:Stage):void
{
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown, false, 10);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp, false, 10);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown, false, 10);
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp, false, 10);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove, false, 10);
}
// Call to query whether a certain keyboard key is down.
// For a non-printable key: Input.isKeyDown(Keyboard.KEY)
// For a letter (case insensitive): Input.isKeyDown('A')
public static function isKeyDown(key:*):Boolean
{
if (typeof key == "string") {
key = key.toUpperCase().charCodeAt(0);
}
return keyState[key];
}
// Property that is true if the mouse is down, false otherwise:
public static function get mouseDown():Boolean
{
return _mouseDown;
}
// Gets the current coordinates of the mouse with respect to a certain DisplayObject.
// Leaving out the DisplayObject paramter will return the mouse location with respect
// to the stage (global coordinates):
public static function getMouseLoc(respectiveTo:DisplayObject = null):Point
{
if (respectiveTo == null) {
return mouseLoc.clone();
}
return respectiveTo.globalToLocal(mouseLoc);
}
// Gets the coordinates where the mouse was when it was last down or up, with respect
// to a certain DisplayObject. Leaving out the DisplayObject paramter will return the
// location with respect to the stage (global coordinates):
public static function getMouseDownLoc(respectiveTo:DisplayObject = null):Point
{
if (respectiveTo == null) {
return mouseDownLoc.clone();
}
return respectiveTo.globalToLocal(mouseDownLoc);
}
// Resets the state of the keyboard and mouse:
public static function reset():void
{
for (var i:String in keyState) {
keyState[i] = false;
}
_mouseDown = false;
mouseLoc = new Point();
mouseDownLoc = new Point();
}
///// PRIVATE METHODS BEWLOW /////
private static function onMouseDown(e:MouseEvent):void
{
_mouseDown = true;
mouseDownLoc = new Point(e.stageX, e.stageY);
}
private static function onMouseUp(e:MouseEvent):void
{
_mouseDown = false;
mouseDownLoc = new Point(e.stageX, e.stageY);
}
private static function onMouseMove(e:MouseEvent):void
{
mouseLoc = new Point(e.stageX, e.stageY);
}
private static function onKeyDown(e:KeyboardEvent):void
{
keyState[e.keyCode] = true;
}
private static function onKeyUp(e:KeyboardEvent):void
{
keyState[e.keyCode] = false;
}
}
}