html5 canvas code blanking out - javascript

I am in desperate need to fix a problem I have in html5 canvas. I am making an educational game, but after adding another photo and making the necessary code changes, but the canvas blanked out. I reverted the changes, but the problem persisted. I spent the past week or so trying to fix it, but I've run out of ideas :( Here is the relevent code (if anyone needs another part, just say so):
// Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 1041;
canvas.height = 550;
document.body.appendChild(canvas);
// Background image
var bgReady = false;
var bgImage = new Image();
bgImage.onload = function () {
bgReady = true;
};
bgImage.src = "images/introbackground.png";
//.... rest of the photos load like this so I won't put it...
// Level 1 background 1 image
var bbg1Ready = false;
var bbg1Image = new Image();
bbg1Image.onload = function () {
bbg1Ready = false; //Exception since it shouldn't load at the beginning
};
bbg1Image.src = "images/pollutedbeach1.png";
// Game objects
var hero = {
speed: 200 // movement in pixels per second
};
var level1;
var level2;
var biolevel = false;
//...Code to make the hero picture move with arrow keys and to reset the game are skipped...
if (hero.x <= (level1.x + 345)
&& level1.x <= (hero.x + 32)
&& hero.y <= (level1.y +50)
&& level1.y <= (hero.y + 32)){
biolevel = true;
return biolevel;
}
var render = function () {
if (bgReady) {
ctx.drawImage(bgImage, 0, 0);
}
//...The same for all photos previously loaded...
if (biolevel == true){
level1Ready = false;
level2Ready = false;
bbg1Ready = true;
hero.speed = 125;
}
};
//...Then the main game loop to animate it...
Sorry It's kind of long, but I really need a solution. Thanks!

Related

How to add a 16x16 grid of pseudo-random tiles as a background in canvas (or other web language)

