Skip to content

Commit

Permalink
Added in ball with a shadow and a line to trace its path.
Browse files Browse the repository at this point in the history
Added simple physics, and invisible walls to contain the ball.
  • Loading branch information
leslieyip02 committed Jul 21, 2022
1 parent b358ac3 commit 0dd9a64
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 8 deletions.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@
When building:
- Add . to the paths to scripts
- Add "tennis" to the .glb file path
- Add . to the .glb file path
-->
6 changes: 5 additions & 1 deletion main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { SceneManager } from "./src/SceneManager";
import { Court } from "./src/Court.js";
import { Racket } from "./src/Racket.js";
import { Net } from "./src/Net";
import { Racket } from "./src/Racket.js";
import { Ball } from "./src/Ball";

const sm = new SceneManager();
const scene = sm.scene;
Expand All @@ -18,10 +19,12 @@ const loader = new GLTFLoader();
const court = new Court(scene);
const net = new Net(scene, loader);
const racket = new Racket(scene, loader);
const ball = new Ball(scene);

court.render();
net.render();
racket.render();
ball.render();

// handle inputs
let keyboard = {};
Expand All @@ -33,6 +36,7 @@ function animate() {
requestAnimationFrame(animate);

racket.update(keyboard);
ball.update(net);
renderer.render(scene, camera);
}

Expand Down
Binary file modified models/racket.blend
Binary file not shown.
Binary file modified models/racket.blend1
Binary file not shown.
Binary file modified models/racket.glb
Binary file not shown.
105 changes: 105 additions & 0 deletions src/Ball.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import * as THREE from "three";

const G = 0.01;
const MAX_POINTS = 180;

export class Ball {
constructor(scene) {
this.scene = scene;
this.model = undefined;
this.line = undefined;
this.linePoints = 0;
this.shadow = undefined;
this.boundingSphere = undefined;

this.x = 25;
this.y = 15;
this.z = 10;

this.dx = 0.3;
this.dy = 0;
this.dz = 0.1;
}

render() {
// ball
const ballGeometry = new THREE.SphereGeometry(0.5, 32, 16);
const ballMaterial = new THREE.MeshBasicMaterial({ color: 0xf6ff82 });
this.model = new THREE.Mesh(ballGeometry, ballMaterial);
this.model.position.set(this.x, this.y, this.z);
this.scene.add(this.model);

// shadow
const shadowGeometry = new THREE.CircleGeometry(0.8, 32);
const shadowMaterial = new THREE.MeshBasicMaterial({ color: 0xb1e9a8, side: THREE.DoubleSide });
this.shadow = new THREE.Mesh(shadowGeometry, shadowMaterial);
this.shadow.position.set(this.x, 0.015, this.z);
this.shadow.rotateX(Math.PI / 2);
this.scene.add(this.shadow);

// line
const lineGeometry = new THREE.BufferGeometry();
lineGeometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(MAX_POINTS * 3), 3));
const lineMaterial = new THREE.LineBasicMaterial({ color: 0xffffff });
this.line = new THREE.Line(lineGeometry, lineMaterial);
this.scene.add(this.line);

// collision detection
this.boundingSphere = new THREE.Sphere(this.model.position, 0.5);
}

