見出し画像

EXCELLENT!!!

おはこんばんにちは

Rikurikuです~。

え?うるさい?

わかりました。音量調節しますね。




本題

これまで、いくつかコードを書いてきましたが、

怪しいと思うのでダウンロードはしなくて良いです。(???)

実は、それぞれ

正二十面体、正十二面体、正八面体がモチーフなんです。

なら次は立方体だろ

と思って作りました。

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>立方体の展開図パズル (Flashy Victory)</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background-color: #ffffff;
            font-family: sans-serif;
        }

        canvas {
            display: block;
        }

        #victory {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            font-size: 100px;
            font-weight: 900;
            color: gold;
            text-shadow:
                4px 4px 0px #b8860b,
                8px 8px 0px rgba(0, 0, 0, 0.2);
            display: none;
            pointer-events: none;
            z-index: 10;
            animation: popIn 0.5s ease-out forwards;
            white-space: nowrap;
        }

        @keyframes popIn {
            0% {
                transform: translate(-50%, -50%) scale(0) rotate(-30deg);
                opacity: 0;
            }

            80% {
                transform: translate(-50%, -50%) scale(1.2) rotate(5deg);
                opacity: 1;
            }

            100% {
                transform: translate(-50%, -50%) scale(1) rotate(0deg);
                opacity: 1;
            }
        }
    </style>
</head>

