Долго, сложно и невыносимо интересно.
Долго, сложно и невыносимо интересно.
Но никто из них не доделывает до конца и не стремится объединяться
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
const okButton = new Buttton(0, 10, 'Ok')
okButton.addEventListener('click', () => { ... })
const cancellButton = new Buttton(0, 10, 'Cancel')
cancellButton.addEventListener('click', () => { ... })
const okButton = new Buttton({
left: 0,
top: 10,
onClick: () => { ... }
})
const cancellButton = new Buttton({...})
[
{
id: 'okButton',
options: {
left: 0,
top: 10,
onClick: () => { ... }
},
},
...
]
<button id="okButton"
left="0"
top="10"
onClick="{doSomething()}"
/>
При сборке он собирается в JSON с предыдущего слайда
<group id="main" ... >
<group id="header" ... >
<text-block ... />
<button ... />
</group>
<group id="footer" ... >
<any-component ... />
<button ... />
</group>
</group>
После каждого шага нужно обновлять позицию героя в дереве отрисовки
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 {...}