How to register hitTest only if Mouse Down is true? - actionscript-3

I was wondering on how to register a Hittest between two Movie Clips only if the MOUSE_DOWN event is equal to true.
So I have a Movie Clip called playerHook that acts as the Mouse and whenever that movie clip comes in contact with another movie clip called octopusBoss and Clicks on it I want it to register a hittest and subtract a point from its health.
Here is what I have so far but not sure what to do next:
In my main constructor function:
playerHook.addEventListener(Event.ENTER_FRAME, playerHookMove);
playerHook.addEventListener(MouseEvent.MOUSE_DOWN, onMDown);
playerHook.addEventListener(MouseEvent.MOUSE_UP, onMUp);
Then I have two booleas set up in the two functions onMDown and onMUp:
private function onMDown(e:MouseEvent):void
{
mouseIsDown = true;
}
private function onMUp(e:MouseEvent):void
{
mouseIsUp = false;
}
Then I have this function which is currently in my gameloop that I would want to happen only if the user clicks on the OctopusBoss:
private function checkPlayerHitOctopusBoss():void
{
if (playerHook.hitTestObject(octopusBoss))
{
trace("Hit Octopus");
octopusBossHealth --;
}else
if (octopusBossHealth <= 0)
{
octopusBoss.destroyOctopusBoss();
}
}
But I am having trouble passing the mouse booleans to the if statement. I know I am missing something crucial!

First you need some name to your octopusBoss like so:
octopusBoss.name = "octopusBoss";
Modify your onMDown function like so:
private function onMDown(e:MouseEvent):void
{
if(e.target.name == "octopusBoss") //Check if octopusBoss is under mouse
mouseIsDown = true;
}
And your checkPlayerHitOctopusBoss function like so:
private function checkPlayerHitOctopusBoss():void
{
if (playerHook.hitTestObject(octopusBoss) && mouseIsDown) // check mouse down
{
trace("Hit Octopus");
octopusBossHealth --;
}else
if (octopusBossHealth <= 0)
{
octopusBoss.destroyOctopusBoss();
}
}
UPDATE:
If it's ok to add MouseEvents directly to octopusBoss like so:
octopusBoss.addEventListener(MouseEvent.MOUSE_DOWN, onMDown);
octopusBoss.addEventListener(MouseEvent.MOUSE_UP, onMUp);
Then, you can also use e.currentTarget.name.

Related

Flash CS4 How do you set variable to the Object Dragging Collison?

I am doing a game for my computer science class and I am working on inventory, dragging an item into it. I do not know how to set the item as whatever the user clicked to drag.
At the moment I hard coded it to the item they are dragging, but in the future I want more items, so a variable set to the item they are dragging would make it work perfectly, but I don't know what it's called to do that.
Here is my code for inventory item dragging
function dragItem (event:MouseEvent):void
{
knife_loot.startDrag();
}
function dropItem (event:MouseEvent):void
{
knife_loot.stopDrag();
if ((knife_loot.hitTestObject(inv_game)) && (inv_game.visible == true))
{
trace("Item dropped in inventory")
trace("")
knife_loot.x = 80
knife_loot.y = 120
}
}
// end of dragging and dropping items
You can start with:
// List the items you want to drag.
var aList:Array = [knife_loot, spoon_loot, fork_loot];
// InteractiveObject is superclass for SimpleButton, Sprite and MovieClip.
// If you're sure what they all are then just use their class instead.
for each (var anItem:InteractiveObject in aList)
{
// Subscribe them all for dragging.
anItem.addEventListener(MouseEvent.MOUSE_DOWN, onDrag);
}
public var draggedItem:InteractiveObject;
function onDrag(e:MouseEvent):void
{
// Use e.currentTarget because original MouseEvent e.target
// could be something from deep inside of top object e.currentTarget.
draggedItem = e.currentTarget as InteractiveObject;
draggedItem.startDrag();
// Let's hook drop events.
stage.addEventListener(Event.MOUSE_LEAVE, onDrop);
stage.addEventListener(MouseEvent.MOUSE_UP, onDrop);
}
function onDrop(e:Event):void
{
// Unhook drop events.
stage.removeEventListener(Event.MOUSE_LEAVE, onDrop);
stage.removeEventListener(MouseEvent.MOUSE_UP, onDrop);
// Drop the item.
draggedItem.stopDrag();
if ((draggedItem.hitTestObject(inv_game)) && (inv_game.visible == true))
{
trace("Item", draggedItem.name, "was dropped to inventory.");
trace("");
draggedItem.x = 80;
draggedItem.y = 120;
}
// Forget the item.
draggedItem = null;
}

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;
}

AS3 - How to get function to run after currentFrame updates?