<body>
    <div id="victory">EXCELLENT!</div>
    <script type="importmap">
        {
            "imports": {
                "three": "https://unpkg.com/three@0.160.0/build/three.module.js",
                "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
            }
        }
    </script>
    <script type="module">
        import * as THREE from 'three';

        // --- シーン設定 ---
        const scene = new THREE.Scene();
        scene.background = new THREE.Color(0xffffff);

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

        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
        scene.add(ambientLight);
        const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
        dirLight.position.set(5, 10, 10);
        scene.add(dirLight);

        // --- グリッド設定 ---
        const SIZE = 2;
        const GRID_COLS = 10;
        const GRID_ROWS = 10;
        const TOTAL_WIDTH = SIZE * GRID_COLS;
        const TOTAL_HEIGHT = SIZE * GRID_ROWS;
        const GRID_OFFSET_X = -TOTAL_WIDTH / 2;
        const GRID_OFFSET_Y = TOTAL_HEIGHT / 2;

        const faceMaterial = new THREE.MeshPhongMaterial({
            color: 0x88ccff, side: THREE.DoubleSide,
            polygonOffset: true, polygonOffsetFactor: 1, polygonOffsetUnits: 1
        });
        const faceMaterialVictory = new THREE.MeshPhongMaterial({
            color: 0xffd700, side: THREE.DoubleSide,
            shininess: 100,
            emissive: 0x442200,
            polygonOffset: true, polygonOffsetFactor: 1, polygonOffsetUnits: 1
        });
        const yellowMaterial = new THREE.MeshPhongMaterial({
            color: 0xffff00, side: THREE.DoubleSide,
            polygonOffset: true, polygonOffsetFactor: 1, polygonOffsetUnits: 1
        });
        const sparkleMaterial = new THREE.MeshBasicMaterial({
            color: 0x0088ff, transparent: true, opacity: 0.5, side: THREE.DoubleSide, depthTest: false
        });
        const teleportMaterial = new THREE.MeshBasicMaterial({
            color: 0xffaa00, transparent: true, opacity: 0.5, side: THREE.DoubleSide, depthTest: false
        });

        const lineMaterial = new THREE.LineBasicMaterial({ color: 0x000000 });
        const gridLineMaterial = new THREE.LineBasicMaterial({ color: 0xcccccc });

        function getCellPosition(col, row) {
            const x = GRID_OFFSET_X + (col * SIZE) + (SIZE / 2);
            const y = GRID_OFFSET_Y - (row * SIZE) - (SIZE / 2);
            return { x, y };
        }

        // --- 1. グリッド描画 ---
        const gridGroup = new THREE.Group();
        scene.add(gridGroup);
        for (let i = 0; i <= GRID_COLS; i++) {
            const x = GRID_OFFSET_X + (i * SIZE);
            const pts = [new THREE.Vector3(x, GRID_OFFSET_Y, 0), new THREE.Vector3(x, GRID_OFFSET_Y - TOTAL_HEIGHT, 0)];
            gridGroup.add(new THREE.Line(new THREE.BufferGeometry().setFromPoints(pts), gridLineMaterial));
        }
        for (let j = 0; j <= GRID_ROWS; j++) {
            const y = GRID_OFFSET_Y - (j * SIZE);
            const pts = [new THREE.Vector3(GRID_OFFSET_X, y, 0), new THREE.Vector3(GRID_OFFSET_X + TOTAL_WIDTH, y, 0)];
            gridGroup.add(new THREE.Line(new THREE.BufferGeometry().setFromPoints(pts), gridLineMaterial));
        }

        // --- 2. 展開図定義 (11種類) ---
        const PATTERNS = [
            // Group A
            [{ x: 1, y: 0 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 3, y: 1 }, { x: 1, y: 2 }],
            [{ x: 2, y: 0 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 3, y: 1 }, { x: 1, y: 2 }],
            [{ x: 3, y: 0 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 3, y: 1 }, { x: 1, y: 2 }],
            [{ x: 3, y: 0 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 3, y: 1 }, { x: 0, y: 2 }],
            [{ x: 0, y: 0 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 3, y: 1 }, { x: 0, y: 2 }],
            [{ x: 1, y: 0 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 3, y: 1 }, { x: 0, y: 2 }],
            // Group B
            [{ x: 0, y: 2 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 2, y: 0 }, { x: 3, y: 0 }],
            [{ x: 1, y: 2 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 2, y: 0 }, { x: 3, y: 0 }],
            [{ x: 2, y: 2 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 2, y: 0 }, { x: 3, y: 0 }],
            // Group C
            [{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 2, y: 2 }, { x: 3, y: 2 }],
            // Group D
            [{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 2, y: 0 }, { x: 2, y: 1 }, { x: 3, y: 1 }, { x: 4, y: 1 }]
        ];

        const validSignatures = new Set();
        function normalizeNet(cells) {
            let minX = Infinity, minY = Infinity;
            cells.forEach(c => { minX = Math.min(minX, c.x); minY = Math.min(minY, c.y); });
            const shifted = cells.map(c => ({ x: c.x - minX, y: c.y - minY }));
            shifted.sort((a, b) => (a.y - b.y) || (a.x - b.x));
            return shifted.map(p => `${p.x},${p.y}`).join('|');
        }
        PATTERNS.forEach(base => {
            let current = base.map(p => ({ ...p }));
            for (let f = 0; f < 2; f++) {
                for (let r = 0; r < 4; r++) {
                    validSignatures.add(normalizeNet(current));
                    current = current.map(p => ({ x: -p.y, y: p.x }));
                }
                current = current.map(p => ({ x: -p.x, y: p.y }));
            }
        });

        // --- 4. 生成ロジック ---
        // MOVED DECLARATIONS UP TO AVOID TEMPORAL DEAD ZONE
        const occupiedCells = new Set();
        let netMeshes = [];
        let yellowList = [];
        let confettiActive = false; // Fixed: Declared before initGame usage
        let particles = [];        // Fixed: Declared before initGame usage

        function initGame() {
            netMeshes.forEach(o => { scene.remove(o.mesh); scene.remove(o.line); });
            netMeshes = [];
            yellowList.forEach(o => { scene.remove(o.mesh); scene.remove(o.line); });
            yellowList = [];
            occupiedCells.clear();
            document.getElementById('victory').style.display = 'none';
            // Stop confetti
            confettiActive = false;
            particles.forEach(p => scene.remove(p));
            particles = [];

            // Net
            let attempts = 0;
            let success = false;
            let netCells = [];

            while (!success && attempts < 100) {
                occupiedCells.clear();
                netCells = [];
                attempts++;

                const patternBase = PATTERNS[Math.floor(Math.random() * PATTERNS.length)];
                const rot = Math.floor(Math.random() * 4);
                const flip = Math.random() < 0.5;

                const pattern = patternBase.map(p => {
                    let x = p.x;
                    let y = p.y;
                    if (flip) { x = -x; }
                    for (let r = 0; r < rot; r++) { [x, y] = [-y, x]; }
                    return { x, y };
                });

                const minX = Math.min(...pattern.map(p => p.x));
                const minY = Math.min(...pattern.map(p => p.y));
                const normPattern = pattern.map(p => ({ x: p.x - minX, y: p.y - minY }));
                const width = Math.max(...normPattern.map(p => p.x)) + 1;
                const height = Math.max(...normPattern.map(p => p.y)) + 1;
                const maxCol = GRID_COLS - width;
                const maxRow = GRID_ROWS - height;
                if (maxCol < 0 || maxRow < 0) continue;

                const startCol = Math.floor(Math.random() * (maxCol + 1));
                const startRow = Math.floor(Math.random() * (maxRow + 1));
                for (let p of normPattern) {
                    const c = startCol + p.x;
                    const r = startRow + p.y;
                    netCells.push({ c, r });
                    occupiedCells.add(`${c},${r}`);
                }
                success = true;
            }

            netCells.forEach(cell => {
                const pos = getCellPosition(cell.c, cell.r);
                const mesh = new THREE.Mesh(new THREE.PlaneGeometry(SIZE, SIZE), faceMaterial.clone());
                mesh.position.set(pos.x, pos.y, 0);
                mesh.userData = { col: cell.c, row: cell.r, isFace: true };
                scene.add(mesh);
                const line = new THREE.LineSegments(new THREE.EdgesGeometry(new THREE.PlaneGeometry(SIZE, SIZE)), lineMaterial.clone());
                line.position.set(pos.x, pos.y, 0);
                scene.add(line);
                netMeshes.push({ mesh, line, col: cell.c, row: cell.r });
            });

            // Yellow Cells
            const OFFSETS_RULES = [
                { v: 1, h: 0 }, { v: 2, h: 0 }, { v: 3, h: 0 },
                { v: 0, h: 1 }, { v: 0, h: 2 }, { v: 0, h: 3 },
                { v: 2, h: 2 }, { v: 3, h: 3 }, 
            ];
            let yellowPlaced = false;
            attempts = 0;
            while (!yellowPlaced && attempts < 200) {
                attempts++;
                const c1 = 3 + Math.floor(Math.random() * 4);
                const r1 = 3 + Math.floor(Math.random() * 4);
                if (occupiedCells.has(`${c1},${r1}`)) continue;

                const rule = OFFSETS_RULES[Math.floor(Math.random() * OFFSETS_RULES.length)];
                const dirH = Math.random() < 0.5 ? 1 : -1;
                const dirV = Math.random() < 0.5 ? 1 : -1;
                const c2 = c1 + (rule.h * dirH);
                const r2 = r1 + (rule.v * dirV);

                if (c2 < 0 || c2 >= GRID_COLS || r2 < 0 || r2 >= GRID_ROWS) continue;
                if (occupiedCells.has(`${c2},${r2}`)) continue;
                if (c1 === c2 && r1 === r2) continue;

                [{ c: c1, r: r1 }, { c: c2, r: r2 }].forEach(t => {
                    const pos = getCellPosition(t.c, t.r);
                    const mesh = new THREE.Mesh(new THREE.PlaneGeometry(SIZE, SIZE), yellowMaterial);
                    mesh.position.set(pos.x, pos.y, 0);
                    scene.add(mesh);
                    const line = new THREE.LineSegments(new THREE.EdgesGeometry(new THREE.PlaneGeometry(SIZE, SIZE)), lineMaterial);
                    line.position.set(pos.x, pos.y, 0);
                    scene.add(line);
                    yellowList.push({ mesh, line, col: t.c, row: t.r });
                });
                yellowPlaced = true;
            }
        }
        initGame();

        // --- 5. インタラクション ---
        const raycaster = new THREE.Raycaster();
        const mouse = new THREE.Vector2();
        let selectedFace = null;
        let highlightMeshes = [];
        let gameLocked = false;

        window.addEventListener('mousemove', (event) => {
            mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
            mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
        });

        window.addEventListener('contextmenu', (event) => {
            if (gameLocked) return;
            event.preventDefault();
            raycaster.setFromCamera(mouse, camera);
            const intersects = raycaster.intersectObjects(netMeshes.map(n => n.mesh));

            if (intersects.length > 0) {
                const clickedMesh = intersects[0].object;
                const target = netMeshes.find(n => n.mesh === clickedMesh);
                if (selectedFace === target) {
                    deselectFace();
                } else {
                    if (selectedFace) deselectFace();
                    selectFace(target);
                }
            } else {
                if (selectedFace) deselectFace();
            }
        });

        window.addEventListener('click', (event) => {
            if (gameLocked || !selectedFace) return;
            raycaster.setFromCamera(mouse, camera);

            const intersects = raycaster.intersectObjects(highlightMeshes);
            if (intersects.length > 0) {
                const hit = intersects[0].object;
                const targetCol = hit.userData.col;
                const targetRow = hit.userData.row;
                const isTeleport = hit.userData.isTeleport;
                moveFace(selectedFace, targetCol, targetRow, isTeleport);
            }
        });

        function selectFace(faceObj) {
            selectedFace = faceObj;
            animateScale(faceObj.mesh, 1.2);
            animateScale(faceObj.line, 1.2);
            faceObj.mesh.renderOrder = 1;
            showValidMoves(faceObj);
        }

        function deselectFace() {
            if (!selectedFace) return;
            animateScale(selectedFace.mesh, 1.0);
            animateScale(selectedFace.line, 1.0);
            selectedFace.mesh.renderOrder = 0;
            selectedFace = null;
            clearHighlights();
        }

        function clearHighlights() {
            highlightMeshes.forEach(m => scene.remove(m));
            highlightMeshes = [];
        }

        function showValidMoves(faceObj) {
            clearHighlights();
            const otherFaces = netMeshes.filter(f => f !== faceObj);
            const candidates = [];
            const isValidPos = (c, r) => c >= 0 && c < GRID_COLS && r >= 0 && r < GRID_ROWS;

            otherFaces.forEach(f => {
                [[0, 1], [0, -1], [1, 0], [-1, 0]].forEach(d => {
                    const nc = f.col + d[0];
                    const nr = f.row + d[1];
                    if (isValidPos(nc, nr)) {
                        const occupiedByNet = netMeshes.some(n => n.col === nc && n.row === nr);
                        const isYellow = yellowList.find(y => y.col === nc && y.row === nr);

                        if (!occupiedByNet && isYellow) {
                            const otherYellow = yellowList.find(y => y !== isYellow);
                            if (otherYellow) {
                                const tc = otherYellow.col;
                                const tr = otherYellow.row;
                                if (!netMeshes.some(n => n.col === tc && n.row === tr)) {
                                    candidates.push({ c: nc, r: nr, isTeleport: true, destC: tc, destR: tr });
                                }
                            }
                        } else if (!occupiedByNet && !isYellow) {
                            if (!candidates.some(cand => cand.c === nc && cand.r === nr)) {
                                candidates.push({ c: nc, r: nr, isTeleport: false });
                            }
                        }
                    }
                });
            });

            candidates.forEach(cand => {
                let trialCells;
                if (cand.isTeleport) {
                    trialCells = otherFaces.map(f => ({ x: f.col, y: f.row }));
                    trialCells.push({ x: cand.destC, y: cand.destR });
                } else {
                    trialCells = otherFaces.map(f => ({ x: f.col, y: f.row }));
                    trialCells.push({ x: cand.c, y: cand.r });
                }

                const sig = normalizeNet(trialCells);
                if (validSignatures.has(sig)) {
                    const pos = getCellPosition(cand.c, cand.r);
                    const mat = cand.isTeleport ? teleportMaterial : sparkleMaterial;
                    const hl = new THREE.Mesh(new THREE.PlaneGeometry(SIZE, SIZE), mat);
                    hl.position.set(pos.x, pos.y, 0.1);
                    hl.userData = { col: cand.c, row: cand.r, isTeleport: cand.isTeleport };
                    scene.add(hl);
                    highlightMeshes.push(hl);
                }
            });
        }

        function moveFace(faceObj, targetCol, targetRow, isTeleport) {
            occupiedCells.delete(`${faceObj.col},${faceObj.row}`);

            let finalC = targetCol;
            let finalR = targetRow;
            const yellowEntry = yellowList.find(y => y.col === targetCol && y.row === targetRow);
            const yellowExit = yellowList.find(y => y !== yellowEntry);

            if (isTeleport && yellowExit) {
                finalC = yellowExit.col;
                finalR = yellowExit.row;
            }

            faceObj.col = finalC;
            faceObj.row = finalR;
            occupiedCells.add(`${finalC},${finalR}`);
            faceObj.mesh.userData.col = finalC;
            faceObj.mesh.userData.row = finalR;

            deselectFace();

            const targetPos = getCellPosition(targetCol, targetRow);

            if (isTeleport) {
                // Phase 1: Spiral Suck In
                animateComplex(faceObj.mesh, {
                    type: 'teleportIn',
                    targetPos: targetPos,
                    duration: 500,
                    onComplete: () => {
                        // Phase 2: Instant Move & Spiral Spit Out
                        const exitPos = getCellPosition(finalC, finalR);
                        faceObj.mesh.position.set(exitPos.x, exitPos.y, faceObj.mesh.position.z);
                        faceObj.line.position.set(exitPos.x, exitPos.y, faceObj.line.position.z);

                        animateComplex(faceObj.mesh, {
                            type: 'teleportOut',
                            targetPos: exitPos,
                            duration: 500,
                            onComplete: triggerVictory
                        });
                        animateComplex(faceObj.line, {
                            type: 'teleportOut',
                            targetPos: exitPos,
                            duration: 500
                        });
                    }
                });
                animateComplex(faceObj.line, {
                    type: 'teleportIn',
                    targetPos: targetPos,
                    duration: 500
                });

            } else {
                const finalPos = getCellPosition(finalC, finalR);
                animateMove(faceObj.mesh, finalPos);
                animateMove(faceObj.line, finalPos);
            }
        }

        // --- Victory ---
        // MOVED DECLARATIONS UP

        function triggerVictory() {
            gameLocked = true;
            document.getElementById('victory').style.display = 'block';
            yellowList.forEach(y => { scene.remove(y.mesh); scene.remove(y.line); });
            yellowList = [];
            netMeshes.forEach(n => { n.mesh.material = faceMaterialVictory; });

            // Start Confetti
            confettiActive = true;
            for (let i = 0; i < 100; i++) {
                const geom = new THREE.PlaneGeometry(0.5, 0.5);
                const mat = new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff, side: THREE.DoubleSide });
                const p = new THREE.Mesh(geom, mat);
                p.position.set(0, 0, 5); // Start center
                // Random velocity
                p.userData = {
                    vx: (Math.random() - 0.5) * 1,
                    vy: (Math.random() - 0.5) * 1 + 0.5, // slightly up
                    vz: (Math.random() - 0.5) * 1,
                    rotx: Math.random() * 0.2,
                    roty: Math.random() * 0.2
                };
                scene.add(p);
                particles.push(p);
            }
        }

        // --- 6. アニメーション ---
        const animations = [];

        function animateScale(obj, targetScale) {
            animations.push({
                obj: obj,
                type: 'scale',
                target: targetScale,
                start: obj.scale.x,
                startTime: Date.now(),
                duration: 200
            });
        }

        function animateMove(obj, targetPos, onComplete) {
            animations.push({
                obj: obj,
                type: 'move',
                target: targetPos,
                startX: obj.position.x,
                startY: obj.position.y,
                startTime: Date.now(),
                duration: 300,
                onComplete: onComplete
            });
        }

        function animateComplex(obj, params) {
            animations.push({
                obj: obj,
                type: params.type,
                target: params.targetPos,
                startParams: {
                    x: obj.position.x, y: obj.position.y,
                    sx: obj.scale.x, rot: obj.rotation.z
                },
                startTime: Date.now(),
                duration: params.duration,
                onComplete: params.onComplete
            });
        }

        function updateAnimations() {
            const now = Date.now();
            for (let i = animations.length - 1; i >= 0; i--) {
                const anim = animations[i];
                const elapsed = now - anim.startTime;
                const progress = Math.min(elapsed / anim.duration, 1);

                const ease = 1 - Math.pow(1 - progress, 3);

                if (anim.type === 'scale') {
                    const s = anim.start + (anim.target - anim.start) * ease;
                    anim.obj.scale.set(s, s, 1);
                } else if (anim.type === 'move') {
                    const x = anim.startX + (anim.target.x - anim.startX) * ease;
                    const y = anim.startY + (anim.target.y - anim.startY) * ease;
                    anim.obj.position.set(x, y, anim.obj.position.z);
                } else if (anim.type === 'teleportIn') {
                    const x = anim.startParams.x + (anim.target.x - anim.startParams.x) * ease;
                    const y = anim.startParams.y + (anim.target.y - anim.startParams.y) * ease;
                    anim.obj.position.set(x, y, anim.obj.position.z);
                    const s = Math.max(0, anim.startParams.sx * (1 - ease));
                    anim.obj.scale.set(s, s, 1);
                    anim.obj.rotation.z = anim.startParams.rot + (4 * Math.PI * ease);
                } else if (anim.type === 'teleportOut') {
                    const s = ease;
                    anim.obj.scale.set(s, s, 1);
                    anim.obj.rotation.z = 4 * Math.PI * (1 - ease);
                }

                if (progress >= 1) {
                    if (anim.type === 'teleportOut') anim.obj.rotation.z = 0;
                    if (anim.onComplete) anim.onComplete();
                    animations.splice(i, 1);
                }
            }
        }

        // --- メインループ ---
        let time = 0;
        function animate() {
            time += 0.05;
            highlightMeshes.forEach((mesh, idx) => {
                mesh.material.opacity = 0.5 + Math.sin(time + idx) * 0.3;
            });
            updateAnimations();

            if (confettiActive) {
                particles.forEach(p => {
                    p.position.x += p.userData.vx;
                    p.position.y += p.userData.vy;
                    p.position.z += p.userData.vz;
                    p.rotation.x += p.userData.rotx;
                    p.rotation.y += p.userData.roty;
                    // Gravity
                    p.userData.vy -= 0.02;
                    // Reset if too low
                    if (p.position.y < -20) {
                        p.position.set(0, 0, 5);
                        p.userData.vy = Math.random() * 1 + 0.5;
                    }
                });

                // Pulse Net
                const scale = 1 + Math.sin(time * 0.2) * 0.05;
                netMeshes.forEach(n => {
                    n.mesh.scale.set(scale, scale, 1);
                    n.line.scale.set(scale, scale, 1);
                });
            }

            renderer.render(scene, camera);
        }
        renderer.setAnimationLoop(animate);

        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.render(scene, camera);
        });
    </script>
</body>

</html>


10×10の升目に、立方体の展開図と黄色の面が二つ表示されます。


一つの面を拡大し、


でてきたマスをクリックで面を移動。


黄色い面が光ったら、そこをクリックで、


EXCELLENT!!!




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

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