basically I have to use Processing for a project (not out of choice) and have come across a problem regarding the pressing of multiple keys at once. In the keyPressed()function, I have multiple conditionals, each mapping a key to an action. This is all well and good, but supposing I want to press multiple keys at once?
Any suggestions?
Thanks.
Create an array. Assign a numeric value to each key that you are checking, then turn on the corresponding value in the array on or off whenever a key is pressed or released. Then in the draw() you check the values of the array to see which are on and off at any given instant.
boolean[] keys = new boolean[4];
final int A = 0;
final int D = 1;
final int W = 2;
final int S = 3;
void setup() {
}
void draw() {
if (keys[D]) {
// do something;
}
if (keys[A]) {
// do something;
}
if (keys[W]) {
// do something;
}
if (keys[S]) {
// do something;
}
} // end of draw()
void keyPressed() {
int mybutton = key; // the ascii value of the key that was pushed
switch (mybutton) {
case 101:
keys[D] = true;
break;
case 97:
keys[A] = true;
break;
case 44:
keys[W] = true;
break;
case 111:
keys[S] = true;
break;
} // end switch
} // end keyPressed
void keyReleased() {
switch (mybutton) {
case 101:
keys[D] = false;
break;
case 97:
keys[A] = false;
break;
case 44:
keys[W] = false;
break;
case 111:
keys[S] = false;
break;
} // end switch
} // end keyReleased
You could use the keydown event and push every keys in a list (array, stack, etc.) then clear it on the keyreleased. The keydown is fired at the moment that the key gets down, so if you compare the list to the one you expect, it will do the trick. Something like :
var arrKeys = [];
function zone_keyDown(event) {
var evt = window.event || event;
arrKeys.push(evt.keyCode);
}
function zone_keyReleased(event) {
arrKeys = [];
}
Instead of using keyPressed() by itself, you could use keyPressed() and keyReleased() and keep track of what keys are down and only perform actions during keyReleased().
Related
I have an array with six buttons called "sostaletters" and an array with six elements called "addedletters". What I want is every time a button is been clicked a new movieclip from the array "addedletters" to be added on stage. For example if the third element of the array "sostaletters" is been clicked then the third element of the array "addedletters" to be added on stage. How i can do that?
This is what i have done for my array "sostaletters"
var sostaletters:Array = [a7,a17,a24,a1,a18]
for each (var letter:MovieClip in sostaletters) {
letter.buttonMode = true;
letter.isClicked = false;
letter.addEventListener(MouseEvent.CLICK, kanoklick2);
function kanoklick2(event:MouseEvent):void
{
event.target.alpha = 0.5;
if(event.currentTarget.isClicked == false){
clickCount2 ++;
event.currentTarget.isClicked = true;
sostaletters[i].x = positionsArray[i].xPos;
sostaletters[i].y = positionsArray[i].yPos;
setChildIndex(MovieClip(e.currentTarget), numChildren - 1);
}
if(clickCount2 == sostaletters.length){
addChild(welldoneman);
myTimer.start();
if (contains(kremmala)) {
removeChild(kremmala)
}
for (var i:int= 0; i< wrongletters.length; i++)
{
wrongletters[i].removeEventListener(MouseEvent.CLICK, kanoklick);
}
for (var o:int= 0; o< sostaletters.length; o++)
{
sostaletters[o].removeEventListener(MouseEvent.CLICK, kanoklick2);
}
trace("All buttons have been clicked");
}
}
}
Neal Davis has already gave a right answer. But I shall make a code review for more clarity. See my comments in the code.
var sostaletters:Array = [a7,a17,a24,a1,a18]
// "sostaletters" is an Array. So it is better to use iteration by index
for each (var letter:MovieClip in sostaletters) {
letter.buttonMode = true;
letter.isClicked = false;
letter.addEventListener(MouseEvent.CLICK, kanoklick2);
// You will create "kanoklick2" function at each iteration.
// Move this declaration from this loop
function kanoklick2(event:MouseEvent):void {
event.target.alpha = 0.5;
// There is no need to compare a boolean property with "false" or "true"
// You can write "if(event.currentTarget.isClicked)"
if(event.currentTarget.isClicked == false) {
clickCount2 ++;
event.currentTarget.isClicked = true;
// You declare an "i" variable below.
// So the compiler will not fire en error.
// But you will not get an index of the current letter.
sostaletters[i].x = positionsArray[i].xPos;
sostaletters[i].y = positionsArray[i].yPos;
setChildIndex(MovieClip(e.currentTarget), numChildren - 1);
}
// If number of "sostaletters" are not changed
// you can save its value in a variable for an example "sostalettersNum"
if (clickCount2 == sostaletters.length) {
addChild(welldoneman);
myTimer.start();
if (contains(kremmala)) {
removeChild(kremmala)
}
// You declare an "i" variable. And use it above to get a current letter.
// But an "i" will not contain its index.
// It will be equal to "wrongletters.length"
for (var i:int= 0; i
So I'm trying to make a spaceship fire lasers when the spacebar is pressed. I've done this before in a pure flex project but have recently gotten creative cloud and am trying to recreate the same effect using flash professional/flash builder.
Unfortunately when I create a new instance of my "Laser" class and try and put it on the stage with addChild() nothing seems to happen.
Here is the main file/document class
public class PlayerShip extends Sprite
{
private var laserTimer:Timer;
private var shipTime:Timer;
private var upKey:Boolean;
private var downKey:Boolean;
private var leftKey:Boolean;
private var rightKey:Boolean;
private var spacebar:Boolean;
private var utils:Utils = new Utils();
//tuning variables
private var MOVE_SPEED:int = 5;
private var REVERSE_SPEED:int = 3;
private var TURN_SPEED:int = 5;
private var laserEmitter:shipLasers = new shipLasers(stage);
public function PlayerShip():void
{
super();
addEventListener(Event.ENTER_FRAME, fly);
stage.addEventListener(KeyboardEvent.KEY_DOWN, movementKeysDown);
stage.addEventListener(KeyboardEvent.KEY_UP, movementKeysUp);
laserTimer = new Timer(1000/1000);
laserTimer.addEventListener(TimerEvent.TIMER, fireLasers);
laserTimer.start();
addChild(laserEmitter);
}
public function fly(e:Event):void {
if(downKey) {
SpaceShip.x -= Math.sin(utils.degreesToRadians(SpaceShip.rotation)) * REVERSE_SPEED;
SpaceShip.y += Math.cos(utils.degreesToRadians(SpaceShip.rotation)) * REVERSE_SPEED;
}
if(upKey) {
SpaceShip.x += Math.sin(utils.degreesToRadians(SpaceShip.rotation)) * MOVE_SPEED;
SpaceShip.y -= Math.cos(utils.degreesToRadians(SpaceShip.rotation)) * MOVE_SPEED;
}
if(leftKey) {
SpaceShip.rotation -= TURN_SPEED;
}
if(rightKey) {
SpaceShip.rotation += TURN_SPEED;
}
}
public function movementKeysUp(e:KeyboardEvent):void { //rotators is key_up :P
switch(e.keyCode) {
case 83:
downKey = false;
break;
case 65:
leftKey = false; // on "a" key_up sets left turn to false. Simple enough.
break;
case 68:
rightKey = false; // K. "d" released makes me not turn right.
break;
case 87:
upKey = false; // I guess case 87 is "w"
break;
case 32:
spacebar = false;
break;
}
}
public function movementKeysDown(e:KeyboardEvent):void { // key_down for movers
switch(e.keyCode) {
case 83:
downKey = true;
break;
case 65:
leftKey = true; //so now on key_down for the "a" key it makes me go left! :D
break;
case 68:
rightKey = true; //same as lft...
break;
case 87:
upKey = true;
break;
case 32:
spacebar = true;
break;
}
}
public function fireLasers(e:TimerEvent) {
if(spacebar) {
laserEmitter.Emit(SpaceShip.x, SpaceShip.y, SpaceShip.rotation);
addChild(laserEmitter);
}
}
public function getShip():MovieClip {
return SpaceShip;
}
}
}
and this is the separate class that is supposed to create new instances of the Laser class and put them on the stage.
public class shipLasers extends Sprite implements Emittable
{
var tempLaserRight:MovieClip;
var tempLaserLeft:MovieClip;
var laserArray:Array = [];
public function shipLasers(stage:Stage):void
{
}
public function Emit(x:int, y:int, rotation:Number):void {
tempLaserRight = new Laser();
tempLaserLeft = new Laser();
tempLaserRight.rotation = tempLaserLeft.rotation = rotation;
tempLaserRight.x = 200;
tempLaserLeft.x = 210;
tempLaserRight.y = 200;
tempLaserLeft.y = 200;
laserArray.push(tempLaserRight);
laserArray.push(tempLaserLeft);
stage.addChild(tempLaserRight);
stage.addChild(tempLaserLeft);
trace("Oh come on!");
}
}
}
Thanks!
You never store passed stage reference in shipLasers class, thus you are trying to refer its own built-in stage ref, which is likely null because your instances of shipLasers don't get added to display list themselves. You need to store the stage ref passed in the constructor and use that to add children.
public class shipLasers ... {
var theStage:Stage;
public function shipLasers(aStage:Stage){
theStage = aStage;
}
public function Emit(...) {
...
theStage.addChild(tempLaserRight);
thestage.addChild(tempLaserLeft);
}
}
Update: It's also a good practice to first check stage availability, then use stage reference. To do this, you need to listen to Event.ADDED_TO_STAGE event in your ship class, as it uses stage left right and center. The common code of achieving this is as follows:
public function PlayerShip() {
....
// leave here only code that does not require stage
if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE,init);
}
private function init(e:Event=null):void {
removeEventListener(Event.ADDED_TO_STAGE,init);
// here place all code that's left from your initialization process
laserEmitter=new shipLasers(stage);
stage.addEventListener(KeyboardEvent.KEY_DOWN, movementKeysDown);
stage.addEventListener(KeyboardEvent.KEY_UP, movementKeysUp);
// etc.
}
So I need to generate random number and use It few times, problem is that It generating on EnterFrame function.
This function generate number 0-5:
function pn():Number {
var pn:Number = Math.round(Math.random()*5);
return pn;
}
Here I check if character not jumping and stage.addEventListener(Event.ENTER_FRAME, jump);
if(!mainJumping){
mainJumping = true;
stage.addEventListener(Event.ENTER_FRAME, jump)
}
And here is jump function where I need to use random generated number:
function jump(e) {
if ( i < 50) {
i++;
item[1][pn()].y -=1
} else {
item[1][pn()].y += 1;
if (item[1][pn()].y == 0){
stage.removeEventListener(Event.ENTER_FRAME, jump)
}
}
Problem is that in jump function every time IF is checked It generates new number. I need to make that number will be generated and not changed till this part:
if (item[1][pn()].y == 0){
stage.removeEventListener(Event.ENTER_FRAME, jump)
}
After this It can generate new number. Have you ideas? Thank you.
// Create a variable higher up the scope chain to store the number
var random = 0;
// Assuming this is in a function at the same level as the jump function
//function() ???? {
if(!mainJumping){
mainJumping = true;
random = pn(); // Generate new random number when jump begins
stage.addEventListener(Event.ENTER_FRAME, jump)
}
//}
function jump(e) {
var item = item[1][random]; // Grab the random item
if ( i < 50) {
i++;
item.y -=1
} else {
item.y += 1;
if (item.y == 0){
stage.removeEventListener(Event.ENTER_FRAME, jump)
}
}
}
Create an instance variable called myRandomNumber. In the getter for it, if it is null, call pn() to populate it.
Something like this:
In your class, define the variable:
private var _myRandomNumber:int = 0;
And in your getter:
public function get myRandomNumber():int {
if (_myRandomNumber == 0) {
_myRandomNumber = pn();
}
return _myRandomNumber;
}
Now, when you call myRandomNumber, you'll get a "random number" :)
I'm trying to set up some default callbacks for listeners dynamically, and I'm not having much success at the moment.
Array:
var URLLoader_SharedCallbacks:Array = new Array();
URLLoader_SharedCallbacks.push(new Array (HTTPStatusEvent, HTTPStatusEvent.HTTP_STATUS, "URLLoader_HTTPStatus"));
URLLoader_SharedCallbacks.push(new Array (IOErrorEvent, IOErrorEvent.IO_ERROR, "URLLoader_IOError"));
URLLoader_SharedCallbacks.push(new Array (Event, Event.OPEN, "URLLoader_Open"));
And then:
function URLLoader_SharedCallbacks_Add(ul:URLLoader):void
{
for each(var arr:Array in this.URLLoader_SharedCallbacks)
{
var fnc:Function = function(evt:arr[0])
{
trace("evt = " + evt)
}
if(!this[ul].hasEventListener(arr[2]))
{
this[ul].addEventListener(fnc);
}
}
}
Suggestions?
even if you need to get anything very specific from this events imho the best choice is to the following:
private function handler(e:Event): void{
switch(e.type){
case IOErrorEvent.IO_ERROR:
//treat it like IOErrorEvent
break;
case Event.CLOSE:
//treat it like Event.CLOSE
break;
case HTTPStatusEvent.HTTP_STATUS:
//treat it like HTTPStatusEvent
break;
}
}
and about dynamical generation, if it's really the only solution:
create 2 arrays - for dispatchers and for listener functions. and one more to hold descriptions of dispatcher-listener pairs. or you may store dispatcher with listener and description in an Object and have an array of such objects, or maybe develop a specific data structure...
anyway:
private var funcArray: Array = new Array();
private var loaderArray: Array = new Array();
private var infoArray: Array = new Array();
private function createListener():Function {
var fn:Function = function(e:Event): void { switch((e as Event).type) { case IOErrorEvent.IO_ERROR: /*treat it like IOErrorEvent*/ break; case Event.CLOSE:/*treat it like Event.CLOSE*/ break; case HTTPStatusEvent.HTTP_STATUS: /*treat it like HTTPStatusEvent*/ break; }};
return fn;
}
private function createURLLoader(url: String, description: String = 'i never care'):void{
var urlo:URLLoader = new URLLoader();
var fun: Function = createListener();
urlo.addEventListener(IOErrorEvent.IO_ERROR, fun);
urlo.addEventListener(Event.CLOSE, fun);
urlo.addEventListener(HTTPStatusEvent.HTTP_STATUS, fun);
var info: Object = { 'url' : url , 'description' : description );
urlo.load(new URLRequest(info['url']))
funcArray.push(fun);
loaderArray.push(urlo);
infoArray.push(info);//mention that arrays have always equal length and dispatcher, listener and descrition are stored under the same index
}
/*when data is loaded/loading timeout is over/loading failed: we need to be careful
killing our anonimous vars: */
private function killEmAll(i:int):void{//i is the index for arrays
(loaderArray[i] as URLLoader).removeEventListener(IOErrorEvent.IO_ERROR, (funcArray[i] as Function));
(loaderArray[i] as URLLoader).removeEventListener(Event.CLOSE, (funcArray[i] as Function));
(loaderArray[i] as URLLoader).removeEventListener(HTTPStatusEvent.HTTP_STATUS, (funcArray[i] as Function));
(loaderArray[i] as URLLoader).close();//just to be sure ;)
loaderArray.splice(i, 1);
funcArray.splice(i, 1);
infoArray.splice(i, 1);
}
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;
}
}
}