344 lines
9.1 KiB
JavaScript
344 lines
9.1 KiB
JavaScript
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;
|
|
}
|
|
}
|
|
});
|
|
}
|