-
{i + 1 + '. ' + e.name}
+ {players.map((player, i) => (
+
+
{i + 1 + '. ' + player.playerName}
))}
diff --git a/FE/src/components/Player.tsx b/FE/src/components/Player.tsx
new file mode 100644
index 0000000..313d406
--- /dev/null
+++ b/FE/src/components/Player.tsx
@@ -0,0 +1,19 @@
+type Props = {
+ name: string;
+ position: [number, number];
+};
+
+export const Player = ({ name, position }: Props) => {
+ const [xPos, yPos] = position;
+ // const randomX = xPos + Math.floor(Math.random() * 100) + 1; // 1~100 범위의 랜덤값을 xPos에 추가
+ // const randomY = yPos + Math.floor(Math.random() * 100) + 1;
+
+ const top = xPos * 100 + '%';
+ const left = yPos * 100 + '%';
+ console.log(top, left);
+ return (
+
+ {'😀' + name}
+
+ );
+};
diff --git a/FE/src/components/QuizOptionBoard.tsx b/FE/src/components/QuizOptionBoard.tsx
index 8db97cf..1ac3e43 100644
--- a/FE/src/components/QuizOptionBoard.tsx
+++ b/FE/src/components/QuizOptionBoard.tsx
@@ -1,3 +1,5 @@
+import { usePlayerStore } from '@/store/usePlayerStore';
+import { Player } from './Player';
type Params = {
options: string[];
};
@@ -16,17 +18,25 @@ const optionColors = [
];
export const QuizOptionBoard = ({ options }: Params) => {
+ const players = usePlayerStore((state) => state.players);
return (
-
- {options.map((option, i) => (
-
- {i + 1 + '. ' + option}
-
- ))}
+
+
+ {players.map((player) => (
+
+ ))}
+
+
+ {options.map((option, i) => (
+
+ {i + 1 + '. ' + option}
+
+ ))}
+
);
};
diff --git a/FE/src/pages/GamePage.tsx b/FE/src/pages/GamePage.tsx
index cc544df..c22115d 100644
--- a/FE/src/pages/GamePage.tsx
+++ b/FE/src/pages/GamePage.tsx
@@ -2,21 +2,29 @@ import Chat from '@/components/Chat';
import ParticipantDisplay from '@/components/ParticipantDisplay';
import { QuizOptionBoard } from '@/components/QuizOptionBoard';
import { Modal } from '../components/Modal';
-import { useState } from 'react';
+import { useState, useEffect } from 'react';
import { GameHeader } from '@/components/GameHeader';
import { HeaderBar } from '@/components/HeaderBar';
-import { useParams } from 'react-router-dom';
import { socketService } from '@/api/socket';
+import { useParams } from 'react-router-dom';
+import { useRoomStore } from '@/store/useRoomStore';
export const GamePage = () => {
+ const { gameId } = useParams<{ gameId: string }>();
+ const updateRoom = useRoomStore((state) => state.updateRoom);
const [playerName, setPlayerName] = useState('');
const [isModalOpen, setIsModalOpen] = useState(true);
- const pin = useParams();
+
+ updateRoom({ gameId });
+
+ useEffect(() => {
+ if (gameId && playerName) {
+ socketService.joinRoom(gameId, playerName);
+ }
+ }, [gameId, playerName]);
const handleNameSubmit = (name: string) => {
setPlayerName(name);
- // 닉네임 설정 소켓 요청
- socketService.joinRoom(String(pin), name);
setIsModalOpen(false); // 이름이 설정되면 모달 닫기
};
@@ -29,7 +37,7 @@ export const GamePage = () => {
-
+
@@ -37,7 +45,7 @@ export const GamePage = () => {
{
+ const gameId = useRoomStore((state) => state.gameId);
const [title, setTitle] = useState('');
const [maxPlayerCount, setMaxPlayerCount] = useState(RoomConfig.DEFAULT_PLAYERS);
const [gameMode, setGameMode] = useState<'SURVIVAL' | 'RANKING'>('RANKING');
const [roomPublic, setRoomPublic] = useState(true);
+ const navigate = useNavigate();
+
+ useEffect(() => {
+ if (gameId) navigate(`/game/${gameId}`);
+ }, [gameId, navigate]);
const handleModeChange = (e: React.ChangeEvent) => {
const value = e.target.value === 'RANKING' ? 'RANKING' : 'SURVIVAL';
@@ -31,7 +38,6 @@ export const GameSetupPage = () => {
gameMode,
isPublic: roomPublic
};
-
socketService.createRoom(roomData);
};
diff --git a/FE/src/store/useChatStore.ts b/FE/src/store/useChatStore.ts
new file mode 100644
index 0000000..01cc62f
--- /dev/null
+++ b/FE/src/store/useChatStore.ts
@@ -0,0 +1,18 @@
+import { socketService } from '@/api/socket';
+import { create } from 'zustand';
+
+type ChatStore = {
+ messages: { playerName: string; message: string }[];
+ addMessage: (playerName: string, message: string) => void;
+};
+
+export const useChatStore = create((set) => ({
+ messages: [],
+ addMessage: (playerName: string, message: string) => {
+ set((state) => ({ messages: [...state.messages, { playerName, message }] }));
+ }
+}));
+
+socketService.on('chatMessage', (data) => {
+ useChatStore.getState().addMessage(data.playerName, data.message);
+});
diff --git a/FE/src/store/usePlayerStore.ts b/FE/src/store/usePlayerStore.ts
new file mode 100644
index 0000000..9ba55fb
--- /dev/null
+++ b/FE/src/store/usePlayerStore.ts
@@ -0,0 +1,51 @@
+import { socketService } from '@/api/socket';
+import { create } from 'zustand';
+
+type Player = {
+ playerId: string; // socketId
+ playerName: string;
+ playerPosition: [number, number]; // [x, y] 좌표
+};
+
+type PlayerStore = {
+ players: Player[];
+ addPlayers: (players: Player[]) => void;
+ updatePlayerPosition: (playerId: string, newPosition: [number, number]) => void; // 위치 업데이트
+ removePlayer: (playerId: string) => void;
+};
+
+export const usePlayerStore = create((set) => ({
+ players: [],
+
+ addPlayers: (players) => {
+ set((state) => ({
+ players: [...state.players, ...players]
+ }));
+ },
+
+ updatePlayerPosition: (playerId, newPosition) => {
+ set((state) => ({
+ players: state.players.map((player) =>
+ player.playerId === playerId ? { ...player, playerPosition: newPosition } : player
+ )
+ }));
+ },
+
+ removePlayer: (playerId) => {
+ set((state) => ({
+ players: state.players.filter((player) => player.playerId !== playerId)
+ }));
+ }
+}));
+
+socketService.on('joinRoom', (data) => {
+ usePlayerStore.getState().addPlayers(data.players);
+});
+
+socketService.on('updatePosition', (data) => {
+ usePlayerStore.getState().updatePlayerPosition(data.playerId, data.playerPosition);
+});
+
+socketService.on('exitRoom', (data) => {
+ usePlayerStore.getState().removePlayer(data.playerId);
+});
diff --git a/FE/src/store/useRoomStore.ts b/FE/src/store/useRoomStore.ts
new file mode 100644
index 0000000..0316e8b
--- /dev/null
+++ b/FE/src/store/useRoomStore.ts
@@ -0,0 +1,38 @@
+import { socketService } from '@/api/socket';
+import { create } from 'zustand';
+
+type RoomOption = {
+ title?: string;
+ gameMode?: 'RANKING' | 'SURVIVAL';
+ maxPlayerCount?: number;
+ isPublic?: boolean;
+ gameId?: string;
+};
+
+type RoomStore = {
+ title: string;
+ gameMode: 'RANKING' | 'SURVIVAL';
+ maxPlayerCount: number;
+ isPublic: boolean;
+ gameId: string;
+ updateRoom: (roomOption: RoomOption) => void;
+};
+
+export const useRoomStore = create((set) => ({
+ title: '',
+ gameMode: 'SURVIVAL',
+ maxPlayerCount: 50,
+ isPublic: true,
+ gameId: '',
+ updateRoom: (roomOption: RoomOption) => {
+ set(() => roomOption);
+ }
+}));
+
+socketService.on('createRoom', (data) => {
+ useRoomStore.getState().updateRoom({ gameId: data.gameId });
+});
+
+socketService.on('updateRoomOption', (data) => {
+ useRoomStore.getState().updateRoom(data);
+});