Dragging movie clips in Action Script 3 - actionscript-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;
}
}

Related

how to hittestObject multiple movie clips at the same time

I have a simple drag and drop to the target system it works fine except I don't someone to be able to drop where there is a movie clip already. decided to hittestObject on the target to check if there is a movie clip there. in if there is it just sends the object back to its original position. however, when I try to code the same thing for more than one object it only works on one of the objects.
I've tried putting my objects in an array and putting them in a variable however I can only hittestobject one object in the array or variable at a time when I need to hitestobject multiple objects at the same time. Please help
if (char2.hitTestObject(target0))
{
slot0=true;
}
if (!char2.hitTestObject(target0))
{
slot0=false;
}
if (char2.hitTestObject(target1))
{
slot1=true;
}
if (!char2.hitTestObject(target1))
{
slot1=false;
}
if (char2.hitTestObject(target2))
{
slot2=true;
}
if (!char2.hitTestObject(target2))
{
slot2=false;
}
I need to also hittestobject on char1 and char0. this code works however if I try to simply duplicate the code and replace char2 with char1 only one other code blocks with work please help. Also an alternative like
if (char2 || char1.hitTestObject(target0))
{
slot0=true;
}
would work that would be helpful. please help show me how to do it or let me know if it's even possible. thank you!!
Full Code:
var slot0:Boolean;
var slot1:Boolean;
var slot2:Boolean;
addEventListener(Event.ENTER_FRAME, fl_EnterFrameHandler);
function fl_EnterFrameHandler(et:Event):void
{
trace(slot0);
}
// Unlike the Object class, that allows String keys only
// the Dictionary class allows you to store and
// access data by the object instance.
var theValids:Dictionary = new Dictionary;
var theCharacters:Dictionary = new Dictionary;
// We'll store the original (x,y) coordinates here.
var theOrigin:Point = new Point;
// The Sprite class is the superclass of MovieClip, furthermore,
// the startDrag method defined for Sprite class, so unless you
// create your own dragging code, you are bound to use Sprites,
// while you cannot drag SimpleButtons and TextFields this way.
// We'll store the current dragged object here.
var theObject:Sprite;
var theChar:Sprite;
// This first argument is the object you want to be draggable.
// The "...targets:Array" means you can call this method with
// any number of arguments, the first one is mandatory, the
// rest will be passed in a form of Array (empty Array if you
// call this method with a single argument).
function setupDraggable(source:Sprite, ...targets:Array):void
{
// Make the object draggable.
source.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
source.mouseChildren = false;
source.mouseEnabled = true;
source.buttonMode = true;
// Keep the list of the object's targets so it can be
// retrieved later by the key of the object itself.
theValids[source] = targets;
}
// Ok, let's setup the objects and link them to their designated
// targets. The whole point of the rest of the code is to make
// this one part as simple as it possible: you just edit
// these lines to tell which one objects go where.
// This object can be dropped to a single target.
// setupDraggable(obj_1 , target1);
// These objects can go to two targets each.
// setupDraggable(obj_10, target1, target10);
// setupDraggable(obj_2 , target2, target20);
stage.addEventListener(MouseEvent.MOUSE_DOWN, setslot1);
function setslot1(e:MouseEvent):void{
if (character1.hitTestObject(target0))
{
slot0=true;
}
if (!character1.hitTestObject(target0))
{
slot0=false;
}
if (character1.hitTestObject(target1))
{
slot1=true;
}
if (!character1.hitTestObject(target1))
{
slot1=false;
}
if (character1.hitTestObject(target2))
{
slot2=true;
}
if (!character1.hitTestObject(target2))
{
slot2=false;
}
}
stage.addEventListener(MouseEvent.MOUSE_DOWN, setslot2);
function setslot2(e:MouseEvent):void{
if (character0.hitTestObject(target0))
{
slot0=true;
}
if (!character0.hitTestObject(target0))
{
slot0=false;
}
if (character0.hitTestObject(target1))
{
slot1=true;
}
if (!character0.hitTestObject(target1))
{
slot1=false;
}
if (character0.hitTestObject(target2))
{
slot2=true;
}
if (!character0.hitTestObject(target2))
{
slot2=false;
}
}
stage.addEventListener(MouseEvent.MOUSE_DOWN, setslot3);
function setslot3(e:MouseEvent):void{
if (char2.hitTestObject(target0))
{
slot0=true;
}
if (!char2.hitTestObject(target0))
{
slot0=false;
}
if (char2.hitTestObject(target1))
{
slot1=true;
}
if (!char2.hitTestObject(target1))
{
slot1=false;
}
if (char2.hitTestObject(target2))
{
slot2=true;
}
if (!char2.hitTestObject(target2))
{
slot2=false;
}
}
// This one object can be dropped to any of targets.
setupDraggable(character0, target0, target1, target2, redslot);
setupDraggable(character1, target0, target1, target2, greenslot);
setupDraggable(char2, target0, target1, target2, blueslot);
// The MOUSE_DOWN event handler.
function onDown(e:MouseEvent):void
{
if (char2.hitTestObject(target0))
{
slot0=true;
}
if (!char2.hitTestObject(target0))
{
slot0=false;
}
if (char2.hitTestObject(target1))
{
slot1=true;
}
if (!char2.hitTestObject(target1))
{
slot1=false;
}
if (char2.hitTestObject(target2))
{
slot2=true;
}
if (!char2.hitTestObject(target2))
{
slot2=false;
}
// Clean-up. Remove the reference, the object is no longer
// being dragged, so you won't need to keep it.
//theObject = null;
// Get the reference to the object under the mouse.
theObject = e.currentTarget as Sprite;
// Keep the object's initial position.
theOrigin.x = theObject.x;
theOrigin.y = theObject.y;
// Put the dragged object on top of anything else.
// We are operating in the parent context of all these
// objects here so there's no need to address anObj.parent.
setChildIndex(theObject, numChildren - 1);
// Start dragging.
theObject.startDrag(true);
// Listen to the MOUSE_UP event, which could happen offstage
// and out of the dragged object, so the only reliable
// way is to listen it from the Stage. That's why we
// are keeping theObject reference as an additional
// variable, without relying on event's data.
stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
}
// The MOUSE_UP event handler.
function onUp(e:MouseEvent):void
{
// Unsubscribe the MOUSE_UP event handler.
//stage.removeEventListener(MouseEvent.MOUSE_UP, onUp);
// Stop the dragging process.
theObject.stopDrag();
// Let's assume there could be more than a single collision.
// We need to figure the one target that is closest.
var theTarget:DisplayObject;
var theDistance:int = 100000;
// Store the dragged object position so we can
// measure distances to the valid collisions, if any.
var thePlace:Point = theObject.localToGlobal(new Point);
// Now, the magic. Lets browse through the
// valid targets and see if there's a collision.
for each (var aTarget:DisplayObject in theValids[theObject])
{
if (theObject.hitTestObject(aTarget))
{
// Let's see if the current collision is closer
// to the dragged object, than the previous one
// (if any, that's what initial 100000 for).
var aPlace:Point = aTarget.localToGlobal(new Point);
var aDistance:int = Point.distance(aPlace, thePlace);
if (aDistance < theDistance)
{
theTarget = aTarget;
theDistance = aDistance;
}
}
}
// If there's at least one collision,
// this variable will not be empty.
if (theTarget)
{
// Make the object non-interactive.
// theObject.removeEventListener(MouseEvent.MOUSE_DOWN, onDown);
// theObject.mouseEnabled = false;
// theObject.buttonMode = false;
// Glue the dragged object to the center of the target.
theObject.x = theTarget.x;
theObject.y = theTarget.y;
}
else
{
// If we're here, that means there was no valid collisions,
// lets return the object to its designated place.
theObject.x = theOrigin.x;
theObject.y = theOrigin.y;
}
if(slot0==true){
if (theObject.hitTestObject(target0))
{
theObject.x = theOrigin.x;
theObject.y = theOrigin.y;
}
}
if(slot1==true){
if (theObject.hitTestObject(target1))
{
theObject.x = theOrigin.x;
theObject.y = theOrigin.y;
}
}
if(slot2==true){
if (theObject.hitTestObject(target2))
{
theObject.x = theOrigin.x;
theObject.y = theOrigin.y;
}
}
// Clean-up. Remove the reference, the object is no longer
// being dragged, so you won't need to keep it.
//theObject = null;
}
Only setslot3 works setslot 1&2 dont work and this happens anytime i put more than one of this specific code.

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

