diff --git a/index.html b/index.html index 348db09..14db98f 100644 --- a/index.html +++ b/index.html @@ -86,6 +86,17 @@ img { cursor: pointer; } + .message { + min-width: 300px; + border: 1px solid black; + background-color: white; + padding: 20px; + text-align: center; + top: 50%; + left: 50%; + position: absolute; + transform: translate(-50%, -50%); + } diff --git a/toast.js b/toast.js index b808c23..36873af 100644 --- a/toast.js +++ b/toast.js @@ -9,9 +9,10 @@ function initGame(){ const clock = new THREE.Clock() const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); - camera.fov = 15 - camera.position.set( 0, 60, 40 ); + camera.fov = 20 + camera.position.set( 0, 320, 200 ); camera.lookAt(new THREE.Vector3(0,0,0)) + camera.updateProjectionMatrix() window.camera = camera const renderer = new THREE.WebGLRenderer({canvas:document.getElementById("canvas"),antialias:true}); @@ -25,6 +26,7 @@ function initGame(){ light.target.position.set(0,0,-30) light.castShadow = true light.shadow.mapSize.width = 2048 + camera.updateProjectionMatrix() light.shadow.mapSize.height = 2048 light.shadow.camera.near = 0.5 light.shadow.camera.far = 500 @@ -46,48 +48,82 @@ function initGame(){ let mixer; let velocity = new THREE.Vector3(0,0,10) + + let slopeAngle = THREE.MathUtils.degToRad(30) + + const GRAVITY = new THREE.Vector3(0,0,9.8) + const FRICTION = new THREE.Vector3(0,0,-1) + const FOREST_WIDTH = 1000 + const zOffset = 300 + + const spawnTree = (i) => { + trees[i].pos.set( + (Math.random() - 0.5)*FOREST_WIDTH, + 0, + (Math.random() * 10) + zOffset, + ) + trees[i].scale = 1 + (Math.random()*0.4) + trees[i].slope = -slopeAngle + } - const GRAVITY = new THREE.Vector3(0,0,1) - const FOREST_WIDTH = 500 - const initTrees = (zOffset) => { + const initTrees = () => { trees.forEach(tree => { tree.pos.set( (Math.random() - 0.5)*FOREST_WIDTH, 0, - (Math.random() - 0.5)*FOREST_WIDTH + zOffset, + (Math.random() - 0.5)*(FOREST_WIDTH/2) + zOffset, ) tree.scale = 1 + (Math.random()*0.4), - tree.slope = THREE.MathUtils.degToRad(-15) + tree.slope = -slopeAngle }) } + let gameOver = false + + const handleCrash = (tree) => { + gameOver = true + const msg = document.createElement('div') + msg.className = "message" + msg.innerHTML = 'Woops, you crashed! Game Over!' + document.body.appendChild(msg) + } + + const TREE_HIT_RADIUS = 2 const slopeAxis = new THREE.Vector3(1,0,0) const SKIER_POS = -50 const tick = (delta) => { + if(gameOver){ + return + } if(skier.position.z < SKIER_POS){ skier.position.add(velocity.clone().multiplyScalar(delta)) }else{ - if(keys.get('ArrowLeft')){ + if(keys.get('ArrowLeft') || keys.get('MouseLeft')){ if(skier.rotation.y > -Math.PI/2) - skier.rotation.y -= delta * 2 - }else if(keys.get('ArrowRight')){ + skier.rotation.y -= delta * 3 + }else if(keys.get('ArrowRight') || keys.get('MouseRight')){ if(skier.rotation.y < Math.PI/2) - skier.rotation.y += delta * 2 + skier.rotation.y += delta * 3 } // update velocity in the direction of skier velocity.set(0,0,velocity.length()).applyAxisAngle(UP,skier.rotation.y) - // TODO add gravity / friction - // todo scale gravity based on slope - velocity.add(GRAVITY.clone().multiplyScalar(delta)) + velocity.add(GRAVITY.clone().multiplyScalar(delta * Math.sin(slopeAngle))) + velocity.add(FRICTION.clone().multiplyScalar(delta * Math.sin(skier.rotation.y))) + velocity.add(velocity.clone().normalize().multiplyScalar(-1).multiplyScalar(0.01)) for(let i=0; i< trees.length;i++){ trees[i].pos.z -= velocity.z * delta trees[i].pos.x -= velocity.x * delta } } - // TODO cull and respawn trees that are past the top of the screen for(let i = 0; i < trees.length; i++){ const tree = trees[i] + if(tree.pos.z < -125){ + spawnTree(i) + } + if(new THREE.Vector3().subVectors(skier.position,tree.pos).length() < TREE_HIT_RADIUS){ + handleCrash(tree) + } const matrix = new THREE.Matrix4().makeScale(tree.scale,tree.scale,tree.scale) matrix.premultiply(new THREE.Matrix4().makeRotationAxis(slopeAxis,tree.slope)) matrix.premultiply(new THREE.Matrix4().makeTranslation(tree.pos.x,tree.pos.y,tree.pos.z)) @@ -100,7 +136,7 @@ function initGame(){ const delta = clock.getDelta() tick(delta) mixer.update(delta) - renderer.render( scene, camera ); + renderer.render(scene,camera) } const loader = new GLTFLoader() @@ -117,10 +153,9 @@ function initGame(){ treeMesh = gltf.scene.children.find(obj => obj.name === "tree") gltf.scene.remove(treeMesh) - console.log(treeMesh) treeInstances = new THREE.InstancedMesh(treeMesh.geometry,new THREE.MeshToonMaterial({color:'DarkGreen'}),TREE_COUNT) treeInstances.castShadow = true - initTrees(300) + initTrees() scene.add(treeInstances) skier.add(gltf.scene) @@ -131,10 +166,38 @@ function initGame(){ camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); + composer.setSize(window.innerWidth, window.innerHeight); }); window.addEventListener('keydown',(e) => keys.set(e.key,true)) window.addEventListener('keyup',(e) => keys.set(e.key,false)) + window.addEventListener('pointerdown', (e) => { + if(e.button !== 0) return + if(e.clientX < window.innerWidth/2){ + keys.set('MouseLeft',true) + keys.set('MouseRight',false) + }else{ + keys.set('MouseLeft',false) + keys.set('MouseRight',true) + } + }) + window.addEventListener('pointermove', (e) => { + if(e.buttons & 1){ + if(e.clientX < window.innerWidth/2){ + keys.set('MouseLeft',true) + keys.set('MouseRight',false) + }else{ + keys.set('MouseLeft',false) + keys.set('MouseRight',true) + } + } + }) + window.addEventListener('pointerup', (e) => { + keys.set('MouseLeft',false) + keys.set('MouseRight',false) + }) + + } function main(){ diff --git a/toasterbot_skifree.glb b/toasterbot_skifree.glb index dd3961c..4ab3c13 100755 Binary files a/toasterbot_skifree.glb and b/toasterbot_skifree.glb differ