fix(pets): use preferWebGLVersion:1 for cat canvas to avoid context conflicts
Cat gets its own canvas with WebGL1 context (Kira uses WebGL2 by default). Different GL versions don't share buffers, so no bindBuffer spam. Cat now renders in the PetZone section of the right sidebar where it belongs. Removed all shared-context onAppReady plumbing.
This commit is contained in:
@@ -33,7 +33,6 @@ export default function App() {
|
||||
const [currentOutfit, setCurrentOutfit] = useState('cozy-hoodie');
|
||||
const [currentAcc, setCurrentAcc] = useState<string | null>(null);
|
||||
const [textInput, setTextInput] = useState('');
|
||||
const [pixiApp, setPixiApp] = useState<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (preferences.scene) setCurrentSceneId(preferences.scene);
|
||||
@@ -148,7 +147,6 @@ export default function App() {
|
||||
outfit={currentOutfit}
|
||||
accessory={currentAcc}
|
||||
onTalkToggle={handleTalkToggle}
|
||||
onAppReady={setPixiApp}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -157,7 +155,7 @@ export default function App() {
|
||||
<MusicPlayer />
|
||||
<WhiteNoise />
|
||||
<Wardrobe onOutfitChange={handleOutfitChange} onAccessoryChange={handleAccessoryChange} />
|
||||
<PetZone app={pixiApp} />
|
||||
<PetZone />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ interface Props {
|
||||
outfit: string;
|
||||
accessory: string | null;
|
||||
onTalkToggle: () => void;
|
||||
onAppReady?: (app: any) => void;
|
||||
}
|
||||
|
||||
type ExpressionName = 'Normal' | 'Smile' | 'Sad' | 'Angry' | 'Surprised' | 'Blushing';
|
||||
@@ -83,7 +82,6 @@ export default function KiraAvatar(props: Props) {
|
||||
app.stage.addChild(model as any);
|
||||
(model as any).isInteractive = () => false;
|
||||
if (!mounted) return;
|
||||
if (props.onAppReady) props.onAppReady(app);
|
||||
|
||||
try {
|
||||
textureRef.current = {
|
||||
|
||||
@@ -1,42 +1,56 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
interface Props {
|
||||
app: any; // shared Pixi Application from KiraAvatar
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function Live2DCat({ app }: Props) {
|
||||
const modelRef = useRef<any>(null);
|
||||
export default function Live2DCat({ className }: Props) {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!app) return;
|
||||
let mounted = true;
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
let app: any = null;
|
||||
|
||||
const init = async () => {
|
||||
try {
|
||||
const pixi = await import('pixi.js');
|
||||
const { Live2DModel } = await import('pixi-live2d-display/cubism4');
|
||||
const { Ticker } = await import('pixi.js');
|
||||
(Live2DModel as any).registerTicker(Ticker as any);
|
||||
(Live2DModel as any).registerTicker(pixi.Ticker as any);
|
||||
|
||||
const w = canvas.clientWidth || 120;
|
||||
const h = canvas.clientHeight || 120;
|
||||
|
||||
// Use explicit CanvasRenderer to avoid WebGL context conflict with KiraAvatar
|
||||
app = new pixi.Application({
|
||||
view: canvas,
|
||||
width: w,
|
||||
height: h,
|
||||
antialias: true,
|
||||
resolution: Math.min(window.devicePixelRatio || 1, 2),
|
||||
backgroundAlpha: 0,
|
||||
autoDensity: true,
|
||||
// Use WebGL1 context (more compatible with separate contexts)
|
||||
preferWebGLVersion: 1,
|
||||
} as any);
|
||||
if (!mounted) { app.destroy(true); return; }
|
||||
|
||||
const model = await Live2DModel.from('/live2d/models/little-cat/LittleCat.model3.json', {
|
||||
autoInteract: false,
|
||||
});
|
||||
if (!mounted) { model.destroy(); return; }
|
||||
if (!mounted) { app.destroy(true); return; }
|
||||
|
||||
// Scale cat to ~15% of avatar canvas height
|
||||
const targetH = app.screen.height * 0.18;
|
||||
const s = targetH / model.height;
|
||||
const sw = app.screen.width;
|
||||
const sh = app.screen.height;
|
||||
const s = Math.min((sw * 0.85) / model.width, (sh * 0.85) / model.height);
|
||||
model.scale.set(s);
|
||||
model.anchor.set(0.5, 0.5);
|
||||
|
||||
// Bottom-right corner of Kira's canvas
|
||||
model.position.set(
|
||||
app.screen.width - (model.width * s) / 2 - 10,
|
||||
app.screen.height - (model.height * s) / 2 - 10
|
||||
);
|
||||
model.position.set(sw / 2, sh / 2);
|
||||
|
||||
app.stage.addChild(model as any);
|
||||
(model as any).isInteractive = () => false;
|
||||
modelRef.current = model;
|
||||
|
||||
try { model.motion('Idle'); } catch { /* */ }
|
||||
} catch (e) {
|
||||
@@ -48,13 +62,15 @@ export default function Live2DCat({ app }: Props) {
|
||||
|
||||
return () => {
|
||||
mounted = false;
|
||||
if (modelRef.current && app) {
|
||||
app.stage.removeChild(modelRef.current);
|
||||
modelRef.current.destroy();
|
||||
modelRef.current = null;
|
||||
}
|
||||
if (app) { app.destroy(true, { children: true }); }
|
||||
};
|
||||
}, [app]);
|
||||
}, []);
|
||||
|
||||
return null; // renders on KiraAvatar's shared canvas
|
||||
return (
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
className={className}
|
||||
style={{ display: 'block' }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
import Live2DCat from './Live2DCat';
|
||||
|
||||
interface Props {
|
||||
app?: any;
|
||||
}
|
||||
|
||||
export default function PetZone({ app }: Props) {
|
||||
export default function PetZone() {
|
||||
return (
|
||||
<div className="p-4 relative overflow-hidden" style={{ minHeight: 140 }}>
|
||||
<div className="p-4 relative overflow-hidden" style={{ minHeight: 160 }}>
|
||||
<h3 className="text-sm font-bold text-kira-plum mb-2 flex items-center gap-2">
|
||||
<span>🐱</span> Pet Zone
|
||||
</h3>
|
||||
{app ? (
|
||||
<div className="flex flex-col items-center">
|
||||
<Live2DCat app={app} />
|
||||
<span className="text-[10px] text-kira-plum/60 mt-1 font-medium">Mochi</span>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-xs text-kira-plum/30">loading...</p>
|
||||
)}
|
||||
<div className="flex flex-col items-center">
|
||||
<Live2DCat className="w-28 h-28" />
|
||||
<span className="text-[10px] text-kira-plum/60 mt-1 font-medium">Mochi</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user