У меня есть переменная G.playerStatsDifference, определенная как массив объектов:
playerStatsDifference: [{
carpenter: 0,
wood: 0,
gunman: 0,
gunpowder: 0,
merchant: 0,
gold: 0,
fleet: 0,
flagship: 0,
}, {
carpenter: 0,
wood: 0,
gunman: 0,
gunpowder: 0,
merchant: 0,
gold: 0,
fleet: 0,
flagship: 0,
}]
Смысл этой переменной в том, чтобы вычислить разницу между G.playerStats, которая часто меняется.
Моя функция для вычисления разницы:
const oldPlayerStats = JSON.parse(JSON.stringify(G.playerStats));
statsDifference(G, oldPlayerStats);
for (let p = 0; p < 2; p++) {
for (let s = 0; s < 8; s++) {
Object.values(G.playerStatsDifference[p])[s] = Object.values(G.playerStats[p])[s] - Object.values(oldPlayerStats[p])[s];
}
}
Ожидаемый результат будет иметь playerStatsDifference
При запуске некоторых тестов я вел журнал консоли, и он дал мне правильные расчеты, но G.playerStatsDiffence не обновлялся.
Вот некоторые из этих тестов с правильными расчетами:
console.info("Current wood is " + Object.values(G.playerStats[0])[1]); //Current wood is 5
console.info("Old wood is " + Object.values(oldPlayerStats[0])[1]); //Old wood is 10
console.info(Object.values(G.playerStats[0])[1] - Object.values(oldPlayerStats[0])[1]); //-5
Я подумал, может быть, я делаю что-то не так с петлями, поэтому потом попробовал следующее:
Object.values(G.playerStatsDifference[0])[1] = Object.values(G.playerStats[0])[1] - Object.values(oldPlayerStats[0])[1];
Однако и это не сработало. Сказав это, следующее работает:
G.playerStatsDifference[0].wood = Object.values(G.playerStats[0])[1] - Object.values(oldPlayerStats[0])[1];
Так что, похоже, у меня проблемы с Object.values на G.playerStatsDifference. Любая идея о том, почему это так и как я могу запустить это через цикл?
=====
Обновлено: Как отметили в комментариях, мой вопрос немного сбивает с толку, поэтому я попытаюсь прояснить его здесь.
Предполагается, что значение G.playerStatsDifference отслеживает разницу между предыдущим значением G.playerStats и текущим значением G.playerStats.
Для этого я устанавливаю значение oldPlayerStats равным G.playerStats, а затем обновляю G.playerStats до нового значения.
Затем мне нужно просмотреть массив объектов и вычесть значение G.playerStats из oldPlayerStats. Это даст значение G.playerStatsDifference
Вот для чего нужен цикл, чтобы пройтись по каждому ключу объекта и выполнить вычисления.
Надеюсь, это внесет некоторую ясность. Извините за плохо сформулированный вопрос.
Итак, playerStatsDifference увеличивается в размерах с каждым новым объявлением playerStats?
@AndrewParks Нет, значения заменяются/обновляются.
Кажется, у вас повсюду есть Object.values. Это не то, что вы думаете.
@JaredSmith Это для того, чтобы указать на это, какие есть альтернативные подходы?
Итак, playerStatsDifference содержит два последних набора различий?
@AndrewParks Он должен содержать разницу между текущим значением и старым значением. Как отмечают другие, похоже, я неправильно использую object.values
@evilgenious448 Я не хочу обижаться, когда говорю это (все мы с чего-то начинаем, и вы не станете лучше, если не будете пытаться), но ваш подход настолько сломан, что я не могу понять, чего вы пытаетесь достичь. Может быть, отредактировать вопрос, чтобы включить больше фона?
Но если он содержит разницу между текущим и старым, почему там два объекта? Почему не только один?
@JaredSmith Хорошо, я отредактирую вопрос, чтобы сделать его более понятным. Без обид, спасибо за вежливость в ответе :)



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


