How to modify object from updates and remove old keys? - javascript

I am creating a multiplayer game and I have an object in javascript, with a number of keys and values.
This object is called players, for holding information about each player that is connected to the game server.
name is the key of the object, and then the value of the object is a Player object which holds information such as x, y, level, etc.
Constantly I am sending a request to the server to get updated information about the players.
Because this is happening very often, I don't want the players object to be reset every time (players = {}), so instead, I am updating the object with any new information.
At the moment I am checking if name in players, and if so, I update the object like this:
players[name].x = x;
players[name].y = y;
// etc.
Otherwise, I simply create a new Player object, with the information and add it to the players object. (If a new player connected for instance)
The problem is, if a player that is already in players is no longer in the updated information from the server (i.e the player disconnected), how do I go about removing them from the object.
Is it necessary to loop trough players, and if the player is no longer in the updated information, remove it from the object, or is there any simpler way of doing this?
If there is no other way, is it a better approach to just reset the dictionary and add the data? It feels like that isn't the best way to do something simple like this.
Here is my code so far:
var newplayers = new info from server;
for(var i=0; i<newplayers.length; i++)
{
var pl = newplayers[i];
var name = pl.name;
var x = pl.x;
var y = pl.y;
// etc.
if(name in players)
{
players[name].x = x;
players[name].y = y;
// etc.
} else
newplayer = new Player();
newplayer.x = x;
newplayer.y = y;
// etc.
players[name] = newplayer;
}
}
// What if the player is no longer in the updated info, but still in players?
All help appreciated! Thanks in advance!

So you have a choice between removing outdated data from your players dictionary or rebuild it from scratch every time?
I think the answer depends a lot on how much data you have. If you have at most 20 players, it probably doesn't matter too much. If you have 1 million players it's different.
If you want to be sure, the best thing to do would be to measure it. Try both solutions with the biggest number of players you want to be able to handle and see what the impact on performance is.
Or just go with the simplest implementation and see if it's good enough for your purpose. No point in optimising before you need it.
Personally I'd just loop through players to remove the outdated data. If the performance is not good enough, then I'd optimise.

Related

Variable set to HTML object changes with the object

I am making a simple chess game in Vanilla JS, and am trying to implement an undo move function where, when you press a button, you can take back your move. There are currently two buttons, one to undo your move, and the other to submit your move and pass the game over to your opponent. Simple functionality of most chess games.
Right now the way this works is by assigning a variable to the dynamically generated 8 by 8 board HTML Object before the player makes a move (so let previousState = board where board is const board = document.getElementById("board")). So, before the player even touches the game, the current state of the board is saved.
Here's the JS:
function drawBoard() {
// Two loops one for even rows one for odd inside of another loop so all rows are drawn
let isWhite = false
let idSwitch = 0
for (let p = 1; p <= 4; p++) {
let oddRow = document.createElement("div")
let evenRow = document.createElement("div")
for (let i = 1; i <= 8; i++) {
idSwitch++
let square = document.createElement("div")
square.classList.add("square")
square.setAttribute("id", `square${idSwitch}`)
if (isWhite) {
isWhite = false
} else {
square.classList.add("black")
isWhite = true
}
oddRow.appendChild(square)
}
board.appendChild(oddRow)
for (let q = 1; q <= 8; q++) {
idSwitch++
let square = document.createElement("div")
square.classList.add("square")
square.setAttribute("id", `square${idSwitch}`)
if (isWhite) {
square.classList.add("black")
isWhite = false
} else {
isWhite = true
}
evenRow.appendChild(square)
}
board.appendChild(evenRow)
}
}
And the HTML:
<section id="board-container">
<div id="board"></div>
</section>
for reference.
Then, when the player makes a move, the variable is supposed to stay exactly the same, so that, if they want to revert to the original, the variable is still there for them.
So, after they move, they have two options. Either pass the move to their opponent, at which point the previousState variable gets reassigned to the new state of the board (i.e. How the board is after the aforementioned move), or, and herein lies the rub, they can press undo, and the board will revert to how it was before they made their turn (i.e. What the board var is set to).
Simple right? You assign a var before they go and then revert the board back to it if they want to take the move back. Otherwise, reset the var to how the board looks after they go. Rinse and repeat.
The only problem is that, for some reason, the variable changes somewhere between when it is initially defined and after the player moves. What happens is that the player goes, and the variable gets logged to the console. It has the parent element board and then all the rows and squares, and then all the pieces in their correct posistions in the squares. Then the player moves, and the variable is logged out again, but now that the board element, now that the actual HTML Object, is different, and, for some reason, the variable, which was never reassigned, mirrors how the board now looks. I do not understand why, as it should just stay the same, and not update in tandem with the board. Why does it?
Here's a codepen to check it out yourself:
https://codepen.io/jacklouden/pen/qBaLPdo.
Thank you!
Are you familiar with the concepts of variables being by reference or by value? I'm guessing that's your issue.
There is a good SO post on it already.
JavaScript by reference vs. by value
I'll take a direct quote from that post to answer this here.
"
Javascript is always pass by value, but when a variable refers to an object (including arrays), the "value" is a reference to the object.
Changing the value of a variable never changes the underlying primitive or object, it just points the variable to a new primitive or object.
However, changing a property of an object referenced by a variable does change the underlying object."
Try making a copy of your html object and hiding it. I didn't deep dive into what you're doing, but you are doing DOM manipulation, you need to create a new DOM element containing the state. This should give you the reset capability you are looking for.

