feat: add in creature rendering
This commit is contained in:
parent
514b033884
commit
73e1530a1d
220
static/index.js
220
static/index.js
|
@ -1,5 +1,217 @@
|
|||
window.onload = () => {
|
||||
console.log('from js');
|
||||
const kennelWindowEle = document.querySelector('#kennel-window');
|
||||
kennelWindowEle.innerHTML = 'rendered from static/index.js';
|
||||
const CREATURE_HEIGHT = 32; // in px
|
||||
const CREATURE_WIDTH = 32; // in px
|
||||
const FRAME_RATE = 12; // in FPS
|
||||
const FRAME_DELAY = 1 / FRAME_RATE * 1000; // in ms
|
||||
const DEFAULT_SPRITE_SHEET_COUNT = 1; // number of default sprite sheets
|
||||
|
||||
/**
|
||||
* Enum of creature states
|
||||
* @readonly
|
||||
* @enum {number}
|
||||
*/
|
||||
const CreatureState = Object.freeze({
|
||||
IDLE: 0,
|
||||
ALERT: 1,
|
||||
SCRATCH_SELF: 2,
|
||||
SCRATCH_NORTH: 3,
|
||||
SCRATCH_SOUTH: 4,
|
||||
SCRATCH_EAST: 5,
|
||||
SCRATCH_WEST: 6,
|
||||
TIRED: 7,
|
||||
SLEEPING: 8,
|
||||
WALK_NORTH: 9,
|
||||
WALK_NORTHEAST: 10,
|
||||
WALK_EAST: 11,
|
||||
WALK_SOUTHEAST: 12,
|
||||
WALK_SOUTH: 13,
|
||||
WALK_SOUTHWEST: 14,
|
||||
WALK_WEST: 15,
|
||||
WALK_NORTHWEST: 16,
|
||||
})
|
||||
|
||||
/**
|
||||
* @typedef {[number, number]} SpriteFrameOffset the offset of the sprite with respect to
|
||||
* the left/top background position offset (off by factor of sprite size)
|
||||
* @type {Object.<number, Array<SpriteFrameOffset>>}
|
||||
*/
|
||||
const CREATURE_STATE_TO_SPRITE_FRAME_OFFSET_INDICES = Object.freeze({
|
||||
[CreatureState.IDLE]: [
|
||||
[-3, -3]
|
||||
],
|
||||
[CreatureState.ALERT]: [
|
||||
[-7, -3]
|
||||
],
|
||||
[CreatureState.SCRATCH_SELF]: [
|
||||
[-5, 0],
|
||||
[-6, 0],
|
||||
[-7, 0],
|
||||
],
|
||||
[CreatureState.SCRATCH_NORTH]: [
|
||||
[0, 0],
|
||||
[0, -1],
|
||||
],
|
||||
[CreatureState.SCRATCH_SOUTH]: [
|
||||
[-7, -1],
|
||||
[-6, -2],
|
||||
],
|
||||
[CreatureState.SCRATCH_EAST]: [
|
||||
[-2, -2],
|
||||
[-2, -3],
|
||||
],
|
||||
[CreatureState.SCRATCH_WEST]: [
|
||||
[-4, 0],
|
||||
[-4, -1],
|
||||
],
|
||||
[CreatureState.TIRED]: [
|
||||
[-3, -2]
|
||||
],
|
||||
[CreatureState.SLEEPING]: [
|
||||
[-2, 0],
|
||||
[-2, -1],
|
||||
],
|
||||
[CreatureState.WALK_NORTH]: [
|
||||
[-1, -2],
|
||||
[-1, -3],
|
||||
],
|
||||
[CreatureState.WALK_NORTHEAST]: [
|
||||
[0, -2],
|
||||
[0, -3],
|
||||
],
|
||||
[CreatureState.WALK_EAST]: [
|
||||
[-3, 0],
|
||||
[-3, -1],
|
||||
],
|
||||
[CreatureState.WALK_SOUTHEAST]: [
|
||||
[-5, -1],
|
||||
[-5, -2],
|
||||
],
|
||||
[CreatureState.WALK_SOUTH]: [
|
||||
[-6, -3],
|
||||
[-7, -2],
|
||||
],
|
||||
[CreatureState.WALK_SOUTHWEST]: [
|
||||
[-5, -3],
|
||||
[-6, -1],
|
||||
],
|
||||
[CreatureState.WALK_WEST]: [
|
||||
[-4, -2],
|
||||
[-4, -3],
|
||||
],
|
||||
[CreatureState.WALK_NORTHWEST]: [
|
||||
[-1, 0],
|
||||
[-1, -1],
|
||||
],
|
||||
});
|
||||
|
||||
/**
|
||||
* Properties for creatures running around the screen
|
||||
* @typedef {Object} Creature
|
||||
* @property {string} name the name of the creature, used as the HTML element's id
|
||||
* @property {string} spriteSheet the file name of the sprite sheet. should exist in {@link /static/sprites}
|
||||
* @property {number} state the current state of the creature (should be member of {@link CreatureState} enum)
|
||||
* @property {number} stateDuration the number of frames the creature has been in its current state
|
||||
* @property {number} positionX the number of pixels away from the left side of the container element
|
||||
* @property {number} positionY the number of pixels away from the top of the container element
|
||||
* @property {HTMLElement} element the HTML element rendering the creature in the DOM
|
||||
* @property {HTMLElement} container the HTML element containing the creature (the kennel, if you will)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns random number between min (inclusive) and max (exclusive)
|
||||
* If max is less than or equal to min, return min
|
||||
* @param {number} min inclusive lower bound
|
||||
* @param {number} max exclusive upper bound
|
||||
* @return {number} number in [min, max)
|
||||
*/
|
||||
const randomInt = (min, max) => {
|
||||
if (max <= min) return min;
|
||||
return Math.floor(Math.random() * (max - min) + min);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a creature's next frame based on the current creature frame
|
||||
* @param {Creature} creature
|
||||
* @return Creature the next frame of the creature
|
||||
*/
|
||||
const getNextCreatureFrame = (creature) => {
|
||||
// TODO
|
||||
return { ...creature, stateDuration: creature.stateDuration + 1 };
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a frame of the creature and loads the next frame for render
|
||||
* @param {Creature} creature
|
||||
* @return Creature the next frame of the creature
|
||||
*/
|
||||
const renderCreature = (creature) => {
|
||||
// set position
|
||||
const positionX = Math.min(creature.positionX, creature.container.clientWidth);
|
||||
const positionY = Math.min(creature.positionY, creature.container.clientHeight);
|
||||
creature.element.style.setProperty('left', `${positionX}px`);
|
||||
creature.element.style.setProperty('top', `${positionY}px`);
|
||||
|
||||
// set sprite
|
||||
const spriteFrames = CREATURE_STATE_TO_SPRITE_FRAME_OFFSET_INDICES[creature.state]
|
||||
const currentSpriteFrameOffset = spriteFrames?.[creature.stateDuration % spriteFrames.length]
|
||||
creature.element.style.setProperty(
|
||||
'background-position',
|
||||
`${currentSpriteFrameOffset[0] * CREATURE_WIDTH}px ${currentSpriteFrameOffset[1] * CREATURE_HEIGHT}px`
|
||||
)
|
||||
|
||||
const nextCreatureFrame = getNextCreatureFrame(creature);
|
||||
setTimeout(renderCreature, FRAME_DELAY, nextCreatureFrame)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the creature and start its rendering
|
||||
* @param {HTMLElement} container container element for creatures. the kennel if you will
|
||||
* @param {string} name name of the creature
|
||||
* @param {string} [spriteSheet] name of the sprite sheet. must be in /static/sprites
|
||||
* uses default sprite sheet if undefined
|
||||
* @param {number} [initialState] starting state of the creature
|
||||
* @param {number} [initialPositionX] initial x position in pixels (from the left side)
|
||||
* @param {number} [initialPositionY] initial y position in pixels (from the top)
|
||||
*/
|
||||
const createCreature = (
|
||||
container,
|
||||
name,
|
||||
spriteSheet,
|
||||
initialState = CreatureState.IDLE,
|
||||
initialPositionX = 0,
|
||||
initialPositionY = 0
|
||||
) => {
|
||||
const creatureEl = document.createElement('div');
|
||||
const spriteSheetUrl = spriteSheet
|
||||
? `url('/static/sprites/${spriteSheet}')`
|
||||
: `url('/static/sprites/defaults/${randomInt(1, DEFAULT_SPRITE_SHEET_COUNT)}.gif')`;
|
||||
|
||||
creatureEl.setAttribute('id', name);
|
||||
creatureEl.style.setProperty('width', `${CREATURE_WIDTH}px`);
|
||||
creatureEl.style.setProperty('height', `${CREATURE_HEIGHT}px`);
|
||||
creatureEl.style.setProperty('position', 'fixed');
|
||||
creatureEl.style.setProperty('image-rendering', 'pixelated');
|
||||
creatureEl.style.setProperty('background-image', spriteSheetUrl);
|
||||
|
||||
container.appendChild(creatureEl);
|
||||
|
||||
renderCreature({
|
||||
name,
|
||||
spriteSheet,
|
||||
state: initialState,
|
||||
stateDuration: 0,
|
||||
positionX: Math.max(0, initialPositionX),
|
||||
positionY: Math.max(0, initialPositionY),
|
||||
element: creatureEl,
|
||||
container
|
||||
});
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const kennelWindowEle = document.querySelector('#kennel-window');
|
||||
createCreature(
|
||||
kennelWindowEle,
|
||||
'test-creature',
|
||||
undefined,
|
||||
CreatureState.SCRATCH_SELF
|
||||
)
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 6.4 KiB |
|
@ -0,0 +1,7 @@
|
|||
#kennel-window {
|
||||
width: 80vw;
|
||||
min-width: 8rem;
|
||||
height: 80vw;
|
||||
min-height: 8rem;
|
||||
margin: auto inherit;
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<link rel="stylesheet" href="{{ url_for('static', path='style.css') }}">
|
||||
<title>Kennel Club</title>
|
||||
</head>
|
||||
<body>
|
||||
|
|
Loading…
Reference in New Issue