I have been reading/searching for an answer to detect collisions of sprites in two arrays. I am not understanding how to pass two arrays into the detection function and have it check all contents of each array against each other. Any input would be greatly appreciated.
<script type="text/javascript">
var FIRE = 0;
var NORTH = 38;
var SOUTH = 40;
var EAST = 39;
var WEST = 37;
var destX = 350;
var destY = 500;
var canvas = null;
var context = null;
var sprites = null;
var player = null;
var island = null;
var enemies = [];
var fires = [];
var gameLoopInterval = null;
var offScreenFire = null;
var isShooting = false;
var intersect = null;
var Fire = function() {
this.spriteX = 278;
this.spriteY = 110;
this.spriteWidth = 13;
this.spriteHeight = 16;
this.destX = player.destX + 25;
this.destY = player.destY;
this.speed = 5;
}
var Player = function(name) {
this.name = name;
this.spriteX = 5;
this.spriteY = 400;
this.spriteWidth = 64;
this.spriteHeight = 64;
this.destX = 350;
this.destY = 500;
this.speed = 5;
this.level = 1;
}
var Enemy = function() {
this.spriteX = 4;
this.spriteY = 4;
this.spriteWidth = 32;
this.spriteHeight = 32;
this.destX = Math.ceil(Math.random() * (800 - this.spriteWidth));
this.destY = this.spriteWidth;
this.speed = Math.ceil(Math.random() * 5);
}
var Island = function() {
this.spriteX = 168;
this.spriteY = 500;
this.spriteWidth = 64;
this.spriteHeight = 64;
this.destX = Math.ceil(Math.random() * (800 - this.spriteWidth));
this.destY = this.spriteWidth - 64;
this.speed = 2;
}
Fire.prototype.takeTurn = function() {
var intersect;
var projdestX = this.destX;
var projdestY = this.destY;
var projspriteWidth = this.spriteWidth;
var projspriteHeight = this.spriteHeight;
for (enemy in enemies) {
intersect = intersect || intersects(enemy.destX, enemy.destY, enemy.spriteWidth, enemy.spriteHeight, projdestX, projdestY, projspriteWidth, projspriteHeight);
}
if(intersect == true) { alert("colliding"); }
else{drawImage(this);}
// if (intersect != true){
// drawImage(this);
// }
// else {
// alert("boom");
// }
if(this.destY <= 0){
offScreenFire = fires.indexOf(this);
fires.splice(offScreenFire, 1);
}
else
this.destY -= this.speed;
}
Player.prototype.takeTurn = function() {
drawImage(this);
}
Enemy.prototype.takeTurn = function() {
drawImage(this);
if (this.destY < canvas.height)
this.destY += this.speed;
else
this.destY = -32;
}
Island.prototype.takeTurn = function() {
drawImage(this);
this.destY += this.speed;
}
function fireAction() {
var fire = new Fire();
drawImage(fire);
fires.push(fire);
}
function drawImage(sprite) {
context.drawImage(sprites, sprite.spriteX, sprite.spriteY, sprite.spriteWidth, sprite.spriteHeight, sprite.destX, sprite.destY, sprite.spriteWidth, sprite.spriteHeight );
}
function gameLoop () {
context.clearRect(0, 0, canvas.width, canvas.height);
island.takeTurn();
player.takeTurn();
//console.log(fires); //debug
for (fire in fires) {
fires[fire].takeTurn();
}
for (enemy in enemies) {
enemies[enemy].takeTurn();
}
}
function intersects(x1, y1, w1, h1, x2, y2, w2, h2) {
if (w2 !== Infinity && w1 !== Infinity) {
w2 += x2;
w1 += x1;
if (isNaN(w1) || isNaN(w2) || x2 > w1 || x1 > w2)
return false;
}
if (y2 !== Infinity && h1 !== Infinity) {
h2 += y2;
h1 += y1;
if (isNaN(h1) || isNaN(y2) || y2 > h1 || y1 > h2)
return false;
}
return true;
}
window.onload = function() {
//alert('here');
canvas = document.getElementById('gameWorld');
context = canvas.getContext("2d");
sprites = new Image();
player = new Player('Brad');
island = new Island();
sprites.onload = function() {
drawImage(player);
for (i = 0; i < 3; i++) {
var enemy = new Enemy();
drawImage(enemy);
enemies.push(enemy);
}
}
sprites.src = "Sprites/1945.png";
gameLoopInterval = setInterval('gameLoop()', 100)
}
window.onkeypress = function(e){
var evt = window.event ? event : e;
//alert(evt.keyCode);
switch(evt.keyCode) {
case NORTH:
if (player.destY > 0)
player.destY -= player.speed;
else
player.destY == player.destY;
break;
case SOUTH:
if (player.destY < canvas.height - player.spriteWidth)
player.destY += player.speed;
else
player.destY == player.destY;
break;
case EAST:
if (player.destX < canvas.width - player.spriteWidth)
player.destX += player.speed;
else
player.destX == player.destY;
break;
case WEST:
if (player.destX > 0)
player.destX -= player.speed;
else
player.destX == player.destX;
break;
case FIRE:
fireAction();
break;
}
}
</script>
Your problems seems to be in:
for (enemy in enemies) {
intersect = intersects(enemy.destX, enemy.destY, enemy.spriteWidth, enemy.spriteHeight, projdestX, projdestY, projspriteWidth, projspriteHeight);
}
intersect will always have the last value saved. (Meaning you are really only checking if it intersects with the last enemy.)
A quick solution would be to change the inner line to:
intersect = intersect || intersects(enemy.destX, enemy.destY, enemy.spriteWidth, enemy.spriteHeight, projdestX, projdestY, projspriteWidth, projspriteHeight);
This will make intersect stay as true if the fire doesn't intersect with the next enemy.
Edit:
Your second problem is in the same for(). In javascript, when you do a for in, the first variable does not have a reference to the instance, but rather is only the key.
Your final for should look like:
for (enemy in enemies) {
intersect = intersect || intersects(enemies[enemy].destX, enemies[enemy].destY, enemies[enemy].spriteWidth, enemies[enemy].spriteHeight, projdestX, projdestY, projspriteWidth, projspriteHeight);
}
You also seem to not be spawning any enemies. In my fiddle of your code (http://jsfiddle.net/path411/umjnQ/) I added the following snippet into your gameLoop():
if(enemies.length < 1) {
enemies.push(new Enemy());
}
This simply creates a new enemy if you don't already have one. (You will probably want to change later).
Related
I wish to create a simple webgame that involves a tiled board. I have a collection of svg's for the background of each square (i.e one for grass, one for stone, one for dirt etc). I also have svg's for items that will be displayed on the layer above the background (such as trees, wood, sword).
I have an in memory database of what the background for each square is and if and which item it contains.
I wish to be able to:
* Zoom in or out
* Scroll left or right
* Scolll up or down
* Have items displayed above the background for that square
Only needs to work in recent versions of modern browsers
What is the best approach for this:
1. Have a canvas object. Get the current zoom, top most XY, canvas width and canvas height. Loop though the squares in the in memory database and print the corresponding SVG's in the correct locations. Each time it is scrolled or zoomed reprint the entire board.
2. Have a div. Get the current zoom, top most XY, canvas width and canvas height. Loop though the squares in the in memory database and create SVG's in the correct locations.
Each time the board scrolls add new SVGs as they become visible, delete SVGs as they move of the board. Translate all the existing SVGs by the appropriate amount.
Each time the board zooms enlarge or shrink all the existing SVGs based on the new zoom level.
3. Some third approach that I am unaware of.
The example below uses two modules svg to load images (Any image format will work) & board which handles panning, zooming & rendering. It also provides an onClick event which will give you an object that describes the tile that has been clicked on.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
background-color: black;
}
canvas {
display: block;
margin: 30px auto 0px auto;
border: solid 1px white;
border-radius: 10px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="application/javascript">
var svg = function() {
"use strict";
var svgImages = [];
var areImagesLoaded = true;
var isPageLoaded = false;
addEventListener("load",function() { isPageLoaded = true; isFinished(); });
var exports = {
onload: function() {},
request: null,
get: null
};
function isFinished() {
if (isPageLoaded && areImagesLoaded) {
exports.onload();
}
}
function onSvgError() {
this.isDone = true;
console.warn("SVG " + this.src + " failed to load.");
}
function onSvgLoaded() {
this.isDone = true;
for (var id in svgImages) {
if (!svgImages[id].isDone) {
return;
}
}
areImagesLoaded = true;
isFinished();
}
function request(id,path) {
if (svgImages[id]) {
return;
}
areImagesLoaded = false;
var img = document.createElement("img");
img.onerror = onSvgError;
img.onload = onSvgLoaded;
img.isDone = false;
img.id = id;
img.src = path;
svgImages[id] = img;
}
function get(id) {
return svgImages[id];
}
exports.request = request;
exports.get = get;
return exports;
}();
var board = function() {
"use strict";
var canvasWidth = 0;
var canvasHeight = 0;
var canvas = null;
var ctx = null;
var frameRequested = false;
var tileWidth = 0;
var tileHeight = 0;
var tileTypes = [];
var boardWidth = 0;
var boardHeight = 0;
var board = [];
var hasInitialized = false;
var camera = {
x: 0.0,
y: 0.0,
zoom: 1.0
};
function mapToBoard(x,y) {
var invZoom = 1.0 / camera.zoom;
return [
(x - (canvasWidth >> 1)) * invZoom - camera.x,
(y - (canvasHeight >> 1)) * invZoom - camera.y
];
}
var isMouseDragging = false;
var mouseStartX = 0;
var mouseStartY = 0;
var mouseLastX = 0;
var mouseLastY = 0;
var tileEvent = {
background: "",
foreground: "",
x: 0,
y: 0
};
function onTileSelected(e) {
}
function onMouseDown(e) {
isMouseDragging = true;
var bounds = canvas.getBoundingClientRect();
mouseStartX = mouseLastX = e.clientX - bounds.left;
mouseStartY = mouseLastY = e.clientY - bounds.top;
}
function onMouseUp(e) {
isMouseDragging = false;
var bounds = canvas.getBoundingClientRect()
var x = e.clientX - bounds.left - mouseStartX;
var y = e.clientY - bounds.top - mouseStartY;
var l = Math.sqrt(x * x + y * y);
if (l < 2.0) {
[x,y] = mapToBoard(e.clientX - bounds.left,e.clientY - bounds.top);
if (x > 0 && y > 0 && x < boardWidth * tileWidth && y < boardHeight * tileHeight) {
x = (x / tileWidth) | 0;
y = (y / tileHeight) | 0;
var tile = board[x + y * boardWidth];
tileEvent.background = tile.background;
tileEvent.foreground = tile.foreground;
tileEvent.x = x;
tileEvent.y = y;
} else {
tileEvent.background = "";
tileEvent.foreground = "";
tileEvent.x = -1;
tileEvent.y = -1;
}
onTileSelected(tileEvent);
}
}
function onMouseMove(e) {
if (hasInitialized && isMouseDragging) {
var bounds = canvas.getBoundingClientRect();
var x = e.clientX - bounds.left;
var y = e.clientY - bounds.top;
camera.x += (x - mouseLastX) / camera.zoom;
camera.y += (y - mouseLastY) / camera.zoom;
mouseLastX = x;
mouseLastY = y;
requestDraw();
}
}
function onWheel(e) {
if (Math.sign(e.deltaY) === -1) {
camera.zoom *= 1.1;
} else {
camera.zoom *= 0.9;
}
requestDraw();
}
function draw() {
ctx.fillStyle = "gray";
ctx.fillRect(-canvasWidth >> 1,-canvasHeight >> 1,canvasWidth,canvasHeight);
var _tileWidth = tileWidth * camera.zoom;
var _tileHeight = tileHeight * camera.zoom;
var _ox = camera.x * camera.zoom;
var _oy = camera.y * camera.zoom;
var _x = _ox;
var _y = _oy;
for (var x = 0; x <boardWidth; ++x) {
for (var y = 0; y < boardHeight; ++y) {
var index = x + y * boardWidth;
var tile = board[index];
var background = tileTypes[tile.background];
var foreground = tileTypes[tile.foreground];
if (background) {
ctx.drawImage(
background,
_x,
_y,
_tileWidth,
_tileHeight
);
}
if (foreground) {
ctx.drawImage(
foreground,
_x,
_y,
_tileWidth,
_tileHeight
);
}
_y += _tileHeight;
}
_y = _oy;
_x += _tileWidth;
}
frameRequested = false;
}
function requestDraw() {
if (!frameRequested) {
frameRequested = true;
requestAnimationFrame(draw);
}
}
return {
BACKGROUND: 0,
FOREGROUND: 1,
set canvas(canvasID) {
if (!hasInitialized) {
canvas = document.getElementById(canvasID);
canvas.onmousedown = onMouseDown;
canvas.onmouseup = onMouseUp;
canvas.onmousemove = onMouseMove;
canvas.onwheel = onWheel;
ctx = canvas.getContext("2d");
}
},
set canvasWidth(w) {
if (!hasInitialized && canvas) {
canvasWidth = canvas.width = w;
}
},
set canvasHeight(h) {
if (!hasInitialized && canvas) {
canvasHeight = canvas.height = h;
}
},
set tileWidth(w) {
if (!hasInitialized) {
tileWidth = w;
}
},
set tileHeight(h) {
if (!hasInitialized) {
tileHeight = h;
}
},
set width(w) {
if (!hasInitialized) {
boardWidth = w;
}
},
set height(h) {
if (!hasInitialized) {
boardHeight = h;
}
},
set onTileSelected(callback) {
onTileSelected = callback;
},
get width() {
return boardWidth;
},
get height() {
return boardHeight;
},
get onTileSelected() {
return onTileSelected;
},
defineTileTypes: function(types) {
if (types.length % 2 !== 0) {
return;
}
for (var i = 0; i < types.length; i += 2) {
var id = types[i];
var img = types[i + 1];
tileTypes[id] = img;
}
},
init: function() {
camera.x = -(boardWidth >> 1) * tileWidth;
camera.y = -(boardHeight >> 1) * tileHeight;
ctx.translate(canvasWidth >> 1,canvasHeight >> 1);
board.length = boardWidth * boardHeight;
for (var i = 0; i < board.length; ++i) {
board[i] = {
background: "",
foreground: ""
};
}
hasInitialized = true;
requestAnimationFrame(draw);
},
set: function(type,id,x,y) {
if (hasInitialized
&& tileTypes[id]
&& x > -1
&& x < boardWidth
&& y > -1
&& y < boardHeight) {
var index = x + y * boardWidth;
if (type === this.BACKGROUND) {
board[index].background = id;
} else {
board[index].foreground = id;
}
requestDraw();
}
}
};
}();
void function() {
"use strict";
svg.request("grass","https://i.stack.imgur.com/CkvU7.png");
svg.request("water","https://i.stack.imgur.com/an6a5.png");
svg.onload = function() {
board.canvas = "canvas";
board.canvasWidth = 180;
board.canvasHeight = 160;
board.tileWidth = 25;
board.tileHeight = 25;
board.width = 20;
board.height = 20;
board.defineTileTypes([
"GRASS",svg.get("grass"),
"WATER",svg.get("water")
]);
board.init();
for (var x = 0; x < board.width; ++x) {
for (var y = 0; y < board.height; ++y) {
board.set(board.BACKGROUND,"WATER",x,y);
if (Math.random() > 0.2) {
board.set(board.BACKGROUND,"GRASS",x,y);
} else {
board.set(board.BACKGROUND,"WATER",x,y);
}
}
}
}
board.onTileSelected = function(e) {
if (e.background === "GRASS") {
board.set(board.BACKGROUND,"WATER",e.x,e.y);
} else {
board.set(board.BACKGROUND,"GRASS",e.x,e.y);
}
}
}();
</script>
</body>
</html>
I am developing a animation in javascript where a car moves towards a person and picks but currently instead of a path I am just driving diagonally to the person with below code.
Car.prototype.main = function() {
var angle = angleBetweenTwoPoints(this.target.position, this.position);
var cos = Math.cos(degreeToRadian(angle)) * -1;
var sin = Math.sin(degreeToRadian(angle));
var _this = _super.call(this) || this;
this.angle = angle;
this.position.x += cos * this.speed;
this.position.y -= sin * this.speed;
if (distance(this.position, this.target.position) < 10 && this.image == GameImage.getImage("hero") ) {
this.target.position.x = Math.random() * mainCanvas.width;
this.target.position.y = Math.random() * mainCanvas.height;
this.hitCount++;
console.log(hitCount);
ctx.fillText("points : " + hitCount, 32, 32);
this.changeImage = true;
_this.speed = 3;
this.changeImageTime = Date.now() + 600; //0.5 sec from now.
this.image = (this.image == GameImage.getImage("hero"))? GameImage.getImage("hero_other") : GameImage.getImage("hero");
}
if(this.changeImage){
if(Date.now() > this.changeImageTime){
this.changeImage = false;
_this.speed = 9;
this.image = (this.image == GameImage.getImage("hero_other"))? GameImage.getImage("hero") : GameImage.getImage("hero_other");
}
}
};
return Car;
}(Actor));
But instaed of this I want to follow a path.I also created some grids when u click the image it logs the console which grid it is.But I am unable move the car in a path.For complete understanding the animation is in
animation.
Any help is appreciated
Waypoints as a queue.
For waypoints path following you use a type of array called a queue. As the name suggests the queue holds items that need to be used, specifically they need to be used in the order in which they arrive. The first object on the queue is the first object out (unless you push in line)
In javascript a queue is easy to implement using an array.
const path = {
points : [],
currentPos : null,
dist : 0,
totalDistMoved : 0,
atEnd : false,
addPoint(x,y) {
if(this.currentPos === null){
this.currentPos = { x :0,y : 0};
this.dist = 0;
this.totalDistMoved = 0;
}
this.points.push({x,y}) ;
},
moveAlong(dist){
if(dist > 0){
if(this.points.length > 1){
var x = this.points[1].x - this.points[0].x;
var y = this.points[1].y - this.points[0].y;
var len = Math.sqrt(x*x+y*y) ;
if(len - this.dist < dist){
this.points.shift();
dist -= (len - this.dist);
this.totalDistMoved += (len - this.dist);
this.dist = 0;
this.moveAlong(dist);
return;
}
const frac = this.dist + dist / len;
this.currentPos.x = this.points[0].x + x * frac;
this.currentPos.y = this.points[0].y + y * frac;
this.dist += dist;
this.totalDistMoved += dist;
}else{
this.currentPos.x = this.points[0].x;
this.currentPos.y = this.points[0].y;
this.dist = 0;
this.atEnd = true;
}
}
}
}
To use
Add some way points.
path.addPoint(1,1);
path.addPoint(100,20);
path.addPoint(110,120);
path.addPoint(210,120);
path.addPoint(250,420);
Then for each step of the animations get a distance along
path.moveAlong(10); // move ten pixels
and use the current position
ctx.drawImage(car,path.currentPos.x,path.currentPos.y);
You know you have reached the end of the path when.
if(path.atEnd) {
// you have arrived
}
And at any time you know how far you have moved with
path.totalDistMoved
This is meant for animations that only play forward. It will ignore negative distances as way points are dumped when you have passed them
You will need to make some modifications if you wish to reuse the path object, or if the waypoints are being added as you go
A simple example.
Thing moves along at constant speed. Click on page to add more waypoints.
const ctx = canvas.getContext("2d");
requestAnimationFrame(mainLoop);
function mainLoop(time){
gTime = !gTime ? time : gTime;
fTime = time - gTime;
gTime = time;
if(canvas.width !== innerWidth || canvas.height !== innerHeight){
canvas.width = innerWidth;
canvas.height = innerHeight;
}else{
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,canvas.width,canvas.height);
}
if(mouse.button){
if(!point){
point = {x:0,y:0};
path.addPoint(point);
}
point.x = mouse.x;
point.y = mouse.y;
}else{
if(point){ point = null }
}
ctx.beginPath();
var i = 0;
while(i < path.points.length){ ctx.lineTo(path.points[i].x,path.points[i++].y)}
ctx.strokeStyle = "blue";
ctx.lineWidth = 2;
ctx.stroke();
var i = 0;
while(i < path.points.length){ ctx.strokeRect(path.points[i].x-4,path.points[i++].y-4,8,8)}
path.moveAlong(4 * fTime / 100);
var x = path.currentPos.x - thingPos.x;
var y = path.currentPos.y - thingPos.y;
thingPos.x = path.currentPos.x;
thingPos.y = path.currentPos.y;
drawThing(thingPos.x,thingPos.y,Math.atan2(y,x));
requestAnimationFrame(mainLoop);
}
var point;
const thingPos = {x:0,y:0};
const path = {
points : [],
currentPos : null,
distAlong : 0,
totalDistMoved : 0,
atEnd : false,
addPoint(x,y) {
if(y === undefined){
this.points.push(x); // add point as object
return;
}
if(this.currentPos === null){
this.currentPos = { x :0,y : 0};
this.distAlong = 0;
this.totalDistMoved = 0;
}
this.points.push({x,y}) ;
},
moveAlong(dist){
if(dist > 0){
if(this.points.length > 1){
var x = this.points[1].x - this.points[0].x;
var y = this.points[1].y - this.points[0].y;
var len = Math.sqrt(x*x+y*y) ;
if(len - this.distAlong < dist){
this.points.shift();
dist -= (len - this.distAlong);
this.totalDistMoved += (len - this.distAlong);
this.distAlong = 0;
this.moveAlong(dist);
return;
}
const frac = (this.distAlong + dist) / len;
this.currentPos.x = this.points[0].x + x * frac;
this.currentPos.y = this.points[0].y + y * frac;
this.distAlong += dist;
this.totalDistMoved += dist;
}else{
this.currentPos.x = this.points[0].x;
this.currentPos.y = this.points[0].y;
this.distAlong = 0;
this.atEnd = true;
}
}
}
}
path.addPoint(20,20);
path.addPoint(120,20);
path.addPoint(220,120);
path.addPoint(320,120);
path.addPoint(420,20);
function mouseEvents(e) {
const m = mouse;
m.x = e.pageX;
m.y = e.pageY;
m.button = e.type === "mousemove" ? m.button : e.type === "mousedown";
}
function drawThing(x,y,dir) {
ctx.setTransform(1,0,0,1,x,y);
ctx.rotate(dir);
ctx.fillStyle = "red";
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
ctx.beginPath();
var i = 0;
while(i < thing.length){ ctx.lineTo(thing[i++],thing[i++]) };
ctx.closePath();
ctx.fill();
ctx.stroke();
}
const thing = [-20,-10,20,-10,22,-7,22,7,20,10,-20,10];
var gTime; // global and frame time
var fTime;
const mouse = { x:0,y:0,button:false};
["mousemove","mousedown","mouseup"].forEach(t=>document.addEventListener(t,mouseEvents));
canvas {
position: absolute;
top : 0px;
left : 0px;
}
<canvas id="canvas"></canvas>
click drag to add waypoints.
I want to click on one of the floating particles, upon clicking it, I want the particle colour to change from light grey to blue and then fade. Particle quantity is set as 100, as particles are clicked on to fade away particle quantity decreases. I'm unfamiliar with js, just started learning about canvas and particle systems yesterday, this is code from a pen that I've changed a bit and would like to add more changes to by adding the fade onclick event. I know that the particles within canvas are under one variable that defines them altogether, I don't know how to isolate one particle in the canvas to work with as I would need to know it's exact x,y coordinates.
Would I then have to find the coordinates for all 100 particles and add elements to each individual particle- and how do I do this?
$(document).ready(function() {
var PARTICLE_QUANT = 100;
var FPS = 60;
var BOUNCE = -1;
var PARTICLE_COLOR = '#ced4d4';
var ARC_RADIUS = 12;
var Particles = function($element) {
if ($element.length === 0) {
return;
}
this.$element = $element;
this.lastTimeStamp = null;
this.particles = [];
this.init();
};
var proto = Particles.prototype;
proto.init = function() {
this.createChildren()
.layout()
.enable();
};
proto.createChildren = function() {
this.canvas = this.$element[0];
this.context = this.canvas.getContext('2d');
this.canvasWidth = this.canvas.width;
this.canvasHeight = this.canvas.height;
this.lastTimeStamp = new Date().getTime();
return this;
};
proto.layout = function() {
window.requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame;
})();
return this;
};
proto.removeChildren = function() {
this.context = null;
this.canvasWidth = null;
this.canvasHeight = null;
this.lastTimeStamp = null;
return this;
};
proto.enable = function() {
this.createParticleData();
this.renderLoop();
};
proto.createParticleData = function() {
var i = 0;
var l = PARTICLE_QUANT;
for (; i < l; i++) {
this.particles[i] = {};
this.setParticleData(this.particles[i]);
}
};
proto.setParticleData = function(particle) {
particle.x = Math.random() * this.canvasWidth;
particle.y = Math.random() * this.canvasHeight;
particle.vx = (Math.random()) - 0.5;
particle.vy = (Math.random()) - 0.5;
};
proto.update = function() {
var i = 0;
var l = PARTICLE_QUANT;
for (; i < l; i++) {
var particle = this.particles[i];
particle.x += particle.vx;
particle.y += particle.vy;
if (particle.x > this.canvasWidth) {
particle.x = this.canvasWidth;
particle.vx *= BOUNCE;
} else if (particle.x < 0) {
particle.x = 0;
particle.vx *= BOUNCE;
}
if (particle.y > this.canvasHeight) {
particle.y = this.canvasHeight;
particle.vy *= BOUNCE;
} else if (particle.y < 0) {
particle.y = 0;
particle.vy *= BOUNCE;
}
}
};
proto.draw = function() {
var i = 0;
if (!this.context) {
return;
}
this.context.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
this.context.fillStyle = PARTICLE_COLOR;
for (; i < PARTICLE_QUANT; i++) {
var particle = this.particles[i];
this.context.save();
this.context.beginPath();
this.context.arc(particle.x, particle.y, ARC_RADIUS, 0, Math.PI * 2);
this.context.fill();
this.context.restore();
}
};
proto.renderLoop = function() {
requestAnimationFrame(this.renderLoop.bind(this));
this.update();
this.draw();
};
var particles = new Particles($('#js-particles'));
//Everything above works, it is what comes next (below), is this the right approach?
var elem = document.getElementById('js-particles'),
elemLeft = elem.offsetLeft,
elemTop = elem.offsetTop,
context = elem.getContext('2d'),
elements = [];
// Add event listener for `click` events.
elem.addEventListener('click', function(event) {
var x = event.pageX - elemLeft,
y = event.pageY - elemTop;
// Collision detection between clicked offset and element.
elements.forEach(function(element) {
if (y > element.top && y < element.top + element.height && x > element.left && x < element.left + element.width)
});
}, false);
// Add element.
elements.push({
colour: '#05EFFF',
width: 150,
height: 100,
top: 20,
left: 15
});
// Render elements.
elements.forEach(function(element) {
context.fillStyle = element.colour;
context.fillRect(element.left, element.top, element.width, element.height);
});
<canvas id="js-particles" class="particles" width="960" height="960">
<p>text appears if browser doesn 't support canvas</p>
</canvas>
OK, so I'm new to JS, so am trying to make the basic 'breakout' game. What I'm trying to do is arrange the bricks into a triangle shape (or more accurately, forming a triangle out of the absence of bricks). But when I choose which items in the 2D array I want to equal 0 (no brick), it only allows me choose one. after that, the game simply won't load.
Weirdest thing is, it will only accept the first line in this part. No matter what I change, the second line onwards will cause the game to not load:
bricks[0][10]=0;
bricks[7][16]=0;
bricks[7][15]=0;
bricks[7][14]=0;
bricks[7][13]=0;
bricks[7][12]=0;
bricks[7][11]=0;
bricks[7][10]=0;
bricks[7][9]=0;
bricks[7][8]=0;
bricks[7][7]=0;
bricks[7][6]=0;
bricks[7][5]=0;
bricks[7][4]=0;
bricks[7][3]=0;
bricks[7][17]=0;
bricks[6][4]=0;
bricks[6][16]=0;
bricks[5][15]=0;
bricks[5][5]=0;
bricks[4][14]=0;
bricks[4][6]=0;
bricks[3][13]=0;
bricks[3][7]=0;
bricks[2][8]=0;
bricks[2][12]=0;
bricks[1][11]=0;
bricks[1][9]=0;
Also, i know the code is incomplete and flawed as it is. It's not finished and still need a lot of polishing up.
Here's my entire code
canvasApp();
function canvasApp(){
var canvas=document.getElementById("canvas")
if (!canvas || !canvas.getContext){
return;
}
var ctx = canvas.getContext("2d");
if (!ctx) {
return
}
//Application States
const GAME_STATE_TITLE = 0;
const GAME_STATE_NEW_LEVEL = 1;
const GAME_STATE_GAME_OVER = 2;
var currentGameState = 0;
var currentGameStateFunction = null;
var brickcount;
var bouncecount = 0;
//Initialise Start Screen State
var titleStarted = false;
var gameStarted = false;
var gameOver = false;
var keyPressList = [];
var keys = false //mouse or keys. false = mouse control, vice versa
var difficulty = 0;
// Declarations for the game
var dx = 6;
var dy = 6;
var x = 150;
var y = 100;
var r = 10;
var WIDTH = 500;
var HEIGHT = 400;
var ballx = 200;
var bally = 200;
var paddlex = WIDTH/1.2;
var paddleh = 10;
var paddlew = 75;
var paddledx = 30
var mouseX;
var bricks;
var NROWS;
var NCOLS;
var BRICKWIDTH;
var BRICKHEIGHT;
var PADDING;
var rowcolours = ["#FF1C0A", "#FFFD0A", "#00A308", "#0008DB", "#EB0093"];
var paddlecolour = "#FF00FF";
var ballcolour = "#00FFFF";
var backcolour = "#0000FF";
function initbricks() {
NROWS = 9
NCOLS = 21
brickcount = NROWS*NCOLS;
BRICKWIDTH = (WIDTH/NCOLS) - 1;
BRICKHEIGHT = 10;
PADDING = 1;
bricks = new Array(NROWS);
for (i=0; i < NROWS; i++) {
bricks[i] = new Array(NCOLS);
for (j=0; j < NCOLS; j++) {
bricks[i][j] = 1;
}
bricks[0][10]=0;
bricks[7][16]=0;
bricks[7][15]=0;
bricks[7][14]=0;
bricks[7][13]=0;
bricks[7][12]=0;
bricks[7][11]=0;
bricks[7][10]=0;
bricks[7][9]=0;
bricks[7][8]=0;
bricks[7][7]=0;
bricks[7][6]=0;
bricks[7][5]=0;
bricks[7][4]=0;
bricks[7][3]=0;
bricks[7][17]=0;
bricks[6][4]=0;
bricks[6][16]=0;
bricks[5][15]=0;
bricks[5][5]=0;
bricks[4][14]=0;
bricks[4][6]=0;
bricks[3][13]=0;
bricks[3][7]=0;
bricks[2][8]=0;
bricks[2][12]=0;
bricks[1][11]=0;
bricks[1][9]=0;
}
}
initbricks();
function switchGameState(newState) {
currentGameState = newState;
switch (currentGameState) {
case GAME_STATE_TITLE:
currentGameStateFunction = gameStateTitle;
break;
case GAME_STATE_NEW_LEVEL:
currentGameStateFunction = gameStatePlayLevel;
break;
case GAME_STATE_GAME_OVER:
currentGameStateFunction = gameStateGameOver;
break;
}
}
function gameStateTitle(){
if (titleStarted != true){
ctx.fillStyle = '#000000';
ctx.fillRect(0,0,500,400);
ctx.fillStyle = '#ffffff';
ctx.font = '20px _sans';
ctx.textBaseline = 'top';
ctx.fillText ("Breakout!", 200,150);
ctx.fillText ("Press Space to Play", 170,200);
if (keys == 0 ) {
ctx.fillText ("Mouse selected", 180,250);
ctx.fillText ("Press k to switch to keys", 140,300);
} else {
ctx.fillText ("Keys selected", 190,250);
ctx.fillText ("Press m to switch to mouse", 140,300);
}
titleStarted = true;
}else{
if (keyPressList[75] == true){
keys = 1;
titleStarted = false;
gameStateTitle(); // Redraw the title page
}
if (keyPressList[77] == true){
keys = 0;
titleStarted = false;
gameStateTitle();
}
if (keyPressList[32] == true){
switchGameState(GAME_STATE_NEW_LEVEL);
titleStarted = false;
}
}
}
function gameStatePlayLevel(){
ctx.fillStyle = '#000000';
ctx.fillRect(0,0,500,400);
ctx.fillStyle = '#ffffff';
// Update the game state and check for game over
function update() {
x+=dx
y+=dy
if (keys == 0) {
paddlex = mouseX;
}else{
if (keyPressList[37]==true){
paddlex-=paddledx;
}
if (keyPressList[39]==true){
paddlex+=paddledx;
}
}
//have we hit a brick?
rowheight = BRICKHEIGHT + PADDING;
colwidth = BRICKWIDTH + PADDING;
row = Math.floor(y/rowheight);
col = Math.floor(x/colwidth);
//if so, reverse the ball and mark the brick as broken
if (y < NROWS * rowheight && row >= 0 && col >= 0 && bricks[row][col] == 1) {
dy = -dy;
bricks[row][col] = 0;
brickcount--;
if (brickcount == 0) {
switchGameState(GAME_STATE_NEW_LEVEL);
difficulty+=1;
initbricks();
x=250;
y=200 + (difficulty*20);
brickcount=NROWS*NCOLS;
bouncecount=0;
}
}
if( x<0 || x>WIDTH) dx=-dx;
if( y<0 || y>HEIGHT) dy=-dy;
else if (y + dy > HEIGHT) {
if (x > paddlex && x < paddlex + paddlew) {
dx = 8 * ((x-(paddlex+paddlew/2))/paddlew);
dy = -dy;
bouncecount++;
}
else {
//game over, so stop the animation
switchGameState(GAME_STATE_GAME_OVER);
initbricks();
}
}
}
function render() {
ctx.save();
function circle(x,y,r) {
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI*2, true);
ctx.fill();
}
function rect(x,y,w,h) {
ctx.beginPath();
ctx.rect(x,y,w,h);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
//draw bricks
for (i=0; i < NROWS; i++) {
ctx.fillStyle = rowcolours [i];
for (j=0; j < NCOLS; j++) {
if (bricks[i][j] == 1) {
rect((j * (BRICKWIDTH + PADDING)) + PADDING,
(i * (BRICKHEIGHT + PADDING)) + PADDING, BRICKWIDTH, BRICKHEIGHT);
}
}
}
circle(x, y, 10);
// init_paddle();
ctx.fillStyle = paddlecolour;
rect (paddlex, HEIGHT-paddleh, paddlew, paddleh);
ctx.restore();
show_result()
}
update();
render();
}
function gameStateGameOver(){
if (gameOver != true){
bouncecount=0;
ctx.fillStyle = '#000000';
ctx.fillRect(0,0,500,400);
ctx.fillStyle = '#ffffff';
ctx.font = '20px _sans';
ctx.textBaseline = 'top';
ctx.fillText ("Game over", 200,150);
ctx.fillText ("Press Space to Restart", 160,200);
ctx.fillText ("You completed " + difficulty + " levels", 160,240);
difficulty=0;
gameOver = true;
}else{
if (keyPressList[32] == true){
switchGameState(GAME_STATE_TITLE);
gameOver = false;
}
}
}
function runGame(){
currentGameStateFunction();
}
// Key handler
document.onkeydown = function(e){
e= e?e:window.event;
keyPressList[e.keyCode] = true;
}
document.onkeyup = function(e){
e= e?e:window.event;
keyPressList[e.keyCode] = false;
}
function onMouseMove(evt) {
// Event data passes to this function
mouseX = evt.clientX-canvas.offsetLeft - paddlew/2;
// Assign the relative position of the mouse in the canvas to mouseX
mouseY = evt.clientY-canvas.offsetTop;
//Do the same for mouseY
document.title="("+mouseX+","+mouseY+")";
//Put the mouse X and Y in the title for info
paddlex = mouseX;
// Position the paddle
}
canvas.addEventListener("mousemove",onMouseMove, false);
//Application start
switchGameState(GAME_STATE_TITLE);
const FRAME_RATE = 40;
var intervalTime = 1000/FRAME_RATE;
setInterval(runGame, intervalTime);
function show_result(){
ctx.fillText ("There are " + brickcount + " bricks", 160,200);
ctx.fillText ("Paddle bounces are " + bouncecount , 160,220);
}
}
With proper indenting, your code looks like this:
bricks = new Array(NROWS);
for (i=0; i < NROWS; i++) {
bricks[i] = new Array(NCOLS);
for (j=0; j < NCOLS; j++) {
bricks[i][j] = 1;
}
bricks[0][10]=0;
bricks[7][16]=0;
In other words, you're attempting to access bricks[7] in the very first iteration when only bricks[0] has been created. Properly close the first for loop with a } before running your list of overrides.
The Problem
I am creating a game that involves dodging projectiles. The player is in control of an image of a ship and I dont want the ship to move exactly together as this looks very unrealistic.
The Question
Is there a way to control how fast the image moves, how can i slow the movemnt of the image down?
The Code
var game = create_game();
game.init();
//music
var snd = new Audio("Menu.mp3");
snd.loop = true;
snd.play();
document.getElementById('mute').addEventListener('click', function (evt) {
if ( snd.muted ) {
snd.muted = false
evt.target.innerHTML = 'mute'
}
else {
snd.muted = true
evt.target.innerHTML = 'unmute'
}
})
function create_game() {
debugger;
var level = 1;
var projectiles_per_level = 1;
var min_speed_per_level = 1;
var max_speed_per_level = 2;
var last_projectile_time = 0;
var next_projectile_time = 0;
var width = 600;
var height = 500;
var delay = 1000;
var item_width = 30;
var item_height = 30;
var total_projectiles = 0;
var projectile_img = new Image();
var projectile_w = 30;
var projectile_h = 30;
var player_img = new Image();
var c, ctx;
var projectiles = [];
var player = {
x: 200,
y: 400,
score: 0
};
function init() {
projectile_img.src = "projectile.png";
player_img.src = "player.png";
level = 1;
total_projectiles = 0;
projectiles = [];
c = document.getElementById("c");
ctx = c.getContext("2d");
ctx.fillStyle = "#ff6600";
ctx.fillRect(0, 0, 500, 600);
c.addEventListener("mousemove", function (e) {
//moving over the canvas.
var bounding_box = c.getBoundingClientRect();
player.x = (e.clientX - bounding_box.left) * (c.width / bounding_box.width) - player_img.width / 2;
}, false);
setupProjectiles();
requestAnimationFrame(tick);
}
function setupProjectiles() {
var max_projectiles = level * projectiles_per_level;
while (projectiles.length < max_projectiles) {
initProjectile(projectiles.length);
}
}
function initProjectile(index) {
var max_speed = max_speed_per_level * level;
var min_speed = min_speed_per_level * level;
projectiles[index] = {
x: Math.round(Math.random() * (width - 2 * projectile_w)) + projectile_w,
y: -projectile_h,
v: Math.round(Math.random() * (max_speed - min_speed)) + min_speed,
delay: Date.now() + Math.random() * delay
}
total_projectiles++;
}
function collision(projectile) {
if (projectile.y + projectile_img.height < player.y + 74) {
return false;
}
if (projectile.y > player.y + 74) {
return false;
}
if (projectile.x + projectile_img.width < player.x + 177) {
return false;
}
if (projectile.x > player.x + 177) {
return false;
}
return true;
}
function maybeIncreaseDifficulty() {
level = Math.max(1, Math.ceil(player.score / 10));
setupProjectiles();
}
function tick() {
var i;
var projectile;
var dateNow = Date.now();
c.width = c.width;
for (i = 0; i < projectiles.length; i++) {
projectile = projectiles[i];
if (dateNow > projectile.delay) {
projectile.y += projectile.v;
if (collision(projectile)) {
initProjectile(i);
player.score++;
} else if (projectile.y > height) {
initProjectile(i);
} else {
ctx.drawImage(projectile_img, projectile.x, projectile.y);
}
}
}
ctx.font = "bold 24px sans-serif";
ctx.fillStyle = "#ff6600";
ctx.fillText(player.score, c.width - 50, 50);
ctx.fillText("Level: " + level, 20, 50);
ctx.drawImage(player_img, player.x, player.y);
maybeIncreaseDifficulty();
requestAnimationFrame(tick);
}
return {
init: init
};
}
https://jsfiddle.net/a6nmy804/4/ (Broken)
Throttle the player's movement using a "timeout" countdown
Create a global var playerFreezeCountdown=0.
In mousemove change player.x only if playerFreezeCountdown<=0.
If playerFreezeCountdown>0 you don't change player.x.
If playerFreezeCountdown<=0 you both change player.x and also set playerFreezeCountdown to a desired "tick timeout" value: playerFreezeCountdown=5. This timeout will cause prevent the player from moving their ship until 5 ticks have passed.
In tick, always decrement playerFreezeCountdown--. This will indirectly allow a change to player.x after when playerFreezeCountdown is decremented to zero or below zero.