I have recently been working on a web based project using canvas on HTML5. The program consists of a 16x16 grid of tiles that have been pseudo-randomly generated. I am relatively new to canvas, but have built this program in several other environments, none of which however compile successfully to a web based language. this is the main code section that is giving me bother:
var A = 8765432352450986;
var B = 8765432352450986;
var M = 2576436549074795;
var X = 1;
var rx = 0;
var ry = 0;
this.image = new Image();
var i = 0;
var ii = 0;
while(i < 16)
{
while(ii < 16)
{
this.image = new Image();
this.image.src = "textures/grass.png";
x = (((A*X)+B)%M)%M;
if((x/2)%1 == 0)
{
this.image.src = "textures/grass.png";
}
if((x/8)%1 == 0)
{
this.image.src = "textures/hill.png";
}
if((x/21)%1 == 0)
{
this.image.src = "textures/trees.png";
}
if((x/24)%1 == 0)
{
this.image.src = "textures/sea.png";
}
if((x/55)%1 == 0)
{
this.image.src = "textures/mountain.png";
}
if((x/78)%1 == 0)
{
this.image.src = "textures/lake.png";
}
if((x/521)%1 == 0)
{
this.image.src = "textures/volcano.png";
}
if((x/1700)%1 == 0)
{
this.image.src = "textures/shrine.png";
}
if((x/1890)%1 == 0)
{
this.image.src = "textures/outpost.png";
}
if((x/1999)%1 == 0)
{
this.image.src = "textures/civ.png";
}
ctx = myGameArea.context;
ctx.drawImage(this.image,rx, ry, 20, 20);
ii ++;
rx += 20;
}
i ++;
rx = 0;
ry += 16;
}
I would like canvas to draw along the lines of this code above, effectively generating a grid like this
pre generated grid image
(please try and ignore the obvious bad tile drawings, I planned on either finding an artist or trying slightly harder on them when I get the game fully working.)
The black square is a separate movable object. I haven't got as far as implementing it in this version, but if you have any suggestions for it please tell me
in the full html file I have now, the canvas renders but none of the background (using the w3schools tutorials, I can make objects render however)
In short: how do I render a background consisting of a 16x16 grid of pseudo-random tiles on an event triggered or on page loaded, using canvas or if that does not work another web based technology
Thank you for your time.
A few problems but the main one is that you need to give an image some time to load before you can draw it to the canvas.
var image = new Image();
image.src = "image.png";
// at this line the image may or may not have loaded.
// If not loaded you can not draw it
To ensure an image has loaded you can add a onload event handler to the image
var image = new Image();
image.src = "image.png";
image.onload = function(){ ctx.drawImage(image,0,0); }
The onload function will be called after all the current code has run.
To load many images you want to know when all have loaded. One way to do this is to count the number of images you are loading, and then use the onload to count the number of images that have loaded. When the loaded count is the same as the loading count you know all have loaded and can then call a function to draw what you want with the images.
// Array of image names
const imageNames = "grass,hill,trees,sea,mountain,lake,volcano,shrine,outpost,civ".split(",");
const images = []; // array of images
const namedImages = {}; // object with named images
// counts of loaded and waiting toload images
var loadedCount = 0;
var imageCount = 0;
// tile sizes
const tileWidth = 20;
const tileHeight = 20;
// NOT SURE WHERE YOU GOT THIS FROM so have left it as you had in your code
// Would normally be from a canvas element via canvasElement.getContext("2d")
var ctx = myGameArea.context;
// seeded random function encapsulated in a singleton
// You can set the seed by passing it as an argument rand(seed) or
// just get the next random by not passing the argument. rand()
const rand = (function(){
const A = 8765432352450986;
const B = 8765432352450986; // This value should not be the same as A?? left as is so you get the same values
const M = 2576436549074795;
var seed = 1;
return (x = seed) => seed = ((A * x) + B) % M;
}());
// function loads an image with name
function addImage(name){
const image = new Image;
image.src = "textures/" + name + ".png";
image.onload = () => {
loadedCount += 1;
if(loadedCount === imageCount){
if(typeof allImagesLoaded === "function"){
allImagesLoaded();
}
}
}
imageCount += 1;
images.push(image);
namedImages[name] = image;
}
imageNames.forEach(addImage); // start loading all the images
// This function draws the tiles
function allImagesLoaded(){ /// function that is called when all the images have been loaded
var i, x, y, image;
for(i = 0; i < 256; i += 1){ // loop 16 by 16 times
ctx.drawImage(
images[Math.floor(rand()) % images.length]; //random function does not guarantee an integer so must floor
(i % 16) * tileWidth, // x position
Math.floor(i / 16) * tileHeight, // y position
tileWidth, tileHeight // width and height
);
}
}

How to avoid flickering images with keypress?

