Flash AS3 custome Dragging using MOUSE_MOVE event - actionscript-3

I am creating grid based map renderer in AS3 which loads required chunks of PNG images and render them in a container. I've applied scrolling/dragging logic in this app using MOUSE_MOVE event handler. What I do is,
I register bDragging flag in MOUSE_DOWN and position of mouse,
In every MOUSE_MOVE, I check displacement of mouse and move main map accordingly
Unregister bDragging flag in MOUSE_UP and set position to NaN.
My problem is dragging is quite jerky/shaky.
Any suggestion would be appreciated. following is sample code I'm using.
function onMouseDown(e:MouseEvent):void
{
m_bDragging = true;
m_ptPrevPoint = new Point(e.stageX, e.stageY);
}
function onMouseUp(e:MouseEvent):void
{
m_bDragging = false;
m_ptPrevPoint = null;
}
function onMouseMove(e:MouseEvent):void
{
if(!m_bDragging || null == m_ptPrevPoint)
return;
var nDiffX:Number = int(e.stageX - m_ptPrevPoint.x);
var nDiffY:Number = int(e.stageY - m_ptPrevPoint.y);
//Make movement smoother
//nDiffX = nDiffX * 4) / 4;
//nDiffY = nDiffY * 4) / 4;
if(nDiffX != 0 || nDiffY != 0)
{
trace("X : " + nDiffX + ", Y : " + nDiffY + ", points-Old " + m_ptPrevPoint + ", New " + new Point(e.stageX, e.stageY) );
m_oCircle.x += nDiffX;
m_oCircle.y += nDiffY;
m_ptPrevPoint = new Point(e.stageX, e.stageY);
e.updateAfterEvent();
}
else
trace("not moved - X : " + nDiffX + ", Y : " + nDiffY+ ", points-Old " + m_ptPrevPoint + ", New " + new Point(e.stageX, e.stageY)) }
Please check FLA file here...made in Flash CS3. Please note if mouse is on circle it will jerk like hell but if you drag it from outside of circle, It will go smoother!
Sample FLA

Thanks guys, I figured out the problem!
actually this might be some bug of adobe, I was strangely getting some odd coordinates and that was the reason movement was not smooth. I converted stageX and stageY using localToGlobal and everything became smoother!!!
now, I dont know why should I be needed to convert stageX/stageY to global. :) my brief changes are as below.
var curPt:Point = DisplayObject(e.currentTarget).localToGlobal(new Point(e.stageX, e.stageY));
var nDiffX:Number = int(curPt.x - m_ptPrevPoint.x);
var nDiffY:Number = int(curPt.y - m_ptPrevPoint.y);
Thanks guys, every help appreciated.

Does my answer in this one help?
Problems replicating drag-and-drop with mouse events
Basically, when you move the mouse, the coordinates of the mouse can sometimes be from a different context that expected, depending on the target of the event. You need to translate the coordinates.

Related

AS3 Add Child at the position of another Child

