3d-mario-bug / index.html
WebashalarForML's picture
Add 2 files
790370e verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pixel Quest 3D: Find Your Friend</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
font-family: 'Press Start 2P', cursive;
image-rendering: pixelated;
}
canvas {
display: block;
image-rendering: pixelated;
}
@font-face {
font-family: 'Press Start 2P';
src: url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
}
.pixel-border {
border: 4px solid #fff;
box-shadow: 0 0 0 4px #000, inset 0 0 0 4px #000;
}
.quiz-option {
transition: all 0.1s;
border: 3px solid #fff;
box-shadow: 0 0 0 3px #000;
}
.quiz-option:hover {
background-color: #4a5568;
transform: translateY(-2px);
}
.quiz-option.correct {
background-color: #48bb78;
}
.quiz-option.wrong {
background-color: #f56565;
}
.pixel-text {
text-shadow: 3px 3px 0 #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
}
</style>
</head>
<body class="bg-gray-900">
<div id="game-container" class="relative w-full h-screen">
<!-- Start Screen -->
<div id="start-screen" class="absolute inset-0 flex flex-col items-center justify-center bg-gray-900 z-10">
<h1 class="text-4xl md:text-6xl text-green-400 mb-8 pixel-text">PIXEL QUEST 3D</h1>
<p class="text-xl text-white mb-12 pixel-text">Find Your Lost Friend!</p>
<div class="w-32 h-32 mb-8 bg-red-500 pixel-border animate-bounce"></div>
<button id="start-btn" class="px-8 py-4 bg-green-500 hover:bg-green-600 text-white font-bold pixel-border transition-all transform hover:scale-105">
START ADVENTURE
</button>
</div>
<!-- Quiz Modal -->
<div id="quiz-modal" class="hidden absolute inset-0 flex items-center justify-center z-20 bg-black bg-opacity-70">
<div class="bg-gray-800 p-6 rounded-lg w-11/12 max-w-md pixel-border">
<h2 id="quiz-question" class="text-xl text-white mb-6 pixel-text"></h2>
<div id="quiz-options" class="space-y-3"></div>
<div id="quiz-feedback" class="mt-4 text-yellow-300 hidden pixel-text"></div>
</div>
</div>
<!-- End Screen -->
<div id="end-screen" class="hidden absolute inset-0 flex flex-col items-center justify-center bg-gray-900 z-10">
<h1 class="text-4xl md:text-6xl text-green-400 mb-8 pixel-text">FRIEND FOUND!</h1>
<div class="flex items-center mb-8">
<div class="w-32 h-32 bg-red-500 pixel-border mr-4"></div>
<div class="w-32 h-32 bg-blue-500 pixel-border"></div>
</div>
<p id="final-score" class="text-xl text-white mb-12 pixel-text"></p>
<button id="restart-btn" class="px-8 py-4 bg-green-500 hover:bg-green-600 text-white font-bold pixel-border transition-all transform hover:scale-105">
PLAY AGAIN
</button>
</div>
<!-- UI Elements -->
<div id="ui-container" class="absolute top-4 left-4 z-10 hidden">
<div class="bg-gray-800 bg-opacity-70 p-3 rounded pixel-border">
<p class="text-white pixel-text">Score: <span id="score-display">0</span></p>
<p class="text-white pixel-text">Quizzes: <span id="quiz-count">0</span>/4</p>
</div>
</div>
</div>
<script>
// Game variables
let scene, camera, renderer;
let player, platforms = [], checkpoints = [], coins = [];
let playerVelocity = { x: 0, y: 0, z: 0 };
let playerSpeed = 0.2;
let jumpForce = 0.4;
let gravity = 0.02;
let isJumping = false;
let gamePaused = false;
let score = 0;
let quizzesCompleted = 0;
let currentCheckpoint = null;
let friendFound = false;
let keys = {};
let worldWidth = 100;
let worldDepth = 20;
let friend;
// Quiz questions
const quizQuestions = [
{
question: "What is your friend's favorite color?",
options: ["Blue", "Green", "Red", "Yellow"],
correct: 0
},
{
question: "Where did you first meet your friend?",
options: ["At school", "At the park", "At a party", "Online"],
correct: 1
},
{
question: "What's your friend's favorite food?",
options: ["Pizza", "Sushi", "Burgers", "Tacos"],
correct: 2
},
{
question: "What was your last adventure together?",
options: ["Hiking", "Movie marathon", "Road trip", "Camping"],
correct: 3
}
];
// Initialize game
function init() {
// Scene setup
scene = new THREE.Scene();
scene.background = new THREE.Color(0x87CEEB); // Sky blue
// Camera (orthographic for 2.5D effect)
const aspect = window.innerWidth / window.innerHeight;
camera = new THREE.OrthographicCamera(
-10 * aspect, 10 * aspect, // left, right
10, -10, // top, bottom
0.1, 1000
);
camera.position.set(0, 5, 20);
camera.lookAt(0, 0, 0);
// Renderer
renderer = new THREE.WebGLRenderer({ antialias: false });
renderer.setPixelRatio(window.devicePixelRatio > 1 ? 2 : 1);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.getElementById('game-container').appendChild(renderer.domElement);
// Lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(10, 20, 10);
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 1024;
directionalLight.shadow.mapSize.height = 1024;
scene.add(directionalLight);
// Create world
createWorld();
// Create player
createPlayer();
// Create checkpoints
createCheckpoints();
// Create friend
createFriend();
// Event listeners
window.addEventListener('resize', onWindowResize);
document.addEventListener('keydown', onKeyDown);
document.addEventListener('keyup', onKeyUp);
// Start button
document.getElementById('start-btn').addEventListener('click', startGame);
document.getElementById('restart-btn').addEventListener('click', restartGame);
// Hide start screen after assets load
setTimeout(() => {
document.getElementById('ui-container').classList.remove('hidden');
}, 100);
}
function createWorld() {
// Ground
const groundGeometry = new THREE.BoxGeometry(worldWidth, 1, worldDepth);
const groundMaterial = new THREE.MeshStandardMaterial({
color: 0x2e8b57,
roughness: 1.0
});
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.position.y = -0.5;
ground.receiveShadow = true;
scene.add(ground);
// Platforms
const platformPositions = [
{ x: 10, y: 2, z: 0, width: 8, depth: 8 },
{ x: 25, y: 3, z: 5, width: 6, depth: 6 },
{ x: 40, y: 4, z: -3, width: 10, depth: 10 },
{ x: 60, y: 2, z: 2, width: 12, depth: 8 },
{ x: 80, y: 5, z: 0, width: 8, depth: 8 }
];
platformPositions.forEach(pos => {
const platformGeometry = new THREE.BoxGeometry(pos.width, 1, pos.depth);
const platformMaterial = new THREE.MeshStandardMaterial({
color: 0x8b4513,
roughness: 1.0
});
const platform = new THREE.Mesh(platformGeometry, platformMaterial);
platform.position.set(pos.x, pos.y, pos.z);
platform.receiveShadow = true;
scene.add(platform);
platforms.push(platform);
});
// Coins
for (let i = 0; i < 20; i++) {
const coinGeometry = new THREE.CylinderGeometry(0.5, 0.5, 0.2, 16);
const coinMaterial = new THREE.MeshStandardMaterial({ color: 0xffd700 });
const coin = new THREE.Mesh(coinGeometry, coinMaterial);
// Position coins randomly above platforms or ground
let x, y, z;
if (Math.random() > 0.5) {
// On platforms
const platform = platforms[Math.floor(Math.random() * platforms.length)];
x = platform.position.x + (Math.random() - 0.5) * (platform.geometry.parameters.width - 2);
y = platform.position.y + 1;
z = platform.position.z + (Math.random() - 0.5) * (platform.geometry.parameters.depth - 2);
} else {
// On ground
x = Math.random() * worldWidth;
y = 0.5;
z = (Math.random() - 0.5) * (worldDepth - 4);
}
coin.position.set(x, y, z);
coin.rotation.x = Math.PI / 2;
coin.userData.isCoin = true;
scene.add(coin);
coins.push(coin);
}
}
function createPlayer() {
// Simple pixel-style character
const playerGeometry = new THREE.BoxGeometry(1, 2, 1);
const playerMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
player = new THREE.Mesh(playerGeometry, playerMaterial);
player.position.set(0, 1, 0);
player.castShadow = true;
scene.add(player);
}
function createCheckpoints() {
// Create 4 checkpoints along the path
const checkpointPositions = [
{ x: 15, y: 0, z: 0 },
{ x: 30, y: 0, z: 0 },
{ x: 50, y: 0, z: 0 },
{ x: 70, y: 0, z: 0 }
];
checkpointPositions.forEach((pos, index) => {
const checkpointGeometry = new THREE.BoxGeometry(2, 2, 2);
const checkpointMaterial = new THREE.MeshStandardMaterial({
color: 0xffff00,
transparent: true,
opacity: 0.7
});
const checkpoint = new THREE.Mesh(checkpointGeometry, checkpointMaterial);
checkpoint.position.set(pos.x, pos.y + 1, pos.z);
checkpoint.userData.isCheckpoint = true;
checkpoint.userData.quizIndex = index;
scene.add(checkpoint);
checkpoints.push(checkpoint);
});
}
function createFriend() {
// Create the friend character at the end
const friendGeometry = new THREE.BoxGeometry(1, 2, 1);
const friendMaterial = new THREE.MeshStandardMaterial({ color: 0x0000ff });
friend = new THREE.Mesh(friendGeometry, friendMaterial);
friend.position.set(worldWidth - 5, 1, 0);
friend.castShadow = true;
friend.userData.isFriend = true;
scene.add(friend);
}
function startGame() {
document.getElementById('start-screen').classList.add('hidden');
document.getElementById('ui-container').classList.remove('hidden');
animate();
}
function restartGame() {
// Reset game state
score = 0;
quizzesCompleted = 0;
friendFound = false;
gamePaused = false;
// Reset player position
player.position.set(0, 1, 0);
playerVelocity = { x: 0, y: 0, z: 0 };
// Reset UI
document.getElementById('score-display').textContent = '0';
document.getElementById('quiz-count').textContent = '0/4';
document.getElementById('end-screen').classList.add('hidden');
document.getElementById('ui-container').classList.remove('hidden');
// Restart animation
animate();
}
function showQuiz(quizIndex) {
gamePaused = true;
currentCheckpoint = quizIndex;
const quiz = quizQuestions[quizIndex];
document.getElementById('quiz-question').textContent = quiz.question;
const optionsContainer = document.getElementById('quiz-options');
optionsContainer.innerHTML = '';
quiz.options.forEach((option, i) => {
const optionBtn = document.createElement('button');
optionBtn.className = 'w-full p-3 bg-gray-700 text-white quiz-option pixel-text';
optionBtn.textContent = option;
optionBtn.addEventListener('click', () => checkAnswer(i));
optionsContainer.appendChild(optionBtn);
});
document.getElementById('quiz-feedback').classList.add('hidden');
document.getElementById('quiz-modal').classList.remove('hidden');
}
function checkAnswer(answerIndex) {
const quiz = quizQuestions[currentCheckpoint];
const options = document.querySelectorAll('.quiz-option');
if (answerIndex === quiz.correct) {
// Correct answer
options[answerIndex].classList.add('correct');
document.getElementById('quiz-feedback').textContent = 'Correct!';
document.getElementById('quiz-feedback').classList.remove('hidden');
setTimeout(() => {
quizzesCompleted++;
document.getElementById('quiz-count').textContent = `${quizzesCompleted}/4`;
document.getElementById('quiz-modal').classList.add('hidden');
gamePaused = false;
// Remove the checkpoint
const checkpoint = checkpoints[currentCheckpoint];
scene.remove(checkpoint);
checkpoints[currentCheckpoint] = null;
// Add score
score += 100;
document.getElementById('score-display').textContent = score;
}, 1000);
} else {
// Wrong answer
options[answerIndex].classList.add('wrong');
options[quiz.correct].classList.add('correct');
document.getElementById('quiz-feedback').textContent = 'Try again!';
document.getElementById('quiz-feedback').classList.remove('hidden');
}
}
function onWindowResize() {
const aspect = window.innerWidth / window.innerHeight;
camera.left = -10 * aspect;
camera.right = 10 * aspect;
camera.top = 10;
camera.bottom = -10;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function onKeyDown(event) {
keys[event.code] = true;
// Jump
if (event.code === 'Space' && !isJumping && !gamePaused) {
playerVelocity.y = jumpForce;
isJumping = true;
}
}
function onKeyUp(event) {
keys[event.code] = false;
}
function checkCollisions() {
// Check platform collisions
let onGround = false;
const playerBox = new THREE.Box3().setFromObject(player);
platforms.forEach(platform => {
const platformBox = new THREE.Box3().setFromObject(platform);
if (playerBox.intersectsBox(platformBox)) {
// Check if player is on top of platform
if (player.position.y > platform.position.y + 0.9) {
player.position.y = platform.position.y + 1 + player.geometry.parameters.height / 2;
playerVelocity.y = 0;
isJumping = false;
onGround = true;
}
}
});
// Ground collision
if (player.position.y <= 0) {
player.position.y = 0;
playerVelocity.y = 0;
isJumping = false;
onGround = true;
}
// Check coin collisions
coins.forEach((coin, index) => {
if (coin && new THREE.Box3().setFromObject(player).intersectsBox(new THREE.Box3().setFromObject(coin))) {
scene.remove(coin);
coins[index] = null;
score += 10;
document.getElementById('score-display').textContent = score;
}
});
// Check checkpoint collisions
checkpoints.forEach((checkpoint, index) => {
if (checkpoint && new THREE.Box3().setFromObject(player).intersectsBox(new THREE.Box3().setFromObject(checkpoint))) {
showQuiz(index);
}
});
// Check friend collision
if (!friendFound && new THREE.Box3().setFromObject(player).intersectsBox(new THREE.Box3().setFromObject(friend)) && quizzesCompleted === 4) {
friendFound = true;
gamePaused = true;
// Show end screen
document.getElementById('final-score').textContent = `Final Score: ${score}`;
document.getElementById('end-screen').classList.remove('hidden');
document.getElementById('ui-container').classList.add('hidden');
}
return onGround;
}
function animate() {
if (gamePaused) return;
requestAnimationFrame(animate);
// Player movement
if (keys['KeyA'] || keys['ArrowLeft']) {
playerVelocity.x = -playerSpeed;
player.rotation.y = Math.PI;
} else if (keys['KeyD'] || keys['ArrowRight']) {
playerVelocity.x = playerSpeed;
player.rotation.y = 0;
} else {
playerVelocity.x = 0;
}
// Apply gravity
playerVelocity.y -= gravity;
// Update player position
player.position.x += playerVelocity.x;
player.position.y += playerVelocity.y;
// Check collisions
checkCollisions();
// Update camera to follow player (side-scrolling)
camera.position.x = player.position.x;
camera.position.z = 20;
camera.lookAt(player.position.x, player.position.y, 0);
renderer.render(scene, camera);
}
// Start the game
init();
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=WebashalarForML/3d-mario-bug" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>