variable from js to html to save value/lose value of max after dying and restarting game scene/phaser-js

So the question I have to make is the following; My project Structure is:
index.html
StartScene.js
GameScene.js
game.js
2 pictures(png) for media files
So I'm making some games with phaser. In my game scene, I have a variable named max which is the max of an array that stores all the scores as the game progresses and the player gets better scores, etc.
So it's inside the function create() of my GameScene class. The problem is every time the player dies I do this.scene.restart(), so all the variables lose their values and my max is not the max from the previous run but 0.
Is there a way to fix this?
(I thought of storing the value to the index.html file then writing a script making an array with all the values and then finding max of that array and printing it out.)
THNX IN ADVANCE!
Easiest thing is probably rather than making max a member of the scene you could just have it be global on window. So something like
if(window.max === undefined) {
window.max = 0
} else if(score > window.max) {
window.max = score;
}
In javascript window is a global object.
You could use registry's data manager to exchange data between scenes and set your max score just before you restart the Game's one.
Initialize in your StartScene's create your max value:
this.registry.set('max', 0);
Set your max score just before restarting:
this.registry.set('max', this.score);
this.scene.restart()
Check the Registry where needed for changes and hit the callback
this.registry.events.on('changedata', this.doStuff, this);
Registry data exchange example on docs.
Here's also Rex's plugin for restorable data you can take a look at (among many other good things).

Web audio API equalizer

I have been looking around for creating an audio equalizer using the Web audio API: http://webaudio.github.io/web-audio-api/
I found a lot of threads about creating a visualizer, but that is of course not what I want to do. I simply want to be able to alter the sound using frequency sliders. I found that the biquadFilter should do the work, but I can't get a good result. The sound is altered consistently when I change any frequency value, but it just lowers the quality of the sound while it should alter the frequencies.
I first load a sound:
Audio.prototype.init = function(callback){
var $this = this;
this.gainScale = d3.scale.linear().domain([0,1]).range([-40,40]);
this.context = new AudioContext();
this.loadSounds(function(){
$this.loadSound(0);
$this.play();
callback.call();
});
};
Everything works well, the sound plays when ready.
I have 10 sliders for frequencies [32,64,125,250,500,1000,2000,4000,8000,16000].
For each slider I create a filter and I connect it to the source, as is described here: Creating a 10-Band Equalizer Using Web Audio API :
Audio.prototype.createFilter = function(index,frequency){
if(this.filters == undefined) this.filters = [];
var filter = this.context.createBiquadFilter();
filter = this.context.createBiquadFilter();
filter.type = 2;
filter.frequency.value = frequency;
// Connect source to filter, filter to destination.
this.source.connect(filter);
filter.connect(this.context.destination);
this.filters[index] = filter;
};
Finally, when I change the value of a slider I update the filter:
Audio.prototype.updateFilter = function(index,newVal){
this.filters[index].frequency.gain = this.gainScale(newVal);
};
NB: my this.gainScale function takes as input a value in [0,1] and returns a value in [-40,40] to set the gain between -40 and 40 for each frequency.
Would appreciate any help !
Multiple things here.
1) You shouldn't use bandpass filters in parallel to implement an equalizer. Among other issues, biquad filtering changes the phase of different parts of the signal, and therefore the different bands will end up in different phases, and you'll have some potentially quite bad effects on your sound when it recombines.
2) The approach that you want is have a low shelf filter on the bottom end, a high shelf filter on the top end, and any number of peaking filters in the middle. These should be connected in series (i.e. the input signal connects to one filter, which connects to another filter, which connects to another filter, et al, and only the final filter should get connected to the audiocontext.destination. The Q values should be tuned (see below), and the gain on the filter determines the boost/cut. (For flat response, all filter gains should be set to zero.)
3) filter.type is an enumerated type that you should set as a string, not as a number. "lowshelf", "highshelf" and "peaking" are the ones you're looking for here.
You can see an example of a simple three-band equalizer in my DJ app - https://github.com/cwilso/wubwubwub/blob/MixTrack/js/tracks.js#L189-L207 sets it up. To modify this into a multiband equalizer, you'll need to tweak the Q value of each filter to get the bands to not overlap too much (it's not bad if they do overlap, but your bands will be more precise if you tune them). You can use http://googlechrome.github.io/web-audio-samples/samples/audio/frequency-response.html to examine the frequency response for a given Q and filter type.
One issue is that you want your sliders to be controlling the gain of the filter at a given frequency, not the filter frequency itself. According to the spec the gain of a bandpass filter is not controllable which is a bit limiting. Fortunately you can put a gain node at the end of each filter.
var filter = this.context.createBiquadFilter();
filter = this.context.createBiquadFilter();
filter.type = 2;
filter.frequency.value = frequency;
var gain = this.context.createGainNode();
// Connect source to filter, filter to the gain, gain to destination.
this.source.connect(filter);
filter.connect(gain);
gain.connect(this.context.destination);
this.filters[index] = filter;
this.gains[index] = gain;
Next you'll need to connect your slider up to the gain parameter of the gain control. I don't really know web audio so I'll leave that to you. The last thing is that you need to to specify the Q of the filter. I get the impression from your list of frequencies that you're trying to create octave wide filters so the Q factor is probably going to be around 1.414. You're really going to need to do a bit of research if you want to get this right.

