メインコンテンツへスキップ
見出し画像

3 D で 五 目 並 べ を 作 る ②

    前回作ったファイルは壊れました。(なんでだよ)

    ともかく、AIを駆使して

    3D五目並べ

    を作ります。

    コード書くのってマジで大変


    画像
    正二十面体


    画像
    画面を右クリックで〇を表示


    画像
    別の面を右クリックで今度は*を表示


    画像
    別の面を右クリックで今度は〇を表示

    後は勝利条件を追加するだけ

    <!DOCTYPE html>
    <html lang="ja">
    
    <head>
        <meta charset="UTF-8">
        <title>Icosahedron</title>
        <style>
            body {
                margin: 0;
                overflow: hidden;
                background-color: #333;
            }
    
            canvas {
                display: block;
            }
        </style>
    </head>
    
    <body>
        <div id="info"
            style="position: absolute; top: 10px; left: 10px; color: white; font-family: sans-serif; pointer-events: none;">
            Right-click on the shape to detect face
        </div>
        <script type="module">
            import * as THREE from 'https://unpkg.com/three@0.160.0/build/three.module.js';
    
            try {
                // Scene setup
                const scene = new THREE.Scene();
                scene.background = new THREE.Color(0xf0f0f0); // 単色背景
    
                // Camera setup
                const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
                camera.position.z = 3;
    
                // Renderer setup
                const renderer = new THREE.WebGLRenderer({ antialias: true });
                renderer.setSize(window.innerWidth, window.innerHeight);
                document.body.appendChild(renderer.domElement);
    
                // Icosahedron creation
                const geometry = new THREE.IcosahedronGeometry(1.2, 0);
    
                // Material: 単色, エフェクトなし (FlatShadingで立体感は出す)
                const material = new THREE.MeshLambertMaterial({
                    color: 0x44ddff, // 鮮やかな青
                    flatShading: true
                });
    
                const mesh = new THREE.Mesh(geometry, material);
                scene.add(mesh);
    
                // Lighting (to show 3D shape with flat shading)
                const light = new THREE.DirectionalLight(0xffffff, 1);
                light.position.set(1, 1, 1).normalize();
                scene.add(light);
    
                const ambientLight = new THREE.AmbientLight(0x999999);
                scene.add(ambientLight);
    
                // Markings Setup
                const faceMarks = []; // Store { circle: Mesh, asterisk: Group } for each face index
    
                // Helper: Iterate over faces to place marks
                // Scene setup
                // This section appears to be a duplicate and will be removed to avoid re-declaration errors.
                // const scene = new THREE.Scene();
                // scene.background = new THREE.Color(0x333333);
    
                // Camera setup
                // const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
                // camera.position.z = 3;
    
                // Renderer setup
                // const renderer = new THREE.WebGLRenderer({ antialias: true });
                // renderer.setSize(window.innerWidth, window.innerHeight);
                // document.body.appendChild(renderer.domElement);
    
                // Icosahedron creation
                // const geometry = new THREE.IcosahedronGeometry(1.2, 0);
    
                // Material
                // const material = new THREE.MeshLambertMaterial({
                //     color: 0x00aaff,
                //     flatShading: true
                // });
    
                // const mesh = new THREE.Mesh(geometry, material);
                // scene.add(mesh);
    
                // Lighting
                // const light = new THREE.DirectionalLight(0xffffff, 1);
                // light.position.set(1, 1, 1).normalize();
                // scene.add(light);
    
                // const ambientLight = new THREE.AmbientLight(0x444444);
                // scene.add(ambientLight);
    
                // Markings Setup
                // const faceMarks = []; // Store { circle: Mesh, asterisk: Group } for each face index
    
                // Helper to get vertex by index or position
                function getVertex(geo, index) {
                    const pos = geo.getAttribute('position');
                    if (geo.index) {
                        const i = geo.index.getX(index);
                        return new THREE.Vector3().fromBufferAttribute(pos, i);
                    } else {
                        return new THREE.Vector3().fromBufferAttribute(pos, index);
                    }
                }
    
                const positionAttribute = geometry.getAttribute('position');
                const indexAttribute = geometry.index;
                const count = indexAttribute ? indexAttribute.count : positionAttribute.count;
    
                // Circle Geometry (Red Torus)
                const circleGeo = new THREE.TorusGeometry(0.2, 0.05, 8, 16);
                const circleMat = new THREE.MeshBasicMaterial({ color: 0xff0000 });
    
                // Asterisk Geometry (Black Group of Boxes)
                const stickGeo = new THREE.BoxGeometry(0.05, 0.5, 0.05);
                const stickMat = new THREE.MeshBasicMaterial({ color: 0x000000 });
    
                for (let i = 0; i < count; i += 3) {
                    const vA = getVertex(geometry, i);
                    const vB = getVertex(geometry, i + 1);
                    const vC = getVertex(geometry, i + 2);
    
                    // Calculate center
                    const center = new THREE.Vector3().addVectors(vA, vB).add(vC).divideScalar(3);
    
                    // Calculate normal
                    const normal = new THREE.Vector3().crossVectors(
                        new THREE.Vector3().subVectors(vB, vA),
                        new THREE.Vector3().subVectors(vC, vA)
                    ).normalize();
    
                    // Create Circle (A state mark)
                    const circle = new THREE.Mesh(circleGeo, circleMat);
                    circle.position.copy(center).add(normal.clone().multiplyScalar(0.01)); // Slight offset
                    circle.lookAt(center.clone().add(normal));
                    circle.visible = false;
                    mesh.add(circle);
    
                    // Create Asterisk (B state mark)
                    const asteriskGroup = new THREE.Group();
    
                    const stick1 = new THREE.Mesh(stickGeo, stickMat);
                    const stick2 = new THREE.Mesh(stickGeo, stickMat);
                    const stick3 = new THREE.Mesh(stickGeo, stickMat);
    
                    stick2.rotation.z = Math.PI / 3; // 60 deg
                    stick3.rotation.z = -Math.PI / 3; // -60 deg
    
                    asteriskGroup.add(stick1, stick2, stick3);
                    asteriskGroup.position.copy(center).add(normal.clone().multiplyScalar(0.01));
                    asteriskGroup.lookAt(center.clone().add(normal));
                    asteriskGroup.visible = false;
                    mesh.add(asteriskGroup);
    
                    // Store references
                    // Note: If non-indexed, i/3 maps correctly to faceIndex if raycaster uses triangle index
                    faceMarks.push({ circle, asterisk: asteriskGroup });
                }
    
    
                // Raycaster definition
                const raycaster = new THREE.Raycaster();
                const mouse = new THREE.Vector2();
                const infoDiv = document.getElementById('info');
    
                // Interaction logic
                let isDragging = false;
                let previousMousePosition = { x: 0, y: 0 };
    
                renderer.domElement.addEventListener('mousedown', (e) => {
                    if (e.button === 0) { // 左クリックのみ
                        isDragging = true;
                        previousMousePosition = { x: e.clientX, y: e.clientY };
                    }
                });
    
                // Global state
                let currentState = 'A';
    
                // Right-click face detection, mark placement, and state toggle
                renderer.domElement.addEventListener('contextmenu', (e) => {
                    e.preventDefault();
    
                    mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
                    mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
    
                    raycaster.setFromCamera(mouse, camera);
                    const intersects = raycaster.intersectObject(mesh);
    
                    if (intersects.length > 0) {
                        const faceIndex = intersects[0].faceIndex;
    
                        if (faceMarks[faceIndex]) {
                            if (currentState === 'A') {
                                // State A: Show Circle, Hide Asterisk
                                faceMarks[faceIndex].circle.visible = true;
                                faceMarks[faceIndex].asterisk.visible = false;
                            } else {
                                // State B: Show Asterisk, Hide Circle
                                faceMarks[faceIndex].asterisk.visible = true;
                                faceMarks[faceIndex].circle.visible = false;
                            }
                        }
    
                        // Toggle State
                        currentState = (currentState === 'A') ? 'B' : 'A';
    
                        infoDiv.innerText = `Detected Face Index: ${faceIndex} | State: ${currentState}`;
                    } else {
                        infoDiv.innerText = `No face detected | State: ${currentState}`;
                    }
                });
    
                const onStopDrag = () => {
                    isDragging = false;
                };
                window.addEventListener('mouseup', onStopDrag);
                window.addEventListener('mouseleave', onStopDrag);
    
                renderer.domElement.addEventListener('mousemove', (e) => {
                    if (isDragging) {
                        const deltaMove = {
                            x: e.clientX - previousMousePosition.x,
                            y: e.clientY - previousMousePosition.y
                        };
    
                        const rotateSpeed = 0.005;
                        const xAxis = new THREE.Vector3(1, 0, 0);
                        const yAxis = new THREE.Vector3(0, 1, 0);
    
                        mesh.rotateOnWorldAxis(xAxis, deltaMove.y * rotateSpeed);
                        mesh.rotateOnWorldAxis(yAxis, deltaMove.x * rotateSpeed);
    
                        previousMousePosition = { x: e.clientX, y: e.clientY };
                    }
                });
    
                // Resize handling
                window.addEventListener('resize', () => {
                    camera.aspect = window.innerWidth / window.innerHeight;
                    camera.updateProjectionMatrix();
                    renderer.setSize(window.innerWidth, window.innerHeight);
                });
    
                // Animation loop
                function animate() {
                    requestAnimationFrame(animate);
                    renderer.render(scene, camera);
                }
                animate();
    
                // Expose for debugging/analysis
                window.THREE = THREE;
                window.mesh = mesh;
    
            } catch (error) {
                console.error("Error during init:", error);
                // Don't replace body content so the analysis script can still try to run
                // document.body.innerHTML = "<div style='color:red; background:black; padding:20px;'><h1>Error</h1>" + error.toString() + "<br>" + error.stack + "</div>";
            }
        </script>
    </body>
    
    </html>

    Created with assistance from AI

    まだ作成途中なので、あまり期待しないで💦。




     
     
     
    フォロバ100%です。暇な時間を折り紙でつぶす人。たまに自分でも何を言っているのか分からなくなる。でもマイクラもフォトナもやる。(?)実は折り紙より得意。 …………………………

    あなたへのおすすめ