<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>RED VS BLUE</title>
    <style>
        body {
            margin: 0;
            background: #0c4a6e;
        }
        #restartButton {
    position: relative;
    background: linear-gradient(45deg, #4CAF50, #45a049);
    color: white;
    padding: 15px 30px;
    border: none;
    cursor: pointer;
    font-size: 22px;
    border-radius: 10px;
    overflow: hidden;
    transition: transform 0.3s, box-shadow 0.3s, background 0.3s;
    text-transform: uppercase;
    letter-spacing: 1px;
    font-weight: bold;
    margin-top: 100px;
    margin-left: 20px;
}

#restartButton::before,
#restartButton::after {
    content: "";
    position: absolute;
    width: 100%;
    height: 100%;
    background: inherit;
    z-index: -1;
    transition: transform 0.3s, opacity 0.3s;
}

#restartButton::before {
    top: 0;
    left: -100%;
    transform: skewX(45deg) translateX(0);
}

#restartButton::after {
    top: 0;
    left: 100%;
    transform: skewX(-45deg) translateX(0);
}

#restartButton:hover {
    background: linear-gradient(45deg, #45a049, #4CAF50);
    transform: scale(1.05) translateZ(10px);
    box-shadow: 0 15px 25px rgba(0, 0, 0, 0.5), 0 10px 15px rgba(0, 0, 0, 0.3);
}

#restartButton:hover::before,
#restartButton:hover::after {
    opacity: 0;
    transform: skewX(45deg) translateX(-100%);
}

#gameTitle {
    text-align: center;
    position: absolute;
    top: 20px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 36px;
    color: white;
    text-transform: uppercase;
    font-weight: bold;
    letter-spacing: 2px;
}
.red-text {
    color: red;
}


.blue-text {
    color: #00FFFF;
}

.vs-text {
    color: red;
    font-weight: bold;
}


.blue-text {
    color: #00FFFF;
}

#gameTitle {
    text-align: center;
    position: absolute;
    top: 20px;
    left: 50%;
    transform: translateX(-50%);
    letter-spacing: 5px;
}


h1 {
    font-size: 48px;
    text-transform: uppercase;
    font-weight: bold;
}


.red-text {
    color: #FF5733;
    text-shadow: 2px 2px 0px rgba(0, 0, 0, 0.2);
}


.vs-text {
    font-size: 36px;
    font-weight: bold;
    color: #FF5733;
    transform: rotate(10deg);
    display: inline-block;
    margin: 0 5px;
    text-shadow: 2px 2px 0px rgba(0, 0, 0, 0.2);
}


.blue-text {
    color: #00FFFF;
    text-shadow: 2px 2px 0px rgba(0, 0, 0, 0.2);
}

#highScore {
    position: absolute;
    top: 1in;
    right: 10px;
    color: red;
    text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
    transform-style: preserve-3d;
    transform: perspective(1000px) rotateY(0deg) translateZ(10px);
    transition: transform 0.5s ease-in-out;
  }

  #highScore:hover {
    animation: flip 1s, bulge 0.5s;
  }

  @keyframes flip {
    0% {
      transform: perspective(1000px) rotateY(0deg) translateZ(10px);
    }
    50% {
      transform: perspective(1000px) rotateY(180deg) translateZ(10px);
    }
    100% {
      transform: perspective(1000px) rotateY(0deg) translateZ(10px);
    }
  }

  @keyframes bulge {
    0% {
      transform: perspective(1000px) rotateY(0deg) translateZ(10px);
    }
    100% {
      transform: perspective(1000px) rotateY(0deg) translateZ(-10px);
    }
  }
    </style>