Hey I have got some cavemen that when they build huts I want the huts to be added at the coords of the caveman I last clicked on which requires the ability to know exactly what caveman the user clicked on/tapped. Here is the code for spawning in the cavemen:
//////////////////////
///Starting Cavemen///
//////////////////////
var cavemanVar:Array = new Array(50);
for (var i:Number = 0; i < 50; i++)
{
cavemanVar[i] = 0;
}
var foo:MovieClip = new btn_caveman();
cavemanVar[0] = addChildAt(foo, 7);
var bar:MovieClip = new btn_caveman();
cavemanVar[1] = addChildAt(bar, 7);
cavemanVar[0].x = 335.50;
cavemanVar[0].y = 316.55;
cavemanVar[1].x = 335.50;
cavemanVar[1].y = 369.5;
stage.addEventListener(Event.ENTER_FRAME, example2);
function example2 (evt:Event) {
for (i = 0; i < 50; i++)
{
if (cavemanVar[i] != 0)
{
cavemanVar[i].addEventListener(TouchEvent.TOUCH_TAP, btn_cavemanMenu3);
}
}
}
function option1CavemenSpawn():void {
trace("using option 1")
actions += 1;
score += 5;
remaningActions += 1;
updateTextBox();
var foo6:MovieClip = new btn_caveman();
cavemanVar[2] = addChildAt(foo6, 7);
cavemanVar[2].x = 352.10;
cavemanVar[2].y = 260.80 + Math.random() * (392.40 - 260.80);
}
Any help would be great, I have tried using 'cavemanVar', 'cavemanVar[2]', 'cavemanVar[i]' and nothing is what I want it to be.
Hope I explained it properly it's a tricky thing to explain. I also have a move caveman feature I want to implement which would select the last clicked/tapped caveman and move it where the user clicks/taps so can any of this be done and if so how?
EDIT:
function btn_cavemanMenu3(event:TouchEvent):void {
btn_cavemanM.gotoAndStop(2);
trace('2');
allowBuildHut();
cancelTapCaveman();
allowTapCavemanClose();
if (remaningActions <= 2 || stone <= 29) {
cancelBuildHut();
}
}
Do you mean something like,
cavemanVar[i].addEventListener(TouchEvent.TOUCH_TAP, onTap);
function onTap(e:TouchEvent):void {
var caveman:btn_caveman = e.currentTarget as btn_caveman;
trace("caveman tapped --- (" + caveman.x + ", " + caveman.y + ")");
}
Okay, some things I noticed right away:
Attaching eventListeners with each frame, again and again. This will make your game slow if you make your game bigger. I'm not sure, but I think multiple event listeners with the same arguments don't stack, so it's not THAT bad.
Missing a return type for example2.
But aside from that, I think the reason that what you're trying to do doesn't work is because you're not referencing the selected caveman in your btn_cavemanMenu3 function. Via event.currentTarget you can reference the tapped caveman.
Currently, your question name and your question description conflict. What exactly do you wish to achieve? What is working and what isn't working right now?

transform a mouse event (drag) to a touch screen event

I'm trying to transform my mouse event to a swipe event (for touch screen), but after hours and hours, I can't figure out how to do it.
Here's my code :
public function DraggedItem(stageRef:Stage, grabbedItem:Object){
this.stageRef = stageRef;
toolbar = Engine.toolbar;
usableItems = Engine.usableItems;
inv = Engine.inv;
puzzle = Engine.puzzle;
player = Engine.player;
linesData = Engine.linesData;
inv.draggingItem = true;
Mouse.hide();
itemRef = getDefinitionByName(grabbedItem.displayName.toLowerCase()+"Proper");
draggedItem = new itemRef;
stageRef.addChild(draggedItem);
draggedItem.displayName = grabbedItem.displayName;
if (grabbedItem.lookTag)
draggedItem.lookTag = grabbedItem.lookTag;
draggedItem.x = mouseX + x;
draggedItem.y = mouseY + y;
draggedItem.scaleX = itemScale;
draggedItem.scaleY = itemScale;
stageRef.addEventListener(MouseEvent.MOUSE_MOVE, dragItem, false, 0, true);
stageRef.addEventListener(Event.ENTER_FRAME, itemHitTest, false, 0, true);
draggedItem.addEventListener(MouseEvent.CLICK, itemClick, false, 0, true);
}
private function dragItem(e:MouseEvent):void{
draggedItem.x = mouseX + x;
draggedItem.y = mouseY + y;
}
On my computer, when I click on my inventory and the select a item, I can drag it where I want on the screen (the item become the mouse, and the mouse is hide).
So I'm trying to transform it for touch screen.
I've tried to using "event.stageX" instead of "mouseX", but it didn't work.
I've tried to replace mouseEvent by TransformGestureEvent, but it didn't work.
And When I click on my item in the inventory, the item stay stuck in the middle corner of the screen and I can't move it. (it's happening just when I'm exporting with adobe Air for Android, if I'm exporting in swf it's working just fine).
Do you know how I can do it ?
Here's a video of the problem : uploaded.net/file/lkwqsgm7
Thank you very much !
You can try using the built in gestures of flash.
Multitouch.inputMode = MultitouchInputMode.GESTURE; //you need to enable gestures
stageRef.addEventListener(TransformGestureEvent.GESTURE_SWIPE , onSwipe);
function onSwipe (e:TransformGestureEvent):void{
if (e.offsetX == 1) {
//swiped right
draggedItem.x += someValue;
}else{
//swiped left
draggedItem.x -= someValue;
}
}
Keep in mind, the swipe event is only for general swiping, if you're actually just dragging an object from position A to position B, this is not a swipe, but well...a drag. I'm not clear on what you're actually trying to accomplish.
If you do want to do multitouch, just change your MouseEvents to TouchEvents. Mouse_Down is equivalent to TOUCH_BEGIN and mouse up is equivalent to TOUCH_END.

