From a7e7b6a3e29282d05066d2e7c7aba1bff755d6a3 Mon Sep 17 00:00:00 2001 From: Vasily Guzov Date: Sun, 29 Oct 2023 05:38:47 +0300 Subject: [PATCH] [chapter 1] sprite animation - generate sprite state data - create animation loop - implement animation logic - implement control to switch different animation - use rxjs for handling chenge select --- src/module/chapter-1/chapter-1.module.css | 16 +++++ src/module/chapter-1/chapter-1.ts | 62 +++++++++++++++++++ src/module/chapter-1/data/animationPhases.ts | 44 +++++++++++++ src/module/chapter-1/data/index.ts | 2 + .../chapter-1/data/spriteAnimationData.ts | 24 +++++++ src/module/chapter-1/index.ts | 0 src/module/chapter-1/select.ts | 31 ++++++++++ src/module/chapter-1/type.ts | 20 ++++++ 8 files changed, 199 insertions(+) create mode 100644 src/module/chapter-1/chapter-1.module.css create mode 100644 src/module/chapter-1/chapter-1.ts create mode 100644 src/module/chapter-1/data/animationPhases.ts create mode 100644 src/module/chapter-1/data/index.ts create mode 100644 src/module/chapter-1/data/spriteAnimationData.ts create mode 100644 src/module/chapter-1/index.ts create mode 100644 src/module/chapter-1/select.ts create mode 100644 src/module/chapter-1/type.ts diff --git a/src/module/chapter-1/chapter-1.module.css b/src/module/chapter-1/chapter-1.module.css new file mode 100644 index 0000000..210d565 --- /dev/null +++ b/src/module/chapter-1/chapter-1.module.css @@ -0,0 +1,16 @@ +.container { + border: 5px solid black; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 600px; + height: 600px; +} + +.select { + position: absolute; + top: 20px; + left: 50%; + transform: translate(0, -50%); +} diff --git a/src/module/chapter-1/chapter-1.ts b/src/module/chapter-1/chapter-1.ts new file mode 100644 index 0000000..c85780a --- /dev/null +++ b/src/module/chapter-1/chapter-1.ts @@ -0,0 +1,62 @@ +import styles from "./chapter-1.module.css"; +import { animationPhases, getAnimationStates } from "./data/index"; +import { drawSelect } from "./select"; +import { AnimationName } from "./type"; + +export function chapter1() { + const canvas = document.getElementById("canvas") as HTMLCanvasElement; + const ctx = canvas.getContext("2d"); + + canvas.classList.add(styles.container); + + const select$ = drawSelect(); + + const CANVAS_W = (canvas.width = 600); + const CANVAS_H = (canvas.height = 600); + + const playerImage = new Image(); + playerImage.src = "/shadow_dog.png"; + + const SPRITE_W = 6876 / 12; + const SPRITE_H = 5230 / 10; + let stateName: AnimationName = "run"; + let gameFrame = 0; + const staggerFrame = 5; + + const animationState = getAnimationStates( + SPRITE_W, + SPRITE_H, + animationPhases + ); + + select$.subscribe((event) => { + stateName = (event.target as HTMLSelectElement).value as AnimationName; + }); + + function animate() { + if (ctx) { + ctx.clearRect(0, 0, CANVAS_W, CANVAS_H); + let position = + Math.floor(gameFrame / staggerFrame) % + animationState[stateName].loc.length; + let frameX = SPRITE_W * position; + let frameY = animationState[stateName].loc[position].y; + + ctx.drawImage( + playerImage, + frameX, + frameY, + SPRITE_W, + SPRITE_H, + 0, + 0, + SPRITE_W, + SPRITE_H + ); + gameFrame++; + requestAnimationFrame(animate); + } + } + + animate(); +} diff --git a/src/module/chapter-1/data/animationPhases.ts b/src/module/chapter-1/data/animationPhases.ts new file mode 100644 index 0000000..2846be2 --- /dev/null +++ b/src/module/chapter-1/data/animationPhases.ts @@ -0,0 +1,44 @@ +import { AnimationPhases } from "../type"; + +export const animationPhases: AnimationPhases[] = [ + { + name: "idle", + frames: 7, + }, + { + name: "jump", + frames: 7, + }, + { + name: "fall", + frames: 7, + }, + { + name: "run", + frames: 9, + }, + { + name: "dizzy", + frames: 11, + }, + { + name: "sit", + frames: 5, + }, + { + name: "roll", + frames: 7, + }, + { + name: "bit", + frames: 7, + }, + { + name: "ko", + frames: 12, + }, + { + name: "getHit", + frames: 4, + }, +]; diff --git a/src/module/chapter-1/data/index.ts b/src/module/chapter-1/data/index.ts new file mode 100644 index 0000000..1ff7bcb --- /dev/null +++ b/src/module/chapter-1/data/index.ts @@ -0,0 +1,2 @@ +export * from "./animationPhases"; +export * from "./spriteAnimationData"; diff --git a/src/module/chapter-1/data/spriteAnimationData.ts b/src/module/chapter-1/data/spriteAnimationData.ts new file mode 100644 index 0000000..f44148e --- /dev/null +++ b/src/module/chapter-1/data/spriteAnimationData.ts @@ -0,0 +1,24 @@ +import { AnimationPhases, AnimationName, AnimationState } from "../type"; + +export function getAnimationStates( + spriteW: number, + spriteH: number, + animationPhases: AnimationPhases[] +) { + let animationStates: unknown = {}; + + animationPhases.forEach((state, index) => { + let loc = []; + for (let j = 0; j < state.frames; j++) { + let x = j * spriteW; + let y = index * spriteH; + loc.push({ x, y }); + } + + (animationStates as Record)[state.name] = { + loc, + }; + }); + + return animationStates as Record; +} diff --git a/src/module/chapter-1/index.ts b/src/module/chapter-1/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/module/chapter-1/select.ts b/src/module/chapter-1/select.ts new file mode 100644 index 0000000..efd821f --- /dev/null +++ b/src/module/chapter-1/select.ts @@ -0,0 +1,31 @@ +import { animationPhases } from "./data/animationPhases"; +import styles from "./chapter-1.module.css"; +import { fromEvent } from "rxjs"; + +export function drawSelect() { + const app = document.getElementById("app"); + const select = document.createElement("select"); + + select.id = "stateName"; + select.name = "stateKey"; + select.classList.add(styles.select); + + const options = animationPhases.map((phase) => { + const opt = document.createElement("option"); + opt.value = phase.name; + opt.text = phase.name.toUpperCase(); + return opt; + }); + + options.forEach((opt) => { + select.appendChild(opt); + }); + + app?.appendChild(select); + + const selectElem = document.getElementById("stateName") as HTMLSelectElement; + + const select$ = fromEvent(selectElem, "change"); + + return select$; +} diff --git a/src/module/chapter-1/type.ts b/src/module/chapter-1/type.ts new file mode 100644 index 0000000..04b393e --- /dev/null +++ b/src/module/chapter-1/type.ts @@ -0,0 +1,20 @@ +export type AnimationName = + | "idle" + | "jump" + | "fall" + | "run" + | "dizzy" + | "sit" + | "roll" + | "bit" + | "ko" + | "getHit"; + +export type AnimationPhases = { + name: AnimationName; + frames: number; +}; + +export type AnimationState = { + loc: { x: number; y: number }[]; +};