Why doesn't my KEY_DOWN event fire? - actionscript-3

I am new to ActionScript and was following the tutorial here to get me started using FlashDevelop. I set it up to move the item every KEY_DOWN event instead of using the frame event but my KEY_DOWN event is never fired. I quickly added a MOUSE_DOWN event to test the extent of the problem which works.
I am adding the listeners as my Paddle (an extension of Sprite) item is added to the stage:
private function addedToStage(e:Event) : void {
// Remove this event listener
removeEventListener(Event.ADDED_TO_STAGE, addedToStage);
// Add the picture
addChild(pic);
// Set the location of the item in the middle of the screen one
// height length of the image down.
this.y = this.height;
this.x = (this.stage.stageWidth - this.width) / 2;
// Add the keyboard listener (broken).
stage.addEventListener(KeyboardEvent.KEY_DOWN, this.keyDownHandler, false, 0, true);
// Add the mouse listener (works).
stage.addEventListener(MouseEvent.MOUSE_DOWN, this.mouseDown, false, 0, true);
// Set the focus to the stage.
stage.focus = stage;
}
I have a breakpoint in my keyDownHandler function and it is never called.
This may be a duplicate of this question, but I am already doing what the answer in that question describes and in the comments he explains that he does not know what fixed the problem only that it suddenly started working.
Another popular answer to that question mentions this article which is why there is a line in my code to set the focus back to the stage. There is nothing else on my stage.
What could be happening?
Edit:
Here is my listener code:
private function keyDownHandler(e:KeyboardEvent):void {
var increment:Number; // Here is my breakpoint
if (e.keyCode == 65 || e.keyCode == 37) {
increment = -5;
} else if(e.keyCode == 68 || e.keyCode == 39) {
increment = 5;
}
var newX:Number = this.x + 5;
if (newX > 0 && newX + this.width < this.stage.width) {
this.x = newX;
}
}

The problem was unrelated to the event itself, but rather how I was testing to see if it fired. You cannot place breakpoints on declarations.
FlashDevelop will allow you to set a breakpoint there but the debugger will not actually stop at this point.
The code to move the paddle does not work (not surprising to me at this point), but if I move the breakpoint to the line in the if statement (or any other line when an actual action performed such as the trace) suddenly it breaks fine. Now that I think about it even the other languages I've worked with have this behavior, I'm just too pampered by IDE's that will move the breakpoint to the nearest available line in such situations.

Related

Need help fixing player jumping diagonally Flash CS4