display lines in AS3

I am baffled by this function, which is called prior to this with parameters 22 and 58 for xVal ad yVal respectively. It doesn't display anything when the swf is compiled and tested, and it's error free. The code is in the document class:
private function mLine(xVal : int, yVal : int) {
var rCol = 0x0000FF;
var incr = Math.round((Math.random() * 20) + 8);
lns.push(new Shape());
var i = lns.length - 1;
this.addChild(lns[i]);
lns[i].graphics.moveTo(xVal, yVal);
lns[i].graphics.lineStyle(10, rCol);
lns[i].graphics.lineTo(xVal, yVal + 20);
lns[i].name = incr;
trace("lns[" + i + "] x is " + lns[i].x); // outputs 'lns[0] x is 0'
trace("xVal is " + xVal); // outputs 'xVal is 22'
trace("yVal is " + yVal); //outputs 'yVal is 58'
trace(stage.contains(lns[i])); // outputs 'true'
}
Assuming you have declared private var lns = []; somewhere, it draws a blue line (20px straight down from the given position).
It doesn't display anything
That means you probably don't have an object of that class on the stage. In your document class, you should use addChild to display an instance of the class containing mLine. mLine needs to be called somehow obviously. You could do this in the class' constructor, but you'd need to remove the last trace statement to avoid a null pointer error, because stage would be null then.
Edit: Missed that you said it is in the Document class. So, try and see if drawing anything else works. The problem doesn't seem to be with this function.
Your code seems like it should work. I have rewrote it to conform better to ActionScript 3 best practices
private function drawLine(xVal:int, yVal:int):void
{
var lineColor:uint = 0x0000FF;
var lineShape:Shape = new Shape();
//lineShape.name = String(Math.round((Math.random() * 20) + 8));
lineShape.graphics.lineStyle(10, lineColor);
lineShape.graphics.moveTo(xVal, yVal);
lineShape.graphics.lineTo(xVal, yVal + 20);
addChild(lineShape);
lines.push(lineShape);
}
The x and y properties of your shape will both be zero because you never set them. you are just drawing lines inside the shape at the xVal and yVal. You could do the same thing like this:
private function mLine(xVal:int, yVal:int)
{
var lineColor:uint = 0x0000FF;
var lineShape:Shape = new Shape();
//lineShape.name = String(Math.round((Math.random() * 20) + 8));
lineShape.graphics.lineStyle(10, lineColor);
lineShape.graphics.moveTo(0, 0);
lineShape.graphics.lineTo(0, 20);
lineShape.x = xVal;
lineShape.y = yVal;
addChild(lineShape);
lines.push(lineShape);
}
Not sure why its not showing up at all for you though.

Switching of depths causes duplication of MC

