Currently programming Hangman and I have the following problem:
When I create a new HangmanController (choosing a new word, ...) the timer works fine, the only thing is, when I create one more HangmanController, the speed is 2x faster, when I create one more HangmanController 3x, etc.
I create a HangmanController only when
Where's the problem? Or how can I make the timer better?
//Here's where I create a new HangmanController. This happens when the user changes the difficulty (pressing a button in the HTML)
function inithm(difficulty) {
hm = new HangmanController();
hm.hmModel.reset(hm.displayedWord.word, false, difficulty);
UsedLetters();
};
//Constructor
HangmanController = function() {
this.hmModel = new HangmanModel();
//this.hmView = new HangmanView(document.getElementById('hm_view'));
this.displayedWord = new DisplayedWord();
this.userGuessField = document.getElementById('guessfield');
this.userGuessField.focus();
this.guessbutton = document.getElementById('guessbutton');
this.guessbutton.disabled = false;
/*this.updateTime = function(stopwatch) {
var e = stopwatch.getElapsed();
document.getElementById('stopwatch').innerHTML = e.hours * 60 + e.minutes + ' mins' + e.seconds + ' secs';
};*/
this.stopwatch = new Stopwatch();
};
//Timer, it is only called here, nowhere else (at setInterval)
Stopwatch = function() {
this.sek = 0;
setInterval(function() {timer();}, 1000);
};
function timer() {
document.getElementById('stopwatch').innerHTML = this.hm.stopwatch.sek;
this.hm.stopwatch.sek++;
}
Stopwatch.prototype.stop = function() {
document.getElementById('stopwatch').innerHTML = 0;
};
You are creating several instances of Stopwatch with each HangmanController, and everyone of them is calling timer() every second, which results in your #stopwatch element beeing increased more often than just once a second ;-)
edit: You should either dispose of the old HangmanController (especially the Stopwatch functionality) before creating a new one, or instead only create one instance of HangmanController, and make a method for restarting, or difficulty changes, etc..
Related
I'm trying to create a non-singleton object in NodeJS but when calling internal instance methods within the object, they are "undefined".
// We auto-refresh the cached data every 'refreshInterval' milliseconds
var refreshInterval = 30*1000;
var refreshTimer = null;
// We stop refreshing 'idleTimeout' milliseconds after the last request for our data
var idleTimeout = 5 * 60 * 1000; // 5 minutes
var idleTimer = null;
var cachedData = null;
var queryCallback = null;
function Cache(options) {
if (options) {
this.refreshInterval = options.refreshInterval || 30;
this.queryCallback = options.queryCallback || null;
this.idleTimeout = options.idleTimeout || 5 * 60 * 1000;
}
}
Cache.prototype.data = function(callback) {
updateIdle();
if (cachedData) {
callback(cachedData);
} else {
queryData(function(newdata) {
callback(newdata);
});
}
}
Cache.prototype.updateIdle = function() {
idleTimer && clearTimeout(idleTimer);
idleTimer = setTimeout(haltRefresh, idleTimeout);
wslog.info(`Will cease cache refresh at ${new Date(Date.now() + idleTimeout)} if no new activity`);
}
// ...
module.exports = Cache;
When I create the object
var Cache = require('./cache.js');
var myCache = Cache(opts);
It works but when I invoke myCache.data(), the invocation of the internal method updateIdle() halts the app because ReferenceError: updateIdle is not defined
Something tells me NONE of my instance methods are going to be "defined" even though they plainly are. And before I made them prototype methods, they worked fine. These would be a valid Java instance method invocations - is there some scope issue with JS?
Should I remove the Cache.prototype. from the functions I don't need exposed and then just use them? Do I have to add this. to any instance method invocations?
What do I need to know to make JS "just work" like normal OOP?
I'm creating a reusable function to monitor how frequently a button is clicked and wait until the user stops clicking it to continue and it seemed to work great until i tried creating 2 buttons (which will be how it will be in production)
I am using objects to store the data while the user is clicking.
If the timer is not reset and runs out, it will post the data.
If you try just spamming the lemon button a few times you will see how it works. The same if you spam the diamond button.
var arr = {};
var SC={};
function SpamControl(u, i) {
this.ui = u+i;
this.Sp = SC[ui];
if (!SC[ui]){
SC[ui] = arr;
SC[ui].timer= "";
SC[ui].count = 0;
}
clearTimeout(SC[ui].timer);
SC[ui].count = SC[ui].count + 1;
SC[ui].timer = setTimeout(function(){
$('#count').prepend(u+" gave "+ SC[ui].count +" "+i+"'s in a controlled fashion!<br>");
delete SC[ui];
}, 1000);
}
The problem comes when you spam between the two buttons. I had hoped it would handle the two users attached to the buttons separately but it seems not and im not sure why.
I realize this is a bit unclear but all i can say is try it out to understand what i mean
Here is a fiddle: https://jsfiddle.net/gbe7dv1r/1/
Don't use global variables, use local ones:
let ui = u + i;
let Sp = SC[ui];
Also all entries in Sp will reference the same arr object, when initualizing an entry you might want to create a new object for each:
SC[ui] = { };
Finally some wise words: Cryptic abbreviations like u, i, ui, SC, Sp, arr will really cause you headaches when maintaining this piece of code.
const controls = { /*[name]: { timer, count } */ };
function spamControl(user, item) {
const name = user + item;
const control = controls[name] || (controls[name] = { timer: 0, count: 0 });
control.count += 1;
clearTimeout(control.timer);
control.timer = setTimeout(function(){
$('#count').prepend(
`${user} gave ${control.count} ${item}"s in a controlled fashion!<br>`
);
delete controls[name];
}, 1000);
}
Hello I'm experimenting with Box2dWeb, and working with top-down car game.
My problem arises when I try to control the car, so it will move, at first only forwards. For simplicity I don't want to use wheels, and just apply the force to the car (a box).
For the controls I made a function for but for a reason it's not getting called... That's where I need a pointer or advice. (Creation and placement of objects works just fine)
Here's part of the code:
var GlobalVar={ }
var KEY = {
UP: 87,//W
DOWN: 83,//s
LEFT: 65,//A
RIGHT: 68//D
}
GlobalVar.pressedKeys = [];//an array to remember which key is pressed or not
$(function(){
$(document).keydown(function(e){
GlobalVar.pressedKeys[e.keyCode] = true;
});
$(document).keyup(function(e){
GlobalVar.pressedKeys[e.keyCode] = false;
});
Rendering();
PlaceStuff(GlobalVar.currentLevel);//placing stuff, like car and boundaries/walls
moveCar();
});
function moveCar(){
if (GlobalVar.pressedKeys[KEY.UP]){
var force = new b2Vec2(0, -10000000);
GlobalVar.car.ApplyForce(force, GlobalVar.car.GetWorldCenter());
}
}
It doesn't look like the moveCar function is being called more than once.
You should do the following:
function moveCar(){
if (GlobalVar.pressedKeys[KEY.UP]){
var force = new b2Vec2(0, -10000000);
GlobalVar.car.ApplyForce(force, GlobalVar.car.GetWorldCenter());
}
requestAnimationFrame(moveCar);
}
You may also want to add a modifier to modify the amount of force added depending on the frame rate:
then = Date.now();
function moveCar(){
var now = Date.now();
var modifier = now - then; // Make modifier the time in milliseconds it took since moveCar was last executed.
then = now;
if (GlobalVar.pressedKeys[KEY.UP]){
var force = new b2Vec2(0, -10000000);
GlobalVar.car.ApplyForce(force * modifier, GlobalVar.car.GetWorldCenter());
}
requestAnimationFrame(moveCar);
}
This will ensure the car doesn't move slower on slower systems.
If you also want the Rendering() function to be executed more than once, you may also want to create another function which gets called as often as possible and calls the other two functions.
then = Date.now();
function moveCar(modifier){
if (GlobalVar.pressedKeys[KEY.UP]){
var force = new b2Vec2(0, -10000000);
GlobalVar.car.ApplyForce(force * modifier, GlobalVar.car.GetWorldCenter());
}
}
function update() {
var now = Date.now();
var modifier = now - then; // Make modifier the time in milliseconds it took since moveCar was last executed.
then = now;
moveCar(modifier);
Rendering();
requestAnimationFrame(update);
}
As pointed out in the comments, you only call moveCall once, but you probably want to do it after each key press:
$(document).on('keydown keyup', function(e){
GlobalVar.pressedKeys[e.keyCode] = true;
moveCar();
});
I have been playing a bit with gskinner.com's EaselJS library (http://easeljs.com/, http://easeljs.com/examples/game/game.html), which makes dealing with HTML5's canvas a lot easier.
So I'm trying to remake something like Space Invaders in the canvas. So far it's not much, just the payer moving left to right. See the progress here: http://jansensan.net/experiments/easeljs-space-invader/
For the invaders, I needed an animation, so I followed a tutorial on how to do so: http://www.youtube.com/watch?v=HaJ615V6qLk
Now that is all good and dandy, however I follow gskinner.com's way of creating "classes": http://easeljs.com/examples/game/Ship.js I'm not certain if I can call that a class, but it is used as such.
So below is the class that I wrote for the Invader, however it seems like the BitmapSequence does not seem to be added to EaselJS's stage. Anyone can guide me through this? Thanks!
// REFERENCES
/*
http://easeljs.com/examples/game/Ship.js
*/
// CLASS
(function(window)
{
function Invader()
{
this.initialize();
}
var p = Invader.prototype = new Container();
// CONSTANTS
// VARS
p.image;
p.bitmapSequence;
// CONSTRUCTOR
p.Container_initialize = p.initialize; //unique to avoid overiding base class
p.initialize = function()
{
this.Container_initialize();
this.image = new Image();
this.image.onload = p.imageLoadHandler;
this.image.onerror = p.imageErrorHandler;
this.image.src = "assets/images/invader-spritesheet.png";
}
p.imageLoadHandler = function()
{
var frameData = {
anim:[0, 1, "anim"]
}
var spriteSheet = new SpriteSheet(p.image, 22, 16, frameData);
p.bitmapSequence = new BitmapSequence(spriteSheet);
p.bitmapSequence.regX = p.bitmapSequence.spriteSheet.frameWidth * 0.5;
p.bitmapSequence.regY = p.bitmapSequence.spriteSheet.frameHeight * 0.5;
p.bitmapSequence.gotoAndStop("anim");
p.addChild(p.bitmapSequence);
}
p.imageErrorHandler = function()
{
console.log("Error: the url assets/images/invader-spritesheet.png could not be loaded.");
}
window.Invader = Invader;
}(window));
Does you p.image/this.Container_initalize actually exist at that point? As you switch between using this. and p. between your init and other functions, while they might seem to be the same practice has often taught me its not the case. Try changing your init function to this:
p.initialize = function()
{
p.Container_initialize();
p.image = new Image();
p.image.onload = p.imageLoadHandler;
p.image.onerror = p.imageErrorHandler;
p.image.src = "assets/images/invader-spritesheet.png";
}
I have this countdown script wrapped as an object located in a separate file
Then when I want to setup a counter, the timeout function in the countdown class can not find the object again that I have setup within the document ready.
I sort of get that everything that is setup in the document ready is convined to that scope,
however it is possible to call functions within other document ready´s.
Does anyone has a solution on how I could setup multiple counters slash objects.
Or do those basic javascript classes have to become plugins
This is the class
function countdown(obj)
{
this.obj = obj;
this.Div = "clock";
this.BackColor = "white";
this.ForeColor = "black";
this.TargetDate = "12/31/2020 5:00 AM";
this.DisplayFormat = "%%D%% Days, %%H%% Hours, %%M%% Minutes, %%S%% Seconds.";
this.CountActive = true;
this.DisplayStr;
this.Calcage = cd_Calcage;
this.CountBack = cd_CountBack;
this.Setup = cd_Setup;
}
function cd_Calcage(secs, num1, num2)
{
s = ((Math.floor(secs/num1))%num2).toString();
if (s.length < 2) s = "0" + s;
return (s);
}
function cd_CountBack(secs)
{
this.DisplayStr = this.DisplayFormat.replace(/%%D%%/g, this.Calcage(secs,86400,100000));
this.DisplayStr = this.DisplayStr.replace(/%%H%%/g, this.Calcage(secs,3600,24));
this.DisplayStr = this.DisplayStr.replace(/%%M%%/g, this.Calcage(secs,60,60));
this.DisplayStr = this.DisplayStr.replace(/%%S%%/g, this.Calcage(secs,1,60));
//document.getElementById(this.Div).innerHTML = this.DisplayStr;
$('#'+this.Div).text(this.DisplayStr);
$('#tel').text(parseInt( $('#tel').text() )+1);
if (this.CountActive) setTimeout(this.obj +".CountBack(" + (secs-1) + ")", 990);
}
function cd_Setup()
{
var dthen = new Date(this.TargetDate);
var dnow = new Date();
ddiff = new Date(dthen-dnow);
gsecs = Math.floor(ddiff.valueOf()/1000);
this.CountBack(gsecs);
}
and setting it up
$(document).ready(function() {
var cd1 = new countdown('cd1');
cd1.Div = "clk";
cd1.TargetDate = "08/15/2010 8:00 PM";
cd1.DisplayFormat = "%%D%% days, %%H%% hours, %%M%% minutes, %%S%% seconds until event AAA happens";
cd1.Setup();
firebug says it errors out with the timeout function
thanks, Richard
cd1 is defined in the local scope. setTimeout will run the function passed as parameter 1 in the window [global] scope, and in your case, window.cd1 is undefined.
The solution for your problem would to make cd1 a global variable. [Remove the "var" in your declaration of cd1]
Off topic: I recommend you look into using anonymous functions, as they can make your code much more pretty/legible at times.