.
This commit is contained in:
commit
1f0daf82cb
15 changed files with 730 additions and 0 deletions
28
public/index.html
Normal file
28
public/index.html
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>FreneticalPVP</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/phaser@v3.90.0/dist/phaser.min.js"></script>
|
||||
<script src="https://unpkg.com/colyseus.js@^0.16.0/dist/colyseus.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
<script src="./js/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
344
public/js/main.js
Normal file
344
public/js/main.js
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue