/** * @file Game Pad Node - Simple game controller with directional buttons */ import * as React from "react"; import type { NodeDefinition, NodeRendererProps, InspectorRenderProps, ExternalDataReference, } from "../../../../../types/NodeDefinition"; import classes from "./GamePadNode.module.css"; export type GamePadData = { id: string; position: { x: number; y: number }; // Current position (0-100) lastAction: "up" | "down" | "left" | "right" | "a" | "b" | null; moveSpeed: number; // pixels to move per button press }; export const GamePadRenderer = ({ node, isSelected, isDragging, externalData, onUpdateNode }: NodeRendererProps) => { const gamePadData = externalData as GamePadData | undefined; const [position, setPosition] = React.useState({ x: 50, y: 50 }); const [activeButton, setActiveButton] = React.useState(null); React.useEffect(() => { if (gamePadData?.position) { setPosition(gamePadData.position); } }, [gamePadData?.position]); const handleAction = (action: GamePadData["lastAction"]) => { if (!action) { return; } let newPosition = { ...position }; // Update position based on action switch (action) { case "up": newPosition.y = Math.max(0, position.y - (gamePadData?.moveSpeed || 10)); break; case "down": newPosition.y = Math.min(100, position.y + (gamePadData?.moveSpeed || 10)); break; case "left": newPosition.x = Math.max(0, position.x - (gamePadData?.moveSpeed || 10)); break; case "right": newPosition.x = Math.min(100, position.x + (gamePadData?.moveSpeed || 10)); break; case "a": case "b": // Action buttons don't change position newPosition = position; break; } setPosition(newPosition); setActiveButton(action); // Determine button states const isAButton = action === "a"; const isBButton = action === "b"; // Send data through output ports onUpdateNode({ data: { ...node.data, lastAction: action, position: newPosition, timestamp: Date.now(), // Output port data "position-output": newPosition, "a-output": isAButton, "b-output": isBButton, }, }); // Auto-release visual feedback and button states setTimeout(() => { setActiveButton(null); // Release button states after 200ms if (isAButton || isBButton) { onUpdateNode({ data: { ...node.data, lastAction: action, position: newPosition, "position-output": newPosition, "a-output": false, "b-output": false, }, }); } }, 200); }; return (
🎮 Game Pad
({Math.round(position.x)}, {Math.round(position.y)})
{gamePadData?.lastAction && (
Last: {gamePadData.lastAction.toUpperCase()}
)}
); }; export const GamePadInspectorRenderer = ({ externalData, onUpdateExternalData }: InspectorRenderProps) => { const gamePadData = externalData as GamePadData | undefined; const [editedData, setEditedData] = React.useState({ id: gamePadData?.id || "", position: gamePadData?.position || { x: 50, y: 50 }, lastAction: gamePadData?.lastAction || null, moveSpeed: gamePadData?.moveSpeed || 10, }); const handleSave = async () => { if (onUpdateExternalData) { await onUpdateExternalData(editedData); } }; return (

Game Pad Configuration

Current Position:
X: {Math.round(editedData.position.x)} / Y: {Math.round(editedData.position.y)}
setEditedData({ ...editedData, moveSpeed: Number(e.target.value) })} className={classes.slider} />
Last Action:
{editedData.lastAction?.toUpperCase() || "None"}

Use directional buttons to control position (0-100). Position and actions are sent to connected nodes through the Actions output port.

); }; export const GamePadNodeDefinition: NodeDefinition = { type: "gamepad", displayName: "Game Pad", description: "Simple game controller with directional buttons and position output", category: "Input", defaultData: { title: "Game Pad", }, defaultSize: { width: 280, height: 220 }, ports: [ { id: "position-output", type: "output", label: "Position", position: "right", }, { id: "a-output", type: "output", label: "A", position: "right", }, { id: "b-output", type: "output", label: "B", position: "right", }, ], renderNode: GamePadRenderer, renderInspector: GamePadInspectorRenderer, loadExternalData: async (ref: ExternalDataReference) => { await new Promise((resolve) => setTimeout(resolve, 200)); return { id: ref.id, position: { x: 50, y: 50 }, lastAction: null, moveSpeed: 10, }; }, updateExternalData: async (_ref: ExternalDataReference, data: unknown) => { await new Promise((resolve) => setTimeout(resolve, 200)); console.log("Updated gamepad data:", data); }, };