diff --git a/README.md b/README.md index a5e93fe..081888c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,14 @@ # XO-Game-Three.js + + + + + + + + + ## Introduction Do you remember playing tic-tac-toe (X and O) when you were bored growing up? Well, imagine taking that classic game to a whole new level of fun with Three.js! This project implements the X and O game in JavaScript using Three.js, adding a 3D experience, physics, and even some music. Get ready to enjoy a modern twist on a timeless game! diff --git a/game.js b/game.js index 8a04b23..e69de29 100644 --- a/game.js +++ b/game.js @@ -1,7 +0,0 @@ -//to do -//to make physical linking points -//if all linking points are same text 'x' or 'o'; -//turn the sqares black and do some animation with the text? -//for each there is one points.lol -//for now thats it but for the final game i want to make even a computer.**) - diff --git a/grids.js b/grids.js index 32ca208..0e284f4 100644 --- a/grids.js +++ b/grids.js @@ -1,7 +1,18 @@ let selectedColorPalette; let color; +const colorMap = { + "Celestial Crimson": [0x811453, 0xbcd4e6, 0xfff5ee, 0x333333], + "Starlight Silver": [0x414e6d, 0x788995, 0xf5f5f5, 0x2c3e50], + "Galactic Teal": [0x034752, 0x3a5f6e, 0xe0f2f1, 0x1abc9c], + "Nebula Purple": [0x490092, 0x835a9b, 0xe6e6fa, 0x8e44ad], + "Cosmic Blue": [0x00171f, 0x16343a, 0xc6e2ff, 0x3498db], + "original": [0x800000, 0xd3d3d3, 0xfff5ee, 0x333333] +}; + -function hexToRgb(hex) { + + + function hexToRgb(hex) { return { r: ((hex >> 16) & 255) / 255, g: ((hex >> 8) & 255) / 255, @@ -9,14 +20,8 @@ function hexToRgb(hex) { }; } -const colorMap = { - "Celestial Crimson": [0x811453, 0xbcd4e6, 0xfff5ee, 0x333333], - "Starlight Silver": [0x414e6d, 0x788995, 0xf5f5f5, 0x2c3e50], - "Galactic Teal": [0x034752, 0x3a5f6e, 0xe0f2f1, 0x1abc9c], - "Nebula Purple": [0x490092, 0x835a9b, 0xe6e6fa, 0x8e44ad], - "Cosmic Blue": [0x00171f, 0x16343a, 0xc6e2ff, 0x3498db], - "original": [0xB22222, 0xd3d3d3, 0xfff5ee, 0x333333] -}; + + function setColorFromPalette() { const selectedPalette = document.getElementById('colorSelect'); @@ -74,16 +79,6 @@ function loadThemeSelection() { - - - - - - - - - - let lineMaterial = new THREE.LineBasicMaterial({ linewidth: 30, }); diff --git a/htmlcube.js b/htmlcube.js index aa9b0b9..417da35 100644 --- a/htmlcube.js +++ b/htmlcube.js @@ -2,7 +2,7 @@ const scene = new THREE.Scene(); const width = window.innerWidth; const height = window.innerHeight; const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000); -camera.position.z = -6; +camera.position.z = 6; const renderer = new THREE.WebGLRenderer(); @@ -55,8 +55,6 @@ window.addEventListener('resize', function () { function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); - xno(); - updateFragments() } animate(); diff --git a/pics of x and o/2.PNG b/pics of x and o/2.PNG new file mode 100644 index 0000000..814411c Binary files /dev/null and b/pics of x and o/2.PNG differ diff --git a/pics of x and o/3.PNG b/pics of x and o/3.PNG new file mode 100644 index 0000000..d901dba Binary files /dev/null and b/pics of x and o/3.PNG differ diff --git a/pics of x and o/Capture.PNG b/pics of x and o/Capture.PNG new file mode 100644 index 0000000..317c126 Binary files /dev/null and b/pics of x and o/Capture.PNG differ diff --git a/pics of x and o/Screenshot 2024-04-08 205410.jpg b/pics of x and o/Screenshot 2024-04-08 205410.jpg new file mode 100644 index 0000000..02e761a Binary files /dev/null and b/pics of x and o/Screenshot 2024-04-08 205410.jpg differ diff --git a/pics of x and o/Screenshot 2024-04-08 205547.jpg b/pics of x and o/Screenshot 2024-04-08 205547.jpg new file mode 100644 index 0000000..f766b11 Binary files /dev/null and b/pics of x and o/Screenshot 2024-04-08 205547.jpg differ diff --git a/pics of x and o/Screenshot 2024-04-08 205604.jpg b/pics of x and o/Screenshot 2024-04-08 205604.jpg new file mode 100644 index 0000000..04bd67e Binary files /dev/null and b/pics of x and o/Screenshot 2024-04-08 205604.jpg differ diff --git a/pics of x and o/setings.PNG b/pics of x and o/setings.PNG new file mode 100644 index 0000000..cac09cb Binary files /dev/null and b/pics of x and o/setings.PNG differ diff --git a/settings.js b/settings.js index 9128d31..b60a2ef 100644 --- a/settings.js +++ b/settings.js @@ -1,16 +1,21 @@ -let isSettingsOpen = false; const settingsButton = document.getElementById("settingsButton"); const modal = document.getElementById("settingsModal"); const closeButton = document.querySelector(".close"); +const volumeInput = document.getElementById("volume"); +let isSettingsOpen = false; + + settingsButton.addEventListener("click", toggleSettings); closeButton.addEventListener("click", closeSettings); document.addEventListener("keydown", handleKeyPress); +volumeInput.addEventListener("input", updateVolume); function toggleSettings() { isSettingsOpen = !isSettingsOpen; if (isSettingsOpen) { openSettings(); + stopgame(); } else { closeSettings(); applySettings(); @@ -19,11 +24,16 @@ function toggleSettings() { function openSettings() { modal.style.display = "block"; + const overlay = document.getElementById("overlay"); + overlay.style.display = "block"; + stopgame(); } function closeSettings() { isSettingsOpen = false; modal.style.display = "none"; + const overlay = document.getElementById("overlay"); + overlay.style.display = "none"; } function handleKeyPress(event) { @@ -32,3 +42,7 @@ function handleKeyPress(event) { applySettings(); } } + +function updateVolume() { + const volume = volumeInput.value / 100; + sound.setVolume(volume);} diff --git a/space-ambience-56265.mp3 b/space-ambience-56265.mp3 new file mode 100644 index 0000000..f09e7c2 Binary files /dev/null and b/space-ambience-56265.mp3 differ diff --git a/sqaureobject.js b/sqaureobject.js index 2e3a48d..ef16e7c 100644 --- a/sqaureobject.js +++ b/sqaureobject.js @@ -1,16 +1,27 @@ + const colorMap = { + "Celestial Crimson": [0x811453, 0xbcd4e6, 0xfff5ee, 0x333333], + "Starlight Silver": [0x414e6d, 0x788995, 0xf5f5f5, 0x2c3e50], + "Galactic Teal": [0x034752, 0x3a5f6e, 0xe0f2f1, 0x1abc9c], + "Nebula Purple": [0x490092, 0x835a9b, 0xe6e6fa, 0x8e44ad], + "Cosmic Blue": [0x00171f, 0x16343a, 0xc6e2ff, 0x3498db], + "original": [0x800000, 0xd3d3d3, 0xfff5ee, 0x333333] +}; + + + function hexToRgb(hex) { + return { + r: ((hex >> 16) & 255) / 255, + g: ((hex >> 8) & 255) / 255, + b: (hex & 255) / 255 + }; +} + let selectedColorPalette; let color; let squares = []; let squareMaterial = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide }); -function hexToRgb(hex) { - return { - r: ((hex >> 16) & 255) / 255, - g: ((hex >> 8) & 255) / 255, - b: (hex & 255) / 255 - }; -} function saveThemeSelection(theme) { localStorage.setItem('selectedTheme', theme); @@ -20,14 +31,7 @@ function loadThemeSelection() { return localStorage.getItem('selectedTheme'); } -const colorMap = { - "Celestial Crimson": [0x811453, 0xbcd4e6, 0xfff5ee, 0x333333], - "Starlight Silver": [0x414e6d, 0x788995, 0xf5f5f5, 0x2c3e50], - "Galactic Teal": [0x034752, 0x3a5f6e, 0xe0f2f1, 0x1abc9c], - "Nebula Purple": [0x490092, 0x835a9b, 0xe6e6fa, 0x8e44ad], - "Cosmic Blue": [0x00171f, 0x16343a, 0xc6e2ff, 0x3498db], - "original": [0xB22222, 0xd3d3d3, 0xfff5ee, 0x333333] -}; + function setColorFromPalette() { const selectedPalette = document.getElementById('colorSelect'); @@ -132,11 +136,11 @@ function create_squares() { new THREE.Vector3(0.76, -1.16, 0.76) // square54 ]; - const squarenumb = squarePositions.length; // Get the total number of squares + const squarenumb = squarePositions.length; for (let i = 1; i <= squarenumb; i++) { const square = new THREE.Mesh(squareGeometry, squareMaterial); - square.position.copy(squarePositions[i - 1]); // Set the position of the square - square.name = "square" + i; // Assign a name to the square + square.position.copy(squarePositions[i - 1]); + square.name = "square" + i; scene.add(square); squares.push(square); } diff --git a/startUI.js b/startUI.js index 8ec327a..c16e9b6 100644 --- a/startUI.js +++ b/startUI.js @@ -1,45 +1,51 @@ -let lay=[]; -let layP=[]; - -const windowGeometry = new THREE.BoxGeometry(5,5,0); - const windowMaterial = new THREE.MeshBasicMaterial({ color: 0x808080, transparent: true, opacity: 0.8}); - const windowMesh = new THREE.Mesh(windowGeometry, windowMaterial,2); - windowMesh.position.set(0, 0, 2); - windowMesh.name='windowMeshstart'; - lay.push(windowMesh); - - -//scene.add(windowMesh); -//addTextToScene(scene, 'Play', new THREE.Vector3(-0.422, 0, 2), fontPath, 0xFF0000); -//addTextToScene(scene, 'X', new THREE.Vector3(-0.7, 1.72 , 2 ), fontPath, false); -//addTextToScene(scene, 'O', new THREE.Vector3(0.5 , 1.72, 2 ), fontPath, false); -//addTextToScene(scene, '&', new THREE.Vector3(-0.1 , 1.7 , 2), fontPath, true); - - - - - function addTextToScene(scene, textValue, position, fontPath,col) { - const loader = new THREE.FontLoader(); - loader.load(fontPath, function (loadedFont) { - const matLite = new THREE.MeshBasicMaterial({ - transparent: false, - opacity: 1, - side: THREE.DoubleSide - }); - - const geometry = new THREE.TextGeometry(textValue, { - font: loadedFont, - size: 0.38, - height: 0.1 - }); - - if (textValue === 'Play'||textValue=='Player1 is X'||textValue=='Player2 is O') { +let lay = []; +let layP = []; +export var game; + +function startGame() { + const delay = 1000; + setTimeout(() => { + game = true; + + }, delay); +} + +const windowGeometry = new THREE.BoxGeometry(5, 5, 0); +const windowMaterial = new THREE.MeshBasicMaterial({ color: 0x808080, transparent: true, opacity: 0.8 }); +const windowMesh = new THREE.Mesh(windowGeometry, windowMaterial, 2); +windowMesh.position.set(0, 0, 2); +windowMesh.name = 'windowMeshstart'; +lay.push(windowMesh); + +let fontPath = './helvetiker_bold.typeface.json'; +scene.add(windowMesh); +addTextToScene(scene, 'Play', new THREE.Vector3(-0.422, 0, 2), fontPath, 0xFF0000); +addTextToScene(scene, 'X', new THREE.Vector3(-0.7, 1.72, 2), fontPath, false); +addTextToScene(scene, 'O', new THREE.Vector3(0.5, 1.72, 2), fontPath, false); +addTextToScene(scene, '&', new THREE.Vector3(-0.1, 1.7, 2), fontPath, false); + +function addTextToScene(scene, textValue, position, fontPath, col) { + const loader = new THREE.FontLoader(); + loader.load(fontPath, function (loadedFont) { + const matLite = new THREE.MeshBasicMaterial({ + transparent: false, + opacity: 1, + side: THREE.DoubleSide + }); + + const geometry = new THREE.TextGeometry(textValue, { + font: loadedFont, + size: 0.38, + height: 0.1 + }); + + if (textValue === 'Play' || textValue == 'Player1 is X' || textValue == 'Player2 is O') { const play = new THREE.Mesh(geometry, matLite); lay.push(play); play.position.copy(position); scene.add(play); play.name = 'play'; - + } else if (textValue === 'X') { const X = new THREE.Mesh(geometry, matLite); X.position.copy(position); @@ -50,7 +56,7 @@ const windowGeometry = new THREE.BoxGeometry(5,5,0); const originalColor = new THREE.Color(0x006699); animateColorChange(X, originalColor); } - + } else if (textValue === 'O') { const O = new THREE.Mesh(geometry, matLite); O.position.copy(position); @@ -68,125 +74,80 @@ const windowGeometry = new THREE.BoxGeometry(5,5,0); mesh.name = '&'; lay.push(mesh); } - - - - - - - - - }); + }); } - function animateColorChange(mesh, originalColor) { - const speed = 0.4; + const speed = 0.4; function updateColor() { const time = performance.now() * 0.001; const value = Math.sin(time * speed); mesh.material.color.set(originalColor.clone().lerp(new THREE.Color(0xFF0000), value)); requestAnimationFrame(updateColor); } - - updateColor(); - - - } - - -const fontPath = './helvetiker_bold.typeface.json'; - - - - - -function onClick(event) { +function onclick(event) { const mouse = new THREE.Vector2(); const raycaster = new THREE.Raycaster(); mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = - (event.clientY / window.innerHeight) * 2 + 1; raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObjects(lay); - if (intersects.length > 0) { - - const clickedObject = intersects[0].object; - - + const playText = scene.getObjectByName('play'); if (clickedObject.name === 'play') { - - xno() - destroymesh(); - breakMesh(clickedObject); + xno(); + xnx(); + scene.remove(windowMesh); + scene.remove(playText); + startGame(); + destroymesh(); setTimeout(() => { Next_Action(); - }, 9000); - - - + }, 2000); + } - - + } } +document.addEventListener('click', onclick); + -} function destroymesh() { let i = 0; while (i < lay.length) { const mesh = lay[i]; - - - // Check if the mesh is not undefined if (mesh) { - // Remove the mesh from the scene scene.remove(mesh); - - // Dispose of the mesh to free up resources mesh.geometry.dispose(); mesh.material.dispose(); - - // Remove the mesh from the array - - lay.splice(i, 1); + lay.splice(i, 1); + layP.splice(i,1); } else { - // Increment i if the mesh is undefined i++; } } - } -function updateFragments() { - +//FUNCTION NOT USED BUT IT IS A GOOD ONE TO UPDATE THE FRAGMENTS....CAN REMOVE THIS ONE IF NEEDED +function updateFragments() { for (let i = fragments.length - 1; i >= 0; i--) { const fragment = fragments[i]; - - fragment.position.add(fragment.userData.velocity.clone().multiplyScalar(9)); - - // Remove fragments that go below the ground - if (fragment.position.y <-10) { - // Remove the fragment from the scene + if (fragment.position.y < -10) { scene.remove(fragment); - - // Dispose of the fragment's geometry and material fragment.geometry.dispose(); fragment.material.dispose(); - - // Remove the fragment from the array fragments.splice(i, 1); + } } - } let createdObjects = []; @@ -196,96 +157,84 @@ function xnx() { const interval = setInterval(() => { if (index <= 22) { const a = index; - const xPosition = -0.7+ a * Math.random(-10); + const xPosition = -0.7 + a * Math.random(-10); const yPosition = 1.72 + a * Math.random(-10); const zPosition = 2 - a / Math.random(1.2); - const objectX = addTextToScene(scene, 'X', new THREE.Vector3(xPosition, yPosition, zPosition), fontPath, false); const objectO = addTextToScene(scene, 'O', new THREE.Vector3(0.5, yPosition, zPosition), fontPath, false); const objectAmp = addTextToScene(scene, '&', new THREE.Vector3(-0.1, 1.7 + a * Math.sin(-10), zPosition), fontPath, true); - - // Store the created objects in the array createdObjects.push(objectX, objectO, objectAmp); index++; } else { - clearInterval(interval); // Stop the interval when all objects are created - // Set a timeout to delete the objects after a certain duration (in milliseconds) + clearInterval(interval); setTimeout(() => { removeTextGradually(scene); - }, 10); // Adjust the duration as needed + fragments.splice(0); + scene.remove(fragments); + }, 10); } - }, 1); // Adjust the interval duration as needed + }, 1); } - - - - - - - function removeTextGradually(scene) { - const delay = 200; + const delay = 0; lay.forEach((object, index) => { setTimeout(() => { scene.remove(object); + lay.splice(index, 1); }, index * delay); }); } - - - - - - - -const fragments = []; +let fragments = []; function breakMesh(mesh) { - const numFragments = 13; + const numFragments = 10; + const fragmentLifetime = 100; + for (let i = 0; i < numFragments; i++) { const fragment = new THREE.Mesh(mesh.geometry, mesh.material.clone()); fragment.position.copy(mesh.position); - fragment.rotation.set(Math.random()-1 * Math.PI, Math.random() -1* Math.PI, Math.random()-1 * Math.PI); + fragment.rotation.set(Math.random() * Math.PI, Math.random() * Math.PI, Math.random() * Math.PI); fragment.scale.set(Math.random() + 0.2, Math.random() + 0.2, Math.random() + 0.2); scene.add(fragment); - fragments.push(fragment); + fragments.push(fragment); + const speed = 0.2; const direction = new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5); direction.normalize(); fragment.userData = { velocity: direction.multiplyScalar(speed) }; + + setTimeout(() => { + scene.remove(fragment); + fragments.splice(fragments.indexOf(fragment), 1); + }, fragmentLifetime); } - - // Remove the original mesh scene.remove(mesh); - - } -function xno(){ + +function xno() { const xPosition = -10; const yPosition = 15; const zPosition = -16; - const initialRotation = new THREE.Euler(Math.PI/9, 0, Math.PI/12); - + const initialRotation = new THREE.Euler(Math.PI / 9, 0, Math.PI / 12); const objectX = addTextToScene(scene, 'X', new THREE.Vector3(xPosition, yPosition, zPosition), fontPath, false, initialRotation); const objectO = addTextToScene(scene, 'O', new THREE.Vector3(5.0, yPosition, zPosition), fontPath, false, initialRotation); const objectAmp = addTextToScene(scene, '&', new THREE.Vector3(-1.5, yPosition, zPosition), fontPath, true, initialRotation); } - - function Next_Action() { const fontPath3 = 'Press Start 2P_Regular.json'; const player1Text = addTextToScene(scene, 'Player1 is X', new THREE.Vector3(-4, 5, -5), fontPath3, 0xFF0000); - const player2Text = addTextToScene(scene, 'Player2 is O' , new THREE.Vector3(2.5, 3.7, -5), fontPath3, 0xFF0000); + const player2Text = addTextToScene(scene, 'Player2 is O', new THREE.Vector3(2.5, 3.7, -5), fontPath3, 0xFF0000); } + diff --git a/styles.css b/styles.css index 78673e2..4a683fc 100644 --- a/styles.css +++ b/styles.css @@ -34,30 +34,30 @@ body { -/* Modal container */ + .modal { - display: none; /* Hidden by default */ - position: fixed; /* Stay in place */ - z-index: 1; /* Sit on top */ + display: none; + position: fixed; + z-index: 1; left: 0; top: 0; - width: 100%; /* Full width */ - height: 100%; /* Full height */ - overflow: auto; /* Enable scroll if needed */ - background-color: rgb(0,0,0); /* Fallback color */ - background-color: rgba(0,0,0,0.4); /* Black w/ opacity */ + width: 100%; + height: 100%; + overflow: auto; + background-color: rgb(0,0,0); + background-color: rgba(0,0,0,0.4); } -/* Modal content */ + .modal-content { background-color: #fefefe; - margin: 15% auto; /* 15% from the top and centered */ + margin: 15% auto; padding: 20px; border: 1px solid #888; - width: 80%; /* Could be more or less, depending on screen size */ + width: 80%; } -/* Close button */ + .close { color: #aaa; float: right; diff --git a/three.html b/three.html index a243e87..1d4b66c 100644 --- a/three.html +++ b/three.html @@ -28,6 +28,10 @@