Uncaught ReferenceError. Phaser.io - javascript

Okay, I am building a game and after implementing a level selection I nearly got my project working. However, there is a error(probably easy to fix) that has caught my eye.
In my create function I create the variable "pages", but when I want to use it in an other function I get the ReferenceError: Uncaught ReferenceError: pages is not defined. I am fairly new to javascript and I also tried to pass the variable in the paramater, the error will go away, but the code won't work as I want to.
I define the pages in the create function and I want to use it in the arrowClicked function
UshanGame.Selection = function(game){};
var thumbRows = 2;
var thumbCols = 3;
var thumbWidth = 128;
var thumbHeight = 128;
var thumbSpacing = 3;
var levelThumbsGroup;
var currentPage = 0;
var leftArrow;
var rightArrow;
UshanGame.Selection.prototype = {
create: function(){
console.log("%c ✔✔ Level Selection Ready! ✔✔", "color:red;");
// array with finished levels and stars collected.
var starsArray = [1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2];
// how many pages are needed to show all levels?
var pages = starsArray.length /(thumbRows * thumbCols);
leftArrow = this.add.button(50, 420, "level_arrows", this.arrowClicked);
leftArrow.anchor.setTo(0.5);
leftArrow.frame = 0;
leftArrow.alpha = 0.3;
rightArrow = this.add.button(270, 420, "level_arrows", this.arrowClicked);
rightArrow.anchor.setTo(0.5);
rightArrow.frame = 1;
// creation of the thumbails group
levelThumbsGroup = this.add.group();
// determining level thumbnails width and height for each page
var levelLength = thumbWidth * thumbCols + thumbSpacing * (thumbCols - 1);
var levelHeight = thumbWidth * thumbRows + thumbSpacing * (thumbRows - 1);
// looping through each page
for(var l = 0; l < pages; l++){
// horizontal offset to have level thumbnails horizontally centered in the page
var offsetX = (this.width-levelLength)/ 2 + game.width * l;
// I am not interested in having level thumbnails vertically centered in the page, but
// if you are, simple replace my "20" with
// (game.height-levelHeight)/2
var offsetY = 20;
// looping through each level thumbnails
for(var i = 0; i < thumbRows; i ++){
for(var j = 0; j < thumbCols; j ++){
var levelNumber = i * thumbCols + j + l *(thumbRows * thumbCols);
var levelThumb = this.add.button(offsetX + j * (thumbWidth + thumbSpacing), offsetY + i * (thumbHeight + thumbSpacing), "levels", this.thumbClicked, this);
levelThumb.frame=starsArray[levelNumber];
levelThumb.levelNumber = levelNumber + 1;
levelThumbsGroup.add(levelThumb);
// if the level is playable, also write level number
if(starsArray[levelNumber] < 4){
var style = {
font: "18px Arial",
fill: "#ffffff"
};
var levelText = this.add.text(levelThumb.x + 5, levelThumb.y + 5, levelNumber + 1,style);
levelText.setShadow(2, 2, 'rgba(0,0,0,0.5)', 1);
levelThumbsGroup.add(levelText);
}
}
}
}
},
thumbClicked: function(button){
// the level is playable, then play the level!!
if(button.frame < 4){
alert("playing level " + button.levelNumber);
} else {
var buttonTween = this.add.tween(button)
buttonTween.to({
x: button.x + thumbWidth / 15
}, 20, Phaser.Easing.Cubic.None);
buttonTween.to({
x: button.x - thumbWidth / 15
}, 20, Phaser.Easing.Cubic.None);
buttonTween.to({
x: button.x + thumbWidth / 15
}, 20, Phaser.Easing.Cubic.None);
buttonTween.to({
x: button.x - thumbWidth / 15
}, 20, Phaser.Easing.Cubic.None);
buttonTween.to({
x: button.x
}, 20, Phaser.Easing.Cubic.None);
buttonTween.start();
}
},
arrowClicked: function(button){
if(button.frame == 1 && currentPage < pages - 1){
leftArrow.alpha = 1;
currentPage++;
if(currentPage == pages - 1){
button.alpha = 0.3;
}
var buttonsTween = this.add.tween(levelThumbsGroup);
buttonsTween.to({
x: currentPage * game.width * -1
}, 500, Phaser.Easing.Cubic.None);
buttonsTween.start();
}
if(button.frame==0 && currentPage>0){
rightArrow.alpha = 1;
currentPage--;
if(currentPage == 0){
button.alpha = 0.3;
}
var buttonsTween = game.add.tween(levelThumbsGroup);
buttonsTween.to({
x: currentPage * game.width * -1
}, 400, Phaser.Easing.Cubic.None);
buttonsTween.start();
}
}
};

