Johnny
Johnny

Reputation: 159

Can't get sprite to move in a circle path

I have this sprite called snitch and I'm trying to make it move in a circle, with the center in the canvas/2 (canvas is named space) and radius 30. I'm using the following code but it doesn't seem to do anything, the "snitch" just stands there doing nothing on coordinates (0,0). I've tried deleting these (0,0) coordinates from the MySprite2 function but when I do, the snitch doesn't even appear.

var ctx = space.getContext("2d"); 
var FPS = 40;
var snitch= new MySprite2("http://i.imgur.com/IgNKTbW.png");

function MySprite2 (img_url) {
  var x = this.x = 0;
  var y = this.y = 0; 
  this.visible= true;
  var img = this.MyImg2 = new Image();
  this.MyImg2.src = img_url;
}

MySprite2.prototype.Do_Frame_Things = function() {
  if (this.visible) ctx.drawImage(this.MyImg2, this.x, this.y);
}; 

function Do_a_Frame () {
  ctx.clearRect(0, 0, space.width, space.height);
  snitch.Do_Frame_Things();
}

function theMoves(snitch){
  var theta = 0;
  for (theta = 0; theta < 2 * Math.PI; theta+=0.1) { 
     snitch.x = (space.width/2) + Math.sin(theta)*30;
     snitch.y = (space.height/2) + Math.cos(theta)*30;
  }
}
setInterval(theMoves, 1000/FPS);
setInterval(Do_a_Frame, 1000/FPS);

Any idea what is wrong? OK GOT IT MOVING BUT now it just runs in a straight line, instead of a circle. New code below.

function MySprite (img_url) {
  var x = this.x = (space_x/2);
  var y = this.y = (space_y/2);
  var angle = this.angle = 0;
  this.visible= true;
  var img = this.MyImg = new Image();
  this.MyImg.src = img_url;
}

MySprite.prototype.Do_Frame_Things = function() {
  if (this.visible) ctx.drawImage(this.MyImg, this.x, this.y);
};  

function theMoves(){
  snitch.x += Math.cos(snitch.angle)*2;
  snitch.y += Math.sin(snitch.angle)*2;
}
setInterval(theMoves, 40);

Upvotes: 2

Views: 473

Answers (3)

Larry Lane
Larry Lane

Reputation: 2191

The first error I see is that your sin function will return undefined because you will need to call it using the following syntax:

 Math.sin()

The second error is that your snitch variable is undefined. The error is thrown by the following line of code.

 snitch.x = (space.width/2) + Math.sin(theta)*30;

Your third error is:

snitch.y = (space.height/2) + cos(theta)*30;

I created a more modular approach to help you with your circular animation.

Live Demo: http://codepen.io/larryjoelane/pen/obGPwY

html:

     <canvas width ="500" height="500" id="canvasOne"></canvas>

JavaScript:

 var canvas = new Canvas("canvasOne");

 var thisCanvas = canvas.instance("canvasOne");

 var imageURL = "http://bytewarestudios.com/assets/images/sphere.png";

 var image = canvas.createImage(imageURL);

 var context = canvas.context2D("canvasOne");

 /* Formula to determine x and y points on the circumference of a    
  * circle
  * cx and cy = (origin or center of the circle)
  * r = radius of the circle
  * a = angle in radians(360deg = 6.285714285714285714285714285714 radian) or 2 * PI
  *  Formulas examples below for x and y points
  *   var x = cx + r * Math.cos(a);
  *   var y = cy + r * Math.sin(a);
  */

 //initialize the degree variable
 var deg = 0;

 //frames per second
 var fps = 45;

 window.requestAnimationFrame(drawCircle); // start the animation

 function drawCircle() { //begin function

   setTimeout(function() {

     //increment the degrees
     deg = deg + 1;

     //used to offset the circle radius to bring the circle in from the border of 
     //the canvas
     var offset = 120;

     //radius of the circle
     var r = ((thisCanvas.width - offset) / 2);

     //x coordinate of the circle origin
     var cx = ((thisCanvas.width) / 2);

     //y coordinate of the circle origin
     var cy = ((thisCanvas.height) / 2);

     //store the angle in radians
     var a = canvas.toRadians(deg);

     var x = cx + r * Math.cos(a);
     var y = cy + r * Math.sin(a);

     //clear the canvas  
     context.clearRect(0, 0, thisCanvas.width, thisCanvas.height);

     //draw the first image
     context.drawImage(image, x, y);

     //start the animation
     window.requestAnimationFrame(drawCircle);

   }, 1000 / fps);

 } //end function

 function Canvas() { //begin canvas constructor    

   Canvas.prototype.createImage = function(imageURL) {

     //create a new image
     var image = new Image();

     //set the image source
     image.src = imageURL;

     //return the image object
     return image;

   };

   Canvas.prototype.context2D = function(id) {

     var canvas = document.getElementById(id);

     var context = canvas.getContext("2d");

     return context;

   };

   Canvas.prototype.instance = function(id) {

     var canvas = document.getElementById(id);

     return canvas;

   };

   Canvas.prototype.toRadians = function(angle) {

     return angle * (Math.PI / 180);

   };

 } //end canvas constructor