I can't figure out why when I try and get my player to jump diagonally by pressing the right and up arrow key it does nothing, I've used trace to see if anything was happening and nothing was when they were used together.
Up won't work by itself or with right but when right is by itself it works.
player is named hero, platforms are named platform, platform2, platform3 etc
platforms are in the platform layer
stop();
var gravity:Number=5; //Very important, allows player to fall
var movex:Number=0; // Moving players X
// Moving player
stage.addEventListener(KeyboardEvent.KEY_DOWN, moveHero);
var speed=10;
function moveHero(event:KeyboardEvent) {
if (event.keyCode==Keyboard.LEFT) {
hero.x-=speed;
hero.play();
}
if (event.keyCode==Keyboard.RIGHT) {
hero.x+=speed;
hero.play();
}
}
hero.addEventListener(Event.ENTER_FRAME, testCollision2);
// Allowing player to jump when on platform
function testCollision2(e: Event) {
//Allowing player to jump when on platform continued
stage.addEventListener(KeyboardEvent.KEY_DOWN, moveHeroUP);
function moveHeroUP(event:KeyboardEvent) {
if (hero.hitTestObject(platform) && event.keyCode==Keyboard.UP) {
gravity=-50;
hero.y=hero.y+gravity;
} else if (hero.hitTestObject(platform2) && event.keyCode==Keyboard.UP) {
gravity=-50;
hero.y=hero.y+gravity;
} else if (hero.hitTestObject(platform4) && event.keyCode==Keyboard.UP) {
gravity=-50;
hero.y=hero.y+gravity;
}
if(hero.hitTestObject(platform) && event.keyCode==Keyboard.UP && event.keyCode==Keyboard.RIGHT){
movex = 20;
hero.x = hero.x + movex;
gravity =-50;
hero.y = hero.y + gravity;
}
}
I'm trying to get the player to jump in the code chunk at the very end
Flash CS4
AS3
To start, let's go through how you've set up this code and explain what's happening.
You have this line:
hero.addEventListener(Event.ENTER_FRAME, testCollision2);
Which is saying, every frame tick that hero exists, run the function testCollision2. Frame tick here doesn't relate to timeline frames, it relates to the frame rate of your application. So if that is set to 12, that function will run 12 times every second.
Inside testCollision2, you add another listener:
stage.addEventListener(KeyboardEvent.KEY_DOWN, moveHeroUP);
and create an inline function called moveHeroUP. So every frame tick, you create a new function, and attach it to a key down event. So (assuming 12 frames per second) 5 seconds into your application, you'll have 60 keyboard listeners all doing the same thing. This is also a memory leak (as you keep creating a new function every frame), so eventually your program will crash.
To get to the actual question, a keyboard event is tied to one specific key. This means the event's keyCode is only ever one key (the key that triggered the event). So doing something like (event.keyCode==Keyboard.UP && event.keyCode==Keyboard.RIGHT) will always be false because event.keyCode only ever holds one value.
A common approach to your situation, is to have one global key down and key up listener. Then use a dictionary to store which keys are currently down:
//create just one key down listener
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
//create a dictionary to store key presses
var keyboardDown:Dictionary = new Dictionary();
function keyDownHandler(e:KeyboardEvent):void {
keyboardDown[e.keyCode] = true;
}
function keyUpHandler(e:KeyboardEvent):void {
keyboardDown[e.keyCode] = false;
}
What you're doing here, is when a key down event fires, you set the value in the dictionary to true (with the keycode as the dictionary key), then on the key up event you set it to false.
Now, in your ENTER_FRAME handler, you use the dictionary values to check for key combinations:
hero.addEventListener(Event.ENTER_FRAME, moveHero);
function moveHero(event:Event) {
//DO ALL YOUR MOVEMENTS IN ONE ENTER FRAME FUNCTION
if (keyboardDown[Keyboard.LEFT]) {
hero.x-=speed;
hero.play();
}
if (keyboardDown[Keyboard.RIGHT]) {
hero.x+=speed;
hero.play();
}
if (hero.hitTestObject(platform) && keyboardDown[Keyboard.UP]) {
gravity=-50;
hero.y=hero.y+gravity;
} else if (hero.hitTestObject(platform2) && keyboardDown[Keyboard.UP]) {
gravity=-50;
hero.y=hero.y+gravity;
} else if (hero.hitTestObject(platform4) && keyboardDown[Keyboard.UP]) {
gravity=-50;
hero.y=hero.y+gravity;
}
if(hero.hitTestObject(platform) && keyboardDown[Keyboard.UP] && keyboardDown[Keyboard.RIGHT]){
movex = 20;
hero.x = hero.x + movex;
gravity =-50;
hero.y = hero.y + gravity;
}
}

AS3 - How do you call previous currentTarget from within a different event?

I have a dropdown menu that lets you select an item to be placed on the stage. The item is drag and droppable so I use event.currentTarget.startDrag(); to start the drag. Ok, everything works fine so far.
However, I also need to be able to rotate the item while it is being dragged (using the spacebar).
stage.addEventListener(KeyboardEvent.KEY_DOWN, myKeyDown);
function myKeyDown(e:KeyboardEvent):void{
if (e.keyCode == Keyboard.SPACE){
rotate++;
if (rotate == 5){
rotate = 1;
}
WHATGOESHERE?.gotoAndStop(rotate);
}
If I hardcode in an instance name of an object everything works fine - so the rotate function is working properly. The problem is, how can I reference event.currentTarget from the startDrag function while inside of the keyDown event?
My first thought was to set event.currentTarget to a variable and then calling the variable from within the keyDown event. However, targetHold = event.currentTarget; does not seem to record the instance name of the object being clicked...
public var targetHold:Object = new Object;
function ClickToDrag(event:MouseEvent):void {
event.currentTarget.startDrag();
targetHold = event.currentTarget;
trace ("targetHold " + targetHold);
stage.addEventListener(KeyboardEvent.KEY_DOWN, myKeyDown);
function myKeyDown(e:KeyboardEvent):void{
if (e.keyCode == Keyboard.SPACE){
rotate++;
if (rotate == 5){
rotate = 1;
}
targetHold.gotoAndStop(rotate); //does not work
}
}
function ReleaseToDrop(event:MouseEvent):void {
event.currentTarget.stopDrag();
}
As you click the object, it should have focus. If you register the listener for the KeyboardEvent on the object and not on the stage, it will be .currentTarget.
Here's an example of what I have in mind. Right after starting the drag, add the listener to the same object instead of the stage.
event.currentTarget.startDrag();
event.currentTarget.addEventListener(KeyboardEvent.KEY_DOWN, myKeyDown);
The proper way of doing this would be to define all the functionality in a class. Within a self contained class, you would not need any .currentTarget.
Here is how I would do this: (well, actually I'd follow #null's advice and encapsulate it in a sub class that all your dragable objects would extend, but that is a little broad so this will do)
public var targetHold:MovieClip; //don't make a new object, just create the empty var
public function YourConstructor(){
//your other constructor code
stage.addEventListener(KeyboardEvent.KEY_DOWN, myKeyDown); //don't add the listener in the click function
}
private function clickToDrag(event:MouseEvent):void {
if(targetHold) ReleaseToDrop(null); //safeguard in case flash lost the focus during the mouse up
targetHold = event.currentTarget as MovieClip; //assign the current target. Might as well cast it as MovieClip and get code completion benefits
targetHold.startDrag();
trace ("targetHold " + targetHold);
}
private function myKeyDown(e:KeyboardEvent):void{
//check if target hold exists
if (targetHold != null && e.keyCode == Keyboard.SPACE){
rotate++;
if (rotate == 5){
rotate = 1;
}
targetHold.gotoAndStop(rotate);
}
}
private function ReleaseToDrop(event:MouseEvent):void {
if(targetHold) targetHold.stopDrag();
targetHold = null;
}

Drag finger on X axis of mc, changes value - AS3

I have a rectangle mc. When the user swipes his finger slowly right on the mc, a value needs to increase, If moved left, it will decrease. 1 to 100 is the limit. How do I do that? i don't want a visible slider. It should not matter where the finger is on the mc, only which direction the finger is moving.
EDIT: I am currently looking into the touchEvent and am researching the web for solutions.
You'll want to keep track of whether or not a swipe is happening and, if so, where it started.
var dragging:Boolean = false;
var startX:Number = 0.0;
Then you'll use simple event listeners to keep track of this bool.
mc.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
mc.addEventListener(MouseEvent.MOUSE_UP, mouseReleased);
function mouseDown(event:MouseEvent):void
{
dragging = true;
startX = event.localX;
}
function mouseReleased(event:MouseEvent):void
{
dragging = false;
}
Then you're MOUSE_MOVE touch event can handle all the logic:
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove); // Notice this event is on stage, not mc.
function mouseMove(event.MouseEvent):void
{
value += event.localX - startX;
if (value < 0) value = 0;
if (value > 100) value = 100;
}
Happy Holidays!

AS3 remove tween before MOTION_FINISH via the movieclip

I've been fumbling with this issue for a bit. I've got a lovely little tooltip movieclip that follows the user's mouse for a few seconds before it removes itself. My problem is that if there is one already there I remove it, however, I cannot seem to remove the MOTION_FINISH event and it still fires and possibly deletes a new tooltip.
What I want is to essentially put in a line item such as var tween(smallhelp_panel).deleteAll();
I saw a tweenlight function killtweensof(mc); However I've used the tweens I've incorporated below throughout my 30k lines of AS3 code.
Here is my tooltip handler. I call it with a simple
Main_Warning("Please don't forget to save!",5);
My movieclip is a 'smallhelp_panel' and I check if it already exists and remove it. However, the alpha and MOTION_FINISH tweens still exist and cause issues with any new smallhelp_panels.
public function Main_Warning( the_text:String, myTimer:int = 4){
if(smallhelp_panel != null){
stage.removeChild( smallhelp_panel );
removeEventListener(Event.ENTER_FRAME, trackmouse);
smallhelp_panel = null;
}
smallhelp_panel = new small_help();
smallhelp_panel.name = "myWarning";
smallhelp_panel.x = mouseX - 50;
smallhelp_panel.y = mouseY + 15;
smallhelp_panel.helptext.text = the_text;
stage.addChild( smallhelp_panel );
addEventListener(Event.ENTER_FRAME, trackmouse);
var myTween:Tween;
myTween = new Tween(smallhelp_panel, "alpha", None.easeOut, 1, 0, myTimer, true);
tweenholder = myTween;
tweenArray.push(tweenholder);
myTween.addEventListener(TweenEvent.MOTION_FINISH, removeTween);
}
That is my Tooltip handler.
for reference purposes my tween remover is:
public function removeTween(e:TweenEvent = null):void{
e.target.removeEventListener(TweenEvent.MOTION_FINISH, removeTween);
if(smallhelp_panel != null){
removeEventListener(Event.ENTER_FRAME, trackmouse);
stage.removeChild( smallhelp_panel );
smallhelp_panel = null;
}
}
and my mouse tracker that moves the tooltip with the mouse is a simple:
public function trackmouse(e:Event):void{
smallhelp_panel.x = mouseX - 50;
smallhelp_panel.y = mouseY + 15;
}
That's because you've added your MOTION_FINISH event listener to the tween, not to the panel. You remove the panel, if one already exists, but the tween still exists in the tweenholder and tweenArray variables - and fires a MOTION_FINISH event, when its calculations are finished. Your event listener method doesn't know which tween the event came from, and correctly removes the help panel.
To fix this, either remove the tween and event listener along with the help panel in your Main_Warning function, or modify the removal block in your event listener method:
public function removeTween(e:TweenEvent = null):void{
e.target.removeEventListener(TweenEvent.MOTION_FINISH, removeTween);
// --- this will check if the Tween belongs to the panel on the stage!
if (smallhelp_panel && e.target.obj == smallhelp_panel ) {
// ---
removeEventListener(Event.ENTER_FRAME, trackmouse);
stage.removeChild( smallhelp_panel );
smallhelp_panel = null;
}
// --- NOW remove the tween from the array (all of them should be removed after use)
tweenArray.splice (tweenArray.indexOf (e.target), 1);
}
I don't understand exactly why you would need both a tweenholder and a tweenArray variable, though ;)
Your TweenEvent is still being listened for. You never remove the previous listener, so it will fire when the tween calculations are complete.
I assume tweenholder is declared somewhere global? (Like the other answer here, I'm confused as to your need of declaring a new tween, storing it in another reference and adding that reference to an array...) If so, try this:
public function Main_Warning( the_text:String, myTimer:int = 4){
tweenholder.removeEventListener(TweenEvent.MOTION_FINISH,removeTween);
if(smallhelp_panel != null){
...

AS3 Stop character from moving through walls

I want to stop the movieclips movement when it hits a wall (another movieclip).
The example below works, but after the collision the movieclip 'blocks' all movement to the left...
My question to you is, is this a good way and why isn't it working well?
There will be something wrong in this code, but i'm learning.
For now the example with the leftArrow key;
variables to check the key, if it's hitting the walls and if it's moving or not:
var leftArrow:Boolean;
var speed:int = 10;
var hitting:Boolean;
var ismoving:Boolean;
event listeners for the keys/movement and detecting collision:
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressed);
stage.addEventListener(KeyboardEvent.KEY_UP, keyReleased);
stage.addEventListener(Event.ENTER_FRAME, walking);
stage.addEventListener(Event.ENTER_FRAME, detectHit);
detecting collision function:
function detectHit(e:Event) :void
{
if(char.hitTestObject(bounds))
{
hitting = true;
}
}
function to the left arrow key:
function keyPressed(event:KeyboardEvent):void
{
if (event.keyCode == Keyboard.LEFT)
{
leftArrow = true;
}
}
function keyReleased(event:KeyboardEvent):void
{
if (event.keyCode == Keyboard.LEFT)
{
leftArrow = false;
}
}
And the reason it's not working is probably here, but I don't understand why not:
function walking(event:Event):void {
if (rightArrow) {
char.x += speed;
}
if (leftArrow && ! hitting) {
char.x -= speed;
}
else
{
ismoving = false
}
if (leftArrow && ! hitting)
char will move if hitting is false. When char.hitTestObject(bounds) is true you are setting hitting to true. You are not setting hitting again to false anywhere. That's why once left wall is hit it stops left movement permanently. You need to figure out suitable condition to set hitting to false again.
Adding an else branch in detectHit should solve the problem.
function detectHit(e:Event):void {
if(char.hitTestObject(bounds))
{
hitting = true;
} else {
hitting = false; // add this
}
}
Allthough Taskinoor's method should work, I would suggest another way to do your hittests.
Since you probably are creating a game (character and bounds), you will have more than one bound. In that case, I would strongly suggest bitmap-hittesting. This way, you can create all your bounds in one movieclip and test for a hit.
I will explain this by using the example of a maze. The maze would then be some lines in a movieclip, randomly put together. If you use HitTestObject and you aren't hitting one of the lines, but your character is over the movieclip, hitTestObject will return true, even though you are not hitting a wall. By using bitmapHitTesting, you can overcome this problem (BitmapHitTest takes transparant pixels into account, whereas hitTestObject does not).
Below you can find an example of how to do bitmapHitTesting. Creating the bitmaps in this function is not necesarry if they do not change shape. In that case, I would suggest placing the code for the bitmapdata in a added_to_stage-method.
private var _previousX:int;
private var _previousY:int;
private var _bmpd:BitmapData ;
private var _physicalBitmapData:BitmapData;
private function walkAround(e:Event):void
{
var _xTo:int = //Calculate x-to-position;
var _yTo:int = //Calculate y-to-position;
//If your character doesn't change shape, you don't have to recalculate this bitmapData over and over.
//I suggest placing it in a ADDED_TO_STAGE-Event in that case.
_bmpd = new BitmapData(char.width, char.height, true, 0);
_bmpd.draw(char);
//If your bounds are static, you don't have to recalculate this bitmapData over and over.
//I suggest placing it in a ADDED_TO_STAGE-Event in that case.
_physicalBitmapData = new BitmapData(bounds.width, bounds.height, true, 0);
_bmpd.draw(bounds);
//The below line is the actual hittest
if(_physicalBitmapData.hitTest(new Point(0, 0), 255, _bmpd, new Point(char.x, char.y), 255))
{
char.x = _previousX;
char.y = _previousY;
}
else
{
char.x = _xTo;
char.y = _yTo;
}
_previousX = char.x;
_previousY = char.y;
}
Look at my hint,
function loop(Event)
{
if(isHit==false)
{
if(isRight==true){head_mc.x+=1}
if(isLeft==true){head_mc.x-=1}
if(isUp==true){head_mc.y-=1}
if(isDown==true){head_mc.y+=1}
}
if(head_mc.hitTestObject(build_mc))
{
isHit=true;
if(isRight==true){head_mc.x-=1}
if(isLeft==true){head_mc.x+=1}
if(isUp==true){head_mc.y+=1}
if(isDown==true){head_mc.y-=1}
}
else
{
isHit=false;
}
}
I use step back to opposite direction instead.