Reputation: 159
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
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
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
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