Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save ShaneBrumback/f21d6b15d295e8b07c55fb2ba7e472c2 to your computer and use it in GitHub Desktop.

Select an option

Save ShaneBrumback/f21d6b15d295e8b07c55fb2ba7e472c2 to your computer and use it in GitHub Desktop.
Three.js Examples Programming 3D Spheres With Collision Events
<!--////////////////////////////////////////////////////////////////////////////////////////
/// ///
/// Example Using Three.js Library, HTML, CSS & JavaScript ///
// 3D Interactive Web Apps & Games 2021-2024 ///
/// Contact Shane Brumback https://www.shanebrumback.com ///
/// Send a message if you have questions about this code ///
/// I am a freelance developer. I develop any and all web. ///
/// Apps Websites 3D 2D CMS Systems etc. Contact me anytime :) ///
/// ///
////////////////////////////////////////////////////////////////////////////////////////////-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Three.js Examples - Programming 3D Sphere Collision Events</title>
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/three@latest/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@latest/examples/js/controls/OrbitControls.js"></script>
<script type="module">
// Set up the scene, camera, and renderer
const scene = new THREE.Scene();
// Set up the camera
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 50;
camera.position.y = 25;
// Set up the renderer
let renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.toneMapping = THREE.ReinhardToneMapping;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.domElement.id = 'renderer';
renderer.setClearColor(0x000000, 1); // Set background color to black
renderer.domElement.style.position = 'fixed';
renderer.domElement.style.zIndex = '-1';
renderer.domElement.style.left = '0';
renderer.domElement.style.top = '0';
document.body.appendChild(renderer.domElement);
// Create the spotlight with shadows
const spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(10, 20, 30);
spotLight.castShadow = true;
scene.add(spotLight);
// Add ambient light
const ambientLight = new THREE.AmbientLight(0xffffff, 1);
scene.add(ambientLight);
// Create a spotlight
const spotlight = new THREE.SpotLight(0xffffff);
spotlight.position.set(200, 200, 0); // Adjust the position to cover the plane
spotlight.target.position.set(0, 0, 0);
spotlight.castShadow = true;
// Configure spotlight properties
spotlight.intensity = 10; // Adjust the intensity as needed
spotlight.angle = Math.PI / 2; // Set the spotlight cone angle to cover the entire plane
spotlight.penumbra = 0; // Set the spotlight softness to 0 for a sharp shadow
spotlight.decay = 2; // Set the spotlight decay
// Enable shadows for the spotlight
spotlight.castShadow = true;
spotlight.shadow.mapSize.width = 2048; // Increase the shadow map size
spotlight.shadow.mapSize.height = 2048; // Increase the shadow map size
spotlight.shadow.camera.near = 1; // Adjust the near value as needed
spotlight.shadow.camera.far = 500; // Adjust the far value as needed
spotlight.shadow.camera.fov = 120; // Adjust the field of view to cover the entire plane
// Add the spotlight to the scene
scene.add(spotlight);
scene.add(spotlight.target);
// Create the plane's geometry
const planeGeometry = new THREE.PlaneGeometry(100, 100);
// Create a material to apply to the plane
const planeMaterial = new THREE.MeshPhongMaterial({
color: 'blue',
side: THREE.DoubleSide,
depthWrite: false
});
// Create the plane
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = Math.PI / 2;
plane.position.y = -3;
plane.receiveShadow = true;
plane.castShadow = true;
// Enable shadows for the plane
plane.receiveShadow = true;
// Add the plane to the scene
scene.add(plane);
//Add a grid helper
var grid = new THREE.GridHelper(100, 30);
grid.renderOrder = 0;
grid.position.y = -3;
scene.add(grid);
// Set up the collision detection variables
const collisionDistance = 10;
// Set up the spheres
const ballGeometry = new THREE.SphereGeometry(5, 32, 32);
const ballMaterial = new THREE.MeshPhongMaterial({ color: 'white' });
const ballMaterial2 = new THREE.MeshPhongMaterial({ color: 'white' });
// Set shadow properties for the sphere materials
ballMaterial.receiveShadow = true;
ballMaterial.castShadow = true;
ballMaterial2.receiveShadow = true;
ballMaterial2.castShadow = true;
const ball1 = new THREE.Mesh(ballGeometry, ballMaterial);
ball1.name = 'ball1';
ball1.castShadow = true; // Enable shadows for ball1
const ball2 = new THREE.Mesh(ballGeometry, ballMaterial2);
ball2.name = 'ball2';
ball2.castShadow = true; // Enable shadows for ball2
// Set the initial positions of the spheres
ball1.position.set(-5, 1, 0);
ball2.position.set(5, 1, 0);
// Add the spheres to the scene
scene.add(ball1);
scene.add(ball2);
// Add camera controls
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.dampingFactor = 0.1; // Reduce camera damping for smoother movement
controls.autoRotate = true; // Make the camera rotate sinuously around the sphe
// Set up the movement variables
const ballSpeed = 2;
let ball1Direction = new THREE.Vector3(Math.random() * ballSpeed, 0, Math.random() * ballSpeed);
let ball2Direction = new THREE.Vector3(Math.random() * ballSpeed, 0, Math.random() * ballSpeed);
// Set up the update loop
function update() {
// Update the sphere positions
ball1.position.add(ball1Direction);
ball2.position.add(ball2Direction);
controls.update();
// Check if the spheres are colliding
const distance = ball1.position.distanceTo(ball2.position);
if (distance <= 10) {
// Generate random direction changes for both balls
const randomDirectionChange = () => Math.random() * 2 - 1; // Random value between -1 and 1
// Apply random direction changes to ball1
ball1Direction.x = -ball1Direction.x + randomDirectionChange();
ball1Direction.z = -ball1Direction.z + randomDirectionChange();
// Apply random direction changes to ball2
ball2Direction.x = -ball2Direction.x + randomDirectionChange();
ball2Direction.z = -ball2Direction.z + randomDirectionChange();
const colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffffff, 0x800080, 0xffa500]; // Red, Green, Blue, White, Purple, Orange
// Set random color for ball1
const randomColorIndex1 = Math.floor(Math.random() * colors.length);
const randomColor1 = colors[randomColorIndex1];
ball1.material.color.set(randomColor1);
// Remove the selected color from the array
colors.splice(randomColorIndex1, 1);
// Set random color for ball2
const randomColorIndex2 = Math.floor(Math.random() * colors.length);
const randomColor2 = colors[randomColorIndex2];
ball2.material.color.set(randomColor2);
}
// Check if the balls are close to the edge of the plane and change their direction if they are
const randomDirectionChange = () => Math.random() * 2 - 1; // Random value between -1 and 1
const edgeThreshold = collisionDistance; // Adjust this value as needed
if (Math.abs(ball1.position.x) > plane.geometry.parameters.width / 2 - edgeThreshold) {
ball1Direction.x = -Math.sign(ball1.position.x) + randomDirectionChange();
ball1Direction.normalize(); // Normalize the direction vector
}
if (Math.abs(ball2.position.x) > plane.geometry.parameters.width / 2 - edgeThreshold) {
ball2Direction.x = -Math.sign(ball2.position.x) + randomDirectionChange();
ball2Direction.normalize(); // Normalize the direction vector
}
if (Math.abs(ball1.position.z) > plane.geometry.parameters.height / 2 - edgeThreshold) {
ball1Direction.z = -Math.sign(ball1.position.z) + randomDirectionChange();
ball1Direction.normalize(); // Normalize the direction vector
}
if (Math.abs(ball2.position.z) > plane.geometry.parameters.height / 2 - edgeThreshold) {
ball2Direction.z = -Math.sign(ball2.position.z) + randomDirectionChange();
ball2Direction.normalize(); // Normalize the direction vector
}
// Render the scene
renderer.render(scene, camera);
// Update again on the next frame
requestAnimationFrame(update);
}
update();
// Add window resize event listener
window.addEventListener('resize', onWindowResize);
// Function to handle window resize event
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment