ThreeJS, Websockets, and NodeJS Client/Server Experiment - javascript

I was toying with socket.io, ThreeJS, Javascript, and NodeJS to create a simple client/server using ThreeJS's graphics. I wasn't sure if all of these frameworks would even work together, but I decided to give it a shot since I've seen similar examples online before even though I cannot find a simple one to dissect or experiment with. It's mainly to experiment with, but I also wanted to make a small little concept-game as proof of what I've learned so far.
I posted my code here: https://gist.github.com/netsider/63c414d83bd806b4e7eb
Sorry if it's a little untidy, but I did my best to make it as readable as possible.
Basically, right now the server-side NodeJS script seems to run fine (Run with "node server-alpha.js"), and the client script (client-alpha.html, which you can just open in a browser) connects to the server, and displays a list of users (who are also connected). However, my intention was for each user to be able to move his/her own cube around, and right now each cube only gets added to the screen (rather than being added, subtracted, and then added again - to give the illusion of movement). If you run both pieces of code and connected one or two users and move the arrow keys a few times for each, you'll see what I'm talking about.
Can anybody help me with this? I tried several different ways to remove the cube (and remembered to call render(), after each)... but everything I tried didn't seem to work. It always resulted in the cubes just being added to the screen, and never subtracted.
I added comments in the code to make things a little easier, as I know this is quite a bit of code to go through (if it's not your own, anyway).
Thanks, any help would be greatly appreciated... as I'm really stuck trying to make the cubes just move.
Also, I'm having trouble adding the Fly-Controls (FlyControls.js - it's commented out ATM), so if someone could tell me where I went wrong I'd appreciate that a lot also.

