Groups | Blog | Home
all groups > flash actionscript > june 2005 >

flash actionscript : drag and drop using object proximity rather than mouse coords


sneakyimp
6/13/2005 11:08:27 PM
I have a flash app where a user must drag each of 20 cards--one each--onto one
of 20 targets. i would like the target spots to light up based on the
proximity of the dragged object rather than the location of the mouse. because
of this, i'm guessing i won't be able to use the drag targeting functionality
of flash.

i'm guessing i'll need to create onPress, onRelease, and onReleaseOutside
handlers for the cards to start and stop the dragging.

I'm also guessing I'll need to create some interval function or onEnterFrame
function for the dragged object which will be responsible for highlighting the
appropriate target--and turning off that highlight when the card is dragged
away to some other target. Can anyone recommend an approach for this? My
experience with interval functions is that they can be a pain--you have to make
sure they get deleted properly. Does an onEnterFrame function update
frequently enough?

Any suggestions (and code snippets) would be much appreciated.


kglad
6/14/2005 12:00:00 AM
you can use startDrag() and stopDrag() with your mouse handlers, and you'll
need to initiate an interval to continually check the distance between the
dragged movieclip and its target and adjust the color/alpha of the target based
on that distance. for example, if you have movieclips card1,card2,...,card20
and each has one target movieclip target1,target2,...,target20, respectively,
you can use;

for (var i = 1; i<=20; i++) {
rclip = this["card"+i];
rclip.ivar = i;
rclip.onPress = function() {
this.startDrag();
proximityI = setInterval(proximityF, 100, this.ivar);
};
rclip.onRelease = function() {
this.startDrag();
clearInterval(proximityI);
};
}
function proximityF(i) {
for (var j = 1; i<=20; i++) {
this["target"+j].setRGB(someFunction(distanceF(i, j)));
}
}
function distanceF(i, j) {
//return distance between this["card"+i] and this["target"+j]
}
function someFunction(num) {
// set your color 0xPQRSTU per your needs
}
mandingo
6/14/2005 12:00:00 AM
I would probably follow kglad but I did something much more confusing...

if you are interested...

on the cards (targetZone) I have a further clip that I used to show a
highlight (targetSpot) and on the card movieClip I have this code :

this.farFarAway = 0x66ff99;
this.farAway = 0x66CC33;
this.away = 0x33CC33;
this.near = 0x999900;
this.nearer = 0xff9900;
this.veryNear = 0xcc3300;
this.rightOnTop = 0xFF0000;


this.myProximity = new Color(this.targetSpot);
this.myProximity.setRGB(this.farFarAway);

this.report = function(proximity){
if(proximity > 300){
setColour = this.farFarAway;
}
if(proximity <= 300){
setColour = this.farAway;
}
if(proximity <= 200){
setColour = this.away;
}
if(proximity <= 150){
setColour = this.near;
}
if(proximity <= 100){
setColour = this.nearer;
}
if(proximity <= 50){
setColour = this.veryNear;
}
if(proximity <= 25){
setColour = this.rightOnTop;
}
this.myProximity.setRGB(setColour);
}


now on my draggable clip I have this code:

this.onPress = function(){
this.startDrag();
this.onEnterFrame = function(){
for(var k in this._parent.myTargets){
distance = Math.floor(Math.sqrt(Math.pow((this._x -
this._parent[this._parent.myTargets[k]]._x),2) + Math.pow((this._y -
this._parent[this._parent.myTargets[k]]._y),2)));
this._parent[this._parent.myTargets[k]].report(distance);
}
}
}
this.onRelease = function(){
stopDrag();
delete this.onEnterFrame;
}

this finds the distance between each of the target cards and the dragging clip
and reports the distance in an onEnterFrame event that is triggered with the
startDrag of the clip. When you release the clip, it also clears the
onEnterFrame event.

the only thing else was an array on the _root level holding the clips that the
cards represent:

this.myTargets =
["targetZone1","targetZone2","targetZone3","targetZone4","targetZone5",
"targetZone6","targetZone7","targetZone8","targetZone9","targetZone10"];

that was it... as you get closer to a clip it changes colour.

cheers,
sneakyimp
6/16/2005 11:56:57 PM
you guys both rock. thanks for suggestions. i worked through kglad's
code...got it working. i was hoping to create an elegant mathematical function
that gracefully handled distances arbitrarily small or large but i could
remember the math...so i relied on the computer logic to handle things.

