diff --git a/assets/audio/bridgebgm.mp3 b/assets/audio/bridgebgm.mp3
new file mode 100644
index 0000000..ff4a006
Binary files /dev/null and b/assets/audio/bridgebgm.mp3 differ
diff --git a/assets/audio/classroombgm.mp3 b/assets/audio/classroombgm.mp3
new file mode 100644
index 0000000..25408de
Binary files /dev/null and b/assets/audio/classroombgm.mp3 differ
diff --git a/assets/audio/culturelandbgm.mp3 b/assets/audio/culturelandbgm.mp3
new file mode 100644
index 0000000..f5c0f9b
Binary files /dev/null and b/assets/audio/culturelandbgm.mp3 differ
diff --git a/assets/audio/parkbgm.mp3 b/assets/audio/parkbgm.mp3
new file mode 100644
index 0000000..f1ddd5a
Binary files /dev/null and b/assets/audio/parkbgm.mp3 differ
diff --git a/assets/boy1.png b/assets/boy1.png
new file mode 100644
index 0000000..cb8b45b
Binary files /dev/null and b/assets/boy1.png differ
diff --git a/assets/boy2.png b/assets/boy2.png
new file mode 100644
index 0000000..344225d
Binary files /dev/null and b/assets/boy2.png differ
diff --git a/assets/boy3.png b/assets/boy3.png
new file mode 100644
index 0000000..90222ff
Binary files /dev/null and b/assets/boy3.png differ
diff --git a/assets/bridge1.png b/assets/bridge1.png
new file mode 100644
index 0000000..ca8863b
Binary files /dev/null and b/assets/bridge1.png differ
diff --git a/assets/bridge2.png b/assets/bridge2.png
new file mode 100644
index 0000000..bc97755
Binary files /dev/null and b/assets/bridge2.png differ
diff --git a/assets/bridge3.png b/assets/bridge3.png
new file mode 100644
index 0000000..0a66ba4
Binary files /dev/null and b/assets/bridge3.png differ
diff --git a/assets/chatbox.png b/assets/chatbox.png
new file mode 100644
index 0000000..0e49776
Binary files /dev/null and b/assets/chatbox.png differ
diff --git a/assets/classroom.png b/assets/classroom.png
new file mode 100644
index 0000000..6c824f6
Binary files /dev/null and b/assets/classroom.png differ
diff --git a/assets/cultureland.png b/assets/cultureland.png
new file mode 100644
index 0000000..0df28cc
Binary files /dev/null and b/assets/cultureland.png differ
diff --git a/assets/girl1.png b/assets/girl1.png
new file mode 100644
index 0000000..aa25791
Binary files /dev/null and b/assets/girl1.png differ
diff --git a/assets/girl2.png b/assets/girl2.png
new file mode 100644
index 0000000..d7d52d1
Binary files /dev/null and b/assets/girl2.png differ
diff --git a/assets/girl3.png b/assets/girl3.png
new file mode 100644
index 0000000..f58b66d
Binary files /dev/null and b/assets/girl3.png differ
diff --git a/assets/invisable.png b/assets/invisable.png
new file mode 100644
index 0000000..1914264
Binary files /dev/null and b/assets/invisable.png differ
diff --git a/assets/logo.png b/assets/logo.png
new file mode 100644
index 0000000..7ecbe3c
Binary files /dev/null and b/assets/logo.png differ
diff --git a/assets/main.jpg b/assets/main.jpg
new file mode 100644
index 0000000..ec56a40
Binary files /dev/null and b/assets/main.jpg differ
diff --git a/assets/nicknamebg.png b/assets/nicknamebg.png
new file mode 100644
index 0000000..6f7213b
Binary files /dev/null and b/assets/nicknamebg.png differ
diff --git a/assets/park.png b/assets/park.png
new file mode 100644
index 0000000..2892dc0
Binary files /dev/null and b/assets/park.png differ
diff --git a/assets/start_button.png b/assets/start_button.png
new file mode 100644
index 0000000..5330f1c
Binary files /dev/null and b/assets/start_button.png differ
diff --git a/assets/worldbg.png b/assets/worldbg.png
new file mode 100644
index 0000000..f23eaa5
Binary files /dev/null and b/assets/worldbg.png differ
diff --git a/bridgescene.js b/bridgescene.js
new file mode 100644
index 0000000..3730b16
--- /dev/null
+++ b/bridgescene.js
@@ -0,0 +1,488 @@
+import { stompClient } from './game.js';
+
+const SERVER_URL = 'https://kuriverse.shop';
+
+class BridgeScene extends Phaser.Scene {
+ constructor() {
+ super('BridgeScene');
+ this.currentDirection = null;
+ this.moveInterval = null;
+ this.otherPlayers = new Map();
+ this.bodytypeMap = new Map();
+ this.activeBubble = null;
+ this.activePortal = null;
+ this.chatInputField = null;
+ this.customizationSub = null;
+ this.positionsSub = null;
+ this.chatSub = null;
+ }
+
+ init(data) {
+ if (data && data.userInfo) {
+ window.userInfo = data.userInfo;
+ }
+ this.roomId = window.userInfo?.roomId || 4;
+ this.character = window.userInfo?.character || 'boy1';
+ this.nickname = window.userInfo?.nickname || '사용자';
+ }
+
+ preload() {
+ this.load.audio('bridgeBgm', 'assets/audio/bridgebgm.mp3');
+ const characterList = ['boy1', 'boy2', 'boy3', 'girl1', 'girl2', 'girl3'];
+ characterList.forEach(key => {
+ if (!this.textures.exists(key)) this.load.image(key, `assets/${key}.png`);
+ });
+
+ const bgMap = { 4: 'bridge3', 5: 'bridge2', 6: 'bridge1' };
+ const bgKey = bgMap[this.roomId] || 'bridge1';
+ if (!this.textures.exists(bgKey)) {
+ this.load.image(bgKey, `assets/${bgKey}.png`);
+ }
+ this.bgKeyToUse = bgKey;
+ }
+
+ isStompConnected() {
+ return stompClient && stompClient.active;
+ }
+
+ create() {
+ this.bgm = this.sound.add('bridgeBgm', { loop: true, volume: 0.1 });
+ this.bgm.play();
+ this.input.keyboard.enabled = true;
+ this.otherPlayers.clear();
+ this.bodytypeMap.clear();
+ this.roomNameMap = { 4: "통로 1", 5: "통로 2", 6: "통로 3" };
+ this.currentRoomName = this.roomNameMap[this.roomId] || "알 수 없는 통로";
+
+ this.events.on('shutdown', this.shutdown, this);
+
+ this.input.keyboard.on('keydown', this.handleKeyDown, this);
+ this.input.keyboard.on('keyup', this.handleKeyUp, this);
+
+ this.add.image(800, 450, this.bgKeyToUse).setDisplaySize(1600, 900).setDepth(0);
+ const titleText = this.currentRoomName;
+ this.add.rectangle(800, 50, 300, 60, 0xB593CC).setDepth(5).setStrokeStyle(2, 0xffffff);
+ this.add.text(800, 50, titleText, { fontSize: '32px', fontFamily: 'Pretendard', color: '#ffffff' }).setOrigin(0.5).setDepth(6);
+ this.add.rectangle(300, 750, 580, 200, 0x000000, 0.4).setDepth(2);
+
+ this.chatLogContainer = this.add.dom(300, 730).createFromHTML(`
+
+`).setOrigin(0.5).setDepth(6);
+
+ this.chatInput = this.add.dom(300, 850).createFromHTML(`
+
+
+
+
+`).setOrigin(0.5).setDepth(10);
+
+ this.chatInput.on('create', (dom) => {
+ this.chatInputField = dom.getChildByID('chat-message');
+ const sendButton = dom.getChildByID('send-btn');
+ const sendMessage = () => {
+ const message = this.chatInputField.value.trim();
+ if (message) {
+ this.addChatLog(`${this.nickname}: ${message}`);
+ this.chatInputField.value = '';
+ this.createBubble(message);
+ }
+ this.chatInputField.blur();
+ };
+
+ this.chatInputField.addEventListener('focus', () => this.input.keyboard.enabled = false);
+ this.chatInputField.addEventListener('blur', () => this.input.keyboard.enabled = true);
+ this.chatInputField.addEventListener('keydown', (event) => {
+ if (event.key === 'Enter') sendMessage();
+ });
+ sendButton.addEventListener('click', sendMessage);
+ });
+
+ this.addChatLog = (text) => {
+ const chatBox = this.chatLogContainer.getChildByID('chat-log-box');
+ if (chatBox) {
+ const p = document.createElement('p');
+ p.textContent = text;
+ p.style.margin = '0 0 6px 0';
+ chatBox.appendChild(p);
+ chatBox.scrollTop = chatBox.scrollHeight;
+ }
+ };
+
+ // 채팅 입력 이벤트 처리
+ this.time.delayedCall(100, () => {
+ const chatInputField = this.chatInput.getChildByID('chat-message');
+ const sendBtn = this.chatInput.getChildByID('send-btn');
+ // 채팅 전송 함수
+ const sendMessage = () => {
+ const message = chatInputField.value.trim();
+ if (message && this.isStompConnected()) {
+ stompClient.publish({
+ destination: '/app/chat.send',
+ body: JSON.stringify({
+ roomId: this.roomId,
+ nickname: this.nickname,
+ content: message
+ })
+ });
+ chatInputField.value = '';
+ } else if (!this.isStompConnected()) {
+ this.addChatLog('[시스템] 연결이 끊어졌습니다. 잠시 후 다시 시도해주세요.');
+ }
+ if (chatInputField.value.trim() === '') {
+ setTimeout(() => {
+ chatInputField.blur();
+ }, 10); // 약간의 딜레이를 주어 blur가 정상 동작하도록
+ }
+ };
+ // 엔터키 전송
+ chatInputField.addEventListener('keydown', (event) => {
+ if (event.key === 'Enter') {
+ event.preventDefault();
+ sendMessage();
+ }
+ });
+ // 버튼 클릭 전송
+ sendBtn.addEventListener('click', sendMessage);
+
+ // 입력창 포커스/블러에 따라 게임 키 입력 활성/비활성
+ chatInputField.addEventListener('focus', () => this.input.keyboard.enabled = false);
+ chatInputField.addEventListener('blur', () => this.input.keyboard.enabled = true);
+ });
+
+ this.player = this.physics.add.sprite(800, 650, this.character).setDisplaySize(100, 120).setCollideWorldBounds(true).setOrigin(0.5);
+ this.nicknameBg = this.add.rectangle(this.player.x, this.player.y + 78, 100, 22, 0x000000, 0.4).setOrigin(0.5).setDepth(5);
+ this.nicknameText = this.add.text(this.player.x, this.player.y + 78, this.nickname, { font: '14px Pretendard', fill: '#ffffff' }).setOrigin(0.5).setDepth(6);
+
+ // 포털 이동키
+ this.eKey = this.input.keyboard.addKey('E');
+ this.createPortals(this.roomId);
+
+ window.addEventListener('beforeunload', () => {
+ if (stompClient.active) {
+ this.leaveRoom(this.roomId, this.nickname);
+ stompClient.deactivate();
+ }
+ // 로그아웃 API 호출
+ navigator.sendBeacon('/api/users/logout');
+ });
+
+ this.initWebSocket(this.roomId, this.nickname);
+
+ const characterList = ['boy1', 'boy2', 'boy3', 'girl1', 'girl2', 'girl3'];
+ const npcCount = Phaser.Math.Between(5, 6);
+
+ for (let i = 0; i < npcCount; i++) {
+ const key = Phaser.Utils.Array.GetRandom(characterList);
+ const x = Phaser.Math.Between(100, 1500);
+ const y = Phaser.Math.Between(600, 880);
+
+ const npc = this.add.sprite(x, y, key)
+ .setDisplaySize(100, 120)
+ .setDepth(1);
+ npc.setFlipX(Math.random() < 0.5); // 랜덤 방향
+ }
+ }
+
+ async joinRoom(roomId, nickname) {
+ try {
+ await fetch(`${SERVER_URL}/api/rooms/${roomId}/join`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ nickname })
+ });
+ console.log('방 입장 성공');
+ } catch (e) {
+ console.error('방 입장 오류:', e);
+ }
+ }
+
+ async leaveRoom(roomId, nickname) {
+ try {
+ await fetch(`${SERVER_URL}/api/rooms/${roomId}/leave`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ nickname: nickname })
+ });
+ console.log("방 퇴장 완료");
+ } catch (error) {
+ console.error("퇴장 오류:", error);
+ }
+ }
+
+ async getAllCustomizations() {
+ try {
+ const response = await fetch(`${SERVER_URL}/api/customization/all`);
+ if (!response.ok) {
+ throw new Error(`전체 외형 정보 로딩 실패: ${response.status}`);
+ }
+ const allCustoms = await response.json();
+ allCustoms.forEach(custom => {
+ const bodytype = custom.bodytype || custom.bodyType;
+ this.bodytypeMap.set(custom.nickname, bodytype);
+ });
+ } catch (e) {
+ console.error('전체 외형 정보 로딩 중 오류:', e);
+ }
+ }
+
+ async initWebSocket(roomId, nickname) {
+ await this.joinRoom(roomId, nickname);
+ await this.getAllCustomizations();
+
+ if (stompClient.active) {
+ this.setupSubscriptions();
+ stompClient.publish({ destination: "/app/move", body: JSON.stringify({ nickname, direction: "init", roomId, x: 800 / 40, y: 650 / 40 }) });
+ } else {
+ stompClient.onConnect = () => {
+ this.setupSubscriptions();
+ stompClient.publish({ destination: "/app/move", body: JSON.stringify({ nickname, direction: "init", roomId, x: 800 / 40, y: 650 / 40 }) });
+ };
+ }
+ }
+
+ setupSubscriptions() {
+ const bodytypeToCharacter = { 1: 'boy1', 2: 'boy2', 3: 'boy3', 4: 'girl1', 5: 'girl2', 6: 'girl3' };
+
+ if (this.chatSub) this.chatSub.unsubscribe();
+ if (this.positionsSub) this.positionsSub.unsubscribe();
+ if (this.customizationSub) this.customizationSub.unsubscribe();
+
+ this.customizationSub = stompClient.subscribe('/topic/customization', (message) => {
+ const data = JSON.parse(message.body);
+ const bodytype = data.bodytype || data.bodyType;
+ this.bodytypeMap.set(data.nickname, bodytype);
+ if (this.otherPlayers.has(data.nickname)) {
+ const player = this.otherPlayers.get(data.nickname);
+ const charKey = bodytypeToCharacter[bodytype] || 'boy1';
+ player.sprite.setTexture(charKey);
+ }
+ });
+
+ this.positionsSub = stompClient.subscribe('/topic/positions', (message) => {
+ const positions = JSON.parse(message.body);
+ const usersInSameRoom = positions.filter(pos => pos.roomName === this.currentRoomName);
+ const receivedNicknames = new Set(usersInSameRoom.map(p => p.nickname));
+ const myData = positions.find(pos => pos.nickname === this.nickname);
+ if (myData && this.player) {
+ const SCALE = 16;
+ this.player.setPosition(myData.x * SCALE, myData.y * SCALE);
+ }
+ usersInSameRoom.forEach(pos => {
+ if (pos.nickname === this.nickname) return;
+ const SCALE = 16;
+ const targetX = pos.x * SCALE;
+ const targetY = pos.y * SCALE;
+ if (!this.otherPlayers.has(pos.nickname)) {
+ const bodytype = this.bodytypeMap.get(pos.nickname);
+ const charKey = bodytypeToCharacter[bodytype] || 'boy1';
+ const otherSprite = this.physics.add.sprite(targetX, targetY, charKey).setDisplaySize(100, 120);
+ const nicknameBg = this.add.rectangle(targetX, targetY + 78, 100, 22, 0x000000, 0.4).setOrigin(0.5).setDepth(5);
+ const nicknameText = this.add.text(targetX, targetY + 78, pos.nickname, { font: '14px Pretendard', fill: '#ffffff' }).setOrigin(0.5).setDepth(6);
+ this.otherPlayers.set(pos.nickname, { sprite: otherSprite, nicknameBg, nicknameText, chatBubble: null });
+ } else {
+ const playerObj = this.otherPlayers.get(pos.nickname);
+ playerObj.sprite.setPosition(targetX, targetY);
+ }
+ });
+ this.otherPlayers.forEach((playerData, nick) => {
+ if (!receivedNicknames.has(nick)) {
+ playerData.sprite.destroy(); playerData.nicknameBg.destroy(); playerData.nicknameText.destroy();
+ this.otherPlayers.delete(nick); this.bodytypeMap.delete(nick);
+ }
+ });
+ });
+
+ stompClient.subscribe(`/queue/warnings/${this.nickname}`, (msg) => {
+ this.addChatLog(`[⚠️ 경고] ${msg.body}`);
+ });
+
+ // 에러 메시지 구독 추가
+ stompClient.subscribe(`/queue/errors/${this.nickname}`, (msg) => {
+ this.addChatLog(`[❌ 에러] ${msg.body}`);
+ });
+
+ this.chatSub = stompClient.subscribe(`/topic/room/${this.roomId}`, (msg) => {
+ let chat;
+ try {
+ chat = JSON.parse(msg.body);
+ } catch (e) {
+ chat = { nickname: '시스템', content: msg.body };
+ }
+ const sender = chat.nickname;
+ const content = chat.content;
+ this.addChatLog(`[${sender}] ${content}`);
+ // 자신의 말풍선 표시
+ if (sender === this.nickname) {
+ this.showChatBubble(this.player, content, true);
+ }
+ // 타 플레이어의 말풍선 표시
+ else if (this.otherPlayers.has(sender)) {
+ this.showChatBubble(this.otherPlayers.get(sender).sprite, content, false, sender);
+ }
+ });
+ }
+
+ showChatBubble(targetSprite, message, isMe, nickname = null) {
+ // 자신의 말풍선 or 타 플레이어 말풍선이 이미 있으면 제거
+ if (isMe && this.activeBubble) {
+ this.activeBubble.destroy();
+ this.activeBubble = null;
+ }
+ if (!isMe && nickname && this.otherPlayers.get(nickname)?.chatBubble) {
+ this.otherPlayers.get(nickname).chatBubble.destroy();
+ this.otherPlayers.get(nickname).chatBubble = null;
+ }
+
+ const bubbleWidth = 220;
+ const bubbleHeight = 90;
+ const tailSize = 12;
+
+ // 말풍선 박스
+ const bubbleRect = this.add.graphics();
+ bubbleRect.fillStyle(0xffffff, 1);
+ bubbleRect.fillRoundedRect(0, 0, bubbleWidth, bubbleHeight, 10);
+
+ // 말풍선 꼬리
+ const tail = this.add.graphics();
+ tail.fillStyle(0xffffff, 1);
+ tail.beginPath();
+ tail.moveTo(bubbleWidth / 2 - tailSize, bubbleHeight);
+ tail.lineTo(bubbleWidth / 2, bubbleHeight + tailSize);
+ tail.lineTo(bubbleWidth / 2 + tailSize, bubbleHeight);
+ tail.closePath();
+ tail.fillPath();
+
+ // 메시지 텍스트
+ const msgText = this.add.text(bubbleWidth / 2, bubbleHeight / 2, message, {
+ font: '14px Pretendard',
+ color: '#000000',
+ align: 'center',
+ wordWrap: { width: bubbleWidth - 20 }
+ }).setOrigin(0.5);
+
+ // 말풍선 컨테이너 (타겟 스프라이트 기준 위치)
+ const bubble = this.add.container(
+ targetSprite.x - bubbleWidth / 2,
+ targetSprite.y - 150,
+ [bubbleRect, tail, msgText]
+ ).setDepth(6);
+
+ // 3초 후 말풍선 제거
+ this.time.delayedCall(3000, () => {
+ bubble.destroy();
+ if (isMe) this.activeBubble = null;
+ else if (nickname && this.otherPlayers.get(nickname)) {
+ this.otherPlayers.get(nickname).chatBubble = null;
+ }
+ });
+
+ // 자신 or 타 플레이어 말풍선 저장
+ if (isMe) this.activeBubble = bubble;
+ else if (nickname && this.otherPlayers.get(nickname)) {
+ this.otherPlayers.get(nickname).chatBubble = bubble;
+ }
+ }
+
+ createPortals(roomId) {
+ const portalConfig = {
+ 4: [{ x: 100, y: 670, target: { scene: 'MainScene', roomId: 1 } }, { x: 1500, y: 670, target: { scene: 'MainScene', roomId: 3 } }],
+ 5: [{ x: 100, y: 640, target: { scene: 'MainScene', roomId: 2 } }, { x: 1500, y: 640, target: { scene: 'MainScene', roomId: 1 } }],
+ 6: [{ x: 100, y: 640, target: { scene: 'MainScene', roomId: 3 } }, { x: 1500, y: 640, target: { scene: 'MainScene', roomId: 2 } }]
+ };
+ if (!portalConfig[roomId]) return;
+
+ this.portals = this.physics.add.staticGroup();
+ portalConfig[roomId].forEach(portalInfo => {
+ const p = this.add.circle(portalInfo.x, portalInfo.y, 30, 0xFF00FF, 0.3).setDepth(4);
+ this.portals.add(p);
+ p.setData('target', portalInfo.target);
+ });
+ }
+
+ update() {
+ if (!this.player) return;
+
+ this.activePortal = null;
+ this.physics.world.overlap(this.player, this.portals, (player, portal) => {
+ this.activePortal = portal.getData('target');
+ }, null, this);
+
+ if (this.activeBubble) this.activeBubble.setPosition(this.player.x - 110, this.player.y - 150);
+
+ if (this.nicknameText && this.nicknameBg) {
+ this.nicknameText.setPosition(this.player.x, this.player.y + 78);
+ this.nicknameBg.setPosition(this.player.x, this.player.y + 78);
+ }
+
+ this.otherPlayers.forEach(playerObj => {
+ const { sprite, nicknameBg, nicknameText, chatBubble } = playerObj;
+ nicknameBg.setPosition(sprite.x, sprite.y + 78);
+ nicknameText.setPosition(sprite.x, sprite.y + 78);
+ if (chatBubble) chatBubble.setPosition(sprite.x - 110, sprite.y - 150);
+ });
+
+ if (this.activePortal && Phaser.Input.Keyboard.JustDown(this.eKey)) {
+ this.input.keyboard.enabled = false;
+ if (this.bgm) {
+ this.bgm.stop();
+ this.bgm.destroy();
+ this.bgm = null;
+ }
+ this.leaveRoom(this.roomId, this.nickname).finally(() => {
+ window.userInfo.roomId = this.activePortal.roomId;
+ this.scene.start(this.activePortal.scene, { userInfo: window.userInfo });
+ });
+ this.activePortal = null;
+ }
+ }
+
+ handleKeyDown(event) {
+ if (!this.input.keyboard.enabled) return;
+ const keyMap = { ArrowUp: 'w', ArrowDown: 's', ArrowLeft: 'a', ArrowRight: 'd' };
+ const dir = keyMap[event.key] || (['w', 'a', 's', 'd'].includes(event.key.toLowerCase()) ? event.key.toLowerCase() : null);
+ if (dir && dir !== this.currentDirection) {
+ this.currentDirection = dir;
+ if (this.moveInterval) clearInterval(this.moveInterval);
+ const payload = { nickname: this.nickname, direction: dir, roomId: this.roomId };
+ stompClient.publish({ destination: "/app/move", body: JSON.stringify(payload) });
+ this.moveInterval = setInterval(() => {
+ stompClient.publish({ destination: "/app/move", body: JSON.stringify(payload) });
+ }, 100);
+ }
+ }
+
+ handleKeyUp(event) {
+ if (!this.input.keyboard.enabled) return;
+ const dirKeys = ['w', 'a', 's', 'd', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
+ if (dirKeys.includes(event.key)) {
+ if (this.moveInterval) clearInterval(this.moveInterval);
+ this.moveInterval = null;
+ this.currentDirection = null;
+ stompClient.publish({ destination: "/app/move", body: JSON.stringify({ nickname: this.nickname, direction: "stop", roomId: this.roomId }) });
+ }
+ }
+
+ shutdown() {
+ console.log(`BridgeScene shutdown: Cleaning up...`);
+ this.input.keyboard.off('keydown', this.handleKeyDown, this);
+ this.input.keyboard.off('keyup', this.handleKeyUp, this);
+ if (this.customizationSub) this.customizationSub.unsubscribe();
+ if (this.positionsSub) this.positionsSub.unsubscribe();
+ if (this.chatSub) this.chatSub.unsubscribe();
+ if (this.moveInterval) clearInterval(this.moveInterval);
+ if (this.bgm) {
+ this.bgm.stop();
+ this.bgm.destroy();
+ this.bgm = null;
+ }
+ }
+}
+
+export default BridgeScene;
\ No newline at end of file
diff --git a/characterselectscene.js b/characterselectscene.js
new file mode 100644
index 0000000..4cfad46
--- /dev/null
+++ b/characterselectscene.js
@@ -0,0 +1,92 @@
+const SERVER_URL = 'https://kuriverse.shop';
+
+class CharacterSelectScene extends Phaser.Scene {
+ constructor() {
+ super('CharacterSelectScene');
+ }
+
+ preload() {
+ this.load.image('character_bg', 'assets/nicknamebg.png');
+ this.load.image('boy1', 'assets/boy1.png');
+ this.load.image('boy2', 'assets/boy2.png');
+ this.load.image('boy3', 'assets/boy3.png');
+ this.load.image('girl1', 'assets/girl1.png');
+ this.load.image('girl2', 'assets/girl2.png');
+ this.load.image('girl3', 'assets/girl3.png');
+ }
+
+ create(data) {
+ const nickname = data?.nickname || window.userInfo?.nickname;
+ if (!nickname) {
+ alert('닉네임 인식 실패로 시작화면으로 돌아갑니다.');
+ this.scene.start('NicknameScene');
+ return;
+ }
+
+ const characterKeyToBodytype = {
+ 'boy1': 1, 'boy2': 2, 'boy3': 3,
+ 'girl1': 4, 'girl2': 5, 'girl3': 6
+ };
+
+ const saveCustomization = async (nickname, characterKey) => {
+ const bodytypeInt = characterKeyToBodytype[characterKey];
+
+ try {
+ const response = await fetch(`${SERVER_URL}/api/customization/update`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ credentials: 'include',
+ body: JSON.stringify({ nickname: nickname, bodyType: bodytypeInt })
+ });
+ if (response.ok) {
+ console.log("커스터마이징 저장 완료:", `nickname=${nickname}, bodytype=${bodytypeInt}`);
+ this.scene.start('WorldMapScene', { nickname, character: characterKey });
+ } else {
+ const errorText = await response.text();
+ alert(`커스터마이징 저장 실패: ${errorText}`);
+ }
+ } catch (error) {
+ console.error("저장 중 오류:", error);
+ }
+ };
+
+ this.add.image(800, 450, 'character_bg').setDisplaySize(1600, 900);
+ this.add.rectangle(800, 100, 496, 72, 0xB593CC).setOrigin(0.5).setDepth(1);
+ this.add.text(800, 100, '캐릭터를 선택해주세요', {
+ fontFamily: 'Pretendard',
+ fontSize: '32px',
+ fontStyle: 'bold',
+ color: '#ffffff'
+ }).setOrigin(0.5).setDepth(2);
+
+ const startX = 300;
+ const gapX = 500;
+
+ const characters = [
+ { key: 'boy1', x: startX + 0 * gapX, y: 320 },
+ { key: 'boy2', x: startX + 1 * gapX, y: 320 },
+ { key: 'boy3', x: startX + 2 * gapX, y: 320 },
+ { key: 'girl1', x: startX + 0 * gapX, y: 580 },
+ { key: 'girl2', x: startX + 1 * gapX, y: 580 },
+ { key: 'girl3', x: startX + 2 * gapX, y: 580 },
+ ];
+
+ characters.forEach(({ key, x, y }) => {
+ const sprite = this.add.image(x, y, key)
+ .setDisplaySize(200, 250)
+ .setInteractive({ useHandCursor: true })
+ .setDepth(3);
+ sprite.on('pointerdown', () => {
+ this.selectCharacter(key);
+ saveCustomization(nickname, key);
+ });
+ });
+ }
+
+ selectCharacter(characterKey) {
+ if (!window.userInfo) window.userInfo = {};
+ window.userInfo.character = characterKey;
+ }
+}
+
+export default CharacterSelectScene;
\ No newline at end of file
diff --git a/game.js b/game.js
index aa7d212..21ab5c6 100644
--- a/game.js
+++ b/game.js
@@ -1,14 +1,59 @@
+
+import StartScene from './StartScene.js';
+import NicknameScene from './NicknameScene.js';
+import CharacterSelectScene from './CharacterSelectScene.js';
+import WorldMapScene from './WorldMapScene.js';
+import MainScene from './mainScene.js';
+import BridgeScene from './BridgeScene.js';
+
+import { Client } from 'https://cdn.jsdelivr.net/npm/@stomp/stompjs@7.0.1/+esm';
+
const config = {
type: Phaser.AUTO,
- width: 1600,
- height: 900,
+ //width: 1600,
+ //height: 900,
+ scale: {
+ mode: Phaser.Scale.FIT,
+ autoCenter: Phaser.Scale.CENTER_BOTH,
+ width: 1600,
+ height: 900,
+ },
+ parent: 'game-container',
physics: {
default: 'arcade',
- arcade: {
- debug: false
- }
+ arcade: { debug: false }
},
- scene: [LoginScene, MainScene]
+ dom: {
+ createContainer: true
+ },
+ scene: [
+ StartScene,
+ NicknameScene,
+ CharacterSelectScene,
+ WorldMapScene,
+ MainScene,
+ BridgeScene
+ ]
};
-const game = new Phaser.Game(config);
\ No newline at end of file
+const game = new Phaser.Game(config);
+
+const socket = new SockJS(WS_URL);
+
+const stompClient = new Client({
+ webSocketFactory: () => socket,
+ reconnectDelay: 5000,
+ debug: (str) => console.log('[STOMP]', str)
+});
+
+stompClient.onConnect = () => {
+ console.log("STOMP WebSocket 연결 성공");
+};
+
+stompClient.onStompError = (frame) => {
+ console.error("STOMP 오류:", frame.headers['message']);
+};
+
+stompClient.activate();
+
+export { stompClient, socket };
diff --git a/index.html b/index.html
index 7640e9f..fd7bdfc 100644
--- a/index.html
+++ b/index.html
@@ -1,37 +1,34 @@
+
Kuriverse
+
+
+
+
-
-
-
-
+
+
-
\ No newline at end of file
+