How would I make a randomly generated collider for a game? - javascript

Catbus Game code
I want to add colliders into my game, so that my main character, the catbus, will jump over them. When he hits them, it will show a game over. I want to be able to randomly generate them, as they come from the right side, and move to the left side. Like the Dinosaur game that shows up when the wifi goes out. I'd like to implement a picture to serve as the collider. The collider that I plan to use goes by:
var tinyToto
I have no idea how to make a collider such as that, nor how to create a random generator. Any help is much appreciated, even if it is only a small portion of it.
Code is as follows:
let img; //background
var bgImg; //also the background
var x1 = 0;
var x2;
var scrollSpeed = 4; //how fast background is
let bing; //for music
let cat;
var mode; //determines whether the game has started
let gravity = 0.2; //jumping forces
let velocity = 0.1;
let upForce = 7;
let startY = 730; //where cat bus jumps from
let startX = 70;
var font1; //custom fonts
var font2;
p5.disableFriendlyErrors = true; //avoids errors
function preload() {
bgImg = loadImage("backgwound.png"); //importing background
bing = loadSound("catbus theme song.mp3"); //importing music
font1 = loadFont("Big Font.TTF");
font2 = loadFont("Smaller Font.ttf");
}
function setup() {
createCanvas(1000, 1000); //canvas size
img = loadImage("backgwound.png"); //background in
x2 = width;
bing.loop(); //loops the music
cat = {
//coordinates for catbus
x: startX,
y: startY,
};
catGif = createImg("catgif.gif"); //creates catbus
catGif.position(cat.x, cat.y); //creates position
catGif.size(270, 100); //creates how big
mode = 0; //game start
textSize(50); //text size
}
function draw() {
let time = frameCount; //start background loop
image(img, 0 - time, 0);
image(bgImg, x1, 2, width, height);
image(bgImg, x2, 2, width, height);
x1 -= scrollSpeed;
x2 -= scrollSpeed;
if (x1 <= -width) {
x1 = width;
}
if (x2 <= -width) {
x2 = width;
} //end background loop
fill(128 + sin(frameCount * 0.05) * 128); //text colour
if (mode == 0) {
textSize(20);
textFont(font1);
text("press SPACE to start the game!", 240, 500); //what text to type
}
fill("white");
if (mode == 0) {
textSize(35);
textFont(font2);
text("CATBUS BIZZARE ADVENTURE", 90, 450); //what text to type
}
cat.y = cat.y + velocity; //code for jumping
velocity = velocity + gravity;
if (cat.y > startY) {
velocity = 0;
cat.y = startY;
}
catGif.position(cat.x, cat.y);
}
function keyPressed() {
if (keyCode === 32 && velocity == 0) {
//spacebar code
mode = 1;
velocity += -upForce;
}
}

Related

Is there a way to space correctly a string of text in p5.js?

