I am attempting to make a little application where multiple people can move a square around with their arrow keys with the help of Socket.IO.
For my backend I am using Sails.JS and on frontend mostly Angular.
Backend:
var remotePlayers = [];
var findPlayerById = {};
var Player;
function LocalPlayer(x, y, id) {
this.x = x;
this.y = y;
this.id = id;
}
socket.on('new player', function(data) {
// data = user.id;
socket.id = data;
LocalPlayer = new LocalPlayer(defaultX,defaultY, socket.id);
remotePlayers.push(LocalPlayer);
for (var i = 0; i < remotePlayers.length; i++) {
findPlayerById[remotePlayers[i].id] = remotePlayers[i];
};
Player = findPlayerById[socket.id];
console.log(Player);
// Emit to all clients
sails.sockets.blast('player joined', remotePlayers);
})
What I am trying to do here is when the 'new player' emit is triggered, I want to save that player to my a Array(remotePlayers). Then I want to emit that Array back to the client, so the client can draw all the players.
On the client side I have a switch case where I check what key was pressed, I emit this keyValue and a userId to the Server.
Server then make sure the move is valid, does some calculations and returns the updated position of the Player.
socket.on('move player', function updatePlayerPosition(data) {
// data = Key and userId
switch(data.key) {
case 37:
// Some checks here then set new value
Player.x = Player.x - 10;
sails.sockets.blast('player moved', remotePlayers);
break;
case 38:
Then after I have emitted 'player moved', I want my client to draw and display the updated position of the Player.
Client:
SocketService.on('player joined', function(data) {
// data = {x: 50, y:50, id: 'random string'}
draw(data);
})
function draw(data) {
ctx.clearRect(0, 0, HEIGHT, WIDTH);
for (var i = 0; i < data.length; i++) {
// data[i].x = 30
ctx.fillStyle = 'black';
ctx.fillRect(data[i].x, data[i].y, 20, 20);
};
}
Problem: When I open two browsers and try to move my Player(rectangle) it always selects the same rectangle to move. If i use one browser it works, then i open up another one(2 browsers) and both browsers will then move the 2nd browsers rectangle. Does that make any sense?
Player is always the last client to join. What am I missing here? I am pretty new to this so it could be plenty I'm afraid :) Need more code? Just ask and I'll add.
Any suggestions, ideas or links are much appreciated!
Related
This question already has answers here:
socket.io - emited objects from server lose their functions and name once received by client
(1 answer)
Socket.io passing javascript object
(2 answers)
Closed 5 years ago.
For an online board game I am making, I have defined a Tile class, whose instances I use to create a "map". The Tile class has several properties, and a method called show which displays the instance on the canvas.
All the scripts are passed on to the clients as static files via script tags in the index.html which is served by node.js. The instantiation of the Tile class worked properly before I introduced the server. Now it produces some very weird and interesting results.
The HexMap class, creates upon instantiation a 2d array of Tiles as follows:
function HexMap (width, height, image) {
this.width = width;
this.height = height;
this.contents = [];
for (let i = 0; i < this.width; i++) {
this.contents[i] = [];
for (let j = 0; j < this.height; j++)
this.contents[i].push(new Tile(i, j, size, image, "SEA"));
}
}
The code for the Tile class is this:
var Tile = function (row, column, side, image, type) {
Vector.call(this, row, column);
this.unit = null;
this.canvas = null;
this.side = side;
this.type = type;
this.startingPoint = new Vector(this.x*Math.sqrt(3)*this.side + (this.y& 1) * this.side*Math.sqrt(3)/2, this.side*1.5*this.y);
this.middlePoint = new Vector(this.startingPoint.x + Math.sqrt(3)*this.side/2, this.startingPoint.y + this.side/2);
this.getOffset = function () {
return {
"SEA" : 0,
"SHORELINE" : 60,
"PLAINS" : 120,
"FOREST_LIGHT" : 180,
"FOREST_HEAVY" : 240,
"MOUNTAINS" : 300,
"SWAMP" : 360,
"MARSH" : 420
}[this.type];
};
this.getVector = function () {
return new Vector(this.x, this.y);
};
this.show = function (context) {
if(!this.canvas){
this.canvas = makeTemplate(side, this.type, image);
}
context.drawImage(this.canvas, this.startingPoint.x - this.getOffset(), this.startingPoint.y);
};
this.getPixelX = function () {
return this.x*Math.sqrt(3)*this.side + (this.y & 1) * this.side;
};
this.getPixelY = function () {
return this.side/2 + this.side*2*this.y;
};
this.setType = function (type){
this.type = type;
};
};
Printing a Tile object in the console would normally display something like this:
Tile {x: 0, y: 0, unit: null, canvas: canvas, side: 15, …}
I tried the same thing using the server this time, and the result is this:
{x: 0, y: 1, unit: null, canvas: null, side: 15, …}
Interestingly enough, the result is indeed an object, but not a Tile object. It has all the properties a Tile object has, but has none of its methods.
The error I end up getting is this:
tile.show is not a function
The map object is created and transferred to the server via socket.io sockets. The following piece of code runs on the client (the guest).
function selectGame(id) {
var hexmap = new HexMap(rowspan, colspan, spritesheet);
hexmap.generateIsland();
socket.emit('game-select', {id: id, hexmap: hexmap});
}
The server then receives the map:
socket.on('game-select', function (data) {
//The 2 sockets corresponding to the players join the same room
io.sockets.connected[data.id].join(games[data.id].identifier);
io.sockets.connected[socket.id].join(games[socket.id].identifier);
//The start-game event is emitted for just the sockets of the same room
io.to(games[socket.id].identifier).emit('start-game', {
map: data.hexmap
});
});
Then both the clients receive it again:
socket.on('start-game', function (data) {
let el = document.getElementById("wrapper");
el.parentNode.removeChild(el);
hexmap = data.map;
contexts = setupCanvas();
displayMap(hexmap, contexts.mapContext);
});
What displayMap does is it iterates through the contents of the map, and displays each Tile using its show method.
I cannot pin down the problem no matter how hard i try... Any suggestion on how to solve this?
I'm trying to create objects on my game update and move them. This is my banana object:
function Banana() {
this.height = 1.96;
this.width = 3.955;
this.pos_x = CENTER - this.width/2;
this.pos_y = -475;
this.banana_image = banana_image;
};
And this is the Move method:
Banana.prototype.move = function(){
if (this.pos_y > 500) {
//this.banana_image.parentElement.removeChild(this.banana_image);
}
this.height += ZOOM_RATE;
this.width += ZOOM_RATE;
this.pos_y += 3;
this.pos_x -= SIDES_RATE;
};
This is the Game Update part:
Game.update = function() {
this.player.move();
//creating bananas
if (objs.lenght <= 0) {
this.banana = new Banana();
} else {
for (i = 0; i < 10; i++) {
objs.push(new Banana());
}
}
//moving bananas
for (i = 0; i < objs.lenght; i++) {
this.objs[0].move();
}
};
Game Draw:
function Game.draw = function() {
this.context.drawImage(road, 0,0, rw, rh);
this.context.drawImage(
this.player.player_image,
this.player.pos_x, this.player.pos_y,
this.player.width, this.player.height);
this.context.drawImage(
this.banana.banana_image,
this.banana.pos_x, this.banana.pos_y,
this.banana.width, this.banana.height);
};
I tried to ask this to multiple people, but I can't find an answer for it.
Let's say you want to move the objects 10 times and then stop.
First you need to add a line to the start of Game.draw, so that it clears the canvas making you always start drawing from scratch:
this.context.clearRect(0,0,500,500); // clear canvas, adjust box size if needed
Then make a function to call both update and draw, and queue that function to be called again:
var count = 10;
function updateAndDraw() {
Game.update();
Game.draw();
count--;
if (count) requestAnimationFrame(updateAndDraw);
}
// start moving:
requestAnimationFrame(updateAndDraw);
The movement may go too fast to your liking, so then adjust the move method to make smaller changes, or use setTimeout instead of requestAnimationFrame (but that will make the animation less fluent).
Note that you have a few errors in your code, which you will need to fix first:
lenght should be length
function Game.draw = function() {: remove function before Game.draw.
... check the error messages you get in console.
i have problem with my js canvas game. project seems mad god game. (rotmg in google)
i use php socket to communication. when player change possition i send updated x and y:
function keysPressed(e) {
keys[e.keyCode] = true;
if (socket_open) {
var msgg = {
type : 'char_pos',
px : pixX,
py : pixY
};
websocket.send(JSON.stringify(msgg));
}
}
server part:
//check for any incomming data
while(socket_recv($changed_socket, $buf, 1024, 0) >= 1)
{
$received_text = unmask($buf); //unmask data
$msg = json_decode($received_text); //json decode
$id=array_search($changed_socket,$clients);
$pos[$id]['x']=$msg->px;
$pos[$id]['y']=$msg->py;
//prepare data to be sent to client
$response_text = mask(json_encode(array('type'=>'rys_inni', 'id'=>$id, 'pos'=>$pos)));
send_message($response_text); //send data
break 2; //exist this loop
}
next, when data come back i do this:
websocket.onmessage = function (ev) {
var msg = JSON.parse(ev.data);
var type = msg.type;
if (type == 'rys_inni') {
tab_innych_charow = [];
for (key in msg.pos)
if (typeof(msg.pos[key]) !== 'undefined')
tab_innych_charow.push(msg.pos[key]);
}
};
and this is in interval:
function rysuj() {
// clear canvas and bckground
for (i = 0; i < tab_innych_charow.length; i++) {
var x = tab_innych_charow[i].x; // +...;
var y = tab_innych_charow[i].y; // +...;
draw(ctx, something, x, y);
}
}
the problem is that the different players drawing with lags and my game look poor.
where i do mistake ? is the problem lies in php? i read about socket.io but i would rather php cause it is easier for me, i think. in my model (realm of the mad god) move is very fast and smooth. i don't know what to do. you are my last chance. thanks.
I'm trying to create an online web tool for eeg signal analysis. The tool suppose to display a graph of an eeg signal synchronize with a movie that was display to a subject.
I've already implemented it successfully on csharp but I can't find a way to do it easily with any of the know javascript chart that I saw.
A link of a good tool that do something similar can be found here:
http://www.mesta-automation.com/real-time-line-charts-with-wpf-and-dynamic-data-display/
I've tried using dygraph, and google chart. I know that it should be relatively easy to create an background thread on the server that examine the movie state every ~50ms. What I was not able to do is to create a marker of the movie position on the chart itself dynamically. I was able to draw on the dygraph but was not able to change the marker location.
just for clarification, I need to draw a vertical line as a marker.
I'm in great suffering. Please help :)
Thanks to Danvk I figure out how to do it.
Below is a jsfiddler links that demonstrate such a solution.
http://jsfiddle.net/ng9vy8mb/10/embedded/result/
below is the javascript code that do the task. It changes the location of the marker in synchronizing with the video.
There are still several improvement that can be done.
Currently, if the user had zoomed in the graph and then click on it, the zoom will be reset.
there is no support for you tube movies
I hope that soon I can post a more complete solution that will also enable user to upload the graph data and video from their computer
;
var dc;
var g;
var v;
var my_graph;
var my_area;
var current_time = 0;
//when the document is done loading, intialie the video events listeners
$(document).ready(function () {
v = document.getElementsByTagName('video')[0];
v.onseeking = function () {
current_time = v.currentTime * 1000;
draw_marker();
};
v.oncanplay = function () {
CreateGraph();
};
v.addEventListener('timeupdate', function (event) {
var t = document.getElementById('time');
t.innerHTML = v.currentTime;
g.updateOptions({
isZoomedIgnoreProgrammaticZoom: true
});
current_time = v.currentTime * 1000;
}, false);
});
function change_movie_position(e, x, points) {
v.currentTime = x / 1000;
}
function draw_marker() {
dc.fillStyle = "rgba(255, 0, 0, 0.5)";
var left = my_graph.toDomCoords(current_time, 0)[0] - 2;
var right = my_graph.toDomCoords(current_time + 2, 0)[0] + 2;
dc.fillRect(left, my_area.y, right - left, my_area.h);
};
//data creation
function CreateGraph() {
number_of_samples = v.duration * 1000;
// A basic sinusoidal data series.
var data = [];
for (var i = 0; i < number_of_samples; i++) {
var base = 10 * Math.sin(i / 90.0);
data.push([i, base, base + Math.sin(i / 2.0)]);
}
// Shift one portion out of line.
var highlight_start = 450;
var highlight_end = 500;
for (var i = highlight_start; i <= highlight_end; i++) {
data[i][2] += 5.0;
}
g = new Dygraph(
document.getElementById("div_g"),
data, {
labels: ['X', 'Est.', 'Actual'],
animatedZooms: true,
underlayCallback: function (canvas, area, g) {
dc = canvas;
my_area = area;
my_graph = g;
bottom_left = g.toDomCoords(0, 0);
top_right = g.toDomCoords(highlight_end, +20);
draw_marker();
}
});
g.updateOptions({
clickCallback: change_movie_position
}, true);
}
I have a memory leak in my code and I cannot figure out what is causing it. I am receiving target data from a WebSocket which contains basically an ID and an X,Y coordinate. The data is passed to a HandleData function which creates a circle for each target and a line (which is updated) to show where the target has moved from/to.
If a target no longer appears in the WebSocket stream it is removed. On testing I have found the webpage quickly amasses hundreds of mb of data in spite of me removing these items. Upon using Chrome's memory profiler it seems Raphael (or something) is holding onto all the path information in spite of me deleting it.
If anyone can help me in anyway I would be very grateful. It does seem that Raphael is holding onto the data but there is every chance I have made a mistake somewhere in my code :)
var arrayColours = ["#f33", "#3f3", "#33f", "#f66", "#6f6", "#66f"];
var targetStructArray = [];
function HandleTargetData(data) {
//Go through all our existing targets and mark them as not updated
for (var i = 0; i < targetStructArray.length; i++) {
targetStructArray[i].updated = false;
}
for (var i = 0; i < data.targets.length; i++) {
var targetData = data.targets[i];
var targetStruct = undefined;
//find our targetStruct
for (var j = 0; j < targetStructArray.length; j++) {
if (targetStructArray[j].id == targetData.id) {
targetStruct = targetStructArray[j];
break;
}
}
//If it doesnt exist, create it
if (!targetStruct) {
var path = paper.path();
path.attr({ "stroke-width": "2", "stroke": "#888" });
path.addPart(['M', targetData.x, targetData.y]);
var circle = paper.circle(targetData.x, targetData.y, 15, 15).attr({
stroke: "none",
fill: arrayColours[Math.floor(Math.random() * arrayColours.length)] //random colour
});
//Create our struct
targetStruct = {
circle: circle,
path: path,
id: targetData.id,
updated: false
};
targetStructArray.push(targetStruct);
}
else {
targetStruct.circle.attr({ cx: targetData.x, cy: targetData.y });
targetStruct.path.addPart(['L', targetData.x, targetData.y]);
}
//ensure we are set as updated
targetStruct.updated = true;
}
//Go through all our existing targets and delete any that werent updated
for (var i = targetStructArray.length - 1; i >= 0; i--) {
if (!targetStructArray[i].updated) {
targetStructArray[i].circle.remove();
targetStructArray[i].path.remove();
targetStructArray[i].circle.removeData();
targetStructArray[i].path.removeData();
targetStructArray[i].circle = null;
targetStructArray[i].path = null;
targetStructArray[i] = null;
targetStructArray.remove(i);
}
}
}
I use two functions not listed here which are John Resig's Array.Remove and a Raphael helper function from a different question