diff --git a/js/Cube.js b/js/Cube.js index 665d3d9..36d6d93 100644 --- a/js/Cube.js +++ b/js/Cube.js @@ -1,12 +1,15 @@ import { Element } from "./Element.js"; export class Cube extends Element { - constructor() { + constructor(color) { super(); this.data = new THREE.Mesh( new THREE.BoxGeometry(1, 1, 1), - new THREE.MeshBasicMaterial({ color: 0x00ff00 }) + new THREE.MeshBasicMaterial({ color: color }) ); + + // Create shadows + this.data.castShadow = true; } } diff --git a/js/Element.js b/js/Element.js index 50b4ca6..40300e3 100644 --- a/js/Element.js +++ b/js/Element.js @@ -1,8 +1,16 @@ export class Element { constructor() { this.data = undefined; + + // Rotate everything on X by this factor to give some perspective + this.gameRotation = 2; } + /** + * Animation of the element + */ + animation = () => {}; + /** * Rotate the element */ diff --git a/js/Env.js b/js/Env.js index 55eb7b8..dd90e76 100644 --- a/js/Env.js +++ b/js/Env.js @@ -28,6 +28,20 @@ export class Env { window.innerHeight / this.quality, false ); + + // Store all elements in the env + this.elements = []; + + // Setup renderer for lights + this.renderer.shadowMap.enabled = true; + this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; + + // Add light source + const light = new THREE.DirectionalLight(THREE.Color.NAMES.white); + // On top : 1, right : 2 and between player (0) and camera (7) : 4 + light.position.set(2, 1, 4); + light.castShadow = true; + this.scene.add(light); } /** @@ -66,9 +80,21 @@ export class Env { * @param {Element} element Element */ addToScene = (element) => { + this.elements.push(element); this.scene.add(element.data); }; + /** + * Animate all the elements in the environnement + */ + animate = () => { + this.elements.forEach((element) => { + if (element.animation) { + element.animation(); + } + }); + }; + /** * Render the current scene, using the camera * @returns diff --git a/js/Plane.js b/js/Plane.js index af58170..d2304f1 100644 --- a/js/Plane.js +++ b/js/Plane.js @@ -11,7 +11,7 @@ export class Plane extends Element { this.data = new THREE.Mesh( new THREE.PlaneGeometry(width, height), - new THREE.MeshBasicMaterial({ + new THREE.MeshPhongMaterial({ color: color1, side: THREE.DoubleSide, map: textureGradient( @@ -23,6 +23,9 @@ export class Plane extends Element { }) ); - this.data.rotation.x = 2; + this.data.rotation.x = this.gameRotation; + + // Shadows + this.data.receiveShadow = true; } } diff --git a/js/main.js b/js/main.js index 4038a7f..75d0f26 100644 --- a/js/main.js +++ b/js/main.js @@ -1,6 +1,6 @@ import { Env } from "./Env.js"; -import { Cube } from "./Cube.js"; import { Plane } from "./Plane.js"; +import { Joueur } from "./player.js"; window.addEventListener("load", () => main()); @@ -17,9 +17,14 @@ const main = () => { ); env.addToScene(plan); + const joueur = new Joueur(THREE.Color.NAMES.red); + env.addToScene(joueur); + + addEventListener("keypress", joueur.controlUser); + const animate = () => { requestAnimationFrame(animate); - + env.animate(); env.render(); }; diff --git a/js/player.js b/js/player.js new file mode 100644 index 0000000..e04c10a --- /dev/null +++ b/js/player.js @@ -0,0 +1,109 @@ +import { Cube } from "./Cube.js"; + +class Rotation { + constructor(position) { + this.default = position.clone(); + this.state = false; + this.rotation = 0; + this.jump = 0; + + this.rotationVelocity = 0.05; + this.jumpVelocity = 0.1; + + this.lastJump = 0; + } + + /** + * Change the state of the animation and update the latest jump time + * @param {number} time + */ + changeState = (time) => { + this.state = !this.state; + if (time) { + this.lastJump = time; + } + }; + + /** + * Change the final rotation + * @param {number} rotation + */ + changeRotation = (rotation) => { + this.rotation = rotation; + }; + + /** + * Change the final position + * @param {number} jump + */ + changeJump = (jump) => { + this.jump = jump; + }; + + /** + * Return the state of the jump + * @returns boolean representing if the object is going up or down + */ + falling = () => { + return this.jump == this.default.y; + }; +} + +export class Joueur extends Cube { + constructor(color) { + super(color); + + this.data.position.y = 0.5; + this.data.rotation.x = this.gameRotation; + this.movementData = new Rotation(this.data.position); + } + + animation = () => { + // If we jump + if (this.movementData.state) { + // Rotation + this.data.rotation.y -= this.movementData.rotationVelocity; + + // If we're falling (2nd part of the jump) + if (this.movementData.falling()) { + // Gravity! + this.data.position.y -= this.movementData.jumpVelocity; + } else { + // If we're jumping (1st part of the jump) : jumping + this.data.position.y += this.movementData.jumpVelocity; + + // Check if the jump stop and we need to goes down + if (this.data.position.y >= this.movementData.jump) { + this.movementData.changeJump(this.movementData.default.y); + } + } + + // End of the rotation + if (this.data.rotation.y <= this.movementData.rotation) { + // Force the final rotation + this.data.rotation.y = this.movementData.rotation; + + // Force the end of the jump + this.data.position.y = this.movementData.default.y; + + // End + this.movementData.changeState(); + } + } + }; + + controlUser = (key) => { + const now = Date.now(); + if ( + key.code == "Space" && + !this.movementData.state && + now - this.movementData.lastJump > 400 + ) { + this.movementData.changeRotation( + this.data.rotation.y - Math.PI / 2 + ); + this.movementData.changeJump(this.data.position.y + Math.PI / 2); + this.movementData.changeState(now); + } + }; +}