Object instance is stripped of its methods [duplicate] - javascript

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?

Related

Phaser 3 : Add arguments to callback functions (time event, keyboard event)

I'm developing a game on Phaser 3 and I'm currently struggling on callback use.
I have created two classes, one called "Entitee" and the other one called "objet"
class Entitee extends Phaser.Physics.Arcade.Sprite{
constructor(scene, x, y, pv, mana, speed, poise, asset){
//Private
var _pv = pv;
var _pvMax = pv;
var _mana = mana;
var _manaMax = mana;
var _speed = speed;
var _poise = poise;
var _asset = asset;
super(scene, x, y, _asset);
scene.add.existing(this);
scene.physics.world.enableBody(this);
//Public
this.hurt = function(objet){
console.log(_pv);
console.log(objet);
_pv += objet.getPvMod();
console.log(_pv);
}
}//END CONSTRUCTOR
}
class Objet {
constructor(name, price, range, cd, ammo, lag, pvMod, manaMod, poiseMod, speedMod, animAtt, animZone){
var _name = name;
var _price = price;
var _range = range;
var _cooldown = cd;
var _ammo = ammo;
var _direction;
var _lag = lag;
var _pvMod = pvMod;
var _manaMod = manaMod;
var _poiseMod = poiseMod;
var _speedMod = speedMod;
var _animAtt = animAtt;
var _animZone = animZone;
this.getName= function(){ return _name};
this.getPrice= function(){ return _price};
this.getRange= function(){ return _range};
this.getCooldown= function(){ return _cooldown};
this.getAmmo= function(){return _ammo};
this.getDirection = function(){return _direction};
this.getPvMod = function(){return _pvMod};
this.getManaMod = function(){return _manaMod};
this.getPoiseMod = function(){return _poiseMod};
this.getSpeedMod = function(){return _speedMod};
this.getAnimAtt = function(){return _animAtt};
this.getAnimZone = function(){return _animZone};
}
}
Now i'm trying to get my entitee (entity in English) hurt by an object i created, called "hammer". But i would like to be able to get it hurts by many different object.
I try to get it hurt when I press "P" and/or every 1 sec.
But I can't found how to give to the callback function the object I want the entity to get hurt with.
class Scene1 extends Phaser.Scene{
constructor() {
super("Scene1")
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> INIT
init(){
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> PRELOAD
preload(){
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> CREATE
create(){
this.joueur = new Entitee(this, 100, 100, 100, 10, 200, 42, "square");
this.hammer = new Objet("Hammer", 12, 12, 12, 12, 0, -10, 0, 10, "empty", "empty");
this.timer_test = this.time.addEvent({ delay: 1000, callback: this.joueur.hurt, args: [this.joueur, this.hammer], loop: true });
this.input.keyboard.on('keydown-P', this.joueur.hurt, this.joueur, this.hammer);
this.joueur.hurt(this.hammer);
}//END CREATE
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> UPDATE
update(){
}//END UPDATE
}//END SCENE
i have this.joueur.hurt(this.hammer); working.
this.timer_test = this.time.addEvent({ delay: 1000, callback: this.joueur.hurt, args: [this.joueur, this.hammer], loop: true }); send me the following error :
Entitee.js:60 Uncaught TypeError: objet.getPvMod is not a function
at Function.Entitee.hurt (Entitee.js:60)
at Clock.update (phaser.js:183771)
at EventEmitter.emit (phaser.js:1774)
at Systems.step (phaser.js:35504)
at SceneManager.update (phaser.js:77851)
at Game.step (phaser.js:139643)
at TimeStep.step (phaser.js:67227)
at step (phaser.js:67474)
And when I press "p" i get
Entitee.js:60 Uncaught TypeError: objet.getPvMod is not a function
at Entitee.hurt (Entitee.js:60)
at KeyboardPlugin.emit (phaser.js:1752)
at KeyboardPlugin.update (phaser.js:166926)
at EventEmitter.emit (phaser.js:1751)
at onKeyDown (phaser.js:71462)
I figure how to avoid callback functions but it would be so easiest if I may give to those callback the arguments I want.
I've been lurking around internet for solutions but none of it have been useful. Some people seems to use console.log() but I can't understand why and how.
Thanks for considering my issue !
Sheolis
I suppose that args should be just [this.hammer] since we don't need joueur as input to the hurt function
this.timer_test = this.time.addEvent({
delay: 1000,
callback: this.joueur.hurt,
args: [this.hammer],
loop: true });
You may also look at callbackScope at the docs.

Add variable property new value to previous value (javascript)

Good afternoon. Is it possible to add a new object property value to its previous value?
I have tried the following code but browser returns "Maximum call stack size exceeded". The property translate will be used to drag a chart horizontally. It will shift the data range of an array that will be painted on the canvas chart. Kind regards.
function drawChart() {
.
.
.
tickerData = [..., ..., ...];
dataLength = tickerData.length;
barSpace = Number(localStorage.getItem("barspace"));
barWidth = (0.7 * barSpace).toFixed(1);
rightSpan = 3 * barSpace;
barStrokeWidth = 1;
if(!(dataLength - dragCanvas.translate() > dataLength)) {
dataLength = dataLength - dragCanvas.translate();
}
else {
dataLength = dataLength;
}
.
.
.
}
var dragCanvas = {
isTrue : false,
drag : [0, 0],
translate : function() {
return this.translate() + Math.ceil((this.drag[1] - this.drag[0])/barSpace);
}
};
function updateChartPaint(e) {
var pointerX = e.clientX;
var pointerY = e.clientY;
if(!dragCanvas.isTrue) {
dragCanvas.drag[0] = pointerX;
dragCanvas.drag[1] = pointerX;
}
else {
dragCanvas.drag[1] = pointerX;
$("#chartCanvas").empty();
drawChart();
}
}
document.addEventListener("mousemove", updateChartPaint, false);
document.getElementById("chartCanvas").onmousedown = function() {
dragCanvas.isTrue = true;
};
document.getElementById("chartCanvas").onmouseup = function() {
dragCanvas.isTrue = false;
};
You are calling this.translate() inside of the translate function so it goes into an infinite loop calling itself until eventually the call stack is exceeded.
From the way translate is called it looks like the function should be defined like this instead:
translate : function() {
const newTranslate = this.oldTranslate + Math.ceil((this.drag[1] - this.drag[0])/barSpace);
this.oldTranslate = newTranslate;
return newTranslate;
}
oldTranslate: 0
I assume that barSpace is declared outside of the function and will be a valid value. It seems to be set from localStorage but if it does not exist, then Number(localStorage.getItem("barspace")) will be zero and the return value from translate will be Infinity. Is that intended?
Also updateChartPaint sets a variable called pointerY but never uses it. Is it correct that dragging in the Y-axis is ignored and only changes in the X-axis matters?

Loading styles from firebase firestore onto canvas error

I am trying to get data from my firestore database and use that data to create a canvas. My goal is to load a set of images and maps to return several canvases.
I am constantly getting this error:
Uncaught (in promise) TypeError: Cannot read property 'getContext' of null
I have tried using setTimeout()'s to delay the speed to help the data load quicker, but nothing seems to work.
var gImgObj;
var gMeme = [];
var gCtx;
var canvas;
function memeDivGet(theId){
setTimeout(function(){
db.collection("memeInfo").where("creator", "==", theId)
.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
db.collection("memeInfo").doc(doc.id).collection("MIMG")
.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
//alert(doc.data())
// alert("here")
console.log(doc.data().Coordinates[0].fontSize)
gMeme.push(doc.data().BaseIMG)
for (var i = 0; i < doc.data().Coordinates.length; i++) {
// alert("here" + i)
gMeme.push({
line: "line",
size: doc.data().Coordinates[i].fontSize,
align: doc.data().Coordinates[i].align,
color: doc.data().Coordinates[i].color, // in color picker, if choosing color from platte notice it stays "solid".
fontFamily: doc.data().Coordinates[i].fontStyle,
lineWidth: 2, // outline width
x: doc.data().Coordinates[i].xPos,
y: doc.data().Coordinates[i].yPos,
text: doc.data().Text[i]
})
}
//console.log(doc.data().BaseIMG)
initCanvas(doc.data().BaseIMG);
})
})
})
})
},1500)
}
function initCanvas(link) {
var canvas = document.querySelector('#canvasImage');
gCtx = canvas.getContext('2d');
gImgObj = new Image();
// console.log(link)
//alert(gMeme[0])
gImgObj.src = link;
gImgObj.onload = function () {
canvas.width = gImgObj.width;
canvas.height = gImgObj.height;
drawCanvas();
};
//console.log("HERE4")
}
function drawCanvas() {
//alert(gMeme)
gCtx.drawImage(gImgObj, 0, 0);
for (var i = 0; i < gMeme.length; i++) {
if(i > 1){
drawTxt(gMeme[i]);
}
}
for (var i = 0; i < gMeme.length - 1; i++) {
drawTxt(gMeme[i + 1]);
}
// gMeme.txts.forEach(function (txt) {
// drawTxt(txt);
// });
}
function drawTxt(txt) {
gCtx.font = txt.size + 'px' + ' ' + txt.fontFamily;
gCtx.textAlign = txt.align;
gCtx.fillStyle = txt.color;
gCtx.fillText(txt.text, txt.x, txt.y);
}
What I want to return is a set of canvases with the parameters I want.
My database is set up in this way for this section of the code:
(fooIds meaning a random id set by firebase)
Collection 'MemeInfo'
Document (fooIds)
SubCollection 'MIMG'
Document 'A'
Inside Each fooId Document, there is a creatorId which helps the computer find who created this meme, I use this to locate the document I am supposed to use for this particular creator's account.
"gMeme" is an array containing information from the database.
An "A Document" has a BaseIMG (the background image), a coordinates array with maps inside (containing the size, color, fontFamily, and other parameters), and a text array with strings inside. There is an image of an "A Document" attached.
I understand that this is quite confusing so if you have any questions please ask them in the comments section.