Ok so you don't want to keep remaking the cubes, all you need to do is change the position.
Also in game development, it is almost a requirement to use object oriented design, a good way to go about this would be to make a player object, so..
CPlayerList = new Array(); // an array of player objects
function ClientPlayer()
{
this.Cube;
this.Name = "unnamed";
this.Id = 0;
this.Create = function(name,pos,id)
{
this.Name = name;
this.Id = id;
var cubeGeometry = new THREE.BoxGeometry(10, 10, 10);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 'red', transparent:false, opacity:1.0});
this.Cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
this.Cube.position.x = pos.x;
this.Cube.position.y = pos.y;
this.Cube.position.z = 20; // don't know why this is 20, remember y axis is up & down in opengl/threejs
scene.add(this.Cube);
}
this.Move = function(vector)
{
this.Cube.position.set(this.Cube.position.x + vector.x, this.Cube.position.y + vector.y, 20);
}
}
So on the server you need a ServerPlayer object which holds similar data, and assign ids on the server before sending them to the clients. So when you send it to the client you want to make a new ClientPlayer, call player.Create() and then push it to the CPlayerList, like so:
function newCPlayer(data)
{
var newPly = new ClientPlayer();
newPly.Create(data.name,data.pos,data.id);
CPlayerList.push(newPly);
}
Then when you call your movePlayer() function, you can simply loop through your players array
function movePlayer(keyStroke, clientID)
{
if (keyStroke == 39)
{
CPlayerList.forEach(function(player,i,a)
{
if(player.Id === clientID)
{
player.Move(new THREE.Vector3(1,0,0));
}
}
}
}
This is just the client code, but this should help you get started, let me know if there's anything you're unclear on.
Also here's an example of a game using a similar design: http://82.199.155.77:3000/ (ctrl+shift+j in chrome to view client sources) and server code: http://pastebin.com/PRPaimG9

Related

Phaser 3: destroying all instances of a sprite

So i'm trying to make my game unload a bunch of un-used resources. It's proving to be a lot more complicated. here's my code:
var meteor = this.physics.add.group();
this.physics.add.collider(meteor, sput, deathControl, null, this);
meteorSpawnFrequency = 500;
setInterval(spawnMeteor, meteorSpawnFrequency);
var meteorCap = 0;
function spawnMeteor() {
//Create Meteors
meteorCap++;
meteors = meteor.create(Math.floor(Math.random() *800) + 1, 30, "meteor");
//Edit Meteors
meteors.depth = -1;
meteors.setVelocityY(500);
meteors.setScale(Math.floor(Math.random() * 2) + 1);
meteors.setCollideWorldBounds(true);
//Destroy meteors after the cap reaches 10
if(meteorCap > 10){
meteors.destroy();
console.log("meteors destroyed");
meteorCap = 0;
}
}
I'm using an interval to spawn the sprites, and all of it works just fine. What I'm trying to do is set a cap that when 100 meteors are present, they'll get destroyed, and the cap gets set back to 0, to repeat this process.
Only problem is it's not working. How do I get this working?
Almost certainly a scope issue and there isn't enough source code shown to determine the root cause. But meteorCap looks dangerous like a local variable, so reading it from spawnMeteor isn't going to work.
Also, don't use setInterval, there is literally a TimerEvents feature built into the Phaser API for exactly this that is game-step safe and manages the scope context for you. Here is one such an example.

PIXI.js AnimatedSprite lag on first play

I need some help understanding what the best practice is for creating a PIXI.extras.AnimatedSprite from spritesheet(s). I am currently loading 3 medium-sized spritesheets for 1 animation, created by TexturePacker, I collect all the frames and then play. However the first time playing the animation is very unsmooth, and almost jumps immediately to the end, from then on it plays really smooth. I have read a bit and I can see 2 possible causes. 1) The lag might be caused by the time taken to upload the textures to the GPU. There is a PIXI plugin called prepare renderer.plugins.prepare.upload which should enable me to upload them before playing and possibly smoothen out the initial loop. 2) Having an AnimatedSprite build from more than one texture/image is not ideal and could be the cause.
Question 1: Should I use the PIXI Prepare plugin, will this help, and if so how do I actually use it. Documentation for it is incredibly limited.
Question 2: Is having frames across multiple textures a bad idea, could it be the cause & why?
A summarised example of what I am doing:
function loadSpriteSheet(callback){
let loader = new PIXI.loaders.Loader()
loader.add('http://mysite.fake/sprite1.json')
loader.add('http://mysite.fake/sprite2.json')
loader.add('http://mysite.fake/sprite3.json')
loader.once('complete', callback)
loader.load()
}
loadSpriteSheet(function(resource){
// helper function to get all the frames from multiple textures
let frameArray = getFrameFromResource(resource)
let animSprite = new PIXI.extras.AnimatedSprite(frameArray)
stage.addChild(animSprite)
animSprite.play()
})
Question 1
So I have found a solution, possibly not the solution but it works well for me. The prepare plugin was the right solution but never worked. WebGL needs the entire texture(s) uploaded not the frames. The way textures are uploaded to the GPU is via renderer.bindTexture(texture). When the PIXI loader receives a sprite atlas url e.g. my_sprites.json it automatically downloads the image file and names it as mysprites.json_image in the loaders resources. So you need to grab that, make a texture and upload it to the GPU. So here is the updated code:
let loader = new PIXI.loaders.Loader()
loader.add('http://mysite.fake/sprite1.json')
loader.add('http://mysite.fake/sprite2.json')
loader.add('http://mysite.fake/sprite3.json')
loader.once('complete', callback)
loader.load()
function uploadToGPU(resourceName){
resourceName = resourceName + '_image'
let texture = new PIXI.Texture.fromImage(resourceName)
this.renderer.bindTexture(texture)
}
loadSpriteSheet(function(resource){
uploadToGPU('http://mysite.fake/sprite1.json')
uploadToGPU('http://mysite.fake/sprite2.json')
uploadToGPU('http://mysite.fake/sprite3.json')
// helper function to get all the frames from multiple textures
let frameArray = getFrameFromResource(resource)
let animSprite = new PIXI.extras.AnimatedSprite(frameArray)
this.stage.addChild(animSprite)
animSprite.play()
})
Question 2
I never really discovered and answer but the solution to Question 1 has made my animations perfectly smooth so in my case, I see no performance issues.

