I have a HTML Canvas on my website that I use to contain balls that drop into the canvas, they bounce around and have a real good time settling down at the bottom in a range of ways.
I was under the impression this was working perfectly. However, it has now been brought to my attention these balls jitter and freak out on other people's computers. I checked the browser they are using and it is the same as mine (Chrome V79). So just to clarify - I have never had it bug on my computer but it consistently has this jitter on other peoples computers, some with more powerful computers as well as lower spec computers.
This is it (this CodePen doesn't have the jitter on their computers, so you may not see it also): https://codepen.io/jason-is-my-name/pen/gObKGRg
This is a video of the bug: https://streamable.com/vtg0g
$(document).ready(function () {
$.ajaxSetup({
cache: true
});
$.when(
$.getScript("https://code.createjs.com/easeljs-0.6.0.min.js"),
$.getScript("https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.10.0/matter.min.js"),
$.Deferred(function (deferred) {
$(deferred.resolve);
})
).done(function () {
new App();
});
});
function App() {
var self = this;
self.running = false;
self.initialized = false;
var stageClicked = false;
var stage, canvas;
var canvasWidth = 410;
var canvasHeight = 550;
var ballBounce = 0.8;
var balls = [];
var matterJsBalls = [];
var _gravityY = 1;
var _gravityX = 0;
var FPS = 60;
var infoText, detailsText;
var ballsInitalized = false;
var iOS = navigator.userAgent.match(/(iPod|iPhone|iPad)/);
var startTime = (new Date()).getTime();
var engine = Matter.Engine.create();
var world = engine.world;
var floor = Matter.Bodies.rectangle(canvasWidth / 2, canvasHeight + 50, canvasWidth, 100, {
isStatic: true,
render: {
visible: false
}
});
var leftWall = Matter.Bodies.rectangle(-50, canvasHeight / 2, 100, canvasHeight, {
isStatic: true,
render: {
visible: false
}
});
var rightWall = Matter.Bodies.rectangle(canvasWidth + 50, canvasHeight / 2, 100, canvasHeight, {
isStatic: true,
render: {
visible: false
}
});
Matter.World.add(world, [floor, leftWall, rightWall]);
self.initialize = function () {
toggleListeners(true);
self.initCanvas();
self.initGame();
};
var toggleListeners = function (enable) {
if (!enable) return;
};
self.refresh = function () {};
self.initCanvas = function () {
canvas = $("#ball-stage").get(0);
stage = new createjs.Stage(canvas);
window.addEventListener("resize", onStageResize, false);
onStageResize();
createjs.Touch.enable(stage);
createjs.Ticker.addListener(tick);
createjs.Ticker.setFPS(FPS);
self.initialized = true;
};
self.initGame = function () {
initBalls(canvasWidth, canvasHeight);
};
var onStageResize = function () {
stage.canvas.width = canvasWidth;
stage.canvas.height = canvasHeight;
};
var initBalls = function (stageX, stageY) {
var imagesArray = [
"https://cdn.mos.cms.futurecdn.net/vChK6pTy3vN3KbYZ7UU7k3-320-80.jpg",
"https://cdn.mos.cms.futurecdn.net/vChK6pTy3vN3KbYZ7UU7k3-320-80.jpg",
"https://cdn.mos.cms.futurecdn.net/vChK6pTy3vN3KbYZ7UU7k3-320-80.jpg",
"https://cdn.mos.cms.futurecdn.net/vChK6pTy3vN3KbYZ7UU7k3-320-80.jpg",
"https://cdn.mos.cms.futurecdn.net/vChK6pTy3vN3KbYZ7UU7k3-320-80.jpg",
"https://cdn.mos.cms.futurecdn.net/vChK6pTy3vN3KbYZ7UU7k3-320-80.jpg",
"https://cdn.mos.cms.futurecdn.net/vChK6pTy3vN3KbYZ7UU7k3-320-80.jpg",
"https://cdn.mos.cms.futurecdn.net/vChK6pTy3vN3KbYZ7UU7k3-320-80.jpg",
"https://cdn.mos.cms.futurecdn.net/vChK6pTy3vN3KbYZ7UU7k3-320-80.jpg",
"https://cdn.mos.cms.futurecdn.net/vChK6pTy3vN3KbYZ7UU7k3-320-80.jpg"
];
for (var i = 0; i < imagesArray.length; i++) {
(function (imageArray) {
setTimeout(function () {
var arrayImage = new Image();
arrayImage.onload = function () {
addBall(arrayImage, stageX / 2, 52);
};
arrayImage.src = imageArray;
}, (i * 1000) + 1000);
})(imagesArray[i]);
}
};
var addBall = function (img, x, y) {
var shape = new createjs.Shape();
shape.id = balls.length;
shape.radius = 51.25;
shape.mass = shape.radius;
shape.x = x;
shape.y = y;
shape.vx = rand(-3, 3);
shape.vy = rand(-3, 3);
shape.stuck = false;
var transform = new createjs.Matrix2D();
transform.appendTransform(-shape.radius, -shape.radius, 1, 1, 0);
shape.graphics.beginBitmapFill(img, "repeat", transform).drawCircle(0, 0, shape.radius);
stage.addChild(shape);
balls.push(shape);
var circle = Matter.Bodies.circle(x, y, shape.radius, {
isStatic: false,
restitution: ballBounce
});
Matter.World.add(world, circle);
Matter.Body.applyForce(circle, {
x: circle.position.x,
y: circle.position.y
}, {
x: shape.vx * 0.05,
y: shape.vy * 0.05
});
matterJsBalls.push(circle);
};
var tick = function () {
Matter.Engine.update(engine, 16);
for (var i = 0; i < matterJsBalls.length; i++) {
var curBall = balls[i];
var curMatterJsBall = matterJsBalls[i];
curBall.x = curMatterJsBall.position.x;
curBall.y = curMatterJsBall.position.y;
}
stage.update();
};
var rand = function (min, max) {
return Math.random() * (max - min) + min;
return Math.random() * max + min;
};
self.initialize();
return self;
}
window.log = function f() {
log.history = log.history || [];
log.history.push(arguments);
if (this.console) {
var args = arguments,
newarr;
args.callee = args.callee.caller;
newarr = [].slice.call(args);
if (typeof console.log === "object")
log.apply.call(console.log, console, newarr);
else console.log.apply(console, newarr);
}
};
(function (a) {
function b() {}
for (
var c = "assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(
","
),
d; !!(d = c.pop());
) {
a[d] = a[d] || b;
}
})(
(function () {
try {
console.log();
return window.console;
} catch (a) {
return (window.console = {});
}
})()
);
.tech-stack-icons-container{width:410px;height:100%}#ball-stage{width:100%;height:100%;background-color:pink;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="tech-stack-icons-container">
<canvas id="ball-stage"></canvas>
</div>
How can I find a fix to this invisible issue?
Related
This is a question from javascript.
if (game.ship.livesPool.getPool[0].getDamaged())
I got an error on this getDamaged() function as undefined inside another function.
game = new Game();
game is defined outside this function as a global variable.
this.ship = new Ship();
ship is defined inside Game class.
this.livesPool = new Pool();
var pool = [];
this.getPool = function(){
return pool;
}
livesPool is defined inside Pool class. pool is an array defined in Pool class.
getPool function will return this array.
pool[i] = new Lives();
each of the pool[i] will be assigned Lives object in Pool class.
this.getDamaged = function(){
return this.damaged;
}
getDamaged() function is defined this way inside Lives class.
Why does it show me that this function is undefined?
Game class
function Game() {
this.init = function () {
// Obtain the canvas from HTML
this.bgCanvas = document.getElementById('background');
this.shipCanvas = document.getElementById('ship');
this.mainCanvas = document.getElementById('main');
// Load the context
/* Just one of them passing to conditional statement is enough to test
* the availability
*/
if (this.bgCanvas.getContext) {
this.bgContext = this.bgCanvas.getContext('2d');
this.shipContext = this.shipCanvas.getContext('2d');
this.mainContext = this.mainCanvas.getContext('2d');
Background.prototype.context = this.bgContext;
Background.prototype.canvasHeight = this.bgCanvas.height;
Background.prototype.canvasWidth = this.bgCanvas.width;
Ship.prototype.context = this.shipContext;
Ship.prototype.canvasHeight = this.shipCanvas.height;
Ship.prototype.canvasWidth = this.shipCanvas.width;
Lives.prototype.context = this.shipContext;
Lives.prototype.canvasHeight = this.shipCanvas.height;
Lives.prototype.canvasWidth = this.shipCanvas.width;
Bullet.prototype.context = this.mainContext;
Bullet.prototype.canvasHeight = this.mainCanvas.height;
Bullet.prototype.canvasWidth = this.mainCanvas.width;
Bullet.prototype.bossContext = this.shipContext;
Enemy.prototype.context = this.mainContext;
Enemy.prototype.canvasHeight = this.mainCanvas.height;
Enemy.prototype.canvasWidth = this.mainCanvas.width;
Boss.prototype.context = this.shipContext;
Boss.prototype.canvasHeight = this.shipCanvas.height;
Boss.prototype.canvasWidth = this.shipCanvas.width;
// Define background in the game
this.background = new Background();
this.background.init(0, 0, imageRepository.background.width, imageRepository.background.height);
// Define ship in the game
this.ship = new Ship();
var shipStartX = this.shipCanvas.width / 2 - imageRepository.ship.width / 2;
var shipStartY = this.shipCanvas.height / 4 * 3 + imageRepository.ship.height / 2;
this.ship.init(shipStartX, shipStartY, imageRepository.ship.width, imageRepository.ship.height);
this.ship.type = "ship";
this.ship.hasExplored = false;
// Define enemy pools in the game
this.enemyPool = new Pool(10);
this.enemyPool.init("enemy");
this.boss = new Boss();
return true;
}
else {
return false;
}
};
this.runGame = function () {
this.ship.draw();
animate();
};
this.restart = function () {
this.bgContext.clearRect(0, 0, this.bgCanvas.width, this.bgCanvas.height);
this.mainContext.clearRect(0, 0, this.mainCanvas.width, this.mainCanvas.height);
this.shipContext.clearRect(0, 0, this.shipCanvas.width, this.shipCanvas.height);
this.enemyPool.init("enemy");
var shipStartX = this.shipCanvas.width / 2 - imageRepository.ship.width / 2;
var shipStartY = this.shipCanvas.height / 4 * 3 + imageRepository.ship.height / 2;
this.ship.x = shipStartX;
this.ship.y = shipStartY;
this.ship.hasExplored = false;
this.ship.bulletPool.init("bullet");
score = 0;
displayedScore = 0;
bossExist = false;
this.boss.life = 100;
this.boss.bulletPool.init("boss_bullet");
this.boss.fireRate = 0;
while(this.ship.livesPool.getSize() < 3){
this.ship.livesPool.increaseLives();
}
document.getElementById('game-over').style.display = "none";
this.runGame();
};
this.start = function () {
gameStarted = true;
document.getElementById('main-menu').style.display = 'none';
document.getElementById('score-board').style.display = 'block';
};
this.backHome = function () {
gameStarted = false;
document.getElementById('game-over').style.display = 'none';
document.getElementById('score-board').style.display = 'none';
document.getElementById('main-menu').style.display = 'block';
this.restart();
};
}
Ship class.
function Ship() {
this.speed = 5;
this.bulletPool = new Pool(maxNumOfBullets);
this.bulletPool.init("bullet");
this.bulletSoundPool = new SoundPool(maxNumOfBullets);
this.bulletSoundPool.init("bullet");
this.livesPool = new Pool(3);
this.livesPool.init("lives");
this.hasExplored = false;
this.life = 3;
var fireRate = 15;
var counter = 0;
this.draw = function () {
if (this.livesPool.getSize() <= 0) {
this.context.clearRect(this.x, this.y, this.width, this.height);
}
else {
this.context.drawImage(imageRepository.ship, this.x, this.y);
}
}
this.move = function () {
counter++;
// Determine if the action is move action
if (KEY_STATUS.left || KEY_STATUS.right ||
KEY_STATUS.down || KEY_STATUS.up) {
// The ship moved, so erase it's current image so it can
// be redrawn in it's new location
this.context.clearRect(this.x, this.y, this.width, this.height);
// Update x and y according to the direction to move and
// redraw the ship. Change the else if's to if statements
// to have diagonal movement.
if (KEY_STATUS.left) {
this.x -= this.speed
if (this.x <= 0) // Keep player within the screen
this.x = 0;
} else if (KEY_STATUS.right) {
this.x += this.speed
if (this.x >= this.canvasWidth - this.width)
this.x = this.canvasWidth - this.width;
} else if (KEY_STATUS.up) {
this.y -= this.speed
if (this.y <= this.canvasHeight / 4 * 3)
this.y = this.canvasHeight / 4 * 3;
} else if (KEY_STATUS.down) {
this.y += this.speed
if (this.y >= this.canvasHeight - this.height)
this.y = this.canvasHeight - this.height;
}
}
this.draw();
if (KEY_STATUS.space && counter >= fireRate) {
this.fire();
counter = 0;
}
};
this.fire = function () {
this.bulletPool.getTwo(this.x + imageRepository.ship.width / 10, this.y, 3);
this.bulletSoundPool.get();
};
}
Pool class.
function Pool(maxSize) {
var size = maxSize;
var pool = [];
var type = "";
// This design enables us to not need to create an object each loop
this.init = function (obj) {
if (obj === "bullet") {
type = "bullet";
for (var i = 0; i < size; i++) {
var bullet = new Bullet("bullet");
bullet.init(0, 0, imageRepository.bullet.width, imageRepository.bullet.height);
bullet.collidableWith = "enemy";
bullet.type = "bullet";
pool[i] = bullet;
}
}
else if (obj === "enemy") {
type = "enemy";
for (var i = 0; i < size; i++) {
var enemy = null;
var rand = Math.floor(Math.random() * 10);
if (rand < 8) {
enemy = new Enemy("enemy", 0, 10);
enemy.init(0, 0, imageRepository.enemy.width, imageRepository.enemy.height);
}
else {
enemy = new Enemy("enemy2", 2, 15);
enemy.init(0, 0, imageRepository.enemy2.width, imageRepository.enemy2.height);
}
enemy.collidableWith = "ship";
enemy.type = "enemy";
pool[i] = enemy;
}
}
else if (obj === "boss_bullet") {
type = "boss_bullet";
for (var i = 0; i < size; i++) {
var bullet = new Bullet("boss_bullet");
bullet.init(0, 0, imageRepository.boss_bullet.width, imageRepository.boss_bullet.height);
bullet.collidableWith = "ship";
bullet.type = "bullet";
pool[i] = bullet;
}
}
else if (obj === "lives") {
type = "lives";
for (var i = 0; i < size; i++) {
var lives = new Lives();
lives.init(imageRepository.background.width - ((i + 1) * imageRepository.lives.width) - 10,
imageRepository.background.height - imageRepository.lives.height - 10,
imageRepository.lives.width, imageRepository.lives.height);
pool[i] = lives;
}
}
};
// Return pool attribute for usage in checking collision
this.getPool = function () {
var res = [];
for (var i = 0; i < pool.length; i++) {
if (pool[i].alive) {
res.push(pool[i]);
}
}
return res;
};
this.get = function (x, y, speed) {
if (pool[size - 1] instanceof Bullet) {
if (!pool[size - 1].alive) {
pool[size - 1].spawn(x, y, speed);
pool.unshift(pool.pop());
}
}
else if (pool[size - 1] instanceof Enemy) {
if (!pool[size - 1].alive) {
if (!(pool[0].alive && pool[0].y <= pool[0].height)) {
pool[size - 1].spawn(x, y, speed);
pool.unshift(pool.pop());
}
}
}
};
this.getTwo = function (x, y, speed) {
if (type === "bullet") {
if (!pool[size - 1].alive && !pool[size - 2].alive) {
this.get(x, y, speed);
this.get(x + (imageRepository.ship.width * 8) / 10
- imageRepository.bullet.width, y, speed);
}
}
else if (type === "boss_bullet") {
if (!pool[size - 1].alive && !pool[size - 2].alive) {
this.get(x, y, speed);
// This will have the center of boss as the center between two bullets
// x + 2 * (imageRepository.boss.width / 2 - x) - imageRepository.boss_bullet.width
this.get(x + imageRepository.boss.width * 3 / 5 - imageRepository.boss_bullet.width,
y, speed);
console.log(x);
console.log(x + imageRepository.boss.width * 3 / 5 - imageRepository.boss_bullet.width);
}
}
};
this.animate = function () {
for (var i = 0; i < size; i++) {
if (pool[i].alive) {
if (pool[i].draw()) {
pool[i].clear();
pool.push((pool.splice(i, 1))[0]);
}
}
else
break;
}
};
this.getSize = function () {
return size;
};
this.setSize = function (input) {
size = input;
};
this.decreaseLives = function () {
if (size >= 1) {
if (pool[size - 1] instanceof Lives) {
pool[size - 1].setDamaged(true);
size--;
}
}
};
this.increaseLives = function(){
if (pool[size - 1] instanceof Lives){
pool[size - 1].setDamaged(true);
size++;
}
};
}
Lives class.
function Lives() {
this.alive = true;
this.damaged = false;
this.draw = function () {
this.context.clearRect(this.x, this.y, this.width, this.height);
this.context.drawImage(imageRepository.lives, this.x, this.y);
if (this.damaged)
return true;
}
this.clear = function () {
alive = false;
this.x = -1 * this.width;
this.y = -1 * this.height;
}
this.getDamaged = function(){
return this.damaged;
}
this.setDamaged = function(input){
this.damaged = input;
}
}
getPool is a function, you need to call it:
if (game.ship.livesPool.getPool()[0].getDamaged())
// ^^
I got an agar.io clone from online and I am trying to remove the Quadtree in the corner (acts as a map) completely! I try to delete everything associating with the quadtree but i end up with a gray screen. Could someone please remove the map from the top right corner for me or tell me which lines to remove?
Here is my HTML:
<div id="viewport">
<canvas id="canvas1"></canvas>
<canvas id="canvas2"></canvas>
<canvas id="canvas3" width="250px" height="250px"></canvas>
</div>
Here is my javascript:
var MouseHandler = (function() {
var x = 0;
var y = 0;
var mouseIn = false;
var init = function(eventSrc) {
eventSrc.addEventListener('mousemove', onMouseMove);
eventSrc.addEventListener('mouseout', onMouseOut);
eventSrc.addEventListener('mouseover', onMouseOver);
};
var onMouseOut = function() {
mouseIn = false;
};
var onMouseOver = function() {
mouseIn = true;
};
var onMouseMove = function(e) {
x = e.clientX;
y = e.clientY;
};
var getPos = function() {
return {
x: x,
y: y
};
};
var isMouseIn = function() {
return mouseIn;
};
return {
init: init,
getPos: getPos,
isMouseIn: isMouseIn
};
}());
Quadtree.MAX_OBJECTS = 5;
Quadtree.MAX_LEVEL = 5;
function Quadtree(lvl, bnds) {
var level = lvl;
var bounds = bnds;
var objects = [];
var nodes = [];
var xMiddle = bounds.x + (bounds.width / 2);
var yMiddle = bounds.y + (bounds.height / 2);
var clear = function() {
objects = [];
nodes = [];
};
var split = function() {
nodes[0] = new Quadtree(level+1, {x: xMiddle, y: bounds.y , width: bounds.width/2, height: bounds.height/2});
nodes[1] = new Quadtree(level+1, {x: bounds.x, y: bounds.y, width: bounds.width/2, height: bounds.height/2});
nodes[2] = new Quadtree(level+1, {x: bounds.x, y: yMiddle, width: bounds.width/2, height: bounds.height/2});
nodes[3] = new Quadtree(level+1, {x: xMiddle, y: yMiddle, width: bounds.width/2, height: bounds.height/2});
};
var getIndex = function(rec) {
var top = (rec.y > bounds.y && (rec.y+rec.height) < yMiddle);
var bottom = (rec.y > yMiddle && (rec.y+rec.height) < (bounds.y+bounds.height));
if(rec.x > bounds.x && (rec.x+rec.width) < xMiddle) {
if(top) {
return 1;
} else if(bottom) {//LEFT
return 2;
}
} else if(rec.x > xMiddle && (rec.x+rec.width) < (bounds.x+bounds.width)) {
if(top) {
return 0;
} else if(bottom) {//RIGHT
return 3;
}
}
return -1;
};
var insert = function(ent) {
var rec = ent.getBounds();
var index = getIndex(rec);
var len = 0;
var i = 0;
if(nodes[0] && index !== -1) {
nodes[index].insert(ent);
return;
}
objects.push(ent);
if(objects.length > Quadtree.MAX_OBJECTS && level < Quadtree.MAX_LEVEL) {
if(!nodes[0]) {
split();
}
len = objects.length;
while(i < objects.length) {
index = getIndex(objects[i].getBounds());
if(index !== -1) {
nodes[index].insert(objects[i]);
objects.splice(i, 1);
} else {
i += 1;
}
}
}
};
var retrieve = function (list, ent) {
var rec1 = bounds;
var rec2 = ent.getBounds();
if(rec2.x < (rec1.x+rec1.width) && (rec2.x+rec2.width) > rec1.x &&
rec2.y < (rec1.y+rec1.height) && (rec2.y+rec2.height) > rec1.y) {
for(var o in objects) {
if(objects[o] !== ent) {
list.push(objects[o]);
}
}
if(nodes.length) {
nodes[0].retrieve(list, ent);
nodes[1].retrieve(list, ent);
nodes[2].retrieve(list, ent);
nodes[3].retrieve(list, ent);
}
}
return list;
};
var drawTree = function(ctx) {
draw(ctx);
if(nodes[0]) {
nodes[0].drawTree(ctx);
nodes[1].drawTree(ctx);
nodes[2].drawTree(ctx);
nodes[3].drawTree(ctx);
}
};
var draw = function(ctx) {
var entAttr = null
ctx.strokeStyle = 'black';
ctx.lineWidth = 1;
ctx.strokeRect(bounds.x/20, bounds.y/20, bounds.width/20, bounds.height/20);
ctx.fillStyle = 'gray';
for(o in objects) {
entAttr = objects[o].getAttr();
ctx.fillRect(entAttr.x/20, entAttr.y/20, 3, 3);
}
};
var toString = function() {
return '('+bounds.x+','+bounds.y+')'+'['+bounds.width+','+bounds.height+']';
};
return {
clear: clear,
insert: insert,
retrieve: retrieve,
drawTree: drawTree,
toString: toString,
};
}
function Circle(attr) {
attr = attr || {};
var x = attr.x || 0;
var y = attr.y || 0;
var mass = attr.mass || 500;
var color = attr.color || 'white';
var borderColor = attr.borderColor || 'black';
var velX = attr.velX || 0;
var velY = attr.velY || 0;
var droplet = attr.droplet || false;
var maxVel = 80 * (80 / Math.sqrt(mass));
var radius = Math.sqrt( mass / Math.PI ); //1 unit of area === 1 uni of mass
var getAttr = function() {
return {
x: x,
y: y,
mass: mass,
radius: radius,
color: color,
velX: velX,
velY: velY,
maxVel: maxVel
};
};
var setAttr = function(attr) {
x = (attr.x !== undefined)? attr.x: x;
y = (attr.y !== undefined)? attr.y: y;
mass = (attr.mass !== undefined)? attr.mass: mass;
color = (attr.color !== undefined)? attr.color: color;
velX = (attr.velX !== undefined)? attr.velX: velX;
velY = (attr.velY !== undefined)? attr.velY: velY;
};
var incMass = function(dMass) {
mass += dMass;
maxVel = 100 * (100 / Math.sqrt(mass));
radius = Math.sqrt( mass / Math.PI );
};
var intersects = function(ent2) {
var ent2Attr = ent2.getAttr();
var dX = Math.abs(ent2Attr.x - x);
var dY = Math.abs(ent2Attr.y - y);
var totalRadius = ent2Attr.radius + radius;
return (dX < totalRadius && dY < totalRadius);
};
var draw = function(ctx, cam) {
var camSize = cam.getSize();
var rPos = cam.getRelPos(getAttr());
//if outside Cam view
if((rPos.x + radius) < 0 || (rPos.y + radius) < 0 || (rPos.x - radius) > camSize.width || (rPos.y - radius) > camSize.height) {
return;
}
ctx.fillStyle = color;
ctx.strokeStyle = borderColor;
ctx.lineWidth = 5;
ctx.beginPath();
ctx.arc(rPos.x, rPos.y, radius, 0, 2 * Math.PI, false);
ctx.fill();
ctx.stroke();
};
var update = !droplet && function(dTime) {
x += velX * dTime;
y += velY * dTime;
};
var getBounds = function() {
return {
x: x-radius,
y: y-radius,
width: radius*2,
height: radius*2,
};
};
return {
getBounds: getBounds,
getAttr: getAttr,
setAttr: setAttr,
incMass: incMass,
intersects: intersects,
draw: draw,
update: update
};
}
var gameManager = (function() {
var canvasGrid = null;
var canvasEnt = null;
var canvasQuadtree = null;
var ctxGrid = null;
var ctxEnt = null;
var ctxQuadTree = null;
var dTime = 1 / 60;
var player = null;
var entityBag = [];
var qTree = null;
var drwQuad = 0;
var addEntity = function(ent) {
entityBag.push(ent);
};
var removeEntity = function(ent) {
var len = entityBag.length;
var i = 0;
for(i=0; i<len; i+=1) {
if(ent === entityBag[i]) {
entityBag.splice(i, 1);
return;
}
}
};
var init = function(cvsGridId, cvsEntId, cvsQuadtree) {
canvasGrid = document.getElementById(cvsGridId);
canvasEnt = document.getElementById(cvsEntId);
canvasQuadtree = document.getElementById(cvsQuadtree);
ctxGrid = canvasGrid.getContext('2d');
ctxEnt = canvasEnt.getContext('2d');
ctxQuadtree = canvasQuadtree.getContext('2d');
fitToContainer(canvasGrid);
fitToContainer(canvasEnt);
ctxGrid.fillStyle = '#F0FBFF';
ctxGrid.strokeStyle = '#BFBFBF';
ctxGrid.lineWidth = 1;
MouseHandler.init(document);
qTree = new Quadtree(0, {x:0, y:0, width:5000, height:5000});
player = new Circle({
x: 50,
y: 50,
color: 'red',
mass: 1000,
velX: 500,
velY:500
});
Camera.init(ctxEnt, player);
addEntity(player);
gameloop();
};
var handleInput = function() {
if(MouseHandler.isMouseIn() === false) {
player.setAttr({ velX: 0, velY: 0 });
return;
}
var pAttr = player.getAttr();
var rPlyrPos = Camera.getRelPos(player.getAttr());
var mPos = MouseHandler.getPos();
var dX = mPos.x - rPlyrPos.x;
var dY = mPos.y - rPlyrPos.y;
var vLength = Math.sqrt( (dX*dX) + (dY*dY) );
var normX = dX / vLength;
var normY = dY / vLength;
var newVelX = normX * (pAttr.maxVel * vLength / 50);
var newVelY = normY * (pAttr.maxVel * vLength / 50);
player.setAttr({
velX: newVelX,
velY: newVelY
});
};
var drawGrid = function() {
var camPos = Camera.getPos();
var camSize = Camera.getSize();
var start = Math.floor(camPos.x / 40);
var relX = Camera.getRelPos({x: (start*40), y: 0}).x;
var numLines = camSize.width / 40;
var i = 0;
ctxGrid.fillRect(0, 0, canvasGrid.width, canvasGrid.height);
for(i=0; i<numLines; i+=1) {
ctxGrid.beginPath();
ctxGrid.moveTo(relX + (40 * i), 0);
ctxGrid.lineTo(relX + (40 * i), camSize.height);
ctxGrid.stroke();
}
start = Math.floor(camPos.y / 40);
var relY = Camera.getRelPos({x: 0, y: (start * 40)}).y;
numLines = camSize.height / 40;
for(i=0; i<numLines; i+=1) {
ctxGrid.beginPath();
ctxGrid.moveTo(0, relY + (40 * i));
ctxGrid.lineTo(camSize.width, relY + (40 * i));
ctxGrid.stroke();
}
};
var handleCollisions = function() {
var possibleColl = [];
var collisions = [];
var coll = null;
var ent = null;
var plyMass = player.getAttr().mass
var entMass = 0;
qTree.clear();
for(ent in entityBag) {
qTree.insert(entityBag[ent]);
}
possibleColl = qTree.retrieve([], player);
while(ent = possibleColl.pop()) {
if(player.intersects(ent)) {
entMass = ent.getAttr().mass;
if(plyMass > (1.5 * entMass)) {
removeEntity(ent);
player.incMass(entMass);
}
}
var entAttr = ent.getAttr();
ctxQuadtree.fillStyle = 'red';
ctxQuadtree.fillRect(entAttr.x/20, entAttr.y/20, 3, 3);
}
};
var fitToContainer = function(canvas) {
canvas.style.width='100%';
canvas.style.height='100%';
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
};
var gameloop = function() {
var len = entityBag.length;
var i = 0;
var ent = null;
handleInput();
Camera.update();
drawGrid();
if(drwQuad === 5) {
ctxQuadtree.fillStyle = 'white';
ctxQuadtree.fillRect(0, 0, 250, 250);
qTree.drawTree(ctxQuadtree);
drwQuad = 0;
}
drwQuad += 1;
ctxEnt.clearRect(0, 0, canvasEnt.width, canvasEnt.height)
for(i=0; i<len; i+=1) {
ent = entityBag[i];
if(ent.update) {
ent.update(dTime);
}
if(ent.draw) {
ent.draw(ctxEnt, Camera);
}
}
handleCollisions();
setTimeout(gameloop, dTime * 1000);
};
return {
init: init,
addEntity: addEntity,
removeEntity: removeEntity
};
}());
var Camera = (function() {
var x = 0;
var y = 0;
var width = 0;
var height = 0;
var ctx = null;
var player = null;
var init = function(_ctx, plyr) {
ctx = _ctx;
player = plyr;
width = ctx.canvas.width;
height = ctx.canvas.height;
};
var update = function() {
width = ctx.canvas.width;
height = ctx.canvas.height;
var plyrAttr = player.getAttr();
x = (plyrAttr.x - width / 2);
y = (plyrAttr.y - height / 2);
};
var getRelPos = function(entAttr) {
var relX = entAttr.x - x;
var relY = entAttr.y - y;
return {
x: relX,
y: relY
};
};
var getPos = function() {
return {
x: x,
y: y
};
};
var getSize = function() {
return {
width: width,
height: height
};
};
return {
init: init,
update: update,
getRelPos: getRelPos,
getPos: getPos,
getSize: getSize
};
}());
var i = 0;
var space = 70;
for(i=0; i<400; i+=1) {
gameManager.addEntity(new Circle({
x: 100 + Math.random() * 4850,//(i%20) * space,
y: 100 + Math.random() * 4850,//Math.floor(i/20)*space,
color: ['red', 'blue', 'green', 'yellow', 'purple', 'brown', 'violet'][Math.floor(Math.random()*7)],
droplet: true
}));
}
gameManager.init('canvas1', 'canvas2', 'canvas3');
And last but not least, my css:
html, body {
background: gray;
width: 100%;
height: 100%;
margin: 0px;
}
#viewport {
position: relative;
width: 100%;
height: 100%;
}
#viewport canvas {
position: absolute;
}
canvas {
background-color: transparent;
}
So I wanted to stream things that aren't only mp3 files.
Things like youtube for example.
Ignore the rest of the code other than the audio part of it. This is a visualizer that I'm revamping from a dude that made a really nice audio visualizer.
How would I go about making it play youtube files and direct links that are streaming links. What I wanted to do was add a list of random songs so it would stream a song off of every single one rather than me uploading 100 songs to my domain and wasting space.
(function() {
var ALPHA, AudioAnalyser, COLORS, MP3_PATH, NUM_BANDS, NUM_PARTICLES, Particle, SCALE, SIZE, SMOOTHING, SPEED, SPIN;
NUM_PARTICLES = 150;
NUM_BANDS = 128;
SMOOTHING = 0.5;
var MP3_PATH = ['website.com/Song.mp3', 'website.com/Song2.mp3', 'website.com/Song3.mp3'];
SCALE = {
MIN: 5.0,
MAX: 20.0
};
SPEED = {
MIN: 0.2,
MAX: 1.0
};
ALPHA = {
MIN: 0.8,
MAX: 0.9
};
SPIN = {
MIN: 0.001,
MAX: 0.005
};
SIZE = {
MIN: 0.5,
MAX: 0.90
};
COLORS = ['#69D2E7', '#1B676B', '#BEF202', '#EBE54D', '#00CDAC', '#1693A5', '#F9D423', '#FF4E50', '#E7204E', '#0CCABA', '#FF006F'];
AudioAnalyser = (function() {
AudioAnalyser.AudioContext = self.AudioContext || self.webkitAudioContext;
AudioAnalyser.enabled = AudioAnalyser.AudioContext != null;
function AudioAnalyser(audio, numBands, smoothing) {
var src;
this.audio = audio != null ? audio : new Audio();
this.numBands = numBands != null ? numBands : 256;
this.smoothing = smoothing != null ? smoothing : 0.3;
if (typeof this.audio === 'string') {
src = this.audio;
this.audio = new Audio();
this.audio.crossOrigin = "anonymous";
this.audio.controls = true;
this.audio.src = src;
}
this.context = new AudioAnalyser.AudioContext();
this.jsNode = this.context.createScriptProcessor(2048, 1, 1);
this.analyser = this.context.createAnalyser();
this.analyser.smoothingTimeConstant = this.smoothing;
this.analyser.fftSize = this.numBands * 2;
this.bands = new Uint8Array(this.analyser.frequencyBinCount);
this.audio.addEventListener('canplay', (function(_this) {
return function() {
_this.source = _this.context.createMediaElementSource(_this.audio);
_this.source.connect(_this.analyser);
_this.analyser.connect(_this.jsNode);
_this.jsNode.connect(_this.context.destination);
_this.source.connect(_this.context.destination);
return _this.jsNode.onaudioprocess = function() {
_this.analyser.getByteFrequencyData(_this.bands);
if (!_this.audio.paused) {
return typeof _this.onUpdate === "function" ? _this.onUpdate(_this.bands) : void 0;
}
};
};
})(this));
}
AudioAnalyser.prototype.start = function() {
return this.audio.play();
};
AudioAnalyser.prototype.stop = function() {
return this.audio.pause();
};
return AudioAnalyser;
})();
Particle = (function() {
function Particle(x1, y1) {
this.x = x1 != null ? x1 : 0;
this.y = y1 != null ? y1 : 0;
this.reset();
}
Particle.prototype.reset = function() {
this.level = 1 + floor(random(4));
this.scale = random(SCALE.MIN, SCALE.MAX);
this.alpha = random(ALPHA.MIN, ALPHA.MAX);
this.speed = random(SPEED.MIN, SPEED.MAX);
this.color = random(COLORS);
this.size = random(SIZE.MIN, SIZE.MAX);
this.spin = random(SPIN.MAX, SPIN.MAX);
this.band = floor(random(NUM_BANDS));
if (random() < 0.5) {
this.spin = -this.spin;
}
this.smoothedScale = 0.0;
this.smoothedAlpha = 0.0;
this.decayScale = 0.0;
this.decayAlpha = 0.0;
this.rotation = random(TWO_PI);
return this.energy = 0.0;
};
Particle.prototype.move = function() {
this.rotation += this.spin;
return this.y -= this.speed * this.level;
};
Particle.prototype.draw = function(ctx) {
var alpha, power, scale;
power = exp(this.energy);
scale = this.scale * power;
alpha = this.alpha * this.energy * 1.5;
this.decayScale = max(this.decayScale, scale);
this.decayAlpha = max(this.decayAlpha, alpha);
this.smoothedScale += (this.decayScale - this.smoothedScale) * 0.3;
this.smoothedAlpha += (this.decayAlpha - this.smoothedAlpha) * 0.3;
this.decayScale *= 0.985;
this.decayAlpha *= 0.975;
ctx.save();
ctx.beginPath();
ctx.translate(this.x + cos(this.rotation * this.speed) * 250, this.y);
ctx.rotate(this.rotation);
ctx.scale(this.smoothedScale * this.level, this.smoothedScale * this.level);
ctx.moveTo(this.size * 0.5, 0);
ctx.lineTo(this.size * -0.5, 0);
ctx.lineWidth = 1;
ctx.lineCap = 'round';
ctx.globalAlpha = this.smoothedAlpha / this.level;
ctx.strokeStyle = this.color;
ctx.stroke();
return ctx.restore();
};
return Particle;
})();
Sketch.create({
particles: [],
setup: function() {
var analyser, error, i, intro, j, particle, ref, warning, x, y;
for (i = j = 0, ref = NUM_PARTICLES - 1; j <= ref; i = j += 1) {
x = random(this.width);
y = random(this.height * 2);
particle = new Particle(x, y);
particle.energy = random(particle.band / 350);
this.particles.push(particle);
}
if (AudioAnalyser.enabled) {
try {
analyser = new AudioAnalyser(random(MP3_PATH), NUM_BANDS, SMOOTHING);
analyser.onUpdate = (function(_this) {
return function(bands) {
var k, len, ref1, results;
ref1 = _this.particles;
results = [];
for (k = 0, len = ref1.length; k < len; k++) {
particle = ref1[k];
results.push(particle.energy = bands[particle.band] / 350);
}
return results;
};
})(this);
analyser.start();
document.body.appendChild(analyser.audio);
intro = document.getElementById('intro');
intro.style.display = 'none';
if (/Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent)) {
warning = document.getElementById('warning2');
return warning.style.display = 'block';
}
} catch (_error) {
error = _error;
}
} else {
warning = document.getElementById('warning1');
return warning.style.display = 'block';
}
},
draw: function() {
var j, len, particle, ref, results;
this.globalCompositeOperation = 'lighter';
ref = this.particles;
results = [];
for (j = 0, len = ref.length; j < len; j++) {
particle = ref[j];
if (particle.y < -particle.size * particle.level * particle.scale * 2) {
particle.reset();
particle.x = random(this.width);
particle.y = this.height + particle.size * particle.scale * particle.level;
}
particle.move();
results.push(particle.draw(this));
}
return results;
}
});
}).call(this);
I'm trying to render an image from a sprite sheet using JS. The curious thing is, unless the object that does the rendering is global, it doesn't work (see code and comments). The behaviour is identical in both FF and Chrome.
resetGame() is executed on page load.
var TILE_SIZE = 24;
function CharacterImage(imageSource)
{
var tile_x = 0;
var tile_y = 0;
var img = new Image();
img.src = imageSource;
this.render = function(ctx, x, y)
{
ctx.drawImage(img, tile_x, tile_y, TILE_SIZE, TILE_SIZE,
x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE);
}
}
function Hero(canvas, image)
{
var ctx = canvas.getContext("2d");
var img = image;
this.render = function()
{
var x = 1;
var y = 1;
img.render(ctx, x, y);
}
}
// If the heroImage is constructed here, instead of within the function below,
// the image is rendered as expected.
var heroImage = new CharacterImage("img/sf2-characters.png");
function resetGame()
{
var heroCanvas = document.getElementById("heroLayer");
// On the otherhand, if the object is constructed here, instead of
// globally, the rendering doesn't work.
var heroImage = new CharacterImage("img/sf2-characters.png");
var hero = new Hero(heroCanvas, heroImage);
hero.render();
}
Oh hang on I think I see what's happening. The image needs time to load, so you should somehow bind an event to the loading of the image. This could be done as for example:
var TILE_SIZE = 24;
function CharacterImage(imageSource)
{
var tile_x = 0;
var tile_y = 0;
var img = new Image();
img.src = imageSource;
this.render = function(ctx, x, y)
{
ctx.drawImage(img, tile_x, tile_y, TILE_SIZE, TILE_SIZE,
x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE);
}
// Set up a "load" event for the img
this.loaded = function(callback) {
img.addEventListener('load', callback);
}
}
function resetGame()
{
var heroCanvas = document.getElementById("heroLayer");
var heroImage = new CharacterImage("img/sf2-characters.png");
var hero;
// Initiate the "load" event
heroImage.loaded(function() {
hero = new Hero(heroCanvas, heroImage);
hero.render();
};
}
What you'll probably want though is some sort of preloader "class"/event that keeps track of everything being loaded before you actually continue with rendering. It could look something like this.
var TILE_SIZE=60;
function Sprite(imageSource)
{
this.img = new Image();
this.img.src = imageSource;
this.position = { x:0, y:0 };
}
Sprite.prototype = {
isLoaded: function() {
return this.img.complete;
},
onLoad: function(callback) {
if (typeof callback !== "function") return;
if (this.isLoaded()) {
callback();
}
else {
this.img.removeEventListener('load', callback);
this.img.addEventListener('load', callback);
}
},
moveBy: function(x, y) {
this.position.x += x;
this.position.y += y;
},
render: function(ctx) {
if (!this.isLoaded()) return;
ctx.drawImage(this.img, this.position.x * TILE_SIZE, this.position.y * TILE_SIZE, TILE_SIZE, TILE_SIZE);
}
};
function SpriteList()
{
this.list = {};
}
SpriteList.prototype = {
isLoaded: function() {
for (var i in this.list) {
if (!this.list[i].isLoaded()) {
return false;
}
}
return true;
},
_onLoadFunc: null,
onLoad: function(callback) {
this._onLoadFunc = callback;
this.onImageLoaded();
},
onImageLoaded: function() {
if (this.isLoaded() && typeof this._onLoadFunc === "function") {
this._onLoadFunc();
}
},
add: function(name, sprite) {
this.list[name] = sprite;
sprite.onLoad(this.onImageLoaded.bind(this));
},
get: function(name) {
return this.list[name];
}
};
var sprites = new SpriteList();
sprites.add("player", new Sprite("http://www.fillmurray.com/200/200"));
sprites.add("enemy", new Sprite("http://www.fillmurray.com/100/100"));
sprites.add("pickup", new Sprite("http://www.fillmurray.com/60/60"));
sprites.get("pickup").moveBy(1,2);
sprites.get("enemy").moveBy(2,0);
sprites.onLoad(function() {
document.getElementById("loading").innerHTML = "Loaded!";
var c = document.getElementById("ctx");
var ctx = c.getContext("2d");
sprites.get("player").render(ctx);
sprites.get("enemy").render(ctx);
sprites.get("pickup").render(ctx);
});
<div id="loading">Loading...</div>
<canvas id="ctx" width="200" height="200">
Anyways, that's why your code isn't firing, probably.
Hey guys not entirely sure what I'm doing wrong. I've played a few HTML5 games and they've seem to suffer from a different issue. The drawing lags behind the movement and it looks weird. This doesn't seem to be the case here.
In my game the drawing seems fine, but it lags like every second as he moves.(movement is arrow keys). It does it without arrow keys also, if I set him up to move automatically, so I don't think it's a key detection issue.
It's almost as if the garbage collector is running every second. I don't think I'm spewing out that many objects though.
I'm using Chrome 21 (MacOSX) and also Firefox 14.
http://tempdrew.dreamhosters.com/spine/
Here is the js fiddle with relevant code.
http://jsfiddle.net/ju3ag/
This is fine on chrome canary. I don't know if it's just because the javascript is so much faster in canary then standard chrome. It's terrible in latest Firefox. I'm not sure what I'm doing wrong. I'm updating movement based on time. If I take that out though it's still bad.
I'm just wondering if anything will stand out to anyone. Thanks for any help.
sg = Class.extend({
});
sg.entities = [];
sg.buttonStates = [];
sg.createEntity = function (entity) {
this.entities.push(entity);
};
window.requestAnimFrame = (function () {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function (callback, element) {
window.setTimeout(callback, 1000 / 60);
};
})();
(function defineUtil() {
sg.util = {};
sg.util.getRandomInt = function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
};
sg.util.getRandomNumber = function getRandomNumber(min, max) {
return Math.random() * (max - min) + min;
};
})();
/*************************/
(function createEntity() {
var Entity = Class.extend({
init: function (x, y) {
this.name = 'Entity';
this.health = 100;
this.pos = {
x: x,
y: y
};
this.vel = {
x: 0,
y: 0
};
this.accel = {
x: 0,
y: 0
}
console.log(this.name + ' created ' + x + ' ' + y);
},
update: function (elapsed) {
},
draw: function (ctx) {
}
});
sg.Entity = Entity;
})();
/************************/
// -- player.js
(function createPlayer() {
var Player = sg.Entity.extend({
x: 0,
y: 0,
moveLeft: false,
moveRight: false,
speed : 5,
init: function (x, y) {
this.x = x;
this.y = y;
this.name = 'Player';
},
draw: function (ctx) {
var x = this.x,
y = this.y;
ctx.beginPath();
ctx.rect(x, y, 40, 50);
ctx.fillStyle = 'white';
ctx.fill();
ctx.lineWidth = .5;
ctx.strokeStyle = 'rgba(0,0,0,.3)';
ctx.stroke();
ctx.fillStyle = 'rgba(0,0,0,.5)';
ctx.fillRect(x + 25, y + 15, 5, 5);
},
update: function (elapsed) {
var distance = (60 / 1000) * elapsed;
if (this.moveLeft) {
this.x += this.speed * distance;
} else if (this.moveRight) {
this.x -= this.speed * distance;
}
},
keyDown: function (e) {
if (e.keyCode === 39) {
this.moveLeft = true;
} else if (e.keyCode === 37) {
this.moveRight = true;
} else {
this.moveLeft = false;
this.moveRight = false;
}
},
keyUp: function (e) {
if (e.keyCode === 39) {
this.moveLeft = false;
} else if (e.keyCode === 37) {
this.moveRight = false;
}
}
});
sg.Player = Player;
})();
/**********************************/
(function createGame() {
var Game = Class.extend({
canvas: null,
context: null,
width: null,
height: null,
init: function (width, height) {
this.canvas = document.getElementById('canvas');
this.context = this.canvas.getContext('2d');
this.width = width || 800;
this.height = height || 600;
this.canvas.width = this.width;
this.canvas.height = this.height;
},
clear: function () {
this.context.clearRect(0, 0, this.width, this.height);
},
draw: function () {
this.clear();
for (var i = 0; i < sg.entities.length; i++) {
sg.entities[i].draw(this.context);
}
},
update: function (elapsed) {
for (var i = 0; i < sg.entities.length; i++) {
sg.entities[i].update(elapsed);
}
},
keyDown: function (e) {
for (var i = 0; i < sg.entities.length; i++) {
if (typeof sg.entities[i].keyDown === 'function') {
sg.entities[i].keyDown(e);
}
}
},
keyUp: function (e) {
for (var i = 0; i < sg.entities.length; i++) {
if (typeof sg.entities[i].keyUp === 'function') {
sg.entities[i].keyUp(e);
}
}
}
});
sg.Game = Game;
var game = sg.currentGame = new sg.Game(800, 600);
var player = new sg.Player(200, 459);
sg.createEntity(player);
function update(elapsed) {
game.update(elapsed);
}
var lastUpdate = Date.now();
function draw() {
var now = Date.now();
var elapsed = (now - lastUpdate);
lastUpdate = now;
game.draw();
update(elapsed);
requestAnimFrame(draw);
}
window.addEventListener('keydown', sg.currentGame.keyDown, false);
window.addEventListener('keyup', sg.currentGame.keyUp, false);
draw();
})();
The keydown/keyup events are waiting a little bit of time between one and another press.
Here is how I solved on my case (not sure if that's exactly your problem); adding a setInterval that every 20ms checks if there are key downs (more than one for diagonal movements).
var keys = {};
$(document).bind('keyup', function(e){
delete keys[e.which];
});
$(document).bind('keydown', function(e){
keys[e.which] = true;
});
setInterval(keyEvent, 20);
function keyEvent(){
for(var idKeyPressed in keys){
switch(idKeyPressed){
case "87": // "W" key; Move to the top
// Do something
break;
}
}
}
Hope that helps :)