How to register hitTest only if Mouse Down is true?

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.

Repeat a Function when it meets the condition

I have a question about action script 3.
I am making a game, the basic rule of the game is:
an object falls from the top
the hero(user) has to avoid the object
if the object hits the ground or the hero: the hero dies or the object falls again from the top.
I am using the add child method for the object, and timer function for the fall.
the problem is:
when the object hits the ground, the function does not loop. it ends just like that. so there wont be any falling objects anymore.
please help me. thanks :)
stage.addEventListener(Event.ENTER_FRAME, addfire1);
function addfire1(e:Event):void
{
if (api1==false)//if object is not on the stage
{
randomx = randomRange();//generate random X
addChild(api);
api.x = randomx;//set x
api1 = true;//object is now on stage
}
if (api.hitTestObject(hero) || api.hitTestObject(ground))
{
falltimer.stop();
//stop timer;
falltimer.reset();
//reset fall Count to zero ;
removeChild(api);//object removed
api1=false;
}
}
function firefall(Event:TimerEvent):void
{
if (api1)
{
api.y += 20;//set speed y
}
}
Just separate the two cases: hero and floor.
if (api.hitTestObject(hero))
{
falltimer.stop();
//stop timer;
falltimer.reset();
//reset fall Count to zero ;
removeChild(api);//object removed
api1=false;
}
else if (api.hitTestObject(ground))
{
//reset object's position
api.y = -20;
}
Assuming there is only one object falling at the time I'd not remove the object but instead just move it to the original y position and a new x position. And instead of having the timer i'd create an update-function that runs every time you enter a frame.
stage.addEventListener(Event.ENTER_FRAME, update);
private function update(e:Event):void
{
if(!api1) //if object is not on the stage (same as api1==false)
{
addfire1();
}
if(api1) //if object if on stage (same as api1==true)
{
firefall();
}
}
private function addfire1(e:Event):void
{
randomx = randomRange();//generate random X
addChild(api);
api.x = randomx;//set x
api1 = true;//object is now on stage
if (api.hitTestObject(hero))
{
hitHero(); //execute what should happen to the hero when the he is hit
resetFire();
}
else if(api.hitTestObject(ground))
{
resetFire();
}
}
private function firefall(Event:TimerEvent):void
{
api.y += 20;//set speed y
}
private function resetFire():void
{
api.x = randomRange();
api.y = 0;
}
If you write it like this you don't have to mix stuff up. Try to keep everything seperated, one function does one thing. If you are making a big game try to seperate everything in classes.
Hopefully this fixes the problem and makes it easier for you to finish the game :)

