Jofthomas's picture
Jofthomas HF staff
revert to main in github
b399f78
raw
history blame
3.4 kB
import { BaseTexture, ISpritesheetData, Spritesheet } from 'pixi.js';
import { useState, useEffect, useRef, useCallback } from 'react';
import { AnimatedSprite, Container, Graphics, Text } from '@pixi/react';
import * as PIXI from 'pixi.js';
export const Character = ({
textureUrl,
spritesheetData,
x,
y,
orientation,
isMoving = false,
isThinking = false,
isSpeaking = false,
emoji = '',
isViewer = false,
speed = 0.1,
onClick,
}: {
// Path to the texture packed image.
textureUrl: string;
// The data for the spritesheet.
spritesheetData: ISpritesheetData;
// The pose of the NPC.
x: number;
y: number;
orientation: number;
isMoving?: boolean;
// Shows a thought bubble if true.
isThinking?: boolean;
// Shows a speech bubble if true.
isSpeaking?: boolean;
emoji?: string;
// Highlights the player.
isViewer?: boolean;
// The speed of the animation. Can be tuned depending on the side and speed of the NPC.
speed?: number;
onClick: () => void;
}) => {
const [spriteSheet, setSpriteSheet] = useState<Spritesheet>();
useEffect(() => {
const parseSheet = async () => {
const sheet = new Spritesheet(
BaseTexture.from(textureUrl, {
scaleMode: PIXI.SCALE_MODES.NEAREST,
}),
spritesheetData,
);
await sheet.parse();
setSpriteSheet(sheet);
};
void parseSheet();
}, []);
// The first "left" is "right" but reflected.
const roundedOrientation = Math.floor(orientation / 90);
const direction = ['right', 'down', 'left', 'up'][roundedOrientation];
// Prevents the animation from stopping when the texture changes
// (see https://github.com/pixijs/pixi-react/issues/359)
const ref = useRef<PIXI.AnimatedSprite | null>(null);
useEffect(() => {
if (isMoving) {
ref.current?.play();
}
}, [direction, isMoving]);
if (!spriteSheet) return null;
let blockOffset = { x: 0, y: 0 };
switch (roundedOrientation) {
case 2:
blockOffset = { x: -20, y: 0 };
break;
case 0:
blockOffset = { x: 20, y: 0 };
break;
case 3:
blockOffset = { x: 0, y: -20 };
break;
case 1:
blockOffset = { x: 0, y: 20 };
break;
}
return (
<Container x={x} y={y} interactive={true} pointerdown={onClick} cursor="pointer">
{/* {isThinking && (
// TODO: We'll eventually have separate assets for thinking and speech animations.
<Text x={-20} y={-10} scale={{ x: -0.8, y: 0.8 }} text={'💭'} anchor={{ x: 0.5, y: 0.5 }} />
)} */}
{isSpeaking && (
// TODO: We'll eventually have separate assets for thinking and speech animations.
<Text x={18} y={-10} scale={0.8} text={'💬'} anchor={{ x: 0.5, y: 0.5 }} />
)}
{isViewer && <ViewerIndicator />}
<AnimatedSprite
ref={ref}
isPlaying={isMoving}
textures={spriteSheet.animations[direction]}
animationSpeed={speed}
anchor={{ x: 0.5, y: 0.5 }}
/>
{emoji && (
<Text x={0} y={-24} scale={{ x: -0.8, y: 0.8 }} text={emoji} anchor={{ x: 0.5, y: 0.5 }} />
)}
</Container>
);
};
function ViewerIndicator() {
const draw = useCallback((g: PIXI.Graphics) => {
g.clear();
g.beginFill(0xffff0b, 0.5);
g.drawRoundedRect(-10, 10, 20, 10, 100);
g.endFill();
}, []);
return <Graphics draw={draw} />;
}