var intCards = 20;
var intCardStartY = 10;
var intCardTargetStartY = 210;

// create targets
for(i=0; i<intCards; i++) {
var mvCard = this.attachMovie('cardTarget', 'cardTarget'+i,
this.getNextHighestDepth());
mvCard._x = (50*i)%550;
mvCard._y = intCardTargetStartY + 30*(Math.floor((50*i)/550));
}
// create cards
for(i=0; i<intCards; i++) {
var mvCard = this.attachMovie('card', 'card'+i, this.getNextHighestDepth());
mvCard.txtLabel.text = i;
mvCard._x = (50*i)%550;
mvCard._y = intCardStartY + 30*(Math.floor((50*i)/550));
mvCard.ivar = i;
mvCard.onPress = function() {
this.startDrag();
proximityI = setInterval(proximityF, 100, this.ivar);
};
mvCard.onRelease = function() {
this.stopDrag();
clearInterval(proximityI);
};
}
function proximityF(i) {
for (j=0; j<intCards; j++) {
var mvTarget = _root['cardTarget'+j];
var targetColor:Color = new Color(mvTarget.mvBackground);
var hexColor = someFunction(distanceF(i, j));
targetColor.setRGB(hexColor);
}
}
function distanceF(i, j) {
//return distance between this["card"+i] and this["target"+j]
var mv1 = this['card' + i];
var x1 = mv1._x;
var y1 = mv1._y;
var mv2 = this['cardTarget' + j];
var x2 = mv2._x;
var y2 = mv2._y;
var result = Math.sqrt(((x1-x2)*(x1-x2)) + ((y1-y2)*(y1-y2)));
return result;
}
function someFunction(num) {
// return your color 0xPQRSTU per your needs as a function of num
if (num>=30) {
return 0xff0000;
}
if (num==0) {
return 0x00ff00;
} else {
var scale = Math.round(255-(255*(num/30)));
var strScale = scale.toString(16);
while(length(strScale) < 2) {
strScale = '0' + strScale;
}
var result = "0xff"+ strScale + "00"
return Number(result);
}
}
kglad
6/17/2005 12:30:51 AM
you're welcome. but there is no fast practical way to handle distances between
movieclips.

from a mathematicians point of view the distance between movieclip1 and
movieclip2 is the minimum of the distances between each pair of points z1 and
z2 where z1 is in movieclip1 and z2 is movieclip2. but that's not going to
compute in flash.

so, you need to make some concessions. if the movieclips are circles, you can
compute the distances between any 2 circles just by knowing the circles'
centers and radii. every other shape is tougher.

anyway, i infer you found a satisfactory solution for your needs. congrats!
sneakyimp
6/17/2005 1:29:42 AM
i remembered the euclidean distance formula for two points. for my
application, i'm comparing the top left corner of the button vs. the target.

the formula i was referring to was some cool way to map distance onto a color.
i settled for a simple inverse linear relationship between distance and color.
as your distance approaches 0, the amount of green increases to 255. i
tinkered around a little bit for fun (NERD) and got a pretty good smooth
scaling function...something like 1/Math.pow(((i/512)+1), 4)

the problem i'm having now is that when you click some card i use swapDepths
to bring the card to the foreground like this:

mvCurrentCard.onPress = function() {
this.swapDepths(10000);
this.startDrag(false, 0, 0, (intStageWidth-this._width),
(intStageHeight-this._height));
}

this results in some weird card movements when you select various cards. for
instance, if i select card 10 it comes to the top--level 10000. if i then
select card 4, then card 4 goes to level 10000 and card 10 takes a much lower
slot than it originally had. it's sort of disconcerting because it makes it
easy to lose cards when they are all piled up.

i thought of using something like this:

mvCurrentCard.onPress = function() {
this.swapDepths(_root.getNextHighestDepth());
this.startDrag(false, 0, 0, (intStageWidth-this._width),
(intStageHeight-this._height));
}

but then i started thinking i might run out of depths. is that likely? isn't
the depth range absurdly high? it seems kinda wrong to be incrementing depth
values without bound. this seems easiest so i'm willing to entertain the idea
but want to be sure it's safe.

i thought of another possibility
when a card is selected and goes to the top, re-assign the depths of all the
cards that were higher than this card to on level lower so that the vacated
level is filled...this sounds like a pain but would keep all the cards in the
same range they start in.

AddThis Social Bookmark Button