A oval motion (entity teleport some pixels to his orbit)[SOLVED]


(billy2000) #1

Hyas :). Basically i know how to do a circular motion.But this time i want to make this motion oval.I tried to modify the sin and cos but then he just jumps to his orbit.The code looks like this:

        private function setupCircle():void
	{
		var playerX:Number = G.pX + 9;
		var playerY:Number = G.pY + 9;
		var dx:Number = playerX - x;
		var dy:Number = playerY - y;
		
		radius = 0.5 * Math.sqrt(dx * dx + dy * dy);
		angle = Math.atan2(dy, -dx);
		centerCoord.x = 0.5 * (playerX + x);
		centerCoord.y = 0.5 * (playerY + y);
		
		angleStep = 0;
		speed = (1.0 / (Math.PI * radius)) * 10;
		//the direction he will go
		speed *= -1;
		
		//var rand:Number = Math.random();
		//if (rand < 0.5) speed *= -1;
	}
	private function updateCircle():void
	{
		angleStep += speed;
                //over here tried to modify sin and cos
		x = centerCoord.x + Math.cos(angle + angleStep) * radius;
                y = centerCoord.y - Math.sin(angle + angleStep) * radius;
		
		//check if he arrived at destination
		var remainDist:Number = FP.distance(x, y, saveCoord.x, saveCoord.y);
		if (remainDist <= 2) {
			whatDo = animation = "fly";
		}
	}

I don’t rly know how to make his motion oval but in the same time when he begin the motion to not teleport to his orbit.I mean the point he is on be4 he starts is like some pixels away from his orbit when he begins the oval motion.Also at circular motion the code is just fine. Do you guys have any ideas? thx.


(JP Mortiboys) #2

I’m not sure where to start here - perhaps the best thing would be for you to do us a diagram of how you want the movement curve to look, depending on the parameters.

One of the problems here is that while a circle only has 3 parameters (centre coords and radius), an axis-aligned ellipse (oval) has 4 (centre coords and major and minor axes) - and a general 2d ellipse has 5 (prev 4 plus rotation) - your x and y coords do not supply sufficient information to produce a single ellipse.

I can tell you that the parametric equation of an axis-aligned ellipse is:

x(t) = centreX + cos(t) * semiWidth
y(t) = centreY + sin(t) * semiHeight

If the ellipse isn’t axis-aligned then you need to rotate these coords, or use a different basis.

I can also tell you that compared to circles, ellipses are a bit of a pig to work with. For example, in the above equations, if the semiWidth and semiHeight are equal, that’s a circle - easy. And t is the angle, easy again. But for an ellipse t is NOT the angle; it’s a parameter which does deviate from the angle by quite a bit, depending on the eccentricity of the ellipse.

On a related note, on a circle, the angle parameter (in radians) corresponds to the length of the unit arc, so radius * angle is the length of the arc - nice and easy for constant speed calculation. On an ellipse however, this doesn’t work. It doesn’t work with the t parameter, it doesn’t work with the angle if you back-calculate it from the parameter. To get the arc length of an ellipse you have to resolve an elliptic integral of the second kind - which is impossible to do exactly in closed form.

In short, I’d say : Avoid using ellipses. They’re a pig to work with. You can approximate ellipses using quadratic or cubic bézier curves easily enough - which FP handles natively via tweens. You could also approximate an elliptic path as linear steps which would work well enough (see this post for an example of a linear path faking a curve).

But in any case, however you want to do it, you’ll need to supply a diagram of how you want the curve to look, because there simply isn’t enough (mathematical) information to determine an ellipse from two points.

Also - why do you want an ellipse, specifically? Off-hand the only thing I can think of that moves in an ellipse is an orbiting body, and you wouldn’t use this sort of system for that.


(billy2000) #3

Oh thx for tips. Well basically i have a enemy.When this enemy see the player(is in his range) he will go with this ellipsoidal motion to the point where he saw him. something like this: Seems like this ellipsoidal motion is a pain to work with …maybe ill try something else. XD If you have any better idea u can give me i’d appreciate :smile:


(billy2000) #4

Actually ill try to use that code from wiggled motion but with 1 wiggle and lower amplitude… XD


(JP Mortiboys) #5

Use a quadratic - like so:

// In your enemy class:

public function curveToTarget(x:Number, y:Number) {
  var dx:Number = x - this.x;
  var dy:Number = y - this.y;
  var centreX:number = 0.5 * (x + this.x);
  var centreY:Number = 0.5 * (y + this.y);
  var archHeightFactor:Number = 0.5; // Try 1, 2... big numbers make big sweeps, 0 = straight line

  var controlX = centreX - dy * archHeightFactor;
  var controlY = centreY + dx * archHeightFactor;

  var motion:QuadMotion = new QuadMotion();
  motion.object = this;
  motion.setMotion(this.x, this.y, controlX, controlY, x, y, 2);
  addTween(motion, false);
}

Then just call curveToTarget(player.x, player.y) when you want to.

Also note that you can use motion.setMotionSpeed(...) in a similar way if you want a constant speed along the curve.


(billy2000) #6

O: so many cool functions i didn’t even know about >.<.Thx so much ill try it right on :smile:


