const client = new Colyseus.Client('ws://localhost:3000'); let room; let player; const otherPlayers = new Map(); let scene; const config = { type: Phaser.AUTO, width: window.innerWidth, height: window.innerHeight, backgroundColor: '#1a1a1a', physics: { default: 'arcade', arcade: { debug: false }, }, scale: { mode: Phaser.Scale.RESIZE, autoCenter: Phaser.Scale.CENTER_BOTH, }, scene: { preload, create, update, }, }; const game = new Phaser.Game(config); const MessageType = { SERVER_CONNECT: 1, SERVER_DISCONNECT: 2, SERVER_MOVE: 3, SERVER_CHAT: 4, CLIENT_WELCOME: 1001, CLIENT_PLAYER_JOINED: 1002, CLIENT_PLAYER_LEFT: 1003, CLIENT_UPDATE_PLAYER: 1004, CLIENT_CHAT_MESSAGE: 1005, CLIENT_ERROR: 1404, CLIENT_PLAYERS_LIST: 1006, // Novo tipo para lista de jogadores }; // Variáveis para movimento fluido const keys = {}; let lastMovementTime = 0; const MOVEMENT_INTERVAL = 16; // ~60fps const SPEED = 500; // pixels por segundo function preload() { console.log('🎮 Preload phase started'); } function create() { scene = this; console.log('🎯 Create phase started'); // Criar o player visual player = createPlayerObject(this, 400, 300, 0x00ff00, 'Você'); // Configurar input contínuo setupContinuousInput(this); // Conectar ao servidor console.log('🔗 Tentando conectar ao servidor...'); client .joinOrCreate('world', { nickname: prompt('Nickname:') }) .then((r) => { room = r; console.log('✅ Conectado com sucesso:', room.sessionId); // Configurar handlers de mensagem setupMessageHandlers(scene); }) .catch((error) => { console.error('❌ Erro ao conectar:', error); }); } function createPlayerObject(scene, x, y, color, nickname) { const playerContainer = scene.add.container(x, y); // Corpo do jogador const body = scene.add.rectangle(0, 0, 32, 32, color); // Nome do jogador const nameText = scene.add .text(0, -25, nickname, { fontSize: '12px', fill: '#ffffff', stroke: '#000000', strokeThickness: 2, }) .setOrigin(0.5); // Texto de chat (inicialmente oculto) const chatText = scene.add .text(0, -50, '', { fontSize: '10px', fill: '#ffffff', backgroundColor: '#000000aa', padding: { x: 4, y: 2 }, }) .setOrigin(0.5) .setVisible(false); playerContainer.add([body, nameText, chatText]); // Adicionar propriedades customizadas playerContainer.body = body; playerContainer.nameText = nameText; playerContainer.chatText = chatText; playerContainer.targetX = x; playerContainer.targetY = y; return playerContainer; } function setupContinuousInput(scene) { // Configurar teclas const cursors = scene.input.keyboard.createCursorKeys(); const wasd = scene.input.keyboard.addKeys('W,S,A,D'); // Armazenar referências das teclas keys.up = cursors.up; keys.down = cursors.down; keys.left = cursors.left; keys.right = cursors.right; keys.w = wasd.W; keys.s = wasd.S; keys.a = wasd.A; keys.d = wasd.D; // Configurar tecla Enter para chat scene.input.keyboard.on('keydown-ENTER', () => { if (!room) return; const msg = prompt('Mensagem:'); if (msg && msg.trim()) { console.log('💬 Enviando mensagem:', msg); room.send(MessageType.SERVER_CHAT, { message: msg.trim() }); } }); } function setupMessageHandlers(scene) { // Welcome message room.onMessage(MessageType.CLIENT_WELCOME, (data) => { console.log('👋 Welcome:', data.message); }); // Lista de jogadores online (novo) room.onMessage(MessageType.CLIENT_PLAYERS_LIST, (players) => { console.log('📋 Players list:', players); players.forEach((playerData) => { if (playerData.sessionId !== room.sessionId) { createOtherPlayer(playerData); } }); }); // Player joined room.onMessage( MessageType.CLIENT_PLAYER_JOINED, ({ sessionId, options }) => { console.log('🧍 Player joined:', sessionId, options); if (sessionId === room.sessionId) return; const newPlayer = createPlayerObject( scene, 400, 300, 0xff0000, options?.nickname || 'Player' ); otherPlayers.set(sessionId, newPlayer); } ); // Player left room.onMessage(MessageType.CLIENT_PLAYER_LEFT, ({ sessionId }) => { console.log('👋 Player left:', sessionId); const playerObj = otherPlayers.get(sessionId); if (playerObj) { playerObj.destroy(); otherPlayers.delete(sessionId); } }); // Player update - com interpolação suave room.onMessage( MessageType.CLIENT_UPDATE_PLAYER, ({ sessionId, position }) => { if (sessionId === room.sessionId) { return; // Não atualizar nosso próprio player } const playerObj = otherPlayers.get(sessionId); if (playerObj) { // Definir posição alvo para interpolação suave playerObj.targetX = position.x; playerObj.targetY = position.y; } } ); // Chat message - exibir acima da cabeça room.onMessage( MessageType.CLIENT_CHAT_MESSAGE, ({ sessionId, message }) => { console.log(`💬 [${sessionId}]:`, message); let playerObj; if (sessionId === room.sessionId) { playerObj = player; } else { playerObj = otherPlayers.get(sessionId); } if (playerObj && playerObj.chatText) { showChatMessage(playerObj, message); } } ); // Error message room.onMessage(MessageType.CLIENT_ERROR, ({ message }) => { console.error('❌ Erro do servidor:', message); }); // Connection events room.onStateChange((state) => { console.log('🔄 State changed:', state); }); room.onError((code, message) => { console.error('❌ Room error:', code, message); }); room.onLeave((code) => { console.log('👋 Left room with code:', code); }); } function createOtherPlayer(playerData) { const newPlayer = createPlayerObject( scene, playerData.position.x, playerData.position.y, 0xff0000, playerData.nickname || 'Player' ); otherPlayers.set(playerData.sessionId, newPlayer); } function showChatMessage(playerObj, message) { if (!playerObj.chatText) return; playerObj.chatText.setText(message); playerObj.chatText.setVisible(true); // Ocultar mensagem após 3 segundos setTimeout(() => { if (playerObj.chatText) { playerObj.chatText.setVisible(false); } }, 3000); } function update(time, delta) { // Movimento fluido baseado em input contínuo if (room && player) { handleContinuousMovement(delta); } // Interpolação suave para outros jogadores interpolateOtherPlayers(delta); } function handleContinuousMovement(delta) { const currentTime = Date.now(); // Verificar se é hora de enviar movimento if (currentTime - lastMovementTime < MOVEMENT_INTERVAL) { return; } let isMoving = false; const movement = { x: 0, y: 0 }; const speed = (SPEED * delta) / 1000; // pixels por frame // Verificar input if (keys.up.isDown || keys.w.isDown) { movement.y = -speed; isMoving = true; } if (keys.down.isDown || keys.s.isDown) { movement.y = speed; isMoving = true; } if (keys.left.isDown || keys.a.isDown) { movement.x = -speed; isMoving = true; } if (keys.right.isDown || keys.d.isDown) { movement.x = speed; isMoving = true; } if (isMoving) { // Atualizar posição local imediatamente player.x += movement.x; player.y += movement.y; // Enviar para o servidor room.send(MessageType.SERVER_MOVE, { x: player.x, y: player.y }); lastMovementTime = currentTime; } } function interpolateOtherPlayers(delta) { const lerpSpeed = 0.1; // Velocidade de interpolação (0-1) otherPlayers.forEach((playerObj) => { if ( playerObj.targetX !== undefined && playerObj.targetY !== undefined ) { // Interpolação linear suave const deltaX = playerObj.targetX - playerObj.x; const deltaY = playerObj.targetY - playerObj.y; if (Math.abs(deltaX) > 1 || Math.abs(deltaY) > 1) { playerObj.x += deltaX * lerpSpeed; playerObj.y += deltaY * lerpSpeed; } else { playerObj.x = playerObj.targetX; playerObj.y = playerObj.targetY; } } }); }