performance issue with audioreactive visuals made with pixi and p5 sound lib

see the example here
I guess it depends on your machine, but for me, after the first song the framerate just drops like crazy. I make sure there are not more sprites than necessary (4: 2 images and 2 displacement maps).
Is this a pixi thing, perhaps WebGL? I'm not sure how to improve it or where to look for a better performance.
Ok. I found the issue. You are adding displacementTexture to the stage (stage.addChild(displacementTexture) again and again and you are never removing it really. So your totalSpritesOnStage do not work correctly.
How about adding something like this:
if (stage.children.length > 4) {
// let's destroy the sprite now
stage.removeChildren(4);
It would quickly looking seem to work with that too, though I didn't check the functionality very thoroughly.
Also this bothered me personally, as the sounds were downloaded again and again :)
function preload(song) {
console.log('preloading song: ' + currentSong);
console.log(song.filename);
if (allSounds[song]) {
sound = allSounds[song];
sound.setVolume(volume);
sound.play();
return;
}
allSounds[song] = sound = new p5.SoundFile('songs/' + song.filename,
onMusicLoaded,
h.onError
);
// The volume is reset (to 1) when a new song is loaded. so we force it
sound.setVolume(volume);
}

Is it possible to extend a class that already inherits from another class?

I know the direct answer is no here but I've spoken with a couple people who seem to think it's possible. Here is my code:
var Tile = function(xPos, yPos, state) {
this.createTile(xPos, yPos, state);
}
Tile.prototype = new createjs.Shape();
Tile.prototype.createTile = function(xPos, yPos, state) {
// debugger;
this.x = xPos;
this.y = yPos;
this.onState = state;
var fillCommand = this.graphics.beginFill("#969696").command;
var shapeCommand = this.graphics.drawRoundRect(0, 0, 50, 50, 10).command;
this.on("click", function(){this.toggleState(fillCommand);});
stage.addChild(this);
stage.update();
}
Tile.prototype.toggleState = function(fillCommand) {
if (this.onState === true) {
fillCommand.style = "#969696";
stage.update();
this.onState = false;
} else if (this.onState === false) {
fillCommand.style = "#000000";
stage.update();
this.onState = true;
}
}
Shape() is implemented in easeljs if anyone is familiar with it. Basically here I'm creating a Tile class to represent little rounded squares that will change color when they're pressed. Syntax-wise I get no errors nothing displays on the web page. I already have the correct code to call Tile and the canvas already set up or I'd put that code here too. What I'm asking here is, do I have the right idea in this implementation?
Its a little unconventional but if it works for you then more power to you. The more common way to inherit from Shape would be doing something like
Tile.prototype = Object.create(createjs.Shape.prototype);
Tile.prototype.constructor = Tile;
Inheritance in Javascript is a little strange so, unless you have the time and energy to go down a deep rabbit hole, I would recommend just sticking with what's working. If you do feel like reading more this is a great article to get you started: http://ejohn.org/blog/simple-javascript-inheritance/
EaselJS contributor here - you can absolutely extend EaselJS classes. There is actually a tutorial on the EaselJS site for it. As other people have mentioned, JavaScript inheritance is kind of a pain, and not necessarily the best approach (for example, I always compose shapes/sprites instead of inheriting from them. But it is totally possible, just look at the EaselJS hierarchy: EventDispatcher > DisplayObject > Bitmap.
With your example, you should probably use the approach in the tutorial, where you override initialize, and ensure the superclasses version is called. Its a little dirty, but used extensively until EaselJS 0.7.1 (the latest release).
The New Way
Even better though, the latest version of CreateJS (only available in GitHub as the NEXT version, which should be tagged and published to the CDN soon!) has a brand new model for inheritance that is even easier, and does things like automatically injecting super methods, including the ability to call the super constructor.
function MyClass(arg) {
this.EventDispatcher_constructor();
this.doSomethingWith(arg);
}
var p = createjs.extend(MyClass, createjs.EventDispatcher);
// Add new methods
p.doSomethingWith = function() {
// whatever
}
// Override methods
p.addEventListener = function(name, listener) {
this.EventDispatcher_addEventListener(name, listener);
// custom stuff here
}
// Promote super class methods to MyClass
window.MyClass = createjs.promote(MyClass, "EventDispatcher");
This is way cleaner, easier to use, and in our tests works great!
Hope that helps :)

How to organize a Javascript UI?

I need good examples and best practices on program architecture.
I'm trying to build a JS UI for an app that works with Google.Maps. In the 1st draft, user should be able to draw geometric shapes on the map in a way similar to G.M. Then the shapes are sent through AJAX and the response is displayed.
Problem is that the code got complicated just with polygons editing.
Inspired by Joel's "Duct-tape Programmer", I tried to sketch a straightforward code that makes actions and switches event handlers, to avoid big if-else trees. "New poly" button creates an observer for map.onclick, changes event handlers for other buttons or hides them, and hides itself, etc.
The downside of this approach is that data handling code is mixed with interface. A code that creates a div container to display the data on the new polygon stands next to the code that deals w/ G.M or w/ the shape data. If I want to revise the UI, I'll need to process the WHOLE app.
I could review it later and move this UI-generating code elsewhere, but then came my lead programmer. He instead insisted on "messaging" approach: a simple event system where objects subscribe to events and fire them. Interface code can be perfectly isolated from data handling and talking to G.M, but now each listener has to double-check if this message is to it.
For example, clicking on a poly on a map must highlight it and start editing. But not if another poly is being drawn. So, some are-you-talking-to-ME?-code is necessary everywhere.
I'll appreciate good examples of UI architecture approaches.
The event handling idea suggested to you is a good approach.
Here's some more ideas:
Make the shape drawing thing a component
The shape drawing component sends events to other code, to react to stuff like "editing" or "clicked"
This component could also handle the editing part - It sends "clicked" event to controller, controllers tells the component to go into edit mode
While in edit mode the component would not send normal "clicked" events until the editing was closed, avoiding your problem of having to check
In general, it's a good idea to have a "view" layer which simply deals with displaying the data and sending events about user actions on that data (ie. clicks, etc.) to a "controller" layer, which then decides what to do - for example it could decide to change the view into editing mode.
I don't know if this is beside the point. But I use this as a temple for all my javascript projects.
(function () {
var window = this,
$ = jQuery,
controller,
view,
model;
controller = {
addEventForMenu : function () {
// Add event function for menu
}
};
view = {
content : {
doStuff : function () {
}
},
menu : {
openMenuItem : function () {
}
}
};
model = {
data : {
makeJson : function () {
// make json of string
},
doAjax : function () {
},
handleResponse : function () {
}
}
}
$.extend(true, $.view, view);
})();
The good thing here is that it's only the view object that is extended to the DOM, the rest is kept inside the anonymous scope.
Also in bug project i create on of these files for each part ie, map.js, content.js, editor.js
If you just mind the naming of your methods in the view object you can have as many files as you like during development. When the project is set in to a production environment I just make it one file and minify it.
..fredrik
In short publisher-subscriber paradigm works well to make geometric shapes. First make command-line which primitive is base polygon publisher publishes. Canvas object seems obvious here to paint, usual method repaint() for updating the client view (eventdriven programming normally in C you can review eg opengl or glut eventdriven graphics), combined with the so-so gmap API I too use, publisher-subscriber pattern or factory are good design patterns whatever graphics implementation. tricky gmaps specific thing is latitude and longitude switch places in the array between json and persistence layer, there's no serverside reverse geocoding yet, naming is kinda inconsistent, and for multilingua apps names both change relative user human language and are doubled (Paris in Text, Paris in France...),. Look if you like my going implementation, registers geographic names and spatial coordinated relative user with geoip worldwide
function wAdd(response){
map.clearOverlays();
if(!response||response.Status.code!=200){
}
else{
try{
place=response.Placemark[0];
point=new GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);
marker=new GMarker(point);
map.addOverlay(marker);
marker.openInfoWindowHtml('<a href="/li?lat='+place.Point.coordinates[1]+'&lon='+place.Point.coordinates[0]+'&cc='+place.AddressDetails.Country.CountryNameCode+'">'+place.AddressDetails.Country.AdministrativeArea.Locality.LocalityName+'<span id="wr2"></span> '+ nads( place.Point.coordinates[1],place.Point.coordinates[0] )+' ' +'<img src="http://geoip.wtanaka.com/flag/'+place.AddressDetails.Country.CountryNameCode.toLowerCase()+'.gif">');
}
catch(e){
try{
place=response.Placemark[0];
point=new GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);
marker=new GMarker(point);
map.addOverlay(marker);
marker.openInfoWindowHtml('<a href="/li?lat='+place.Point.coordinates[1]+'&lon='+place.Point.coordinates[0]+'&cc='+place.AddressDetails.Country.CountryNameCode+'">'+place.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName+'<span id="wr2"></span> '+ nads( place.Point.coordinates[1],place.Point.coordinates[0] )+' ' +'<img src="http://geoip.wtanaka.com/flag/'+place.AddressDetails.Country.CountryNameCode.toLowerCase()+'.gif">');
}
catch(e){
try {
place=response.Placemark[0];
point=new GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);
marker=new GMarker(point);
map.addOverlay(marker);
marker.openInfoWindowHtml('<a href="/li?lat='+place.Point.coordinates[1]+'&lon='+place.Point.coordinates[0]+'&cc='+place.AddressDetails.Country.CountryNameCode+'">'+place.AddressDetails.Country.CountryName+'<span id="wr2"></span> '+ nads( place.Point.coordinates[1],place.Point.coordinates[0] )+' ' +'<img src="http://geoip.wtanaka.com/flag/'+place.AddressDetails.Country.CountryNameCode.toLowerCase()+'.gif">');
}
catch(e){
place=response.Placemark[0];
marker=new GMarker(point);
map.addOverlay(marker);
marker.openInfoWindowHtml(''+place.address+'');
}
} }
}map.addOverlay(geoXml);
}
i would recommend having few object variables containing the state (0, drawing, editing, ... any other required) - this would help you deciding whether to allow event handling or just bury it if for example drawing is done and clicking on editable polygone happens.
as for the UI - I am not sure if your question is aimed at you - developing the script or at the user as you are mixing two things here.
keep in mind that for a user everything should be as simple as possible: if he is editing, don't allow him to draw. if he is drawing, don't allow him to edit (overlapping of polygons could occur). however - allow user quickly to switch from editing (e.g. right click?) to drawing - or in other words cancel the current state.
The first thing I would do is create a service that wraps over the google API. This is so that later if you need to change mapping services (windows maps or yahoo maps). Then you can put a facade over the google service. Then you might want to put some wrappers over your service and split it up into a view(output) and model(input) and manage this with controllers/presenters. Check into Model View Controller / Model View Presenter / Presenter First / Humble Dialog on Wikipedia. It should discuss the seperation that your looking for. Also Martin Fowler web page goes into presentation patterns. You should check out my old blog ugly-lisp-code. I have references to event driven/event messaging.
If you have a one-to-one pub/sub just store an event-handler(closure/lambda/first-order-function) in the object that is going to fire the event.
If you have a one-to-many pub/sub then you will need a more complex object to store your closures.
LOL! Right now I've been looking at this same exact issue. I'm going to be writing about using a presenter-first in JavaScript on my blog. A bare bones start on presenter and model.
[edit] you might want to check out this stackoverflow question. One of the answer has a link to separating concerns into MVC. The link is on A List Apart.

Categories