<!DOCTYPE html>
<html lang="ja" >
<head>
<meta charset="UTF-8" >
<title>Image Booster 1.0 </title>
<style>
body { margin: 0 ; overflow: hidden; background:
h1 { font-size: 60 px; margin: 0 ; color:
.pop-text {
position: absolute;
font-weight: bold;
font-size: 32 px;
pointer-events: none;
animation: moveUpFade 0.8 s ease-out forwards;
white-space: nowrap;
}
@keyframes moveUpFade {
0 % { transform: translateY(0 ); opacity: 1 ; }
100 % { transform: translateY(-100 px); opacity: 0 ; }
}
position: absolute;
top: 0 ; left: 0 ; width: 100 %; height: 100 %;
background: radial-gradient(circle, transparent 30 %, rgba(255 ,255 ,255 ,0.2 ) 100 %);
display: none;
pointer-events: none;
mask-image: repeating-conic-gradient(from 0 deg,
-webkit-mask-image: repeating-conic-gradient(from 0 deg,
}
</style>
<script type="importmap" >
{ "imports" : { "three" : "https://unpkg.com/three@0.160.0/build/three.module.js" } }
</script>
</head>
<body>
<div id="ui-layer" >
<div id="speed-lines" ></div>
<div id="hud" >
<div id="hp-disp" >HP: 10 </div>
<div id="score-disp" >SCORE: 0 </div>
<div id="time-disp" >TIME: 180.00 </div>
</div>
<div id="center-text" ></div>
</div>
<script type="module" >
import * as THREE from 'three' ;
const MAX_HP = 10 ;
const STAGE_TIME_LIMIT = 180 ;
const LANE_WIDTH = 8 ;
let scene, camera, renderer, clock;
let gameState = "TITLE" ;
let score = 0 , hp = MAX_HP, timeLeft = STAGE_TIME_LIMIT;
let isBoost = false , boostTimer = 0 ;
let player = { mesh: null , x: 0 , y: 0 , z: 0 , vx: 0 , vy: 0 , vz: 0.5 , rotZ: 0 , rotX: 0 };
let worldObjects = [];
let particles = [];
let keys = {};
let nextSpawnZ = 50 ;
function PlaySound (filename) {
const sound = new Audio(`./Sound/${filename}`);
sound.volume = 0.5 ;
sound.play().catch (() => {});
}
function init () {
scene = new THREE.Scene();
scene.background = new THREE.Color(0x050505 );
scene.fog = new THREE.Fog(0x050505 , 30 , 160 );
camera = new THREE.PerspectiveCamera(60 , window.innerWidth / window.innerHeight, 0.1 , 1000 );
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const ambient = new THREE.AmbientLight(0xffffff , 0.6 );
scene.add(ambient);
const sun = new THREE.DirectionalLight(0xffffff , 1.2 );
sun.position.set(10 , 20 , 10 );
scene.add(sun);
player.mesh = new THREE.Mesh(new THREE.BoxGeometry(1.6 , 0.8 , 3 ), new THREE.MeshPhongMaterial({ color: 0x00ccff }));
scene.add(player.mesh);
const ground = new THREE.Mesh(new THREE.PlaneGeometry(30 , 10000 ), new THREE.MeshPhongMaterial({ color: 0x111111 }));
ground.rotation.x = -Math.PI / 2 ;
scene.add(ground);
window.addEventListener('keydown' , (e) => {
keys[e.code] = true ;
if (e.code === 'Space' ) handleSpace();
});
window.addEventListener('keyup' , (e) => keys[e.code] = false );
clock = new THREE.Clock();
changeState("TITLE" );
animate();
}
function handleSpace () {
if (gameState === "TITLE" ) { changeState("STAGE_START" ); PlaySound("pageup.mp3" ); }
else if (gameState === "STAGE_START" ) { changeState("PLAYING" ); PlaySound("pageup.mp3" ); }
else if (gameState === "PLAYING" && player.y <= 0.01 ) { player.vy = 0.6 ; PlaySound("jump_up.mp3" ); }
}
function changeState (s) {
gameState = s;
document.getElementById('hud' ).style.display = (s === "PLAYING" ) ? "block" : "none" ;
const menu = document.getElementById('center-text' );
if (s === "TITLE" ) menu.innerHTML = "<h1>Image Booster 1.0</h1><p>PRESS SPACE TO START</p>" ;
else if (s === "STAGE_START" ) { menu.innerHTML = "<h1>STAGE 1</h1><p>READY? PRESS SPACE</p>" ; resetPlayer(); }
else if (s === "PLAYING" ) menu.innerHTML = "" ;
}
function resetPlayer () {
player.x = 0 ; player.y = 0 ; player.z = 0 ; player.vz = 0.5 ;
hp = MAX_HP; score = 0 ;
}
function showPopText (text, color = "white" ) {
const div = document.createElement('div' );
div.className = 'pop-text' ;
div.innerText = text;
div.style.color = color;
div.style.left = (50 + (Math.random()*20 -10 )) + "%" ;
div.style.top = (40 + (Math.random()*20 -10 )) + "%" ;
document.getElementById('ui-layer' ).appendChild(div);
setTimeout(() => div.remove(), 800 );
}
function animate () {
requestAnimationFrame(animate);
if (gameState === "PLAYING" ) {
updatePhysics();
updateObjects();
updateParticles();
}
updateCamera();
renderer.render(scene, camera);
}
function updatePhysics () {
if (keys['ArrowUp' ]) player.vz += 0.015 ;
else if (keys['ArrowDown' ]) { player.vz -= 0.03 ; if (player.vz > 0.3 ) PlaySound("break.mp3" ); }
else player.vz *= 0.995 ;
player.vz = Math.max(0.2 , Math.min(player.vz, isBoost ? 2.5 : 1.3 ));
if (keys['ArrowLeft' ]) { player.vx = THREE.MathUtils.lerp(player.vx, 0.4 , 0.1 ); player.rotZ = THREE.MathUtils.lerp(player.rotZ, 0.4 , 0.1 ); }
else if (keys['ArrowRight' ]) { player.vx = THREE.MathUtils.lerp(player.vx, -0.4 , 0.1 ); player.rotZ = THREE.MathUtils.lerp(player.rotZ, -0.4 , 0.1 ); }
else { player.vx = THREE.MathUtils.lerp(player.vx, 0 , 0.1 ); player.rotZ = THREE.MathUtils.lerp(player.rotZ, 0 , 0.1 ); }
player.y += player.vy;
if (player.y > 0 ) { player.vy -= 0.03 ; player.rotX = player.vy * 0.5 ; }
else { if (player.y < 0 ) PlaySound("jump_down.mp3" ); player.y = 0 ; player.vy = 0 ; player.rotX = 0 ; }
player.x += player.vx;
player.z += player.vz;
player.mesh.position.set(player.x, player.y + 0.5 , player.z);
player.mesh.rotation.z = player.rotZ;
player.mesh.rotation.x = -player.rotX;
timeLeft -= 1 /60 ;
updateUI();
}
function updateCamera () {
camera.position.set(player.x * 0.5 , player.y + 5 , player.z - 12 );
camera.lookAt(new THREE.Vector3(player.x, player.y + 1 , player.z + 10 ));
camera.rotation.z += player.rotZ * 0.3 ;
}
function updateObjects () {
if (player.z + 150 > nextSpawnZ) { spawnObject(nextSpawnZ); nextSpawnZ += 12 ; }
worldObjects.forEach ((obj, idx) => {
if (player.mesh.position.distanceTo(obj.mesh.position) < 2.5 ) {
handleCollision(obj);
scene.remove(obj.mesh);
worldObjects.splice(idx, 1 );
}
if (obj.mesh.position.z < player.z - 20 ) { scene.remove(obj.mesh); worldObjects.splice(idx, 1 ); }
});
}
function spawnObject (z) {
const types = ["block" , "hurdle" , "score" , "boost" , "heal" ];
const type = types[Math.floor(Math.random() * types.length)];
const colors = { block: 0x884422 , hurdle: 0xff3300 , score: 0xffff00 , boost: 0x00ffff , heal: 0x00ff00 };
const mesh = new THREE.Mesh(new THREE.BoxGeometry(2 , 2 , 2 ), new THREE.MeshPhongMaterial({ color: colors[type] }));
mesh.position.set((Math.random() - 0.5 ) * LANE_WIDTH * 2 , 1 , z);
scene.add(mesh);
worldObjects.push({ mesh, type });
}
function handleCollision (obj) {
if (obj.type === "block" ) {
score += 10 ; PlaySound("block.mp3" );
showPopText("+10" , "#ffaa00" );
createExplosion(obj.mesh.position, 0xffaa00 , 15 );
} else if (obj.type === "hurdle" ) {
if (isBoost) {
score += 50 ; PlaySound("block.mp3" );
showPopText("+50" , "#ff3300" );
createExplosion(obj.mesh.position, 0xff3300 , 20 );
} else {
hp--; PlaySound("hurdle.mp3" );
showPopText("HIT! HP-1" , "#ffffff" );
createExplosion(obj.mesh.position, 0xffffff , 10 );
}
} else if (obj.type === "score" ) {
score += 100 ; PlaySound("heal.mp3" );
showPopText("+100" , "#ffff00" );
} else if (obj.type === "boost" ) {
isBoost = true ; boostTimer = 300 ;
PlaySound("boost.mp3" );
showPopText("BOOST!" , "#00ffff" );
} else if (obj.type === "heal" ) {
hp = Math.min(MAX_HP, hp + 1 );
PlaySound("heal.mp3" );
showPopText("HP+1" , "#00ff00" );
}
}
function createExplosion (pos, color, count) {
for (let i = 0 ; i < count; i++) {
const p = new THREE.Mesh(new THREE.BoxGeometry(0.4 , 0.4 , 0.4 ), new THREE.MeshBasicMaterial({ color: color }));
p.position.copy(pos);
const vel = new THREE.Vector3((Math.random()-0.5 )*0.8 , Math.random()*0.8 , (Math.random()-0.5 )*0.8 );
scene.add(p);
particles.push({ mesh: p, vel: vel, life: 40 });
}
}
function updateParticles () {
for (let i = particles.length - 1 ; i >= 0 ; i--) {
const p = particles[i];
p.mesh.position.add(p.vel);
p.vel.y -= 0.02 ;
if (--p.life <= 0 ) { scene.remove(p.mesh); particles.splice(i, 1 ); }
}
}
function updateUI () {
document.getElementById('hp-disp' ).innerText = "HP: " + hp;
document.getElementById('score-disp' ).innerText = "SCORE: " + score;
document.getElementById('time-disp' ).innerText = "TIME: " + Math.max(0 , timeLeft).toFixed(2 );
const speedLines = document.getElementById('speed-lines' );
if (isBoost) {
speedLines.style.display = 'block' ;
if (--boostTimer <= 0 ) isBoost = false ;
} else {
speedLines.style.display = 'none' ;
}
}
init();
</script>
</body>
</html>
copy
コメント