Starling mouseover detection - actionscript-3

I'm making a point & click game where is clickable objects. When player moves mouse over the object, there appear a tooltip beside the cursor. It works almost as intended with the code below:
private function added():void
{
removeEventListener(Event.ADDED, added);
this.addEventListener(TouchEvent.TOUCH, onTouch);
}
protected function onTouch(e:TouchEvent):void
{
var touchHover:Touch = e.getTouch(this, TouchPhase.HOVER);
if (touchHover)
{
trace("show");
//mouse is hovered over this object. Therefore call Hovertext:
if (Game.hoverText.message != name_)
Game.hoverText.message = name_
}
else
{
//mouse leaves the object
trace("hide");
Game.hoverText.hideMessage(name_);
}
}
However, it has a strange problem I don't understand. If I move mouse over object and then move it downwards still staying over the object, it triggers hiding function in every second frame or so. Same thing happens when I move cursor to the right, but not when moving it up or left.
So my question is what is wrong with my code? Is this even the best way to go to detect when mouse rolls over object and when it rolls away?
EDIT: I have been going through following iterations with each of them the same problem:
var touch:Touch = event.getTouch(this);
if (touch == null) {
// Set Object alpha to 0;
//trace("pois");
Game.hoverText.hideMessage(name_);
}
else if (touch.phase == TouchPhase.HOVER) {
// Set Object alpha to 1;
//trace("paalla");
if (Game.hoverText.message != name_)
Game.hoverText.message = name_;
}
else {
// for a phase BEGIN/MOVE/STATIONARY case
// see if the touch is over the bounds of the tile (assuming 'this' is the tile)
HELPER_POINT.x = touch.globalX;
HELPER_POINT.y = touch.globalY;
this.globalToLocal(HELPER_POINT, HELPER_POINT);
if(this.hitTest(HELPER_POINT, true) != null)
{
// Set Object alpha to 1; over tile
trace("paalla");
}
else
{
// Set Object alpha to 0; not over tile
trace("pois");
}
}
var touchHover:Touch = e.getTouch(this);
if (touchHover && touchHover.phase == TouchPhase.HOVER)
{
trace("show");
//mouse is hovered over this object. Therefore call Hovertext:
if (Game.hoverText.message != name_)
Game.hoverText.message = name_
}
if (touchHover == null)
{
//mouse leaves the object
trace("hide");
Game.hoverText.hideMessage(name_);
}
Also here is swf for demonstating the problem:
http://www.students.tut.fi/~salmi26/ScorpionBox.html

private function isPressed(event:TouchEvent):void
{
var touch:touch = event.getTouch(this);
if(touch.phase == TouchPhase.BEGAN){
trace("show");
//mouse is hovered over this object. Therefore call Hovertext:
if (Game.hoverText.message != name_)
Game.hoverText.message = name_
} else if(touch.phase == TouchPhase.ENDED){
trace("release");
//stop doing stuff
removeEventListener(Event.ENTER_FRAME, onButtonHold);
}
}

import starling.events.Touch;
import starling.events.TouchEvent;
import starling.events.TouchPhase;
private var touches:Vector.<Touch>;
private var touch:Touch;
private var touchStage:Touch;
private function onTouch(e:TouchEvent=null):void {
touches= e.getTouches(DisplayObject(e.target));
if (touches.length == 1)
{
touch = touches[0];
if (touch.phase == TouchPhase.BEGAN){
m_TouchTarget = touch.target;
//HERE IS A MOUSE DOWN
}
if (touch.phase == TouchPhase.ENDED){
m_TouchEndedPoint = new Point(touch.globalX, touch.globalY);
if (stage.hitTest(m_TouchEndedPoint, true) == m_TouchTarget)
{
//HERE IS A MOUSE UP
}
}
if (touch.phase == TouchPhase.MOVED){
m_TouchEndedPoint = new Point(touch.globalX, touch.globalY);
//HERE IS A MOUSE OUT
if (stage.hitTest(m_TouchEndedPoint, true) != m_TouchTarget)
{
//HERE IS A MOUSE OVER
}
}
}
}

