Count Amount of Colored Squares in Canvas - javascript

Here is the fiddle: http://jsfiddle.net/sw31uokt/
Here is some of the relevant code for the incrementValue function I set up to count overall clicks within the canvas element.
What I would like to do is be able to display a count of each color, so "you have placed 14 red pixels, 3 blue pixels, 4 black pixels'.
function incrementValue()
{
var value = parseInt(document.getElementById('number').value, 10);
value = isNaN(value) ? 0 : value;
value++;
document.getElementById('number').value = value;
}
$(c_canvas).click(function(evt) {
var pos = getNearestSquare(getMousePos(c_canvas, evt));
if (pos != null) {
context.fillStyle=(currentColor);
context.fillRect(pos.x,pos.y,19,19);
incrementValue();
}
});

Basically, what MarkE said above ...
In the outer scope, add two new vars :
var palette = ["333333", "0000ff", "a0522d", "46ad42", "808080", "ffc0cb", "d73952", "ffe2a8", "ffff7d", "ffffff"];//as originally defined in the .spectrum() call.
var gridModel = [];//Eventually a sparse array of sparse arrays, representing colored grid squares. Uncolored grid squares remain undefined.
And two new functions, in the same scope :
function updateGridModel(pos, color) {
var x = (pos.x - 0.5) / 20;
var y = (pos.y - 0.5) / 20;
color = color.substr(1).toLowerCase();
if (!gridModel[x]) {
gridModel[x] = [];
}
gridModel[x][y] = palette.indexOf(color);
}
function paletteTally() {
//initialise an array, same length as palettes, with zeros
var arr = palette.map(function () {
return 0;
});
for (var x = 0; x < gridModel.length; x++) {
if (gridModel[x]) {
for (var y = 0; y < gridModel[x].length; y++) {
if (gridModel[x][y] !== undefined) {
arr[gridModel[x][y]] += 1;
}
}
}
}
return arr;
}
Modify the canvas's click handler to keep the gridModel up to date :
$(c_canvas).click(function (evt) {
var pos = getNearestSquare(getMousePos(c_canvas, evt));
if (pos != null) {
context.fillStyle = currentColor;
context.fillRect(pos.x, pos.y, 19, 19);
incrementValue();
updateGridModel(pos, currentColor); //keep the gridModel up to date.
}
});
Modify printColor() as follows :
function printColor(color) {
currentColor = color.toHexString();
$(".label").text(currentColor);
}
Modify the .spectrum() options and add an initialising call to printColor() as follows :
$("#showPaletteOnly").spectrum({
color: palette[0],
showPaletteOnly: true,
showPalette: true,
hideAfterPaletteSelect: true,
change: printColor,
palette: [palette] //<<<< palette is now defined as an outer var
});
printColor( $("#showPaletteOnly").spectrum('get') );//initialize currentcolor and $(".label").text(...) .
Now paletteTally() will return an array congruent with palette containing counts of each color.
EDIT 1
Original code above was untested but is now debugged and includes improved spectrum options. Demo.

Related

Trying to use createGraphics() in p5.js along with eye tracker and it isn't working

