PhaserJs/javascript: My array is updating too many times - javascript

So I have some code making a simple inventory, like this:
else if (this.inventory.indexOf(tileType) != -1) {
this.inventory[this.inventory.indexOf(tileType) + 1] =
this.inventory[this.inventory.indexOf(tileType) + 1] - 1;
console.log(this.inventory);
return;
In phasers update function, i'm using this.input.manager.activePointer.isDown as my event trigger, and it subtracts it for as long as the mouse is down, which results in at least 6 updates cycles for the quickest clicks
I've tried methods like return; etc. , but it always goes subtracting multiple times.
So is there any javascript method i could use, or a better phaser input event?

As #Geshode mentioned in the comments, I would also setup the mouse event in the create function, and remove the other mouse code from the update function. (checkout the documentation for details)
Since this event will be executed only once per click/touch.
function create(){
...
this.input.on('pointerdown', () => {
if (this.inventory.indexOf(tileType) != -1) {
this.inventory[this.inventory.indexOf(tileType) + 1] =
this.inventory[this.inventory.indexOf(tileType) + 1] - 1;
console.log(this.inventory);
...
}
});
}
Check out these official Mouse examples
Update example:
The idea is to make all local variables needed in the event function, that are created / used in the update function with "scene properties".
Out of tileType you would have to make this.tileType, and so on. (you just have to beware of naming collisions)
How it could look like:
function create(){
...
this.input.on('pointerdown', () => {
// the tileType is now a property of the current scene
if (this.inventory.indexOf(this.tileType) != -1) {
this.inventory[this.inventory.indexOf(this.tileType) + 1] =
this.inventory[this.inventory.indexOf(this.tileType) + 1] - 1;
console.log(this.inventory);
...
}
});
}
Or better use a "gameState" object. In the create function add this.gameState = {}; and than each variable you would like to add / access, just write this.gameState.tileType = 1;. So long the context (this) is the scene, this should work, and there will be no naming problem.

Related

How to trigger a var after x actions

I have the following code. I want to trigger the action in function activityDetected(eventName) only after 100 click. How to do this ?
I know I have to put let a = 1; ++a but not sure where...
https://pastebin.com/SMsJsikE
const intervalTimeout = 2000;
//here is where code should be added. let a = 1; ++a...
function activityDetected(eventName) {
console.log(`Activity detected with the event name: ${eventName}!`);
clearInterval(activityTimeout);
activityTimeout = setInterval(recordNoActivity, intervalTimeout);
}
document.addEventListener('click', _ => {
activityDetected('click');
});
You need to declare a counter outside the function and up it by 1 when the eventName is 'click'. After that check for a % 100 and put whatever action you want to call every 100 clicks in there.
Look at the code example:
// For ease, change this to a smaller value to detect more often, higher to detect less often!
const intervalTimeout = 2000;
let a = 0;
// Here's our interval, setting up the initial capture of no activity
let activityTimeout = setInterval(recordNoActivity, intervalTimeout);
// A single function to handle the events we're listening to.
// clears the interval and restarts it, also tells us which event has cleared the interval!
//here is where code should be added. let a = 1; ++a...
function activityDetected(eventName) {
if(eventName == 'click'){
a++;
if(a%100 == 0){
// Trigger whatever you want to trigger after every 100 clicks
}
}
console.log(`Activity detected with the event name: ${eventName}!`);
clearInterval(activityTimeout);
activityTimeout = setInterval(recordNoActivity, intervalTimeout);
}
// Set listening events
document.addEventListener('keydown', _ => {
activityDetected('keydown');
});
document.addEventListener('click', _ => {
activityDetected('click');
});
As correctly pointed by others on this thread the variable a should be declared and defined outside the function but the reason why this approach would work is because of Closure
So when the function is getting invoked an execution context is created which contains
scopeChain - it contains variableObject + all parent execution context's variableObject
variableObject - it contains function arguments / parameters, inner variable and function declarations
this - the this context.
Thus the variable a values would be saved before invoking the function and the variable will keep incrementing.

Access an instance from a function outside of the object constructor

I have a problem I can't understand after a lot of attempts to solve it.
To help you understand, there are 2 classes (Game and Board), and a third file with the jQuery keypress controls. Game is about the logic of the game, and Board about the display.
Here is a part of the code I hope sufficient to understand.
// GAME CLASS
function Game(width, height) {
this.width = width;
this.height = height;
this.forbiddenPosition = [];
this.chartBoard = this.resetBoard();
this.generateGame();
}
Game.prototype.generateGame = function () {
this.player1 = new Player("Joueur 1", 100, dagger);
this.player2 = new Player("Joueur 2", 100, dagger);
const playerArray = [this.player1, this.player2];
}
Game.prototype.getPlayer1 = function () {
return this.player1;
};
Game.prototype.getPlayer2 = function () {
return this.player2;
};
Game.prototype.switchTurn = function (player1, player2) {
console.log(player1);
console.log(player2);
};
// BOARD CLASS
const ctx = $('#board').get(0).getContext('2d');
function Board (width, height) {
this.width = width;
this.height = height;
this.game = new Game(this.width, this.height);
this.displayInfoPlayers(this.game.getPlayer1(), this.game.getPlayer2());
}
Board.prototype.displayInfoPlayers = function (player1, player2) {
$('.canvas-side__left').css('visibility', 'visible');
$('.canvas-side__right').css('visibility', 'visible');
$('.canvas-side__left').addClass('animated slideInLeft');
$('.canvas-side__right').addClass('animated slideInRight');
$(".canvas-side__left").html("<h2 class='canvas-side--title'>" + player1.name + "</h2><p class='canvas-side--health'>" + player1.health + "</p><p class='canvas-side--health'>" + player1.weapon.name + "</p>");
$(".canvas-side__right").html("<h2 class='canvas-side--title'>" + player2.name + "</h2><p class='canvas-side--health'>" + player2.health + "</p><p class='canvas-side--health'>" + player2.weapon.name + "</p>");
};
// CONTROL
$(document).on('keypress', function (e) {
if (e.which == 13) {
Game.prototype.switchTurn(Game.prototype.getPlayer1(), Game.prototype.getPlayer2());
e.stopPropagation();
}
});
Board class is linked to Game class and so uses this. The control using jQuery code are in a third file and not into a class.
When I press Enter, I get undefined for player1 and 2. I tried different ways to call the getter functions and nothing works. I also tried to put the controls inside the Game file and still nothing.
I get either undefined or getPlayer1() is not a function.
I am looking for a way to call these getter functions from everywhere so I can use player1 and 2 which I need to move on the board.
There are several issues there.
The keypress event handler is using Game.prototype, not an instance of Game. You want to be using an instance of Game you've created and stored somewhere. Game.prototype doesn't have the player1 and player2 properties. They're added to instances of Game by the Game constructor. Nothing ever adds them to Game.prototype (which is correct, they shouldn't be on the prototype).
There's no need for getPlayer1, etc. You can directly access player1 and player2. (It's possible to make player1 and player2 private and only provide accessors for them, but it's a bit complicated at the moment and probably not something you want to take on yet.)
Within Game methods, you need to consistently use this.player1 and this.player2, don't pass the players around.
It seems odd for Board to create an instance of Game. It seems like it should be the other way around.
I suggest stepping back from this task and trying something simpler first (like creating a class, an instance of the class, and using that instance in an event handler), then incrementally adding complexity and making sure at each stage you're clear on what's happening. As you go, you may have more specific questions, which you can post on SO (after thorough research, etc.).
You can do something like this and it should work. Essentially, you prototype the function you're trying to access which is not declared until after the constructor.
class Test {
constructor() {
this.five = Test.prototype.getFive();
}
getFive() {
return 5;
}
}
let test = new Test();
console.log(test.five); // Returns 5

