Долго, сложно и невыносимо интересно.
Долго, сложно и невыносимо интересно.
const me = {name: 'Alex', left: 0}
...
setInterval(() => update(), 1000)
...
window.addEventListener('keyup', () => me.left++)
...
requestAnimationFrame(() => draw())
const me = {name: 'Alex', left: 0}
...
setInterval(() => update(), 1000)
...
window.addEventListener('keyup', () => me.left++)
...
requestAnimationFrame(() => draw())
const me = {name: 'Alex', left: 0}
...
setInterval(() => update(), 1000)
...
window.addEventListener('keyup', () => me.left++)
...
requestAnimationFrame(() => draw())
const me = {name: 'Alex', left: 0}
...
setInterval(() => update(), 1000)
...
window.addEventListener('keyup', () => me.left++)
...
requestAnimationFrame(() => draw())
const me = {name: 'Alex', left: 0}
...
setInterval(() => update(), 1000)
...
window.addEventListener('keyup', () => me.left++)
...
requestAnimationFrame(() => draw())
Но никто из них не доделывает до конца и не стремится объединяться
Это набор библиотек и утилит, для работы с картами
const ctx = canvas.getContext('2d')
ctx.drawImage(hero, 0, 0)
ctx.clearRect(0, 0, 100, 100)
ctx.drawImage(hero, 100, 0)
ctx.clearRect(0, 0, 100, 100)
ctx.drawImage(hero, 200, 0)
const ctx = canvas.getContext('2d')
ctx.drawImage(hero, 0, 0)
ctx.drawImage(grass, 0, 0)
ctx.drawImage(hero, 100, 0)
ctx.drawImage(grass, 100, 0)
ctx.drawImage(hero, 200, 0)
<canvas id="terrain">
<canvas id="objects">
<canvas id="ui">
Иллюзия непрерывного блока спасибо Яндекс-картам за идею
А ведь кол-во объектов на карте может достигать over 9000!
+ отсечение видимых объектов
+ корректное перекрытие
+ более дешевая итерация
const renderTree = {
32: [object, object, ...]
64: [object, object]
96: [object, object, ...]
128: [object]
}
const object = getObject(id)
const {x, y} = getAnimationFrame(object)
const offsetleft = getMapLeft()
const offsetTop = getMapTop()
context.drawImage(object.texture, x - offsetleft, ...
context.drawImage(object.texture, x, y)
Поэтому мы просто возьмем и запишем их в Render tree!
const drawer = context.drawImage.bind(context, ...)
renderTree.add(x, y, drawer)
И получим отличный прирост производительности!
new Promise(resolve => {
setTimeout(() => {
// расчеты для анимации
requestAnimationFrame(() => /* рисование */)
resolve()
})
})
startAnimation()
.then(step)
.then(step)
.then(step)
.then(step)
.then(doAction)
.then(endAnimation)
AsyncSequence([
startAnimation, [
step
step
...],
doAction,
endAnimation
])
* а точнее — тайлы
const map = [
[{...}, {...}, {...}, {...}, {...}, {...}],
[{...}, {...}, {...}, {...}, {...}, {...}],
[{...}, {...}, {...}, {...}, {...}, {...}],
...
]
const tile = map[1][3]
const tile = {
// данные для отрисовки
render: {...},
// данные для поиска пути
passability: {...},
// данные которые нужны значительно реже
otherStuff: {...},
}
Цикл отрисовки | массив функций отрисовки |
Поиск пути | массив чисел |
Ассоциация объектов к тайлам | массив строк |
Дополнительные свойства тайлов | массив чисел |
Для игровой логики | Map объектов c их ID |
Остается только своевременное обновление данных из медленных хранилищ в быстрые
const map = [
[{...}, {...}, {...}, {...}, {...}, {...}],
[{...}, {...}, {...}, {...}, {...}, {...}],
[{...}, {...}, {...}, {...}, {...}, {...}],
...
]
const map = [{...}, {...}, {...}, {...}, ...]
const tile = map[y * width + x]
const map = [{...}, {...}, {...}, {...}, ...]
const tile = map[y * width + x]
map.forEach((value, index) => {
const y = Math.floor(index / width)
const x = index - (y * width)
})
Math.floor(X / 2n) === X >> n
const map = [{...}, {...}, {...}, {...}, ...]
const powerOfTwo = Math.ceil(Math.log2(width))
const tile = map[y << powerOfTwo + x]
map.forEach((value, index) => {
const y = index >> powerOfTwo
const x = index - (y << powerOfTwo)
})
const grid = new Grid(32)
const tile = grid.get(x, y)
grid.forEach((value, x, y) => {})
- только квадратные сетки
- неэффективен для сеток стороной более чем 256
object.on('ownerChanged', () => ...)
object.on('objectRemoved', () => ...)
object.on('heroGoesToHellWithHisHorse', () => ...)
object.on('someStrangeEventThatYouWantToDescribe', () => ...)
const OWNER_CHANGED = 'ownerChanged';
const OBJECTS_REMOVED = 'objectsRemoved';
dwelling.on(OWNER_CHANGED, () => ...)
dwelling.on(OBJECTS_REMOVED, () => ...)
hero.on(OWNER_CHANGED, () => ...)
hero.on(OBJECTS_REMOVED, () => ...)
Итак, я пришел к хранению стейта
const state = new StateStore()
state.set('objects.234.owner', hero.owner.id)
const object = state.get('objects.234')
state.set('objects.234.owner', hero.owner.id)
{
objects: {
234: {owner: 1}
}
}
// 'objects.234', 'objects.234.*'
state.on('objects.234', object => ...)
// 'objects.234.owner', 'objects.{N}.owner' ...
state.on('*.owner', object => ...)
<group id="main" ... >
<group id="header" ... >
<text-block ... />
<button ... />
</group>
<group id="footer" ... >
<any-component ... />
<button ... />
</group>
</group>
<group width="{windowWidth}" ... >
<group width="100%" ... >
</group>
<group bottom="0" ... >
</group>
</group>
<group width="{expression}">
Фреймворки либо компилировали это в JS, либо имели собственный интерпретатор JS на JS
Use of the with statement is not recommended, as it may be the source of confusing bugs and compatibility issues. See the "Ambiguity Contra" paragraph in the "Description" section below for details.https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with
...
window.$w = function(o, e){with (o) {return eval(e)}};
'use strict'
window.$w = function(o, e){with (o) {return eval(e)}};
window.$w = function(o, e){with (o) {return eval(e)}};
'use strict'
// code in strict mode
$w(context, 'some code here')
Это была бы история не про "Герои"
После каждого шага нужно обновлять позицию героя в дереве отрисовки
const objectInAction = Objects.get(ID)
const hero = Player.activeHero
objectInAction.events.dispatch('action', hero)
...
this.events.on('action', hero => {
hero.owner.resources.set('gems', this.value)
this.remove()
})
Доля динамических объектов около 10% от всех.
Так почему бы не сэкономить на расчетах:
// только при загрузке карты, содержит много данных
const baseGrid = new Grid(mapSize)
// обновляется намного чаще, потому содержит мало данных
const dynamicGrid = new Grid(mapSize)
// Объект содержит гарнизон и может быть атакован
@Mixin(Attacable)
class TownObject extends OwnershipObject {...}
// Содержит все для отрисовки флажка, его смены и т.п.
class OwnershipObject extends MapObject {...}
// Содержит все базовые поля для объекта карты
class MapObject {...}