I'm still a beginner coder and I'm making a project where the eye positions (networked by socket) of two clients when landing on the same positions on the canvas would play the music note it lands on. I'm still in the beginning stages of this project and currently, I'm trying to draw the client's eye position on the canvas while the grid of music notes on a p5 renderer object. I had coded the grid to add an ellipse to where the mouse has clicked. The grid is successfully drawn but I no longer can interact with the grid i.e add or remove ellipses on click.
So now I'm a bit lost on how to solve this issue. Before when the eye was being drawn on p5.renderer I also tried the clear() function to get rid of the trails but it didn't work.
So I have two problems 1) trying to get rid of eye position trails and 2) using create graphics with mouse pressed function not working in code below.
// NETWORKED + EYE VARIABLES
let socket = io();
let ctracker;
let clients = {};
let data = {};
let w = 1200;
let h = 600;
let leftEyeX, leftEyeY, rightEyeX, rightEyeY;
let cnvPosX;
let cnvPosY;
/// SOUND VARIABLES //
let bell, bell1, bell2, bell3; // contains sound source
let bPat, b1Pat, b2Pat; // an array of no.s that we can use to make beats // 1 = on, 0= off
let bPhrase, b1Phrase, b2Phrase; // defines how the beat pattern is interpreted
let part; // attach all the instruments to the to make it into a machine i.e on/off
let bpmCTRL;
let beatLength; // how big is sequence before it starts looping
let cellSize;
let cnv;
let overlayCnv;
let bg, largeText,smallText, MleftEye, MRightEye;
let row1 =[];
function preload() {
// background
bg = loadImage("https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2Fdaw-bg.png?v=1589319965142");
//main game instruction
largeText = loadImage("https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2FAsset%2015.png?v=1589381930278");
// small game description
smallText = loadImage("https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2FAsset%203.png?v=1589381926354");
//sound files
bell = loadSound (
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203490__tesabob2001__g-5.mp3?v=1589326854551",
() => {}
);
bell1 = loadSound (
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203485__tesabob2001__c5.mp3?v=1589326924689",
() => {}
);
bell2 = loadSound (
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203489__tesabob2001__f5.mp3?v=1589326917439",
() => {}
);
bell3 = loadSound (
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203491__tesabob2001__g-4.mp3?v=1589326867294", () => {}
);
};
function setup() {
cnvPosX = 120;
cnvPosY = 50;
// setup camera capture
var videoInput = createCapture(VIDEO);
videoInput.size(w,h);
videoInput.position(cnvPosX, cnvPosY);
videoInput.hide();
// setup canvas
ctracker = new clm.tracker();
ctracker.init(pModel);
ctracker.start(videoInput.elt);
noStroke();
socket.on('getUpdate', function(data){
clients[data.name] = data;
});
cnv = createCanvas(w, h);
cnv.position(cnvPosX,cnvPosY)
overlayCnv = createGraphics(w,h);
// overlayCnv.position(cnvPosX,cnvPosY);
overlayCnv.mousePressed(canvasPressed);
beatLength = 6;
cellSize = 200;
numOfRows = 3;
// canvas for eyes
// basic structure of a DAW
// time is a sceduled delay in note play time
bPat = [0, 0, 0, 0, 0, 0];
b1Pat = [0, 0, 0, 0, 0, 0];
b2Pat = [0, 0, 0, 0, 0, 0];
function selectSong() {
row1 = [bell3, bell];
selected = row1[floor(random(2))];
console.log(selected);
selected.play();
}
// name, callback, array
bPhrase = new p5.Phrase(
"bell",
time => {
selectSong()
},
bPat
);
b1Phrase = new p5.Phrase(
"bell1",
time => {
// make a variable to go there insiead of bell -> use random function to give a value to the variable
bell1.play(time); },
b1Pat
);
b2Phrase = new p5.Phrase(
"bell2",
time => {
bell2.play(time);
},
b2Pat
);
part = new p5.Part();
part.addPhrase(bPhrase);
part.addPhrase(b1Phrase);
part.addPhrase(b2Phrase);
bpmCTRL = createSlider(30, 200, 60, 1); // smallest val, highest val, starting val, incremental val
bpmCTRL.position(10, 10); // x, y
bpmCTRL.input(() => {
part.setBPM(bpmCTRL.value());
});
part.setBPM("60");
drawMatrix();
///// user interact
function canvasPressed() {
console.log("mousepressed")
let rowClicked = floor(numOfRows * (mouseY / height));
let columnClicked = floor(beatLength * (mouseX / width));
if (rowClicked === 0) {
console.log("first row");
bPat[columnClicked] = +!bPat[columnClicked];
} else if (rowClicked === 1) {
console.log("second row");
b1Pat[columnClicked] = +!b1Pat[columnClicked];
} else if (rowClicked === 2) {
console.log("third row");
b2Pat[columnClicked] = +!b2Pat[columnClicked];
}
drawMatrix();
}
/// drawing the grid
function drawMatrix() {
overlayCnv.background(bg);
//line
overlayCnv.stroke(25,40,100);
overlayCnv.strokeWeight(2);
for (let i = 0; i < beatLength + 1; i++) {
overlayCnv.line(i * cellSize, 0, i * cellSize, height);
}
for (let i = 0; i < numOfRows + 1; i++) {
overlayCnv.line(0, (i * height) / numOfRows, width, (i * height) / numOfRows);
}
//circle
overlayCnv.noStroke();
overlayCnv.fill(25,40,100);
for (let i = 0; i < beatLength; i++) {
if (bPat[i] === 1) {
overlayCnv.ellipse(i * cellSize + 0.5 * cellSize, 100, 50);
}
if (b1Pat[i] === 1) {
overlayCnv.ellipse(i * cellSize + 0.5 * cellSize, 300, 40);
}
if (b2Pat[i] === 1) {
overlayCnv.ellipse(i * cellSize + 0.5 * cellSize, 500, 30);
}
}
}
image(overlayCnv, 0, 0);
}
function draw() {
let positions = ctracker.getCurrentPosition();
for (let i = 0; i < positions.length; i++) {
// draw ellipse at each position point
leftEyeX = positions[27][0];
leftEyeY = positions[27][1];
rightEyeX = positions[32][0];
rightEyeY = positions[32][1];
// ellipse(positions[i][0], positions[i][1], 8, 8);
fill(255);
ellipse(rightEyeX, rightEyeY, 18, 18);
fill(255);
ellipse(leftEyeX, leftEyeY, 18, 18);
// formatting each point into a dict entry to send over socket io
data[(27).toString() + "." + '0'] = leftEyeX;
data[(27).toString() + "." + '1'] = leftEyeY;
data[(32).toString() + "." + '0'] = rightEyeX;
data[(32).toString() + "." + '1'] = rightEyeY;
// image(eyeCnv,0,0); // x, y relative to canvas x & y
}
}
/////// conditional to check if all files are loaded
function keyPressed() {
if (key === " ") {
if (bell.isLoaded() && bell1.isLoaded() && bell2.isLoaded()) {
if (!part.isPlaying) {
part.loop();
} else {
part.stop();
}
} else {
console.log("relax");
}
}
}
Nevermind, the error was just that background kept drawing in draw()

Trying to use mousePressed on a button and it ain't working?