Related

Adding multiple instances of the same object to the stage

I'm making a "spit" card game and I'm trying to be able to have 3 cards on the stage at once to be able to drag to the pile. So my question is how do I have multiple instances of the same object on the stage, but be able to assign each card different frames, etc. Here is the code to understand the context of it.
"cardArray" has 53 frames, 52 of which have a card face, 53rd is a "deck" picture.
//variables, constants
var cardDeck:Array = new Array();
var timer:Timer = new Timer(2000);
var j:int = 0;
var card:MovieClip = new CardArray();
var cardInDeck:Boolean;
const deckSize:int = 52;
//events:
card.addEventListener(MouseEvent.MOUSE_DOWN,fDown);
card.addEventListener(MouseEvent.MOUSE_UP,fUp);
startRandomise();
//This is where the unshuffled "deck" is created - values loaded into the array.
function createDeck():void
{
var i:int;
for(i =0; i<deckSize; i++)
{
cardDeck[i] = i+1;
}
}
function randomizeDeck( a : *, b : * ):int
{
return (Math.random()>.5) ? 1 : -1;
}
//Grab the value from the (presumably) current frame of the card
function convertCard(cardNo:int):int
{
var deckPos:int;
if (cardNo <= deckSize/4)
{
deckPos = cardNo;
}
else if (cardNo > deckSize/4 && cardNo <= deckSize/2)
{
deckPos = cardNo -13;
}
else if (cardNo > deckSize/2 && cardNo <=deckSize*3/4)
{
deckPos = cardNo -deckSize/2;
}
else if (cardNo >39)
{
deckPos = cardNo -39;
}
return deckPos;
}
btn.addEventListener(MouseEvent.MOUSE_UP,showArray);
function showArray(event:MouseEvent):void
{
if(j<deckSize /2)
{
addChild(card);
card.gotoAndStop(cardDeck[j]);
trace(cardDeck[j]+","+deckCompare[j]);
j++;
}
if(cardInDeck)
{
card.x = 200;
card.y = 200;
cardInDeck+false;
}
}
function fDown(evt:MouseEvent)
{
card.startDrag();
}
function fUp(evt:MouseEvent)
{
stopDrag();
if(card.hitTestObject(deck))
{
trace("deck: "+convertCard(deck.currentFrame));
trace("card: "+convertCard(card.currentFrame));
if(convertCard(card.currentFrame) == convertCard(deck.currentFrame)+1 || convertCard(card.currentFrame) == convertCard(deck.currentFrame)-1 || convertCard(card.currentFrame) == 13 && convertCard(deck.currentFrame) == 1 || convertCard(card.currentFrame) == 1 && convertCard(deck.currentFrame) == 13 || convertCard(deck.currentFrame) == 14)
{
deck.gotoAndStop(card.currentFrame);
removeChild(card);
cardInDeck=true;
}
else
{
card.x = 200;
card.y = 200;
}
}
else
{
card.x = 200;
card.y = 200;
}
}
function startRandomise():void
{
createDeck();
cardDeck.sort(randomizeDeck);
cardDeck.sort(randomizeDeck);
trace("random: "+cardDeck);
trace("deckCompare: "+deckCompare);
card.x = 200;
card.y = 200;
deck.gotoAndStop(53);
}
To have multiple cards, you need to instantiate more than one card. I don't know anything about the card game spit and don't understand the purpose of many of your functions, but you'll want to do something along these lines:
Create a function that generates (instantiates) a new card:
function createCard(faceFrame):MovieClip {
var tmpCard:MovieClip = new CardArray(); //this creates a new instance of a card
tmpCard.addEventListener(MouseEvent.MOUSE_DOWN, cardMouseDown); //make the card call the mouse down function when that event is triggered
tmpCard.gotoAndStop(faceFrame); //assign the card it's face (frame)
addChild(tmpCard); //add the card to the screen
return tmpCard;
}
Then, in your mouse down handler (I renamed it to be more clear what is is), you can do the following to card that was mouse downed:
function cardMouseDown(evt:MouseEvent) {
//the events currentTarget property is reference to the item that you attached the listener to, so in this case the card who triggered the mouse down
card = evt.currentTarget as MovieClip; //assign the current clicked card to the global card variable (so we can access it in the mouse up function)
card.startDrag();
//add the mouse up listener on the stage, since there are cases where you can drag so fast that the mouse isn't over the item it's dragging (and then if you were to release the mouse at that moment it wouldn't dispatch the event)
stage.addEventListener(MouseEvent.MOUSE_UP, stageMouseUp);
}
Then your mouse up function would look something like this:
function stageMouseUp(evt:MouseEvent) {
//remove the mouse up listener from the stage
stage.removeEventListener(MouseEvent.MOUSE_UP, stageMouseUp);
card.stopDrag();
if(card.hitTestObject(deck))
{
......//rest if the code
Seems like you would want to generate a new card in the showArray function you have? So something like this:
function showArray(event:MouseEvent):void
{
var newCard:MovieClip = createCard(cardDeck[j]);
j++
newCard.y = newCard.x = 200;
trace(cardDeck[j]+","+deckCompare[j]);
}
As an aside, you should consider calling your Card object something more sensible, like Card instead of CardArray, as it's not an array...

dispatch mouse out event when the mouse not out

I created a tooltip class.
When the mouse over on MovieClip it enable and when it out it disable.
The movieclip containt some other movieclips.
My code is that:
to.addEventListener(MouseEvent.MOUSE_OVER, showTip);
to.addEventListener(MouseEvent.MOUSE_OUT, hideTip);
to.addEventListener(MouseEvent.MOUSE_MOVE, MoveTip);
and the functions is that:
private function showTip(evt: MouseEvent) {
if (tip != null && !tip.visible) {
tip.x = evt.stageX;
tip.y = evt.stageY;
tip.visible = true;
}
}
private function hideTip(evt: MouseEvent) {
if (tip != null && tip.visible) {
tip.visible = false;
}
}
private function MoveTip(evt: MouseEvent) {
if (tip != null && tip.visible) {
tip.x = evt.stageX;
tip.y = evt.stageY;
}
}
Its work but sometimes the hideTip function and the showTip function enable in same time and the tip is flashing.
Apparently your tooltip obscures the underlying to movieclip, thus effectively making Flash to think that mouse is out of the to MC, triggering the mouse out listener. A possible solution is shifting the tip off the mouse cursor instead of displaying it right on top of the mouse position.
const offsetX:Number=4;
const offsetY:Number=4; // experiment with these
private function showTip(evt: MouseEvent) {
if (tip != null && !tip.visible) {
tip.x = evt.stageX+offsetX;
tip.y = evt.stageY+offsetY;
tip.visible = true;
}
}
private function MoveTip(evt: MouseEvent) {
if (tip != null && tip.visible) {
tip.x = evt.stageX+offsetX;
tip.y = evt.stageY+offsetY;
}
}
Try to use
to.addEventListener(MouseEvent.ROLL_OVER, showTip);
to.addEventListener(MouseEvent.ROLL_OUT, hideTip);
That avoids the problem of dispatching the event, if the mouse changes e.g. between a letter and a transparancy in an area or between children of the same tooltip-target, which should completely have one tooltip. Sry for my english. Hope, it helps. Greetings
As #BotMaster and #Vesper suggested, the tip is displayed under the mouse, which causes the MouseEvent.MOUSE_OUT to fire.
To prevent this, do:
tip.mouseEnabled = false;
tip.mouseChildren = false;

My Character Won't Move :( Multiple .as files AS3

I have a few .as files. They are: MainClass.as, FrontEnd.as, Levels.as, and Hero.as. My problem (as far as I know) is in my Hero.as file. Let me descibe how I have it all set up thusfar because I have been a bit concerned that there are better ways of doing things in AS3.
MainClass.as makes a variable of FrontEnd (menus, namely the main menu) and calls it up (addChild).
FrontEnd.as are my menus. buttons and whatnot...
Levels.as right now just calls up level 1 when the start new game button is pressed on the main menu. Had one hell of a time figuring out how to use functions from a different .as file. Hero.as I will add my code for. I'm posting the whole thing because I don't know where my problem is.
public class Hero extends MovieClip
{
public var roger:player = new player();
private var animationState:String = "down";
public var facing:String = "down";
private var isLeft:Boolean = false;
private var isRight:Boolean = false;
private var isUp:Boolean = false;
private var isDown:Boolean = false;
public var currentPlayer:MovieClip = roger;
public function Hero()
{
addEventListener(Event.ENTER_FRAME, loop);
addEventListener(Event.ADDED_TO_STAGE, onStage);
trace(currentPlayer);
}
public function onStage( event:Event ):void
{
removeEventListener( Event.ADDED_TO_STAGE, onStage );
}
public function addCurrentPlayer():void
{
roger.x = stage.stageWidth * .5;
roger.y = stage.stageHeight * .5;
addChild(roger);
currentPlayer = roger;
setBoundaries();
}
public function keyDownHandler(event:KeyboardEvent)
{
if (event.keyCode == 39)//right press
{
isRight = true;
}
if (event.keyCode == 37)//left pressed
{
isLeft = true;
}
if (event.keyCode == 38)//up pressed
{
isUp = true;
}
if (event.keyCode == 40)//down pressed
{
isDown = true;
}
}
public function keyUpHandler(event:KeyboardEvent)
{
if (event.keyCode == 39)//right released
{
isRight = false;
}
if (event.keyCode == 37)//left released
{
isLeft = false;
}
if (event.keyCode == 38)//up released
{
isUp = false;
}
if (event.keyCode == 40)//down released
{
isDown = false;
}
}
public function loop(Event):void
{
if (currentPlayer == null)
{
addCurrentPlayer();//make sure at least roger is on the screen
}
currentPlayer.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
currentPlayer.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
//----------------------------------0
//Animation States
//----------------------------------0
if (isDown == true)
{
currentPlayer.y += 5;
animationState = "walk_down";
facing = "down";
currentPlayer.gotoAndStop(animationState);
}
else if (isUp == true)
{
currentPlayer.y -= 5;
animationState = "walk_up";
facing = "up";
currentPlayer.gotoAndStop(animationState);
}
else if (isRight == true)
{
currentPlayer.x += 5;
animationState = "walk_right";
facing = "right";
currentPlayer.gotoAndStop(animationState);
}
else if (isLeft == true)
{
currentPlayer.x -= 5;
animationState = "walk_left";
facing = "left";
currentPlayer.gotoAndStop(animationState);
}
//----------------------------------0;
//IDLE STATES
//----------------------------------0
else if (isDown == false)
{
currentPlayer.gotoAndStop(facing);
}
else if (isUp == false)
{
currentPlayer.gotoAndStop(facing);
}
else if (isRight == false)
{
currentPlayer.gotoAndStop(facing);
}
else if (isLeft == false)
{
currentPlayer.gotoAndStop(facing);
}
}
public function setBoundaries():void
{
var halfHeight:int = currentPlayer.height * .5;
var halfWidth:int = currentPlayer.width * .5;
if(currentPlayer.y <= 1)
{
currentPlayer.y += halfHeight;
}
else if(currentPlayer.y > stage.stageHeight)
{
currentPlayer.y -= halfHeight;
}
else if(currentPlayer.x <= 1)
{
currentPlayer.x += halfWidth;
}
else if(currentPlayer.x > stage.stageWidth)
{
currentPlayer.x -= halfWidth;
}
}
}
}
trace(currentPlayer); is giving me [object player] instead of the instance name "roger". (Later on I want more playable characters.) I'm not sure if the problem is there or in my levels file, which I'll post here. (not as long as Hero.as)
public class Levels extends MovieClip
{
var currentLevel:MovieClip;
public function Levels()
{
addEventListener(Event.ADDED_TO_STAGE, onStage);
}
private function onStage(event:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, gotoLevelOne);
}
public function gotoLevelOne():void
{
var levelOne:LevelOne = new LevelOne();
var hero:Hero = new Hero();
addChild(hero);
levelOne.x = stage.stageWidth * .5;
levelOne.y = stage.stageHeight * .5;
addChild(levelOne);
currentLevel = levelOne;
hero.currentPlayer.x = 100;
hero.currentPlayer.y = 100;
addChild(hero.currentPlayer);
}
}
}
If I remove = roger; from var currentPlayer:MovieClip = roger; it gives me #1009 null object even though I told it in addCurrentPlayer() to change currentPlayer to roger. On level 1, everything shows up but I can't move my character. I know that it worked when I was working on his animations and I would call him to the main menu. Everything worked on him. What's the problem now?
Firstly, there's a lot of things wrong with your code:
In your Hero Class, the 'onStage' Event handler doesn't actually do anything other than remove the event listener that triggers it. While it's good practice to remove the event listener, there should be some other purpose to the Event handler. If there isn't you can remove it and not bother listening for the ADDED_TO_STAGE Event.
Similarly, in your Levels Class 'onStage' Event handler you attempt to remove the event, but name the wrong handler. I assume you want to remove the event handler and then run the 'gotoLevelOne' method. If so, just have the Event.ADDED_TO_STAGE listener call 'gotoLevelOne' and then remove the Event listener there:
public function Levels()
{
addEventListener(Event.ADDED_TO_STAGE, gotoLevelOne);
}
public function gotoLevelOne():void
{
removeEventListener(Event.ADDED_TO_STAGE, gotoLevelOne);
// class continues....
OK, so to your question:
You will be getting the null error because you are referring to currentPlayer from outside the Hero Class, before calling addCurrentPlayer (where it is set).
If you temporarily define currentPlayer as a private variable, the compiler should give you a line number where you first refer to currentPlayer from OUTSIDE the Hero Class (the error will be something like 'Class X is trying to access a private (or non-existent) property of Y Class').
You can then sort out WHY you are accessing currentPlayer before calling addCurrentPlayer. You may also want to think about if currentPlayer NEEDS to be public (if so, then what is 'addCurrentPlayer' for? That function effectively works as a setter for currentPlayer).
EDIT:
At the moment you are adding new KEY_DOWN and KEY_UP event listeners EVERY frame of your game loop. This is unnecessary. Add them both ONCE in the initialisation of your game (perhaps in your Hero's onStage handler), and count on them to trigger the appropriate handlers.
You will want to add the KEY_DOWN and KEY_UP listeners to 'stage', not currentPlayer, so:
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
You will need to add the listeners AFTER your Hero instance has been added to the Stage though, so it has access to 'stage'. That's why it makes sense to add the listeners in the Hero's onstage handler.

AS3 - Removing event listener failed

here's my code, i don't know why it doesn't remove the event listener.
What i want to do is having some button to turn the sound on and off according to the beat. On the first click it worked perfectly, but when i want to remove the sound on the second click it cannot remove the event listener.
plat.BEAT1.addEventListener(TouchEvent.TOUCH_BEGIN, BEATDown(plat.BEAT1, hihatz, 2));
plat.BEAT2.addEventListener(TouchEvent.TOUCH_BEGIN, BEATDown(plat.BEAT2,cymbalz, 4));
function BEATDown (padNum, sounz, tiempo) {
return function (e:TouchEvent) {
var currentSound:Sound = null;
var currentSoundChannel:SoundChannel;
var active:int;
if (padNum.currentFrame == 1) {
padNum.gotoAndStop(3);
padNum.addEventListener(Event.ENTER_FRAME, PlayBeats);
active = 1;
} else {
padNum.gotoAndStop(1);
padNum.removeEventListener(Event.ENTER_FRAME, PlayBeats);
active = 0;
}
function playSound(sound:Sound):void
{
if (active == 0)
{
// Stop playing ANY sound
currentSound = null;
currentSoundChannel = null;
}
else
{
// Play a different sound
currentSound = sound;
currentSoundChannel = sound.play();
}
}
function PlayBeats(event:Event):void
{
if (tiempo == 1) {
if (fl_SecondsElapsed <= 4) {
playSound(sounz);
}
}
if (tiempo == 2) {
if (fl_SecondsElapsed == 1 || fl_SecondsElapsed == 3) {
playSound(sounz);
}
}
if (tiempo == 4) {
if (fl_SecondsElapsed == 1) {
playSound(sounz);
}
}
}
}
}
EDIT:
The listener i want to remove is padNum.addEventListener(Event.ENTER_FRAME, PlayBeats);
plat.BEAT1 is the button instance. I use multiple instance to trigger different sound, each toggle sound on and off according to the Tiempo count.
I would say it's because you don't add / remove your listener on the same object;
is plat.BEAT1 et plat.BEAT2 are the same? I guess not.
I would recommend you to try by adding the EnterFrame listener on stage on this or on the parent element to the if the event is Removed correctly. Then if you want your object works independently, try to target the right object.
if (padNum.currentFrame == 1) {
padNum.gotoAndStop(3);
stage.addEventListener(Event.ENTER_FRAME, PlayBeats);
active = 1;
} else {
padNum.gotoAndStop(1);
stage.removeEventListener(Event.ENTER_FRAME, PlayBeats);
active = 0;
}

swap depthes when movie clips loaded from library

I have two movie clips in the library with linkage.
on the stage I have two buttons - each load a movie clip to a specific mc target on the stage.
I also have a third button that removes the mc target, to clear the stage.
I want to know how can I change the code in AS3 so the loaded movie clips will not show at the same time, but swap each other, like I used to use depth in AS2.
This is the code:
var myIgool = new igool ();
var myRibooa = new ribooa ();
loadigool.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler_3);
function fl_MouseClickHandler_3(event:MouseEvent):void
{
mc_all.addChild (myIgool);
}
loadribooa.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler_4);
function fl_MouseClickHandler_4(event:MouseEvent):void
{
mc_all.addChild (myRibooa);
}
unloadall.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler_6);
function fl_MouseClickHandler_6(event:MouseEvent):void
{
removeChild(mc_all);
;
}
I would recommend something like this:
var myIgool = new igool ();
var myRibooa = new ribooa ();
mc_all.addChild(myIgool);
mc_all.addChild(myRibooa);
myIgool.visible = false;
myRibooa.visible = false;
loadigool.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler_3);
function fl_MouseClickHandler_3(event:MouseEvent):void
{
myIgool.visible = true;
myRibooa.visible = false;
}
loadribooa.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler_4);
function fl_MouseClickHandler_4(event:MouseEvent):void
{
myIgool.visible = false;
myRibooa.visible = true;
}
unloadall.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler_6);
function fl_MouseClickHandler_6(event:MouseEvent):void
{
myIgool.visible = false;
myRibooa.visible = false;
}
But if you really want to swap, you could also do the following, however I recommend setting the visible flag as it's more efficient rather than covering something up that wouldn't need to draw.
loadigool.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler_3);
function fl_MouseClickHandler_3(event:MouseEvent):void
{
if (myIgool.parent != mc_all)
{
mc_all.addChild(myIgool);
}
else
{
mc_all.setChildIndex(myIgool, mc_all.numChildren - 1);
}
}
loadribooa.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler_4);
function fl_MouseClickHandler_4(event:MouseEvent):void
{
if (myRibooa.parent != mc_all)
{
mc_all.addChild(myRibooa);
}
else
{
mc_all.setChildIndex(myRibooa, mc_all.numChildren - 1);
}
}