I'm currently working on a game where I need a bunch of random bubbles to fall, and a rectangle below it that can move to "burst" these bubbles. I have a code currently that is not dropping these bubbles. Can someone tell me where I am going wrong? Here is my code:
var canvasColor;
var x,y,radius,color;
var x=50, y=30;
var bubbles= [];
var counter;
var lastBubble=0;
var steps=0, burst=0, escaped=0;
var xx=200;
var moveRectangleRight=false;
var moveRectangleLeft=false;
function startGame()
{
var r, g, b;
canvasColor= '#f1f1f1';
x=10;
y=10;
radius=10;
clearScreen();
counter=0;
while (counter<100)
{
x= Math.floor(450*Math.random()+1);
r = Math.floor(Math.random()*256);
g = Math.floor(Math.random()*256);
b = Math.floor(Math.random()*256);
color='rgb('+r+','+g+','+b+')';
bubbles[counter]=new Bubble(x,y,radius,color);
counter+=1;
}
setInterval('drawEverything()',50);
}
function Bubble(x,y,radius,color)
{
this.x=x;
this.y=y;
this.radius=radius;
this.color=color;
this.active=false;
}
function drawEverything()
{
var canvas=document.getElementById('myCanvas');
var context= canvas.getContext('2d');
steps+=1;
clearScreen();
if (steps%20==0 && lastBubble <100) {
bubbles[lastBubble].active=true;
lastBubble+=1;
}
drawRectangle();
counter=0;
while(counter<100)
{
if (bubbles[counter].active==true) {
context.fillStyle= bubbles[counter].color;
context.beginPath();
context.arc(bubbles[counter].x, bubbles[counter].y,
bubbles[counter.radius], 0, 2*Math.PI);
context.fill();
bubbles[counter].y+=2;
}
y=bubbles[counter].y;
x=bubbles[counter].x;
if (y>=240 && y<=270 && x>=xx-10 && x<=xx+60)
{
bubbles[counter]=false;
}
else if (y>=450)
{
bubbles[counter]=false;
}
counter+=1;
}
}
function clearScreen ()
{
var canvas=document.getElementById('myCanvas');
var context= canvas.getContext('2d');
context.fillStyle= canvasColor;
context.fillRect(0,0,450,300);
}
function drawRectangle()
{ var canvas, context;
if (moveRectangleRight==true && xx<400){
xx+=20;
}
if (moveRectangleLeft==true && xx>0){
xx-=20;
}
canvas=document.getElementById('myCanvas');
context= canvas.getContext('2d');
context.fillStyle = 'blue';
context.fillRect(xx,250,50,10);
}
function moveLeft () {
moveRectangleLeft=true;
}
function moveRight() {
moveRectangleRight=true;
}
function stopMove () {
moveRectangleRight=false;
moveRectangleLeft=false;
}
Your problem is that you initialize counter as 1, so when you add items to counter, you begin with the index of 1, which is actually the 2nd item. Thus, when you try to access bubbles[0], it returns undefined. Instead, initialize counter as 0.
A bracket was located in the wrong place, and this solved the problem.
Related
{
var selectEl = document.getElementById("shapes");
var selectedIndex = selectEl.selectedIndex;
var selectedShape = selectEl[selectedIndex].value;
var context = null;
var canvasObj = document.getElementById("myCanvas");
//check if canvas api is supported
if (canvasObj.getContext)
context = canvasObj.getContext("2d");
else
alert("Canvas API not supported");
if(selectedShape === "square")
{
//context.clearRect(0,0,canvasObj.width,canvasObj.height);
for(var i = 0; i<20; i++)
changeBackgroundColor(canvasObj, context);
writeToCanvas(canvasObj, context);
drawSquare(canvasObj,context);
}
else if(selectedShape === "circle")
{
//context.clearRect(0,0,canvasObj.width,canvasObj.height);
for(var i = 0; i<20; i++)
changeBackgroundColor(canvasObj, context);
writeToCanvas(canvasObj, context);
drawCircle(canvasObj,context);
}
}
Here is the code for my getShape function and stuff.
Now here is the code for my drawSquare function:
{
//if I want a random number between 0 and 50 random()*(max-min-1)+min
var xCoord = Math.floor(Math.random()* theCanvas.width); //returns a number between 0 and just under 1
//alert(xCoord);
var yCoord = Math.floor(Math.random()* theCanvas.height);
//alert(yCoord);
ctx.beginPath();
ctx.fillStyle = "#00ff00";
ctx.fillRect(xCoord,yCoord,10,10);
ctx.stroke();
}
Anyone here know why my canvas only draws 1 of its shape? I have for a loop. It worked before, but now it doesn't.
I wrote a program in JavaScript that displays 5 rectangles, one after each other with a ~1s delay.
As there is no static variable in Javascript as in C or other languages, I used an IIEF that returns a function in a variable draw to have an internal counter that is visible only in the function.
This code works perfectly.
let myCanvas=document.getElementById("my-canvas");
let draw=(function() {
let ctx = myCanvas.getContext('2d');
let counter=0;
return function() {
if (counter<5) {
ctx.fillRect(25+counter*20, 25, 10, 100);
counter++;
setTimeout(() => {window.requestAnimationFrame(draw)},1000);
}
}
})();
draw();
But now, I would like to fill the rectangle with a texture instead of the black color. Something like that :
let myPatternImg=new Image();
myPatternImg.onload = function() {
let myPattern=ctx.createPattern(myPatternImg,'repeat');
context.fillStyle=pattern;
... // code to draw rectangle
}
myPatternImg.src='pattern-file.png';
I do not how to do because when I initialize draw a function is returned directly. And this does not work with an onload event. I do not want a global variable for the counter. That is why I use an IIEF that returns a function to init the variable draw.
Any help would be appreciated. Thanks.
You can try use promises, to hide pattern creation:
function makePattern(ctx, src) {
return new Promise(function(resolve) {
let myPatternImg = new Image();
myPatternImg.onload = function() {
let myPattern = ctx.createPattern(myPatternImg, 'repeat');
resolve(myPattern);
};
myPatternImg.src = src;
});
}
let myCanvas=document.getElementById("my-canvas");
let draw = (function() {
let ctx = myCanvas.getContext('2d');
let counter = 0;
return function() {
makePattern(ctx, 'pattern-file.png').then(function(pattern) {
ctx.fillStyle = pattern;
if (counter < 5) {
ctx.fillRect(25+counter*20, 25, 10, 100);
counter++;
setTimeout(() => {window.requestAnimationFrame(draw)},1000);
}
});
}
})();
it's always good idea the separate your code into functions that do one thing.
But this code make little of sens, you are executing in a loop draw and each will draw 5 rectangles, I think that what you really want is something like this:
let draw = (function() {
let ctx = myCanvas.getContext('2d');
let counter = 0;
return function() {
makePattern(ctx, 'pattern-file.png').then(function(pattern) {
ctx.fillStyle = pattern;
(function loop() {
if (counter < 5) {
ctx.fillRect(25+counter*20, 25, 10, 100);
counter++;
setTimeout(() => {
window.requestAnimationFrame(loop);
}, 1000);
}
})();
});
}
})();
also this will fire each rectangle after 1000 seconds, if you want delay between each rectangle to be 1 second then use this:
let draw = (function() {
let ctx = myCanvas.getContext('2d');
let counter = 0;
return function() {
makePattern(ctx, 'pattern-file.png').then(function(pattern) {
ctx.fillStyle = pattern;
(function loop() {
if (counter < 5) {
ctx.fillRect(25+counter*20, 25, 10, 100);
counter++;
setTimeout(loop, 1000);
}
})();
});
}
})();
if you call draw multiple times it will create pattern each time, you can make it request only once if you use this code:
let draw = (function() {
let ctx = myCanvas.getContext('2d');
let counter = 0;
return function(pattern) {
ctx.fillStyle = pattern;
(function loop() {
if (counter < 5) {
ctx.fillRect(25+counter*20, 25, 10, 100);
counter++;
setTimeout(loop, 1000);
}
})();
}
})();
makePattern(ctx, 'pattern-file.png').then(draw);
I have it so that when you (the PaintBrush) hit the finish everything clears and a button appears. When the button is clicked it starts level two, here it creates a new canvas. I've added some code so that when the button is clicked the old canvas is deleted, then the new one is made. However, this requires the canvas to have an id. how do i get this code:
canvas: document.createElement("canvas"),
To also include an id for it? I cannot have it say var canvas = blah blah and then have
canvas.id = "canvas";
because of the way i have it set up.
P.S the remove code is this if you need to know:
Element.prototype.remove = function() {
this.parentElement.removeChild(this);
}
NodeList.prototype.remove = HTMLCollection.prototype.remove = function() {
for(var i = this.length - 1; i >= 0; i--) {
if(this[i] && this[i].parentElement) {
this[i].parentElement.removeChild(this[i]);
}
}
}
and in the button "click" function area i put this for the remove:
document.getElementById("canvas").remove();
Please help! Thanks in Advance!
EDIT: Here is some extra code to help, it is what occurs when the PaintBrush hits the finish:
else if (PaintBrush.crashWith(Finish)) {
PaintBrush.y = 50;
var button = document.createElement("button");
button.innerHTML = "Level 3";
button.id = "button-2"; // add the id to the button
document.body.appendChild(button); // append to body
GameArena2.clear();
GameArena2.stop();
button.addEventListener ("click", function() {
startGame2();
document.getElement("canvas").remove();
document.getElementById("button-2").remove();
});
EDIT 2: Game canvas code:
var GameArena2 = {
canvas: document.createElement("canvas"),
start: function() {
this.canvas.width = 1280;
this.canvas.height = 480;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.frameNo = 0;
this.interval = setInterval(updateGameArea2, 20);
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
},
stop: function() {
clearInterval(this.interval);
},
drawGameOver: function() {
ctx2 = GameArena2.context;
ctx2.fillStyle = "rgba(0,0,0,"+fader +")";
ctx2.fillRect(0,0,GameArena2.width,GameArena2.height);
drawStatic(fader/2);
fader += .1 * 1/fps
ctx2.fillStyle = "rgba(255,255,255," + fader + ")";
ctx2.font = "72px sans-serif";
ctx2.fillText("GAME OVER",GameArena2.width/2 - 220,GameArena2.height/2);
}
}
function everyinterval(n) {
if ((GameArena.frameNo / n) % 1 == 0) {
return true;
}
else if ((GameArena.frameNo / n) % 1 == 0) {
return true;
}
return false;
}
I think the best way is to create a static function like this:
function createElementOnDom (type,id) {
var element=document.createElement(type);
element.id=id;
return element;
}
console.log(createElementOnDom('CANVAS','myId'));
So you can simply create the canvas adding it his specific id using a simple single row function call like you need.
I only know a way to do direcly this on jQuery, that you wouldn't like to use.
simply you need to do that:
function createElementOnDom (type,id) {
var element=document.createElement(type);
element.id=id;
return element;
}
var GameArena2 = {
canvas: createElementOnDom('CANVAS','myId'),
Simple! Just assign it immediately after the GameArena2 object is created.
var GameArena2 = {
canvas: document.createElement("canvas"),
start: function() {
...
...
}
// give id to a canvas
GameArena2.canvas.id = "GameArena2";
function everyinterval(n) {
if ((GameArena.frameNo / n) % 1 == 0) {
...
...
}
I have this code working, but I'd like to improve it:
var c = canvas.getContext("2d");
//this is called as an object method I created
var animar = function () {
var obj = this;//saves the object that called it to later access to its properties
var counter= 0;
var animacion = setInterval(function(){
c.save()
c.clearRect(0, 0, canvas.width, canvas.height);
c.rotate(0.1*counter);
obj.pintar();
c.restore();
counter++;
}, 50);
}
I'd like to use a outter function for posible future use but when I change the code I have, there is a hoisting problem and I don't know how to get the counter inside the rotar() function without overwriting it all the time:
var animar = function () {
var obj = this;
var counter= 0;
var animacion = setInterval(function(){
rotar(obj)
}, 50);
}
function rotar (obj) {
c.save()
c.clearRect(0, 0, canvas.width, canvas.height);
c.rotate(0.1*counter);
obj.pintar();
c.restore();
counter++;
}
I get that the first one works because they are nested, while in the second example isn't. How can I have a counter for the setInterval WITHOUT USING A GLOBAL VARIABLE?
(I mean... if I call it a second time, it will not start in 0. If I set it to be 0 in the animar() function, it would work, but then I'd need to set it to 0 in every function that uses a counter or using counters with diferent names. This two posibilities don't sound good.)
Thanks
You just need to put var counter = 0 outside the functions:
var counter;
var animar = function () {
var obj = this;
counter = 0; // reset every time before 'animacion'...
var animacion = setInterval(function(){
rotar(obj)
}, 50);
}
function rotar (obj) {
c.save()
c.clearRect(0, 0, canvas.width, canvas.height);
c.rotate(0.1*contador);
obj.pintar();
c.restore();
counter++;
}
Alternatively, if you don't want a global variable, you can follow Walter's answer, or do this:
var animar = function () {
var obj = this;
var counter = 0; // new variable every time animar() is called
var animacion = setInterval(function(){
rotar(obj, counter);
counter++;
}, 50);
}
function rotar (obj, counter) {
c.save()
c.clearRect(0, 0, canvas.width, canvas.height);
c.rotate(0.1*contador);
obj.pintar();
c.restore();
}
make counter scoped inside of your animar object:
var animar = function () {
var obj = this;
obj.counter= 0;
var animacion = setInterval(function(){
rotar(obj)
}, 50);
}
function rotar (obj) {
c.save()
c.clearRect(0, 0, canvas.width, canvas.height);
c.rotate(0.1*obj.counter);
obj.pintar();
c.restore();
obj.counter++;
}
html:
<div id="container">
<canvas id="canvas" width="1024" height="1024"></canvas>
</div>
<button id = "click" >fire</button>
<script type="text/javascript" src = "Jquery/easy.js"></script>
javascript:
var ctx = canvas.getContext('2d');
var chop1 = new Image();
chop1.src = "img/chopper.png";
var blt = new Image();
blt.src = "img/bullet.png"
var chopperX = 0;
var chopperY = 0;
var ascent = 2;
var limit = 500;
var start = null;
var bltX = 135;
var hit = document.getElementById("click");
window.onclick = function()
{
fly()
}
hit.onclick = function()
{
fire();
}
function fire()
{
bltX +=ascent;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(blt,bltX,30,
chop1.width, chop1.height);
requestAnimationFrame(fire);
}
function up()
{
chopperY-=ascent;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(chop1,0,chopperY ,
chop1.width, chop1.height);
requestAnimationFrame(up);
if(chopperY == 0){
fly();
}
}
function fly() {
chopperY+=ascent;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(chop1,0,chopperY ,
chop1.width, chop1.height);
if (chopperY < limit) {
requestAnimationFrame(fly);
}
if(chopperY==limit){
up();
}
}
When i click on the fire button it doesn't call the "fire" method and calls the "fly" function. i want to know why "fly" method get called instead of "fire" method?
Is there any mistake in this code? what is the solution of it?
You have two event handlers
window.onclick = function() {
fly()
}
hit.onclick = function() {
fire();
}
which by the way could be written
window.onclick = fly;
etc. But the click bubbles to the window level when you click any element inside the window, even hit, so the fly() function is called as well.
You could try stopping the propagation
hit.onclick = function(event) {
event.stopPropagation();
fire();
}
There already is a solution for your problem:
see here
What you want to do is:
document.getElementById("click").onclick = fire;