as3 draw with mouse and erase lines when hitTestObject is reached - actionscript-3

I am trying to create a simple hitTestObject and see the lines that are being drawing on screen hit with a Ball. If it does it will erase all lines and start fresh. I am very close. The checkIt() function is where the code needs to happen and its clearing the lines but not making a new shape.
How can I erase the lines the mouse drew and start fresh when a hit test is reached?
var myshape:Shape;
myshape = new Shape();
myshape.graphics.lineStyle(12, 0xC807DE);
var alreadyDrawn:Shape;
alreadyDrawn = new Shape();
stage.addEventListener(MouseEvent.MOUSE_DOWN, activateDraw);
function activateDraw(event:MouseEvent):void
{
myshape.graphics.moveTo(mouseX,mouseY);
addChild(myshape);
stage.addEventListener(MouseEvent.MOUSE_MOVE, lineDraw);
function lineDraw(myevent:MouseEvent):void
{
stage.addEventListener(MouseEvent.MOUSE_UP, stopDraw);
function stopDraw(endevent:MouseEvent):void
{
alreadyDrawn = myshape;
stage.removeEventListener(MouseEvent.MOUSE_MOVE, lineDraw);
}
myshape.graphics.lineTo(mouseX,mouseY);
myevent.updateAfterEvent();
checkIt();
}
}
function checkIt()
{
if (alreadyDrawn.hitTestObject(Ball) == true)
{
trace("Hit The Balls");
myshape.graphics.clear();
myshape.graphics.lineStyle(12, 0xC807DE);
}
}

You actually have ONE shape instead of two, because once you do alreadyDrawn=myshape you lose whatever object you attached there. You should instead do alreadyDrawn.graphics.copyFrom(myshape.graphics); this will copy all the strokes from argument to object without losing the other instance.
Next, you seemingly need to also stop drawing if you've detected a hit vs alreadyDrawn, for this, call stopDraw(null); from checkIt().
And next, you are not clearing all the listeners off stage while adding more and more. See, if you draw several lines in quick succession (press-drag-release), the stopDraw listener is added multiple times and not removed even once. To do this, use removeEventListener(MouseEvent.MOUSE_UP, stopDraw); inside stopDraw function and move the adding line into activateDraw, because there are multiple "mouse move" events while you drag the mouse, and another listener is added each time you process the event.
And finally, PLEASE don't nest functions without actual need!
The fixed code should be this:
var myshape:Shape;
myshape = new Shape();
myshape.graphics.lineStyle(12, 0xC807DE);
var alreadyDrawn:Shape;
alreadyDrawn = new Shape();
stage.addEventListener(MouseEvent.MOUSE_DOWN, activateDraw);
function activateDraw(event:MouseEvent):void
{
myshape.graphics.moveTo(mouseX,mouseY);
addChild(myshape);
stage.addEventListener(MouseEvent.MOUSE_MOVE, lineDraw);
stage.addEventListener(MouseEvent.MOUSE_UP, stopDraw);
}
function lineDraw(event:MouseEvent):void
{
myshape.graphics.lineTo(mouseX,mouseY);
event.updateAfterEvent();
checkIt();
}
function stopDraw(event:MouseEvent):void
{
alreadyDrawn.graphics.copyFrom(myshape.graphics);
stage.removeEventListener(MouseEvent.MOUSE_MOVE, lineDraw);
stage.removeEventListener(MouseEvent.MOUSE_UP, stopDraw);
}
function checkIt()
{
if (alreadyDrawn.hitTestObject(Ball) == true)
{
trace("Hit The Balls");
myshape.graphics.clear();
myshape.graphics.lineStyle(12, 0xC807DE);
alreadyDrawn.graphics.clear(); // clear this too
stopDraw(null); // stop active draw, if any
}
}
copyFrom() manual reference

Interesting question~
I think the problem may be you forgot to moveTo after clear.
Here is my fix:
Pass mouseX, mouseY to checkIt.
checkIt( mouseX, mouseY );
function checkIt( mouseX:Number, mouseY:Number ):void
{
if (alreadyDrawn.hitTestObject(Ball))//you don't need == true here
{
trace("Hit The Balls");
myshape.graphics.clear();
myshape.graphics.lineStyle(12, 0xC807DE);
myshape.graphics.moveTo(mouseX,mouseY);
}
}

Related

How can I use "ease" for smoother dragging?

