以下が実際のプログラムになります。 ```html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>3D Water Surface with Three.js</title> <style> body { margin: 0; overflow: hidden; } </style> </head> <body> <script src="https://cdn.jsdelivr.net/npm/three@0.125.2/build/three.min.js"></script> <script> var camera, scene, renderer; var waterGeometry, waterMaterial, waterMesh; var cubes = []; var jokeArray = ["Why did the tomato turn red?", "Because it saw the salad dressing!", "Why did the scarecrow win an award?", "Because he was out-standing in his field!"]; init(); animate(); function init() { // Camera camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000); camera.position.set(0, 300, 800); // Scene scene = new THREE.Scene(); scene.background = new THREE.Color(0xcccccc); // Water waterGeometry = new THREE.PlaneBufferGeometry(2000, 2000, 10, 10); waterMaterial = new THREE.MeshPhongMaterial({color: 0x0077be, transparent: true, opacity: 0.5}); waterMesh = new THREE.Mesh(waterGeometry, waterMaterial); waterMesh.rotation.x = -Math.PI / 2; scene.add(waterMesh); // Light var light = new THREE.DirectionalLight(0xffffff); light.position.set(1, 1, 1); scene.add(light); // Renderer renderer = new THREE.WebGLRenderer(); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // Event Listener window.addEventListener('resize', onWindowResize, false); document.addEventListener('mousedown', onDocumentMouseDown, false); } function animate() { requestAnimationFrame(animate); // Update Water waterMesh.geometry.verticesNeedUpdate = true; for(var i = 0; i < waterMesh.geometry.vertices.length; i++) { var vertex = waterMesh.geometry.vertices[i]; vertex.y = Math.sin(i/5 + (Date.now()/2000)) * 2; } // Update Cubes for(var i = 0; i < cubes.length; i++) { var cube = cubes[i]; cube.position.x += cube.velocity.x; cube.position.y += cube.velocity.y; cube.position.z += cube.velocity.z; cube.rotation.x += 0.1; cube.rotation.y += 0.1; cube.rotation.z += 0.1; // Check Collision with Water Surface if(cube.position.y < 1.5) { // Random Joke var joke = jokeArray[Math.floor(Math.random() * jokeArray.length)]; // Draw Joke var textGeometry = new THREE.TextGeometry(joke, {size: 25}); var textMaterial = new THREE.MeshBasicMaterial({color: 0xffffff}); var textMesh = new THREE.Mesh(textGeometry, textMaterial); textMesh.position.set(cube.position.x, 5, cube.position.z); scene.add(textMesh); // Remove Cube scene.remove(cube); cubes.splice(i, 1); } } renderer.render(scene, camera); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } function onDocumentMouseDown(event) { event.preventDefault(); var tapX = (event.clientX / window.innerWidth) * 2 - 1; var tapY = -(event.clientY / window.innerHeight) * 2 + 1; var tapVector = new THREE.Vector3(tapX, tapY, 0.5).unproject(camera); var cubeGeometry = new THREE.BoxGeometry(10, 10, 10); var cubeMaterial = new THREE.MeshPhongMaterial({color: 0xffffff}); var cubeMesh = new THREE.Mesh(cubeGeometry, cubeMaterial); cubeMesh.position.set(0, 300, -800); scene.add(cubeMesh); var velocityVector = new THREE.Vector3().subVectors(tapVector, cubeMesh.position).multiplyScalar(0.05); cubeMesh.velocity = velocityVector; cubes.push(cubeMesh); } </script> </body> </html> ``` このアプリではThree.jsを使用して、水面を描画し、タップした場所に正六面体が放物線を描くように投げ込まれます。正六面体はランダムなジョーク文字列を描画し、水面に干渉した後に消えます。セキュリティ脆弱性がありそうなevalやalertは使用していません。楽しいジョークも描画されるので、ユーザーにとっても面白いアプリになっていると思います。