space_invaders_v2 / index.html
giseldo's picture
Update index.html
b7c7602 verified
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Space Invaders com Three.js</title>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #000;
font-family: 'Orbitron', 'Arial', sans-serif;
--theme-color: #00bfff;
--theme-color-dark: #0099cc;
--theme-color-bright: #66ccff;
transition: background-color 0.5s ease;
}
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap');
canvas { display: block; }
.planet-transition {
animation: flash 0.5s;
}
@keyframes flash {
0% { background-color: rgba(255, 255, 255, 0.1); }
50% { background-color: rgba(255, 255, 255, 0.3); }
100% { background-color: rgba(0, 0, 0, 0); }
}
#ui {
position: absolute;
top: 15px;
left: 15px;
color: #33ff33;
font-family: 'Orbitron', Arial, sans-serif;
display: none;
background-color: rgba(0, 0, 0, 0.5);
padding: 10px 15px;
border-radius: 10px;
border-left: 3px solid #33ff33;
box-shadow: 0 0 10px rgba(51, 255, 51, 0.5);
transition: all 0.3s ease;
}
#ui p {
margin: 5px 0;
text-shadow: 0 0 5px #33ff33;
}
#center-ui {
position: absolute;
top: 15px;
left: 50%;
transform: translateX(-50%);
color: #33ffff;
font-family: 'Orbitron', Arial, sans-serif;
text-align: center;
display: none;
background-color: rgba(0, 0, 0, 0.5);
padding: 10px 15px;
border-radius: 10px;
border-bottom: 3px solid #33ffff;
box-shadow: 0 0 10px rgba(51, 255, 255, 0.5);
transition: all 0.3s ease;
}
#center-ui p {
margin: 5px 0;
text-shadow: 0 0 5px #33ffff;
}
#deaths-ui {
position: absolute;
top: 15px;
right: 15px;
color: #ff3333;
font-family: 'Orbitron', Arial, sans-serif;
text-align: right;
display: none;
background-color: rgba(0, 0, 0, 0.5);
padding: 10px 15px;
border-radius: 10px;
border-right: 3px solid #ff3333;
box-shadow: 0 0 10px rgba(255, 51, 51, 0.5);
transition: all 0.3s ease;
}
#deaths-ui p {
margin: 5px 0;
text-shadow: 0 0 5px #ff3333;
}
#powerup-indicator {
position: absolute;
bottom: 15px;
left: 15px;
color: white;
font-family: 'Orbitron', Arial, sans-serif;
display: none;
background-color: rgba(0, 0, 0, 0.5);
padding: 10px 15px;
border-radius: 10px;
border-left: 3px solid #ffaa00;
box-shadow: 0 0 10px rgba(255, 170, 0, 0.5);
}
#powerup-indicator h3 {
margin: 5px;
color: #ffaa00;
text-shadow: 0 0 5px #ffaa00;
font-size: 16px;
}
#powerup-list {
display: flex;
gap: 10px;
flex-wrap: wrap;
margin-top: 5px;
}
.powerup-item {
display: inline-block;
width: 30px;
height: 30px;
border-radius: 50%;
background-color: #333;
text-align: center;
line-height: 30px;
font-weight: bold;
border: 1px solid #ffaa00;
box-shadow: 0 0 5px #ffaa00;
transition: all 0.3s ease;
}
.powerup-item.active {
background-color: #ffaa00;
color: #000;
transform: scale(1.1);
box-shadow: 0 0 10px #ffaa00;
}
#level-notification {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-family: 'Orbitron', Arial, sans-serif;
text-align: center;
display: none;
background-color: rgba(0, 0, 0, 0.7);
padding: 20px;
border-radius: 10px;
border: 2px solid #33ffff;
box-shadow: 0 0 20px rgba(51, 255, 255, 0.5);
z-index: 100;
animation: fadeInOut 2s forwards;
}
#level-notification h2 {
margin: 0;
font-size: 28px;
color: #33ffff;
text-shadow: 0 0 10px #33ffff;
}
#level-notification p {
margin: 10px 0;
}
@keyframes fadeInOut {
0% { opacity: 0; transform: translate(-50%, -50%) scale(0.8); }
20% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
80% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
100% { opacity: 0; transform: translate(-50%, -50%) scale(1.2); }
}
#mobile-controls {
position: absolute;
bottom: 20px;
left: 0;
right: 0;
display: none;
justify-content: space-between;
padding: 0 20px;
}
.mobile-control-group {
display: flex;
gap: 20px;
}
.mobile-btn {
width: 60px;
height: 60px;
background-color: rgba(255, 255, 255, 0.2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
cursor: pointer;
user-select: none;
touch-action: manipulation;
border: 2px solid rgba(255, 255, 255, 0.4);
}
.mobile-btn:active {
background-color: rgba(255, 255, 255, 0.4);
transform: scale(0.95);
}
#gameOver {
position: absolute;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-family: 'Orbitron', 'Arial', sans-serif;
text-align: center;
display: none;
background-color: rgba(0, 0, 0, 0.6);
padding: 30px;
border-radius: 15px;
box-shadow: 0 0 30px rgba(255, 0, 0, 0.6);
width: 80%;
max-width: 600px;
border: 2px solid #ff3333;
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
z-index: 10;
}
#gameOver h1 {
font-size: 40px;
margin-bottom: 20px;
text-shadow: 0 0 10px #ff3333, 0 0 20px #ff3333;
color: #ffffff;
letter-spacing: 2px;
}
#gameOver p {
font-size: 18px;
margin: 15px 0;
border-top: 1px solid rgba(255, 51, 51, 0.5);
border-bottom: 1px solid rgba(255, 51, 51, 0.5);
padding: 15px 0;
color: #f5f5f5;
}
#gameOver .stats {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
margin: 20px 0;
}
#gameOver .stat-item {
flex: 1;
min-width: 200px;
padding: 15px;
margin: 5px;
border-radius: 10px;
background-color: rgba(100, 0, 0, 0.5);
box-shadow: 0 0 15px rgba(255, 0, 0, 0.3) inset;
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
}
#gameOver button {
font-size: 24px;
padding: 12px 30px;
cursor: pointer;
border-radius: 30px;
margin-top: 20px;
transition: all 0.3s ease;
font-weight: bold;
letter-spacing: 2px;
border: none;
margin: 10px;
font-family: 'Orbitron', sans-serif;
}
#gameOver button:first-of-type {
background-color: #4CAF50;
color: white;
box-shadow: 0 0 10px rgba(76, 175, 80, 0.8);
}
#gameOver button:first-of-type:hover {
background-color: #3e8e41;
transform: scale(1.05);
box-shadow: 0 0 15px rgba(76, 175, 80, 1);
}
#gameOver button:last-of-type {
background-color: #ff5722;
color: white;
box-shadow: 0 0 10px rgba(255, 87, 34, 0.8);
}
#gameOver button:last-of-type:hover {
background-color: #e64a19;
transform: scale(1.05);
box-shadow: 0 0 15px rgba(255, 87, 34, 1);
}
#startScreen {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-family: 'Orbitron', 'Arial', sans-serif;
text-align: center;
display: block;
background-color: rgba(0, 0, 0, 0.8);
padding: 30px;
border-radius: 15px;
box-shadow: 0 0 30px rgba(0, 255, 255, 0.6);
width: 80%;
max-width: 600px;
border: 2px solid #00bfff;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
#startScreen h1 {
font-size: 48px;
margin-bottom: 20px;
text-shadow: 0 0 10px #00bfff, 0 0 20px #00bfff;
color: #ffffff;
letter-spacing: 3px;
animation: glow 1.5s infinite alternate;
}
@keyframes glow {
from {
text-shadow: 0 0 5px #00bfff, 0 0 10px #00bfff;
}
to {
text-shadow: 0 0 10px #00bfff, 0 0 20px #00bfff, 0 0 30px #00bfff;
}
}
#startScreen .instructions {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
margin: 20px 0;
border-top: 1px solid rgba(0, 191, 255, 0.5);
border-bottom: 1px solid rgba(0, 191, 255, 0.5);
padding: 20px 0;
}
#startScreen .instruction-item {
flex: 1;
min-width: 200px;
padding: 15px;
margin: 5px;
border-radius: 10px;
background-color: rgba(0, 50, 100, 0.5);
box-shadow: 0 0 15px rgba(0, 191, 255, 0.3) inset;
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
}
#startScreen .key {
display: inline-block;
background-color: #222;
padding: 8px 12px;
border-radius: 5px;
margin: 5px;
font-weight: bold;
border: 1px solid #00bfff;
box-shadow: 0 0 5px #00bfff;
}
#startScreen p {
font-size: 18px;
margin: 5px 0;
color: #f5f5f5;
}
#startScreen button {
font-size: 24px;
padding: 12px 30px;
cursor: pointer;
background-color: #00bfff;
color: white;
border: none;
border-radius: 30px;
margin-top: 20px;
transition: all 0.3s ease;
box-shadow: 0 0 10px rgba(0, 191, 255, 0.8);
font-weight: bold;
letter-spacing: 2px;
font-family: 'Orbitron', sans-serif;
}
#startScreen button:hover {
background-color: #0099cc;
transform: scale(1.05);
box-shadow: 0 0 15px rgba(0, 191, 255, 1);
}
#pausedScreen {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-family: 'Orbitron', 'Arial', sans-serif;
text-align: center;
display: none;
background-color: rgba(0, 0, 0, 0.8);
padding: 30px;
border-radius: 15px;
box-shadow: 0 0 30px rgba(255, 165, 0, 0.6);
width: 80%;
max-width: 600px;
border: 2px solid #ffa500;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
#pausedScreen h1 {
font-size: 48px;
margin-bottom: 20px;
text-shadow: 0 0 10px #ffa500, 0 0 20px #ffa500;
color: #ffffff;
letter-spacing: 3px;
animation: pauseGlow 2s infinite alternate;
}
@keyframes pauseGlow {
from {
text-shadow: 0 0 5px #ffa500, 0 0 10px #ffa500;
}
to {
text-shadow: 0 0 10px #ffa500, 0 0 20px #ffa500, 0 0 30px #ffa500;
}
}
#pausedScreen .instructions {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
margin: 20px 0;
border-top: 1px solid rgba(255, 165, 0, 0.5);
border-bottom: 1px solid rgba(255, 165, 0, 0.5);
padding: 20px 0;
}
#pausedScreen .instruction-item {
flex: 1;
min-width: 200px;
padding: 15px;
margin: 5px;
border-radius: 10px;
background-color: rgba(100, 50, 0, 0.5);
box-shadow: 0 0 15px rgba(255, 165, 0, 0.3) inset;
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
}
#pausedScreen .key {
display: inline-block;
background-color: #222;
padding: 8px 12px;
border-radius: 5px;
margin: 5px;
font-weight: bold;
border: 1px solid #ffa500;
box-shadow: 0 0 5px #ffa500;
}
#pausedScreen p {
font-size: 18px;
margin: 5px 0;
color: #f5f5f5;
}
#pausedScreen button {
font-size: 24px;
padding: 12px 30px;
cursor: pointer;
background-color: #ffa500;
color: white;
border: none;
border-radius: 30px;
margin-top: 20px;
transition: all 0.3s ease;
box-shadow: 0 0 10px rgba(255, 165, 0, 0.8);
font-weight: bold;
letter-spacing: 2px;
font-family: 'Orbitron', sans-serif;
}
#pausedScreen button:hover {
background-color: #ff8c00;
transform: scale(1.05);
box-shadow: 0 0 15px rgba(255, 165, 0, 1);
}
@media (max-width: 768px) {
#ui, #center-ui, #deaths-ui {
font-size: 14px;
padding: 8px 12px;
}
#gameOver, #startScreen, #pausedScreen {
width: 90%;
padding: 20px;
}
#gameOver h1, #startScreen h1, #pausedScreen h1 {
font-size: 32px;
}
#mobile-controls {
display: flex;
}
.powerup-item {
width: 24px;
height: 24px;
line-height: 24px;
font-size: 12px;
}
}
</style>
</head>
<body>
<div id="ui">
<p>Score: <span id="score">0</span></p>
<p>Planet: <span id="level">1</span></p>
</div>
<div id="center-ui">
<p>Hi-Score: <span id="hiScore">0</span></p>
<p>Hi-Planet: <span id="hiPlanet">1</span></p>
</div>
<div id="deaths-ui">
<p>Mortes: <span id="deaths">0</span></p>
<p>Próximo Power-Up: <span id="nextPowerUp">Planet 2</span></p>
</div>
<div id="powerup-indicator">
<h3>Power-Ups</h3>
<div id="powerup-list"></div>
</div>
<div id="level-notification">
<h2>Planeta Completo!</h2>
</div>
<!-- Elementos de áudio para efeitos sonoros -->
<audio id="enemy-hit-sound" src="https://assets.codepen.io/21542/howler-push.mp3" preload="auto"></audio>
<audio id="player-shot-sound" src="https://assets.codepen.io/21542/howler-sfx-levelup.mp3" preload="auto"></audio>
<audio id="shield-hit-sound" src="https://assets.codepen.io/21542/howler-sfx-volume1.mp3" preload="auto"></audio>
<audio id="game-over-sound" src="https://assets.codepen.io/21542/howler-sfx-death.mp3" preload="auto"></audio>
<audio id="background-music" src="https://assets.codepen.io/21542/epic-space-opera.mp3" loop preload="auto"></audio>
<audio id="level-complete-sound" src="https://assets.codepen.io/21542/howler-sfx-levelup.mp3" preload="auto"></audio>
<div id="mobile-controls">
<div class="mobile-control-group">
<div class="mobile-btn" id="left-btn"></div>
<div class="mobile-btn" id="right-btn"></div>
</div>
<div class="mobile-control-group">
<div class="mobile-btn" id="fire-btn">🔥</div>
<div class="mobile-btn" id="pause-btn">⏸️</div>
</div>
</div>
<div id="gameOver">
<h1>Game Over</h1>
<p>Tentar novamente mantem os power-ups e reduz uma barreira!</p>
<div class="stats">
<div class="stat-item">
<p>Pontuação: <span id="finalScore">0</span></p>
<p>Planeta: <span id="finalPlanet">1</span></p>
</div>
<div class="stat-item">
<p>Maior Pontuação: <span id="finalHiScore">0</span></p>
<p>Planeta Mais Alto: <span id="finalHiPlanet">1</span></p>
</div>
</div>
<button onclick="retry()">Tentar Novamente</button>
<button onclick="restartGame()">Reiniciar Jogo</button>
</div>
<div id="pausedScreen">
<h1>JOGO PAUSADO</h1>
<div class="instructions">
<div class="instruction-item">
<h3>Controles</h3>
<p><span class="key">P</span> Continuar Jogo</p>
<p><span class="key">R</span> Reiniciar Jogo</p>
</div>
<div class="instruction-item">
<h3>Estatísticas Atuais</h3>
<p>Pontuação: <span id="pauseScore">0</span></p>
<p>Planeta: <span id="pausePlanet">1</span></p>
</div>
</div>
<p>Tome um fôlego antes de enfrentar os alienígenas novamente!</p>
<button onclick="resumeGame()">CONTINUAR JOGO</button>
<button onclick="restartGame()">REINICIAR JOGO</button>
</div>
<div id="startScreen">
<h1>Space Invaders</h1>
<div class="instructions">
<div class="instruction-item">
<h3>Movimento</h3>
<p><span class="key"></span> Mover para Esquerda</p>
<p><span class="key"></span> Mover para Direita</p>
</div>
<div class="instruction-item">
<h3>Ações</h3>
<p><span class="key">Espaço</span> Atirar</p>
<p><span class="key">P</span> Pausar</p>
<p><span class="key">R</span> Reiniciar</p>
</div>
</div>
<p>Defenda seu planeta contra os invasores alienígenas!</p>
<p>Sobreviva para desbloquear novos poderes a cada fase.</p>
<button onclick="startGame()">INICIAR JOGO</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
<script>
// Configuração da cena
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000022);
const camera = new THREE.OrthographicCamera(-8, 8, 8, -8, 0.1, 100);
camera.position.z = 10;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Função para liberar recursos
function disposeObject(obj) {
scene.remove(obj);
if (obj.geometry) obj.geometry.dispose();
if (obj.material) {
if (Array.isArray(obj.material)) {
obj.material.forEach(mat => mat.dispose());
} else {
obj.material.dispose();
}
}
}
// Variáveis do jogo
let player, enemies = [], bullets = [], enemyBullets = [], shields = [], explosions = [], stars = [];
let score = 0, hiScore = 0, level = 1, hiPlanet = 1, deaths = 0;
let unlockedPowerUps = [0];
let isPaused = false, gameOver = false, isGameStarted = false;
let enemyDirection = 1, enemySpeed = 0.01;
let lastShotTime = 0, tapShotDelay = 250, holdShotDelay = 600; // Ajustado para balancear
let isSpacePressed = false;
let floatingPoints = [];
const colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00];
const powerUpDescriptions = [
"Tiro simples",
"Tiro duplo",
"Tiro triplo",
"Bomba simples",
"Bomba dupla",
"Tiro diagonal",
"Tiro diagonal duplo",
"Tiro diagonal triplo"
];
// Estrelas (fundo)
function createStars() {
const geometry = new THREE.SphereGeometry(0.03, 8, 8);
const material = new THREE.MeshBasicMaterial({ color: 0xffffff });
const instancedMesh = new THREE.InstancedMesh(geometry, material, 100);
const matrix = new THREE.Matrix4();
for (let i = 0; i < 100; i++) {
matrix.setPosition(
Math.random() * 30 - 15,
Math.random() * 20 - 10,
-5
);
const scale = Math.random() + 0.5;
matrix.scale(new THREE.Vector3(scale, scale, scale));
instancedMesh.setMatrixAt(i, matrix);
instancedMesh.setColorAt(i, new THREE.Color(Math.random(), Math.random(), Math.random()).multiplyScalar(0.5).add(new THREE.Color(0.5, 0.5, 0.5)));
}
instancedMesh.twinkle = {
speed: 0.02,
factor: 0.8,
phase: Math.random() * Math.PI * 2
};
stars = [instancedMesh];
scene.add(instancedMesh);
}
// Jogador
function createPlayer() {
const bodyGeometry = new THREE.BoxGeometry(0.5, 0.2, 0.3);
const bodyMaterial = new THREE.MeshBasicMaterial({ color: 0x3399ff });
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
const wingGeometry = new THREE.BoxGeometry(0.8, 0.1, 0.1);
const wingMaterial = new THREE.MeshBasicMaterial({ color: 0x66ccff });
const wings = new THREE.Mesh(wingGeometry, wingMaterial);
wings.position.y = -0.1;
const cockpitGeometry = new THREE.BoxGeometry(0.2, 0.15, 0.2);
const cockpitMaterial = new THREE.MeshBasicMaterial({ color: 0x99ddff });
const cockpit = new THREE.Mesh(cockpitGeometry, cockpitMaterial);
cockpit.position.y = 0.1;
const thrusterGeometry = new THREE.BoxGeometry(0.1, 0.15, 0.1);
const thrusterMaterial = new THREE.MeshBasicMaterial({ color: 0xff3300 });
const thrusterLeft = new THREE.Mesh(thrusterGeometry, thrusterMaterial);
thrusterLeft.position.set(-0.2, -0.15, 0);
const thrusterRight = new THREE.Mesh(thrusterGeometry, thrusterMaterial);
thrusterRight.position.set(0.2, -0.15, 0);
player = new THREE.Group();
player.add(body, wings, cockpit, thrusterLeft, thrusterRight);
player.position.set(0, -6, 0);
player.thrusterLeft = thrusterLeft;
player.thrusterRight = thrusterRight;
player.animationTime = 0;
scene.add(player);
}
function animatePlayer() {
if (!player) return;
player.animationTime += 0.1;
const scale = 0.8 + Math.sin(player.animationTime * 2) * 0.2;
player.thrusterLeft.scale.y = scale;
player.thrusterRight.scale.y = scale;
if (keys['ArrowLeft'] && player.position.x > -7.5) {
player.rotation.z = THREE.MathUtils.lerp(player.rotation.z, 0.2, 0.1);
} else if (keys['ArrowRight'] && player.position.x < 7.5) {
player.rotation.z = THREE.MathUtils.lerp(player.rotation.z, -0.2, 0.1);
} else {
player.rotation.z = THREE.MathUtils.lerp(player.rotation.z, 0, 0.1);
}
}
// Inimigos
function createEnemies() {
enemies = [];
const baseColor = colors[(level - 1) % colors.length];
for (let row = 0; row < 3; row++) {
for (let col = 0; col < 9; col++) {
const x = -6 + col * 1.5;
const y = 7 - row * 1.5;
const enemy = createEnemyByType(row, x, y, baseColor);
enemies.push(enemy);
scene.add(enemy);
}
}
}
function createEnemyByType(type, x, y, baseColor) {
let enemy;
if (type === 0) {
const group = new THREE.Group();
const bodyGeometry = new THREE.BoxGeometry(0.4, 0.2, 0.2);
const bodyMaterial = new THREE.MeshBasicMaterial({ color: baseColor });
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
const wingGeometry = new THREE.BoxGeometry(0.6, 0.1, 0.1);
const wingMaterial = new THREE.MeshBasicMaterial({ color: new THREE.Color(baseColor).multiplyScalar(0.8) });
const wings = new THREE.Mesh(wingGeometry, wingMaterial);
wings.position.y = -0.05;
const antennaGeometry = new THREE.BoxGeometry(0.05, 0.15, 0.05);
const antennaMaterial = new THREE.MeshBasicMaterial({ color: new THREE.Color(baseColor).multiplyScalar(1.2) });
const antenna = new THREE.Mesh(antennaGeometry, antennaMaterial);
antenna.position.y = 0.15;
group.add(body, wings, antenna);
enemy = group;
enemy.scoreValue = 30;
} else if (type === 1) {
const group = new THREE.Group();
const bodyGeometry = new THREE.BoxGeometry(0.3, 0.3, 0.2);
const bodyMaterial = new THREE.MeshBasicMaterial({ color: baseColor });
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
const cockpitGeometry = new THREE.BoxGeometry(0.2, 0.1, 0.15);
const cockpitMaterial = new THREE.MeshBasicMaterial({ color: new THREE.Color(baseColor).multiplyScalar(1.2) });
const cockpit = new THREE.Mesh(cockpitGeometry, cockpitMaterial);
cockpit.position.y = 0.1;
group.add(body, cockpit);
enemy = group;
enemy.scoreValue = 20;
} else {
const group = new THREE.Group();
const bodyGeometry = new THREE.BoxGeometry(0.3, 0.3, 0.2);
const bodyMaterial = new THREE.MeshBasicMaterial({ color: baseColor });
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
group.add(body);
enemy = group;
enemy.scoreValue = 10;
}
enemy.position.set(x, y, 0);
enemy.type = type;
enemy.animationTime = Math.random() * Math.PI * 2;
return enemy;
}
function animateEnemies() {
enemies.forEach(enemy => {
enemy.animationTime += 0.05;
if (enemy.type === 0) {
enemy.rotation.z = Math.sin(enemy.animationTime * 0.5) * 0.2;
} else if (enemy.type === 1) {
enemy.position.y += Math.sin(enemy.animationTime) * 0.01;
} else {
const scale = 1 + Math.sin(enemy.animationTime) * 0.1;
enemy.scale.set(scale, scale, scale);
}
});
}
// Proteções
function createShields() {
shields = [];
let positions;
if (deaths === 0) {
positions = [-6, -2, 2, 6];
} else if (deaths === 1) {
positions = [-4, 0, 4];
} else if (deaths === 2) {
positions = [-2, 2];
} else if (deaths === 3) {
positions = [0];
} else {
positions = [];
}
positions.forEach(x => {
const shield = createShieldGroup(x, -4);
shields.push(shield);
scene.add(shield);
});
}
function createShieldGroup(x, y) {
const group = new THREE.Group();
group.position.set(x, y, 0);
group.health = 30;
const baseGeometry = new THREE.BoxGeometry(1.4, 0.3, 0.3);
const baseMaterial = new THREE.MeshBasicMaterial({ color: 0x007766, transparent: true, opacity: 0.7 });
const base = new THREE.Mesh(baseGeometry, baseMaterial);
base.position.y = -0.25;
const leftGeometry = new THREE.BoxGeometry(0.3, 0.8, 0.3);
const leftMaterial = new THREE.MeshBasicMaterial({ color: 0x006655, transparent: true, opacity: 0.7 });
const left = new THREE.Mesh(leftGeometry, leftMaterial);
left.position.set(-0.55, 0.1, 0);
const rightGeometry = new THREE.BoxGeometry(0.3, 0.8, 0.3);
const rightMaterial = new THREE.MeshBasicMaterial({ color: 0x006655, transparent: true, opacity: 0.7 });
const right = new THREE.Mesh(rightGeometry, rightMaterial);
right.position.set(0.55, 0.1, 0);
const topGeometry = new THREE.BoxGeometry(0.8, 0.3, 0.3);
const topMaterial = new THREE.MeshBasicMaterial({ color: 0x007766, transparent: true, opacity: 0.7 });
const top = new THREE.Mesh(topGeometry, topMaterial);
top.position.set(0, 0.45, 0);
const glowGeometry = new THREE.BoxGeometry(1.5, 0.9, 0.1);
const glowMaterial = new THREE.MeshBasicMaterial({ color: 0x00aa99, transparent: true, opacity: 0.2 });
const glow = new THREE.Mesh(glowGeometry, glowMaterial);
glow.position.set(0, 0.05, -0.2);
group.add(base, left, right, top, glow);
group.components = { base, left, right, top, glow };
return group;
}
function updateShieldVisual(shield) {
const healthPercent = shield.health / 30;
if (shield.health <= 22) {
const color = new THREE.Color(0xaa7700);
shield.components.base.material.color.set(color);
shield.components.left.material.color.set(color.clone().multiplyScalar(0.9));
shield.components.right.material.color.set(color.clone().multiplyScalar(0.9));
shield.components.top.material.color.set(color);
shield.components.glow.material.color.set(color);
shield.components.base.scale.set(0.9, 0.9, 0.9);
shield.components.left.scale.set(0.9, 0.85, 0.9);
shield.components.right.scale.set(0.9, 0.85, 0.9);
shield.components.top.scale.set(0.8, 0.8, 0.8);
}
if (shield.health <= 14) {
const color = new THREE.Color(0xaa5500);
shield.components.base.material.color.set(color);
shield.components.left.material.color.set(color.clone().multiplyScalar(0.9));
shield.components.right.material.color.set(color.clone().multiplyScalar(0.9));
shield.components.top.material.color.set(color);
shield.components.glow.material.color.set(color);
shield.components.base.scale.set(0.8, 0.7, 0.8);
shield.components.left.scale.set(0.8, 0.7, 0.8);
shield.components.right.scale.set(0.8, 0.7, 0.8);
shield.components.top.scale.set(0.7, 0.6, 0.7);
if (shield.health === 14 && shield.components.top.visible) {
shield.components.top.visible = Math.random() > 0.5;
}
}
if (shield.health <= 7) {
const color = new THREE.Color(0xaa3300);
shield.components.base.material.color.set(color);
shield.components.left.material.color.set(color.clone().multiplyScalar(0.9));
shield.components.right.material.color.set(color.clone().multiplyScalar(0.9));
shield.components.top.material.color.set(color);
shield.components.glow.material.color.set(color);
shield.components.base.scale.set(0.5, 0.5, 0.5);
shield.components.left.scale.set(0.5, 0.4, 0.5);
shield.components.right.scale.set(0.5, 0.4, 0.5);
shield.components.top.scale.set(0.4, 0.3, 0.4);
shield.components.glow.material.opacity = 0.15;
if (shield.health === 7) {
if (Math.random() > 0.3) shield.components.left.visible = false;
if (Math.random() > 0.3) shield.components.right.visible = false;
shield.components.top.visible = false;
}
}
if (shield.health < 15) {
shield.position.x += (Math.random() - 0.5) * 0.05 * (1 - healthPercent);
shield.position.y += (Math.random() - 0.5) * 0.05 * (1 - healthPercent);
setTimeout(() => {
if (shield.position) {
shield.position.x = Math.round(shield.position.x * 20) / 20;
shield.position.y = Math.round(shield.position.y * 20) / 20;
}
}, 16);
}
}
// Tiros
function shoot(isTap = false) {
const now = Date.now();
const delay = isTap ? tapShotDelay : holdShotDelay;
if (now - lastShotTime < delay || bullets.length > 50) return;
lastShotTime = now;
// Reproduzir o som de tiro do jogador
// document.getElementById('player-shot-sound').currentTime = 0;
// document.getElementById('player-shot-sound').play();
// Iterar sobre todos os power-ups desbloqueados
unlockedPowerUps.forEach(powerUp => {
// Garantir que o power-up esteja dentro do intervalo válido
if (powerUp >= powerUpDescriptions.length) return;
if (powerUp >= 5) {
const offsets = powerUp === 5 ? [0] : powerUp === 6 ? [-0.3, 0.3] : [-0.5, 0, 0.5];
offsets.forEach(offset => {
const bulletLeft = createBullet(offset);
bulletLeft.velocity = new THREE.Vector3(-0.075, 0.1, 0);
bulletLeft.isDiagonal = true;
bullets.push(bulletLeft);
scene.add(bulletLeft);
const bulletRight = createBullet(offset);
bulletRight.velocity = new THREE.Vector3(0.075, 0.1, 0);
bulletRight.isDiagonal = true;
bullets.push(bulletRight);
scene.add(bulletRight);
});
} else if (powerUp >= 3) {
const offsets = powerUp === 3 ? [0] : [-0.3, 0.3];
offsets.forEach(offset => {
const geometry = new THREE.SphereGeometry(0.3, 8, 8);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const bomb = new THREE.Mesh(geometry, material);
bomb.position.set(player.position.x + offset, player.position.y, 0);
bomb.velocity = new THREE.Vector3(0, 0.1, 0);
bomb.isBomb = true;
bomb.updateTrail = function() {}; // Função vazia para bombas
bullets.push(bomb);
scene.add(bomb);
});
} else {
const offsets = powerUp === 0 ? [0] : powerUp === 1 ? [-0.3, 0.3] : [-0.5, 0, 0.5];
offsets.forEach(offset => {
const bullet = createBullet(offset);
bullet.velocity = new THREE.Vector3(0, 0.1, 0);
bullet.isNormal = true;
bullets.push(bullet);
scene.add(bullet);
});
}
});
function createBullet(offset) {
const geometry = new THREE.BoxGeometry(0.15, 0.4, 0.15);
const material = new THREE.MeshBasicMaterial({ color: 0x33ff33, emissive: 0x33ff33, transparent: true, opacity: 0.9 });
const bullet = new THREE.Mesh(geometry, material);
bullet.position.set(player.position.x + offset, player.position.y, 0);
const glowGeometry = new THREE.BoxGeometry(0.25, 0.5, 0.1);
const glowMaterial = new THREE.MeshBasicMaterial({ color: 0x66ff66, transparent: true, opacity: 0.5 });
const glow = new THREE.Mesh(glowGeometry, glowMaterial);
bullet.add(glow);
bullet.updateTrail = function() {
if (Math.random() > 0.8) {
const trailGeometry = new THREE.SphereGeometry(0.05, 6, 6);
const trailMaterial = new THREE.MeshBasicMaterial({ color: 0x66ff66, transparent: true, opacity: 0.8 });
const trail = new THREE.Mesh(trailGeometry, trailMaterial);
trail.position.set(
this.position.x + (Math.random() - 0.5) * 0.05,
this.position.y - 0.2,
this.position.z
);
trail.life = 0;
trail.maxLife = 0.5;
trail.scale.set(1, 1, 1);
scene.add(trail);
explosions.push(trail);
}
};
return bullet;
}
}
function enemyShoot(enemy) {
const geometry = new THREE.BoxGeometry(0.15, 0.5, 0.15);
const material = new THREE.MeshBasicMaterial({ color: 0xff00ff, transparent: true, opacity: 0.9 });
const bullet = new THREE.Mesh(geometry, material);
bullet.position.set(enemy.position.x, enemy.position.y - 0.3, 0);
bullet.velocity = new THREE.Vector3(0, -0.05, 0);
const glowGeometry = new THREE.BoxGeometry(0.3, 0.7, 0.1);
const glowMaterial = new THREE.MeshBasicMaterial({ color: 0xff55ff, transparent: true, opacity: 0.5 });
const glow = new THREE.Mesh(glowGeometry, glowMaterial);
bullet.add(glow);
bullet.animationTime = 0;
bullet.updateAnimation = function() {
this.animationTime += 0.15;
const pulseFactor = 0.8 + Math.sin(this.animationTime) * 0.2;
glow.scale.set(pulseFactor, pulseFactor, 1);
if (Math.random() > 0.8) this.createTrail();
};
bullet.createTrail = function() {
const trailGeometry = new THREE.SphereGeometry(0.05, 6, 6);
const trailMaterial = new THREE.MeshBasicMaterial({ color: 0xff77ff, transparent: true, opacity: 0.7 });
const trail = new THREE.Mesh(trailGeometry, trailMaterial);
trail.position.set(
this.position.x + (Math.random() - 0.5) * 0.05,
this.position.y + 0.2 + (Math.random() - 0.5) * 0.05,
this.position.z
);
trail.life = 0;
trail.maxLife = 0.4;
scene.add(trail);
explosions.push(trail);
};
enemyBullets.push(bullet);
scene.add(bullet);
}
// Explosões
function createExplosion(x, y) {
const particles = [];
const particleCount = 8;
const colors = [0xffa500, 0xff4500, 0xff0000, 0xffff00];
for (let i = 0; i < particleCount; i++) {
const size = Math.random() * 0.2 + 0.1;
const geometry = new THREE.SphereGeometry(size, 8, 8);
const material = new THREE.MeshBasicMaterial({ color: colors[Math.floor(Math.random() * colors.length)], transparent: true, opacity: 1 });
const particle = new THREE.Mesh(geometry, material);
particle.position.set(x + (Math.random() - 0.5) * 0.3, y + (Math.random() - 0.5) * 0.3, 0);
const speed = Math.random() * 0.03 + 0.01;
const angle = Math.random() * Math.PI * 2;
particle.velocity = new THREE.Vector3(Math.cos(angle) * speed, Math.sin(angle) * speed, 0);
particle.life = 0;
particle.maxLife = Math.random() * 0.5 + 0.5;
explosions.push(particle);
scene.add(particle);
}
}
// UI
function updateUI() {
document.getElementById('score').textContent = score;
document.getElementById('hiScore').textContent = hiScore;
document.getElementById('level').textContent = level;
document.getElementById('hiPlanet').textContent = hiPlanet;
document.getElementById('deaths').textContent = deaths;
const nextPowerUpPhase = unlockedPowerUps.length + 1;
document.getElementById('nextPowerUp').textContent = nextPowerUpPhase <= 8 ? `Planet ${nextPowerUpPhase}` : 'Todos Desbloqueados';
updatePowerUpIndicator();
}
function updatePowerUpIndicator() {
const powerupList = document.getElementById('powerup-list');
powerupList.innerHTML = '';
for (let i = 0; i < 8; i++) {
const powerupItem = document.createElement('div');
powerupItem.className = 'powerup-item';
powerupItem.textContent = i + 1;
powerupItem.title = powerUpDescriptions[i] || 'Desconhecido';
if (unlockedPowerUps.includes(i)) {
powerupItem.classList.add('active');
}
powerupList.appendChild(powerupItem);
}
document.getElementById('powerup-indicator').style.display = isGameStarted && !gameOver ? 'block' : 'none';
}
function showLevelNotification() {
const levelNotification = document.getElementById('level-notification');
if (level <= powerUpDescriptions.length) {
levelNotification.style.display = 'block';
setTimeout(() => {
levelNotification.style.display = 'none';
}, 2000);
}
}
function updateGameTheme() {
const baseColor = colors[(level - 1) % colors.length];
const baseColorHex = '#' + baseColor.toString(16).padStart(6, '0');
const darkColor = new THREE.Color(baseColor).multiplyScalar(0.5).getHex();
const darkColorHex = '#' + darkColor.toString(16).padStart(6, '0');
const brightColor = new THREE.Color(baseColor).multiplyScalar(1.5).getHex();
const brightColorHex = '#' + brightColor.toString(16).padStart(6, '0');
const bgColor = new THREE.Color(baseColor).multiplyScalar(0.1);
scene.background = bgColor;
document.documentElement.style.setProperty('--theme-color', baseColorHex);
document.documentElement.style.setProperty('--theme-color-dark', darkColorHex);
document.documentElement.style.setProperty('--theme-color-bright', brightColorHex);
document.body.classList.add('planet-transition');
setTimeout(() => {
document.body.classList.remove('planet-transition');
}, 1000);
}
// Jogo
function startGame() {
isGameStarted = true;
document.getElementById('startScreen').style.display = 'none';
document.getElementById('ui').style.display = 'block';
document.getElementById('center-ui').style.display = 'block';
document.getElementById('deaths-ui').style.display = 'block';
document.getElementById('powerup-indicator').style.display = 'block';
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
document.getElementById('mobile-controls').style.display = 'flex';
setupMobileControls();
}
createPlayer();
createEnemies();
createShields();
createStars();
updateUI();
}
function playLevelCompleteSound() {
const levelCompleteSound = document.getElementById('level-complete-sound');
if (levelCompleteSound) {
levelCompleteSound.currentTime = 0;
levelCompleteSound.play().catch(e => console.log("Erro ao reproduzir som de nível completo:", e));
}
}
// Adicionar chamada ao som no final do nível
function nextLevel() {
showLevelNotification();
playLevelCompleteSound(); // Reproduzir som de nível completo
level++;
hiPlanet = Math.max(hiPlanet, level);
// Adicionar novo power-up, se válido
const nextPowerUpIndex = level - 1;
if (nextPowerUpIndex < powerUpDescriptions.length && !unlockedPowerUps.includes(nextPowerUpIndex)) {
unlockedPowerUps.push(nextPowerUpIndex);
}
enemySpeed = Math.min(enemySpeed + 0.005, 0.05);
enemyBullets.forEach(bullet => disposeObject(bullet));
enemyBullets = [];
createEnemies();
updateGameTheme();
updateUI();
}
// Certifique-se de que o som de game over seja carregado corretamente
const gameOverSound = document.getElementById('game-over-sound');
gameOverSound.volume = 0.5; // Ajustar o volume para garantir que seja audível
function playGameOverSound() {
if (gameOverSound) {
gameOverSound.currentTime = 0; // Reinicia o som
gameOverSound.play().catch(e => console.error("Erro ao reproduzir o som de game over:", e));
}
}
// Substituir a chamada existente no método endGame
function endGame() {
playGameOverSound(); // Reproduzir o som de Game Over
deaths++;
gameOver = true;
if (player) {
createExplosion(player.position.x, player.position.y);
disposeObject(player);
player = null;
}
updateUI();
document.getElementById('finalScore').textContent = score;
document.getElementById('finalPlanet').textContent = level;
document.getElementById('finalHiScore').textContent = hiScore;
document.getElementById('finalHiPlanet').textContent = hiPlanet;
document.getElementById('gameOver').style.display = 'block';
}
window.restartGame = function() {
score = 0;
level = 1;
deaths = 0;
unlockedPowerUps = [0];
enemySpeed = 0.01;
gameOver = false;
isPaused = false;
isGameStarted = true;
lastShotTime = 0;
isSpacePressed = false;
// Limpar todos os objetos
bullets.forEach(bullet => disposeObject(bullet));
enemyBullets.forEach(bullet => disposeObject(bullet));
enemies.forEach(enemy => disposeObject(enemy));
shields.forEach(shield => disposeObject(shield));
explosions.forEach(explosion => disposeObject(explosion));
stars.forEach(star => disposeObject(star));
floatingPoints.forEach(point => disposeObject(point));
if (player) disposeObject(player);
// Limpar arrays
bullets = [];
enemyBullets = [];
enemies = [];
shields = [];
explosions = [];
stars = [];
floatingPoints = [];
player = null;
// Limpar cena
while (scene.children.length > 0) {
scene.remove(scene.children[0]);
}
// Esconder interfaces
document.getElementById('gameOver').style.display = 'none';
document.getElementById('pausedScreen').style.display = 'none';
document.getElementById('startScreen').style.display = 'none';
// Exibir interface do jogo
document.getElementById('ui').style.display = 'block';
document.getElementById('center-ui').style.display = 'block';
document.getElementById('deaths-ui').style.display = 'block';
document.getElementById('powerup-indicator').style.display = 'block';
// Recriar elementos
scene.background = new THREE.Color(0x000022);
createPlayer();
createEnemies();
createShields();
createStars();
updateUI();
};
window.retry = function() {
score = 0;
level = 1;
enemySpeed = 0.01;
gameOver = false;
isPaused = false;
isGameStarted = true;
lastShotTime = 0;
isSpacePressed = false;
// Limpar todos os objetos
bullets.forEach(bullet => disposeObject(bullet));
enemyBullets.forEach(bullet => disposeObject(bullet));
enemies.forEach(enemy => disposeObject(enemy));
shields.forEach(shield => disposeObject(shield));
explosions.forEach(explosion => disposeObject(explosion));
stars.forEach(star => disposeObject(star));
floatingPoints.forEach(point => disposeObject(point));
if (player) disposeObject(player);
// Limpar arrays
bullets = [];
enemyBullets = [];
enemies = [];
shields = [];
explosions = [];
stars = [];
floatingPoints = [];
player = null;
// Limpar cena
while (scene.children.length > 0) {
scene.remove(scene.children[0]);
}
// Esconder interfaces
document.getElementById('gameOver').style.display = 'none';
document.getElementById('pausedScreen').style.display = 'none';
document.getElementById('startScreen').style.display = 'none';
// Exibir interface do jogo
document.getElementById('ui').style.display = 'block';
document.getElementById('center-ui').style.display = 'block';
document.getElementById('deaths-ui').style.display = 'block';
document.getElementById('powerup-indicator').style.display = 'block';
// Recriar elementos
scene.background = new THREE.Color(0x000022);
createPlayer();
createEnemies();
createShields();
createStars();
updateUI();
};
window.resumeGame = function() {
if (isPaused) {
isPaused = false;
document.getElementById('pausedScreen').style.display = 'none';
startBackgroundMusic(); // Reinicia a música ao continuar o jogo
}
};
// Controles
const keys = {};
document.addEventListener('keydown', e => {
if (e.code === 'KeyR') {
if (isGameStarted && !gameOver) {
restartGame();
}
return;
}
if (!isGameStarted || gameOver) return;
keys[e.code] = true;
if (e.code === 'Space' && !isSpacePressed) {
isSpacePressed = true;
shoot(true);
} else if (e.code === 'KeyP') {
isPaused = !isPaused;
document.getElementById('pausedScreen').style.display = isPaused ? 'block' : 'none';
}
});
document.addEventListener('keyup', e => {
keys[e.code] = false;
if (e.code === 'Space') {
isSpacePressed = false;
}
});
function setupMobileControls() {
const leftBtn = document.getElementById('left-btn');
let leftInterval;
leftBtn.addEventListener('touchstart', (e) => {
e.preventDefault();
keys['ArrowLeft'] = true;
leftInterval = setInterval(() => {
if (player && player.position.x > -7.5) {
player.position.x -= 0.1;
}
}, 16);
});
leftBtn.addEventListener('touchend', () => {
keys['ArrowLeft'] = false;
clearInterval(leftInterval);
});
const rightBtn = document.getElementById('right-btn');
let rightInterval;
rightBtn.addEventListener('touchstart', (e) => {
e.preventDefault();
keys['ArrowRight'] = true;
rightInterval = setInterval(() => {
if (player && player.position.x < 7.5) {
player.position.x += 0.1;
}
}, 16);
});
rightBtn.addEventListener('touchend', () => {
keys['ArrowRight'] = false;
clearInterval(rightInterval);
});
const fireBtn = document.getElementById('fire-btn');
let fireInterval;
fireBtn.addEventListener('touchstart', (e) => {
e.preventDefault();
isSpacePressed = true;
shoot(true);
fireInterval = setInterval(() => {
shoot(false);
}, holdShotDelay);
});
fireBtn.addEventListener('touchend', () => {
isSpacePressed = false;
clearInterval(fireInterval);
});
const pauseBtn = document.getElementById('pause-btn');
pauseBtn.addEventListener('touchstart', (e) => {
e.preventDefault();
isPaused = !isPaused;
document.getElementById('pausedScreen').style.display = isPaused ? 'block' : 'none';
});
}
// Animação
function animate() {
if (!isGameStarted) {
renderer.render(scene, camera);
requestAnimationFrame(animate);
return;
}
if (gameOver) {
document.getElementById('gameOver').style.display = 'block';
renderer.render(scene, camera);
requestAnimationFrame(animate);
return;
}
if (isPaused) {
document.getElementById('pauseScore').textContent = score;
document.getElementById('pausePlanet').textContent = level;
document.getElementById('pausedScreen').style.display = 'block';
renderer.render(scene, camera);
requestAnimationFrame(animate);
return;
}
document.getElementById('pausedScreen').style.display = 'none';
// Animar estrelas
stars.forEach(star => {
if (star.isInstancedMesh) {
star.twinkle.phase += star.twinkle.speed;
for (let i = 0; i < star.count; i++) {
const scale = star.twinkle.factor + Math.sin(star.twinkle.phase + i) * (1 - star.twinkle.factor);
const matrix = new THREE.Matrix4();
star.getMatrixAt(i, matrix);
matrix.scale(new THREE.Vector3(scale, scale, scale));
star.setMatrixAt(i, matrix);
}
star.instanceMatrix.needsUpdate = true;
}
});
animatePlayer();
animateEnemies();
if (player && keys['ArrowLeft'] && player.position.x > -7.5) {
player.position.x -= 0.05;
}
if (player && keys['ArrowRight'] && player.position.x < 7.5) {
player.position.x += 0.05;
}
if (keys['Space'] && isSpacePressed) {
shoot(false);
}
let moveDown = false;
enemies.forEach(enemy => {
enemy.position.x += enemySpeed * enemyDirection;
if (enemy.position.x > 7.5 || enemy.position.x < -7.5) moveDown = true;
if (enemy.position.y < -5.5) endGame();
if (Math.random() < 0.005) {
enemyShoot(enemy);
}
});
if (moveDown) {
enemyDirection *= -1;
enemies.forEach(enemy => enemy.position.y -= 0.5);
}
bullets.forEach((bullet, bIndex) => {
if (!bullet.position || !bullet.velocity) return;
bullet.position.add(bullet.velocity);
bullet.updateTrail();
if (bullet.position.y > 8 || bullet.position.x < -8 || bullet.position.x > 8) {
disposeObject(bullet);
bullets.splice(bIndex, 1);
}
});
enemyBullets.forEach((bullet, bIndex) => {
if (!bullet.position || !bullet.velocity) return;
bullet.position.add(bullet.velocity);
if (bullet.updateAnimation) {
bullet.updateAnimation();
}
if (bullet.position.y < -8) {
disposeObject(bullet);
enemyBullets.splice(bIndex, 1);
}
});
bullets.forEach((bullet, bIndex) => {
enemies.forEach((enemy, eIndex) => {
if (bullet.position && enemy.position && bullet.position.distanceTo(enemy.position) < 0.3) {
// Reproduzir o som quando um inimigo é atingido
document.getElementById('enemy-hit-sound').currentTime = 0;
document.getElementById('enemy-hit-sound').play();
createExplosion(enemy.position.x, enemy.position.y);
disposeObject(enemy);
enemies.splice(eIndex, 1);
if (!bullet.isBomb) {
disposeObject(bullet);
bullets.splice(bIndex, 1);
}
const points = enemy.scoreValue || 10;
score += points;
createFloatingPoints(enemy.position.x, enemy.position.y, points);
hiScore = Math.max(hiScore, score);
updateUI();
}
});
});
enemyBullets.forEach((bullet, bIndex) => {
shields.forEach((shield, sIndex) => {
if (bullet.position && shield.position && bullet.position.distanceTo(shield.position) < 0.5) {
disposeObject(bullet); // Remove o tiro inimigo
enemyBullets.splice(bIndex, 1); // Remove da lista
return;
}
});
if (player && bullet.position && bullet.position.distanceTo(player.position) < 0.3) {
disposeObject(bullet);
enemyBullets.splice(bIndex, 1);
for (let i = 0; i < 20; i++) {
createExplosion(player.position.x, player.position.y);
}
endGame();
}
});
explosions.forEach((explosion, index) => {
if (!explosion.position) return;
explosion.position.add(explosion.velocity || new THREE.Vector3(0, 0, 0));
explosion.life += 0.05;
if (explosion.material.opacity) {
explosion.material.opacity = Math.max(0, 1 - explosion.life / explosion.maxLife);
}
if (explosion.life > explosion.maxLife) {
disposeObject(explosion);
explosions.splice(index, 1);
}
});
floatingPoints.forEach((point, index) => {
if (!point.position) return;
point.position.y += 0.03;
point.life += 0.05;
if (point.life > 1) {
disposeObject(point);
floatingPoints.splice(index, 1);
}
});
if (enemies.length === 0) nextLevel();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
// Redimensionamento
window.addEventListener('resize', () => {
renderer.setSize(window.innerWidth, window.innerHeight);
const aspectRatio = window.innerWidth / window.innerHeight;
const scale = 8;
if (aspectRatio >= 1) {
camera.left = -scale * aspectRatio;
camera.right = scale * aspectRatio;
camera.top = scale;
camera.bottom = -scale;
} else {
camera.left = -scale;
camera.right = scale;
camera.top = scale / aspectRatio;
camera.bottom = -scale / aspectRatio;
}
camera.updateProjectionMatrix();
});
// Pontuação flutuante
function createFloatingPoints(x, y, points) {
const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([0, 0, 0]);
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
const material = new THREE.PointsMaterial({
color: 0xffff00,
size: 0.5,
map: createTextTexture(`+${points}`),
transparent: true,
alphaTest: 0.5
});
const pointsObj = new THREE.Points(geometry, material);
pointsObj.position.set(x, y, 0);
pointsObj.life = 0;
floatingPoints.push(pointsObj);
scene.add(pointsObj);
}
function createTextTexture(text) {
const canvas = document.createElement('canvas');
const size = 128;
canvas.width = size;
canvas.height = size;
const context = canvas.getContext('2d');
context.fillStyle = 'transparent';
context.fillRect(0, 0, size, size);
context.font = 'Bold 64px Orbitron, Arial';
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillStyle = '#000000';
context.fillText(text, size/2 + 2, size/2 + 2);
context.fillStyle = '#ffff00';
context.fillText(text, size/2, size/2);
const texture = new THREE.CanvasTexture(canvas);
texture.needsUpdate = true;
return texture;
}
// Música de fundo
let bgMusic = document.getElementById('background-music');
bgMusic.volume = 0.3; // Volume mais baixo para não sobrepor efeitos sonoros
function startBackgroundMusic() {
bgMusic.currentTime = 0;
bgMusic.play().catch(e => console.log("Erro ao iniciar música:", e));
}
function stopBackgroundMusic() {
bgMusic.pause();
}
window.startGame = function() {
isGameStarted = true;
document.getElementById('startScreen').style.display = 'none';
document.getElementById('ui').style.display = 'block';
document.getElementById('center-ui').style.display = 'block';
document.getElementById('deaths-ui').style.display = 'block';
document.getElementById('powerup-indicator').style.display = 'block';
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
document.getElementById('mobile-controls').style.display = 'flex';
setupMobileControls();
}
createPlayer();
createEnemies();
createShields();
createStars();
updateUI();
startBackgroundMusic();
};
// Inicialização
animate();
</script>
</body>
</html>