I have 2 copies of a MovieClip (mcA) on my main timeline. Within mcA, I have 5 more MovieClips (mcB) and this code:
addEventListener(MouseEvent.CLICK, clickedPoint);
function clickedPoint(e:MouseEvent) {
e.target.play();
setStat(e.target.parent);
}
So I toggle the frame of the mcB that was clicked, and I run a function that references the corresponding mcA on the main timeline.
In setStat, I call another function that checks the currentFrame of all the mcBs in the mcA.
getPoints(stat) {
var points = 0;
for(var i = 1; i <= 5; i++)
{
if(stat["pnt"+i].currentFrame == 2) points++;
}
trace(points);
return points;
}
My problem is that the setStat function seems to run before the currentFrame of the mcB that has been clicked on updates.
How should I change my code so that the mcB that is clicked on registers as having changed its currentFrame when I call setStat?
You could call setStat() after the next frame update. A simple way to do this is to add an ENTER_FRAME event handler which removes itself when first invoked:
function clickedPoint(e:MouseEvent) {
e.target.play();
e.target.addEventListener(Event.ENTER_FRAME, function(e:Event):void {
e.target.removeEventListener(Event.ENTER_FRAME, arguments.callee);
setStat(e.target.parent);
});
}
You can encapsulate this behavior in a function that behaves like setTimeout:
function callNextFrame(target:DisplayObject, callback:Function, ...params:Array):void {
target.addEventListener(Event.ENTER_FRAME, function(e:Event):void {
target.removeEventListener(Event.ENTER_FRAME, arguments.callee);
callback.apply(null, params);
});
}
// Usage:
callNextFrame(e.target as DisplayObject, setStat, e.target);

Dragging movie clips in Action Script 3

Hi so recently I have been attempting to Drag a movie clip in AS3 but I'm having some trouble picking up with the hit tests anyone got any ideas? Just to clarify, the issue is that when the movieclips hit the drag test object, they're not executing the gotoframe() function.
initDrag() adds action listeners:
MOUSE_DOWN on the object
MOUSE_UP on the stage so it doesn’t matter if you are off the object
endDrag() removes the action listeners; call this (for each object) before you go to another frame
startADrag()create a rectangle within which the object can be dragged (in this case the stage)
call startDrag() on the object
stopADrag() call stopDrag() on the object from currentObject (but only if currentObject is not null).
var currentObject:MovieClip = null;
initDrag(block1);
initDrag(block2);
initDrag(block3);
initDrag(block4);
function initDrag(obj:MovieClip )
{
obj.addEventListener(MouseEvent.MOUSE_DOWN,startADrag);
stage.addEventListener(MouseEvent.MOUSE_UP,stopADrag);
}
function endDrag(obj:MovieClip )
{
obj.removeEventListener(MouseEvent.MOUSE_DOWN,startADrag);
stage.removeEventListener(MouseEvent.MOUSE_UP,stopADrag);
}
function startADrag(e:MouseEvent):void
{
currentObject = (MovieClip)(e.target);
var rect:Rectangle = new Rectangle(0,0,stage.stageWidth - currentObject.width,stage.stageHeight - currentObject.height + 100);
currentObject.startDrag(false,rect);
}
function stopADrag(e:MouseEvent):void
{
if (currentObject != null)
{
currentObject.stopDrag();
}
}
if(block1.hitTestObject(dragtest)){
gotoAndStop("lose");
}
if(block2.hitTestObject(dragtest)){
gotoAndStop(27);
}
if(block3.hitTestObject( dragtest)){
gotoAndStop("lose");
}
if(block4.hitTestObject( dragtest)){
gotoAndStop("lose");
}
thanks for any advice or answers.
The following code should work as expected. The problem is, as i already stated in my comment, that your calls to hitTestObject(obj) only get executed once, at the very beginning of your application. What you need to do though is to check it constantly.
Think about it, if your calls to hitTestObject-calls only get executed once at the beginning, when you didn't even have a chance to drag one of your objects, it will always return false, right? Because your objects are still in their initial position (outside of the dragtest objecti must assume).
With an event listener for Event.ENTER_FRAME you check it once per frame instead. So even if all the results for hitTestObject are false, it will check them all again on the next frame (if you are currently dragging, controlled through a simple boolean called dragging).
var currentObject:MovieClip = null;
var dragging:Boolean = false;
initDrag(block1);
initDrag(block2);
initDrag(block3);
initDrag(block4);
addEventListener(Event.ENTER_FRAME, checkForHit);
function checkForHit(e:Event):void{
if(dragging){
if(block1.hitTestObject(dragtest)){
gotoAndStop("lose");
}
if(block2.hitTestObject(dragtest)){
gotoAndStop(27);
}
if(block3.hitTestObject( dragtest)){
gotoAndStop("lose");
}
if(block4.hitTestObject( dragtest)){
gotoAndStop("lose");
}
}
}
function initDrag(obj:MovieClip )
{
obj.addEventListener(MouseEvent.MOUSE_DOWN,startADrag);
stage.addEventListener(MouseEvent.MOUSE_UP,stopADrag);
}
function endDrag(obj:MovieClip )
{
obj.removeEventListener(MouseEvent.MOUSE_DOWN,startADrag);
stage.removeEventListener(MouseEvent.MOUSE_UP,stopADrag);
}
function startADrag(e:MouseEvent):void
{
currentObject = (MovieClip)(e.target);
var rect:Rectangle = new Rectangle(0,0,stage.stageWidth - currentObject.width,stage.stageHeight - currentObject.height + 100);
currentObject.startDrag(false,rect);
dragging = true;
}
function stopADrag(e:MouseEvent):void
{
if (currentObject != null)
{
currentObject.stopDrag();
dragging = false;
}
}

