Oscillator not stoppingor ramping down in Web Audio API - javascript

I am trying to make a metronome as part of a bigger app by using an oscillator that switches on and off at set intervals in Web Audio Api, I have tried both the Start()/Stop() method and the LinearRampToValue(), in the code below. The oscillator plays at the correct bpm/interval but it won't leave a silent gap between clicks (I hope it makes sense)...what am I doing wrong? it's more of a pulse than a click with gaps as of now. Thanks!
function startMetronome() {
document.getElementById("startStopButton").innerText = "Stop";
document.getElementById("startStopButton").style.background = "black";
playingMetronome = true;
const bpm = parseInt(bpmInput.value);
const interval = (60 / bpm) * 1000;
oscillator = audioContext.createOscillator();
oscillator.frequency.value = 440;
oscillator.type = "triangle";
let gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
const now = audioContext.currentTime;
let nextStartTime = now;
oscillator.start(nextStartTime);
gainNode.gain.setValueAtTime(1, nextStartTime);
intervalId = setInterval(function () {
nextStartTime += interval / 1000;
gainNode.gain.linearRampToValueAtTime(0, nextStartTime + 0.5);
oscillator = audioContext.createOscillator();
oscillator.frequency.value = 440;
oscillator.type = "triangle";
const newGainNode = audioContext.createGain();
oscillator.connect(newGainNode);
newGainNode.connect(audioContext.destination);
oscillator.start(nextStartTime);
newGainNode.gain.setValueAtTime(1, nextStartTime);
gainNode = newGainNode;
}, interval);
}
the version with .start()/.stop()
function startMetronome() {
document.getElementById("startStopButton").innerText = "Stop";
document.getElementById("startStopButton").style.background = "black";
playingMetronome = true;
const bpm = parseInt(bpmInput.value);
const interval = (60 / bpm) * 1000;
oscillator = audioContext.createOscillator();
oscillator.frequency.value = 440;
oscillator.type = "triangle";
oscillator.connect(audioContext.destination);
const now = audioContext.currentTime;
let nextStartTime = now;
oscillator.start(nextStartTime);
intervalId = setInterval(function () {
nextStartTime += interval / 1000;
oscillator.stop(nextStartTime + 0.5);
oscillator = audioContext.createOscillator();
oscillator.frequency.value = 440;
oscillator.type = "triangle";
oscillator.connect(audioContext.destination);
oscillator.start(nextStartTime);
}, interval);
}

I managed to fix that moving a few things around. I mixed both version so that using gainNode I can fade the oscillator out each time.
function startMetronome() {
document.getElementById("startStopButton").innerText = "Stop";
document.getElementById("startStopButton").style.background = "black";
playingMetronome = true;
const bpm = parseInt(bpmInput.value);
const interval = (60 / bpm) * 1000;
const gainNode = audioContext.createGain();
gainNode.connect(audioContext.destination);
gainNode.gain.setValueAtTime(1, audioContext.currentTime);
function colorChange() {
setTimeout(function () {
document.getElementById("startStopButton").style.background = "black";
}, 7);
document.getElementById("startStopButton").style.background = "gray";
}
oscillator = audioContext.createOscillator();
oscillator.frequency.value = 440;
oscillator.type = "triangle";
oscillator.connect(gainNode);
const now = audioContext.currentTime;
let nextStartTime = now;
oscillator.start(nextStartTime);
gainNode.gain.setValueAtTime(1, nextStartTime);
gainNode.gain.linearRampToValueAtTime(0, nextStartTime + 0.1);
oscillator.stop(nextStartTime + 0.2);
colorChange();
intervalId = setInterval(function () {
nextStartTime += interval / 1000;
oscillator = audioContext.createOscillator();
oscillator.frequency.value = 440;
oscillator.type = "triangle";
oscillator.connect(gainNode);
oscillator.start(nextStartTime);
gainNode.gain.setValueAtTime(1, nextStartTime);
gainNode.gain.linearRampToValueAtTime(0, nextStartTime + 0.1);
oscillator.stop(nextStartTime + 0.2);
colorChange();
}, interval);
}