Type Error - Referencing Javascript Classes

Firstly a little elaboration of the project I'm working on. I have started building a 'map maker' for a 2d game I am working on. The project is just for fun and has proven so far to be a great way to learn new things.
I recently showed my working map maker code to a friend who suggested it would be much more re-usable if I restructured the project to be more OOR, which I am now attempting.
The problem I have is when I add a 'Guild' instance to my map, the first one works fine, but the second causes a type error that is giving me a headache!
I will post all of the relevant code from the different files below, but the overall structure is as follows:
Map.js = Map class file, container for setting the map overall size and iterating over (and placing) map objects.
MapObject.js = Class file for simple map objects such as walls, contains the position and icon properties.
Guild.js = Class file, extends MapObject.js, this is where my problem seems to be, adds an additional 'MapIcon' and will have other features such as levels and names etc.
map-maker.js = Main file for generating the map-maker page, utilises the above class files to create the map.
Below is the code used to create an instance of 'Guild' on my map:
map-maker.js (creating the map / map object / guild instances)
// Initialise a new instance of map class from Map.js using the user
provided values for #mapX and #mapY.
var currentMap = new Map(XY,40);
// Get the user box volume * map boxsize from Map.js
currentMap.calcDimensions();
// Create a Map Object and push it to the currentMap with its position.
function createMapObject(x,y,floor){
currentMap.objects.push(new MapObject(x,y,floor));
}
// Create a Guild Object (extension of Map Object) and push it to the currentMap with its position.
function createGuildObject(x,y,floor){
currentMap.objects.push(new Guild(x,y,floor));
}
....
case 13: // Enter Key (Submit)
unhighlightTools();
currentMap.drawMap();
if(currentFloor != null){
currentFloor.hasFloor = true;
if(currentFloor.tileName == "Guild"){
createGuildObject(currentFloor.position.x,currentFloor.position.y,currentFloor);
}else {
createMapObject(currentFloor.position.x,currentFloor.position.y,currentFloor);
}
console.log("Map object created at - X:"+currentFloor.position.x+" Y:"+currentFloor.position.y);
}
currentFloor = [];
highlightTools();
break;
}
Guild.js (constructor and assigning map icon)
class Guild extends MapObject {
constructor(x,y,floor) {
super(x,y,floor);
this.levels = [];
}
mapIcon() {
this.mapIcon = new Image();
this.mapIcon.src = "../images/mapSprites/obj-1.png"
return this.mapIcon;
}
}
MapObject.js (position setup and constructor)
class MapObject {
constructor(x,y,floor) {
this.position = {x, y};
this.icon = this.wallFloorIcons(floor);
}
wallFloorIcons(floor) {
this.img = new Image();
this.name = "";
this.name += (floor.wallNorth) ? 'n' : '';
this.name += (floor.wallEast) ? 'e' : '';
this.name += (floor.wallSouth) ? 's' : '';
this.name += (floor.wallWest) ? 'w' : '';
this.name = 'wall-'+this.name+'.png';
if(this.name == 'wall-.png'){
this.img.src = "../images/mapSprites/floor.png";
}else {
this.img.src = "../images/mapSprites/"+this.name;
}
return this.img;
}
getIcon() {
return this.img;
}
}
Map.js (processing the objects at a given location and drawing the canvas)
class Map {
// Map Width / Height and number of boxes. Used to generate map and zoom level.
constructor(wh, boxSize) {
this.size = wh;
this.width = wh[0];
this.height = wh[1];
this.boxSize = boxSize;
this.objects = [];
this.boxes = wh[0];
}
// Calculates the width and height * boxSize for rendering the canvas.
calcDimensions(){
this.realX = Math.floor(this.width * this.boxSize);
this.realY = Math.floor(this.height * this.boxSize);
this.realX = parseInt(this.realX,10);
this.realY = parseInt(this.realY,10);
this.realXY = [
this.realX,
this.realY
];
return this.realXY;
}
// Draws the canvas, grid and adds the objects that belong to the map.
drawMap(){
var self = this;
self.canvas = document.getElementById("canvas");
self.c = self.canvas.getContext("2d");
self.background = new Image();
self.background.src = "../images/mapSprites/oldPaperTexture.jpg";
// Make sure the image is loaded first otherwise nothing will draw.
self.background.onload = function(){
self.c.drawImage(self.background,0,0);
self.fillMap();
}
}
fillMap(){
var self = this;
self.c.lineWidth = 1;
self.c.strokeStyle = 'black';
self.c.fillStyle = "rgba(255, 255, 255, 0.2)";
for (var row = 0; row < self.boxes; row++) {
for (var column = 0; column < self.boxes; column++) {
var x = column * self.boxSize;
var y = row * self.boxSize;
self.c.beginPath();
self.c.rect(x, y, self.boxSize, self.boxSize);
self.c.stroke();
self.c.closePath();
for (var i=0; i<self.objects.length; i++) {
var floor = self.objects[i];
if (floor.position.x == column && floor.position.y == row) {
if (self.objectsAtPosition({x:floor.position.x, y:floor.position.y}) != null) {
var mapObjects = self.objectsAtPosition({x:floor.position.x, y:floor.position.y})
for (var mapObject of mapObjects) {
this.c.drawImage(mapObject.getIcon(), x, y, self.boxSize, self.boxSize);
console.log(mapObject);
if(mapObject instanceof Guild){
console.log(mapObject);
this.c.drawImage(mapObject.mapIcon(), x, y, self.boxSize, self.boxSize);
}
}
}
}
}
}
}
}
deleteObject(pos){
this.objectsAtPosition(pos);
for( var i = 0; i < this.objects.length; i++){
if(this.objects[i] == this.objs){
delete this.objects[i];
this.objects.splice(i,1);
}
}
}
objectsAtPosition(position) {
var objs = [];
for (var o of this.objects) {
if (o.position.x == position.x && o.position.y == position.y) {
objs.push(o);
}
}
return objs;
}
}
When I run the code, this is my error:
Uncaught TypeError: mapObject.mapIcon is not a function
at Map.fillMap (Map.js:70)
at Image.self.background.onload (Map.js:39)
The error comes after I add 1 guild then try to add any other map object. Guild or otherwise.
Sorry if this question is a little vague, I'm still learning (as you can see :p).
Thanks for your time!
Earl Lemongrab
Got a solution from a friend in the end.
The issue was that I was reassigning this.mapIcon = new Image() so it exploded when it was called a second time.
Feel pretty silly for not spotting it.
Thanks for the help everyone.

HTML5 - Socket.IO & Canvas

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!

Categories

Resources