I have dragging mc called "box" but the dragging is not smooth at all.
So how can I make it smoother by using "ease" var.
I'm trying to use "/ease" anywhere but not work.
var topY:int = stage.stageHeight - box.height;
var botY:int = 0;
box.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
box.addEventListener(MouseEvent.MOUSE_UP, onUp);
var constrainY:Rectangle = new Rectangle(box.x, topY ,0, box.y+ (box.height-stage.stageHeight) );
var dragxy:String = "";
function onDown(e:MouseEvent):void
{
dragxy = mouseX + "_" + mouseY;
e.currentTarget.startDrag(false, constrainY);
removeEventListener(MouseEvent.MOUSE_DOWN, onDown);
addEventListener(MouseEvent.MOUSE_UP, onUp);
}
function onUp(e:MouseEvent):void
{
e.currentTarget.stopDrag();
addEventListener(MouseEvent.MOUSE_DOWN, onDown);
removeEventListener(MouseEvent.MOUSE_UP, onUp);
}
a simple example of an easing function would be:
private function easeTo(obj:MovieClip,currentGoalx:Number,currentGoaly:Number,easeFac:Number):void{
obj.x += (currentGoalx-obj.x)/easeFac;
obj.y += (currentGoaly-obj.y)/easeFac;
}
first you will need a timer or enter frame loop such as this added to the stage:
addEventListener(Event.ENTER_FRAME,enterFrame);
then in your enterFrame function do this:
private function enterFrame(e:Event):void{
easeTo(box,stage.mouseX,stage.mouseY,3);
}
an easeFac of 3 is a slower and smoother ease-in than an easeFac of 2, and 4 is even smoother and buttery-er.
or something like that. The idea is that the obj goes half the distance to the goal on each function call. So if this function is getting called on every frame, you'll get a smooth "ease in" effect. If you set currentGoal as the mouse position, then the obj will follow the mouse wherever it goes, but not in a 1:1 matching, rather it will trail behind and smoothly catch up.

Actionscript hitTest drawing

I've gotten actions on a frame, what I'm trying to do is have a hitTest that triggers gotoAndStop(<lose frame>) when the shape I am drawing collides with the touchTest. The only issue I'm having is I cannot get the hitTest to register directly when the line hits it, it only registers after the next click event. The other issue I'm encountering is a hit box on the touchTest is many times larger than the actual image of the symbol.
var myshape:Shape;
myshape = new Shape();
myshape.graphics.lineStyle(5, 0xC807DE);
var alreadyDrawn:Shape;
alreadyDrawn = new Shape();
stage.addEventListener(MouseEvent.MOUSE_DOWN, activateDraw);
function activateDraw(event:MouseEvent):void
{
myshape.graphics.moveTo(mouseX,mouseY);
addChild(myshape);
stage.addEventListener(MouseEvent.MOUSE_MOVE, lineDraw);
stage.addEventListener(MouseEvent.MOUSE_UP, stopDraw);
}
function lineDraw(event:MouseEvent):void
{
myshape.graphics.lineTo(mouseX,mouseY);
checkIt();
}
function stopDraw(event:MouseEvent):void
{
alreadyDrawn.graphics.copyFrom(myshape.graphics);
stage.removeEventListener(MouseEvent.MOUSE_MOVE, lineDraw);
stage.removeEventListener(MouseEvent.MOUSE_UP, stopDraw);
}
function checkIt()
{
if (alreadyDrawn.hitTestObject(touchTest) == true)
{
trace("wall");
myshape.graphics.clear();
myshape.graphics.lineStyle(5, 0xC807DE);
alreadyDrawn.graphics.clear(); // clear this too
stopDraw(null); // stop active draw, if any
}
}
it only registers after the next click event
This is because the object you are testing the collision against alreadyDrawn doesn't have a collision area yet. You create the new shape, add your listeners, and test your collision in your lineDraw() using the method checkIt(), but the shape doesn't have a collision area until your mouse up function stopDraw() where it does alreadyDrawn.graphics.copyFrom(myshape.graphics);
So to fix this you would have to create the graphics object earlier. The change could look something like this (at the top):
var alreadyDrawn:Shape = new Shape();
alreadyDrawn.graphics.copyFrom(myshape.graphics);
That would give a collision area to test against in checkIt()
The other issue I'm encountering is a hit box on the touchTest is many
times larger than the actual image of the symbol.
For this issue, you can access the clip or a symbol inside it and grab its bounds relative to the parent of the alreadyDrawn shape. Then you can use the bounds of both shapes to test for a collision. This will give you a more accurate collision area for testing:
function checkIt()
{
var alreadyDrawnBounds:Rectangle = alreadyDrawn.getBounds( alreadyDrawn.parent );
var testBounds:Rectangle = touchTest.someSymbolName.getBounds( alreadyDrawn.parent );
//could also try this instead:
//var alreadyDrawnBounds:Rectangle = alreadyDrawn.getBounds( touchTest.parent );
//var testBounds:Rectangle = touchTest.getBounds( touchTest );
if ( alreadyDrawnBounds.intersects( testBounds ) ) {
trace("wall");
myshape.graphics.clear();
myshape.graphics.lineStyle(5, 0xC807DE);
alreadyDrawn.graphics.clear(); // clear this too
stopDraw(null); // stop active draw, if any
}
}

