BatBut
BatBut

Reputation: 45

Making something travel in a circle in Javascript using canvas

This is my first time using canvasses, and my first time using Javascript to make a full on game-type thing, so forgive the beginner confusion!

I am trying to make a sort of circular Flappy Bird type thing where you travel around a circle, are drawn into the circle in a sort of gravity like way, and clicking makes you rise up. Go too high and you die, go too low and you die. However, I am falling down at the first hurdles...

The two main problems I have right now, starting with the smallest, is with the .clearRect function. I currently have it vaguely clearing behind the ball as it moves, but this is a little imperfect and cuts into the middle circle if it gets too close. If I set the whole window to clear on each frame, everything flashes horribly; is there a better way of doing this?

The second problem is just making the ball go in a circle... I have tried a lot of solutions, and I know that this is very likely to be a huge failing of my maths skills, but I can't make it orbit the main circle at all. I can make it move, and I have made it bounce off of the walls so that I can at least see what it's doing, but it just isn't circling. I imagine I'll be asking about the other aspects of this game at some point in the future if I have the same difficulty, but, as I said, I'm trying to learn!

In the dx and dy variable, 'sun' represents the middle point of the circle that I am trying to get the planet to orbit around. The rest of the variable is based on one answer I found elsewhere but I haven't managed to get it to work either.

var canvas = document.createElement("canvas"),
      context = canvas.getContext("2d"),
      sun = 300,
      sunRadius = sun / 2,    
      x = sun +110,
      y = sun -110,
        
      angle = 0,
      distance = 10,
      dx = sun + sunRadius * Math.cos(angle*Math.PI/180),
      dy = sun + sunRadius * Math.sin(angle*Math.PI/180),  
    
      planetRadius = 10;
    
    
    
    document.body.appendChild(canvas);
    canvas.width = canvas.height = sun * 2;
    
    function makeSun() {
      context.beginPath();
      context.strokeStyle = "yellow";
      context.arc(sun, sun, 60, 0, 2 * Math.PI);
      context.fillStyle = "yellow";
      context.fill();
      context.closePath();
    }
    
    function makePlanet() {
      context.beginPath();
      context.arc(x, y, planetRadius, 0, Math.PI * 2);
      context.fillStyle = "green";
      context.fill();
      context.strokeStyle = "green";
      context.stroke();
    }
    
    function draw() {
      context.clearRect(x + -20, y - 15, 40, 40);
      makePlanet();
      
       
       if (x + dx > canvas.width - planetRadius || x + dx < planetRadius) {
        dx = -dx;
      }
      if (y + dy > canvas.height - planetRadius || y + dy < planetRadius) {
        dy = -dy;
      }
      
    x += dx;
    y += dy;
    }
    
    
    setInterval(makeSun, 10);
    setInterval(draw, 10);

Upvotes: 3

Views: 1333

Answers (2)

Boris
Boris

Reputation: 1191

Ok, so

first :

the screen is flashing because you are setting two different intervals for makesun and draw. javascript makes zero promises that it won't update the screen between the time it cals makesun and draw. the best solution I think is to effectively clear the whole rect then redraw your sun ( the graphics are simple enough so you won't have performance issues).

second :

to animate the ball, you want to get the current time and decide how fast (in rotations / sec) you want it to go. As others suggested, requestAnimationFrame is better than setInterval, as it passes you a timestamp.

example

I've adapted your code to make the planet spin. I also added movement to the sun so you see the planet is actually relative to it. Have a look and please ask if there's something you don't understand.

const canvas = document.createElement("canvas"),
      context = canvas.getContext("2d"),
      checkbox = document.getElementById("moveSunBx"),
      sun = 300,
      sunRadius = sun / 2,
      controllables = {
        movesun : false,
        rotationspeed : 1/2 // half a turn every second (timestamps are in milliseconds)
      },

      distancePtoS = 110,//distance from planet to sun
      planetRadius = 10;

var   planetAngle = 0.0,// Starting angles
      sunAngle = 0.0,
      lastTimestamp;


var gui = new dat.GUI();
gui.add(controllables, 'movesun').name("move sun");
gui.add(controllables, 'rotationspeed', -6, 6).name("planet speed");// in turns per second

function makeSun(x, y) {
  context.beginPath();
  context.strokeStyle = "yellow";
  context.arc(x, y, 60, 0, 2 * Math.PI);
  context.fillStyle = "yellow";
  context.fill();
  context.closePath();
}

function makePlanet(x, y) {
  context.beginPath();
  context.arc(x, y, planetRadius, 0, Math.PI * 2);
  context.fillStyle = "green";
  context.fill();
  context.strokeStyle = "green";
  context.stroke();
}
    
function draw(timestamp) {
  requestAnimationFrame(draw);//immediately ask for next frame
  
  if(!lastTimestamp){
    lastTimestamp = timestamp;
    return;
  }
  

  var speed = Math.PI * 2.0 * controllables.rotationspeed / 1000, // convert speed from turns per second to radian per millisec 
      timestep = timestamp - lastTimestamp;
      
  lastTimestamp = timestamp;//we save the stamp

  planetAngle += timestep * speed;//we update the angle depending on the currentspeed and the timestep

  //angle = angle % Math.PI / 2;// this is for better comprehension, Math.cos and sin do the clamping for us
debugger;
  //let's make the sun move!!!
  if(controllables.movesun){
    sunAngle += timestep * speed;
  }
  var sunx = sunRadius + Math.cos(sunAngle /2) * distancePtoS;// <- sin of 2 angle gives a beautiful infinity
  var suny = sunRadius + Math.sin(sunAngle) * distancePtoS / 2;


  //x and y don't need to be kept, I made them local to draw()
  var planetx = sunx + Math.cos(planetAngle) * distancePtoS;
  var planety = suny + Math.sin(planetAngle) * distancePtoS;

  context.clearRect(0, 0, canvas.width, canvas.height);
  makeSun(sunx, suny);
  makePlanet(planetx, planety);
}
    
document.body.appendChild(canvas);
canvas.width = canvas.height = sun * 2;
    
draw();//we start the cycle
<script src="//cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script>

Upvotes: 2

user7951676
user7951676

Reputation: 367

Use requestAnimationFrame() instead of the set intervals and you shouldn't have the flashing afterwards and the equation for a semicircle is y = ±√(r^2-x^2) Use a variable to determine if it should be positive or negative and change the x variable of the orbiting object each draw to move it in a circle

Upvotes: 0

Related Questions