I saw many times really cool examples of kinetic typography. In these examples every letter is a particle. The text is a particle system and it may be subject to various forces such gravity or even centrifugal force. These systems are made in Processing and in p5.js.
I am building an interactive screen for the web filled with text using p5.js, inspired by these example of kinetic typography.
When the user moves the cursor on the text this start bounce all around the screen.
I translated the sketch from Processing to p5.js and I have noticed this problem related to the spacing of the text in the setup() function.
In Processing the code looks like this and it function correctly.
I want to focus on this section of the Processing code:
void setup() {
size(640, 360);
//load the font
f = createFont("Arial", fontS, true);
textFont(f);
// Create the array the same size as the String
springs = new Spring[message.length()];
// Initialize Letters (Springs) at the correct x location
int locx = 40;
//initialize Letters (Springs) at the correct y location
int locy = 100;
for (int i = 0; i < message.length(); i++) {
springs[i] = new Spring(locx, locy, 40, springs, i, message.charAt(i));
locx += textWidth(message.charAt(i));
//boudaries of text just to make it a nice "go to head"
if (locx >= 360) {
locy+=60;
locx = 40;
}
}
}
You can see the result
As you can see the parameter
springs[i] = new Spring(locx, locy, 40, springs, i, message.charAt(i));
locx += textWidth(message.charAt(i));
does its job, spacing the letters.
However when I translate this sketch in P5.js, I don't get the same nice spacing.
This is the same section but is the p5.js code:
function setup() {
createCanvas(640, 360);
noStroke();
textAlign(LEFT);
// Create the array the same size as the String
springs = new Array(message.length);
// Initialize Letters (Springs) at the correct x location
var locx = 10;
//initialize Letters (Springs) at the correct y location
var locy = 120;
for (var i = 0; i < message.length; i++) {
springs[i] = new Spring(locx, locy, 40, springs, i, message.charAt(i));
locx += textWidth(message.charAt(i));
//boudaries of text just to make it a nice "go to head"
if(locx>= 390){
locy+= 60;
locx= 40;
}
}
}
The result is show
I am sure, in the p5js code, that there is a problem regarding this part:
springs[i] = new Spring(locx, locy, 40, springs, i, message.charAt(i));
locx += textWidth(message.charAt(i));
Because I tried to fix it multiplying the value of the locx as shown here:
springs[i] = new Spring(locx*5, locy, 40, springs, i, message.charAt(i));
I then got
which can seems correct, but I'm sure it is not.
At this point I have no idea how to fix it, it must be something of p5.js I'm unaware of.
Any help is greatly appreciate.
//Edit
As suggested in the comment here you can find the Spring class written in p5.js:
// Spring class
class Spring {
constructor (_x, _y, _s, _others, _id, letter_) {
// Screen values
this.x_pos = this.tempxpos = _x;
this.y_pos = this.tempypos = _y;
this.size = _s;
this.over = false;
this.move = false;
// Spring simulation constants
this.mass = 8.0; // Mass
this.k = 0.2; // Spring constant
this.damp =0.98; // Damping
this.rest_posx = _x; // Rest position X
this.rest_posy = _y; // Rest position Y
// Spring simulation variables
//float pos = 20.0; // Position
this.velx = 0.0; // X Velocity
this.vely = 0.0; // Y Velocity
this.accel = 0; // Acceleration
this.force = 0; // Force
this.friends = _others;
this.id = _id;
this.letter = letter_;
}
update() {
if (this.move) {
this.rest_posy = mouseY;
this.rest_posx = mouseX;
}
this.force = -this.k * (this.tempypos - this.rest_posy); // f=-ky
this.accel = this.force / this.mass; // Set the acceleration, f=ma == a=f/m
this.vely = this.damp * (this.vely + this.accel); // Set the velocity
this.tempypos = this.tempypos + this.vely; // Updated position
this.force = -this.k * (this.tempxpos - this.rest_posx); // f=-ky
this.accel = this.force / this.mass; // Set the acceleration, f=ma == a=f/m
this.velx = this.damp * (this.velx + this.accel); // Set the velocity
this.tempxpos = this.tempxpos + this.velx; // Updated position
if ((this.overEvent() || this.move) && !(this.otherOver()) ) {
this.over = true;
} else {
this.over = false;
}
}
// Test to see if mouse is over this spring
overEvent() {
let disX = this.x_pos - mouseX;
let disY = this.y_pos - mouseY;
let dis = createVector(disX, disY);
if (dis.mag() < this.size / 2 ) {
return true;
} else {
return false;
}
}
// Make sure no other springs are active
otherOver() {
for (let i = 0; i < message.length; i++) {
if (i != this.id) {
if (this.friends[i].over == true) {
this.velx = -this.velx;
return true;
}
}
}
return false;
}
//the springs collides with the edges of the screen
box_collision() {
if(this.tempxpos+this.size/2>width){
this.tempxpos = width-this.size/2;
this.velx = -this.velx;
} else if (this.tempxpos - this.size/2 < 0){
this.tempxpos = this.size/2;
this.velx = -this.velx;
}
if (this.tempypos+this.size/2>height) {
this.tempypos = height-this.size/2;
this.vely = -this.vely;
} else if (this.tempypos- this.size/2 < 0) {
this.tempypos = this.size/2;
this.vely = -this.vely;
}
}
//the springs collides with each other
collide() {
for (var i = this.id + 1; i < message.length; i++) {
var dx = this.friends[i].tempxpos - this.tempxpos;
var dy = this.friends[i].tempypos - this.tempypos;
var distance = sqrt(dx*dx + dy*dy);
var minDist = this.friends[i].size/2 + this.size/2;
if (distance < minDist) {
var angle = atan2(dy, dx);
var targetX = this.tempxpos + cos(angle) * minDist;
var targetY = this.tempypos + sin(angle) * minDist;
var ax = (targetX - this.friends[i].tempxpos) * 0.01;
var ay = (targetY - this.friends[i].tempypos) * 0.01;
this.velx -= ax;
this.vely -= ay;
this.friends[i].velx += ax;
this.friends[i].vely += ay;
}
}
}
//display the letter Particle
display() {
if (this.over) {
fill(255, 0, 0);
} else {
fill(255);
}
noStroke();
textSize(fontS);
//for debugging
// ellipse(this.tempxpos, this.tempypos, this.size, this.size);
text(this.letter, this.tempxpos, this.tempypos);
}
pressed() {
if (this.over) {
this.move = true;
} else {
this.move = false;
}
}
released() {
this.move = false;
this.rest_posx = this.x_pos;
this.rest_posy = this.y_pos;
}
}
And here you can find the link to the P5.js editor with the code:
Spring text p5js code
note: I had to fix the Spring class because I didn't realize that all of my functions where initialized in the constructor. Now the functions that compose the Class are outside the constructor. I still haven't figured out how to fix the spacing problem.
I managed to resolve the issue.
As far as I understood the code was correct from the beginning.
What I missed through the journey was that I have to set the font size in the function setup() if I want to display the text on the screen correctly.
You can still see the result by checking the link to the p5.js editor I posted previously.