I’m running into some errors. I want the user to select an eye player through clicking a button (either left eye or right eye) and then draw to the canvas only the selected input. I am using an eye tracker to draw some png files onto the canvas. I want them to draw only the left eye one if the user clicks on the left button.
I'm sorry for the extremely long code, I'm very new to code and don't know how to do it efficiently. The main variables I've been using for my problem is 'left' which is a button I created in setup() and in draw() I go through an array of points (clm eye tracker) and then save the left and right eye positions to objects, I then call the function mouse pressed on the left button.
// NETWORKED + EYE VARIABLES
let socket = io(); // new socket on client side
let ctracker; // eye tracker obj
let clients = {}; // client object
let data = {}; // data object
let w = 1200; // matrix width
let h = 600; // matrix height
let leftEye, rightEye; // main eye image
let SleftEye, SrightEye; // shadow eye image
let leftEyeX, leftEyeY, rightEyeX, rightEyeY; // current user eye data point vars
let cnvPosX, cnvPosY; // postion of main canvas
let playerLeft = {};
let playerRight = {};
let left,right;
/// SOUND VARIABLES //
let bell, bell1, bell2, bell3; // contains sound source
let bPat, b1Pat, b2Pat; // an array of nums that we can use to make a sequence of beats // 1 = on, 0= off
let bPhrase, b1Phrase, b2Phrase; // defines how the beat pattern is interpreted
let part; // attach all the above (sound,array,phrase) into a machine (part) i.e on/off,looper
let beatLength; // how big is sequence before it starts looping
let cellSize; // each sqauare size contains beat
let row1 = []; // array of sounds in row 1
// DOM variables
let play; // play button
let bg, largeText, smallText; // image variables for game instructions and background
let cnv; // main canvas
let matrixCnv; // canvas that draws the matrix ( music user interface )
let instructCnv; // instruction canvas at the topmost part
let bpmCTRL; // beat per minute slider
// let previousCo = {
// left: {row: null, column: null},
// right: {row: null, column:null}
// };
function preload() {
// background
bg = loadImage(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2Fdaw-bg.png?v=1589319965142"
);
//main game instruction
largeText = loadImage(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2FAsset%2015.png?v=1589381930278"
);
// small game description
smallText = loadImage(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2FAsset%203.png?v=1589381926354"
);
// game main player left eye
leftEye = loadImage(
"https://cdn.glitch.com/7e3760d0-fed0-482e-a757-d1ca0280067e%2Fleft-eye.png?v=1589284305903"
);
// game main player right eye
rightEye = loadImage(
"https://cdn.glitch.com/7e3760d0-fed0-482e-a757-d1ca0280067e%2Fright-eye.png?v=1589284306011"
);
// game shadow player left eye
SleftEye = loadImage(
"https://cdn.glitch.com/7e3760d0-fed0-482e-a757-d1ca0280067e%2Fother-left-eye.png?v=1589284306106"
);
// game shadow player left eye
SrightEye = loadImage(
"https://cdn.glitch.com/7e3760d0-fed0-482e-a757-d1ca0280067e%2Fother-right-eye.png?v=1589284332165"
);
//sound files
bell = loadSound(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203490__tesabob2001__g-5.mp3?v=1589326854551",
() => {}
);
bell1 = loadSound(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203485__tesabob2001__c5.mp3?v=1589326924689",
() => {}
);
bell2 = loadSound(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203489__tesabob2001__f5.mp3?v=1589326917439",
() => {}
);
bell3 = loadSound(
"https://cdn.glitch.com/e024d4d5-2c9e-473c-acd3-c2e01a1ef9cd%2F203491__tesabob2001__g-4.mp3?v=1589326867294",
() => {}
);
}
function setup() {
cnvPosX = 120;
cnvPosY = 50;
// setup camera capture
var videoInput = createCapture(VIDEO);
videoInput.size(w, h);
videoInput.position(cnvPosX, cnvPosY);
videoInput.hide();
// setup tracker
ctracker = new clm.tracker();
ctracker.init(pModel); // feed it model pca 20
ctracker.start(videoInput.elt); // feed it video data
// open socket io for connections
socket.on("getUpdate", function(data) {
clients[data.name] = data;
});
instructCnv = createGraphics(w, h);
instructCnv.background(0);
matrixCnv = createGraphics(w, h); // matrix canvas of daw
cnv = createCanvas(w, h); // main canvas that the eys are being drawn on
cnv.position(cnvPosX, cnvPosY); // main. canvas position
matrixCnv.mousePressed(canvasPressed); //drawMatrix() function on matrix function
cnv.mousePressed(canvasPressed); // drawMatrix() function on main function
beatLength = 6; // number of beats it loops thru && columns of matrix
cellSize = 200; // size of square
numOfRows = 3; // rows of matrix
play = createButton('Play/Pause');
play.position(1300, 10);
play.size(80,30);
play.mousePressed(playAudio);
// basic structure of a Digital Audio Workstation
// time is a sceduled delay in note play time
bPat = [0, 1, 0, 0, 0, 0];
b1Pat = [0, 0, 1, 0, 1, 1];
b2Pat = [1, 0, 0, 1, 0, 0];
// random selection of sound(bell3 or bell) in row 1
function selectSong() {
row1 = [bell3, bell];
selected = row1[floor(random(2))];
selected.setVolume(0.1);
selected.play();
}
// name, callback, array
bPhrase = new p5.Phrase(
"bell",
time => {
selectSong();
},
bPat
);
b1Phrase = new p5.Phrase(
"bell1",
time => {
// make a variable to go there insiead of bell -> use random function to give a value to the variable
bell1.setVolume(0.1);
bell1.play(time);
},
b1Pat
);
b2Phrase = new p5.Phrase(
"bell2",
time => {
bell2.setVolume(0.1);
bell2.play(time);
},
b2Pat
);
// creating a new part that contains all the above
part = new p5.Part();
part.addPhrase(bPhrase);
part.addPhrase(b1Phrase);
part.addPhrase(b2Phrase);
bpmCTRL = createSlider(30, 200, 60, 1); // smallest val, highest val, starting val, incremental val
bpmCTRL.position(10, 10); // x, y
bpmCTRL.input(() => {
part.setBPM(bpmCTRL.value());
});
part.setBPM("60");
//button left
left = createButton('Left');
left.position(300,15);
// drawing the grid
drawMatrix();
}
function draw() {
image(matrixCnv, 0, 0);
// background(bg);
let positions = ctracker.getCurrentPosition();
for (let i = 0; i < positions.length; i++) {
// find in array each position point
playerLeft = {
x : positions[27][0],
y : positions[27][1]
playerRight = {
x : positions[32][0],
y : positions[32][1]
}
left.mousePressed(drawLeft(playerLeft.x, playerLeft.y));
// formatting each point into a dict entry to send over socket io
data[(27).toString() + "." + "0"] = playerLeft.x;
data[(27).toString() + "." + "1"] = playerLeft.y;
data[(32).toString() + "." + "0"] = playerRight.x;
data[(32).toString() + "." + "1"] = playerRight.y;
// update client details
data["name"] = socket.id;
data["updated"] = new Date().getTime();
socket.emit("update", data);
//recieving data by other client and drawing
var x, y;
for (var c in clients) {
if (c == socket.id) {
// if client is not the the current user
continue;
}
var points = clients[c];
// console.log(points)
var leftEyePoint = {
x: points['27.0'],
y: points['27.1']
};
var rightEyePoint = {
x: points['32.0'],
y: points['32.1']
};
image(SleftEye, leftEyePoint.x, leftEyePoint.y, 150, 100)
image(SrightEye, rightEyePoint.x, rightEyePoint.y, 150, 100)
// canvasEyed(leftEyePoint, rightEyePoint)
}
}
drawMatrix();
}
function drawLeft(x,y) {
push();
translate(x, y);
image(leftEye, 0, 0, 150, 100);
pop();
}
function drawRight() {
push();
translate(playerRight.x, playerRight.y);
image(rightEye, 0, 0, 150, 100);
pop();
}
/////// conditional to check if all files are loaded
function playAudio() {
if (bell.isLoaded() && bell1.isLoaded() && bell2.isLoaded()) {
if (!part.isPlaying) {
part.loop();
} else {
part.stop();
}
} else {
console.log("relax and wait for sound to load");
}
}
/// user interact
function canvasPressed() {
// console.log("mousepressed")
let rowClicked = floor(numOfRows * (mouseY / height));
let columnClicked = floor(beatLength * (mouseX / width));
if (rowClicked === 0) {
console.log("first row");
bPat[columnClicked] = +!bPat[columnClicked];
} else if (rowClicked === 1) {
console.log("second row");
b1Pat[columnClicked] = +!b1Pat[columnClicked];
} else if (rowClicked === 2) {
console.log("third row");
b2Pat[columnClicked] = +!b2Pat[columnClicked];
}
}
/// drawing the grid
function drawMatrix() {
matrixCnv.background(bg);
//line
matrixCnv.stroke(25, 40, 100);
matrixCnv.strokeWeight(2);
for (let i = 0; i < beatLength + 1; i++) {
matrixCnv.line(i * cellSize, 0, i * cellSize, height);
}
for (let i = 0; i < numOfRows + 1; i++) {
matrixCnv.line(
0,
(i * height) / numOfRows,
width,
(i * height) / numOfRows
);
}
//circle
matrixCnv.noStroke();
matrixCnv.fill(25, 40, 100);
for (let i = 0; i < beatLength; i++) {
if (bPat[i] === 1) {
matrixCnv.ellipse(i * cellSize + 0.5 * cellSize, 100, 50);
}
if (b1Pat[i] === 1) {
matrixCnv.ellipse(i * cellSize + 0.5 * cellSize, 300, 40);
}
if (b2Pat[i] === 1) {
matrixCnv.ellipse(i * cellSize + 0.5 * cellSize, 500, 30);
}
}
}
Dan O is right,
That is not the correct way of passing variables as arguments in callback functions.
left.mousePressed(drawLeft(playerLeft.x, playerLeft.y));
It Should be:
left.mousePressed( function()=>{
drawLeft( playerLeft.x, playerLeft.y );
} );
This way you create a new function, an extra step inbetween. The function calls drawLeft(). Then pass that new function as the callback function. It's the same as doing this:
let afterMousePressed = function(){
drawLeft( playerLeft.x, playerLeft.y );
};
left.mousePressed( afterMousePressed );

Why is my sprite graphing algorithm sometimes leaving gaps while graphing?

My friends and I are working on a game. However, I've run into difficulty with the star sprite management. Sometimes, there are gaps (areas where sprites should be graphed but instead are not). I have included the entire program as a code snippet but below are also code blocks which are likely directly responsible for the problem.
There are coordinate pairs for each sprite. The coordinate map is presented below:
[{x:xCoordinate,y:yCoordinate},{x:xCoordinate,y:yCoordinate},...];
This code spawns and removes sprites...
for(var i = 0; i < stars.positions.length; i++){
//store coordinate positions...
var x = stars.positions[i].x;
var y = stars.positions[i].y;
//delete sprites no longer visible within the viewport...
if(x > window.innerWidth || x < -spriteWidth || y < -spriteHeight || y > window.innerHeight){
//sprite is no longer visible within viewport; remove it...
stars.positions.splice(i,1);
}
//find necessary comparative coordinates...
var lowestXCoordinatePair = stars.meta.lowestXCoordinatePair;
var highestXCoordinatePair = stars.meta.highestXCoordinatePair;
var lowestYCoordinatePair = stars.meta.lowestYCoordinatePair;
var highestYCoordinatePair = stars.meta.highestYCoordinatePair;
//gather star sprite meta data...
var spriteWidth = stars.meta.spriteWidth;
var spriteHeight = stars.meta.spriteHeight;
if(lowestXCoordinatePair.x > 0){
//Gap on the left side. New sprites necessary to fill the gap on left row...
//console.log('adding sprites left row...')
for(var i = 0; i < stars.meta.imagesYRequired; i++){
stars.positions.push({
x:lowestXCoordinatePair.x-spriteWidth,
y:lowestXCoordinatePair.y+i*spriteHeight
});
}
}
if(highestXCoordinatePair.x < window.innerWidth-spriteWidth){
//Gap on the right side. New sprites necessary to fill the gap on the right row...
//console.log('adding sprites right row...')
for(var i = 0; i < stars.meta.imagesYRequired; i++){
stars.positions.push({
x:highestXCoordinatePair.x+spriteWidth,
y:highestXCoordinatePair.y+i*spriteHeight
});
}
}
if(lowestYCoordinatePair.y > 0){
//Gap on the top side. New sprites necessary to fill the gap on the top row...
//console.log('adding sprites top row...')
for(var i = 0; i < stars.meta.imagesXRequired; i++){
stars.positions.push({
x:lowestYCoordinatePair.x+i*spriteWidth,
y:lowestYCoordinatePair.y-spriteHeight
});
}
}
if(highestYCoordinatePair.y < window.innerHeight-spriteHeight){
//Gap on the bottom side. New sprites necessary to fill the gap on the bottom row...
console.log('adding sprites bottom row...')
for(var i = 0; i < stars.meta.imagesXRequired; i++){
stars.positions.push({
x:highestYCoordinatePair.x+i*spriteWidth,
y:highestYCoordinatePair.y+spriteHeight
});
}
}
'use strict';
//global variables
var canvas, c;
//viewport variables
var viewportPosition = {
x:false,
y:false
};
//game matrix settings
var gameMatrixConfig = {
width:20000,
height:20000
};
//cursor position
var cursorPosition = {
x:false,
y:false
};
//spaceship position
var spaceship = {
x:false,
y:false,
rotation:0,
gameMatrixPositionX:false,
gameMatrixPositionY:false
};
//fps monitor (for monitoring frame rate for development purposes)...
var fps = {
lastFrameTime:undefined,
timeSinceLastFrame:undefined,
startDisplayTimeInterval:function(){
setInterval(function(){
document.getElementById('fpsLabel').innerHTML = Math.floor(1000/getTimeSinceLastFrame());
},500);
}
};
function getTimeSinceLastFrame(){
return fps.timeSinceLastFrame;
}
//resize throttle timer global variable holder
var resizeTimer = false;
//the drawing frame:
var renderFrame = false;
//global events
window.addEventListener('load',function(){
initialize('load');
});
window.addEventListener('resize',function(){
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function(){
initialize('resize');
},100);
});
window.addEventListener('mousemove',function(e){
cursorPosition.x = e.clientX;
cursorPosition.y = e.clientY;
});
//global functions
function initialize(type){
if(type == 'load'){
preLoadSprites();
}
initializeCanvas();
}
function initializeCanvas(){
canvas = document.getElementById('canvas');
c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
c.width = canvas.width;
c.height = canvas.height;
}
/*a class with a filePath argument.
This class is used to create new sprite images
from an instance of the class.*/
function sprite(filePath){
//create a new image preload
this.sprite = new Image();
this.sprite.src = filePath;
//load status
this.loaded = false;
/*
holds all current positions of the sprite.
default start coordinates are 0,0
*/
this.positions = [];
//original associative array data structure...
//this.positions = {};
//the image has been preloaded
this.sprite.addEventListener('load',function(){
this.loaded = true;
/*
bind the "this" reference of the constructor to avoid referencing
the load event function
*/
}.bind(this));
}
function preLoadSprites(){
//create new objects for all the sprites to be used in the game
var sprites = {
stars:new sprite('https://drive.google.com/uc?export=download&id=0B821h2dKD0r_bERZb0RHeVRTQnM')
};
/*
check the load status of the sprites
loop through all properties of sprites object twice per second
check for load status. Load flag default is true. Set flag to false
when an unloaded image is discovered.
*/
var interval = setInterval(function(){
var x, loaded = true;
for(x in sprites){
if(!sprites[x].loaded){
loaded = false;
}
}
if(loaded){
clearInterval(interval);
//complete other tasks such as hiding a load spinner...
//ready sprites and context for graphing and provide access to sprites....
initializeGraphing(sprites);
}
},50);
}
function initializeGraphing(sprites){
//set initial viewport position in game matrix....
viewportPosition.x = gameMatrixConfig.width/2;
viewportPosition.y = gameMatrixConfig.height/2;
//start graph animation loop; provide access to sprites....
graph(sprites);
}
/*research how to inherit values from another object with
local variables.*/
function graph(sprites){
updateSpritePositions(sprites);
//testing frame rate...
setInterval(function(){draw()},16.67);
//60fps interval code. Uncomment when testing is complete...
//setInterval(function(){draw()},16.67);
//calculate sprite requirements for viewport and configure a sprite matrix...
initializeStars(sprites.stars);
fps.startDisplayTimeInterval();
//render the graphic frame
function draw(){
//console.log(fps.timeSinceLastFrame)
fps.timeSinceLastFrame = Date.now()-fps.lastFrameTime;
//fps.displayTimeInterval();
//fps.lastFrameTime = Date.now();
//clear the canvas
c.clearRect(0,0,window.innerWidth,window.innerHeight);
//graph the stars...
graphStars(sprites.stars);
updateSpritePositions(sprites);
fps.lastFrameTime = Date.now();
}
}
function initializeStars(stars){
/*
calculate sprite requirements for viewport and configure a sprite matrix
this only needs to happen once unless the viewport is resized
*/
/*
meta data used for various calculations throughout the script...
*/
stars.meta = {
//required sprites to fill the viewport width (based on sprite width)...
imagesXRequired:Math.ceil(window.innerWidth/(stars.sprite.width))+2,
//required sprites to fill the viewport height (based on sprite height)...
imagesYRequired:Math.ceil(window.innerHeight/(stars.sprite.height))+2,
//required sprites to fill the entire viewport...
get requiredSprites(){
return this.imagesXRequired*this.imagesYRequired;
},
//the sprite width...
spriteWidth:stars.sprite.width,
//the sprite height...
spriteHeight:stars.sprite.height,
//the lowest x value in stars.positions...
get lowestXCoordinatePair(){
var xCoordinates = [];
var yCoordinates = [];
for(var i = 0; i < stars.positions.length; i++){
xCoordinates.push(stars.positions[i].x);
yCoordinates.push(stars.positions[i].y);
}
var x = Math.min.apply(Math, xCoordinates);
var index = xCoordinates.indexOf(x);
var y = yCoordinates[index];
return {
x:x,
y:y
};
},
//the highest x value in stars.positions...
get highestXCoordinatePair(){
var xCoordinates = [];
var yCoordinates = [];
for(var i = 0; i < stars.positions.length; i++){
xCoordinates.push(stars.positions[i].x);
yCoordinates.push(stars.positions[i].y);
}
var x = Math.max.apply(Math, xCoordinates);
var index = xCoordinates.indexOf(x);
var y = yCoordinates[index];
return {
x:x,
y:y
};
},
//the lowest y value in stars.positions...
get lowestYCoordinatePair(){
var xCoordinates = [];
var yCoordinates = [];
for(var i = 0; i < stars.positions.length; i++){
xCoordinates.push(stars.positions[i].x);
yCoordinates.push(stars.positions[i].y);
}
var y = Math.min.apply(Math, yCoordinates);
var index = yCoordinates.indexOf(y);
var x = xCoordinates[index];
return {
x:x,
y:y
};
},
//the highest y value in stars.positions...
get highestYCoordinatePair(){
var xCoordinates = [];
var yCoordinates = [];
for(var i = 0; i < stars.positions.length; i++){
xCoordinates.push(stars.positions[i].x);
yCoordinates.push(stars.positions[i].y);
}
var y = Math.max.apply(Math, yCoordinates);
var index = yCoordinates.indexOf(y);
var x = xCoordinates[index];
return {
x:x,
y:y
};
}
};
//the y coordinate in a scaled matrix system for sprites...
var y = 0;
//the x coordinate in a scaled matrix system for sprites...
var x = 0;
//loop through the number of required sprites and graph...
for(var i = 0; i < stars.meta.requiredSprites; i++){
//calculate when a new row is necessary
if((i)%stars.meta.imagesXRequired == 0){
x = 0;
y++;
}
//set actual starting viewport matrix coordinate positions....
stars.positions[i] = {
x:x*stars.meta.spriteWidth,
y:y*stars.meta.spriteWidth
};
x++;
}
}
function graphStars(stars){
/*
prior to graphing, determine if a sprite is no longer within
the viewport matrix and remove it...
if new sprites are necessary, add new coordinates accordingly...
*/
/*
==============IMPORTANT NOTE==================
*/
for(var i = 0; i < stars.positions.length; i++){
//store coordinate positions...
var x = stars.positions[i].x;
var y = stars.positions[i].y;
//delete sprites no longer visible within the viewport...
if(x > window.innerWidth || x < -spriteWidth || y < -spriteHeight || y > window.innerHeight){
//sprite is no longer visible within viewport; remove it...
stars.positions.splice(i,1);
}
//find necessary comparative coordinates...
var lowestXCoordinatePair = stars.meta.lowestXCoordinatePair;
var highestXCoordinatePair = stars.meta.highestXCoordinatePair;
var lowestYCoordinatePair = stars.meta.lowestYCoordinatePair;
var highestYCoordinatePair = stars.meta.highestYCoordinatePair;
//gather star sprite meta data...
var spriteWidth = stars.meta.spriteWidth;
var spriteHeight = stars.meta.spriteHeight;
if(lowestXCoordinatePair.x > 0){
//Gap on the left side. New sprites necessary to fill the gap on left row...
//console.log('adding sprites left row...')
for(var i = 0; i < stars.meta.imagesYRequired; i++){
stars.positions.push({
x:lowestXCoordinatePair.x-spriteWidth,
y:lowestXCoordinatePair.y+i*spriteHeight
});
}
}
if(highestXCoordinatePair.x < window.innerWidth-spriteWidth){
//Gap on the right side. New sprites necessary to fill the gap on the right row...
//console.log('adding sprites right row...')
for(var i = 0; i < stars.meta.imagesYRequired; i++){
stars.positions.push({
x:highestXCoordinatePair.x+spriteWidth,
y:highestXCoordinatePair.y+i*spriteHeight
});
}
}
if(lowestYCoordinatePair.y > 0){
//Gap on the top side. New sprites necessary to fill the gap on the top row...
//console.log('adding sprites top row...')
for(var i = 0; i < stars.meta.imagesXRequired; i++){
stars.positions.push({
x:lowestYCoordinatePair.x+i*spriteWidth,
y:lowestYCoordinatePair.y-spriteHeight
});
}
}
if(highestYCoordinatePair.y < window.innerHeight-spriteHeight){
//Gap on the bottom side. New sprites necessary to fill the gap on the bottom row...
console.log('adding sprites bottom row...')
for(var i = 0; i < stars.meta.imagesXRequired; i++){
stars.positions.push({
x:highestYCoordinatePair.x+i*spriteWidth,
y:highestYCoordinatePair.y+spriteHeight
});
}
}
c.drawImage(stars.sprite,x,y);
}
}
function updateViewportPosition(){
}
function updateSpritePositions(sprites){
/*gather information from the cursor to influence the next frame data
cursor is a local object variable template for use within this function only
cursor stores information about the cursor's position
*/
var cursor = {
distance:{
//the cursor's distances on the planes from the origin for X and Y
cursorXDistance:Math.abs(window.innerWidth/2-cursorPosition.x),
cursorYDistance:Math.abs(window.innerHeight/2-cursorPosition.y)
},
quadrant:function(){
//method returns the appropriate quadrant number for the unit circle
if(cursorPosition.x > window.innerWidth/2 && cursorPosition.y < window.innerHeight/2){
//first quadrant
return 1;
}
if(cursorPosition.x > window.innerWidth/2 && cursorPosition.y > window.innerHeight/2){
//fourth quadrant
return 4;
}
if(cursorPosition.x < window.innerWidth/2 && cursorPosition.y < window.innerHeight/2){
//second quadrant
return 2;
}
if(cursorPosition.x < window.innerWidth/2 && cursorPosition.y > window.innerHeight/2){
//third quadrant
return 3;
}
}
};
//calculate the velocity (the number of pixels to move for the next frame)...
function velocity(){
/*
To calculate velocity ratio, divide the hypotenuse of the cursor's position
by the viewport hypotenuse.
*/
return Math.sqrt(Math.pow(cursor.distance.cursorXDistance,2) + Math.pow(cursor.distance.cursorYDistance,2))/100;
}
//calculate the movement ratio: the number of x pixels per y pixels
function movementRatio(){
var xRatio = cursor.distance.cursorXDistance/(cursor.distance.cursorYDistance+cursor.distance.cursorXDistance);
var yRatio = cursor.distance.cursorYDistance/(cursor.distance.cursorYDistance+cursor.distance.cursorXDistance);
return {xRatio,yRatio};
}
//update positions of sprites...
//retrieve the current movement ratio object...
var coordinateChange = movementRatio();
//retrieve the current quadrant of the unit circle for the cursor's position...
var quadrant = cursor.quadrant();
//retrieve velocity coefficient...
var velocity = velocity();
//update viewport position based on quadrant position...
var i;
for(i in sprites.stars.positions){
if(quadrant == 1){
//update star sprite position
sprites.stars.positions[i].x -= coordinateChange.xRatio*velocity;
sprites.stars.positions[i].y += coordinateChange.yRatio*velocity;
}
if(quadrant == 2){
//update star sprite position
sprites.stars.positions[i].x += coordinateChange.xRatio*velocity;
sprites.stars.positions[i].y += coordinateChange.yRatio*velocity;
}
if(quadrant == 3){
//update the star sprite position
sprites.stars.positions[i].x += coordinateChange.xRatio*velocity;
sprites.stars.positions[i].y -= coordinateChange.yRatio*velocity;
}
if(quadrant == 4){
//update star sprite position
sprites.stars.positions[i].x -= coordinateChange.xRatio*velocity;
sprites.stars.positions[i].y -= coordinateChange.yRatio*velocity;
}
}
}
html,body{
width:100%;
height:100%;
margin:0;
padding:0;
}
canvas{
width:100%;
height:100%;
margin-bottom:-8px;
background:#434343;
}
#gameInterface{
width:100%;
height:100%;
}
.hidden{
width:1px;
height:1px;
position:fixed;
top:0;
left:0;
z-index:-100;
}
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Canvas Game v0.35</title>
<!--<link rel="stylesheet" href="css/index.css" type="text/css">-->
<!--<script type="text/javascript" src="js/index.js"></script>-->
</head>
<body>
<div id="fpsLabel" style="position:fixed; top:0; left:0; display:table; background:#fff;">
</div>
<div id="intro">
</div>
<div id="gameInterface">
<canvas id="canvas"></canvas>
</div>
</body>
</html>
I've brainstormed a list of possible causes for the problem:
Gaps are caused by sprites being removed too soon.
Gaps are caused by sprites not being spawned.
(sub item) Spawning conditional is correct but the loop to create a new row is flawed.
(sub item) Spawning conditional is flawed.
Could someone help me understand why there are occasionally gaps and help me understand how to fix it? Thank you.