this is a repost of a previous question with more info this time.
The files: 2shared.com/file/hRKhEiqh/Script_Test.html and 2shared.com/video/UZNmqzXt/a_anconeus.html
This issue is reproducible on my machine with a new .fla project in Actionscript 3.0 in Flash Professional CS5. It's an edit of my original question with more information.
I'm working on a project to load external SWF's and search through instance names for matching keywords, namely 'drag' and 'drop' to identify movieclip matches, then attach event listeners to these MC's which contain the D&D event listeners and code.
The specific problem is the switching of depths for Movieclips nested in dynamically loaded external SWF files.
Where I am having trouble is the specific commands:
swapChildrenAt, setChildIndex, swapChildren, removeChild/addChild. I've tried all four with the same problem of duplication. Let me explain.
When a draggable MC is clicked, it is moved to the top index of the dynamically loaded SWF so it's visible above everything else in that SWF. The problem is that trying any of these commands all duplicate the MC. What happens is this:
MOUSE_DOWN event fires on MC:
Index of target MC is recorded as '2', the index we will switch to is '20' (maximum index of the SWF)
setChildIndex is called on the target MC parent: mc.parent.setChildIndex(mc, (mc.parent.numChildren-1))
MC moves to index 20 then another instance of the MC is created at the layer it was located previously (index 2), this happens after the MOUSE_DOWN event finishes, I'm not sure exactly when.
This duplicate has been confirmed using the EVENT.ADDED_TO_STAGE listener attached to the stage to catch every object of MovieClip types that is added to the stage. I've inspected the SWF to confirm there's no duplicate MovieClips, the traces also confirm this.
Similar posts mention the same duplication problem but no one has a solution that I've found will work in my case.
http://www.kirupa.com/forum/showthread.php?t=359452
http://board.flashkit.com/board/showthread.php?t=775200
http://forums.adobe.com/thread/199983
As a last note, if I comment out the index swap statement, the code works perfectly with the MC simply staying on its lower z-index instead of being on top. Unfortunately that's not going to work as a solution since I'm not building the external SWF's.
To duplicate this behavior, you need an external AS3 SWF with two at least two MC's with instance names "drag01" and "drop01" or something that matches the keywords 'drag' and 'drop'. Point the String variable 'SWF' to that file and you should see the duplication problem.
Thanks and regards
Cameron
Edit: new trimmed code, copied into a blank AS 3.0 file with the same errors.
import flash.net.URLRequest;
import flash.display.Loader;
import flash.events.Event;
import flash.events.ProgressEvent;
import flash.display.MovieClip;
import flash.display.DisplayObject;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.net.URLLoader;
var swf:String = "a_anconeus.swf";
loadSWF(swf);
stage.addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStageReport, true);
stage.addEventListener ( Event.ADDED_TO_STAGE , onAddedToStageReport , true ) ;
function onRemovedFromStageReport (evt:Event)
{
{
trace("REMOVED: " + evt.target.name + " at depth: " + evt.target.parent.getChildIndex(evt.target));
}
}
function onAddedToStageReport (evt:Event)
{
{
trace("ADDED: " + evt.target.name + " at depth: " + evt.target.parent.getChildIndex(evt.target));
}
}
function onDragMouseDown(event:MouseEvent):void
{
var drag:MovieClip = MovieClip(event.target);
var topPosition:uint = drag.parent.numChildren - 1;
trace("click: "+drag.name +", ontarget = "+drag.ontarget + ", current z: " + drag.parent.getChildIndex(drag) + " new z: " + topPosition);
//drag.parent.setChildIndex(drag, topPosition);
var indexToDelete:int = drag.parent.getChildIndex(drag);
//this.parent.removeChild(this);
trace("index to delete: " + indexToDelete +", what's there: " + drag.parent.getChildAt(indexToDelete).name);
drag.startDrag();
//drag.parent.setChildIndex(drag, drag.parent.numChildren-1); //set child depth to top
trace("after change, what's there: " + drag.parent.getChildAt(indexToDelete).name);
//drag.parent.swapChildren(drag, drag.parent.getChildAt(drag.parent.numChildren -1));
}
function onDragMouseUp(event:MouseEvent):void
{
trace("mouse up: "+event.target.name + ", index: " +event.target.parent.getChildIndex(event.target));
var drag:MovieClip = MovieClip(event.target);
drag.stopDrag(); //Movieclips have simple drag methods
}
/*function dragEnterFrameHandler(event:Event):void {
var drag:MovieClip = MovieClip(event.target);
if (drag.mousedown == false)
{
if (drag.onTarget == true)
{
//send it to the drop X/Y
drag.x -= (drag.x - drag.dropon.x)/5;
drag.y -= (drag.y - drag.dropon.y)/5;
}
else if (drag.onTarget == false)
{
drag.x -= (drag.x - drag.homeX)/5;
drag.y -= (drag.y - drag.homeY)/5;
}
}
}*/
function loadSWF(filepath:String)
{
trace("calling loader");
var loader:Loader = new Loader();
var url:String = filepath;
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onCompleteHandler);
loader.load(new URLRequest(url));
}
function onAddedToStage ( evt:Event )
{
trace("calling onAddedToStage");
/*if (evt.target is MovieClip) //filter only movieclips
{
var mc:MovieClip = MovieClip(evt.target);
// trace ( "onAddedToStage, evt: " + mc.name + ", " +(mc.parent.numChildren-1) ) ;
if (String(mc.name).indexOf("drag") != -1)
{
var dropString:String = String(mc.name).split("drag").join("drop");
if (mc.parent.getChildByName(dropString))
{
trace("our droptarget: " + mc.dropon);
mc.addEventListener(MouseEvent.MOUSE_DOWN, onDragMouseDown);
mc.addEventListener(MouseEvent.MOUSE_UP, onDragMouseUp);
mc.buttonMode = true;
}
}
}*/
}
function onCompleteHandler(loadEvent:Event)
{
trace("load complete");
var swf:MovieClip = MovieClip(loadEvent.currentTarget.content);
//swf.addEventListener ( Event.ADDED_TO_STAGE , onAddedToStage, true ) ;
//trace("event attached, adding child");
trace("child add started");
addChild(swf);
trace("child add finished");
var children:Number = (swf.numChildren-1);
trace("children: " + children);
for (var i:Number = 0; i <= children; i++)
{
trace("LOOP STARTS HERE");
if (swf.getChildAt(i) is MovieClip)
{
//trace("MC: yes");
var mc:MovieClip = MovieClip(swf.getChildAt(i));
trace("name: " + mc.name);
if (String(mc.name).indexOf("drag") != -1)
{
var dropString:String = String(mc.name).split("drag").join("drop");
if (swf.getChildByName(dropString))
{
trace("removing: " + mc.name);
children--;
swf.removeChild(mc);
trace("removed");
//swf.addChild(mc);
/*mc.addEventListener(MouseEvent.MOUSE_DOWN, onDragMouseDown);
mc.addEventListener(MouseEvent.MOUSE_UP, onDragMouseUp);
mc.buttonMode = true;*/
}
}
}
}
trace("finish function");
}
mystery solved :)
looking at your swf I can see that it has two frames, so as soon as you start dragging, the next frame renders and the object comes back.
to test this I added swf.stop(); to yout onCompleteHandler
I can't open your sample files because I only have CS3, but I can tell you what I think is the most likely cause. When you reparent those clips and then later find they've duplicated, is there any kind of timeline playing that happens in the meantime? Like in this sample fla?
Unintentional cloning
The problem is that when you remove a movieclip from a display list into which it was published, but that display list is in a movieclip that is playing, when the clip re-plays the old frame, or in any other way "expects" the clip to be where it was when you published the fla, it just "clones" it and puts it back... or rather, it "clones" what it knows it would have been if you hadn't moved it already. That's what I've illustrated in the fla above. I re-parent the foo clip, randomly place it somewhere onstage, then when the movieclip cycles back to frame 1 it finds that "there should be a foo here, but there's not. better make one."

