🔬 AI発明ギャラリー syunsuke デストロイヤー
🔒 サンドボックス内で実行中 ⛶ 全画面で遊ぶ
HTML / CSS / JS
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>デストロイア 3Dモデル (Three.js)</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            overflow: hidden;
            background-color: #050000;
            font-family: sans-serif;
        }
        #canvas-container {
            width: 100vw;
            height: 100vh;
            display: block;
        }
        #loading {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: #ff3333;
            font-size: 24px;
            font-weight: bold;
            text-shadow: 0 0 10px #ff0000;
            pointer-events: none;
            transition: opacity 0.5s;
            z-index: 10;
        }
        #ui {
            position: absolute;
            bottom: 20px;
            left: 20px;
            color: rgba(255, 255, 255, 0.7);
            text-shadow: 1px 1px 2px black;
            z-index: 10;
        }
        #ui h2 { margin: 0 0 5px 0; font-size: 20px; }
        #ui p { margin: 0 0 10px 0; font-size: 12px; }
        #form-selector {
            background: rgba(20, 0, 0, 0.8);
            color: #ffaa55;
            border: 1px solid #ff3333;
            padding: 8px 12px;
            font-size: 16px;
            border-radius: 4px;
            outline: none;
            cursor: pointer;
            box-shadow: 0 0 10px rgba(255, 0, 0, 0.3);
        }
        #form-selector:hover { background: rgba(40, 0, 0, 0.9); }
    </style>