(billy2000) #7

I tried the code :smile:.But the problem i encounter is that when he start the motion he teleport to y=0 and x=0 and from there he goes to player.Sry to bother with that but is 1st time i see the function and i have kinda no idea what to do xD.Is there something wrong …or something that i have to do more? Thx :smile:


(billy2000) #8

Here is the enemy class …if it helps:

package  
{
import net.flashpunk.Entity;
import net.flashpunk.graphics.Graphiclist;
import net.flashpunk.graphics.Spritemap;
import net.flashpunk.FP;
import net.flashpunk.tweens.motion.QuadMotion;

public class EnemyPurple extends Entity 
{
	[Embed(source = "../Assets/enemies/purple.png")]private const PURPMAP:Class
	private var purpMap:Spritemap;
	private var animation:String;
	public var theGraphic:Graphiclist; 
	
	//life stuff
	public var life:int = 3;
	
	//motion stuff
	private var whatDo:String;
	private var timerSee:int;
	private var shakeX:Number;
	private var shakeNow:Boolean;
	
	public function EnemyPurple() 
	{
		purpMap = new Spritemap(PURPMAP, 22, 19);
		
		maps();
	}
	override public function added():void
	{
		centerOrigin();
		whatDo = animation = "fly";
		
		
		theGraphic = new Graphiclist();
		
		graphic = theGraphic;
		theGraphic.add(purpMap);
		purpMap.play(animation);
		purpMap.x = -11;
		purpMap.y = -9;
		
		
		
		timerSee = 0;
	}
	public function setup(x:Number, y:Number,name:String):void
	{
		this.x = x;
		this.y = y;
		this.name = name;
		type = "enem";
		subType = "PurpE";
		
		setHitbox(18, 15, -2, -2);
		
		
		
	}
	override public function update():void
	{
		super.update();
		
		changeAnim();
		motion();
	}
	private function changeAnim():void
	{
		purpMap.play(animation);
	}
	private function checkForPlayer():void
	{
		//if (whatDo != "fly" || !Player.alive) return;
		
		var dist:Number = FP.distance(x, y, G.pX + 9, G.pY + 9);
		
		if (dist <= 100) {
			whatDo = animation = "justSaw";
			shakeX = 6;
			shakeNow = true;
			//timerSaw = 30;
		}
	}
	private function motion():void
	{
		if (!Player.alive) return;
		
		switch(whatDo) {
			case "fly":
				
				if (timerSee > 0) timerSee--;
				else checkForPlayer();
				
				break;
			case "justSaw":
				if (Math.abs(shakeX) > 0.1) { 
					
					if(shakeNow){
					x += shakeX;
					
					if (shakeX > 0) shakeX -= 0.2;
					else shakeX += 0.2;
					
					shakeX *= -1;
					shakeNow = false;
					}
					else shakeNow = true;
				}
				else {
					
					
					whatDo = animation = "goToHim";
					
					var togoX:Number = G.pX + 9;
					var togoY:Number = G.pY + 9;
					
					curveToTarget(togoX, togoY);
					
					timerSee = 30;
				}
				
				break;
			case "goToHim":
				
				break;
		}
	}
	public function curveToTarget(x:Number, y:Number):void {
		var dx:Number = x - this.x;
		var dy:Number = y - this.y;
		var centreX:Number = 0.5 * (x + this.x);
		var centreY:Number = 0.5 * (y + this.y);
		var archHeightFactor:Number = 0.5; // Try 1, 2... big numbers make big sweeps, 0 = straight line
		
		var controlX:Number = centreX - dy * archHeightFactor;
		var controlY:Number = centreY + dx * archHeightFactor;
		
		var motion:QuadMotion = new QuadMotion();
		motion.object = this;
		motion.setMotion(this.x, this.y, controlX, controlY, x, y, 100);
		addTween(motion, false);
	}
	
	override public function removed():void
	{
		//addAmunition(); //amunition is added when hit by bullet
		//destroyAnim();  //same
		//var parent_:EBrSpawner = FP.world.getInstance(name + "parent");
	   // parent_.removeTheInstance();
		//theGraphic.removeAll();
		
	}
	private function maps():void
	{
		purpMap.add("fly", FP.frames(0, 5), 0.12, true);
		purpMap.add("justSaw", [6], 0 , false);
		purpMap.add("goToHim", [7], 0 , false);
	}
}

}

(billy2000) #9

IDK why but it seems like the problem is from this.x and this.y (when the motion is set) because if i change them with enemy’s coords it starts properly.


(JP Mortiboys) #10

Try swapping these two lines:

		motion.object = this;
		motion.setMotion(this.x, this.y, controlX, controlY, x, y, 100);

so the assignment is afterwards:

		motion.setMotion(this.x, this.y, controlX, controlY, x, y, 100);
		motion.object = this;

Apparently when you set object it automatically sets the object’s x and y to the tween’s current values… which aren’t set yet. This should help.


(billy2000) #11

Yep that was the problem. thank you very much :smiley: Its working perfectly ! :smile:


(JP Mortiboys) #12

No worries mate, any time. If it’s maths-related, I can do it :smile: