admin管理员组文章数量:1435767
I have an object I want to make orbit a star. I've managed to make the object move towards the star, but now I need to set up lateral movement as well.
Obviously this isn't as easy as just adjusting X, as when it moves round to the side of the star I'll have to adjust Y as well. I'm wondering how I could use some math to figure out how much I need to adjust X and Y by as the object moves around the star.
Here's my code so far:
var c = document.getElementById('canvas');
var ctx = c.getContext('2d');
c.width = window.innerWidth;
c.height = window.innerHeight;
var star = {
x: c.width / 2,
y: c.height / 2,
r: 100,
g: 2,
draw: function()
{
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, 2*Math.PI);
ctx.fillStyle = 'orange';
ctx.fill();
ctx.closePath();
}
};
var node = {
x: c.width / 2,
y: 100,
r: 20,
draw: function()
{
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, 2*Math.PI);
ctx.fillStyle = 'blue';
ctx.fill();
ctx.closePath();
}
};
//GAME LOOP
function gameLoop()
{
update();
render();
window.requestAnimationFrame(gameLoop);
}
function update()
{
//Move towards star
var dx = star.x - node.x;
var dy = star.y - node.y;
var angle = Math.atan2(dy, dx);
node.x += Math.cos(angle);
node.y += Math.sin(angle);
//Lateral movement
node.x += 2;
}
function render()
{
ctx.clearRect(0, 0, c.width, c.height);
star.draw();
node.draw();
}
window.requestAnimationFrame(gameLoop);
<html>
<head>
<style>
body
{
margin: 0;
padding: 0;
overflow: hidden;
}
#canvas
{
background-color: #001319;
}
</style>
</head>
<body>
<canvas id="canvas">
</canvas>
<script src="Orbit.js"></script>
</body>
</html>
I have an object I want to make orbit a star. I've managed to make the object move towards the star, but now I need to set up lateral movement as well.
Obviously this isn't as easy as just adjusting X, as when it moves round to the side of the star I'll have to adjust Y as well. I'm wondering how I could use some math to figure out how much I need to adjust X and Y by as the object moves around the star.
Here's my code so far:
var c = document.getElementById('canvas');
var ctx = c.getContext('2d');
c.width = window.innerWidth;
c.height = window.innerHeight;
var star = {
x: c.width / 2,
y: c.height / 2,
r: 100,
g: 2,
draw: function()
{
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, 2*Math.PI);
ctx.fillStyle = 'orange';
ctx.fill();
ctx.closePath();
}
};
var node = {
x: c.width / 2,
y: 100,
r: 20,
draw: function()
{
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, 2*Math.PI);
ctx.fillStyle = 'blue';
ctx.fill();
ctx.closePath();
}
};
//GAME LOOP
function gameLoop()
{
update();
render();
window.requestAnimationFrame(gameLoop);
}
function update()
{
//Move towards star
var dx = star.x - node.x;
var dy = star.y - node.y;
var angle = Math.atan2(dy, dx);
node.x += Math.cos(angle);
node.y += Math.sin(angle);
//Lateral movement
node.x += 2;
}
function render()
{
ctx.clearRect(0, 0, c.width, c.height);
star.draw();
node.draw();
}
window.requestAnimationFrame(gameLoop);
<html>
<head>
<style>
body
{
margin: 0;
padding: 0;
overflow: hidden;
}
#canvas
{
background-color: #001319;
}
</style>
</head>
<body>
<canvas id="canvas">
</canvas>
<script src="Orbit.js"></script>
</body>
</html>
Share
Improve this question
asked Dec 1, 2016 at 9:21
Hadrian HughesHadrian Hughes
1031 silver badge10 bronze badges
1
- 1) Are you drawing a circle, with the sun in the middle, or 2) an ellipse with the sun in 1 focus point, ... and 3) does the orbit process (the ellipse itself rotates around a point...)? – Emmanuel Delay Commented Dec 1, 2016 at 9:55
2 Answers
Reset to default 5Newton's and Kepler's clockwork universe
When Newton worked out the maths for calculating orbits he noted something that prompted him to coin the term "clockwork universe". In a two body simulation the orbital paths of both objects repeat precisely. This mean that both objects will at the same time be in the exact same position at the exact same speed they were in the last orbit, there will be no precession.
Gravity, force, mass, and distance.
For a more accurate model of gravity you can use the laws of gravity as discovered by Newton. F = G * (m1*m2)/(r*r) where F is force, G is the Gravitational constant (and for simulations it is just a scaling factor) m1,m2 are the mass of each body and r is the distance between the bodies.
Mass of a sphere
We give both the star and planet some mass. Let's say that in the puter 1 pixel cubed is equal to 1 unit mass. Thus the mass of a sphere of radius R is 4/3 * R3 * PI.
Force, mass, and acceleration
The force is always applied along the line between the bodies and is called acceleration.
When a force is applied to an object we use another of Newton's discovered laws, F=ma where a is acceleration. We have the F (force) and m (mass) so now all we need is a. Rearrange F=ma to get a = f/m.
If we look at both formulas in terms of a (acceleration) a = (G * (m1*m2)/(r*r)) / m1 we can see that the mass of the object we are apply force to is cancelled out a = G * (m2)/(r*r). Now we can calculate the acceleration due to gravity. Acceleration is just change in velocity over time, and we know that that change is in the direction of the other body. So we get the vector between the bodies (o1
,o2
for object 1 and 2) dx = o2.x-o1.x
, dy = o2.y-o1.y
Then find the length of that vector (which is the r in the gravity formula) dist = Math.sqrt(dx* dx + dy * dy)
. Then we normalise the vector (make its length = one) by dividing by its length. dx /= dist
, dy /= dist
. Calculate the a (acceleration) and multiply the normalised vector between the object by a then add that to the object's velocity and that is it. Perfect Newtonian clockwork orbits (for two bodies that is).
Clean up with Kepler.
All that math is good but it does not make for a nice simulation. When the math is done both objects start moving and if the starting velocities are not in balance then the whole system will slowly drift of the canvas.
We could just make the display relative to one of the bodies, this would eliminate any drift in the system, but we still have the problem of getting an orbit. If one object is moving to fast it will fly off and never e back. If it is going too slow then it will fall in very close to the centerpoint of the other object. If this happens the change in velocity will approch infinity, something puters are not that good at handling.
So to get nice circular orbits we need one last bit of math.
Using Kepler's second law modified to fit into Newton's math we get a formula that will give the approximate (It is an approximate as the actual calculations involve an infinite series and I can not be bothered writing that out.) orbital velocity v = sqrt(G*(m1 + m2)/r). It looks similar to Newton's gravity law but in this the masses are summed not multiplied, and the distance is not squared.
So we use this to calculate the tangential speed of both bodies to give them near circular orbits. It is important that each object go in the opposite direction to each other.
I created a setup function that sets up the correct orbits for both the sun and the planet. But the value of G (Gravitational constant) is likely way to large. To get a better value I scale G (via kludge math) so that the sun's orbit speed is close to a desired ideal sunV
(pixels per frame) To make the whole sim run quicker increase this value
As I have set up the code to have more than two bodies the calculation of starting velocity will only work if each object is significantly more massive than the next. I have added a moon (you need to un-ment to see) to the planet, but it is too big and it's starting velocity is a little too low. It gets pulled (gravity sling shot) by the Earth into a higher orbit,. but this also pulls the earth into a lower orbit making its orbit more eccentric
NOTE After all that I find that something is not quite right and there is still a tiny bit of drift in the system. As I am out of time I have just fixed the sun position to keep the system on the canvas.
var c = document.getElementById('canvas');
c.width = innerWidth;
c.height = innerHeight;
var ctx = c.getContext('2d');
const STAR_RADIUS = 100;
const PLANET_RADIUS = 10;
const MOON_RADIUS = 4.5;
var G = 1; // gravitational constant is not so constant as need to
// scale it to find best value for the system.
// for that I will scale it so that the suns orbital speed around the
// planet is approx 0.1 pixels per frame
const sunV = 0.1; // the sun's orbital desired speed. THis is used to tune G
const DRAW = function () {
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, 2*Math.PI);
ctx.fillStyle = this.col;
ctx.fill();
ctx.closePath();
}
var star = {
x: c.width / 2,
y: c.height / 2,
vx : 0,
vy : 0,
r: STAR_RADIUS,
mass : (4/3) * Math.pow(STAR_RADIUS,3) * Math.PI,
col : 'orange',
draw : DRAW,
};
// kludge to fix drift
const sunStartX = star.x;
const sunStartY = star.y;
var node = {
x: c.width / 2 - STAR_RADIUS - PLANET_RADIUS * 5,
y: c.height / 2,
r: PLANET_RADIUS,
mass : (4/3) * Math.pow(PLANET_RADIUS,3) * Math.PI,
col : "blue",
draw : DRAW,
vx: -1,
vy: 0,
};
var moon = {
x: c.width / 2- STAR_RADIUS - PLANET_RADIUS * 7 ,
y: c.height / 2,
r: MOON_RADIUS,
mass : (4/3) * Math.pow(PLANET_RADIUS,3) * Math.PI,
col : "#888",
draw : DRAW,
vx: -1,
vy: 0,
};
const objects = [star, node];//, moon];
function setup(){
var dist,dx,dy,o1,o2,v,c,dv;
o1 = objects[0];
o1.vx = 0;
o1.vy = 0;
for(var j = 0; j < objects.length; j ++){
if(j !== 0){ // object can not apply force to them selves
o2 = objects[j];
dx = o2.x - o1.x;
dy = o2.y - o1.y;
dist = Math.sqrt(dx * dx + dy * dy);
dx /= dist;
dy /= dist;
// Find value og G
if(j === 1){ // is this not sun
v = Math.sqrt(G * ( o2.mass ) / dist);
dv = sunV - v;
while(Math.abs(dv) > sunV * sunV){
if(dv < 0){ // sun too fast
G *= 0.75;
}else{
G += G * 0.1;
}
v = Math.sqrt(G * ( o2.mass ) / dist);
dv = sunV - v;
}
}
v = Math.sqrt(G * ( o2.mass ) / dist);
o1.vx -= v * dy; // along the tangent
o1.vy += v * dx;
}
}
for(var i = 1; i < objects.length; i ++){
o1 = objects[i];
o1.vx = 0;
o1.vy = 0;
for(var j = 0; j <objects.length; j ++){
if(j !== i){
o2 = objects[j];
dx = o2.x - o1.x;
dy = o2.y - o1.y;
dist = Math.sqrt(dx * dx + dy * dy);
dx /= dist;
dy /= dist;
v = Math.sqrt(G * ( o2.mass ) / dist);
o1.vx += v * dy; // along the tangent
o1.vy -= v * dx;
}
}
}
}
//GAME LOOP
function gameLoop(){
update();
render();
requestAnimationFrame(gameLoop);
}
// every object exerts a force on every other object
function update(){
var dist,dx,dy,o1,o2,a;
// find force of acceleration each object applies to each object
for(var i = 0; i < objects.length; i ++){
o1 = objects[i];
for(var j = 0; j < objects.length; j ++){
if(i !== j){ // object can not apply force to them selves
o2 = objects[j];
dx = o2.x - o1.x;
dy = o2.y - o1.y;
dist = Math.sqrt(dx * dx + dy * dy);
dx /= dist; // normalise the line between the objects (makes the vector 1 unit long)
dy /= dist;
// get force
a = (G * o2.mass ) / (dist * dist);
o1.vx += a * dx;
o1.vy += a * dy;
}
}
}
// once all the forces have been found update objects positions
for(var i = 0; i < objects.length; i ++){
o1 = objects[i];
o1.x += o1.vx;
o1.y += o1.vy;
}
}
function render(){
ctx.clearRect(0, 0, c.width, c.height);
// kludge to fix drift
var offsetX = objects[0].x - sunStartX;
var offsetY = objects[0].y - sunStartY;
ctx.setTransform(1,0,0,1,-offsetX,-offsetY);
for(var i = 0; i < objects.length; i ++){
objects[i].draw();
}
ctx.setTransform(1,0,0,1,0,0);
}
setup();
requestAnimationFrame(gameLoop);
<canvas id='canvas'></canvas>
Okay, I've answered my own question.
Rather than making the star's gravity directly affect the x and y coordinates, I have a vx and vy of the object, and I cause the gravity to affect that value, and then just adjust x and y by vx and vy on each update.
Here's the code:
var c = document.getElementById('canvas');
var ctx = c.getContext('2d');
c.width = window.innerWidth;
c.height = window.innerHeight;
var star = {
x: c.width / 2,
y: c.height / 2,
r: 100,
g: 0.5,
draw: function()
{
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, 2*Math.PI);
ctx.fillStyle = 'orange';
ctx.fill();
ctx.closePath();
}
};
var node = {
x: c.width / 2,
y: 50,
r: 20,
vx: 15,
vy: 0,
draw: function()
{
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, 2*Math.PI);
ctx.fillStyle = 'blue';
ctx.fill();
ctx.closePath();
}
};
//GAME LOOP
function gameLoop()
{
update();
render();
window.requestAnimationFrame(gameLoop);
}
function update()
{
node.x += node.vx;
node.y += node.vy;
//Move towards star
var dx = star.x - node.x;
var dy = star.y - node.y;
var angle = Math.atan2(dy, dx);
node.vx += (Math.cos(angle) * star.g);
node.vy += (Math.sin(angle) * star.g);
}
function render()
{
ctx.clearRect(0, 0, c.width, c.height);
star.draw();
node.draw();
}
window.requestAnimationFrame(gameLoop);
<canvas id='canvas'></canvas>
本文标签: javascriptHTML5 CanvasHow to move an object in a directionStack Overflow
版权声明:本文标题:javascript - HTML5 Canvas - How to move an object in a direction? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745647384a2668216.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论