Pivot an obect in the direction of the mouse as3

I am trying to have an object pivot in the direction of the mouse while being dragged. For example i would like the car to point in the direction it is being dragged. All I have right now is the drag and drop code.
car.addEventListener(MouseEvent.MOUSE_DOWN, pickUp);
car.addEventListener(MouseEvent.MOUSE_UP, dropIt);
function pickUp(event:MouseEvent):void {
event.target.startDrag(true);
event.target.parent.addChild(event.target);
}
function dropIt(event:MouseEvent):void {
event.target.stopDrag();
}
Once you start dragging the object, you can start listening for MouseMove events. As you do so, you compare the current mouse position to the previous mouse position and determine the angle between the two. Then, use that angle as the rotation for the object:
car.addEventListener(MouseEvent.MOUSE_DOWN, pickUp);
car.addEventListener(MouseEvent.MOUSE_UP, dropIt);
var oldPoint:Point;
function pickUp(event:MouseEvent):void
{
event.target.startDrag(true);
event.target.parent.addChild(event.target);
oldPoint = new Point(mouseX, mouseY);
// start listening to mouse move events
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
}
function dropIt(event:MouseEvent):void
{
oldPoint = null;
event.target.stopDrag();
// stop listening to mouse move events
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
}
function onMouseMove(event:MouseEvent):void
{
if(!oldPoint)
{
return;
}
var newPoint:Point = new Point(mouseX, mouseY);
// get the angle between the two points and set it as the rotation
car.rotation = point_direction(oldPoint, newPoint);
}
function point_direction(p1:Point, p2:Point):Number
{
return Math.atan2(p2.y - p1.y, p2.x - p1.x) * (180 / Math.PI);
}
NOTE: (Per #Vesper's comment) When the mouse is going left to right, this will produce a rotation of 0. This means that you'll want the graphics of your car object facing towards the right.

AS3 Works but I get a ReferenceError: Error #1069 Property startDrag not found

I am trying to make a simple project when you click a button a draggable MovieClip is added to the stag and when you click it releases the MovieClip to the X/Y where you clicked, you can then pickup the MovieClip and drag it into a bin (MovieClip) where it destroys itself. The code is working great I can make multiple Movieclips with the button and they are all destroyed when I drag them in the bin however I don't like having "Error Codes".
import flash.events.MouseEvent;
var rubbish:my_mc = new my_mc();
btntest.addEventListener(MouseEvent.CLICK, makeRubbish);
function makeRubbish (event:MouseEvent):void {
addChild(rubbish);
rubbish.x = mouseX - 10;
rubbish.y = mouseY - 10;
rubbish.width = 50;
this.addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
rubbish.buttonMode = true;
}
function stopDragging (event:MouseEvent):void {
rubbish.stopDrag()
event.target.addEventListener(MouseEvent.CLICK, startDragging);
rubbish.buttonMode = true;
if (event.target.hitTestObject(bin))
{
trace("hit");
event.target.name = "rubbish";
removeChild(getChildByName("rubbish"));
}
}
function startDragging (event:MouseEvent):void {
event.target.startDrag();
this.addEventListener(MouseEvent.CLICK, stopDragging);
}
Some Pointers:
The target property of an Event is not always what it seems. It actually refers to the current phase in the event bubbling process. Try using the currentTarget property.
I would also recommend tying the stopDragging method to the stage, as sometimes your mouse won't be over the drag as you're clicking.
I would use the MOUSE_UP event as opposed to a CLICK for standard dragging behaviour.
When dragging, keep a global reference to the drag in order to call the stopDrag method on the correct object.
Try This:
import flash.events.MouseEvent;
var rubbish:my_mc = new my_mc();
var dragging:my_mc;
btntest.addEventListener(MouseEvent.CLICK, makeRubbish);
function makeRubbish (event:MouseEvent):void {
addChild(rubbish);
rubbish.x = mouseX - 10;
rubbish.y = mouseY - 10;
rubbish.width = 50;
rubbish.addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
rubbish.buttonMode = true;
}
function stopDragging (event:MouseEvent):void {
this.stage.removeEventListener(MouseEvent.MOUSE_UP, stopDragging);
if(dragging !== null){
dragging.stopDrag();
if (event.currentTarget.hitTestObject(bin)){
removeChild(dragging);
}
dragging = null;
}
}
function startDragging (event:MouseEvent):void {
dragging = event.currentTarget as my_mc;
dragging.startDrag();
this.stage.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
}

How to get this code to show movieclip correctly?

I have the following code and it works fine, except I want it to play the Explosion movieclip in the library when an EnemyShip disappears but I only want it to play once and then disappear but not quite sure how to do it (I've tried a few things and it either makes the explosion animation loop and the ships don't disappear, which I believe is as a result of putting it inside the kill function, or I get other ArgumentErrors).
var speed:Number;
var shot = new ShotSound();
var explosion = new Explosion();
this.x = 800;
this.y = Math.random() * 275 + 75;
speed = Math.random()*5 + 9;
addEventListener("enterFrame", enterFrame);
addEventListener(MouseEvent.MOUSE_DOWN, mouseShoot);
function enterFrame(e:Event)
{
this.x -= speed;
if(this.x < -100)
{
removeEventListener("enterFrame", enterFrame);
stage.removeChild(this);
}
}
function kill()
{
stage.addChild(this);
explosion.x = this.x;
explosion.y = this.y;
removeEventListener("enterFrame", enterFrame);
stage.removeChild(this);
shot.play();
}
function mouseShoot(event:MouseEvent)
{
kill();
}
Thank for for any help I may receive.
You need some script to fire when the Explosion reaches it's final frame. A few ways you can do this:
1.
Dispatch an event on the last frame of your explosion animation. eg. this.dispatchEvent(new Event(Event.COMPLETE)); then listen for that event:
var explosion = new Explosion();
addChild(explosion);
explosion.addEventListener(Event.COMPLETE, removeExplosion);
function removeExplosion(e:Event):void {
MovieClip(e.currentTarget).stop();
MovieClip(e.currentTarget).removeEventListener(Event.COMPLETE, removeExplosion);
removeChild(MovieClip(e.currentTarget));
}
2.
Have the explosion remove itself on the last frame of the animation. eg. if(this.parent) parent.removeChild(this);
3.
If you can't or don't want to modify the explosion timeline, use the undocumented AddFrameScript command:
var explosion = new Explosion();
addChild(explosion);
explosion.addFrameScript(explosion.totalFrames-1, function():void {
explosion.stop();
if(explosion.parent){
explosion.parent.removeChild(explosion);
}
});
Here is a tip with your setup:
add a function called removeMe (or similiar) to avoid redundant code (so you call this function on kill or when the ship goes out of bounds
function removeMe(e:Event = null):void {
this.removeEventLIstener(Event.ENTER_FRAME,enterFrame);
if(this.parent){
this.parent.removeChild(this);
}
//any other cleanup that's required
}
Next, an updated kill function:
function kill(){
var explosion = new Explosion();
explosion.addEventListener(Event.COMPLETE, removeMe); //you need to dispatch this event on the last frame of the explosion timeline as shown below
addChild(explosion); //add it to the ship so it stays with it as the ship moves
//not sure what shot.play() does and if that belongs here.
}
//on the last frame of your explosion timeline:
stop();
dispatchEvent(new Event(Event.COMPLETE));
For starters, you're never adding explosion to the display list.
Change
var explosion = new Explosion();
stage.addChild(this);
to
var explosion = new Explosion();
stage.addChild(explosion);
Once you've done that, you'll want to add a listener to explosion to find out when it has finished playing:
explosion.addEventListener(Event.ENTER_FRAME, onExplosionProgress);
function onExplosionProgress(e:Event):void
{
if(explosion.currentFrame == explosion.totalFrames)
{
// explosion has reached end of its timeline
explosion.removeEventListener(Event.ENTER_FRAME, onExplosionProgress);
explosion.stop();
stage.removeChild(explosion);
}
}