I am developing a basic game where the user needs to go through the openings and avoid crashing with the obstacles. My issue now is that the flow of the game is from the bottom to the top, when in fact I need the obstacles to come from the top to the bottom.
What am I missing in the code? Any help is appreciated.
let myObstacles = [];
let myGameArea = {
canvas: document.createElement("canvas"),
frames: 0,
start: function() {
this.canvas.width = 700;
this.canvas.height = 500;
this.context = this.canvas.getContext("2d");
this.canvas.classList.add('canvasBg');
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.interval = setInterval(updateGameArea, 20);
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
},
stop: function() {
clearInterval(this.interval);
},
score: function() {
var points = Math.floor(this.frames / 5);
this.context.font = "18px serif";
this.context.fillStyle = "black";
this.context.fillText("Score: " + points, 350, 50);
}
}
class Component {
constructor(width, height, color, x, y) {
this.width = width;
this.height = height;
this.color = color;
this.x = x;
this.y = y;
this.speedX = 0;
this.speedY = 0;
}
update() {
let ctx = myGameArea.context;
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
newPos() {
this.x += this.speedX;
this.y += this.speedY;
}
left() {
return this.x;
}
right() {
return this.x + this.width;
}
top() {
return this.y;
}
bottom() {
return this.y + this.height;
}
crashWith(obstacle) {
return !(
this.bottom() < obstacle.top() ||
this.top() > obstacle.bottom() ||
this.right() < obstacle.left() ||
this.left() > obstacle.right()
);
}
}
function checkGameOver() {
let crashed = myObstacles.some(function(obstacle) {
return player.crashWith(obstacle);
});
if (crashed) {
myGameArea.stop();
}
}
document.onkeydown = function(e) {
switch (e.keyCode) {
case 38: // up arrow
player.speedY -= 1;
break;
case 40: // down arrow
player.speedY += 1;
break;
case 37: // left arrow
player.speedX -= 1;
break;
case 39: // right arrow
player.speedX += 1;
break;
}
};
function updateObstacles() {
for (i = 0; i < myObstacles.length; i++) {
myObstacles[i].y += -3;
myObstacles[i].update();
}
myGameArea.frames += 1;
if (myGameArea.frames % 60 === 0) {
let x = myGameArea.canvas.width;
let y = myGameArea.canvas.height;
let minWidth = 20;
let maxWidth = 200;
let width = Math.floor(
Math.random() * (maxWidth - minWidth + 1) + minWidth
);
var minGap = 70;
var maxGap = 200;
var gap = Math.floor(Math.random() * (maxGap - minGap + 1) + minGap);
myObstacles.push(new Component(width, 10, "green", 0, y));
myObstacles.push(
new Component(y-width-gap, 10, "green", width+gap, y)
);
}
}
document.onkeyup = function(e) {
player.speedX = 0;
player.speedY = 0;
};
function updateGameArea() {
myGameArea.clear();
player.newPos();
player.update();
updateObstacles();
checkGameOver();
myGameArea.score();
};
myGameArea.start();
let player = new Component(30, 30, "red", 0, 110);
I got it to work by changing 3 things in your updateObstacles function.
First, change myObstacles[i].y += -3 to myObstacles[i].y += 3
Second, change let y = myGameArea.canvas.height to let y = 0
Third, change
new Component(y-width-gap, 10, "green", width+gap, y)
to
new Component(x-width-gap, 10, "green", width+gap, y)
Full code:
function updateObstacles() {
for (i = 0; i < myObstacles.length; i++) {
myObstacles[i].y += 3;
myObstacles[i].update();
}
myGameArea.frames += 1;
if (myGameArea.frames % 60 === 0) {
let x = myGameArea.canvas.width;
let y = 0;
let minWidth = 20;
let maxWidth = 200;
let width = Math.floor(
Math.random() * (maxWidth - minWidth + 1) + minWidth
);
var minGap = 70;
var maxGap = 200;
var gap = Math.floor(Math.random() * (maxGap - minGap + 1) + minGap);
myObstacles.push(new Component(width, 10, "green", 0, y));
myObstacles.push(
new Component(x-width-gap, 10, "green", width+gap, y)
);
}
}
Example jsfiddle
Related
I have made a post on this before however, it was not in a canvas, but when you try to apply this to a canvas It gives an error along the lines of Cant change the color of undefined so I thought it was an error of the way I defined it so I would add it to a class then run it as changing the color of all elements in the class, but when I try it the canvas just disappears.. but getting to the point is there an easy way to change the color of a HTML element in a canvas or am I just crazy
Code
var myGamePiece = el.classList.add("class1");
var myObstacles = [];
var myScore;
function startGame() {
myGamePiece = new component(30, 30, "red", 10, 120);
myGamePiece.gravity = 0.05;
myScore = new component("30px", "Consolas", "black", 280, 40, "text");
myGameArea.start();
}
var myGameArea = {
canvas: document.createElement("canvas"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.frameNo = 0;
this.interval = setInterval(updateGameArea, 20);
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(width, height, color, x, y, type) {
this.type = type;
this.score = 0;
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.gravity = 0;
this.gravitySpeed = 0;
this.update = function() {
ctx = myGameArea.context;
if (this.type == "text") {
ctx.font = this.width + " " + this.height;
ctx.fillStyle = color;
ctx.fillText(this.text, this.x, this.y);
} else {
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
this.newPos = function() {
this.gravitySpeed += this.gravity;
this.x += this.speedX;
this.y += this.speedY + this.gravitySpeed;
this.hitBottom();
}
this.hitBottom = function() {
var rockbottom = myGameArea.canvas.height - this.height;
if (this.y > rockbottom) {
this.y = rockbottom;
this.gravitySpeed = 0;
}
}
this.crashWith = function(otherobj) {
var myleft = this.x;
var myright = this.x + (this.width);
var mytop = this.y;
var mybottom = this.y + (this.height);
var otherleft = otherobj.x;
var otherright = otherobj.x + (otherobj.width);
var othertop = otherobj.y;
var otherbottom = otherobj.y + (otherobj.height);
var crash = true;
if ((mybottom < othertop) || (mytop > otherbottom) || (myright < otherleft) || (myleft > otherright)) {
crash = false;
}
return crash;
}
}
function updateGameArea() {
var x, height, gap, minHeight, maxHeight, minGap, maxGap;
for (i = 0; i < myObstacles.length; i += 1) {
if (myGamePiece.crashWith(myObstacles[i])) {
return;
}
}
myGameArea.clear();
myGameArea.frameNo += 1;
if (myGameArea.frameNo == 1 || everyinterval(150)) {
x = myGameArea.canvas.width;
minHeight = 20;
maxHeight = 200;
height = Math.floor(Math.random() * (maxHeight - minHeight + 1) + minHeight);
minGap = 50;
maxGap = 200;
gap = Math.floor(Math.random() * (maxGap - minGap + 1) + minGap);
myObstacles.push(new component(10, height, "green", x, 0));
myObstacles.push(new component(10, x - height - gap, "green", x, height + gap));
}
for (i = 0; i < myObstacles.length; i += 1) {
myObstacles[i].x += -1;
myObstacles[i].update();
}
myScore.text = "SCORE: " + myGameArea.frameNo;
myScore.update();
myGamePiece.newPos();
myGamePiece.update();
}
function everyinterval(n) {
if ((myGameArea.frameNo / n) % 1 == 0) {
return true;
}
return false;
}
function accelerate(n) {
myGamePiece.gravity = n;
}
function Test() {
document.getElementsByClassName("class1").style.background = "yellow";
}
canvas {
border: 1px solid #d3d3d3;
background-color: #f1f1f1;
}
.class1 {}
<body onload="startGame()">
<button onmousedown="accelerate(-0.2)" onmouseup="accelerate(0.05)">ACCELERATE</button>
<p> Credit To W3Schools.com </p>
<button onclick="test">Click To Change Player's Color</button>
</body>
The code starts with: var myGamePiece = el.classList.add("class1");el is not defined but anyway myGamePiece needs to be a component as created in startGame, it is not an ordinary HTML element.
It is the creation of that component that the color is set. Currently the color is hardcoded as 'red'. To change the color you need to set a variable, let's call it playerColor, and use that in the component creation call rather than 'red'.
You are using function test to set up a color so let's change the test function to do that, but you need to get that color set up before startGame otherwise the canvas will have the incorrect color for the piece (or no set color).
var playersColor = "red"; //the default value if the player doesn't change it
var myGamePiece;
var myObstacles = [];
var myScore;
function startGame() {
myGamePiece = new component(30, 30, playersColor, 10, 120);//use playersColor
myGamePiece.gravity = 0.05;
myScore = new component("30px", "Consolas", "black", 280, 40, "text");
myGameArea.start();
}
var myGameArea = {
canvas: document.createElement("canvas"),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.frameNo = 0;
this.interval = setInterval(updateGameArea, 20);
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(width, height, color, x, y, type) {
this.type = type;
this.score = 0;
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.gravity = 0;
this.gravitySpeed = 0;
this.update = function() {
ctx = myGameArea.context;
if (this.type == "text") {
ctx.font = this.width + " " + this.height;
ctx.fillStyle = color;
ctx.fillText(this.text, this.x, this.y);
} else {
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
this.newPos = function() {
this.gravitySpeed += this.gravity;
this.x += this.speedX;
this.y += this.speedY + this.gravitySpeed;
this.hitBottom();
}
this.hitBottom = function() {
var rockbottom = myGameArea.canvas.height - this.height;
if (this.y > rockbottom) {
this.y = rockbottom;
this.gravitySpeed = 0;
}
}
this.crashWith = function(otherobj) {
var myleft = this.x;
var myright = this.x + (this.width);
var mytop = this.y;
var mybottom = this.y + (this.height);
var otherleft = otherobj.x;
var otherright = otherobj.x + (otherobj.width);
var othertop = otherobj.y;
var otherbottom = otherobj.y + (otherobj.height);
var crash = true;
if ((mybottom < othertop) || (mytop > otherbottom) || (myright < otherleft) || (myleft > otherright)) {
crash = false;
}
return crash;
}
}
function updateGameArea() {
var x, height, gap, minHeight, maxHeight, minGap, maxGap;
for (i = 0; i < myObstacles.length; i += 1) {
if (myGamePiece.crashWith(myObstacles[i])) {
return;
}
}
myGameArea.clear();
myGameArea.frameNo += 1;
if (myGameArea.frameNo == 1 || everyinterval(150)) {
x = myGameArea.canvas.width;
minHeight = 20;
maxHeight = 200;
height = Math.floor(Math.random() * (maxHeight - minHeight + 1) + minHeight);
minGap = 50;
maxGap = 200;
gap = Math.floor(Math.random() * (maxGap - minGap + 1) + minGap);
myObstacles.push(new component(10, height, "green", x, 0));
myObstacles.push(new component(10, x - height - gap, "green", x, height + gap));
}
for (i = 0; i < myObstacles.length; i += 1) {
myObstacles[i].x += -1;
myObstacles[i].update();
}
myScore.text = "SCORE: " + myGameArea.frameNo;
myScore.update();
myGamePiece.newPos();
myGamePiece.update();
}
function everyinterval(n) {
if ((myGameArea.frameNo / n) % 1 == 0) {
return true;
}
return false;
}
function accelerate(n) {
myGamePiece.gravity = n;
}
function test() {
playersColor = "yellow";
}
<body>
<button onmousedown="accelerate(-0.2)" onmouseup="accelerate(0.05)">ACCELERATE</button>
<p> Credit To W3Schools.com </p>
<button onclick="test()">Click To Change Player's Color</button>
<button onclick="startGame();">Click to start the game</button>
</body>
...............................................................................
Although the following errors are not fundamentally what is causing non-changing of the player's color, they contain some coding errors which it might be helpful to point out for the future.
<button onclick="test">Click To Change Player's Color</button>
<script>
function Test() {
document.getElementsByClassName("class1").style.background = "yellow";
</script>
The onclick="test" does nothing - you may be confusing the structure element.onclick = test - which sets which function is run on the element being clicked - with the syntax for the onclick="test();" needed in the button element attributes.
A further error is that the function is called Test, not test. Javascript is case sensitive. Also you need to select the one element with class1, not retrieve a collection of such elements, so you can set its style (if that is useful, is it?).
As of now, all I know to be certain is to set the global variable of 'paused' to be false. Adding an eventlistener and updating my loop function, etc. is where I am uncertain on implementation, otherwise the game is "finished" by all means!
var canvas = document.getElementById('canvas');
canvas.width = 1280;
canvas.height = 700;
var ctx = canvas.getContext('2d');
let obstacles = [];
var cancelMe = '';
let difficulty = 10;
let id;
let dis = 0;
let miles = 0;
let paused = false;
function getScore() {
let highScore = localStorage.getItem('highscore');
console.log('highscore is ', highScore);
document.getElementById('score').innerHTML = 'Highscore: ' + highScore + ' ft.';
}
getScore();
function saveScore(score) {
let highScore;
if (!isNaN(localStorage.getItem('highscore'))) {
highScore = localStorage.getItem('highscore');
} else {
highScore = 0;
}
console.log(highScore);
highScore = Math.max(score, highScore);
localStorage.setItem('highscore', highScore);
}
var img = new Image();
img.src = './images/background.jpg';
img.onload = function() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
};
var durianImg = new Image();
durianImg.src = './images/durian.png';
durianImg.onload;
var backgroundImage = {
img: img,
x: 0,
speed: -1.5,
move: function() {
backgroundImage.x += this.speed;
backgroundImage.x %= canvas.width;
sprite.distance += 0.2;
},
draw: function() {
ctx.drawImage(this.img, this.x, 0);
if (this.speed < 0) {
ctx.drawImage(this.img, this.x + canvas.width, 0);
} else {
ctx.drawImage(this.img, this.x - this.img.width, 0);
}
}
};
var timeFalling = 0;
function clamp(num, min, max) {
return num <= min ? min : num >= max ? max : num;
}
var sprite = {
name: 'Mr. Sprite',
x: 2,
y: 528,
distance: 0,
int: null,
moveLeft: function() {
sprite.x -= 30;
sprite.x = clamp(this.x, 0, 1280);
sprite.distance -= 30;
},
moveRight: function() {
sprite.x += 30;
sprite.x = clamp(this.x, 0, 1230);
sprite.distance += 30;
},
moveUp: function() {
if ((sprite.y = 528)) {
sprite.y -= 70;
this.beginFall();
}
},
beginFall: function() {
clearInterval(this.int);
timeFalling = 0;
this.int = setInterval(function() {
timeFalling = timeFalling + 1;
}, 10);
},
draw: function() {
spriteImg = new Image();
spriteImg.src = './images/sprite.png';
ctx.drawImage(spriteImg, sprite.x, sprite.y, 50, 60);
},
fall: function() {
if (this.y < 528) {
this.y += 9.8 * timeFalling / 150;
} else {
clearInterval(this.int);
this.y = 528;
}
}
};
class obstacle {
constructor(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
fall() {
if (this.y < 528) {
this.y++;
}
}
}
document.onkeydown = function(e) {
switch (e.keyCode) {
case 37:
case 65:
sprite.moveLeft();
console.log('left', sprite);
break;
case 38:
case 87:
case 32:
sprite.moveUp();
console.log('right', sprite);
break;
case 39:
case 68:
sprite.moveRight();
console.log('right', sprite);
break;
}
};
function checkCollision(obstacle) {
if (obstacle.y + 60 > sprite.y && obstacle.y < sprite.y + 60) {
if (obstacle.x + 50 < sprite.x + 50 && obstacle.x + 50 > sprite.x) {
console.log('Collision');
gameOver();
} else if (obstacle.x < sprite.x + 41 && obstacle.x > sprite.x) {
console.log('Collision');
gameOver();
}
}
}
function updateCanvas() {
backgroundImage.move();
ctx.clearRect(0, 0, canvas.width, canvas.height);
backgroundImage.draw();
dis++;
if (dis % 20 == 0) {
miles++;
}
ctx.fillStyle = '#606060';
ctx.fillText('Distance Traversed: ' + miles + ' ft.', 540, 40);
sprite.draw();
sprite.fall();
if (dis % 40 == 0) {
obstacles.push(randomObstacle());
}
for (let i = 0; i < obstacles.length; i++) {
obstacles[i].draw();
obstacles[i].fall();
checkCollision(obstacles[i]);
if (obstacles[i].y > 520) obstacles.splice(i, 1);
}
}
function startGame() {
difficulty = Number(document.querySelector('#diffSelect').value);
id = setInterval(updateCanvas, difficulty);
startGameButton.disabled = true;
init();
loop();
}
var startGameButton = document.getElementById('startGameButton');
startGameButton.onclick = startGame;
function restartGame() {
clearInterval(id);
obstacles = [];
var timeFalling = 0;
sprite.x = 2;
sprite.y = 528;
sprite.distance = 0;
sprite.int = null;
dis = 0;
miles = 0;
fallSpeed = 1.0005;
startGameButton.disabled = false;
ctx.fillStyle = 'white';
ctx.font = '18px serif';
ctx.clearRect(0, 0, canvas.width, canvas.height);
backgroundImage.draw();
audio.pause();
location.reload();
}
var retryGameButton = document.getElementById('retryGameButton');
retryGameButton.onlick = restartGame;
ctx.fillStyle = 'white';
ctx.font = '18px serif';
function randomObstacle() {
let x = Math.random() * canvas.width;
let y = 0;
return new Durian(x, y);
}
let fallSpeed = 1.0003;
setInterval(function() {
fallSpeed += 0.0008; // tweak this to change how quickly it increases in difficulty
// console.log(fallSpeed);
}, 8000); // timer at which it gets harder
class Durian {
constructor(x, y) {
this.x = x;
this.y = y;
}
draw() {
ctx.drawImage(durianImg, this.x, this.y, 50, 60);
}
fall() {
if (this.y < 528) this.y = (this.y + 1) ** fallSpeed;
this.x -= 1.5;
}
}
var hotbod = document.querySelector('body');
function doStuff() {
hotbod.className += ' animate';
}
window.onload = function() {
doStuff();
};
function gameOver() {
clearInterval(id);
ctx.fillStyle = '#606060';
ctx.font = '70px Anton';
ctx.fillText('GAME OVER', 430, 300);
console.log('save ', miles);
saveScore(miles);
audio.pause();
new Audio('sounds/game_over.wav').play();
}
function init() {
audio = document.getElementById('audio');
// add listener function to loop on end
audio.addEventListener('ended', loop, false);
// set animation on perpetual loop
setInterval(animate);
}
function loop() {
audio.play();
}
And for my index.html where the buttons are displayed:
<div id="menu">
<div class="custom-select instruct">
<select id="diffSelect">
<option value="6.5">Easy</option>
<option value="5.5">Medium</option>
<option value="4.5">Hard</option>
<option value="3">Extreme</option>
</select>
</div>
<input id="startGameButton" type="button" class="instruct" onclick="startGame()" value="Start" />
<input id="retryGameButton" type="button" class="instruct" onclick="restartGame()" value="Reset" />
<p class="disclaimer">DISCLAIMER: Once you wipe out, hit reset & change to a higher difficulty if you dare, then hit start to play again!</p>
</div>
Try the following
btw any ... represents your code I didn't include to save space
...
let gameRunning = false;
...
document.onkeydown = function(e) {
switch (e.keyCode) {
case 80:
if(gameRunning) {
paused = !paused;
startGameButton.value = paused ? "Un-Pause" : "Pause";
}
break;
...
}
};
...
function startGame() {
if(gameRunning){
paused = !paused;
startGameButton.value = paused ? "Un-Pause" : "Pause";
}
else {
difficulty = Number(document.querySelector('#diffSelect').value);
id = setInterval(updateCanvas, difficulty);
gameRunning = true;
startGameButton.value = "Pause";
init();
loop();
}
}
...
I'm working on a game that uses projectiles and a shielding system. The player would hold down 'Space' to use the shield. My plan is to get the projectiles to bounce of of the enemies shields (I've implemented speed so I already know how to do that). The problem I am having is with the collision, since the player rotates to follow the mouse I struggled with finding the best way to create the shield but I eventually settled on an arc, I used some trigonometry to get the left, leftHalf, mid, rightHalf, and right point of the arc/shield. The Player with Shield. The issue is I can't get the collision to work from just 5, x/y coordinates (the arc is just being drawn for show I'm only sending the points to the server). This is what I have for my collision so far:
p: Player object
self: bullet object
bot: a variable based on the direction the character is facing (bottom: true or false)
shieldLeft, sheildRight, etc: an array containing x and y coordinate 0 for x, 1 for y
if (self.getDistance(p) < 32 && self.parent !== p.id)
{
if (p.isShielding == true)
{
switch(self.bot)
{
case true:
if (self.x >= p.shieldRight[0] && self.x <= p.shieldLeft[0])
{
console.log("BOT X");
if ((self.y >= p.shieldLeft[1] || self.y >= p.shieldRight[1]) && self.y <= p.shieldMid[1])
{
console.log("BOT Y");
self.spdX = -self.spdX;
self.spdY = -self.spdY;
}
}
break;
case false:
if (self.x <= p.shieldRight[0] && self.x >= p.shieldLeft[0])
{
console.log("TOP X");
if ((self.y <= p.shieldLeft[1] || self.y <= p.shieldRight[1]) && self.y >= p.shieldMid[1])
{
console.log("TOP Y");
self.spdX = -self.spdX;
self.spdY = -self.spdY;
}
}
break;
}
}
}
I would really appreciate any help, I can't continue with the game features until there actually is a game. Thank you!
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
background-color: black;
}
canvas {
position: absolute;
margin: auto;
left: 0;
right: 0;
border: solid 1px white;
border-radius: 10px;
cursor: crosshair;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="application/javascript">
// Anonymous closure
(function() {
// Enforce strict rules for JS code
"use strict";
// App variables
var canvasWidth = 180;
var canvasHeight = 160;
var canvas = null;
var bounds = null;
var ctx = null;
var player = null;
var projectiles = [];
projectiles.length = 5;
// Classes
// Constructor function
function Player(x,y) {
this.x = x;
this.y = y;
this.dx = 0.0;
this.dy = 0.0;
this.rotation = 0.0;
this.targetX = 0.0;
this.targetY = 0.0;
this.isShieldUp = false;
this.isShieldRecharging = false;
this.shieldPower = this.shieldPowerMax;
this.left = false;
this.right = false;
this.up = false;
this.down = false;
window.addEventListener("keydown",this.onkeydown.bind(this));
window.addEventListener("keyup",this.onkeyup.bind(this));
window.addEventListener("mousemove",this.onmousemove.bind(this));
}
// shared properties/functions across all instances
Player.prototype = {
width: 10,
height: 10,
shieldRadius: 15.0,
shieldArcSize: 3.0, // In Radians
shieldPowerMax: 50.0,
shieldPowerCharge: 0.5,
shieldPowerDrain: 0.75,
onkeydown: function(e) {
switch(e.key) {
case " ": this.isShieldUp = true && !this.isShieldRecharging; break;
case "w": this.up = true; break;
case "s": this.down = true; break;
case "a": this.left = true; break;
case "d": this.right = true; break;
}
},
onkeyup: function(e) {
switch(e.key) {
case " ": this.isShieldUp = false; break;
case "w": this.up = false; break;
case "s": this.down = false; break;
case "a": this.left = false; break;
case "d": this.right = false; break;
}
},
onmousemove: function(e) {
this.targetX = e.clientX - bounds.left;
this.targetY = e.clientY - bounds.top;
},
tick: function() {
var x = (this.targetX - this.x);
var y = (this.targetY - this.y);
var l = Math.sqrt(x * x + y * y);
x = x / l;
y = y / l;
this.rotation = Math.acos(x) * (y < 0.0 ? -1.0 : 1.0);
if (this.isShieldUp) {
this.shieldPower = this.shieldPower - this.shieldPowerDrain;
if (this.shieldPower < 0.0) {
this.shieldPower = 0.0;
this.isShieldUp = false;
this.isShieldRecharging = true;
}
} else {
this.shieldPower = this.shieldPower + this.shieldPowerCharge;
if (this.shieldPower > this.shieldPowerMax) {
this.shieldPower = this.shieldPowerMax;
this.isShieldRecharging = false;
}
}
if (this.up) { --this.y; this.dy = -1; } else
if (this.down) { ++this.y; this.dy = 1; } else { this.dy = 0; }
if (this.left) { --this.x; this.dx = -1; } else
if (this.right) { ++this.x; this.dx = 1; } else { this.dx = 0; }
},
render: function() {
ctx.fillStyle = "darkred";
ctx.strokeStyle = "black";
ctx.translate(this.x,this.y);
ctx.rotate(this.rotation);
ctx.beginPath();
ctx.moveTo(0.5 * this.height,0.0);
ctx.lineTo(-0.5 * this.height,0.5 * this.width);
ctx.lineTo(-0.5 * this.height,-0.5 * this.width);
ctx.lineTo(0.5 * this.height,0.0);
ctx.fill();
ctx.stroke();
if (this.isShieldUp) {
ctx.strokeStyle = "cyan";
ctx.beginPath();
ctx.arc(0.0,0.0,this.shieldRadius,this.shieldArcSize * -0.5,this.shieldArcSize * 0.5,false);
ctx.stroke();
}
ctx.rotate(-this.rotation);
ctx.translate(-this.x,-this.y);
ctx.fillStyle = "black";
ctx.fillRect(canvasWidth - 80,canvasHeight - 20,75,15);
ctx.fillStyle = this.isShieldRecharging ? "red" : "cyan";
ctx.fillRect(canvasWidth - 75,canvasHeight - 15,65 * (this.shieldPower / this.shieldPowerMax),5);
}
};
function Projectile(x,y,dx,dy) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
}
Projectile.prototype = {
radius: 2.5,
tick: function(player) {
this.x = this.x + this.dx;
this.y = this.y + this.dy;
if (this.x + this.radius < 0.0) { this.x = canvasWidth + this.radius; }
if (this.x - this.radius > canvasWidth) { this.x = -this.radius; }
if (this.y + this.radius < 0.0) { this.y = canvasHeight + this.radius; }
if (this.y - this.radius > canvasHeight) { this.y = -this.radius; }
if (player.isShieldUp) {
var px = (player.x - this.x);
var py = (player.y - this.y);
var pl = Math.sqrt(px * px + py * py);
var ml = Math.sqrt(this.dx * this.dx + this.dy * this.dy);
var mx = this.dx / ml;
var my = this.dy / ml;
px = px / pl;
py = py / pl;
if (Math.acos(px * mx + py * my) < player.shieldArcSize * 0.5 && pl < player.shieldRadius) {
px = -px;
py = -py;
this.dx = this.dx - 2.0 * px * (this.dx * px + this.dy * py) + player.dx;
this.dy = this.dy - 2.0 * py * (this.dx * px + this.dy * py) + player.dy;
}
}
},
render: function() {
ctx.moveTo(this.x + this.radius,this.y);
ctx.arc(this.x,this.y,this.radius,0.0,2.0 * Math.PI,false);
}
}
// Game loop
function loop() {
// Tick
player.tick();
for (var i = 0; i < projectiles.length; ++i) {
projectiles[i].tick(player);
}
// Render
ctx.fillStyle = "#555555";
ctx.fillRect(0,0,canvasWidth,canvasHeight);
player.render();
ctx.fillStyle = "white";
ctx.strokeStyle = "black";
ctx.beginPath();
for (var i = 0; i < projectiles.length; ++i) {
projectiles[i].render();
}
ctx.fill();
ctx.stroke();
//
requestAnimationFrame(loop); // Runs the loop at 60hz
}
// "Main Method", executes after the page loads
window.onload = function() {
canvas = document.getElementById("canvas");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
bounds = canvas.getBoundingClientRect();
ctx = canvas.getContext("2d");
player = new Player(90.0,80.0);
for (var i = 0; i < projectiles.length; ++i) {
projectiles[i] = new Projectile(
Math.random() * canvasWidth,
Math.random() * canvasHeight,
Math.random() * 2.0 - 1.0,
Math.random() * 2.0 - 1.0
);
}
loop();
}
})();
</script>
</body>
</html>
I have a problem with using the interp to improve my drawing function. For some reason the realX returns undefined every 3 frames. I'm using this guide to implement this functionality. The end goal is to make the collision system work so it would reflect bullets on an incomming shield (basicly how the bullets currently interact with the canvas borders. This is part of the code:
//INT
var canvas = document.getElementById('ctx'),
cw = canvas.width,
ch = canvas.height,
cx = null,
bX = canvas.getBoundingClientRect().left,
bY = canvas.getBoundingClientRect().top,
mX,
mY,
lastFrameTimeMs = 0,
maxFPS = 60,
fps = 60,
timestep = 1000/fps,
lastTime = (new Date()).getTime(),
currentTime = 0,
delta = 0,
framesThisSecond = 0,
lastFpsUpdate = 0,
running = false,
started = false,
frameID = 0;
var gametimeStart = Date.now(),
frameCount = 0,
score = 0,
player,
enemyList = {},
boostList = {},
bulletList = {},
pulseList = {},
shieldList = {};
//CREATE , create object
var Unit = function(){
this.x;
this.y;
this.realX;
this.realY;
this.type = "unit";
this.hp = 0;
this.color;
this.collision = function(arc){
return this.x + this.r + arc.r > arc.x
&& this.x < arc.x + this.r + arc.r
&& this.y + this.r + arc.r > arc.y
&& this.y < arc.y + this.r + arc.r
};
this.position = function(delta){
boundX = document.getElementById('ctx').getBoundingClientRect().left;
boundY = document.getElementById('ctx').getBoundingClientRect().top;
this.realX = this.x;
this.realX = this.y;
this.realR = this.r;
this.x += this.spdX * delta / 1000;
this.y += this.spdY * delta / 1000;
if (this.x < this.r || this.x + this.r > cw){
this.spdX = -this.spdX;
if (Math.random() < 0.5) {
this.spdY += 100;
} else {
this.spdY -= 100;
}
}
if (this.y < this.r || this.y + this.r > ch){
this.spdY = -this.spdY;
if (Math.random() < 0.5) {
this.spdX += 100;
} else {
this.spdX -= 100;
}
}
};
this.draw = function(interp){
ctx.save();
ctx.fillStyle = this.color;
ctx.beginPath();
//THIS DOESN'T WORK, the code is working perfectly without it
this.x = (this.realX + (this.x - this.realX) * interp);
this.y = (this.realY + (this.y - this.realY) * interp);
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
ctx.arc(this.x,this.y,this.r,0,2*Math.PI,false);
ctx.fill();
ctx.restore();
};
}
//Player
var Player = function(){
//Coördinates
this.x = 50;
this.y = 50;
this.r = 10;
//Stats
this.spdX = 30;
this.spdY = 5;
this.atkSpd = 1;
this.hp = 100;
//Other
this.name;
this.color = "green";
this.type = "player";
this.angle = 1;
this.attack = function attack(enemy){
enemy.hp -= 10;
};
};
Player.prototype = new Unit();
//Boost
var Boost = function(){
//Coördinates
this.x = 300;
this.y = 300;
this.r = 5;
};
Boost.prototype = new Unit();
//Bullet
var Bullet = function(){
//Coördinates
this.x = player.x;
this.y = player.y;
this.r = 3;
//Stats
var angle = player.angle*90;
this.spdX = Math.cos(angle/180*Math.PI)*200;
this.spdY = Math.sin(angle/180*Math.PI)*200;
//Other
if (player.angle === 4)
player.angle = 0;
player.angle++;
};
Bullet.prototype = new Unit();
var player = new Player();
var boost = new Boost();
var bullet = new Bullet();
//UPDATE
update = function(delta){
bullet.position(delta);
if(player.collision(boost)){
alert("collide");
}
};
//DRAW
draw = function(interp){
ctx.clearRect(0,0,cw,ch);
fpsDisplay.textContent = Math.round(fps) + ' FPS';
boost.draw(interp);
bullet.draw(interp);
player.draw(interp);
};
//LOAD
if (!document.hidden) { console.log("viewed"); }
function panic() { delta = 0; }
function begin() {}
function end(fps) {
if (fps < 25) {
fpsDisplay.style.color = "red";
}
else if (fps > 30) {
fpsDisplay.style.color = "black";
}
}
function stop() {
running = false;
started = false;
cancelAnimationFrame(frameID);
}
function start() {
if (!started) {
started = true;
frameID = requestAnimationFrame(function(timestamp) {
draw(1);
running = true;
lastFrameTimeMs = timestamp;
lastFpsUpdate = timestamp;
framesThisSecond = 0;
frameID = requestAnimationFrame(mainLoop);
});
}
}
function mainLoop(timestamp) {
if (timestamp < lastFrameTimeMs + (1000 / maxFPS)) {
frameID = requestAnimationFrame(mainLoop);
return;
}
delta += timestamp - lastFrameTimeMs;
lastFrameTimeMs = timestamp;
begin(timestamp, delta);
if (timestamp > lastFpsUpdate + 1000) {
fps = 0.25 * framesThisSecond + 0.75 * fps;
lastFpsUpdate = timestamp;
framesThisSecond = 0;
}
framesThisSecond++;
var numUpdateSteps = 0;
while (delta >= timestep) {
update(timestep);
delta -= timestep;
if (++numUpdateSteps >= 240) {
panic();
break;
}
}
draw(delta / timestep);
end(fps);
frameID = requestAnimationFrame(mainLoop);
}
start();
if (typeof (canvas.getContext) !== undefined) {
ctx = canvas.getContext('2d');
ctx.font = '30px Arial';
}
//CONTROLLES
//Mouse Movement
document.onmousemove = function(mouse){
var mouseX = mouse.clientX - bX;
var mouseY = mouse.clientY - bY;
if(mouseX < player.width/2)
mouseX = player.width/2;
if(mouseX > cw-player.width/2)
mouseX = cw-player.width/2;
if(mouseY < player.height/2)
mouseY = player.height/2;
if(mouseY > ch-player.height/2)
mouseY = ch-player.height/2;
player.x = mX = mouseX;
player.y = mY = mouseY;
}
//Pauze Game
var pauze = function(){
if(window.event.target.id === "pauze"){
stop();
}
if(window.event.target.id === "ctx"){
start();
}
};
document.addEventListener("click", pauze);
html {
height: 100%;
box-sizing: border-box;
}
*,*:before,*:after {
box-sizing: inherit;
}
body {
position: relative;
margin: 0;
padding-bottom: 6rem;
min-height: 100%;
font-family: "Helvetica Neue", Arial, sans-serif;
}
main {
margin: 0 auto;
padding-top: 64px;
max-width: 640px;
width: 94%;
}
main h1 {
margin-top: 0;
}
canvas {
border: 1px solid #000;
}
<canvas id="ctx" width="500" height="500"></canvas>
<div id="fpsDisplay" style="font-size:50px; position:relative; margin: 0 auto;"></div>
<p id="status"></p>
<button id="pauze">Pauze</button>
Declare, define, use.
The problem is you are using a variables that are undefined.
You have 3 objects, Player, Boost, and Bullet each of which you assign the object Unit to their prototypes.
You declare and define unit
function Unit(){
this.x;
this.y;
this.realX;
this.realY;
// blah blah
this.draw = function(){
// blah blah
this.draw = function (interp) {
ctx.save();
ctx.fillStyle = this.color;
ctx.beginPath();
// this.realX and this.realY are undefined
this.x = (this.realX + (this.x - this.realX) * interp);
this.y = (this.realY + (this.y - this.realY) * interp);
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false);
ctx.fill();
ctx.restore();
};
}
Then Player
var Player = function () {
//Coördinates
this.x = 50;
this.y = 50;
this.r = 10;
//Stats
// etc....
};
Then add a Unit to the prototype.
Player.prototype = new Unit();
This gives Player the unit properties x, y, realX, realY But realX and realY are undefined. Same with the two other objects Boost, Bullet
Any existing properties with the same name stay as is and keep their values. Any properties not in Player will get them from Unit, but in unit you have only declared the properties realX, realY you have not defined them.
You need to define the properties by giving them a value before you use them. You can do this in Unit
function Unit(){
this.x = 0; // default value I am guessing
this.y = 0;
this.realX = 10;
this.realY = 10;
Then in Player
function Player(){
this.x = 50; // these will keep the value they have
this.y = 50;
// realX and realY will have the defaults when you add to the prototype
// Or you can define them here
this.realX = 20;
this.realY = 20;
Before you can use a variable you must define what it is or check if it is defined and take the appropriate action.
// in draw function.
if(this.realX === undefined){
this.realX = 100; // set the default if not defined;
}
BTW you should get rid of the alerts they are very annoying when you have them appear one after the other without a chance to navigate away from the page.
I've been trying to make objects scroll through the canvas, like when it goes down, it will come out of the top. I have got it working for the bottom and right sides, but it just gets stuck on the left and top side.
Here is what the code looks like in action
http://output.jsbin.com/gavuqo/
Here is my javascript code:
var count = 1;
var myGamePiece;
var myObstacle;
var myHealth;
var myScore;
function startGame() {
myGamePiece = new component(40, 40, "http://www.link", 10, 120, "image");
myObstacle = new component(60, 60, "http://www.link", 300, 120, "image");
myHealth = new component(60, 60, "http://www.link", 300, 100, "image");
myGameArea.start();
}
var myGameArea = {
canvas : document.createElement("canvas"),
start : function() {
this.canvas.width = 600;
this.canvas.height = 400;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.frameNo = 0;
this.interval = setInterval(updateGameArea, 20);
},
clear : function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
},
stop : function() {
clearInterval(this.interval);
}
};
function component(width, height, color, x, y, type) {
this.type = type;
if (type == "image") {
this.image = new Image();
this.image.src = color;
}
this.width = width;
this.height = height;
this.angle = Math.floor(Math.random() * 360) + 0 ;
this.speed = 1;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.update = function() {
ctx = myGameArea.context;
if (type == "image") {
ctx.drawImage(this.image,
this.x,
this.y,
this.width, this.height);
} else {
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
this.newPos = function() {
if( this.x<=0 || this.x>=600){this.x = -this.speed * Math.sin(this.angle);}else{ this.x += this.speed * Math.sin(this.angle);
}
if( this.y<=0 || this.y>=400) {this.y = -this.speed * Math.cos(this.angle);}else{
this.y += this.speed * Math.cos(this.angle);}
}
//myObstacle.newPos();
this.newPosmain = function() {
this.x += this.speedX;
this.y += this.speedY;
};
this.crashWith = function(otherobj) {
var myleft = this.x;
var myright = this.x + (this.width);
var mytop = this.y;
var mybottom = this.y + (this.height);
var otherleft = otherobj.x;
var otherright = otherobj.x + (otherobj.width);
var othertop = otherobj.y;
var otherbottom = otherobj.y + (otherobj.height);
var crash = true;
if ((mybottom < othertop) ||
(mytop > otherbottom) ||
(myright < otherleft) ||
(myleft > otherright)) {
crash = false;
}
return crash;
};
}
function updateGameArea() {
if (myGamePiece.crashWith(myObstacle) && count==1) {
myObstacle = new component(60, 60, "http://www.link", 300, 120, "image");
count++;
myGamePiece = new component(80, 80, "http://www.link", 10, 120, "image");
} else {
myGameArea.clear();
myObstacle.update();
//myObstacle.wallBounce();
myObstacle.newPos();
myHealth.update();
myHealth.newPos();
//myHealth.wallBounce();
myGamePiece.newPosmain();
myGamePiece.update();
}
if(myGamePiece.crashWith(myObstacle) && count==2){
myObstacle = new component(60, 60, "http://www.link", 300, 120, "image");
myGamePiece = new component(100, 100, "http://www.link", 10, 120, "image");
count++;
}
if(myGamePiece.crashWith(myObstacle) && count==3){
myGamePiece = new component(500, 500, "#", 0, 0, "image");
myeGameArea.stop();
document.body.style.backgroundImage = "url('#')";
count=0;
}
if(myGamePiece.crashWith(myHealth) && count==1){
myGamePiece = new component(100, 100, "http://www.link", 10, 120, "image");
count=0;
count--;
}
if(myGamePiece.crashWith(myHealth) && count==2){
myGamePiece = new component(40, 40, "http://www.link", 10, 120, "image");
count--;
}
if(myGamePiece.crashWith(myObstacle) && count==0){
myObstacle = new component(60, 60, "http://www.link", 300, 120, "image");
myGamePiece = new component(100, 100, "http://www.link", 10, 120, "image");
count++;
}
if(myGamePiece.crashWith(myHealth) && count==0){
myGamePiece = new component(500, 500, "http://example.com", 0, 0, "image");
}
}
function moveup() {
myGamePiece.speedY = -2;
}
function movedown() {
myGamePiece.speedY = 2;
}
function moveleft() {
myGamePiece.speedX = -2;
}
function moveright() {
myGamePiece.speedX = 2;
}
function clearmove() {
myGamePiece.speedX = 0;
myGamePiece.speedY = 0;
}
Simply adjust either the x or y position to be opposite of the exit point. If it exits at the bottom then reset it to top minus diameter of the object without adjusting x, and so forth.
In generic code:
// dia = diameter of object (you may have to use different values for width/height)
if (x < -dia) x = width;
else if (x >= width) x = -dia;
if (y < -dia) y = height;
else if (y >= height) y = -dia;
Example
(click run again to change direction)
var ctx = c.getContext("2d"),
x = c.width * 0.5, y = c.height * 0.5,
dx = Math.max(2, Math.random() * 4),
dy = Math.max(2, Math.random() * 4),
dia = 10;
dx *= Math.random() < 0.5 ? -1 : 1;
dy *= Math.random() < 0.5 ? -1 : 1;
(function loop() {
ctx.clearRect(0,0,c.width,c.height);
ctx.fillRect(x, y, dia, dia);
x += dx;
y += dy;
if (x < -dia) x = c.width;
else if (x >= c.width) x = -dia;
if (y < -dia) y = c.height;
else if (y >= c.height) y = -dia;
requestAnimationFrame(loop)
})()
#c {border:1px solid #999}
<canvas id=c>