</head>
<body>
    <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>

    <script type="importmap">
        {
            "imports": {
                "three": "https://unpkg.com/three@0.150.1/build/three.module.js",
                "three/addons/": "https://unpkg.com/three@0.150.1/examples/jsm/"
            }
        }
    </script>
    <div id="gameOverScreen" style="display: none;">
        <h1 hidden>Game Over</h1>
        <button id="restartButton">Restart</button>
        <div id="gameTitle">
            <div id="highScore" style="position: absolute; top: 1in; right: 10px; color: white;">High Score: 0</div>
            <audio id="backgroundMusic" loop>
                <source src="Hitman.mp3" type="audio/mpeg">
                Your browser does not support the audio element.
            </audio>
        <h1>
            <h1>
                <span class="red-text">RED</span>
                <span class="vs-text">V</span>
                <span class="blue-text">S</span>
                <span class="blue-text">BLUE</span>
            </h1>
        </div>
    </div>
    <script type="module">
        import * as THREE from 'three'
        import { OrbitControls } from 'three/addons/controls/OrbitControls.js'

        const scene = new THREE.Scene()
        const camera = new THREE.PerspectiveCamera(
            75,
            window.innerWidth / window.innerHeight,
            0.1,
            1000
        )
        camera.position.set(4.61, 2.74, 8)

        const renderer = new THREE.WebGLRenderer({
            alpha: true,
            antialias: true
        })
        renderer.shadowMap.enabled = true
        renderer.setSize(window.innerWidth, window.innerHeight)
        document.body.appendChild(renderer.domElement)

        const controls = new OrbitControls(camera, renderer.domElement)
        const backgroundMusic = document.getElementById('backgroundMusic');

// Play the background music when the page loads
document.addEventListener('DOMContentLoaded', () => {
    backgroundMusic.play();
});
        class Box extends THREE.Mesh {
            constructor({
                width,
                height,
                depth,
                color = '#00ff00',
                velocity = {
                    x: 0,
                    y: 0,
                    z: 0
                },
                position = {
                    x: 0,
                    y: 0,
                    z: 0
                },
                zAcceleration = false
            }) {
                super(
                    new THREE.BoxGeometry(width, height, depth),
                    new THREE.MeshStandardMaterial({ color })
                )

                this.width = width
                this.height = height
                this.depth = depth;

                this.position.set(position.x, position.y, position.z)

                this.right = this.position.x + this.width / 2
                this.left = this.position.x - this.width / 2

                this.bottom = this.position.y - this.height / 2
                this.top = this.position.y + this.height / 2

                this.front = this.position.z + this.depth / 2
                this.back = this.position.z - this.depth / 2

                this.velocity = velocity
                this.gravity = -0.002

                this.zAcceleration = zAcceleration
            }

            updateSides() {
    this.right = this.position.x + this.width / 2;
    this.left = this.position.x - this.width / 2;

    this.bottom = this.position.y - this.height / 2;
    this.top = this.position.y + this.height / 2;

    this.front = this.position.z + this.depth / 2;
    this.back = this.position.z - this.depth / 2;
}

            update(ground) {
                this.updateSides()

                if (this.zAcceleration) this.velocity.z += 0.0003

                this.position.x += this.velocity.x
                this.position.z += this.velocity.z

                this.applyGravity(ground)
            }

            applyGravity(ground) {
                this.velocity.y += this.gravity


                if (
                    boxCollision({
                        box1: this,
                        box2: ground
                    })
                ) {
                    const friction = 0.5
                    this.velocity.y *= friction
                    this.velocity.y = -this.velocity.y
                } else this.position.y += this.velocity.y
            }
        }

const highScoreElement = document.getElementById('highScore');
let highScore = localStorage.getItem('highScore') || 0;


function updateHighScore() {
    highScoreElement.textContent = `High Score: ${highScore}`;
}


function updateHighScoreIfNeeded() {
    if (scoreCount > highScore) {
        highScore = scoreCount;
        localStorage.setItem('highScore', highScore);
        updateHighScore();
    }
}

updateHighScore();

        function boxCollision({ box1, box2 }) {
            const xCollision = box1.right >= box2.left && box1.left <= box2.right
            const yCollision =
                box1.bottom + box1.velocity.y <= box2.top && box1.top >= box2.bottom
            const zCollision = box1.front >= box2.back && box1.back <= box2.front

            return xCollision && yCollision && zCollision
        }

        const cube = new Box({
    width: 1,
    height: 1,
    depth: 1,
    velocity: {
        x: 0,
        y: -0.01,
        z: 0
    },
    color: '#00FFFF'
})
        cube.castShadow = true
        scene.add(cube)

        const ground = new Box({
            width: 10,
            height: 0.5,
            depth: 50,
            color: '#0369a1',
            position: {
                x: 0,
                y: -2,
                z: 0
            }
        })

        ground.receiveShadow = true
        scene.add(ground)

        const light = new THREE.DirectionalLight(0xffffff, 1)
        light.position.y = 3
        light.position.z = 1
        light.castShadow = true
        scene.add(light)

        scene.add(new THREE.AmbientLight(0xffffff, 0.5))

        camera.position.z = 5
        console.log(ground.top)
        console.log(cube.bottom)

        const keys = {
    a: {
        pressed: false
    },
    d: {
        pressed: false
    },
    s: {
        pressed: false
    },
    w: {
        pressed: false
    },
    ArrowLeft: {
        pressed: false
    },
    ArrowRight: {
        pressed: false
    },
    ArrowUp: {
        pressed: false
    },
    ArrowDown: {
        pressed: false
    }
};