Related

How to make ObjectControls well

Please OrbitControls suddenly dosen't work after I added loaderManager.I don't understand why this heppend and how to fix.This work before but suddenly dosen't now.
controls.minDistance = 0.1;
controls.maxDistance = 1000;
controls.enablePlan = false;
controls.enableDamping = true;
controls.dampingFactor = 0.2;
controls.autoRotate = true;
controls.keys = {
LEFT: 'ArrowLeft',
UP: 'ArrowUp',
RIGHT: 'ArrowRight',
BOTTOM: "ArrowDown"
}
controls.listenToKeyEvents(window);
renderer.setSize(conatiner.clientWidth ,conatiner.clientHeight);
conatiner.appendChild(renderer.domElement);
const progressBar = document.getElementById("progress-bar")
const LoadingManager = new THREE.LoadingManager();
LoadingManager.onProgress = function(url, loaded, total){
progressBar.value = (loaded / total) * 100;
}
const progressBarContainer = document.querySelector(".progress-bar-contianer");
LoadingManager.onLoad = function(){
progressBarContainer.style.display = 'none';
}
let intre;
const loader = new THREE.GLTFLoader(LoadingManager);
const rgbeLoader = new THREE.RGBELoader(LoadingManager);
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
loader.load("../3d/test/scene.gltf", (gltf) =>{
intre = gltf.scene.children[0];
intre.scale.set(0.5,0.5,0.5);
scene.add(gltf.scene);
})
}

How to continually refill buffer/stream in and play audio without that annoying pause and click sound at the end of buffer?