You're getting the error because pages is defined within your create function but isn't defined anywhere else. pages doesn't exist in your arrowClicked function.
To fix this, make pages a global variable by adding it alongside your var declarations at the top:
...
var leftArrow;
var rightArrow;
var pages;
Then modify your create function to instead reference this variable (by dropping the var keyword):
// array with finished levels and stars collected.
var starsArray = [1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2];
// how many pages are needed to show all levels?
pages = starsArray.length /(thumbRows * thumbCols);

Related

Display Absolute positioned images inside a div

i am trying to absolute position images at random positions inside a div. i'm not sure how to get the calculations right for 'top' and 'left' but the images on occasions display outside of the div. i want to also prevent the overlapping of the images.
Any ideas would help
(function() {
//array of links to the images
var images = ["http://via.placeholder.com/150/800",
"http://via.placeholder.com/150/080",
"http://via.placeholder.com/150/008",
"http://via.placeholder.com/150/880"
];
//function to calculate a random integer
var getRandomInt = function(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
//function to get a top and left value position for each image
var pos = function() {
var wrapWidth = document.getElementById("wrap");
wrapWidth = $("#wrap").width();
wrapHeight = $("#wrap").height();
// Image Position
var xPos = getRandomInt(0, wrapWidth - 150);
var yPos = getRandomInt(0, wrapHeight - 150);
return {
x: xPos + "px",
y: yPos + "px"
}
}
var displayImages = function(images) {
var elementArray = [];
for (var i = 0; i < images.length; i++) {
var src = images[i];
elementArray[i] = '<img class="imagePos" style="top:' + pos().x + '; left:' + pos().y + ' " src="' + src + ' "/>';
}
console.log(elementArray);
elementArray.forEach(function(element) {
console.log(element);
$("#wrap").append(element);
});
}
displayImages(images);
})();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="IntTree">
<div id="header">
<h1>Interactive Tree</h1>
</div>
<div id="wrap">
</div>
</div>
I'm assuming that you have some css resembling this:
img {
position: absolute;
width:150;
height:150;
}
Regarding your first issue, you appear to have your x and y assignments backwards in the bit where you're adding the elems to the array. Also, you're making 2 times as many calls to pos() as needed.
That line should be:
let position = pos();
elementArray[i] = '<img class="imagePos" style="top:'+position.y+'; left:'+position.x+' " src="'+src+' "/>';
For the second issue, you need to check for each image whether any of the corners overlap a different image. The easy way to achieve this by adding an array to track the positions you've already used, and comparing against the items in the array for subsequent position calculations.
(function (){
//array of links to the images
var images = ["http://via.placeholder.com/150/800",
"http://via.placeholder.com/150/080",
"http://via.placeholder.com/150/008",
"http://via.placeholder.com/150/880"
];
//function to calculate a random integer
var getRandomInt = function (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// array to track previous positions of images
var positions = [];
//function to get a top and left value position for each image
var pos = function (){
var wrapWidth = $("#wrap").width();
var wrapHeight = $("#wrap").height();
// Image Position
var xPos = getRandomInt(0, wrapWidth - 150);
var yPos = getRandomInt(0, wrapHeight - 150);
var overlapX = true;
var overlapY = true;
while(overlapX && overlapY) {
overlapX = false;
overlapY = false;
for(var i = 0; i < positions.length; i++) {
// check if x coord is inside previously placed image
if ( (xPos > positions[i].x && xPos < positions[i].x+150) ||
(xPos+150 > positions[i].x && (xPos+150) < positions[i].x+150) ){
overlapX = true;
}
// check if y coord is inside previously placed image
if( (yPos > positions[i].y && yPos < positions[i].y+150) ||
(yPos+150 > positions[i].y && yPos+150 < positions[i].y+150) ) {
overlapY = true;
}
}
if (overlapX) {
xPos = getRandomInt(0, wrapWidth - 150);
}
if (overlapY) {
yPos = getRandomInt(0, wrapHeight - 150);
}
}
positions.push({x:xPos,y:yPos});
return {
x: xPos + "px",
y: yPos + "px"
}
}
var displayImages = function(images){
var elementArray = [];
for (var i = 0; i < images.length; i++) {
var src = images[i];
let position = pos();
elementArray[i] = '<img class="imagePos" style="top:'+position.y+'; left:'+position.x+' " src="'+src+' "/>';
}
elementArray.forEach(function(element) {
$("#wrap").append(element);
});
}
displayImages(images);
})();

Javascript setInterval randomly stops

I'm using setInterval to call a function that animates a fractal on a HTML5 canvas. There is also a slider to allow the user to change the quality of the fractal. Everything works fine until I start changing the slider. When I change it, the fractal animation becomes choppy, and eventually the "drawFractal" function stops being called.
Here is the slider HTML:
<input type="range" id="qualitySlider" min="1" max="10"></input>
Here is the javascript (it just generates a fractal):
var count = 0.5;
var slider = document.getElementById("qualitySlider");
var g = document.getElementById("drawingCanvas").getContext("2d");
function drawFractal() {
var cellSize = Math.ceil(slider.value);
//canvas is 700 by 400
g.fillStyle = "black";
g.clearRect(0, 0, 700, 400);
//Eveything from here to the end of this function generates the fractal
var imagC = Math.cos(count)*0.8;
var realC = Math.sin(count)*0.5;
for (x = 0; x < 700; x+=cellSize) {
for (y = 0; y < 400; y+=cellSize) {
var yCoord = (x / 700.0 - 0.5)*3;
var xCoord = (y / 400.0 - 0.5)*3;
var real = xCoord;
var imag = yCoord;
var broken = 0;
for (i = 0; i < 8; i++) {
var temp = real*real - imag*imag + realC;
imag = 2*imag*real + imagC;
real = temp;
if (real*real + imag*imag >= 4) {
broken = true;
break;
}
}
if (!broken) {
g.fillRect(x, y, cellSize, cellSize);
}
}
}
count = count + 0.04;
}
setInterval(drawFractal, 60);
I just need the "drawFractal" function to be called reliably every 60 milliseconds.
This is my improved code. I just used requestAnimationFrame to recursively call the "drawFractal" function. I also restricted the animation to 24 frames/sec with the setTimeout function.
var count = 0.5;
var qualitySlider = document.getElementById("qualitySlider");
var g = document.getElementById("drawingCanvas").getContext("2d");
function drawFractal() {
var cellSize = Math.ceil(qualitySlider.value);
//canvas is 700 by 400
g.fillStyle = "black";
g.clearRect(0, 0, 700, 400);
var imagC = Math.cos(count)*0.8;
var realC = Math.sin(count)*0.5;
for (x = 0; x < 700; x+=cellSize) {
for (y = 0; y < 400; y+=cellSize) {
var yCoord = (x / 700.0 - 0.5)*3;
var xCoord = (y / 400.0 - 0.5)*3;
var real = xCoord;
var imag = yCoord;
var broken = 0;
for (i = 0; i < 8; i++) {
var temp = real*real - imag*imag + realC;
imag = 2*imag*real + imagC;
real = temp;
if (real*real + imag*imag >= 4) {
broken = true;
break;
}
}
if (!broken) {
g.fillRect(x, y, cellSize, cellSize);
}
}
}
count = count + 0.04;
setTimeout(function() {
requestAnimationFrame(drawFractal);
}, 41);
}
drawFractal();
You are using setInterval() to call drawFractal every 60 ms, and then every time drawFractal is executed, you're calling setInterval() again, which is unnecessary. You now have two timers attempting to draw fractals every 60 ms... then you'll have 4, then 8, etc.
You need to either (1) call setInterval() once at the start of program execution and not call it again, or (2) switch to using setTimeout(), and call it at the end of each drawFractal().
I'd use the second option, just in case your fractal ever takes more than 60 ms to draw.

Canvas redrawing/erasing when window resizes

Im having a few issues resetting my canvas when the window re-sizes or after a given amount of time. I want to complete reset and have it fresh. The problem is if your wait a few minutes (Because the refresh runs) or you re-size your window, everything starts to go haywire. I believe its because its drawing the canvas over top of an existing one and that's whats bleeding through. Any ideas on how to resolve this?
// Constellations
function constellations() {
var pr = (function () {
var ctx = document.createElement("canvas").getContext("2d"),
dpr = window.devicePixelRatio || 1,
bsr = ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio || 1;
return dpr / bsr;
})();
function t() {
this.x = Math.random() * o.width, this.y = Math.random() * o.height, this.vx = -.5 + Math.random(), this.vy = -.5 + Math.random(), this.radius = Math.random()
}
function d() {
for (a.clearRect(0, 0, o.width, o.height), i = 0; i < r.nb; i++) r.array.push(new t), dot = r.array[i], dot.create();
dot.line(), dot.animate()
}
var o = document.querySelector("#constellations");
var ratio = pr;
o.width = $(window).width() * ratio;
o.height = $(window).height() * ratio;
o.style.width = $(window).width() + "px";
o.style.height = $(window).height() + "px";
o.style.display = "block";
var n = "#1A2732";
var linecolor = "#FF535A";
a = o.getContext("2d");
a.setTransform(ratio, 0, 0, ratio, 0, 0);
a.clearRect(0, 0, o.width, o.height);
a.fillStyle = n;
a.lineWidth = .1;
a.strokeStyle = linecolor;
var e = {
x: 30 * o.width / 100,
y: 30 * o.height / 100
},
r = {
nb: o.width / 10,
distance: 80,
d_radius: 150,
array: []
};
t.prototype = {
create: function() {
a.beginPath(), a.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, !1), a.fill()
},
animate: function() {
for (i = 0; i < r.nb; i++) {
var t = r.array[i];
t.y < 0 || t.y > o.height ? (t.vx = t.vx, t.vy = -t.vy) : (t.x < 0 || t.x > o.width) && (t.vx = -t.vx, t.vy = t.vy), t.x += t.vx, t.y += t.vy
}
},
line: function() {
for (i = 0; i < r.nb; i++)
for (j = 0; j < r.nb; j++) i_dot = r.array[i], j_dot = r.array[j], i_dot.x - j_dot.x < r.distance && i_dot.y - j_dot.y < r.distance && i_dot.x - j_dot.x > -r.distance && i_dot.y - j_dot.y > -r.distance && i_dot.x - e.x < r.d_radius && i_dot.y - e.y < r.d_radius && i_dot.x - e.x > -r.d_radius && i_dot.y - e.y > -r.d_radius && (a.beginPath(), a.moveTo(i_dot.x, i_dot.y), a.lineTo(j_dot.x, j_dot.y), a.stroke(), a.closePath())
}
};
var refresh = setInterval(d, 1e3 / 30);
$(window).resize(function() {
window.clearInterval(refresh);
constellations();
});
}constellations();
<canvas id="constellations"></canvas>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
To see it working in action. Just re-size the divider and watch. It may even be a case of reseting the function. Im not sure what should be done to resolve this issue. Its a weird one.
https://jsfiddle.net/v4dqgazr/
The problem is, as the browser is being resized, $(window).resize() is triggered continuously as the resizing is in progress. You can use David Walsh's debounce method to solve this issue.
Here is an update demo with event logs
The debounce method looks like this
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
Just wanted to give some insight on what's happening:
Every instant that the window is being resized, constellations() is being called. Apparently different browsers handle this differently, Dhiraj's answer should help you.
I added code like this, which shows you that many instances are being created and are running:
function constellations(instanceID) {
...
function d() {
console.log("Draw called from instance id: " + instanceID;
...
}
$(window).resize(function() {
window.clearInterval(refresh);
constellations(Math.random());
});
You'll see a lot of log calls being made once you resize.

Creating a class of Crafty JS entity (class of a class?)

I am trying to create a class which creates a Crafty entity with specific properties. So far, the functions within the class do not run because 'this' refers to the window object
$(document).ready(function () {
Crafty.init(window.innerWidth, window.innerHeight);
var player = new controller(37,38,39,40);
player.d.color("red").attr({
w: 50,
h: 50,
x: 0,
y: 0
});
// Jump Height = velocity ^ 2 / gravity * 2
// Terminal Velocity = push * (1 / viscosity)
var gravity = 1;
var viscosity = 0.5;
var frame = (1 / 20);
var distanceMultiplier = 10; //pixels per meter
var timeMultiplier = 20; //relative to actual time
var keystart = [];
var keyboard = [];
function controller (controls) {
this.d = Crafty.e();
this.d.addComponent("2D, Canvas, Color, Collision");
this.d.collision();
this.d.mass = 1;
this.d.a = {
extradistance : 0,
velocity : 0,
acceleration : 0,
force : 0,
resistance : 0
};
this.d.a.push = 0;
this.d.v = {
extradistance : 0,
velocity : 0,
acceleration : 0,
force : 0
};
this.d.jumping = true;
this.d.onHit("Collision", function () {
var a = this.d.hit("Collision");
if (a) {
for (var b in a) {
this.d.x = this.d.x - a[b].normal.x * a[b].overlap;
this.d.y = this.d.y - a[b].normal.y * a[b].overlap;
if (a[b].normal.y < -0.5) {
this.d.jumping = false;
}
if (Math.abs(a[b].normal.x) < 0.2) {
this.d.v.velocity = this.d.v.velocity * a[b].normal.y * 0.2;
}
if (Math.abs(a[b].normal.y) < 0.2) {
this.d.a.velocity = this.d.a.velocity * a[b].normal.x * 0.2;
}
}
return;
}
});
this.d.physics = function () {
if (keyboard[arguments[1]] && !this.jumping) {
this.v.velocity = 5;
this.jumping = true;
}
if (keyboard[arguments[1]] && this.jumping) {
var now = new Date();
if (now.getTime() - keystart[arguments[1]].getTime() < 500) {
this.v.velocity = 5;
}
}
if (keyboard[arguments[0]] && keyboard[arguments[2]]) {
this.a.velocity = 0;
} else {
if (keyboard[arguments[0]]) {
this.a.velocity = -3;
}
if (keyboard[arguments[2]]) {
this.a.velocity = 3;
}
}
if (keyboard[arguments[3]]) {
this.v.velocity = -5;
}
this.a.force = this.a.push - this.a.resistance;
this.a.acceleration = this.a.force / this.mass;
this.a.velocity = this.a.velocity + (this.a.acceleration * frame);
this.a.extradistance = (this.a.velocity * frame);
this.a.resistance = this.a.velocity * viscosity;
this.attr({
x: (this.x + (this.a.extradistance * distanceMultiplier))
});
this.v.force = gravity * this.mass;
this.v.acceleration = this.v.force / this.mass;
this.v.velocity = this.v.velocity - (this.v.acceleration * frame);
this.v.extradistance = (this.v.velocity * frame);
this.attr({
y: (this.y - (this.v.extradistance * distanceMultiplier))
});
setTimeout(this.physics, (frame * 1000) / timeMultiplier);
};
this.d.listen = function(){ document.body.addEventListener("keydown", function (code) {
var then = new Date();
if (!keyboard[code.keyCode] && !this.jumping && code.keyCode == arguments[1]) { //only if not yet pressed it will ignore everything until keyup
keyboard[code.keyCode] = true; //start movement
keystart[code.keyCode] = then; //set time
}
if (!keyboard[code.keyCode] && code.keyCode != arguments[1]) { //only if not yet pressed it will ignore everything until keyup
keyboard[code.keyCode] = true; //start movement
keystart[code.keyCode] = then; //set time
}
});
};
}
player.d.physics();
player.d.listen();
document.body.addEventListener("keyup", function (code) {
keyboard[code.keyCode] = false;
});
});
In trying to put the functions as prototypes of the class, I run into a problem.
Crafty.init(500,500);
function block () {
block.d = Crafty.e("2D, Color, Canvas");
block.d.color("red");
block.d.attr({x:0,y:0,h:50,w:50});
}
block.d.prototype.green = function() {
this.color("green");
}
var block1 = new block();
block1.d.color();
If an object is defined in the constructor, I cannot use it to add a prototype to.
Generally in Crafty, we favor composition. That is, you extend an entity by adding more components to it. You can have kind of a hierarchy by having one component automatically add others during init.
I haven't looked through all of your example code, because there's a lot! But consider the second block:
function block () {
block.d = Crafty.e("2D, Color, Canvas");
block.d.color("red");
block.d.attr({x:0,y:0,h:50,w:50});
}
block.d.prototype.green = function() {
this.color("green");
}
var block1 = new block();
block1.d.color();
You're trying to combine Crafty's way of doing things (an entity component system) with classes in a way that's not very idiomatic. Better to do this:
// Define a new component with Crafty.c(), rather than creating a class
Crafty.c("Block", {
// On init, add the correct components and setup the color and dimensions
init: function() {
this.requires("2D, Color, Canvas")
.color("red")
.attr({x:0,y:0,h:50,w:50});
},
// method for changing color
green: function() {
this.color("green");
}
});
// Create an entity with Crafty.e()
block1 = Crafty.e("Block");
// It's not easy being green!
block1.green();

Using Raphael.setViewBox zoom/pan to show entire contents of set / paper

My objective is to create some code that will show (no animation required) a set of objects as large as possible, in the center of the paper.
My current attempt gets the bounding box of a set of objects or paper and returns dimensions which is then used by setViewBox. Unfortunately it just doesn't work and I have no idea why.
Here is my code
function GetContainingViewBoxFor(object)
{
var maxValues = { x: 0, y: 0 };
var minValues = { x: 0, y: 0 };
//Find max and min points
object.forEach(function (el) {
if (el.data("background")) return;
var bbox = el.getBBox();
if (bbox.y < minValues.y) minValues.y = bbox.y;
if (bbox.y2 < minValues.y) minValues.y = bbox.y2;
if (bbox.y > maxValues.y) maxValues.y = bbox.y;
if (bbox.y2 > maxValues.y) maxValues.y = bbox.y2;
if (bbox.x < minValues.x) minValues.x = bbox.x;
if (bbox.x2 < minValues.x) minValues.x = bbox.x2;
if (bbox.x > maxValues.x) maxValues.x = bbox.x;
if (bbox.x2 > maxValues.x) maxValues.x = bbox.x2;
});
//Padding
var padding = 50;
maxValues.x += padding;
minValues.x -= padding;
maxValues.y += padding;
minValues.y -= padding;
var w = maxValues.x - minValues.x;
var h = maxValues.y - minValues.y;
var cviewBox = {X:minValues.x - paper.width,Y:minValues.y,width:w,height:h};
return cviewBox;
}
And it is used like so:
var containingViewBox = GetContainingViewBoxFor(paper);
paper.setViewBox(containingViewBox.X, containingViewBox.Y, containingViewBox.width, containingViewBox.height, false);

Categories