window.addEventListener('keydown', (event) => {
    switch (event.code) {
        case 'KeyA':
        case 'ArrowLeft':
            keys.a.pressed = true;
            break;
        case 'KeyD':
        case 'ArrowRight':
            keys.d.pressed = true;
            break;
        case 'KeyS':
        case 'ArrowDown':
            keys.s.pressed = true;
            break;
        case 'KeyW':
        case 'ArrowUp':
            keys.w.pressed = true;
            break;
        case 'Space':
            cube.velocity.y = 0.08;
            break;
    }
});

window.addEventListener('keyup', (event) => {
    switch (event.code) {
        case 'KeyA':
        case 'ArrowLeft':
            keys.a.pressed = false;
            break;
        case 'KeyD':
        case 'ArrowRight':
            keys.d.pressed = false;
            break;
        case 'KeyS':
        case 'ArrowDown':
            keys.s.pressed = false;
            break;
        case 'KeyW':
        case 'ArrowUp':
            keys.w.pressed = false;
            break;
    }
});

        const enemies = []

        let frames = 0
        let spawnRate = 200
        let isGameOver = false;
        let animationId
        let livesCount = 3;
        let gameStarted = false;

const livesContainer = document.createElement('div');
livesContainer.style.position = 'fixed';
livesContainer.style.top = '20px';
livesContainer.style.left = '20px';
livesContainer.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
livesContainer.style.padding = '10px';
livesContainer.style.borderRadius = '5px';
livesContainer.style.color = 'white';
livesContainer.style.display = 'flex';
livesContainer.style.alignItems = 'center';
livesContainer.style.fontFamily = 'Arial, sans-serif';
livesContainer.style.fontSize = '36px';
livesContainer.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.5)';
document.body.appendChild(livesContainer);
const livesElement = document.createElement('span');
livesElement.textContent = `Lives: ${livesCount}`;
livesContainer.appendChild(livesElement);

livesElement.style.textTransform = 'uppercase';
livesElement.style.fontWeight = 'bold';
livesElement.style.textShadow = '0 0 10px rgba(255, 255, 255, 0.7), 0 0 20px rgba(255, 255, 255, 0.7), 0 0 30px rgba(255, 255, 255, 0.7)';
livesElement.style.animation = 'pulse 1.5s infinite';

const styleSheet = document.styleSheets[0];
styleSheet.insertRule(`
  @keyframes pulse {
    0% {
      text-shadow: 0 0 10px rgba(255, 255, 255, 0.7), 0 0 20px rgba(255, 255, 255, 0.7), 0 0 30px rgba(255, 255, 255, 0.7);
      transform: scale(1);
    }
    50% {
      text-shadow: 0 0 20px rgba(255, 255, 255, 0.9), 0 0 40px rgba(255, 255, 255, 0.9), 0 0 60px rgba(255, 255, 255, 0.9);
      transform: scale(1.1);
    }
    100% {
      text-shadow: 0 0 10px rgba(255, 255, 255, 0.7), 0 0 20px rgba(255, 255, 255, 0.7), 0 0 30px rgba(255, 255, 255, 0.7);
      transform: scale(1);
    }
  }
`, 0);

let isColliding = false;

let scoreCount = 0;