I'm attempting to write a program where the ball ignores hitting a green obstacle but ends the game when a red obstacle indicates game over

I'm attempting to write a JavaScript program where the ball ignores hitting a green obstacle but ends the game when a red obstacle indicates game over. However in my code the game ends when the ball hits an obstacle of any color.
My definition of the obstacle is:
function Obstacle(x, size, horizon, color) {
this.x = x;
this.y = horizon - size;
this.size = size;
this.color = color;
this.onScreen = true;
}
/**
* handle x and onScreen values
*/
Obstacle.prototype.update = function(speed) {
/* check if offscreen */
this.onScreen = (this.x > -this.size);
/* movement */
this.x -= speed;
};
Obstacle.prototype.draw = function() {
fill(this.color);
stroke(255);
strokeWeight(2);
rect(this.x, this.y, this.size, this.size);
};
Obstacle.prototype.hits = function(ball) {
var halfSize = this.size / 2;
var minimumDistance = halfSize + (ball.radius); // closest before collision
/* find center coordinates */
var xCenter = this.x + halfSize;
var yCenter = this.y + halfSize;
var distance = dist(xCenter, yCenter, ball.x, ball.y); // calculate distance from centers
return (distance < minimumDistance); // return result
};
This is the code for the game:
function setup() {
createCanvas(600, 200);
textAlign(CENTER);
horizon = height - 40;
score = 0;
obstacleSpeed = 6;
var size = 20;
ball = new bol(size * 2, height - horizon, size);
textSize(20);
}
function draw() {
background(51);
drawHUD();
handleLevel(frameCount);
ball.update(horizon);
handleObstacles();
//handlepowerups();
}
/**
* draws horizon & score
*/
function drawHUD() {
/* draw horizon */
stroke(255);
strokeWeight(2);
line(0, horizon, width, horizon);
/* draw score */
noStroke();
text("Score: " + score, width / 2, 30);
ball.draw();
}
/**
* updates, draws, and cleans out the obstacles
*/
function handleObstacles() {
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].update(obstacleSpeed);
obstacles[i].draw();
if (obstacles[i].hits(ball)) // if there's a collision
{
if (obstacles[i].color = color(225, 0, 0))
endGame();
else
obstacles[i].color = color(0, 0, 225)
}
if (!obstacles[i].onScreen) // if it's no longer showing
obstacles.splice(i, 1); // delete from array
}
}
/**
* speeds game up, pushes new obstacles, & handles score
*/
function handleLevel(n) {
if (n % 100 === 0) { // every 0.5 seconds
var n = noise(n);
if (n > 0.5)
newObstacle(n); // push new obstacle
if (n % 120 === 0) // every 2 seconds
obstacleSpeed *= 1.05; // speed up
}
score++;
}
/**
* pushes random obstacle
*/
function newObstacle(n) {
var col1 = color(225, 0, 0);
var col2 = color(0, 225, 0);
var size = random(25) + 20;
var obs = new Obstacle(width + size, size, horizon, col2);
var obs2 = new Obstacle(width + size, size, horizon, col1);
var cases = random(1);
if (cases < 0.5)
obstacles.push(obs);
else
obstacles.push(obs2)
}
function keyPressed() {
if ((keyCode === UP_ARROW || keyCode === 32) && ball.onGround) // jump if possible
ball.jump();
boingaudio.play()
}
function endGame() {
noLoop();
noStroke();
textSize(40);
text("GAME OVER", width / 2, height / 2);
if (highscore !== null) {
if (score > highscore) {
localStorage.setItem("highscore", score);
}
} else {
localStorage.setItem("highscore", score);
}
textSize(20);
text("Highscore: " + highscore, width / 2, height / 2 + 20);
gameoveraudio.play()
}
I don't understand why the ball keeps ending game for no matter what colored ball appears. Can you help me figure out what's wrong?
You seem to be assigning a value to obstacles[i].color rather than testing it:
if (obstacles[i].color = color(225, 0, 0))
endGame();
else
obstacles[i].color = color(0, 0, 225)
This should probably read:
if (obstacles[i].color == color(225, 0, 0))
endGame();
Without your else, which seemed to serve no purpose.