Calling a function within a function (invoked functions)

I'm struggling to get my head around invoked functions and their usage. I have a function called addExp - it adds experience points to the player object and determines whether or not the player has levelled up.
Here is the function:
function addExp(amt) {
return function(amt) {
player.exp += amt;
while(player.exp >= player.reqExp) {
var carry = player.exp - player.reqExp;
player.level++;
nextLevel(player.level);
player.exp = 0 + carry;
}
}
}
Here is what happens when I use it:
addExp(200); // adds no exp to the player
addExp(200)(200); // adds 200 exp to the player
addExp()(200); // adds 200 exp to the player
I have searched around online and read other questions but can't understand why it works like this - which (if any) of those function calls is done correctly? Could someone please explain, in simple enough terms, why returning a function within a function is advantageous?

Write a wrapper object in Javascript

First off, let me apologize if my question isn't worded correctly - I'm not a professional coder so my terminology might be weird. I hope my code isn't too embarrassing :(
I have a fade() method that fades an image in and out with a mouse rollover. I would like to use a wrapper object (I think this is the correct term), to hold the image element and a few required properties, but I don't know how to accomplish this. fade() is called from the HTML, and is designed to be dropped into a page without much additional setup (so that I can easily add new fading images to any HTML), just like this:
<div id="obj" onmouseover="fade('obj', 1);" onmouseout="fade('obj', 0);">
The fade(obj, flag) method starts a SetInterval that fades the image in, and when the pointer is moved away, the interval is cleared and a new SetInterval is created to fade the image out. In order to save the opacity state, I've added a few properties to the object: obj.opacity, obj.upTimer, and obj.dnTimer.
Everything works okay, but I don't like the idea of adding properties to HTML elements, because it might lead to a future situation where some other method overwrites those properties. Ideally, I think there should be a wrapper object involved, but I don't know how to accomplish this cleanly without adding code to create the object when the page loads. If anyone has any suggestions, I would greatly appreciate it!
Here's my fader method:
var DELTA = 0.05;
function fade(id, flag) {
var element = document.getElementById(id);
var setCmd = "newOpacity('" + id + "', " + flag + ")";
if (!element.upTimer) {
element.upTimer = "";
element.dnTimer = "";
}
if (flag) {
clearInterval(element.dnTimer);
element.upTimer = window.setInterval(setCmd, 10);
} else {
clearInterval(element.upTimer);
element.dnTimer = window.setInterval(setCmd, 10);
}
}
function newOpacity(id, flag) {
var element = document.getElementById(id);
if (!element.opacity) {
element.opacity = 0;
element.modifier = DELTA;
}
if (flag) {
clearInterval(element.dnTimer)
element.opacity += element.modifier;
element.modifier += DELTA; // element.modifier increases to speed up fade
if (element.opacity > 100) {
element.opacity = 100;
element.modifier = DELTA;
return;
}
element.opacity = Math.ceil(element.opacity);
} else {
clearInterval(element.upTimer)
element.opacity -= element.modifier;
element.modifier += DELTA; // element.modifier increases to speed up fade
if (element.opacity < 0) {
element.opacity = 0;
element.modifier = DELTA;
return;
}
element.opacity =
Math.floor(element.opacity);
}
setStyle(id);
}
function setStyle(id) {
var opacity = document.getElementById(id).opacity;
with (document.getElementById(id)) {
style.opacity = (opacity / 100);
style.MozOpacity = (opacity / 100);
style.KhtmlOpacity = (opacity / 100);
style.filter = "alpha(opacity=" + opacity + ")";
}
}
You are right, adding the handlers in your HTML is not good. You also loose the possible to have several handlers for event attached to one object.
Unfortunately Microsoft goes its own way regarding attaching event handlers. But you should be able to write a small wrapper function to take care of that.
For the details, I suggest you read quirksmode.org - Advanced event registration models.
An example for W3C compatible browsers (which IE is not): Instead of adding your event handler in the HTML, get a reference to the element and call addEventListener:
var obj = document.getElementById('obj');
obj.addEventListener('mouseover', function(event) {
fade(event.currentTarget, 1);
}, false);
obj.addEventListener('mouseout', function(event) {
fade(event.currentTarget, 0);
}, false);
As you can see I'm passing directly a reference to the object, so in you fade method you already have a reference to the object.
You could wrap this in a function that accepts an ID (or reference) and every time you want to attach an event handler to a certain element, you can just pass the ID (or reference) to this function.
If you want to make your code reusable, I suggest to put everything into an object, like this:
var Fader = (function() {
var DELTA = 0.05;
function newOpacity() {}
function setStyle() {}
return {
fade: function(...) {...},
init: function(element) {
var that = this;
element.addEventListener('mouseover', function(event) {
that.fade(event.currentTarget, 1);
}, false);
element.addEventListener('mouseout', function(event) {
that.fade(event.currentTarget, 0);
}, false);
}
};
}())
Using an object to hold your functions reduces pollution of the global namespace.
Then you could call it with:
Fader.init(document.getElementById('obj'));
Explanation of the above code:
We have an immediate function (function(){...}()) which means, the function gets defined and executed (()) in one go. This function returns an object (return {...};, {..} is the object literal notation) which has the properties init and fade. Both properties hold functions that have access to all the variables defined inside the immediate function (they are closures). That means they can access newOpacity and setStyle which are not accessible from the outside. The returned object is assigned to the Fader variable.
This doesn't directly answer your question but you could use the jQuery library. It's simple, all you have to do is add a script tag at the top:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js">
Then your div would look like:
<div id="obj" onmouseover="$('#obj').fadeIn()" onmouseout="$('#obj').fadeOut()">
jQuery will handle all the browser dependencies for you so you don't have to worry about things like differences between firefox and mozilla etc...
If you want to keep your HTML clean, you should consider using JQuery to set up the events.
Your HTML will look like this:-
<div id="obj">
Your JavaScript will look "something" like this:-
$(document).ready(function() {
$("#obj").mouseover(function() {
Page.fade(this, 1);
}).mouseout(function(){
Page.fade(this, 0);
});
});
var Page = new function () {
// private-scoped variable
var DELTA = 0.05;
// public-scoped function
this.fade = function(divObj, flag) {
...
};
// private-scoped function
var newOpacity = function (divObj, flag) {
...
};
// private-scoped function
var setStyle = function (divObj) {
...
};
};
I introduced some scoping concept in your Javascript to ensure you are not going to have function overriding problems.