update(net) {
if (!this.model || !net.boundingBox)
return;

// gravity
this.dy -= G;
if (this.y < 0.5) {
this.y = 0.5;
this.dy *= -0.95;
}

// temporary walls
if (Math.abs(this.x) > 32)
this.dx *= -1;
if (Math.abs(this.z) > 24)
this.dz *= -1;

// collision
if (this.boundingSphere.intersectsBox(net.boundingBox)) {
this.x = this.x > 0 ? 1 : -1;
this.dx *= -1;
}

// movement
this.x += this.dx;
this.y += this.dy;
this.z += this.dz;
this.model.position.set(this.x, this.y, this.z);
this.boundingSphere.set(this.model.position, 0.5);

// shadow
let shadowScale = (25 - this.y) / 25;
this.shadow.scale.set(shadowScale, shadowScale, shadowScale);
this.shadow.position.set(this.x, 0.015, this.z);

// path line
if (this.linePoints < MAX_POINTS) {
this.line.geometry.attributes.position.array[this.linePoints * 3] = this.x;
this.line.geometry.attributes.position.array[this.linePoints * 3 + 1] = this.y;
this.line.geometry.attributes.position.array[this.linePoints * 3 + 2] = this.z;
this.line.geometry.attributes.position.needsUpdate = true;
this.line.geometry.setDrawRange(0, this.linePoints);
this.linePoints++;
} else {
for (let i = 0; i < MAX_POINTS * 3; i++)
this.line.geometry.attributes.position.array[i] =
this.line.geometry.attributes.position.array[i + 3];

this.line.geometry.attributes.position.array[MAX_POINTS * 3 - 3] = this.x;
this.line.geometry.attributes.position.array[MAX_POINTS * 3 - 2] = this.y;
this.line.geometry.attributes.position.array[MAX_POINTS * 3 - 1] = this.z;
this.line.geometry.attributes.position.needsUpdate = true;
}
}
}
6 changes: 5 additions & 1 deletion src/Net.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ export class Net {
this.scene = scene;
this.loader = loader;
this.model = undefined;
this.boundingBox = undefined;
}

async render() {
this.model = await this.loader.loadAsync(netData)
.then(data => data.scene.children[0]);
this.model.position.set(0, 2.75, 0);

this.scene.add(this.model);

const min = new THREE.Vector3(-0.5, 0, -26);
const max = new THREE.Vector3(0.5, 4.75, 26);
this.boundingBox = new THREE.Box3(min, max);
}
}
7 changes: 4 additions & 3 deletions src/SceneManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ export class SceneManager {

// camera
this.camera = new THREE.OrthographicCamera(window.innerWidth / -32, window.innerWidth / 32,
window.innerHeight / 32, window.innerHeight / -32, -100, 100);
this.camera.position.set(10, 8, -12);
window.innerHeight / 32, window.innerHeight / -32, -64, 64);
this.camera.position.set(16, 12, -12);
this.camera.lookAt(0, 0, 0);

// renderer
this.renderer = new THREE.WebGLRenderer({ canvas: document.querySelector("#court") });
Expand All @@ -22,7 +23,7 @@ export class SceneManager {

// helpers
const gridHelper = new THREE.GridHelper(200, 50);
const axesHelper = new THREE.AxesHelper(5);
const axesHelper = new THREE.AxesHelper(15);
// this.scene.add(gridHelper, axesHelper);
}
}
1 change: 1 addition & 0 deletions src/court.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export class Court {
const leftMid = new THREE.Mesh(new THREE.PlaneGeometry(27, 16.25), fieldMaterial);
const rightMid = new THREE.Mesh(new THREE.PlaneGeometry(27, 16.25), fieldMaterial);

// magic numbers
leftEdge.position.set(0, 0.01, 20);
rightEdge.position.set(0, 0.01, -20);
bottomEdge.position.set(22.5, 0.01, 0);
Expand Down
17 changes: 15 additions & 2 deletions src/racket.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,21 @@ export class Racket {
this.model = undefined;

this.x = 0;
this.y = 3;
this.z = 0;

this.dx = 0;
this.dy = 0;
this.dz = 0;
}

async render() {
this.model = await this.loader.loadAsync(racketData)
.then(data => data.scene.children[0]);
this.model.position.set(this.x, 10, this.z);
this.model.position.set(this.x, this.y, this.z);
// this.model.rotateZ(Math.PI / 2);
// this.model.rotateY(Math.PI / 4);
// this.model.rotateX(-Math.PI / 4 * 3);

this.scene.add(this.model);
}
Expand All @@ -42,6 +48,13 @@ export class Racket {
// movement
this.x += this.dx;
this.z += this.dz;
this.model.position.set(this.x, 10, this.z);
this.model.position.set(this.x, this.y, this.z);

// if (this.model.rotation.x < Math.PI / 4)
// this.model.rotation.x += 0.01;
// if (this.model.rotation.y < Math.PI / 4)
// this.model.rotation.y += 0.01;
// if (this.model.rotation.z < Math.PI / 2)
// this.model.rotation.z += 0.01;
}
}

0 comments on commit 0dd9a64

Please sign in to comment.