How to hitTestObject MC items of an array with a MC target?

I have items in an array, each is a movieclip, which play at delayed intervals of 1 second and a target movieclip (carTarget) controlled by arrow keys where the aim is to avoid a collision with the array items (obstacleArray[i]). I have been attempting to create a hitTestObject statement to notify when there is a collision. However I have received errors such as parameter hitTestObject most be non-null and cannot figure out how to resolve it.
//obstacles
function randomSort(a:*, b:*):Number
{
if (Math.random() < 0.5) return -1;
else return 1;
}
var obstacleArray:Array = [obstacleCar,obstacleCar2,obstacleCar3];
obstacleArray.sort(randomSort);
trace(obstacleArray);
trace(obstacleArray.length);
//OBSTACLE START DELAY
var timerPlay:Timer = new Timer(1000,1);
timerPlay.addEventListener(TimerEvent.TIMER, ontimerPlay);
timerPlay.start();
var i:int = 0;
var timerDelay:Timer = new Timer(1000, 3);
function ontimerPlay(evt:TimerEvent):void{
obstacleArray[i].addEventListener(Event.ENTER_FRAME,checkHitTest);
//FOR EACH OBSTACLE
timerDelay.addEventListener(TimerEvent.TIMER, ontimerDelay);
timerDelay.start();
}
function checkHitTest(event:Event):void{
if(carTarget.hitTestObject(obstacleArray[i]))
{
trace("HIT!");
}
}
function ontimerDelay(evt:TimerEvent):void{
obstacleArray[i].play();
trace(obstacleArray[i]);
i++;
}
Most likely, the problem is in this block of code:
function checkHitTest(event:Event):void{
if(carTarget.hitTestObject(obstacleArray[i]))
{
trace("HIT!");
}
}
After i changes with the timer calls, then obstacleArray[i] will be null or worse - another object other than the one you need to check.
You should change this to:
function checkHitTest(event:Event):void{
if(carTarget.hitTestObject(event.target))
{
trace("HIT!");
}
}
event.target will get the current obstacle calling the checkHitTest from Enterframe event
It is very likely that hitTestObject does not exist at the moment you're trying to use it. From the code you shared, I do not see where you create it in your code. So, assuming carTarget is an object on the stage and that it has the property or object hitTestObject initialized when you want to use it -assuming those things, then you need to make sure that when this code is running, that object still exists.