'use client' import { useEffect, useRef, useState, useCallback } from 'react' import { LearningNode } from '@/app/geo-lernwelt/types' interface UnityViewerProps { aoiId: string manifestUrl?: string learningNodes: LearningNode[] geoServiceUrl: string } interface UnityInstance { SendMessage: (objectName: string, methodName: string, value?: string | number) => void } export default function UnityViewer({ aoiId, manifestUrl, learningNodes, geoServiceUrl, }: UnityViewerProps) { const canvasRef = useRef(null) const [unityInstance, setUnityInstance] = useState(null) const [loadingProgress, setLoadingProgress] = useState(0) const [isLoading, setIsLoading] = useState(true) const [error, setError] = useState(null) const [selectedNode, setSelectedNode] = useState(null) const [showNodePanel, setShowNodePanel] = useState(false) // Placeholder mode when Unity build is not available const [placeholderMode, setPlaceholderMode] = useState(true) useEffect(() => { // Check if Unity build exists // For now, always use placeholder mode since Unity build needs to be created separately setPlaceholderMode(true) setIsLoading(false) }, []) // Unity message handler (for when Unity build exists) useEffect(() => { if (typeof window !== 'undefined') { // eslint-disable-next-line @typescript-eslint/no-explicit-any (window as any).handleUnityMessage = (message: string) => { try { const data = JSON.parse(message) switch (data.type) { case 'nodeSelected': const node = learningNodes.find((n) => n.id === data.nodeId) if (node) { setSelectedNode(node) setShowNodePanel(true) } break case 'terrainLoaded': console.log('Unity terrain loaded') break case 'error': setError(data.message) break } } catch (e) { console.error('Error parsing Unity message:', e) } } } return () => { if (typeof window !== 'undefined') { // eslint-disable-next-line @typescript-eslint/no-explicit-any delete (window as any).handleUnityMessage } } }, [learningNodes]) const sendToUnity = useCallback( (objectName: string, methodName: string, value?: string | number) => { if (unityInstance) { unityInstance.SendMessage(objectName, methodName, value) } }, [unityInstance] ) // Send AOI data to Unity when ready useEffect(() => { if (unityInstance && manifestUrl) { sendToUnity('TerrainManager', 'LoadManifest', manifestUrl) // Send learning nodes const nodesJson = JSON.stringify(learningNodes) sendToUnity('LearningNodeManager', 'LoadNodes', nodesJson) } }, [unityInstance, manifestUrl, learningNodes, sendToUnity]) const handleNodeClick = (node: LearningNode) => { setSelectedNode(node) setShowNodePanel(true) if (unityInstance) { sendToUnity('CameraController', 'FocusOnNode', node.id) } } const handleCloseNodePanel = () => { setShowNodePanel(false) setSelectedNode(null) } // Placeholder 3D view (when Unity is not available) const PlaceholderView = () => (
{/* Sky */}
{/* Sun */}
{/* Clouds */}
{/* Mountains */} {/* Background mountains */} {/* Foreground mountains */} {/* Ground */} {/* Learning Nodes as Markers */}
{learningNodes.map((node, idx) => { // Position nodes across the view const positions = [ { left: '20%', top: '55%' }, { left: '40%', top: '45%' }, { left: '60%', top: '50%' }, { left: '75%', top: '55%' }, { left: '30%', top: '60%' }, ] const pos = positions[idx % positions.length] return ( ) })}
{/* 3D View Notice */}

Vorschau-Modus

Unity WebGL-Build wird fuer die volle 3D-Ansicht benoetigt

{/* Controls hint */}

Klicke auf die Marker um Lernstationen anzuzeigen

) return (
{/* Loading overlay */} {isLoading && (

Lade 3D-Lernwelt... {loadingProgress}%

)} {/* Error display */} {error && (
⚠️

Fehler beim Laden

{error}

)} {/* Unity Canvas or Placeholder */} {placeholderMode ? ( ) : ( )} {/* Learning Node Panel */} {showNodePanel && selectedNode && (
{/* Header */}
Station {learningNodes.indexOf(selectedNode) + 1}

{selectedNode.title}

{/* Content */}
{/* Question */}

Aufgabe

{selectedNode.question}

{/* Hints */} {selectedNode.hints.length > 0 && (

Hinweise

    {selectedNode.hints.map((hint, idx) => (
  • {hint}
  • ))}
)} {/* Answer (collapsible) */}
Loesung anzeigen

{selectedNode.answer}

{selectedNode.explanation && (

{selectedNode.explanation}

)}
{/* Points */}
Punkte {selectedNode.points}
)} {/* Node List (minimized) */} {!showNodePanel && learningNodes.length > 0 && (

Lernstationen

{learningNodes.map((node, idx) => ( ))}
)}
) }