[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