Create a new collection from an existing one backbone.js

I have a view that should render models for the player who has the maximum points between all the teams. There are many ways to do this but here is the path I am leading down.
getMax : function(attribute) {
return this.collection.max(function (team) {
//return team.get('players').get(attribute);
var test = new PlayersCollection(team.get('players'));
console.log(test)
}, this);
},
This is in a marionette collectionView for teams (well composite, but it works like a collection). I understand why test returns the players for each team, but I can't think of a way to merge all the players into one collection then query who is the max points leader.
That said I may be able to avoid merging them in the first place if there is a way to determine who is the leader, but since the collection is nested I am a little stumped.
Since this.collection are the Teams, I thought something like this.collection.get('players').get('points') would allow me to get the max value of all the teams, but that didn't work.
Weird solution 1 I did a little hacking and came up with this. Alot of problems with this because Its stripped of backbone functionality meaning I cant return the model of the max player, only the points of that player, thats it.. still thinking (brain bleeding lol)
teams = App.data.teams
var points1 = teams.get('5368dcc1227a937829b2cb4a').players.pluck('points')
console.log(points1)
var points2 = teams.get('5368dcd9227a937829b2cb4c').players.pluck('points')
console.log(points2)
var test = points1.concat(points2);
console.log(test)
var maxi = _.max(test);
console.log(maxi)
Slightly better solution 2 merging the object arrays
teams = App.data.teams
var home = teams.get('5368dcc1227a937829b2cb4a').players.models;
var away = teams.get('5368dcd9227a937829b2cb4c').players.models;
all = home.concat(away);
console.log(all)
I think what you are looking for is something like this:
_.max(this.collection.get('players').pluck('points'));
Okay so I think I managed to create a somewhat elegant solution, playing in the console can really teach you a lot (highly recommended if you want to get better).
teams = App.data.teams
var home = teams.get('5368dcc1227a937829b2cb4a').players.models;
var away = teams.get('5368dcd9227a937829b2cb4c').players.models;
all = home.concat(away);
leaders = new PlayersCollection(all)
function mostPoints() {
return leaders.max(function(leader) {
return leader.get('points');
});
}
mostPoints();
Now the function will return the model of the player who has the most points out of everyone, pretty cool!

Box2dWeb call function when objects collide

I'm fairly new to javascript and box2d, i was wondering if someone knows how i can call a custom function when two objects collide. I tried using some examples that uses the b2ContactListener without any succes. I've placed an object above another and let the standard Box2d physics do it's thing.
I recieve two console outputs, the first is null and the second is Ball with the following code:
var listener = new Box2D.Dynamics.b2ContactListener;
listener.BeginContact = function(contact) {
console.log(contact.GetFixtureA().GetBody().GetUserData());
console.log(contact.GetFixtureB().GetBody().GetUserData());
};.
The two objects that need to collide are a b2_dynamicbody (ball) and a b2PolygonShape. (rectangle). Using bodyDef.userData = "Ball"; in my Ball.js and bodyDef.userData = "Mouse"; in my Mouse.js i try to identify if they are hit. Instead only the ball is displayed.
Next to that i'm sure this is not the correct way for detecting collision :P I hope i've explained it well enough, could somebody steer me in the right direction?
Ok I solved it myself, apparently I had to add my custom event to the world I create with box2d. So, the issue was solved by me reading big chunks of box2d documentation/manual which can be found here:
I started with adding a String as UserData() to every object which can collide and has to do something else next to just colliding. Using the following code:
bodyDef.userData = "Car";
note: every object has to have it's own unique string.
Then I created a new contact listener (formulated in my question above) and listened for fixtures colliding with each other. When that happens, I 'save' the fixtures UserData() in variables which I can then use to look what objects collide with each other.
var contactListener = new Box2D.Dynamics.b2ContactListener;
contactListener.BeginContact = function(contact) {
var fixA = contact.GetFixtureA().GetBody().GetUserData();
var fixB = contact.GetFixtureB().GetBody().GetUserData();
// if else statement here...
};
world.SetContactListener(contactListener);
Finally, I added the last statement world.SetContactListener(contactListener); to add the event to the world, making it possible for it to listen to collisions, which I forgot to add and thus was my problem.
Hope someone finds this usefull!

Categories