How to Add Event Handler with Arguments to an Array of Elements in Javascript?

I have a three-step process that is entirely reliant upon JavaScript and Ajax to load data and animate the process from one step to the next. To further complicate matters, the transition (forward and backward) between steps is animated :-(. As user's progress through the process anchor's appear showing the current step and previous steps. If they click on a previous step, then it takes them back to the previous step.
Right now, the entire process (forward and backward) works correctly, if you begin at step 1, but if you jump straight to step 3 then the anchors for step 1 and step 2 also perform the same action as step 3.
This is the portion of the code that loops through all of the steps up to the current step that the user would be on and displays each anchor in turn and assigns the appropriate function to the click event:
for (var i = 0; i < profile.current + 1; i++) {
if ($('step_anchor_' + i).innerHTML.empty()) {
var action = profile.steps[i].action;
var dao_id = profile.steps[i].dao_id;
$('step_anchor_' + i).innerHTML = profile.steps[i].anchor;
$('step_anchor_' + i).observe('click', function(){
pm.loadData(action, dao_id, true);
});
Effect.Appear('step_anchor_' + i, {
duration: 1,
delay: (down_delay++)
});
}
}
I know that problem lies in the way that the action and dao_id parameters are being passed in. I've also tried passing profile.steps[i].action and profile.steps[i].dao_id but in that case both profile and i or at least i are out scope.
How do I make it so that I can assign the parameters for action and dao_id correctly for each step? (If it makes any difference we are using Prototype and Scriptaculous)
Your closure scope chain is causing your problems. By declaring the handler function inline, you've created a closure. Obviously you did this to take advantage of the loop.
However, since you have created a closure, you're playing by closure scoping rules. Those rules state that the local variables within the parent function remain active and available as long as the closure exists.
You are trying to pass and then use "action" and "dao_id" to your closure, but you are passing references here, not values. So when your closures (handlers) are called they use the value that the reference was last assigned. In your case, the Step 3 handler.
Closure scoping rules are confusing enough, but you may also be confused by the fact that "action" and "dao_id" are still alive even though the loop block has finished executing. Well, in JavaScript there is no such thing as block scope. Once you declare a variable it is available until the end of the function or until is it deleted. Whichever comes first.
All that said, you need to break the scope chain. Here are two ways to do that:
Try this:
for (var i = 0; i < profile.current + 1; i++) {
if ($('step_anchor_' + i).innerHTML.empty()) {
var action = profile.steps[i].action;
var dao_id = profile.steps[i].dao_id;
$('step_anchor_' + i).innerHTML = profile.steps[i].anchor;
$('step_anchor_' + i).observe('click', function(a, b){
return function(){pm.loadData(a, b, true)};
}(action, dao_id));
Effect.Appear('step_anchor_' + i, {
duration: 1,
delay: (down_delay++)
});
}
}
Or this:
function createHandler(action, dao_id) {
return function(){pm.loadData(action, dao_id, true);};
}
/* snip - inside some other function */
for (var i = 0; i < profile.current + 1; i++) {
if ($('step_anchor_' + i).innerHTML.empty()) {
var action = profile.steps[i].action;
var dao_id = profile.steps[i].dao_id;
$('step_anchor_' + i).innerHTML = profile.steps[i].anchor;
$('step_anchor_' + i).observe('click', createHandler(action, dao_id));
Effect.Appear('step_anchor_' + i, {
duration: 1,
delay: (down_delay++)
});
}
}
First, remember your execution scope in the click event. The this keyword in that context refers to the element being clicked on. Is there any way you can determine the dao_id from the element that is clicked on?

Categories

Resources