// for testing purposes, create an object with some random stats
const randomPlayerStats = () => Object.fromEntries(
['carpenter','wood','gunman','gunpowder','merchant','gold','fleet','flagship']
.map(k=>[k,Math.random()*10|0]));
// array of the last player stats recorded for each player
let lastPlayerStats = [];
// create a new object from the existing object, subtracting each entry
// from the old object from the entry from the new object
// note: uses the ?? operator so that if there is no last object yet,
// the last object value will be treated as being zero
const difference = (playerStats, lastPlayerStats) => {
let r = Object.fromEntries(Object.entries(playerStats).map(([k,v])=>
[k, v-(lastPlayerStats?.[k]??0)]));
lastPlayerStats = playerStats;
return r;
};
// simulate 5 rounds of the game, with 2 players in the game
const playerCount = 2;
const simulatedRounds = 5;
for(let c=0;c<simulatedRounds;c++) {
let playerStats = [...Array(playerCount).keys()].map(i=>randomPlayerStats());
let playerStatsDifference = playerStats.map((s,i)=>
difference(s, lastPlayerStats[i]??{}));
console.info('playerStats:');
console.info(playerStats);
console.info('playerStatsDifference:');
console.info(playerStatsDifference);
}Привет, @Andrew Parks, можешь ли ты отредактировать ответ, чтобы вывести разницу в той же структуре, что и статистика. Итак { "carpenter": 4, "wood": 0, "gunman": 6, "gunpowder": 8, "merchant": 0, "gold": 8, "fleet": 5, "flagship": 5 }
@evilgenious448 ой, исправлено
@AndrewParks, вам действительно нужно добавить какое-то объяснение к этой стене кода. Это не очень полезно в его нынешнем состоянии.
@evilgenious448 Наконец-то я понял, почему в playerStatsDifference было несколько записей — это многопользовательская игра. Мой ответ теперь работает для многопользовательской игры
Привет @Andrew Parks, извините, я должен был объяснить, что это многопользовательская игра с двумя игроками. Спасибо за редактирование вашего ответа. Кажется, он работает именно так, как я хочу. Но ваш код немного нов для меня, поэтому я был бы признателен, если бы вы могли оставить некоторые комментарии и посмотреть, что делают определенные части кода.
const diffBetweenObjectValues = (a, b) => {
return Object.entries(a).reduce((result, [aKey, aVal]) => {
result[aKey] = aVal - (b[aKey] ?? 0);
return result;
}, {});
}
const stats = { a: 1, b: 2 };
const updatedStats = { a: 1, b: 1 };
// Initial player stats are { a: 1, b: 2 }
const player = { stats: stats, diff: {} };
// Set the diff, value is { a: 0, b: 1 }
player.diff = diffBetweenObjectValues(player.stats, updatedStats);
// Actually update the stats, value is { a: 1, b: 1 }
player.stats = updatedStats;
Обратите внимание, что если ключ присутствует в b, но не a, он игнорируется. Также обратите внимание, что это работает правильно, только если все значения свойств являются числовыми.
Вы можете поместить переход состояния в функцию и просто запускать ее, когда вам нужно обновить статистику (например, каждый тик игрового цикла).
Хорошо, давайте добавим еще одну вспомогательную функцию
const zip = (a, b) => a.map((x, i) => [x, b[i]]);
const players = [...]; // array of players
const statUpdates = [...]; // array of stat updates
zip(players, statUpdates).forEach(([player, stats]) => {
player.diff = diffBetweenObjectValues(player.stats, stats);
player.stats = stats;
});
Zip объединяет массив игроков и массив обновлений статистики в пары, затем перебирает их с помощью forEach, деструктурирует биты обратно и запускает обновление. Вы также можете просто использовать цикл for, который быстрее, но его труднее читать и легче ошибиться (например, ошибки по одному). Я бы придерживался этой версии до тех пор, пока ваш профилировщик не скажет вам, что она слишком медленная.
const currentStats = [{ a: 1, b: 2 }, {a: 3, b: 2 }];
const updatedStats = [{ a: 0, b: 1 }, {a: 4, b: 1 }];
const diffedStats = zip(currentStats, updatedStats).map(([current, updated]) => {
return diffBetweenObjectValues(current, updated);
});
Привет, Джаред, просто пытаюсь обдумать этот ответ. Кроме того, что было бы правильным сделать, чтобы stats был массивом с двумя объектами, где stats[0] — статистика для первого игрока, а stats[1] — статистика для второго игрока.
@evilgenious448 см. мою правку к ответу.
Привет @Jared Smith, извините за задержку с моим ответом, я пытался интегрировать ваше решение в свой код. Значения для stats и updatedStats будут выглядеть следующим образом: const stats = [{ a: 1, b: 2 }, { a: 1, b: 2 }]; const updatedStats = [{ a: 1, b: 1 }, { a: 1, b: 1 }]; Если я правильно понимаю, ваше решение не совсем работает со значениями как с двумя объектами в массиве.
@evilgenious448 Как правило, структурировать код таким образом ошибочно, за исключением разработки игр AAA на C++, и даже не всегда. В объектно-ориентированном коде объект должен владеть своим состоянием, статистика игрока должна быть свойством объекта игрока, а не массивом отдельных сущностей, связанных положением в массиве. Я думаю, что отчасти причина, по которой людям трудно ответить на ваш вопрос, заключается в том, что ни один опытный разработчик не стал бы разрабатывать его таким образом (опять же, без обид).
все хорошо, опять без обид. На самом деле я использую фреймворк, который требует, чтобы игроки были структурированы таким образом (думаю, это не очень хороший фреймворк, ха-ха).
@evilgenious448 Я предполагаю, что он был разработан кем-то из разработчиков игр AAA с использованием C++, где такое структурирование имеет смысл для повышения частоты промахов кеша. Но это не C++, это Javascript, и очень немногие написанные от руки JS-игры нуждаются в таком уровне производительности. Насколько мне известно, высокопроизводительные JS-игры обычно пишутся на чем-то другом (например, на C++ с SDL, C# на Unity), а затем компилируются в нечитаемый человеком JS или даже WASM.
Ах хорошо, это ценно знать. Возможно, для следующей игры я рассмотрю более подходящий фреймворк. Можете ли вы изменить свой ответ, чтобы он соответствовал структуре массива/объекта?
Давайте продолжим обсуждение в чате.
Просто хочу добавить последний комментарий о том, что @Jared Smith определенно является тем, на кого StackOverflow нужно больше людей, чтобы быть похожими. Делал все возможное, чтобы помочь мне, даже когда я, вероятно, откусывал немного больше, чем мог прожевать, он был терпелив и вежлив и помог мне добраться туда, где мне нужно быть. Хотел бы я дать ему все 500 баллов, он их заслуживает.
Object.values— это статический метод корневогоObjectкласса. Вы не можете назначить его. Также нет смысла вызывать его с результатом такой математической операции, как вычитание.