Overlap callback never called

I am creating a small space invaders-like game.
In create function I create enemies
enemies = game.add.group();
enemies.enableBody = true;
enemies.physicsBodyType = Phaser.Physics.ARCADE;
enemies.x = 100;
enemies.y = 50;
for (var y = 1; y < 200; y += 50) {
for (var x = 233; x <= 800; x += 50) {
var enemy = enemies.create(x, y, 'enemy');
enemy.anchor.setTo(0.5, 0.5);
enemy.body.moves = false;
}
}
and bullets
bullets = game.add.group();
bullets.enableBody = true;
bullets.physicsBodyType = Phaser.Physics.ARCADE;
bullets.createMultiple(30, 'bullet');
bullets.setAll('anchor.x', 0.5);
bullets.setAll('anchor.y', 1);
bullets.setAll('outOfBoundsKill', true);
bullets.setAll('checkWorldBounds', true);
and set the overlap callback
game.physics.arcade.overlap(bullets, enemies, collisionHandler);
But, unfortunately, when the bullet overlaps an enemy, nothing happens.
Callback is
function collisionHandler (bullet, enemy) {
console.log("poft");
bullet.kill();
enemy.kill();
}
In your case you only need to check if there is a collision between both groups, so you would choose to use the 'overlap' method that will be evaluated in the function update:
function update() {
game.physics.arcade.overlap(bullets, enemies, collisionHandler, null, this);
}
The method receives five arguments, you can consult them here.
And simple example of Physics Arcade: Group vs Group

