Okay, so I want to make a javascript/html canvas game where the player is being followed by some enemies, and after a little bit of 'research' her is the most important part of my Monster (enemy) class:
this.UpdateAngle = function() {
this.dx = this.x - player.x;
this.dy = this.y - player.y;
this.angle = Math.atan2(this.dy, this.dx) * 180 / Math.PI;
if (this.angle < 0) {
this.angle += 2 * Math.PI;
}
}
this.UpdateSpeed = function() {
this.speedX = this.speed * Math.cos(this.angle);
this.speedY = this.speed * Math.sin(this.angle);
}
this.Move = function() {
this.UpdateAngle();
this.UpdateSpeed();
this.x += this.speedX;
this.y += this.speedY;
}
So what I meant to do here, was to calculate the angle from the enemy to the player with atan2() and then calculate how much I should move in the x and y axis by using cos() and sin(), the speed and the angle I calculated, and then just moved the calculated pixels.
This all seems to work well, until I move the player, then the enemies start to move in weird directions. I have no idea whats wrong, it would be awesome if someone could learn me how this is meant to be done. :D
You can see it in action here. *I have updated the code with PremierBromanov's suggestion.
It may have something to do with this block
this.angle = Math.atan2(this.dy,this.dx) * 180 / Math.PI;
if (this.angle < 0) {
this.angle += 2 * Math.PI;
You are using Math.atan2 which outputs the angle in radians, then you are converting to degrees with * 180 / Math.PI; After that, you're checking to see if it's less than zero and adding 2Pi to the angle to make sure it's correctly calculating it's actual angle of a full circle minus the angle. BUT, you are using radians here instead of degrees. So when your code is negative, you're adding 2Pi to the degree, which isn't very much, but sometimes causes it to go positive. This is why your dots are spinning when you move. If you notice, the dots spin slower when you are farther away, meaning the negative angle is larger than 2Pi and so doesn't circle around right away.
in short, try changing it to this
if (this.angle < 0) {
this.angle += 360;
}
Ok, so it was actually Premier Bromanov who answered this, thanks, but I can't accept a comment, which it was, so I will just do this to make it more clear, if anyone should come by and want the answer too.
The math i did was a bit wrong, and here is how my code ended up looking like:
this.UpdateAngle = function() {
this.dx = player.x - this.x;
this.dy = player.y - this.y;
this.distance = Math.sqrt((this.dx*this.dx) + (this.dy*this.dy));
this.angle = Math.atan2(this.dy,this.dx) * 180 / Math.PI;
}
this.UpdateSpeed = function() {
this.speedX = this.speed * (this.dx/this.distance);
this.speedY = this.speed * (this.dy/this.distance);
}
this.Move = function() {
this.UpdateAngle();
this.UpdateSpeed();
this.x += this.speedX;
this.y += this.speedY;
}
Thanks again to Premier Bromanov, this is his answer, and also to everyone else, this was my first post and i am glad how fast i got a response! (I was the slowest one here) :D
Related
I'm drawing out sine curves using the following formula,
this.x += this.speed
this.y = this.amp * Math.sin( this.x * this.cycles ) + this.center
I'm dotting out points by drawing tiny circles every frame. What this achieves is a nice looking sine curve that draws from left to right. But if I want to rotate this curve so that it draws in a random 360 degree, how should I modify the code? Aka, sometimes I want the curve to be the same drawing from left to right, other times I want it drawing at a 45 degree downwards, sometimes, backwards, etc...
One option is to calculate the point in Cartesian coordinates, convert to polar, add your angle offset, and convert back to Cartesian again.
this.x += this.speed
this.y = this.amp * Math.sin( this.x * this.cycles ) + this.center
this.r = Math.sqrt(this.x * this.x + this.y * this.y);
this.a = Math.atan2(this.y, this.x);
this.a += rotation_angle;
this.x = this.r * Math.cos(this.a);
this.y = this.r * Math.sin(this.a);
This increases the number of Math library calls from one to five, so I would expect it to be about one fifth as fast as your original calculation. This may or may not be acceptable. We can actually do a little better using the trigonometric identities sin(a+b) = sin(a)cos(b) + cos(a)sin(b) and cos(a+b) = cos(a)cos(b) - sin(a)sin(b) identities.
For the rotated X-coordinate:
this.r * cos(this.a)
= this.r * cos(atan2(this.y, this.x) + rotation)
= this.r * (cos(atan2(this.y, this.x))cos(rotation) - sin(atan2(this.y, this.x))sin(rotation))
= this.r * ((this.x / this.r)cos(rotation) - (this.y / this.r)sin(rotation))
= this.x * cos(rotation) - this.y * sin(rotation)
For the rotated Y-coordinate:
this.r * sin(this.a)
= this.r * sin(atan2(this.y, this.x) + rotation)
= this.r * (sin(atan2(this.y, this.x))cos(rotation) + cos(atan2(this.y, this.x))sin(rotation))
= this.r * ((this.y / this.r)cos(rotation) + (this.x / this.r)sin(rotation))
= this.y * cos(rotation) + this.x * sin(rotation)
Our new code looks like this:
x = this.x + this.speed
y = this.amp * Math.sin( x * this.cycles ) + this.center
this.x = x * cos(rotation_angle) - y * sin(rotation_angle);
this.y = y * cos(rotation_angle) + x * sin(rotation_angle);
We introduced variables x and y since we need each in its original form to calculate each of this.x and this.y. We no longer need to work in polar coordinates because our identities allowed us to eliminate those intermediate steps. Also, if rotation_angle is constant, it can be pre-computed; otherwise, you can leave the calls in the calculation of each point and get spiral-type effects.
If you prefer not to work with angles at all, you can work in terms of arbitrary functions by using parametric definitions x = f(t) and y = g(t) to define a curve, determining the perpendicular at each point t - either by analytically finding and encoding f'(t) and g'(t) - or by approximating these numerically around points of interest. Then, you simply plot a point at a displacement along that directed normal line equal to the value you are currently calculating for this.y. So, if you wanted to plot a sine curve along a parabola, you could do this:
t += speed;
r = this.amp * Math.sin(t * this.cycles) + this.center;
x = t;
y = t * t;
dxdt = 1;
dydt = 2t;
dydx = dydt / dxdt;
if (-epsilon <= dydt && dydt <= epsilon) {
this.x = x;
this.y = y + r;
} else {
normal = -1.0 / dydx;
this.x = x + r * ( 1.0 / Math.sqrt(1 + dydx * dydx));
this.y = y + r * (dydx / Math.sqrt(1 + dydx * dydx));
}
I haven't tried running that, so it might have some errors, but in theory those errors should be fixable so that you get the intended effect: a sine curve wrapped around the (directed!) parabola y = x^2 oriented to from negative to positive x.
Im trying to make an particle move towards where on canvas was clicked however the formula returns NaN. Why is this and how can I fix this?
step 1: Particle gets placed at player.x, player.y properly.
step 2: At first tick the particle goes to the upper left point of the canvas without x,y position.
Screenie of particle props
example at jsfiddle
Particle Object
function Shuriken(mouseX, mouseY) {
this.rot = 0;
this.vel = 2;
this.angle = 0;
this.x = player.x || WIDTH / 2;
this.y = player.y || HEIGHT / 2;
this.mouseX = mouseX || WIDTH /2;
this.mouseY = mouseY || HEIGHT /2
this.width = shurikenImg.width;
this.height = shurikenImg.height;
}
update on tick
Shuriken.prototype.move = function() {
this.angle = Math.atan2(this.mouseY, this.x) * (180 / Math.PI);
this.x += this.vel * Math.cos(this.angle);
this.y += this.vel * Math.sin(this.angle);
}
You convert angle from radians into degrees. Nicer to debug (because degrees are still taught in school) but alas, the sin and cos in the next lines expect their arguments to be in radians again. Granted, the functions ought still to work; but the values you get are no longer what you expect.
this.vel is undefined in the jsfiddle. This is most likely what lead to the observed NaN.
This is a wrong calculation:
this.x = this.vel * Math.cos(this.angle);
this.y = this.vel * Math.sin(this.angle);
You don't want the x and y positions calculated this way. Add or subtract the (adjusted) velocity from the x/y coordinates.
I have a point drawn in an canvas (html5). Then I want this point to animate in a circular path.
I saw an example using time differences to set the x and y variables, in respect to time. Some of the variables and formulas used are quite vague, I have forgotten my physics, d*mn. But I have researched quite a bit on circular motion, so I can understand some of it. Here is my codepen on how it was done.
Basically here are the parts I have identified so far:
this.orbit = 100; // this is the radius of the circular orbit
this.radius = 5; // orbiting object's radius
this.velocity = 50; // yeah velocity but without direction, should be speed (agree?)
var angle = 0; starting angle of the point in the orbit inside the canvas's quadrant,
set x and y coordinates with respect to the coordinates of the canvas
first get the center of the canvas by dividing the width and the height by 2
then adding to the product of the orbit's radius and the position of x and y
with respect to the initial position in the orbit(angle), and since Math trigonometric
functions uses radians, we should multiply it to the quotient of PI and 180.
this.x = _width / 2 + this.orbit * Math.cos(angle * Math.PI / 180)
this.y = _height / 2 + this.orbit * Math.sin(angle * Math.PI / 180)
// by doing the above, we now get the initial position of x and y in the orbit.
What is quite trivial to me are the next variables _dx and _dy and also the _magnitude.
Here is how the point is animated:
Point.prototype.update = function(dt) {
var dps = this.orbit * 2 * Math.PI / this.velocity;
var angle = (360 / dps) * dt / 1000 * -1;
this.vx = this.vx * Math.cos(angle * Math.PI / 180) - this.vy*Math.sin(angle * Math.PI / 180);
this.vy = this.vx * Math.sin(angle * Math.PI / 180) + this.vy*Math.cos(angle * Math.PI / 180);
var _magnitude = Math.sqrt( this.vx * this.vx + this.vy * this.vy);
this.vx = this.vx / _magnitude * this.velocity;
this.vy = this.vy / _magnitude * this.velocity;
this.x += this.vx * dt / 1000;
this.y += this.vy * dt / 1000;
}
And here is the execution of the script:
function animate () {
dt = new Date() - ldt;
if (dt < 500) {
// context.clearRect(0, 0, canvas.width, canvas.height);
point.update(dt);
point.draw(context);
};
ldt = new Date();
setTimeout(function() {
window.requestAnimationFrame(animate);
}, 1000 / 30)
}
ldt = new Date();
animate();
With the unclear variables, like _dx _dy _magnitude I cannot understand how it works and how the computation of variables, vx vy which I assume the velocity with respect to x and y respectively.
I wanted to use greensock tweenlite for the animation and it is done like so:
Point.prototype.update = function(p){
var _to = {
x: , // change the value of x
y: , // change the value of y
ease: Cubic.easeInOut,
onComplete: function () { this.update(p) }
}
TweenLite.to(point, 2, _to)
}
As you can see the first parameter is the current object (point), then the second parameter is the time, I assume this to be the velocity and the the third parameter is the change in the object's properties, x and y.
Question
I made the codepen, now How do I use gsap tweenlite to animate the circle like what I did, I suppose using tweenlite will make it a little simple.
In your case you are trying to use TweenLite to animate point as the crow flies, and you trigger TweenLite.to() function for each new position of point. This method of using TweenLite.to() function has no sense and performance, because distance between 2 position of point is too short. So, this method will only slow down your animation because instead of just draw point in new position you want to animate it.
The best solution in this case is trying to use TweenLite's methods to animate whole circle.
Take a look on this article: Tween around circle
Especially on these examples:
1) http://codepen.io/GreenSock/pen/jCdbq (not canvas, but it displays the main idea)
TweenMax.to("#logo", 4, {rotation:360, transformOrigin:"40px -100px", repeat:10, ease:Linear.easeNone});
2) and http://codepen.io/rhernando/pen/kjmDo
I am using time based movement, but its still super choppy. this code shoots in arrow in the air then calculates the difference in movement to determine to rotation of the arrow. It works most of the time, but sometimes it just jitters.
for(i=0;i<this.arrows.length;i++){
a = this.arrows[i];
point0 = [a.x,a.y];
x_speed = e.delta/1000*a.speed * Math.cos(a.angle * Math.PI / 180);
y_speed = e.delta/1000*a.speed * Math.sin(a.angle * Math.PI / 180);
a.x += x_speed;
a.y += y_speed;
a.y += a.velocity;
a.velocity += e.delta/1000*this.gravity;
alert(e.delta);
ang = Math.atan2(point0[1]-a.y,point0[0]-a.x);
ang = (ang * (180/Math.PI))+180;
a.rotation = ang;
}
1) I'm surprised you are using a.angle, ang and a.rotation. I guess ang is a var declared before the for loop, but isn't there an issue here ?
2) You might use radians, not degrees, to avoid conversions.
3) There's also an issue with speed vs velocity : you don't update speed (linear speed) when you change velocity (speed on Y).
•• Anyway there's a simpler way of proceeding : compute speed/position just like for a regular point moving, then use the speed vector to compute where the end of the arrow is.
It works fine in this fiddle : http://jsbin.com/dexaroriwixo/1/
update :
var i=0, a=null, delta_ms = 1e-3 * e.delta;
for(i=0;i<this.arrows.length;i++){
a = this.arrows[i];
a.x += delta_ms*a.speedX;
a.y += delta_ms*a.speedY;
a.speedY += delta_ms*this.gravity;
a.speedNorm = Math.sqrt(sq(a.speedX)+sq(a.speedY));
}
to draw :
Arrow.prototype.draw = function(ctx) {
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.lineTo(this.x+ this.length*this.speedX/this.speedNorm,
this.y+ this.length*this.speedY/this.speedNorm );
ctx.strokeStyle = '#F00';
ctx.stroke();
}
to 'launch' an arrow,
Arrow.prototype.launch(initialSpeed, angle) {
this.speedX = initialSpeed * Math.cos(angle);
this.speedY = initialSpeed * Math.sin(angle);
}
I have a sprite representing a bullet and its basic implementation is as follows:
function Bullet(x, y, rotation) {
this.x = x;
this.y = y;
this.direction = rotation;
this.speed = 5;
}
Bullet.prototype.update = function() {
// Move the bullet forward
this.x = Math.sin(this.rotation) * this.speed;
this.x = Math.cos(this.rotation) * this.speed;
}
What I'm trying to do here is move the bullet forward in the direction it's facing and relative to its speed. However, when calling the update() method this.x and this.x is NaN.
What's the correct way of making a sprite move in the direction it's facing if given its x, y and rotation information?
You have a typo. This:
this.x = Math.sin(this.rotation) * this.speed;
should be
this.x = Math.sin(this.direction) * this.speed;