How to avoid repeated?

Good day,
I am generating some circles with colors, sizes and positions. All of this things randomly.
But, my problem is that I do not want them to collide, so that no circle is inside another, not even a little bit.
The logic explained in detail within the code, I would like to know why the failure and why the infinite loop.
The important functions are:
checkSeparation and setPositions
window.addEventListener("load", draw);
function draw() {
var canvas = document.getElementById("balls"), // Get canvas
ctx = canvas.getContext("2d"); // Context
canvas.width = document.body.clientWidth; // Set canvas width
canvas.height = document.documentElement.scrollHeight; // Height
var cW = canvas.width, cH = canvas.height; // Save in vars
ctx.fillStyle = "#fff022"; // Paint background
ctx.fillRect(0, 0, cW, cH); // Coordinates to paint
var arrayOfBalls = createBalls(); // create all balls
setPositions(arrayOfBalls, cW, cH);
arrayOfBalls.forEach(ball => { // iterate balls to draw
ctx.beginPath(); // start the paint
ctx.fillStyle = ball.color;
ctx.arc(ball.x, ball.y, ball.radius, 0, (Math.PI/180) * 360, false); // draw the circle
ctx.fill(); // fill
ctx.closePath(); // end the paint
});
}
function Ball() {
this.x = 0; // x position of Ball
this.y = 0; // y position of Ball
this.radius = Math.floor(Math.random() * ( 30 - 10 + 1) + 10);
this.color = "";
}
Ball.prototype.setColor = function(){
for(var j = 0, hex = "0123456789ABCDEF", max = hex.length,
random, str = ""; j <= 6; j++, random = Math.floor(Math.random() * max), str += hex[random])
this.color = "#" + str;
};
function random(val, min) {
return Math.floor(Math.random() * val + min); // Random number
}
function checkSeparation(value, radius, toCompare) {
var min = value - radius, // Min border of circle
max = value + radius; // Max border of circle
// Why ? e.g => x position of circle + this radius it will be its right edge
for(; min <= max; min++) {
if(toCompare.includes(min)) return false;
/*
Since all the positions previously obtained, I add them to the array, in order to have a reference when verifying the other positions and that they do NOT collide.
Here I check if they collide.
In the range of:
[pos x - its radius, pos x + its radius]
*/
}
return true; // If they never collided, it returns true
}
function createBalls() {
var maxBalls = 50, // number of balls
balls = []; // array of balls
for(var j = 0; j < maxBalls; j++) { // create 50 balls
var newBall = new Ball(); // create ball
newBall.setColor(); // set the ball color
balls.push(newBall); //push the ball to the array of balls
}
return balls; // return all balls to draw later
}
function setPositions(balls, canvasW, canvasH) {
var savedPosX = [], // to save x pos of balls
savedPosY = []; // to save y pos of balls
for(var start = 0, max = balls.length; start < max; start++) {
var current = balls[start], // current ball
randomX = random(canvasW, current.radius), // get random value for x pos
randomY = random(canvasH, current.radius); // get random value for y pos
if(checkSeparation(randomX, current.radius, savedPosX)) {
current.x = randomX; // If it position, along with your radio does not touch another circle, I add the position
} else {
// start--; continue;
console.log("X: The above code causes an infinite loop");
}
if(checkSeparation(randomY, current.radius, savedPosY)) {
current.y = randomY;
} else {
// start--; continue;
console.log("Y: The above code causes an infinite loop");
}
}
}
body,html {
margin: 0; border: 0; padding: 0; overflow: hidden;
}
<canvas id="balls"></canvas>
In your code, you test possible collisions by means of arrays of already used x and y positions, but you never add new positions to these arrays. You also check the x and y coordinates separately, which means you are really testing a collision of a bounding box.
Two circles collide when the distance between their centres is smaller than the sum of their radii, so you could use:
function collides(balls, n, x, y, r) {
for (let i = 0; i < n; i++) {
let ball = balls[i];
let dx = ball.x - x;
let dy = ball.y - y;
let dd = dx*dx + dy*dy;
let rr = r + ball.radius;
if (dd < rr * rr) return true;
}
return false;
}
function setPositions(balls, canvasW, canvasH) {
for (let i = 0, max = balls.length; i < max; i++) {
let ball = balls[i],
r = ball.radius,
maxTries = 20;
ball.x = -canvasW;
ball.y = -canvasH;
for (let tries = 0; tries = maxTries; tries++) {
let x = random(canvasW - 2*r, r),
y = random(canvasH - 2*r, r);
if (!collides(balls, i, x, y, r)) {
ball.x = x;
ball.y = y;
break;
}
}
}
}
This is reasonably fast for 50 balls, but will be slow if you have more balls. In that case, some spatial data structures can speed up the collision search.
You must also guard against the case that no good place can be found. The code above gives up after 20 tries and moves the ball outside the visible canvas. You can improve the chances of placing balls by sorting the balls by radius and plaing the large balls first.
Finally, you add one hex digit too many to your random colour. (That for loop, where everything happens in the loop control is horrible, by the way.)