Click event outside MovieClip in AS3

Is there any way to detect if the user click outside a MovieClip?
For instance, I need to detect it to close a previously opened menu (like Menu bar style: File, Edition, Tools, Help, etc).
How can I detect this kind of event? Thanks!
Add a listener to stage and check if stage is the target of the event.
Example of code here:
http://wonderfl.net/c/eFao
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class FlashTest extends Sprite
{
private var _menu : Sprite;
public function FlashTest()
{
_menu = new Sprite();
_menu.x = 100;
_menu.y = 100;
_menu.alpha = 0.5;
with(_menu.graphics)
{
beginFill(0xFF0000, 1);
drawRect(0, 0, 300, 300);
endFill();
}
addChild(_menu);
_menu.addEventListener(MouseEvent.CLICK, onClickHandler);
stage.addEventListener(MouseEvent.CLICK, onClickHandler);
}
private function onClickHandler(event : MouseEvent) : void
{
switch(event.target)
{
case _menu:
_menu.alpha = 0.5;
break;
case stage:
_menu.alpha = 1;
break;
}
}
}
}
You can add a listener to the click event of the root element:
MovieClip(root).addEventListener(MouseEvent.CLICK, clickObject);
then in the function clickObject, you can check to see what you are clicking.
function clickObject(e:Event):void
{
var hoverArray:Array = MovieClip(root).getObjectsUnderPoint(new Point(stage.mouseX, stage.mouseY));
var hoverOverObject:* = hoverArray[hoverArray.length - 1];
}
hoverOverObject references the element that you are clicking on. Often this will be the shape within the movie clip, so you'll need to look at it's parent then compare it to your movie clip. If the click wasn't on the drop down movie clip, trigger the close.
var container:MovieClip = new MovieClip();
var mc:MovieClip = new MovieClip();
with(mc.graphics){
beginFill(0xff0000,1);
drawCircle(0,0,30);
endFill();
}
mc.name = "my_mc";
container.addChild(mc);
addChild(container);
stage.addEventListener(MouseEvent.CLICK, action);
function action (e:MouseEvent):void
{
if(e.target.name != "my_mc"){
if(container.numChildren != 0)
{
container.removeChild(container.getChildByName("my_mc"));
}
}
}
Use capture phase:
button.addEventListener(MouseEvent.CLICK, button_mouseClickHandler);
button.stage.addEventListener(MouseEvent.CLICK, stage_mouseClickHandler, true);
//...
private function button_mouseClickHandler(event:MouseEvent):void
{
trace("Button CLICK");
}
private function stage_mouseClickHandler(event:MouseEvent):void
{
if (event.target == button)
return;
trace("Button CLICK_OUTSIDE");
}
Note that using stopPropagation() is good for one object, but failed for several. This approach works good for me.
Use a stage and a sprite (menu) click listener with the sprite listener executing first and apply the stopPropagation() method to the click handler of the sprite. Like this:
menu.addEventListener(MouseEvent.CLICK, handleMenuClick);
stage.addEventListener(MouseEvent.CLICK, handleStageClick);
private function handleMenuClick(e:MouseEvent):void{
// stop the event from propagating up to the stage
// so handleStageClick is never executed.
e.stopPropagation();
// note that stopPropagation() still allows the event
// to propagate to all children so if there are children
// within the menu overlay that need to respond to click
// events that still works.
}
private function handleStageClick(e:MouseEvent):void{
// put hide or destroy code here
}
The idea is that a mouse click anywhere creates a single MouseEvent.CLICK event that bubbles from the stage, down through all children to the target, then back up through the parents of the target to the stage. Here we interrupt this cycle when the target is the menu overlay by not allowing the event to propagate back up to the parent stage, ensuring that the handleStageClick() method is never invoked. The nice thing about this approach is that it is completely general. The stage can have many children underneath the overlay and the overlay can have its own children that can respond to clicks and it all works.