Trying to create soothing hydrogen sound generator but web audio api is too limited or I'm missing something. No onbufferend or onrequestmoredata or similar. Only thing that exist is onended from AudioBufferSourceNode. Is what I'm trying to do not possible?
stackoverflow is whining about I should add more details because the post contains mostly code but I have no more details to add.
document.addEventListener("DOMContentLoaded", () => {
const button = document.querySelector('button');
const buttonStop = document.querySelector('#buttonStop');
let AudioContext = window.AudioContext || window.webkitAudioContext;
let audioCtx;
// Mono
const channels = 1;
function init() {
audioCtx = new AudioContext();
}
buttonStop.onclick = function() {
audioCtx.close();
audioCtx = null;
}
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
const hz_REAL_TIME_FREQUENCY = 440;
let dk;
let dPos = 0.0;
let firsttime = true;
const table = [];
function sum(t) {
if (firsttime) {
for (var i = 0; i < 9; ++i) {
let n = i + 2; // todo: this should continually increase, 2 -> infinite
table[i] = [];
table[i][0] = ((1 - 1/Math.pow( n , 2)) );
table[i][1] = ((1/4 - 1/Math.pow((n+1), 2)) );
table[i][2] = ((1/9 - 1/Math.pow((n+2), 2)) );
table[i][3] = (((1 / Math.pow(4, 2)) - 1/Math.pow((n+3), 2)) );
table[i][4] = (((1 / Math.pow(5, 2)) - 1/Math.pow((n+4), 2)) );
table[i][5] = (((1 / Math.pow(6, 2)) - 1/Math.pow((n+5), 2)) );
}
firsttime = false;
}
let sum_value = 0.0;
for (let i = 0; i < 9; ++i)
{
sum_value += Math.sin(table[i][0] * t);
sum_value += Math.sin(table[i][1] * t);
sum_value += Math.sin(table[i][2] * t);
sum_value += Math.sin(table[i][3] * t);
sum_value += Math.sin(table[i][4] * t);
sum_value += Math.sin(table[i][5] * t);
}
return sum_value;
}
button.onclick = function() {
if(!audioCtx) {
init();
dk = hz_REAL_TIME_FREQUENCY * 2 * Math.PI / audioCtx.sampleRate;
}
// Create an empty two second stereo buffer at the
// sample rate of the AudioContext
let frameCount_buffersize = audioCtx.sampleRate * 2.0;
let myArrayBuffer = audioCtx.createBuffer(channels, frameCount_buffersize, audioCtx.sampleRate);
function fillAudioBuffer() {
// Fill the buffer with white noise;
//just random values between -1.0 and 1.0
for (let channel = 0; channel < channels; channel++) {
// This gives us the actual array that contains the data
let nowBuffering = myArrayBuffer.getChannelData(channel);
for (let i_sampleNumber = 0; i_sampleNumber < frameCount_buffersize; i_sampleNumber++) {
// audio needs to be in [-1.0; 1.0]
nowBuffering[i_sampleNumber] = clamp(sum(dPos) * 0.03, -1.0, 1.0);
dPos += dk;
}
//console.log(nowBuffering);
}
}
function continueSource() {
// Get an AudioBufferSourceNode.
// This is the AudioNode to use when we want to play an AudioBuffer
let source = audioCtx.createBufferSource();
// set the buffer in the AudioBufferSourceNode
fillAudioBuffer();
source.buffer = myArrayBuffer;
// connect the AudioBufferSourceNode to the
// destination so we can hear the sound
source.connect(audioCtx.destination);
// OR
/*let gain = audioCtx.createGain();
// Set parameters
gain.gain.value = 0.1;
// Connect graph
source.connect(gain);
gain.connect(audioCtx.destination);/**/
// start the source playing
source.start();
source.onended = () => {
source.disconnect(audioCtx.destination);
continueSource();
}
}
continueSource();
}
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width">
<title>Hydrogen sound</title>
</head>
<body>
<h1>Hydrogen sound</h1>
<button>Make hydrogen sound</button>
<button id="buttonStop">Stop</button>
</body>
</html>
Edit:
document.addEventListener("DOMContentLoaded", () => {
const button = document.querySelector('button');
const buttonStop = document.querySelector('#buttonStop');
let AudioContext = window.AudioContext || window.webkitAudioContext;
let audioCtx;
// Mono
const channels = 1;
function init() {
audioCtx = new AudioContext();
}
buttonStop.onclick = function() {
audioCtx.close();
audioCtx = null;
}
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
const hz_REAL_TIME_FREQUENCY = 440;
let dk;
let dPos = 0.0;
let firsttime = true;
const table = [];
function sum(t) {
if (firsttime) {
for (var i = 0; i < 9; ++i) {
let n = i + 2; // todo: this should continually increase, 2 -> infinite
table[i] = [];
table[i][0] = ((1 - 1/Math.pow( n , 2)) );
table[i][1] = ((1/4 - 1/Math.pow((n+1), 2)) );
table[i][2] = ((1/9 - 1/Math.pow((n+2), 2)) );
table[i][3] = (((1 / Math.pow(4, 2)) - 1/Math.pow((n+3), 2)) );
table[i][4] = (((1 / Math.pow(5, 2)) - 1/Math.pow((n+4), 2)) );
table[i][5] = (((1 / Math.pow(6, 2)) - 1/Math.pow((n+5), 2)) );
}
firsttime = false;
}
let sum_value = 0.0;
for (let i = 0; i < 9; ++i)
{
sum_value += Math.sin(table[i][0] * t);
sum_value += Math.sin(table[i][1] * t);
sum_value += Math.sin(table[i][2] * t);
sum_value += Math.sin(table[i][3] * t);
sum_value += Math.sin(table[i][4] * t);
sum_value += Math.sin(table[i][5] * t);
}
return sum_value;
}
button.onclick = function() {
if(!audioCtx) {
init();
dk = hz_REAL_TIME_FREQUENCY * 2 * Math.PI / audioCtx.sampleRate;
}
// Create an empty two second stereo buffer at the
// sample rate of the AudioContext
let frameCount_buffersize = audioCtx.sampleRate * 2.0;
let myArrayBuffer = audioCtx.createBuffer(channels, frameCount_buffersize, audioCtx.sampleRate);
let myArrayBuffer2 = audioCtx.createBuffer(channels, frameCount_buffersize, audioCtx.sampleRate);
function fillAudioBuffer() {
// Fill the buffer with white noise;
//just random values between -1.0 and 1.0
for (let channel = 0; channel < channels; channel++) {
// This gives us the actual array that contains the data
let nowBuffering = myArrayBuffer.getChannelData(channel);
for (let i_sampleNumber = 0; i_sampleNumber < frameCount_buffersize; i_sampleNumber++) {
// audio needs to be in [-1.0; 1.0]
nowBuffering[i_sampleNumber] = clamp(sum(dPos) * 0.03, -1.0, 1.0);
dPos += dk;
}
//console.log(nowBuffering);
}
}
function fillAudioBuffer2() {
// Fill the buffer with white noise;
//just random values between -1.0 and 1.0
for (let channel = 0; channel < channels; channel++) {
// This gives us the actual array that contains the data
let nowBuffering = myArrayBuffer2.getChannelData(channel);
for (let i_sampleNumber = 0; i_sampleNumber < frameCount_buffersize; i_sampleNumber++) {
// audio needs to be in [-1.0; 1.0]
nowBuffering[i_sampleNumber] = clamp(sum(dPos) * 0.03, -1.0, 1.0);
dPos += dk;
}
//console.log(nowBuffering);
}
}
let i = 0;
fillAudioBuffer();
function continueSource() {
// Get an AudioBufferSourceNode.
// This is the AudioNode to use when we want to play an AudioBuffer
let source = audioCtx.createBufferSource();
// set the buffer in the AudioBufferSourceNode
if (i++ & 1) {
fillAudioBuffer();
source.buffer = myArrayBuffer2;
} else {
fillAudioBuffer2();
source.buffer = myArrayBuffer;
}
// connect the AudioBufferSourceNode to the
// destination so we can hear the sound
source.connect(audioCtx.destination);
// OR
/*let gain = audioCtx.createGain();
// Set parameters
gain.gain.value = 0.1;
// Connect graph
source.connect(gain);
gain.connect(audioCtx.destination);/**/
// start the source playing
source.start();
source.onended = () => {
source.disconnect(audioCtx.destination);
continueSource();
}
}
continueSource();
}
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width">
<title>Hydrogen sound</title>
</head>
<body>
<h1>Hydrogen sound</h1>
<button>Make hydrogen sound</button>
<button id="buttonStop">Stop</button>
</body>
</html>
Edit two. Buffer size increased (no needed) and fillAudioBuffer functions offloaded from main thread. This works. I didn't need to do this on my C++ version. Javascript is slower than I thought.
document.addEventListener("DOMContentLoaded", () => {
const button = document.querySelector('button');
const buttonStop = document.querySelector('#buttonStop');
let AudioContext = window.AudioContext || window.webkitAudioContext;
let audioCtx;
// Mono
const channels = 1;
function init() {
audioCtx = new AudioContext();
}
buttonStop.onclick = function() {
audioCtx.close();
audioCtx = null;
}
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
const hz_REAL_TIME_FREQUENCY = 440;
let dk;
let dPos = 0.0;
let firsttime = true;
const table = [];
function sum(t) {
if (firsttime) {
for (var i = 0; i < 9; ++i) {
let n = i + 2; // todo: this should continually increase, 2 -> infinite
table[i] = [];
table[i][0] = ((1 - 1/Math.pow( n , 2)) );
table[i][1] = ((1/4 - 1/Math.pow((n+1), 2)) );
table[i][2] = ((1/9 - 1/Math.pow((n+2), 2)) );
table[i][3] = (((1 / Math.pow(4, 2)) - 1/Math.pow((n+3), 2)) );
table[i][4] = (((1 / Math.pow(5, 2)) - 1/Math.pow((n+4), 2)) );
table[i][5] = (((1 / Math.pow(6, 2)) - 1/Math.pow((n+5), 2)) );
}
firsttime = false;
}
let sum_value = 0.0;
for (let i = 0; i < 9; ++i)
{
sum_value += Math.sin(table[i][0] * t);
sum_value += Math.sin(table[i][1] * t);
sum_value += Math.sin(table[i][2] * t);
sum_value += Math.sin(table[i][3] * t);
sum_value += Math.sin(table[i][4] * t);
sum_value += Math.sin(table[i][5] * t);
}
return sum_value;
}
button.onclick = function() {
if(!audioCtx) {
init();
dk = hz_REAL_TIME_FREQUENCY * 2 * Math.PI / audioCtx.sampleRate;
}
// Create an empty two second stereo buffer at the
// sample rate of the AudioContext
let frameCount_buffersize = audioCtx.sampleRate * 20.0;
let myArrayBuffer = audioCtx.createBuffer(channels, frameCount_buffersize, audioCtx.sampleRate);
let myArrayBuffer2 = audioCtx.createBuffer(channels, frameCount_buffersize, audioCtx.sampleRate);
function fillAudioBuffer() {
// Fill the buffer with white noise;
//just random values between -1.0 and 1.0
for (let channel = 0; channel < channels; channel++) {
// This gives us the actual array that contains the data
let nowBuffering = myArrayBuffer.getChannelData(channel);
for (let i_sampleNumber = 0; i_sampleNumber < frameCount_buffersize; i_sampleNumber++) {
// audio needs to be in [-1.0; 1.0]
nowBuffering[i_sampleNumber] = clamp(sum(dPos) * 0.03, -1.0, 1.0);
dPos += dk;
}
//console.log(nowBuffering);
}
}
function fillAudioBuffer2() {
// Fill the buffer with white noise;
//just random values between -1.0 and 1.0
for (let channel = 0; channel < channels; channel++) {
// This gives us the actual array that contains the data
let nowBuffering = myArrayBuffer2.getChannelData(channel);
for (let i_sampleNumber = 0; i_sampleNumber < frameCount_buffersize; i_sampleNumber++) {
// audio needs to be in [-1.0; 1.0]
nowBuffering[i_sampleNumber] = clamp(sum(dPos) * 0.03, -1.0, 1.0);
dPos += dk;
}
//console.log(nowBuffering);
}
}
let i = 0;
fillAudioBuffer();
function continueSource() {
// Get an AudioBufferSourceNode.
// This is the AudioNode to use when we want to play an AudioBuffer
let source = audioCtx.createBufferSource();
// set the buffer in the AudioBufferSourceNode
if (i++ & 1) {
console.log('Using myArrayBuffer2', i);
setTimeout(() => fillAudioBuffer(), 0);
source.buffer = myArrayBuffer2;
} else {
console.log('Using myArrayBuffer', i);
setTimeout(() => fillAudioBuffer2(), 0);
source.buffer = myArrayBuffer;
}
// connect the AudioBufferSourceNode to the
// destination so we can hear the sound
source.connect(audioCtx.destination);
// OR
/*let gain = audioCtx.createGain();
// Set parameters
gain.gain.value = 0.1;
// Connect graph
source.connect(gain);
gain.connect(audioCtx.destination);/**/
// start the source playing
source.start();
source.onended = () => {
source.disconnect(audioCtx.destination);
continueSource();
}
}
continueSource();
}
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width">
<title>Hydrogen sound</title>
</head>
<body>
<h1>Hydrogen sound</h1>
<button>Make hydrogen sound</button>
<button id="buttonStop">Stop</button>
</body>
</html>
Here is one possible approach to do what you want, assuming the clicking you get is because there's a gap between the end of one buffer and the start of the next.
Create two separate buffers and two corresponding AudioBufferSourceNodes. Set an onended event handler for each one. Start playing the first buffer and schedule the second to start just at the end of the first.
When you get the onended event, create a new buffer and source and schedule it to start playing at the end of the second buffer. Set a new onended event handler for this buffer that basiscally does the same.
Now when you get an onended event, there will be a buffer already scheduled to play without gaps, and you can create a new one ready to go when the currently playing one is done.
However, you may still get some clicks between buffers because the value at the end of one buffer may be very different from the value at the beginning of the next. To fix this, you may need to either ramp down (via a gain node) the end of one buffer, and ramp up the beginning of the next. Or cross-fade the two buffers to have smooth transition.
The fade-in/fade-out of the buffers could be done via AudioBufferSourceNode AudioParam automations, or you can do it when you fill the buffer.

What is the benefit of requestAnimationFrame(...paramList...)?

Question:
Why is the requestAnimationFrame called with this function? For example, if I was building a game what benefit would requestAnimationFrame be over just repainting the canvas.
code:
function main() {
var now = Date.now();
var delta = now - then;
update(delta / 1000);
render();
then = now;
// Request to do this again ASAP
requestAnimationFrame(main);
};
All Code Below: (You would need 3 .jpg files)
<html>
<head>
<title>test</title>
</head>
<body>
<script>
// Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 512;
canvas.height = 480;
document.body.appendChild(canvas);
// Background image
var bgReady = false;
var bgImage = new Image();
bgImage.onload = function () {
bgReady = true;
};
bgImage.src = "background.png";
// Hero image
var heroReady = false;
var heroImage = new Image();
heroImage.onload = function () {
heroReady = true;
};
heroImage.src = "hero.png";
// Monster image
var monsterReady = false;
var monsterImage = new Image();
monsterImage.onload = function () {
monsterReady = true;
};
monsterImage.src = "monster.png";
// Game objects
var hero = {
speed: 256 // movement in pixels per second
};
var monster = {};
var monstersCaught = 0;
// Handle keyboard controls
var keysDown = {};
addEventListener("keydown", function (e) {
keysDown[e.keyCode] = true;
}, false);
addEventListener("keyup", function (e) {
delete keysDown[e.keyCode];
}, false);
// Reset the game when the player catches a monster
var reset = function () {
hero.x = canvas.width / 2;
hero.y = canvas.height / 2;
// Throw the monster somewhere on the screen randomly
monster.x = 32 + (Math.random() * (canvas.width - 64));
monster.y = 32 + (Math.random() * (canvas.height - 64));
};
// Update game objects
var update = function (modifier) {
if (38 in keysDown) { // Player holding up
hero.y -= hero.speed * modifier;
}
if (40 in keysDown) { // Player holding down
hero.y += hero.speed * modifier;
}
if (37 in keysDown) { // Player holding left
hero.x -= hero.speed * modifier;
}
if (39 in keysDown) { // Player holding right
hero.x += hero.speed * modifier;
}
// Are they touching?
if (hero.x <= (monster.x + 32) &&
monster.x <= (hero.x + 32) &&
hero.y <= (monster.y + 32) &&
monster.y <= (hero.y + 32)
) {
++monstersCaught;
reset();
}
};
// Draw everything
var render = function () {
if (bgReady) {
ctx.drawImage(bgImage, 0, 0);
}
if (heroReady) {
ctx.drawImage(heroImage, hero.x, hero.y);
}
if (monsterReady) {
ctx.drawImage(monsterImage, monster.x, monster.y);
}
// Score
ctx.fillStyle = "rgb(250, 250, 250)";
ctx.font = "24px Helvetica";
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.fillText("Goblins caught: " + monstersCaught, 32, 32);
};
// The main game loop
/*var main = function () {
var now = Date.now();
var delta = now - then;
update(delta / 1000);
render();
then = now;
// Request to do this again ASAP
requestAnimationFrame(main);
};*/
// The main game loop -- This is the second way of doing this
function main() {
var now = Date.now();
var delta = now - then;
update(delta / 1000);
render();
then = now;
// Request to do this again ASAP
requestAnimationFrame(main);
};
// Cross-browser support for requestAnimationFrame
var w = window;
requestAnimationFrame = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.msRequestAnimationFrame || w.mozRequestAnimationFrame;
// Let's play this game!
var then = Date.now();
reset();
main();
</script>
</body>
</html>
Because repeatedly calling main would cause the browser to hang until it gave up with a stack overflow error.
requestAnimationFrame simply says "do this when the browser is ready to render another frame".

Javascript Html5 canvas problems

This is a game I am making. I cant figure out why it is not working. I have the JS fiddle here http://jsfiddle.net/aa68u/4/
// Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 512;
canvas.height = 480;
document.body.appendChild(canvas);
// Background image
var bgReady = false;
var bgImage = new Image();
bgImage.onload = function () {
bgReady = true;
};
bgImage.src = "http://6269-9001.zippykid.netdna-cdn.com/wp-content/uploads/2013/11/Woods-Wallpaper.jpg";
// Ship image
var shipReady = false;
var shipImage = new Image();
shipImage.onload = function () {
shipImage = true;
};
shipImage.src = "http://s29.postimg.org/3widtojzn/hero.png";
// Astroid image
var astroidReady = false;
var astroidImage = new Image();
astroidImage.onload = function () {
astroidReady = true;
};
astroidImage.src = "http://s29.postimg.org/4r4xfprub/monster.png";
// Game objects
var ship = {
speed: 256;
};
var astroid = {};
var health = 100;
window.keyStates = {};
addEventListener('keydown', function (e) {
keyStates[e.keyCode || e.key] = true;
e.preventDefault();
e.stopPropagation();
}, true);
addEventListener('keyup', function (e) {
keyStates[e.keyCode || e.key] = false;
e.preventDefault();
e.stopPropagation();
}, true);
var reset = function () {
astroid.width = 10;
astroid.height = 10;
astroid.x = 32 + (Math.random() * (canvas.width - 64));
astroid.y = 32 + (Math.random() * (canvas.height - 64));
ship.speed = 256;
for (var p in keyStates) keyStates[p]= false;
};
// Update game objects
function update (modifier) {
if (keyStates[38] ==true) { // Player holding up
astroid.x -= ship.speed * modifier;
}
if (keyStates[40]==true) { // Player holding down
astroid.x += ship.speed * modifier;
}
if (keyStates[37]==true) { // Player holding left
astroid.y -= ship.speed * modifier;
}
if (keyStates[39]==true) { // Player holding right
astroid.y += ship.speed * modifier;
}
if (astroid.width) < 200{
astroid.width +=10;
astroid.height += 10;
}
if (astroid.width) > 200{
reset();
}
// Are they touching?
if (keyStates[32] == true && ship.x <= (astroid.x + 32) && astroid.x <= (ship.x + 32) && ship.y <= (astroid.y + 32) && astroid.y <= (ship.y + 32)) {
monstersCaught += 1;
reset();
}
};
// Draw everything
var render = function () {
if (bgReady) {
ctx.drawImage(bgImage, 0, 0);
}
if (shipReady) {
ctx.drawImage(heroImage, hero.x, hero.y);
}
if (astroidReady) {
ctx.drawImage(monsterImage, monster.x, monster.y);
}
// Score
ctx.fillStyle = "rgb(250, 250, 250)";
ctx.font = "24px Helvetica";
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.fillText("Your Health: " + health, 32, 32);
};
// The main game loop
var main = function () {
var now = Date.now();
var delta = now - then;
if (delta > 20) delta = 20;
update(delta / 1000);
render();
then = now;
};
// Let's play this game!
reset();
var then = Date.now();
setInterval(main, 1); // Execute as fast as possible
The game is supposed to be a ship(shoe image) that is avoiding astroids that get bigger(ants) but when you move your ship(shoe) stays in the same place and the astroids(ants) move. The ants/astroids also get bigger like you are going close to them.
var ship = {
speed: 256;
};
Remove the ; after the value.
if astroid.width < 200{
and
if astroid.width > 200{
Need parentheses around the if conditions.
The error console is helpful! But now it's just stuck in an infinite loop of monsterImage is not defined. Just... go back, write your code more carefully, and use the error console! It's there for a reason!

Canvas wont hide?

My HTML5 Canvas element doesn't seem to hide.
I created a timer that should hide the element when the timer reaches 0, I tested this with an alert flag and that worked fine.
Issue:
if (TotalSeconds <= 0)
{
ctx.clearRect(0, 0, canvas.width, canvas.height)
}
Entire code:
// Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 512;
canvas.height = 480;
document.body.appendChild(canvas);
// Background image
var bgReady = false;
var bgImage = new Image();
bgImage.onload = function () {
bgReady = true;
};
bgImage.src = "images/background.png";
// Hero image
var heroReady = false;
var heroImage = new Image();
heroImage.onload = function () {
heroReady = true;
};
heroImage.src = "images/hero.png";
// Monster image
var monsterReady = false;
var monsterImage = new Image();
monsterImage.onload = function () {
monsterReady = true;
};
monsterImage.src = "images/flamingo.png";
// Game objects
var hero = {
speed: 256 // movement in pixels per second
};
var monster = {};
var monstersCaught = 0;
// Handle keyboard controls
var keysDown = {};
addEventListener("keydown", function (e) {
keysDown[e.keyCode] = true;
}, false);
addEventListener("keyup", function (e) {
delete keysDown[e.keyCode];
}, false);
// Reset the game when the player catches a monster
var reset = function () {
hero.x = canvas.width / 2;
hero.y = canvas.height / 2;
// Throw the monster somewhere on the screen randomly
monster.x = 32 + (Math.random() * (canvas.width - 64));
monster.y = 32 + (Math.random() * (canvas.height - 64));
};
// Update game objects
var update = function (modifier) {
if (38 in keysDown) { // Player holding up
hero.y -= hero.speed * modifier;
}
if (40 in keysDown) { // Player holding down
hero.y += hero.speed * modifier;
}
if (37 in keysDown) { // Player holding left
hero.x -= hero.speed * modifier;
}
if (39 in keysDown) { // Player holding right
hero.x += hero.speed * modifier;
}
// Are they touching?
if (
hero.x <= (monster.x + 32)
&& monster.x <= (hero.x + 32)
&& hero.y <= (monster.y + 32)
&& monster.y <= (hero.y + 32)
)
{
++monstersCaught;
reset();
}
if (hero.x <= 0)
{
hero.x = 510
}
if (hero.y <= 0)
{
hero.y = 478
}
};
CreateTimer(5);
var Timer;
var TotalSeconds;
function CreateTimer(Time) {
Timer = $("#timer").text();
TotalSeconds = Time;
UpdateTimer()
window.setTimeout("Tick()", 1000);
}
function Tick() {
TotalSeconds -= 1;
UpdateTimer()
window.setTimeout("Tick()", 1000);
}
function UpdateTimer() {
$("#timer").text(TotalSeconds);
if (TotalSeconds <= 0)
{
ctx.clearRect(0, 0, canvas.width, canvas.height)
}
}
// Draw everything
var render = function () {
if (bgReady) {
ctx.drawImage(bgImage, 0, 0);
}
if (heroReady) {
ctx.drawImage(heroImage, hero.x, hero.y);
}
if (monsterReady) {
ctx.drawImage(monsterImage, monster.x, monster.y);
}
// Score
ctx.fillStyle = "rgb(250, 250, 250)";
ctx.font = "24px Helvetica";
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.fillText("Flamingos slaughtered: " + monstersCaught, 32, 32);
ctx.textAlign = "right";
ctx.textBaseline = "bottom";
ctx.fillText("Time left: " + TotalSeconds, 150,32);
};
// The main game loop
var main = function () {
var now = Date.now();
var delta = now - then;
update(delta / 1000);
render();
then = now;
};
// Let's play this game!
reset();
var then = Date.now();
setInterval(main, 1); // Execute as fast as possible
The html is just <canvas></canvas>
Pretty new to this so any help would be appreciated.
Thanks!
This does not hide the canvas but clear the context, which means it's clear the drawn shape on canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
You can hide it by appying a CSS style for it:
if (TotalSeconds <= 0) {
ctx.canvas.style.display = 'none';
}
and to reveal it again simple use block or inline-block instead of none.
If you want to hide it but keep its space you can use:
ctx.canvas.style.visibility = 'hidden';
You can also remove it completely from the DOM tree by using:
parent.removeChild(canvasElement);
where parent is the container element (for example document.body as you are using now) and canvasElement is a reference is your canvas.
All that being said - if you want to just clear the canvas then you need to stop the loop you have running. You can use for example a flag to do this:
function Tick() {
TotalSeconds--;
UpdateTimer();
if (TotalSeconds > 0) setTimeout(Tick, 1000); // only loop if sec > 0
}
and then clear it as you already do.
Hope this helps!

Categories