JTabbedPane: Attach mouse listener to the part of the tab-select UI that doesn't contain the tabs

This is in conjunction to the question posed here:
JTabbedPane: Components before and after the tabs themselves
I want to attach a mouse listener that allows for dragging the constructed google chrome-like frame. Starting out, the initial dragging code is rather easy, and the mouse-dragging code at this Kirill-post can be used pretty much directly. I'd only want this behaviour if the user clicks and drags on the "title bar" of the frame, that is, the area where the tabs (the stick-uppers) reside. This is also easy - just change the dragging code to only accept clicks in the upper area of the JTabbedPane, the part that contains the tabs.
However, I want to reduce the grabbable area further, and only allow click-and-drag-frame in the area NOT occupied by the tabs (the stick-uppers - anyone have a better name for this GUI element?) - again quite like Google Chrome (Chrome also adds a bar above the tabs when in windowed mode, to easier get hold of the frame if many tabs are active. But Chrome do this perfect: It is possible to grab the window in the tabbed part that doesn't have tabs, and even in the small v's inbetween the tabs!)
What I'd effectively want to do, is to be able to attach the mouse listeners to the background of the GUI for the tabs - but how to accomplish something like this?
After looking at the solutions for this question (draggable tabs in Swing), I found that the actual TabbedPaneUI has some methods that can fix both problems: Only drag window in tab area, which turned out to be the hardest part, and not drag when above the tabs themselves. The relevant code follows, in the two sections marked with "// ::". The code is adapted from the question-mentioned Kirill-code. The code doesn't handle other cases than when the tabs are at top - which makes sense when considering what I want to do.
// mouse listener for dragging the host window
MouseAdapter adapter = new MouseAdapter() {
int lastX;
int lastY;
boolean _dragInitiated;
#Override
public void mousePressed(MouseEvent e) {
TabbedPaneUI ui = _windowTabs.getUI();
// :: Won't drag if we're positioned above a tab in tab area
if (ui.tabForCoordinate(_windowTabs, e.getX(), e.getY()) != -1) {
_dragInitiated = false;
return;
}
// :: Won't drag if we're below the tab area
int maxY = 0;
for (int i = 0; i < _windowTabs.getTabCount(); i++) {
Rectangle bounds = ui.getTabBounds(_windowTabs, i);
int y = bounds.y + bounds.height;
if (y > maxY) {
maxY = y;
}
}
_dragInitiated = true;
if (maxY > 0) {
if (e.getY() > maxY) {
_dragInitiated = false;
}
}
Point eventLocationOnScreen = e.getLocationOnScreen();
if (eventLocationOnScreen == null) {
Component source = (Component) e.getSource();
eventLocationOnScreen = new Point(e.getX() + source.getLocationOnScreen().x, e.getY()
+ source.getLocationOnScreen().y);
}
lastX = eventLocationOnScreen.x;
lastY = eventLocationOnScreen.y;
}
#Override
public void mouseDragged(MouseEvent e) {
if (!_dragInitiated) {
return;
}
Point eventLocationOnScreen = e.getLocationOnScreen();
if (eventLocationOnScreen == null) {
Component source = (Component) e.getSource();
eventLocationOnScreen = new Point(e.getX() + source.getLocationOnScreen().x, e.getY()
+ source.getLocationOnScreen().y);
}
int dx = eventLocationOnScreen.x - lastX;
int dy = eventLocationOnScreen.y - lastY;
Window win = POTabbedFrame.this;
Point loc = win.getLocation();
win.setLocation(loc.x + dx, loc.y + dy);
lastX = eventLocationOnScreen.x;
lastY = eventLocationOnScreen.y;
}
};
_windowTabs.addMouseListener(adapter);
_windowTabs.addMouseMotionListener(adapter);