[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 selectmain
parent
614621e1f4
commit
a7e7b6a3e2
@ -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%);
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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,
|
||||
},
|
||||
];
|
||||
@ -0,0 +1,2 @@
|
||||
export * from "./animationPhases";
|
||||
export * from "./spriteAnimationData";
|
||||
@ -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<AnimationName, AnimationState>)[state.name] = {
|
||||
loc,
|
||||
};
|
||||
});
|
||||
|
||||
return animationStates as Record<AnimationName, AnimationState>;
|
||||
}
|
||||
@ -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$;
|
||||
}
|
||||
@ -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 }[];
|
||||
};
|
||||
Loading…
Reference in New Issue