見出し画像

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

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




いいなと思ったら応援しよう!

Rikuriku|フォロバ100 ⚠️警告⚠️ もしあなたがチップをくれたら、その分あなたのお金が減ります(?)。お金は大切に。