diff --git a/client/src/pixi/Canvas.tsx b/client/src/pixi/Canvas.tsx index 91b631c4..4f689783 100644 --- a/client/src/pixi/Canvas.tsx +++ b/client/src/pixi/Canvas.tsx @@ -14,6 +14,7 @@ import { } from "../model/position"; import Explorer from "./components/Explorer"; import PIXI from "pixi.js"; +import useExplorerDispatcher from "../api/explorer"; const mountHandler = import.meta.env.DEV ? (app: PIXI.Application) => { @@ -40,6 +41,7 @@ const Canvas: React.FC = (props) => { } | null>(null); const [intervalID, setIntervalID] = useState(null); const stageRef = useRef(null); + const dispatcher = useExplorerDispatcher(); useEffect(() => { const width = (window.innerWidth * 3) / 5; @@ -66,25 +68,36 @@ const Canvas: React.FC = (props) => { }; }, [fieldSize]); - const updateUserPosition = useCallback((targetPosition: Position) => { - setUserPosition((position) => { - if (position === null) { - return null; - } - const diff = { - x: targetPosition.x - position.x, - y: targetPosition.y - position.y, - }; - if (Math.abs(diff.x) < 3 && Math.abs(diff.y) < 3) { - return targetPosition; - } - const nextPosition = calcNewPosition(position, { - x: diff.x / 10, - y: diff.y / 10, + const updateUserPosition = useCallback( + (targetPosition: Position) => { + setUserPosition((position) => { + if (position === null || fieldSize === null) { + return null; + } + const diff = { + x: targetPosition.x - position.x, + y: targetPosition.y - position.y, + }; + if (Math.abs(diff.x) < 3 && Math.abs(diff.y) < 3) { + dispatcher({ + position: targetPosition, + size: fieldSize, + }); + return targetPosition; + } + const nextPosition = calcNewPosition(position, { + x: diff.x / 10, + y: diff.y / 10, + }); + dispatcher({ + position: nextPosition, + size: fieldSize, + }); + return nextPosition; }); - return nextPosition; - }); - }, []); + }, + [dispatcher, fieldSize], + ); const onFieldClick = useCallback( (e: React.MouseEvent) => { diff --git a/client/src/pixi/World.tsx b/client/src/pixi/World.tsx index 57eb30ec..afcfb140 100644 --- a/client/src/pixi/World.tsx +++ b/client/src/pixi/World.tsx @@ -12,6 +12,10 @@ import messagesAtom from "../state/message"; import MessageIcon from "./components/MessageIcon"; import useMessageExpanded from "./hooks/message"; import { isInsideField } from "../util/field"; +import speakerPhonesAtom from "../state/speakerPhone"; +import reactionsAtom from "../state/reactions"; +import explorersAtom from "../state/explorer"; +import OtherExplorer from "./components/OtherExplorer"; interface Props { userPosition: Position; @@ -27,7 +31,11 @@ const World: React.FC = ({ const { expanded, collapseMessage, expandMessage, message } = useMessageExpanded(); const messages = useAtomValue(messagesAtom); - const messageNodes = []; + const speakerPhones = useAtomValue(speakerPhonesAtom); + const reactions = useAtomValue(reactionsAtom); + const explorers = useAtomValue(explorersAtom); + + const messageNodes: JSX.Element[] = []; for (const message of messages.values()) { if (!isInsideField(message.position, fieldSize, userPosition)) { continue; @@ -42,24 +50,88 @@ const World: React.FC = ({ ); } - //TODO: モック用なので後で消す - for (let i = 1; i <= 3; i++) { - messageNodes.push( - + isInsideField(speakerPhone.position, fieldSize, userPosition), + ) + .map((speakerPhone) => ( + + )); + + const reactionsNodes = Array.from(reactions.values()) + .filter((reaction) => + isInsideField(reaction.position, fieldSize, userPosition), + ) + .map((reaction) => ( + , + /> + )); + + const explorerNodes = Array.from(explorers.values()).map((explorer) => { + return ( + ); + }); + + //TODO: モック用なので後で消す + { + for (let i = 1; i <= 3; i++) { + messageNodes.push( + , + ); + } + for (let i = 4; i <= 6; i++) { + speakerPhoneNodes.push( + , + ); + } + for (let i = 7; i <= 9; i++) { + reactionsNodes.push( + , + ); + } } return ( @@ -79,42 +151,10 @@ const World: React.FC = ({ fillColor={0xeeeeee} fillAlpha={1} /> - - - + {speakerPhoneNodes} {messageNodes} - - - + {explorerNodes} + {reactionsNodes} = ({ explorer, previousPosition }) => { + // previousPosition: 1つ前の受信時の位置 + const { position: targetPosition, userId } = explorer; // 目標の位置 + const { data, error, isLoading } = useUser(userId); + const [position, setPosition] = useState(targetPosition); // 実際の現在の位置 + + useTick(() => { + if (previousPosition) { + if (position.x === targetPosition.x && position.y === targetPosition.y) { + return; + } + setPosition((pos) => { + const diff = { + x: targetPosition.x - pos.x, + y: targetPosition.y - pos.y, + }; + if (Math.abs(diff.x) < 3 && Math.abs(diff.y) < 3) { + return targetPosition; + } + const speed = 0.1; + return { + x: position.x + diff.x * speed, + y: position.y + diff.y * speed, + }; + }); + } + }); + + if (isLoading || error || !data) { + return null; + } + const user = data.user; + if (!user) { + return null; + } + + return ( + + ); +}; + +export default OtherExplorer;