Moving a sprite to click location and stop there PIXIJS

This simple game makes a sprite move around to the position a user clicks. I got it working that the sprite moves to the location, but I need to make it stop at the click location. This code makes the sprite only stop at the click location when the sprite moves towards the bottom right corner. How do I fix this to make it always stop at the click location?
var Container = PIXI.Container,
autoDetectRenderer = PIXI.autoDetectRenderer,
loader = PIXI.loader,
resources = PIXI.loader.resources,
Sprite = PIXI.Sprite;
var stage = new PIXI.Container(),
renderer = PIXI.autoDetectRenderer(1000, 1000);
document.body.appendChild(renderer.view);
PIXI.loader
.add("animal.png")
.load(setup);
var rocket, state;
function setup() {
//Create the `tileset` sprite from the texture
var texture = PIXI.utils.TextureCache["animal.png"];
//Create a rectangle object that defines the position and
//size of the sub-image you want to extract from the texture
var rectangle = new PIXI.Rectangle(192, 128, 32, 32);
//Tell the texture to use that rectangular section
texture.frame = rectangle;
//Create the sprite from the texture
rocket = new Sprite(texture);
rocket.anchor.x = 0.5;
rocket.anchor.y = 0.5;
rocket.x = 50;
rocket.y = 50;
rocket.vx = 0;
rocket.vy = 0;
//Add the rocket to the stage
stage.addChild(rocket);
document.addEventListener("click", function(){
rocket.clickx = event.clientX;
rocket.clicky = event.clientY;
var x = event.clientX - rocket.x;
var y = event.clientY - rocket.y;
rocket.vmax = 5;
var total = Math.sqrt(x * x + y * y);
var tx = x/total;
var ty = y/total;
rocket.vx = tx*rocket.vmax;
rocket.vy = ty*rocket.vmax;
});
state = play;
gameLoop();
}
function gameLoop() {
//Loop this function at 60 frames per second
requestAnimationFrame(gameLoop);
state();
//Render the stage to see the animation
renderer.render(stage);
}
function play(){
rocket.x += rocket.vx;
rocket.y += rocket.vy;
if(rocket.x >= rocket.clickx){
if(rocket.y >= rocket.clicky){
rocket.x = rocket.clickx;
rocket.y = rocket.clicky;
}
}
}
So your sprite has the velocity 5. Then let's just check out the distance between the sprite and the stop position. Whenever it's less than 5, make it stop at the position.
function play(){
var dx = rocket.x - rocket.clickx;
var dy = rocket.y - rocket.clicky;
if (Math.sqrt(dx * dx + dy * dy) <= 5) {
rocket.x = rocket.clickx;
rocket.y = rocket.clicky;
}
else {
rocket.x += rocket.vx;
rocket.y += rocket.vy;
}
}
You can modify the if statement like below to avoid Math.srqt call.
if ((dx * dx + dy * dy) <= (5 * 5)) {

How to create a camera view in canvas that will follow a players rotation and rotation?

I'm trying to create a game in canvas with javascript where you control a spaceship and have it so that the canvas will translate and rotate to make it appear like the spaceship is staying stationary and not rotating.
Any help would be greatly appreciated.
window.addEventListener("load",eventWindowLoaded, false);
function eventWindowLoaded() {
canvasApp();
}
function canvasSupport() {
return Modernizr.canvas;
}
function canvasApp() {
if (!canvasSupport()) {
return;
}
var theCanvas = document.getElementById("myCanvas");
var height = theCanvas.height; //get the heigth of the canvas
var width = theCanvas.width; //get the width of the canvas
var context = theCanvas.getContext("2d"); //get the context
var then = Date.now();
var bgImage = new Image();
var stars = new Array;
bgImage.onload = function() {
context.translate(width/2,height/2);
main();
}
var rocket = {
xLoc: 0,
yLoc: 0,
score : 0,
damage : 0,
speed : 20,
angle : 0,
rotSpeed : 1,
rotChange: 0,
pointX: 0,
pointY: 0,
setScore : function(newScore){
this.score = newScore;
}
}
function Star(){
var dLoc = 100;
this.xLoc = rocket.pointX+ dLoc - Math.random()*2*dLoc;
this.yLoc = rocket.pointY + dLoc - Math.random()*2*dLoc;
//console.log(rocket.xLoc+" "+rocket.yLoc);
this.draw = function(){
drawStar(this.xLoc,this.yLoc,20,5,.5);
}
}
//var stars = new Array;
var drawStars = function(){
context.fillStyle = "yellow";
if (typeof stars !== 'undefined'){
//console.log("working");
for(var i=0;i< stars.length ;i++){
stars[i].draw();
}
}
}
var getDistance = function(x1,y1,x2,y2){
var distance = Math.sqrt(Math.pow((x2-x1),2)+Math.pow((y2-y1),2));
return distance;
}
var updateStars = function(){
var numStars = 10;
while(stars.length<numStars){
stars[stars.length] = new Star();
}
for(var i=0; i<stars.length; i++){
var tempDist = getDistance(rocket.pointX,rocket.pointY,stars[i].xLoc,stars[i].yLoc);
if(i == 0){
//console.log(tempDist);
}
if(tempDist > 100){
stars[i] = new Star();
}
}
}
function drawRocket(xLoc,yLoc, rWidth, rHeight){
var angle = rocket.angle;
var xVals = [xLoc,xLoc+(rWidth/2),xLoc+(rWidth/2),xLoc-(rWidth/2),xLoc-(rWidth/2),xLoc];
var yVals = [yLoc,yLoc+(rHeight/3),yLoc+rHeight,yLoc+rHeight,yLoc+(rHeight/3),yLoc];
for(var i = 0; i < xVals.length; i++){
xVals[i] -= xLoc;
yVals[i] -= yLoc+rHeight;
if(i == 0){
console.log(yVals[i]);
}
var tempXVal = xVals[i]*Math.cos(angle) - yVals[i]*Math.sin(angle);
var tempYVal = xVals[i]*Math.sin(angle) + yVals[i]*Math.cos(angle);
xVals[i] = tempXVal + xLoc;
yVals[i] = tempYVal+(yLoc+rHeight);
}
rocket.pointX = xVals[0];
rocket.pointY = yVals[0];
//rocket.yLoc = yVals[0];
//next rotate
context.beginPath();
context.moveTo(xVals[0],yVals[0])
for(var i = 1; i < xVals.length; i++){
context.lineTo(xVals[i],yVals[i]);
}
context.closePath();
context.lineWidth = 5;
context.strokeStyle = 'blue';
context.stroke();
}
var world = {
//pixels per second
startTime: Date.now(),
speed: 50,
startX:width/2,
startY:height/2,
originX: 0,
originY: 0,
xDist: 0,
yDist: 0,
rotationSpeed: 20,
angle: 0,
distance: 0,
calcOrigins : function(){
world.originX = -world.distance*Math.sin(world.angle*Math.PI/180);
world.originY = -world.distance*Math.cos(world.angle*Math.PI/180);
}
};
var keysDown = {};
addEventListener("keydown", function (e) {
keysDown[e.keyCode] = true;
}, false);
addEventListener("keyup", function (e) {
delete keysDown[e.keyCode];
}, false);
var update = function(modifier) {
if (37 in keysDown) { // Player holding left
rocket.angle -= rocket.rotSpeed* modifier;
rocket.rotChange = - rocket.rotSpeed* modifier;
//console.log("left");
}
if (39 in keysDown) { // Player holding right
rocket.angle += rocket.rotSpeed* modifier;
rocket.rotChange = rocket.rotSpeed* modifier;
//console.log("right");
}
};
var render = function (modifier) {
context.clearRect(-width*10,-height*10,width*20,height*20);
var dX = (rocket.speed*modifier)*Math.sin(rocket.angle);
var dY = (rocket.speed*modifier)*Math.cos(rocket.angle);
rocket.xLoc += dX;
rocket.yLoc -= dY;
updateStars();
drawStars();
context.translate(-dX,dY);
context.save();
context.translate(-rocket.pointX,-rocket.pointY);
context.translate(rocket.pointX,rocket.pointY);
drawRocket(rocket.xLoc,rocket.yLoc,50,200);
context.fillStyle = "red";
context.fillRect(rocket.pointX,rocket.pointY,15,5);
//context.restore(); // restores the coordinate system back to (0,0)
context.fillStyle = "green";
context.fillRect(0,0,10,10);
context.rotate(rocket.angle);
context.restore();
};
function drawStar(x, y, r, p, m)
{
context.save();
context.beginPath();
context.translate(x, y);
context.moveTo(0,0-r);
for (var i = 0; i < p; i++)
{
context.rotate(Math.PI / p);
context.lineTo(0, 0 - (r*m));
context.rotate(Math.PI / p);
context.lineTo(0, 0 - r);
}
context.fill();
context.restore();
}
// the game loop
function main(){
requestAnimationFrame(main);
var now = Date.now();
var delta = now - then;
update(delta / 1000);
//now = Date.now();
//delta = now - then;
render(delta / 1000);
then = now;
// Request to do this again ASAP
}
var w = window;
var requestAnimationFrame = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.msRequestAnimationFrame || w.mozRequestAnimationFrame;
//start the game loop
//gameLoop();
//event listenters
bgImage.src = "images/background.jpg";
} //canvasApp()
Origin
When you need to rotate something in canvas it will always rotate around origin, or center for the grid if you like where the x and y axis crosses.
You may find my answer here useful as well
By default the origin is in the top left corner at (0, 0) in the bitmap.
So in order to rotate content around a (x,y) point the origin must first be translated to that point, then rotated and finally (and usually) translated back. Now things can be drawn in the normal order and they will all be drawn rotated relative to that rotation point:
ctx.translate(rotateCenterX, rotateCenterY);
ctx.rotate(angleInRadians);
ctx.translate(-rotateCenterX, -rotateCenterY);
Absolute angles and positions
Sometimes it's easier to keep track if an absolute angle is used rather than using an angle that you accumulate over time.
translate(), transform(), rotate() etc. are accumulative methods; they add to the previous transform. We can set absolute transforms using setTransform() (the last two arguments are for translation):
ctx.setTransform(1, 0, 0, 1, rotateCenterX, rotateCenterY); // absolute
ctx.rotate(absoluteAngleInRadians);
ctx.translate(-rotateCenterX, -rotateCenterY);
The rotateCenterX/Y will represent the position of the ship which is drawn untransformed. Also here absolute transforms can be a better choice as you can do the rotation using absolute angles, draw background, reset transformations and then draw in the ship at rotateCenterX/Y:
ctx.setTransform(1, 0, 0, 1, rotateCenterX, rotateCenterY);
ctx.rotate(absoluteAngleInRadians);
ctx.translate(-rotateCenterX, -rotateCenterY);
// update scene/background etc.
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transforms
ctx.drawImage(ship, rotateCenterX, rotateCenterY);
(Depending on orders of things you could replace the first line here with just translate() as the transforms are reset later, see demo for example).
This allows you to move the ship around without worrying about current transforms, when a rotation is needed use the ship's current position as center for translation and rotation.
And a final note: the angle you would use for rotation would of course be the counter-angle that should be represented (ie. ctx.rotate(-angle);).
Space demo ("random" movements and rotations)
The red "meteors" are dropping in one direction (from top), but as the ship "navigates" around they will change direction relative to our top view angle. Camera will be fixed on the ship's position.
(ignore the messy part - it's just for the demo setup, and I hate scrollbars... focus on the center part :) )
var img = new Image();
img.onload = function() {
var ctx = document.querySelector("canvas").getContext("2d"),
w = 600, h = 400, meteors = [], count = 35, i = 0, x = w * 0.5, y, a = 0, a2 = 0;
ctx.canvas.width = w; ctx.canvas.height = h; ctx.fillStyle = "#555";
while(i++ < count) meteors.push(new Meteor());
(function loop() {
ctx.clearRect(0, 0, w, h);
y = h * 0.5 + 30 + Math.sin((a+=0.01) % Math.PI*2) * 60; // ship's y and origin's y
// translate to center of ship, rotate, translate back, render bg, reset, draw ship
ctx.translate(x, y); // translate to origin
ctx.rotate(Math.sin((a2+=0.005) % Math.PI) - Math.PI*0.25); // rotate some angle
ctx.translate(-x, -y); // translate back
ctx.beginPath(); // render some moving meteors for the demo
for(var i = 0; i < count; i++) meteors[i].update(ctx); ctx.fill();
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transforms
ctx.drawImage(img, x - 32, y); // draw ship as normal
requestAnimationFrame(loop); // loop animation
})();
};
function Meteor() { // just some moving object..
var size = 5 + 35 * Math.random(), x = Math.random() * 600, y = -200;
this.update = function(ctx) {
ctx.moveTo(x + size, y); ctx.arc(x, y, size, 0, 6.28);
y += size * 0.5; if (y > 600) y = -200;
};
}
img.src = "http://i.imgur.com/67KQykW.png?1";
body {background:#333} canvas {background:#000}
<canvas></canvas>

Categories

Resources