Я использую p5.js, node.js и socket.io для создания небольшой многопользовательской игры в браузере. Объект с именем «игрок» передается от одного клиента к другому для синхронизации обоих клиентов. Это прекрасно работает для другого кода, который не полагается на вызовы методов. Но теперь, когда я также хочу получить доступ к методам сохраненных объектов в объекте игрока, это больше не работает.
Это будет объект, в котором хранятся все переменные и массив с объектами между двумя клиентами:
var player = {
structures: []
};
Теперь, если я добавлю к нему объект с именем костер, вот так:
player.structures.push(new campfire(mouseX,mouseY));
И попробуйте вызвать метод draw() объекта костра, который выглядит так:
class campfire {
constructor(x,y){
this.scale = 40;
this.x = x;
this.y = y;
this.maxLevel = 3;
this.button = new button(this.x, this.y+50, this.x-50, this.y-70, upgrade, upgrade_hover, this);
this.show_upgrade_button = true;
}
draw(){
switch(player.campfire.level){
case 1: image(fire, this.x, this.y, this.scale, this.scale);break;
case 2: image(campfire_2, this.x, this.y, this.scale, this.scale);break;
case 3: image(campfire_3, this.x, this.y, this.scale, this.scale);break;
default: console.info("cant upgrade, this shouldn't happen.");
}
if (this.show_upgrade_button){
this.button.draw();
}
}
trigger(){
if (player.campfire.level + 1 <= this.maxLevel && this.show_upgrade_button == true){
this.button.trigger();
}
}}
с использованием
player.structures[i].draw();
Консоль (клиента, не помещающего объект "костер" в массив "структуры") выводит следующую ошибку:
gui.js:38 Uncaught TypeError: player.structures[i].draw is not a function
Клиент, который помещает объект костра в массив, может выполнить вызов draw() и не выводить сообщение об ошибке. Однако другой клиент, который не отправил объект в массив, не может использовать этот метод и выводит ошибку, даже если они оба используют один и тот же объект «игрок». Странно то, что тот, кто распечатывает ошибку, по-прежнему может получить доступ ко всем переменным объекта костра, поэтому, похоже, это влияет только на методы.
Единственный ресурс, который я нашел по этому поводу, это статья https://airbrake.io/blog/javascript-error-handling/x-is-not-a-function-typeerror Я попытался переименовать функцию в draw = function(){} и campfire.prototype.draw = function(){}, но для меня это не имеет значения. Я думаю, что функции не объявлены правильно? Это было бы единственным возможным объяснением для меня прямо сейчас.
Спасибо за время, которое вы потратили, чтобы прочитать мой вопрос!
Обновлено: вот ссылка на проект: https://github.com/Mayhoon/Многопользовательская браузерная игра
Пожалуйста, опубликуйте Минимальный, полный и проверяемый пример
@DacreDenny да, оба клиента знают длину массива, и я могу получить доступ к переменным объектов, например, при использовании player.structures[0].x. Цикл for, который я использую для перебора массива, работает, но только для клиента, который помещает объект в массив.
Можете ли вы опубликовать больше кода, показывающего, как вы заполняете массив структур, или предоставить ссылку, скажем, на репозиторий github?
@Ele вот ссылка на проект: github.com/Mayhoon/Многопользовательская браузерная игра . Я использую «nodemon server.js» для запуска сервера на порту 3000. Вам нужно открыть 2 вкладки одновременно. После загрузки обоих нажмите h, нажмите на изображение и нажмите где-нибудь на экране. Предполагается, что значок молотка выполняет часть обновления и синхронизирует процесс с другим клиентом при нажатии. Я включил только необходимые классы.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я думаю, что ваша проблема больше на уровне дизайна. Socket.io (или любая другая библиотека синхронизации) на самом деле не предназначена для передачи целых фрагментов кода туда и обратно. В вашем случае это будут функции костра.
Я бы посоветовал только толкать state вперед и назад. У игрока есть молоток? Сколько жизней у игрока? У игрока есть костер? Другими словами, синхронизируйте только те данные, которые постоянно меняются.
Поскольку функциональность костра (функция розыгрыша) одинакова для всех игроков, нет необходимости нажимать этот код туда-сюда каждые несколько секунд! Это совсем неэффективно!
Вместо этого вы можете создавать экземпляры костра в клиенте, когда ваш объект синхронизации сообщает, что у игрока есть костер.
В коде, который может выглядеть так (просто идея):
Синхронизация объекта для socket.io
var player = {
structures: {campfire:true}
};
Код клиента
// create campfire instances, tell the campfire which player it belongs to
if (player.structures.campfire) {
let cf = new Campfire(player)
cf.draw()
}
примечание: вполне может быть, что socket.io удаляет исполняемый код
Спасибо за ваш ответ, я уже сделал что-то похожее с индикатором выполнения, который указывает на процесс обновления костра, и задался вопросом, есть ли лучший способ сделать это. Я не был уверен, что это проблема с javascript, потому что сообщение об ошибке предполагало это, поэтому я спросил здесь. Как вы уже писали, возможно, socket.io удаляет все методы, но я не воспользуюсь вашим советом и вместо этого просто использую переменные состояния - ваш ответ мне очень помог, большое спасибо!
Socket IO сериализует свои объекты. Во время сериализации JSON все методы исчезают, а сохраняются только свойства.
Вы уверены, что ваш индекс
iнаходится в диапазоне от0доplayer.structures.length - 1?