Adding obstacles & collision to canvas game

I am trying to add some obstacles to the canvas game that I've got but something seems to be wrong and I can't seem to put my finger on it.
I just want some simple walls here and there to make the game harder and collision with the walls (so that if the player hits the wall it's game over).
var
// variables
COLS = 25, // Columns
ROWS = 25, // Rows
EMPTY = 0, // Empty Cell
SNAKE = 1, // Snake
FRUIT = 2, // Fruit
LEFT = 0, // left direction (key)
UP = 1, // up direction (key)
RIGHT = 2, // right direction (key)
DOWN = 3, // down direction (key)
KEY_LEFT = 37, // key codes for keyboard input (Codes can be found online)
KEY_UP = 38, // key codes for keyboard input (Codes can be found online)
KEY_RIGHT = 39, // key codes for keyboard input (Codes can be found online)
KEY_DOWN = 40, // key codes for keyboard input (Codes can be found online)
obstacle = [[0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0]],
// [0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0],
// [0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0],
// [0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0],
// [0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0]],
// Objects
canvas, // Canvas
ctx, // Canvas render
keystate, // Key inputs
frames, // frames per second
score; // player score
grid = {
width: null, // Amount of columns
height: null, // Amount of rows
_grid: null, // Array
init: function(d, c, r) { // initiation with direction, columns and rows.
this.width = c; // Set width to number of columns (c)
this.height = r; // set height to number of rows (r)
this._grid = []; // Initiate grid with empty array
for (var x=0; x < c; x++) {
this._grid.push([]);
for (var y=0; y < r; y++) {
this._grid[x].push(d); // set current column and push new value for each row in column
}
}
},
set: function(val, x, y) { // set values for the grid cells with x and y position
this._grid[x][y] = val;
},
get: function(x, y) { // get the value of x and y position
return this._grid[x][y];
}
}
snake = { // Creating snake
direction: null, // Direction of snake
last: null, // last element in queue pointer
_queue: null, // queue array
// Sets start position of snake, same initiation method as before
init: function(d, x, y) {
this.direction = d; // Direction set to d
this._queue = []; // Another empty queue array
this.insert(x, y); // Inserting x & y position
},
// Insert method that adds elements to queue with x and y position
insert: function(x, y) {
this._queue.unshift({x:x, y:y}); // unshift prepends an element to array
this.last = this._queue[0];
},
// Remove function to remove and return element to queue
remove: function() {
return this._queue.pop(); // pop returns the last element of array
}
};
function obstacle() {
empty.push({obstacle});
ctx.beginPath();
ctx.rect(obstacle);
ctx.fillStyle = "#7a26ce";
ctx.fill();
ctx.closePath();
}
function setFood() { // Food for hungry snake
var empty = []; // tracks all empty places in the grid
// for loop to find all empty cells in grid
for (var x=0; x < grid.width; x++) {
for (var y=0; y < grid.height; y++) {
if (grid.get(x, y) === EMPTY) {
empty.push({x:x, y:y});
}
}
}
// variable randomposition to pick random empty cell
var randpos = empty[Math.round(Math.random()*(empty.length - 1))];
grid.set(FRUIT, randpos.x, randpos.y);
}
function main() { // call all the functions that we will use in the game
// canvas
canvas = document.createElement("canvas");
canvas.width = COLS*20; // Sets canvas width to columns * 20
canvas.height = ROWS*20; // Sets canvas height to columns * 20
ctx = canvas.getContext("2d");
document.body.appendChild(canvas); // Adds canvas element to the body of the document
ctx.font = "12px sans-serif"; // font
frames = 0;
keystate = {};
document.addEventListener("keydown", function(evt) { // Track all keyboard input
keystate[evt.keyCode] = true;
});
document.addEventListener("keyup", function(evt) { // Track all keyboard input
delete keystate[evt.keyCode];
});
init(); // Initiate Game Loop
loop(); // Start Game Loop
}
function init() { // Reset and intiate game objects
score = 0; // set start score to 0
grid.init(EMPTY, COLS, ROWS);
var sp = {x:Math.floor(COLS/2), y:ROWS-1};
snake.init(UP, sp.x, sp.y); // Start direction
grid.set(SNAKE, sp.x, sp.y);
setFood();
grid._grid = grid._grid.concat(obstacle);
}
function loop() { // Game loop for rendering and objects
update();
draw();
window.requestAnimationFrame(loop, canvas); // Canvas will call loop function when it needs to redraw
}
function update() { // update function
frames++;
// Keyboard input
if (keystate[KEY_LEFT] && snake.direction !== RIGHT) {
snake.direction = LEFT;
}
if (keystate[KEY_UP] && snake.direction !== DOWN) {
snake.direction = UP;
}
if (keystate[KEY_RIGHT] && snake.direction !== LEFT) {
snake.direction = RIGHT;
}
if (keystate[KEY_DOWN] && snake.direction !== UP) {
snake.direction = DOWN;
}
// Update game every 5 frames.
if (frames%5 === 0) {
// last element from the snake queue
var nx = snake.last.x;
var ny = snake.last.y;
// Updating the position of snake depending on the direction it is heading
switch (snake.direction) {
case LEFT:
nx--;
break;
case UP:
ny--;
break;
case RIGHT:
nx++;
break;
case DOWN:
ny++;
break;
}
// if statement checking conditions whether game should keep running or reset aka game over
if (0 > nx || nx > grid.width-1 ||
0 > ny || ny > grid.height-1 ||
grid.get(nx, ny) === SNAKE
) {
return init();
}
// Checks the new position of the snake and if it's on a fruit item or not.
if (grid.get(nx, ny) === FRUIT) {
// If it is on a fruit item it will increase your score and create a new food in a random cell.
score++;
setFood();
} else {
// Takes out the tail (first item) from queue and removes identifier from the grid.
var tail = snake.remove();
grid.set(EMPTY, tail.x, tail.y);
}
// Snake identifier that is created at the new position and is added to the queue
grid.set(SNAKE, nx, ny);
snake.insert(nx, ny);
}
}
function draw() { // render grid to canvas
var tw = canvas.width/grid.width; // Calculate tile width
var th = canvas.height/grid.height; // Calculate tile height
for (var x=0; x < grid.width; x++) { // for-loop loops through entire grid to draw cells
for (var y=0; y < grid.height; y++) {
// Depending on the identifier of each cell sets certain fillstyle defined below.
switch (grid.get(x, y)) {
case EMPTY:
ctx.fillStyle = "#5a5a5a";
break;
case SNAKE:
ctx.fillStyle = "#B54548";
break;
case FRUIT:
ctx.fillStyle = "lightblue";
break;
case obstacle:
ctx.fillStyle = "yellow";
break;
}
ctx.fillRect(x*tw, y*th, tw, th);
}
}
// Change fillstyle and show score on the screen.
ctx.fillStyle = "#000";
ctx.fillText("SCORE: " + score, 10, canvas.height-10);
}
// Game Start!
main();
canvas {
display: block;
position: absolute;
border: 2px solid #000;
margin: auto;
top: 0;
bottom: 0;
right: 0;
left: 0;
}
or Fiddle.
I have been making games with collisions for a while now, and I find that using a collision map is the easiest way, instead of making one object that stores all entities at once. Here's an example:
var collisions = [];
function addCollision(x,y){
if(typeof collisions[x] == "undefined")collisions[x] = []; //If the row is empty, create it
collisions[x][y] = true; //Set the row and column to true
};
function checkCollision(x,y){
return (typeof collisions[x] != "undefined")?//If the row is undefined, there's nothing in it, so return false
((typeof collisions[x][y] != "undefined") //If not, but the column is undefined, return false
?true:false):false; //If the row and column is true, return true.
};
Now, addCollision(x,y) will put a new piece to collide with, and checkCollision(x,y) will see there is anything at x,y.
This is good for (relatively) small maps, as a large one will eat up a lot of memory. I have been able to do 500x500, but I'm not sure what the max size is.

Categories