问题
Skip to JSFiddle
I'm creating one of these fun scripts that interact with mouse. I decided I want an object to folow mouse slowly. I made such object, that has these properties:
speed - current speed of the object
speed change ratio - derivative of the speed function: "How's the speed changing now?"
direction - radian angle upon which the object is moving
direction change - same as for speed, this is used to change the direction
Change ratios are randomly generated and added to speed
and direction
:
//Do not do it allways
if(rand(0,5)>3) {
speed+=speed_change;
direction+=dir_change;
//Only sometimes, so that they actually have some time to take effect
if(rand(0,10)<=3) {
dir_change = rand(-1,1);
speed_change = rand(-2,2)/10;
}
}
Only way to smoothen the speed-change effect any more would be adding ratios for ratios and ratios for ratios of ratios etc... I can't figure anything else.
To the point now. You've seen that the movement by default is random, which is intended. If I dissabled the random ratios and applied this code instead, it followed mouse position:
var my_angle = Math.atan2(flyPos[1]-flyTo[1], flyPos[0]-flyTo[0])-Math.PI;
direction = my_angle;
//Inital speed is random, so here is some fix for it (the class also has soft-cap for speed, so it won't reach high numbers)
if(speed<0)
speed_change = 0.1;
Here is simple soft cap for speed I talked about, just in case you wanted to see it:
if(Math.abs(speed)>6) {
//Dividing speed by |speed| leads +1 or -1, but it's pretty unefective
speed_change = (-1)*(speed/Math.abs(speed));
}
Now, what I want is to alter dir_change
instead of direction
, so that if the target changes (which happens all the time with mouse cursor) it will not turn instantly but smoothly (a litle bit like a car would).
I tried this calculation:
dir_change -= (direction-my_angle)/Math.PI;
/Math.PI
was intended to reduce change size, -
should yield the difference. Does not work.
I also made example on JSFiddle, in hope that you won't just skip this question as unclear. Thank you for any help.
This is what it can do though, it's quite fun :)
回答1:
Well, here's a working fiddle for your solution -> Fiddle Link <-.
Mind you, I've changed some of the variables names, and added comments.
Regarding some of issues with your code:
The negative angle was because you called atan2 with current-target
, instead of target-current
. You should give the atan2 the difference between the flyTo and flyPos, since that will be the equivalent of looking at the target from where you are (which is the way atan2 works). So:
var my_angle = Math.atan2(flyPos[1]-flyTo[1],
flyPos[0]-flyTo[0]) -Math.PI;
should have been:
var my_angle = Math.atan2(flyTo[1]-flyPos[1],
flyTo[0]-flyPos[0]);
And then you shouldn't have worried about the -PI
.
Speed should be always positive, since it's a scalar, and we have the direction (also, negative speed = positive speed in negative direction).
I've added decelerate
and accelerate
methods, mainly to avoid the annoying "buzz" when the text tries to reach the target, and keeps moving back and forth between tiny locations. Now it'll decelerate when it get's closer, and accelerate if you move away.
A small thing to note: according to MDN you should call your setInterval
like this:
setInterval( letter.flyAway, refreshEvery );
instead of with the self executing method (and it's much easier on the eyes as well).
To get your nice round turning, what we want to do is look at the current direction, and slowly change towards the target direction.
A possible bug exists in the following naive implementation (where dPhi
stands for delta phi, a small angle):
if ( Math.abs(angle_to_target - direction) > dPhi )
if (angle_to_target > direction)
direction += dPhi;
else
direction -= dPhi;
else
direction = angle_to_target;
If direction is for example 179 deg, and the target is 181 deg, instead of closing the 2 degree gap, it'll go through the 358 degree AROUND the other side. The reason is the way atan2
deals with the angle, returning values between -PI
and PI
. The solution is to add 2*PI
in a couple of cases:
if (direction - Math.PI > angle_to_target )
angle_to_target += 2*Math.PI;
if (angle_to_target - Math.PI > direction )
direction += 2*Math.PI;
There you go, working fiddle with start
and stop
buttons will show you the results.
You might want to add the random speed
/ angle
to that solution, but that shouldn't be hard now :)
来源:https://stackoverflow.com/questions/20928558/slowly-change-one-angle-to-another