Upvotes: 0

Blindman67
Blindman67

Reputation: 54128

To many problems so I had done a rewrite. Have gone through and explained what I have done and why I do it.

First lines All good but don't need FPS as that will be up to the browser to decide. For a game you can never guarantee any frame rate unless you have dedicated hardware and a deterministic OS. Only dedicated games machine provide this.

var ctx = space.getContext("2d"); 

Using new and prototypes are not good for game design as they incur a lot of extra overhead that is not required. Infact it is generally a good idea to avoid using this type of constructor altogether.

Use a generator function and don't touch the prototype directly

Declare your methods first before the generator BTW in Javascript capitals at the start of a variable is reserved for class type object but that is a personal style choice nothing more

// draw the sprite is visible
var doFrameThings = function() {
    if (this.visible) { // though you don't have to put the curly braces here
                        // you should make it a habit  to always use them
        ctx.drawImage(this.myImg , this.x, this.y);
    }
}; 

Creating a function to update the sprite, use the last frame time to ensure a consistent velocity if the frame rate should drop

var updateSprite = function(frameTime){
    this.theta += this.deltaTheta * frameTime;
    this.x = (space.width/2) + Math.sin(this.theta)*30;
    this.y = (space.height/2) + Math.cos(this.theta)*30;
}

Creates a sprite. This method is almost indistinguishable from the prototypal method but it offers greater performance

function createSprite(imgURL){
    var sprite = {  // create the sprite
        x:0,
        y:0,
        theta: 0,
        deltaTheta: Math.PI/40,
        visible:false,           // not visible until it has loaded.
        draw : doFrameThings,    // add the draw function
        update : updateSprite,   // add the update function
        myImg : (function(){     // call a annon function that creates the 
                                 // image returning it to myImg
            var image = new Image();
            image.src = imgUrl;
            image.onload = function(){  // flag it as visible only when it has loaded.
                sprite.visible = true;
            };
            return image;
        })()
    };
    return sprite; // return the created sprite
                   // this is exactly the same as new does except new returns
                   // this instead of the named object.
}        

Create a sprite. Now you don't have to invoke all the overhead of using New and engaging the prototype system.

var snitch = createSprite("http://i.imgur.com/IgNKTbW.png");

Using setInterval is a very bad way to do animation. First you have two setInterval s they will get out of sync and there is no guarantee that they will occur at the requested interval. Nor do they sync up with the display hardware so you will get shearing. Also because they are not synced to the browser's rendering systems you may get flickering or worse.

If your rendering time gets greater than the interval it will eventually cause your app to crash and there is no easy way to know if that is happening

So never use setInterval. Personally NEVER use setInterval for anything it is a dangerous method and should be removed from Javascript.

Use requestAnimationFrame to get a steady 60 fps and synced to the display hardware and the browser's rendering system. Because it is synced to the display the time between frames will be in units of 1/60th of a second approx. If you miss the first frame you will have to wait for the next refresh which will be in 1/60 of a second.

Create a main loop/update function and call all the animations from the same function.

var lastTime = new Date().valueOf();  // for tracking the frame time
function update(time){  // requestAnimationFrame provides the current time
    var frameTime = time-lastTime;
    // clear the canvas
    ctx.clearRect(0, 0, space.width, space.height);

    snitch.update(frameTime); // update sprite
    snitch.draw(); // draw the sprite


    requestAnimationFrame(update); // request the next animation frame
    lastTime = time;  // save the time this frame was called 
}

requestAnimationFrame(update); // start the animation

That should get the snitch moving well round.

Upvotes: 1

st.
st.

Reputation: 589

You have a snitch parameter on theMoves function. It gets in the way as an undefined object when it's get called by the interval tick with no parameters.

If you want to use it globally, just simply delete the parameter.

Or you can do it this way

setInterval(function(){theMoves(snitch)}, 1000/FPS);

Upvotes: 0

Related Questions