</head>
<body>
    <div id="loading">Generating Monster...</div>
    <div id="canvas-container"></div>
    <div id="ui">
        <h2>デストロイア (進化系統)</h2>
        <p>ドラッグ:回転 / スクロール:縮尺</p>
        <select id="form-selector" onchange="changeForm(this.value)">
            <option value="perfect">完全体 (Perfect Form)</option>
            <option value="flying">飛行体 (Flying Form)</option>
            <option value="aggregate">集合体 (Aggregate Form)</option>
            <option value="micro">幼体/微小体 (Micro Form)</option>
        </select>
    </div>

    <!-- Three.jsとOrbitControlsの読み込み -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>

    <script>
        window.addEventListener('DOMContentLoaded', () => {
            // --- 1. 基本設定 ---
            const container = document.getElementById('canvas-container');
            const scene = new THREE.Scene();
            scene.fog = new THREE.FogExp2(0x0a0000, 0.012);

            const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
            camera.position.set(0, 15, 45);

            const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.shadowMap.enabled = true;
            renderer.shadowMap.type = THREE.PCFSoftShadowMap;
            container.appendChild(renderer.domElement);

            const controls = new THREE.OrbitControls(camera, renderer.domElement);
            controls.enableDamping = true;
            controls.dampingFactor = 0.05;
            controls.target.set(0, 5, 0);
            controls.maxPolarAngle = Math.PI / 2 + 0.1;

            // --- 2. ライティング ---
            scene.add(new THREE.AmbientLight(0x402020, 1.2));
            const dirLight = new THREE.DirectionalLight(0xffaaaa, 1.5);
            dirLight.position.set(15, 25, 10);
            dirLight.castShadow = true;
            dirLight.shadow.camera.top = 25; dirLight.shadow.camera.bottom = -25;
            dirLight.shadow.camera.left = -25; dirLight.shadow.camera.right = 25;
            scene.add(dirLight);
            const pointLight = new THREE.PointLight(0xff3300, 2.5, 60); 
            pointLight.position.set(0, -5, 10);
            scene.add(pointLight);

            // --- 3. マテリアル共通定義 ---
            const colors = {
                darkRed: 0x2a0505, armorRed: 0x4a0a0a, wingMembrane: 0xaa2222,
                bellyBone: 0xd2b48c, boneColor: 0xeeddcc, eyeYellow: 0xffcc00
            };
            const matBody = new THREE.MeshStandardMaterial({ color: colors.darkRed, roughness: 0.9, flatShading: true });
            const matArmor = new THREE.MeshStandardMaterial({ color: colors.armorRed, roughness: 1.0, flatShading: true });
            const matWing = new THREE.MeshStandardMaterial({ color: colors.wingMembrane, roughness: 0.6, side: THREE.DoubleSide, flatShading: true });
            const matBelly = new THREE.MeshStandardMaterial({ color: colors.bellyBone, roughness: 0.8, flatShading: true });
            const matBone = new THREE.MeshStandardMaterial({ color: colors.boneColor, roughness: 0.5, flatShading: true });
            const matEye = new THREE.MeshBasicMaterial({ color: colors.eyeYellow });

            // 状態管理
            let currentModel = null;
            let updateActions = []; // アニメーション関数配列
            let time = 0;

            // --- 4. 共通パーツ生成関数 ---
            
            // 【頭部 (完全体・集合体・飛行体共通)】
            function buildHead(hasLight = false) {
                const headGroup = new THREE.Group();
                const skull = new THREE.Mesh(new THREE.SphereGeometry(1.6, 8, 8), matArmor);
                skull.position.set(0, 0.5, 0); skull.castShadow = true; headGroup.add(skull);

                const snout = new THREE.Mesh(new THREE.CylinderGeometry(0.6, 1.5, 2.5, 4), matArmor);
                snout.rotation.set(Math.PI / 2, Math.PI / 4, 0); snout.position.set(0, 0.2, 1.6); snout.castShadow = true; headGroup.add(snout);

                const jaw = new THREE.Mesh(new THREE.CylinderGeometry(0.4, 1.4, 2.5, 4), matArmor);
                jaw.rotation.set(Math.PI / 2 + Math.PI / 8, Math.PI / 4, 0); jaw.position.set(0, -0.6, 1.4); jaw.castShadow = true; headGroup.add(jaw);

                const mainHorn = new THREE.Mesh(new THREE.ConeGeometry(0.5, 4.0, 4), matBone);
                mainHorn.position.set(0, 1.8, 1.2); mainHorn.rotation.x = Math.PI / 6; mainHorn.castShadow = true; headGroup.add(mainHorn);

                for (let i = -1; i <= 1; i += 2) {
                    const upperFrill = new THREE.Mesh(new THREE.ConeGeometry(0.8, 3.5, 4), matArmor);
                    upperFrill.position.set(i * 1.4, 1.8, -0.8); upperFrill.rotation.set(-Math.PI / 6, 0, i * -Math.PI / 5); headGroup.add(upperFrill);
                    
                    const midFrill = new THREE.Mesh(new THREE.ConeGeometry(0.7, 2.8, 4), matArmor);
                    midFrill.position.set(i * 1.8, 0.5, -0.8); midFrill.rotation.set(-Math.PI / 6, 0, i * -Math.PI / 2.5); headGroup.add(midFrill);

                    const cheekTusk = new THREE.Mesh(new THREE.ConeGeometry(0.2, 1.5, 4), matBone);
                    cheekTusk.position.set(i * 1.4, -0.2, 0.8); cheekTusk.rotation.set(Math.PI / 6, 0, i * -Math.PI / 2.5); headGroup.add(cheekTusk);
                    
                    const eye = new THREE.Mesh(new THREE.SphereGeometry(0.2, 8, 8), matEye);
                    eye.position.set(i * 0.7, 0.8, 1.6); headGroup.add(eye);

                    const brow = new THREE.Mesh(new THREE.BoxGeometry(0.9, 0.4, 0.6), matArmor);
                    brow.position.set(i * 0.7, 1.0, 1.7); brow.rotation.set(0, i * Math.PI / 6, i * -Math.PI / 6); headGroup.add(brow);
                }

                for(let i=-1; i<=1; i+=2) {
                    for(let j=0; j<3; j++) {
                        const fangT = new THREE.Mesh(new THREE.ConeGeometry(0.1, 0.5, 4), matBone);
                        fangT.position.set(i * (0.3 + j*0.15), -0.2, 2.5 - j*0.4); fangT.rotation.x = Math.PI; headGroup.add(fangT);
                        const fangB = new THREE.Mesh(new THREE.ConeGeometry(0.1, 0.5, 4), matBone);
                        fangB.position.set(i * (0.2 + j*0.15), 0.3, 1.2 - j*0.4); jaw.add(fangB);
                    }
                }

                let bLight = null;
                if (hasLight) {
                    bLight = new THREE.PointLight(colors.eyeYellow, 0, 5);
                    bLight.position.set(0, -0.5, 1.5); headGroup.add(bLight);
                }
                return { root: headGroup, light: bLight };
            }

            // 【尻尾 (全形態共通ベース)】
            function buildTail(count, radiusStart, length) {
                const tailRoot = new THREE.Group();
                let currentParent = tailRoot;
                const segments = [];

                for (let i = 0; i < count; i++) {
                    const segGroup = new THREE.Group();
                    const radius = radiusStart * (1 - i / count) + 0.3;

                    const segment = new THREE.Mesh(new THREE.CylinderGeometry(radius + 0.2, radius, length, 8), i % 2 === 0 ? matArmor : matBody);
                    segment.rotation.x = Math.PI / 2; segment.position.z = -length / 2; segment.castShadow = true;
                    
                    if (i < count - 1) {
                        const spike = new THREE.Mesh(new THREE.ConeGeometry(radius * 0.4, radius * 1.5, 4), matArmor);
                        spike.position.set(0, radius, -length / 2); segGroup.add(spike);
                        if(i % 2 === 0) {
                            for(let j=-1; j<=1; j+=2) {
                                const sideSpike = new THREE.Mesh(new THREE.ConeGeometry(radius*0.2, radius*1.0, 4), matArmor);
                                sideSpike.position.set(j * radius, 0, -length / 2); sideSpike.rotation.z = j * -Math.PI / 2; segGroup.add(sideSpike);
                            }
                        }
                    }
                    segGroup.add(segment);

                    if (i === count - 1) {
                        const bladeGeo = new THREE.ConeGeometry(0.8, 4, 4);
                        const mainBlade = new THREE.Mesh(bladeGeo, matArmor);
                        mainBlade.position.set(0, 0, -length - 1.5); mainBlade.rotation.x = Math.PI / 2; mainBlade.scale.z = 0.3; segGroup.add(mainBlade);
                        for(let j=-1; j<=1; j+=2) {
                            const sideBlade = new THREE.Mesh(new THREE.ConeGeometry(0.5, 3.5, 4), matArmor);
                            sideBlade.position.set(j * 0.8, 0, -length - 1); sideBlade.rotation.set(Math.PI / 2, j * Math.PI / 6, 0); sideBlade.scale.z = 0.3; segGroup.add(sideBlade);
                        }
                    }
                    segGroup.position.z = i === 0 ? 0 : -length + 0.3;
                    currentParent.add(segGroup); segments.push(segGroup); currentParent = segGroup;
                }
                return { root: tailRoot, segments: segments };
            }

            // 【翼 (完全体・飛行体共通)】
            function buildWings(scale = 1.0) {
                const wingsRoot = new THREE.Group();
                const wingObjects = [];

                function addBone(parent, sx, sy, ex, ey, thick, hasClaw) {
                    const dx = ex - sx, dy = ey - sy;
                    const len = Math.sqrt(dx*dx + dy*dy), ang = Math.atan2(dy, dx);
                    const bone = new THREE.Mesh(new THREE.CylinderGeometry(thick, thick * 0.4, len, 6), matArmor);
                    bone.position.set(sx + dx/2, sy + dy/2, 0); bone.rotation.z = ang - Math.PI/2; parent.add(bone);
                    if (hasClaw) {
                        const claw = new THREE.Mesh(new THREE.ConeGeometry(0.3, 1.8, 4), matBone);
                        claw.position.set(ex + Math.cos(ang)*0.8, ey + Math.sin(ang)*0.8, 0); claw.rotation.z = ang - Math.PI/2; parent.add(claw);
                    }
                }

                for(let sign of [-1, 1]) {
                    const wingGroup = new THREE.Group();
                    
                    // 主翼
                    const mwRoot = new THREE.Group(); mwRoot.position.set(sign * 2.5, 7, -2);
                    const mwShape = new THREE.Shape();
                    mwShape.moveTo(0, 0); mwShape.lineTo(sign * 5, 4); mwShape.lineTo(sign * 14, 12);
                    mwShape.quadraticCurveTo(sign * 13, 6, sign * 17, 3); mwShape.quadraticCurveTo(sign * 12, 0, sign * 12, -4);
                    mwShape.quadraticCurveTo(sign * 7, -2, sign * 5, -2); mwShape.lineTo(0, 0);
                    
                    const mwMesh = new THREE.Mesh(new THREE.ExtrudeGeometry(mwShape, { depth: 0.05, bevelEnabled: false }), matWing);
                    mwMesh.position.z = -0.025; mwMesh.castShadow = true; mwRoot.add(mwMesh);
                    
                    addBone(mwRoot, 0, 0, sign*5, 4, 0.5, false); addBone(mwRoot, sign*5, 4, sign*14, 12, 0.35, true);
                    addBone(mwRoot, sign*5, 4, sign*17, 3, 0.3, true); addBone(mwRoot, sign*5, 4, sign*12, -4, 0.3, true); addBone(mwRoot, sign*5, 4, sign*5, -2, 0.3, false);
                    
                    wingGroup.add(mwRoot); wingObjects.push({ root: mwRoot, isLeft: sign === -1, speed: 1.0 });

                    // 副翼
                    const swRoot = new THREE.Group(); swRoot.position.set(sign * 2.5, 3, -3);
                    const swShape = new THREE.Shape();
                    swShape.moveTo(0, 0); swShape.lineTo(sign * 3, 1); swShape.lineTo(sign * 8, 3);
                    swShape.quadraticCurveTo(sign * 7, 0, sign * 9, -2); swShape.quadraticCurveTo(sign * 5, -2, sign * 4, -4); swShape.lineTo(0, 0);
                    
                    const swMesh = new THREE.Mesh(new THREE.ExtrudeGeometry(swShape, { depth: 0.05, bevelEnabled: false }), matWing);
                    swMesh.position.z = -0.025; swMesh.castShadow = true; swRoot.add(swMesh);
                    
                    addBone(swRoot, 0, 0, sign*3, 1, 0.3, false); addBone(swRoot, sign*3, 1, sign*8, 3, 0.2, true);
                    addBone(swRoot, sign*3, 1, sign*9, -2, 0.2, false); addBone(swRoot, sign*3, 1, sign*4, -4, 0.2, false);

                    wingGroup.add(swRoot); wingObjects.push({ root: swRoot, isLeft: sign === -1, speed: 1.2 });
                    
                    wingsRoot.add(wingGroup);
                }
                wingsRoot.scale.set(scale, scale, scale);
                return { root: wingsRoot, wings: wingObjects };
            }

            // --- 5. 形態別構築関数 ---

            // 【幼体/微小体】
            function createMicroForm() {
                const swarmGroup = new THREE.Group();
                const animators = [];

                // 1体の幼体を生成するローカル関数
                function buildSingleMicro(x, z, timeOffset) {
                    const group = new THREE.Group();
                    group.position.set(x, 1.0, z);

                    // 胴体 (縦長で直立姿勢)
                    const bodyGeo = new THREE.CylinderGeometry(1.8, 2.2, 4.5, 8);
                    const body = new THREE.Mesh(bodyGeo, matBody);
                    body.position.set(0, 2.5, 0);
                    body.rotation.x = Math.PI / 12; // 少し胸を張る
                    body.castShadow = true;
                    group.add(body);

                    // 背中と側面のトゲ
                    for(let i=0; i<4; i++) {
                        const backSpike = new THREE.Mesh(new THREE.ConeGeometry(0.6, 2.0, 4), matArmor);
                        backSpike.position.set(0, 4 - i * 1.2, -1.5);
                        backSpike.rotation.x = -Math.PI / 4;
                        group.add(backSpike);
                        
                        for (let sign of [-1, 1]) {
                            const sideSpike = new THREE.Mesh(new THREE.ConeGeometry(0.5, 1.5, 4), matArmor);
                            sideSpike.position.set(sign * 1.8, 3.5 - i * 1.0, -0.5);
                            sideSpike.rotation.z = sign * -Math.PI / 3;
                            sideSpike.rotation.x = -Math.PI / 6;
                            group.add(sideSpike);
                        }
                    }

                    // 胸部(花びらパーツ)
                    const bellyGroup = new THREE.Group();
                    bellyGroup.position.set(0, 2.2, 1.8);
                    bellyGroup.rotation.x = Math.PI / 8;
                    
                    const bellyCenterMesh = new THREE.Mesh(new THREE.SphereGeometry(1.2, 8, 8), matBelly);
                    bellyCenterMesh.scale.set(1, 1, 0.4);
                    bellyGroup.add(bellyCenterMesh);
                    
                    const petalGeo = new THREE.ConeGeometry(0.6, 2.0, 4);
                    petalGeo.translate(0, 1.0, 0); 
                    for(let i=0; i<8; i++) {
                        const petal = new THREE.Mesh(petalGeo, matBelly);
                        petal.rotation.set(-Math.PI / 12, 0, (Math.PI * 2 / 8) * i);
                        petal.scale.z = 0.3;
                        bellyGroup.add(petal);
                    }
                    group.add(bellyGroup);

                    // 頭部
                    const headGroup = new THREE.Group();
                    headGroup.position.set(0, 5.0, 0.5);
                    
                    const skull = new THREE.Mesh(new THREE.SphereGeometry(1.4, 8, 8), matArmor);
                    headGroup.add(skull);
                    
                    const snout = new THREE.Mesh(new THREE.CylinderGeometry(0.6, 1.2, 1.8, 4), matArmor);
                    snout.rotation.set(Math.PI / 2, Math.PI / 4, 0);
                    snout.position.set(0, -0.2, 1.2);
                    headGroup.add(snout);

                    // 王冠フリル (幼体用)
                    for (let i = -1; i <= 1; i += 2) {
                        const upperFrill = new THREE.Mesh(new THREE.ConeGeometry(0.6, 2.5, 4), matArmor);
                        upperFrill.position.set(i * 1.2, 1.0, -0.5);
                        upperFrill.rotation.set(-Math.PI / 6, 0, i * -Math.PI / 4);
                        headGroup.add(upperFrill);
                        
                        const midFrill = new THREE.Mesh(new THREE.ConeGeometry(0.5, 1.8, 4), matArmor);
                        midFrill.position.set(i * 1.4, 0.2, -0.5);
                        midFrill.rotation.set(-Math.PI / 6, 0, i * -Math.PI / 2);
                        headGroup.add(midFrill);
                    }
                    
                    // 目 (白く発光)
                    const matWhiteEye = new THREE.MeshBasicMaterial({ color: 0xffffff });
                    for (let i = -1; i <= 1; i += 2) {
                        const eye = new THREE.Mesh(new THREE.SphereGeometry(0.25, 8, 8), matWhiteEye);
                        eye.position.set(i * 0.6, 0.3, 1.2);
                        headGroup.add(eye);
                    }

                    // 口元の白い牙
                    for (let i = -1; i <= 1; i += 2) {
                        const fang = new THREE.Mesh(new THREE.ConeGeometry(0.15, 0.8, 4), matBone);
                        fang.position.set(i * 0.4, -0.6, 1.8);
                        fang.rotation.x = Math.PI; // 下向き
                        headGroup.add(fang);
                    }

                    group.add(headGroup);

                    // 下部の2本の牙(腹部の下)
                    for (let i = -1; i <= 1; i += 2) {
                        const bottomFang = new THREE.Mesh(new THREE.ConeGeometry(0.4, 2.0, 4), matArmor);
                        bottomFang.position.set(i * 0.8, 0.5, 1.5);
                        bottomFang.rotation.set(Math.PI / 6, 0, i * Math.PI / 8);
                        group.add(bottomFang);
                    }

                    // 多脚 (カニ/クモのような脚)
                    const legs = [];
                    for (let i = 0; i < 3; i++) {
                        for (let sign of [-1, 1]) {
                            const leg = new THREE.Group();
                            leg.position.set(sign * 1.8, 1.8, 1.5 - i * 1.5);
                            
                            const upper = new THREE.Mesh(new THREE.CylinderGeometry(0.5, 0.4, 3.0, 8), matArmor);
                            upper.position.set(sign * 1.2, 0.5, 0);
                            upper.rotation.z = sign * -Math.PI / 3;
                            leg.add(upper);
                            
                            const lower = new THREE.Mesh(new THREE.ConeGeometry(0.4, 3.0, 4), matArmor);
                            lower.position.set(sign * 2.5, -0.8, 0);
                            lower.rotation.z = sign * Math.PI / 6;
                            leg.add(lower);

                            const legSpike = new THREE.Mesh(new THREE.ConeGeometry(0.3, 1.0, 4), matArmor);
                            legSpike.position.set(sign * 1.2, 1.8, 0);
                            legSpike.rotation.z = sign * -Math.PI / 6;
                            leg.add(legSpike);

                            group.add(leg);
                            legs.push({ root: leg, offset: i + sign });
                        }
                    }

                    // 尻尾
                    const tailObj = buildTail(8, 1.0, 1.5);
                    tailObj.root.position.set(0, 1.0, -1.0);
                    group.add(tailObj.root);

                    // この個体専用のアニメーション関数
                    const animateFn = () => {
                        const t = time + timeOffset; // 個体ごとにタイミングをずらす
                        group.position.y = 1.0 + Math.sin(t * 3) * 0.1; // 呼吸
                        legs.forEach(leg => {
                            leg.root.rotation.x = Math.sin(t * 8 + leg.offset) * 0.1; // ワシャワシャ
                            leg.root.rotation.z = Math.cos(t * 8 + leg.offset) * 0.05;
                        });
                        tailObj.segments.forEach((seg, i) => {
                            seg.rotation.y = Math.sin(t * 4 - i * 0.5) * 0.2;
                        });
                    };

                    return { root: group, animate: animateFn };
                }

                // 群れの配置座標(中心、周り、少し離れた場所など)
                const positions = [
                    [0, 0], [-6, 3], [6, 3], [-4, -6], [4, -6], [-10, -1], [10, -1], [0, 8], [0, -10]
                ];

                // 配置座標をもとに複数体を生成して並べる
                positions.forEach((pos) => {
                    // 少しだけランダムに位置を散らす
                    const x = pos[0] + (Math.random() - 0.5) * 2.0;
                    const z = pos[1] + (Math.random() - 0.5) * 2.0;
                    
                    // アニメーションの開始タイミングをランダムにずらす
                    const timeOffset = Math.random() * 10;
                    
                    const mf = buildSingleMicro(x, z, timeOffset);
                    
                    // 個体ごとに少し向いている方向をバラけさせる
                    mf.root.rotation.y = (Math.random() - 0.5) * 0.8; 
                    
                    swarmGroup.add(mf.root);
                    animators.push(mf.animate); // 更新リストに追加
                });

                // ループ更新時に全個体のアニメーションを実行する
                updateActions.push(() => {
                    animators.forEach(anim => anim());
                });

                return swarmGroup;
            }

            // 【集合体】
            function createAggregateForm() {
                const group = new THREE.Group();
                group.position.y = 3.5; // 脚が長くなるため重心を高く

                // 胴体 (前傾姿勢)
                const bodyGeo = new THREE.CylinderGeometry(2.5, 4, 8, 8);
                const body = new THREE.Mesh(bodyGeo, matBody);
                body.rotation.x = Math.PI / 2 + Math.PI / 6; // 前傾
                body.position.set(0, 3, 0);
                body.castShadow = true;
                group.add(body);

                // 腹部 (オレンジ色の花びらパーツ)
                const bellyGroup = new THREE.Group();
                bellyGroup.position.set(0, 1.0, 3.5);
                bellyGroup.rotation.x = Math.PI / 6;
                const bellyCenterMesh = new THREE.Mesh(new THREE.SphereGeometry(1.5, 8, 8), matBelly);
                bellyCenterMesh.scale.set(1, 1, 0.4);
                bellyGroup.add(bellyCenterMesh);
                const petalGeo = new THREE.ConeGeometry(0.7, 2.5, 4);
                petalGeo.translate(0, 1.2, 0); 
                for(let i=0; i<8; i++) {
                    const petal = new THREE.Mesh(petalGeo, matBelly);
                    petal.rotation.set(-Math.PI / 12, 0, (Math.PI * 2 / 8) * i);
                    petal.scale.z = 0.3;
                    bellyGroup.add(petal);
                }
                group.add(bellyGroup);

                // 長い首 (複数の関節で構築)
                const neckSegments = [];
                const neckRoot = new THREE.Group();
                neckRoot.position.set(0, 4.5, 2.5);
                neckRoot.rotation.x = -Math.PI / 4; // 上前方へ向かう
                group.add(neckRoot);
                
                let currentNeckParent = neckRoot;
                const neckCount = 4;
                for(let i=0; i<neckCount; i++) {
                    const neckSeg = new THREE.Group();
                    const radius = 1.8 - i * 0.15;
                    const len = 1.5;
                    const mesh = new THREE.Mesh(new THREE.CylinderGeometry(radius - 0.1, radius, len, 8), matArmor);
                    mesh.position.y = len / 2;
                    mesh.castShadow = true;
                    neckSeg.add(mesh);
                    
                    // 首の背中のトゲ
                    const spike = new THREE.Mesh(new THREE.ConeGeometry(0.4, 1.5, 4), matArmor);
                    spike.position.set(0, len / 2, -radius);
                    spike.rotation.x = -Math.PI / 4;
                    neckSeg.add(spike);

                    neckSeg.position.y = i === 0 ? 0 : 1.4;
                    currentNeckParent.add(neckSeg);
                    neckSegments.push(neckSeg);
                    currentNeckParent = neckSeg;
                }

                // 頭部 (首の先に接続)
                const headObj = buildHead(true);
                headObj.root.position.set(0, 1.5, 0.5);
                headObj.root.rotation.x = Math.PI / 4 + Math.PI / 8; // 正面下を向くように調整
                currentNeckParent.add(headObj.root);

                // カマ(前肢)- クロスしないように外側に開いて構える
                const arms = [];
                for (let sign of [-1, 1]) {
                    const arm = new THREE.Group();
                    arm.position.set(sign * 2.5, 4.0, 3.5); // 胴体からの発生位置
                    
                    // 腕の根元(上腕)
                    const upper = new THREE.Mesh(new THREE.CylinderGeometry(0.8, 0.6, 5, 8), matArmor);
                    upper.position.set(sign * 2.0, 1.0, 1.5); // 外側・斜め前・上へ向かって伸びる
                    upper.rotation.set(Math.PI / 4, sign * -Math.PI / 8, sign * -Math.PI / 3);
                    arm.add(upper);
                    
                    // 鎌の刃部分
                    const bladeGroup = new THREE.Group();
                    bladeGroup.position.set(sign * 4.0, 2.0, 3.0); // 腕の先端に接続
                    // 刃が前方を向きつつ、下に向かって鋭くカーブする角度
                    bladeGroup.rotation.set(Math.PI / 6, sign * Math.PI / 8, sign * Math.PI / 4);
                    
                    const bladeGeo = new THREE.ConeGeometry(0.6, 6, 4); // 刃を少し長く
                    const blade = new THREE.Mesh(bladeGeo, matBone);
                    blade.position.set(0, -3, 0); // 中心点を上にずらして吊り下げる
                    blade.rotation.x = Math.PI; // 下向き
                    bladeGroup.add(blade);
                    
                    arm.add(bladeGroup);
                    group.add(arm);
                    arms.push({ root: arm, sign: sign, blade: bladeGroup });
                }

                // クモのような多脚 (長く、一度上に上がってから下に降りる関節)
                const legs = [];
                const legPositions = [
                    { z: 2, yRot: Math.PI / 6 },   // 前脚
                    { z: -1, yRot: 0 },            // 中脚
                    { z: -4, yRot: -Math.PI / 6 }  // 後脚
                ];
                
                legPositions.forEach((pos, i) => {
                    for (let sign of [-1, 1]) {
                        const legRoot = new THREE.Group();
                        legRoot.position.set(sign * 2.5, 3, pos.z);
                        legRoot.rotation.y = sign * pos.yRot;
                        
                        // 太もも (上に持ち上がる)
                        const thighLen = 4.5;
                        const thigh = new THREE.Mesh(new THREE.CylinderGeometry(0.8, 0.6, thighLen, 8), matArmor);
                        thigh.position.set(sign * (thighLen/2) * Math.cos(Math.PI/6), (thighLen/2) * Math.sin(Math.PI/6), 0);
                        thigh.rotation.z = sign * -Math.PI / 3; // 斜め上へ
                        legRoot.add(thigh);
                        
                        // 関節のトゲ
                        const tSpike = new THREE.Mesh(new THREE.ConeGeometry(0.5, 2.0, 4), matArmor);
                        tSpike.position.set(sign * (thighLen * Math.cos(Math.PI/6)), thighLen * Math.sin(Math.PI/6) + 0.5, 0);
                        tSpike.rotation.z = sign * -Math.PI / 8;
                        legRoot.add(tSpike);

                        // すね (下に鋭く伸びる)
                        const shinLen = 6.5;
                        const shinGrp = new THREE.Group();
                        shinGrp.position.set(sign * thighLen * Math.cos(Math.PI/6), thighLen * Math.sin(Math.PI/6), 0);
                        
                        const shin = new THREE.Mesh(new THREE.CylinderGeometry(0.6, 0.1, shinLen, 8), matBody);
                        shin.position.set(sign * (shinLen/2) * Math.cos(-Math.PI/2.5), (shinLen/2) * Math.sin(-Math.PI/2.5), 0);
                        shin.rotation.z = sign * -Math.PI / 6; // 斜め下へ
                        shinGrp.add(shin);
                        
                        // 脛の装甲
                        const sArmor = new THREE.Mesh(new THREE.CylinderGeometry(0.7, 0.3, shinLen*0.8, 4), matArmor);
                        sArmor.position.copy(shin.position);
                        sArmor.rotation.copy(shin.rotation);
                        shinGrp.add(sArmor);

                        legRoot.add(shinGrp);
                        group.add(legRoot);
                        legs.push({ root: legRoot, offset: i + sign });
                    }
                });

                // 背部の2本の長い触手(ハサミ付き)
                const tentacles = [];
                for (let sign of [-1, 1]) {
                    const tentacle = buildTail(12, 0.9, 2.2);
                    tentacle.root.position.set(sign * 1.5, 6, -1);
                    // 斜め上・前方にカーブするように初期回転
                    tentacle.root.rotation.set(-Math.PI / 6, sign * Math.PI / 4, 0);
                    
                    // 各セグメントを少しずつ曲げて、サソリの尾のようにアーチを描かせる
                    tentacle.segments.forEach((seg, idx) => {
                        seg.rotation.x = Math.PI / 10; // 前に曲がる
                    });
                    
                    group.add(tentacle.root);
                    tentacles.push({ obj: tentacle, sign: sign });
                }

                // 尻尾(下の方から後ろへ)
                const tailObj = buildTail(10, 1.2, 2.5);
                tailObj.root.position.set(0, 2, -3);
                group.add(tailObj.root);

                updateActions.push(() => {
                    group.position.y = 3.5 + Math.sin(time * 2) * 0.2; // 全体の上下動
                    if(headObj.light) headObj.light.intensity = (Math.sin(time * 3) + 1) * 0.5 + 0.5;
                    
                    // 首の不気味なうねり
                    neckSegments.forEach((seg, i) => {
                        seg.rotation.x = Math.sin(time * 2 - i * 0.5) * 0.05;
                        seg.rotation.z = Math.sin(time * 1.5 - i * 0.5) * 0.02;
                    });

                    // カマの動き (クロスしないよう控えめな動きに)
                    arms.forEach(arm => { 
                        arm.root.rotation.x = Math.sin(time * 3) * 0.05; 
                        arm.root.rotation.y = Math.cos(time * 2) * 0.05 * arm.sign; 
                        arm.blade.rotation.x = Math.sin(time * 3 + arm.sign) * 0.05; 
                    });
                    
                    // クモ脚のワシャワシャ歩行
                    legs.forEach(leg => { 
                        leg.root.rotation.x = Math.sin(time * 6 + leg.offset) * 0.1; 
                        leg.root.position.y = 3 + Math.sin(time * 6 + leg.offset + Math.PI/2) * 0.4;
                    });
                    
                    // 尻尾
                    tailObj.segments.forEach((seg, i) => { 
                        seg.rotation.y = Math.sin(time * 2 - i * 0.3) * 0.2; 
                    });

                    // 背部触手のうねり
                    tentacles.forEach(t => {
                        t.obj.segments.forEach((seg, i) => {
                            // 基本の曲がり具合(アーチ維持)+うねりアニメーション
                            seg.rotation.x = Math.PI / 10 + Math.sin(time * 3 - i * 0.4) * 0.05;
                            seg.rotation.y = Math.cos(time * 2 - i * 0.3) * 0.1 * t.sign;
                        });
                    });
                });
                return group;
            }

            // 【飛行体】
            function createFlyingForm() {
                const group = new THREE.Group();
                group.position.y = 8.0; // 空中

                // 細長い胴体(飛行姿勢)
                const body = new THREE.Mesh(new THREE.CylinderGeometry(2.5, 2.0, 10, 8), matBody);
                body.rotation.x = Math.PI / 2; body.castShadow = true; group.add(body);

                // 頭部(前方に)
                const headObj = buildHead(true);
                headObj.root.position.set(0, 0, 6); headObj.root.rotation.x = Math.PI / 12; group.add(headObj.root);

                // 翼(水平に広げる)
                const wingsObj = buildWings(1.2);
                wingsObj.root.position.set(0, 1, 2);
                wingsObj.wings.forEach(w => w.root.rotation.x = 0); // 前傾させない
                group.add(wingsObj.root);

                // 垂れ下がる脚
                for (let sign of [-1, 1]) {
                    const leg = new THREE.Group(); leg.position.set(sign * 2, -1, -2);
                    const thigh = new THREE.Mesh(new THREE.CylinderGeometry(1, 0.8, 4, 8), matArmor);
                    thigh.position.set(0, -1.5, -1); thigh.rotation.x = -Math.PI / 4; leg.add(thigh);
                    const shin = new THREE.Mesh(new THREE.ConeGeometry(0.5, 3, 8), matBone);
                    shin.position.set(0, -3.5, -2.5); shin.rotation.x = -Math.PI / 6; leg.add(shin);
                    group.add(leg);
                }

                // まっすぐな尻尾
                const tailObj = buildTail(12, 1.2, 2.8);
                tailObj.root.position.set(0, 0, -5); group.add(tailObj.root);

                updateActions.push(() => {
                    group.position.y = 8.0 + Math.sin(time * 2) * 1.0; // 浮遊
                    group.rotation.x = Math.sin(time * 1.5) * 0.05;
                    if(headObj.light) headObj.light.intensity = (Math.sin(time * 3) + 1) * 0.5 + 0.5;
                    wingsObj.wings.forEach(w => {
                        w.root.rotation.z = Math.sin(time * 4 * w.speed) * 0.3 * (w.isLeft ? -1 : 1); // 大きく羽ばたく
                    });
                    tailObj.segments.forEach((seg, i) => {
                        seg.rotation.x = Math.sin(time * 3 - i * 0.4) * 0.1; // 上下にうねる
                        seg.rotation.y = Math.cos(time * 2 - i * 0.3) * 0.05;
                    });
                });
                return group;
            }

            // 【完全体】 (前回のコードベースを共通関数で再構築)
            function createPerfectForm() {
                const group = new THREE.Group();

                // 胴体
                const bodyGroup = new THREE.Group();
                const torso = new THREE.Mesh(new THREE.CylinderGeometry(3, 5, 9, 8), matBody);
                torso.position.y = 5.5; torso.castShadow = true; bodyGroup.add(torso);
                const backArmor = new THREE.Mesh(new THREE.BoxGeometry(5, 8, 4), matArmor);
                backArmor.position.set(0, 5.5, -1.5); backArmor.rotation.x = -Math.PI / 12; bodyGroup.add(backArmor);
                
                const bellyGroup = new THREE.Group(); bellyGroup.position.set(0, 5, 3.5); bellyGroup.rotation.x = Math.PI / 8;
                
                // --- 修正箇所:Object.assignでscaleを上書きするのをやめ、個別に設定 ---
                const bellyCenterMesh = new THREE.Mesh(new THREE.SphereGeometry(1.5, 8, 8), matBelly);
                bellyCenterMesh.scale.set(1, 1, 0.4);
                bellyGroup.add(bellyCenterMesh);
                // ----------------------------------------------------------------------
                
                const petalGeo = new THREE.ConeGeometry(0.7, 2.5, 4); petalGeo.translate(0, 1.2, 0); 
                for(let i=0; i<8; i++) {
                    const petal = new THREE.Mesh(petalGeo, matBelly);
                    petal.rotation.set(-Math.PI / 12, 0, (Math.PI * 2 / 8) * i); petal.scale.z = 0.3; bellyGroup.add(petal);
                }
                bodyGroup.add(bellyGroup);
                group.add(bodyGroup);

                // 頭部
                const headObj = buildHead(true);
                headObj.root.position.set(0, 10.5, 1.5); group.add(headObj.root);

                // 腕・脚 (二足歩行用)
                for(let sign of [-1, 1]) {
                    // 腕
                    const arm = new THREE.Group();
                    const shoulder = new THREE.Mesh(new THREE.SphereGeometry(1.6, 8, 8), matArmor); shoulder.position.set(sign*3.8, 7.5, 0); arm.add(shoulder);
                    const ua = new THREE.Mesh(new THREE.CylinderGeometry(0.9, 0.7, 3.5, 8), matBody); ua.position.set(sign*4.8, 5.5, 0.5); ua.rotation.set(Math.PI/6, 0, sign*Math.PI/6); arm.add(ua);
                    const la = new THREE.Mesh(new THREE.CylinderGeometry(0.8, 1.0, 3.5, 8), matArmor); la.position.set(sign*5.2, 3.5, 2.5); la.rotation.set(-Math.PI/4, 0, sign*Math.PI/12); arm.add(la);
                    for(let i=-1; i<=1; i++) {
                        const claw = new THREE.Mesh(new THREE.ConeGeometry(0.3, 1.8, 4), matBone); claw.position.set(sign*5.2+(i*0.4), 1.8, 3.5); claw.rotation.set(-Math.PI/2, 0, sign*(i*0.2)); arm.add(claw);
                    }
                    group.add(arm);

                    // 脚
                    const leg = new THREE.Group();
                    const thigh = new THREE.Mesh(new THREE.CylinderGeometry(2.8, 3.2, 5.5, 8), matArmor); thigh.position.set(sign*3.5, 4.5, -1); thigh.rotation.set(Math.PI/8, 0, sign*-Math.PI/12); leg.add(thigh);
                    const shin = new THREE.Mesh(new THREE.CylinderGeometry(2.5, 2.0, 4.5, 8), matBody); shin.position.set(sign*4, 2.0, 1.0); shin.rotation.x = -Math.PI/12; leg.add(shin);
                    const foot = new THREE.Mesh(new THREE.BoxGeometry(3.5, 1.5, 4.5), matArmor); foot.position.set(sign*4.2, 0.75, 2.0); leg.add(foot);
                    for(let i=-1; i<=1; i++) {
                        const toe = new THREE.Mesh(new THREE.ConeGeometry(0.6, 2.0, 4), matBone); toe.position.set(sign*4.2+(i*1.2), 0.5, 4.0); toe.rotation.set(-Math.PI/2, 0, sign*(i*-0.2)); leg.add(toe);
                    }
                    group.add(leg);
                }

                // 翼と尻尾
                const wingsObj = buildWings(1.0);
                wingsObj.wings.forEach(w => { w.root.rotation.x = -Math.PI / 10; w.root.rotation.y = (w.isLeft ? -1 : 1) * Math.PI / 8; });
                group.add(wingsObj.root);
                
                const tailObj = buildTail(14, 2.2, 2.8);
                tailObj.root.position.set(0, 3, -3); group.add(tailObj.root);

                updateActions.push(() => {
                    group.position.y = Math.sin(time * 0.5) * 0.2; // 呼吸
                    if(headObj.light) headObj.light.intensity = (Math.sin(time * 3) + 1) * 0.5 + 0.5;
                    wingsObj.wings.forEach(w => {
                        const sign = w.isLeft ? -1 : 1;
                        w.root.rotation.y = (sign * Math.PI / 8) + Math.sin(time * 2 * w.speed) * 0.1 * sign;
                        w.root.rotation.z = Math.sin(time * 2 * w.speed) * 0.05 * sign;
                    });
                    tailObj.segments.forEach((seg, i) => {
                        seg.rotation.y = Math.sin(time * 1.5 - i * 0.4) * 0.15;
                        seg.rotation.x = Math.cos(time * 1 - i * 0.3) * 0.05;
                    });
                });
                return group;
            }


            // --- 6. フォーム切り替えと初期化 ---
            window.changeForm = function(formName) {
                if (currentModel) { scene.remove(currentModel); }
                updateActions = []; // アニメーションリセット

                switch (formName) {
                    case 'micro': currentModel = createMicroForm(); break;
                    case 'aggregate': currentModel = createAggregateForm(); break;
                    case 'flying': currentModel = createFlyingForm(); break;
                    case 'perfect': default: currentModel = createPerfectForm(); break;
                }
                scene.add(currentModel);
            };

            // 地面作成
            const groundGeo = new THREE.PlaneGeometry(100, 100, 32, 32);
            const pos = groundGeo.attributes.position;
            for(let i=0; i<pos.count; i++) pos.setZ(i, (Math.random() - 0.5) * 1.5);
            groundGeo.computeVertexNormals();
            const ground = new THREE.Mesh(groundGeo, new THREE.MeshStandardMaterial({ color: 0x110505, roughness: 0.9, flatShading: true }));
            ground.rotation.x = -Math.PI / 2; ground.receiveShadow = true; scene.add(ground);

            document.getElementById('loading').style.opacity = '0';

            // リサイズ対応
            window.addEventListener('resize', () => {
                camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix();
                renderer.setSize(window.innerWidth, window.innerHeight);
            });

            // --- 7. アニメーションループ ---
            function animate() {
                requestAnimationFrame(animate);
                time += 0.05;
                updateActions.forEach(action => action(time)); // 各形態の専用アニメーションを実行
                controls.update();
                renderer.render(scene, camera);
            }

            // 初回表示
            changeForm('perfect');
            animate();
        });
    </script>
</body>
</html>

デストロイヤー

僕の1番のお気に入りは完全体

🤖 使用したAI
Gemini
👁 5 回閲覧
📅 投稿:2026年3月25日 🔄 更新:2026年4月11日
応援スタンプを押してみよう!