const scoreContainer = document.createElement('div');
scoreContainer.style.position = 'fixed';
scoreContainer.style.top = '20px';
scoreContainer.style.right = '20px';
scoreContainer.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
scoreContainer.style.padding = '15px';
scoreContainer.style.borderRadius = '5px';
scoreContainer.style.color = 'white';
scoreContainer.style.display = 'flex';
scoreContainer.style.alignItems = 'center';
scoreContainer.style.fontFamily = 'Arial, sans-serif';
scoreContainer.style.fontSize = '24px';
scoreContainer.style.boxShadow = '0 0 15px rgba(0, 0, 0, 0.5)';
scoreContainer.style.transition = 'background-color 0.3s, box-shadow 0.3s';
scoreContainer.style.backgroundImage = 'linear-gradient(45deg, #f06, #9f6)';

document.body.appendChild(scoreContainer);


const scoreElement = document.createElement('span');
scoreElement.textContent = `Score: ${scoreCount}`;
scoreContainer.appendChild(scoreElement);
scoreContainer.addEventListener('mouseenter', () => {
  scoreContainer.style.backgroundColor = 'rgba(0, 0, 0, 0.9)';
  scoreContainer.style.boxShadow = '0 0 20px rgba(0, 0, 0, 0.7)';
});

scoreContainer.addEventListener('mouseleave', () => {
  scoreContainer.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
  scoreContainer.style.boxShadow = '0 0 15px rgba(0, 0, 0, 0.5)';
});


function updateScore(newScore) {
  scoreCount = newScore;
  scoreElement.textContent = `Score: ${scoreCount}`;
}

        function handleCollision() {
    if (livesCount > 0 && !isColliding) {
        livesCount--;
        livesElement.textContent = `Lives: ${livesCount}`;
        isColliding = true;

        if (livesCount === 0) {
            gameOver();
        }
    }
}

function gameOver() {
    isGameOver = true;
    cancelAnimationFrame(animationId);
    const gameOverScreen = document.getElementById('gameOverScreen');
    gameOverScreen.style.display = 'block';

    // Pause the background music
    backgroundMusic.pause();

    updateHighScoreIfNeeded();
}



function restartGame() {
    isGameOver = false;

    // Play the background music
    backgroundMusic.play();

    livesCount = 3;
    livesElement.textContent = `Lives: ${livesCount}`;
    scoreCount = 0;
    scoreElement.textContent = `Score: ${scoreCount}`;

    const gameOverScreen = document.getElementById('gameOverScreen');
    gameOverScreen.style.display = 'none';

    cube.position.set(0, 0, 0);

    for (const enemy of enemies) {
        scene.remove(enemy);
    }
    enemies.length = 0;

    animate();
}

const restartButton = document.getElementById('restartButton');
restartButton.addEventListener('click', restartGame);

function animate() {
    animationId = requestAnimationFrame(animate);

    if (!isGameOver) {
        renderer.render(scene, camera);

        isColliding = false;

        cube.velocity.x = 0;
        cube.velocity.z = 0;

        if (keys.a.pressed || keys.ArrowLeft.pressed) cube.velocity.x = -0.05;
        else if (keys.d.pressed || keys.ArrowRight.pressed) cube.velocity.x = 0.05;

        if (keys.s.pressed || keys.ArrowDown.pressed) cube.velocity.z = 0.05;
        else if (keys.w.pressed || keys.ArrowUp.pressed) cube.velocity.z = -0.05;

        cube.update(ground);

        for (const enemy of enemies) {
            enemy.update(ground);

            if (boxCollision({ box1: cube, box2: enemy })) {
                cube.velocity.z = 0;

                const penetrationDepth = Math.abs(cube.front - enemy.back);
                cube.position.z -= penetrationDepth;

                handleCollision();
            }
        }

        if (cube.velocity.z < 0) {
            scoreCount++;
            scoreElement.textContent = `Score: ${scoreCount}`;
        }

        if (frames % spawnRate === 0) {
            if (spawnRate > 20) spawnRate -= 20;

            const enemy = new Box({
                width: 1,
                height: 1,
                depth: 1,
                position: {
                    x: (Math.random() - 0.5) * 10,
                    y: 0,
                    z: -20
                },
                velocity: {
                    x: 0,
                    y: 0,
                    z: 0.005
                },
                color: 'red',
                zAcceleration: true
            });

            enemy.castShadow = true;
            scene.add(enemy);
            enemies.push(enemy);
        }

        frames++;
    }
}

animate();
    </script>
</body>
</html>

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.