I have a piece of code where a user has to press 0 to mute the sound or 1 to play the sound of a music piece. If the sound is muted, the image changes and otherwise when the music plays.
var audio = new Audio();
window.onload = function geluidMaken() {
audio.src = "../Geluiden/Achtergrond_Geluid.mp3";
audio.play();
audio.loop = true;
window.addEventListener("keypress", function geluidsKnoppen() {
var codeGeluid = event.which || event.keyCode;
if(codeGeluid == 48) {
var geluidAanKnop = new Image();
geluidAanKnop.src = "../Afbeeldingen/Sound_Button.png";
mijnObject.drawImage(geluidAanKnop, (canvas.width/2)-25, 850, geluidAanKnop.width, geluidAanKnop.height);
audio.muted = true;
}
else if(codeGeluid = 49) {
var geluidAfKnop = new Image();
geluidAfKnop.src = "../Afbeeldingen/Mute_Button.png";
mijnObject.drawImage(geluidAfKnop, (canvas.width/2)-25, 850, geluidAfKnop.width, geluidAfKnop.height);
audio.muted = false;
}
});
}
I have also a other piece of code where I render all my other functions
function tekenenObjecten() {
mijnObject.clearRect(0, 0, canvas.width, canvas.height);
makenBalkKort();
makenBal();
makenMuur();
makenBord();
The images aren't visible on the canvas but when I click on the keys 0 or 1 the images appears and dissapears directly. How to make sure the images stay on the canvas and change when the 0 or 1 is pressed?
When working with a canvas in html5, remember that when you redraw a frame, you remove everything that is on the canvas, then draw everything you want to see in the new frame.
In your keypress handler, you draw the image once. When a new frame is drawn in the other part of your code, the canvas is cleared, and you never redraw it.
What you want to do is probably have a placeholder that is always drawn, then only change it source and let the main loop take care of it.
var audioKnop = new Image();
var audioAanSource = "../Afbeeldingen/Sound_Button.png";
var audioMuteSource = "../Afbeeldingen/Mute_Button.png";
function init() {
audioKnop.src = audioMuteSource;
}
function tekenenObjecten() {
mijnObject.clearRect(0, 0, canvas.width, canvas.height);
makenBalkKort();
makenBal();
makenMuur();
makenBord();
maakAudioButton();
}
function maakAudioButton() {
mijnObject.drawImage(audioKnop, (canvas.width/2)-25, 850, audioKnop.width, audioKnop.height);
}
window.addEventListener("keypress", function () {
var codeGeluid = event.which || event.keyCode;
if(codeGeluid == 48) {
audioKnop.src = audioAanSource;
audio.muted = true;
} else if(codeGeluid == 49) {
audioKnop.src = audioMuteSource;
audio.muted = false;
}
});

ActivePointer location not updating

I'm making a sidescroller using Phaser (latest version) and I want the player's projectiles to go towards the pointer when the player clicks, like they do in this example http://phaser.io/examples/v2/games/tanks. I've used some of the code from the example but in my game, the activePointer x and y co-ordinates seem to only initialise when the game starts and never change. So when the player shoots, it's always going towards the same co-ordinates.
I have the following code (note I have removed bits about item collection, enemies etc. for posting on here):
var SideScroller = SideScroller || {};
var startPosX = 100;
var startPosY = 300;
var shooter;
var playerBullets;
var nextFire = 0;
var fireRate = 100;
var cursors;
var currentLoc;
SideScroller.Game = function () {};
SideScroller.Game.prototype = {
create: function () {
//create player
//params = (game, startPositionX,startPositionY, key, frame)
this.player = this.game.add.sprite(startPosX, startPosY, 'player');
//get canvas width and height for later use
canvasWidth = this.game.canvas.width;
canvasHeight = this.game.canvas.height;
//create enemy
var x = this.game.rnd.between(80, this.game.world.width);
var y = this.game.rnd.between(0, 113);
// Point to shoot projectiles from
// allows rotation, if this had been done on the player object, the graphic would have rotated, which we don't want
this.shooter = this.game.add.sprite(startPosX, startPosY, 'blank');
this.shooter.anchor.setTo(0.5, 0.5);
//make a group of player projectiles
playerBullets = this.game.add.group();
playerBullets.enableBody = true;
playerBullets.physicsBodyType = Phaser.Physics.ARCADE;
playerBullets.createMultiple(1000, 'peePower');
playerBullets.setAll('anchor.x', 0.5);
playerBullets.setAll('anchor.y', 0.5);
playerBullets.setAll('outOfBoundsKill', true);
playerBullets.setAll('checkWorldBounds', true);
//enable physics on the player
this.game.physics.arcade.enable(this.player);
//bring player shooting point to the top (not totally necessary)
this.shooter.bringToTop();
//player gravity
this.player.body.gravity.y = gravity;
//player collides with all four edges of the game world
this.player.body.collideWorldBounds = true;
this.player.anchor.setTo(0.5, 0.5);
//the camera will follow the player in the world
this.game.camera.follow(this.player);
//move player with cursor keys
cursors = this.game.input.keyboard.createCursorKeys();
},
update: function () {
currentLoc = this.game.input.activePointer;
//collision between player and platforms
this.game.physics.arcade.collide(this.player, this.blockedLayer, null, null, this);
//make co-ordinates match
this.shooter.x = this.player.x;
this.shooter.y = this.player.y;
//this.shooter's angle towards
this.shooter.rotation = this.game.physics.arcade.angleToPointer(this.shooter, this.game.input.activePointer);
//only respond to keys if the player is alive
if (this.player.alive) {
this.player.body.velocity.x = 0;
if (this.game.input.activePointer.isDown) {
console.log("pointer is down");
this.fire();
}
else if (cursors.right.isDown) {
this.playerForward();
}
else if (cursors.left.isDown) {
this.playerBack();
}
else if (cursors.up.isDown) {
this.playerJump();
}
else if (cursors.down.isDown) {
this.fire();
this.playerDuck();
}
}
},
fire: function () {
//for debugging
console.log("fire was called");
console.log(this.game.input.activePointer.x);
console.log(this.game.input.activePointer.y);
if (this.game.time.now > nextFire && playerBullets.countDead() > 0)
{
nextFire = this.game.time.now + fireRate;
var bullet = playerBullets.getFirstExists(false);
bullet.reset(this.shooter.x, this.shooter.y);
currentLoc = this.game.input.activePointer;
bullet.rotation = this.game.physics.arcade.moveToPointer(bullet, 1000, currentLoc, 1000);
console.log(this.game.input.activePointer);
}
},
playerForward: function () {
this.player.loadTexture('player');
this.player.body.setSize(this.player.standDimensions.width, this.player.standDimensions.height);
this.player.body.velocity.x = 700;
this.player.isMoving = true;
//console.log("Forward height:" + this.player.standDimensions.height);
//console.log("Forward width:" + this.player.standDimensions.width);
},
playerBack: function () {
this.player.loadTexture('playerBack');
this.player.body.velocity.x -= 700;
this.player.isMoving = true;
},
playerJump: function () {
if (this.player.body.blocked.down) {
this.player.body.velocity.y -= 700;
this.player.loadTexture('playerJump');
//console.log("Jump height:" + this.player.jumpDimensions.height);
//console.log("Jump width:" + this.player.jumpDimensions.width);
}
},
playerDuck: function () {
//change image and update the body size for the physics engine
this.player.loadTexture('playerDuck');
this.player.body.setSize(this.player.duckedDimensions.width, this.player.duckedDimensions.height);
//keep track of whether player is ducked or not
this.player.isDucked = true;
},
playerDead: function () {
//set to dead (this doesn't affect rendering)
this.player.alive = false;
//stop moving to the right
this.player.body.velocity.x = 0;
//change sprite image
this.player.loadTexture('playerDead');
},
};
Shooter is a blank sprite on top of the player (much like the turret in the tank example) to allow for rotation without the player rotating (please let me know also if there's a better way to do that!).
I tried updating the currentLoc variable in the update method to the activePointer location but that didn't work.
In addition, this condition has never been hit:
if (this.game.input.activePointer.isDown) {
console.log("pointer is down");
this.fire();
}
So something must be going awry with detecting mouse clicks and I don't know if that's part of the problem?
I think you should look it up in the API. There are few points in your code that are questionable.
http://phaser.io/docs/2.3.0/Phaser.Pointer.html
http://phaser.io/docs/2.3.0/Phaser.Physics.Arcade.html#moveToPointer
The point is that you are actually giving the reference to the pointer (to currentLoc) but not the position. So it should always fire to 0;0.
And for the isDown detection, have you done it in the update function or somewhere else?
Hope i could help!

Drawing application undo button

i have problem with undo button for my drawing application
<input id="undo" type="image" src="images/undo.ico" onclick="cUndo()" width="25" height="25">
var cPushArray = new Array();
var cStep = -1;
var ctx;
// ctx = document.getElementById('myCanvas').getContext("2d");
function cPush() {
cStep++;
if (cStep < cPushArray.length) { cPushArray.length = cStep; }
cPushArray.push(document.getElementById('myCanvas').toDataURL());
}
function cUndo() {
if (cStep > 0) {
cStep--;
var canvasPic = new Image();
canvasPic.src = cPushArray[cStep];
canvasPic.onload = function () { ctx.drawImage(canvasPic, 0, 0); }
}
}
But this doesn't work.Please help
First remark : As #markE underlines, saving with DataURL has a high memory cost. You might consider saving the draw commands + their arguments within an array instead.
Seek for tuts/Stack Overflow post on the topic, out of a few posts you should get some nice ideas.
Anyway, you can go with the dataURL solution in a first time to get your application working (with a limit of 20 undos or like to avoid memory explosion), then you can later improve the undo to reach a higher limit.
I updated my code to handle such a stack limit.
For your issue : onload should be hooked prior to setting the src, but anyway with a DataURL you are not async : the image is built at once, so no need to hook unload.
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext("2d");
var historic = [];
var maxHistoricLength = 20; // might be more or less, depending on canvas size...
function saveForUndo() {
historic.push(canvas.toDataURL());
if (historic.length === maxHistoricLength +1) historic.shift();
}
function canUndo() {
return (historic.length !== 0 );
}
function undo() {
if (!canUndo()) return;
var lastDataURL = historic.pop();
var tmpImage = new Image();
tmpImage.src = lastDataURL;
ctx.drawImage(tmpImage, 0, 0);
}

Animating canvas with a javascript constructor

Hello stackoverflow community!
First I must say that I dont have much experience with constructors.
So. What I am trying to do, is to animate a parachutist to fly from top to bottom of the screen.
I thought I could use a constructor to set up a parachutist:
var parachute = function() {
this.height = 35;
this.width = 30;
this.speed = 50;
this.xPos = Math.round(Math.random() * (window.width - this.width));
this.animate = function() {
this.img = new Image();
this.yPos = 0;
this.img.onload = function() {
ctxPara.globalCompositeOperation = 'copy';
ctxPara.translate(0, this.yPos);
ctxPara.drawImage(this.img, this.xPos, 0);
};
this.img.src = 'para.png';
this.yPos++;
};
};
This constructor is used in a function called 'fly':
var fly = function() {
var newParachute = new parachute();
setInterval(newParachute.animate, newParachute.speed);
};
And this 'fly' function is triggered when the window loads:
window.onload = function() {
var canvasBg = document.getElementById('canvasBg');
// I splitt the Background and the parachutists in two canvas elements
// handling the problem (to erase content and draw new content) with
// the canvas animation.
var canvasPara = document.getElementById('canvasPara');
ctxPara = canvasPara.getContext('2d');
canvasPara.width = window.width;
canvasPara.height = window.height;
canvasBg.width = window.width;
canvasBg.height = window.height;
fly();
clouds(); // background is loading here
};
What you should see, is a Parachutist flying down the screen. But unfortunately you don't...
Now, after that Long text. (Iam very sorry that it is so long :-( ) My question is: Do you know what I am doing wrong? Is my constuctor correct? Is, what i am trying to do, supposed to be written like this? Any advices or suggestions for a succesfull opportunity? (I hope my english isn't that terrible I think it is :-) )
Oh i forgot to mention the error. It's a TypeMissMatchError.
That means 'this.img' is not an img element at this line:
ctxPara.drawImage(this.img, this.xPos, 0);
Now, I followed the example of markE.
Instead of showing me a parachutist. It shows me an error in this line: ctxPara.drawImage(this.img, this.xPos, this.yPos);
var fly = function () {
var newParachute = new parachute();
newParachute.img.load.call(newParachute);
setInterval(newParachute.animate.call(newParachute), newParachute.speed);
};
var parachute = function () {
this.height = 35;
this.width = 30;
this.speed = 25;
this.xPos = Math.round(Math.random() * (window.innerWidth - this.width));
this.img = new Image();
this.yPos = 0;
this.img.isLoaded = false;
this.img.load = function () {
this.img.isLoaded = true;
};
this.img.src = 'parachute.png';
this.animate = function () {
if (this.img.isLoaded) {
ctxPara.clearRect(0, 0, canvasPara.width, canvasPara.height);
ctxPara.drawImage(this.img, this.xPos, this.yPos); // ERROR: 'Unknown Error'.
this.yPos++;
console.log('animating');
}
};
};
I am stuck again. But now i don't even know the reason... Please help!?
Demo: http://jsfiddle.net/m1erickson/ym55y/
A couple of issues:
(1) To get the window width you can use:
window.innerWidth
(2) setInterval calls newParachute.animate.
setInterval(newParachute.animate, newParachute.speed);
But this inside animate the window object--not the Parachute object.
To give the correct this to animate you can use the call method like this:
var newParachute = new parachute();
setInterval(function(){newParachute.animate.call(newParachute);}, newParachute.speed);
(3) You need to deal with clearing previously drawn images or they will still show on your canvas.

Categories

Resources