ブラウザゲームとAIのゲームプログラムとゲーム制作ツールの無料配布ページ

ゲーム制作で役に立つ無料ツールです

フォトショップのように背景切り抜き&複数分割カットができます

雷・サンダーの魔法・電流エフェクト

電流エフェクト調整

画面をドラッグ、またはクリックすると電流の始点・終点を変更できます
プログラムのコードを表示する
<div class="wp-lightning-simulator-container">
    <div id="wp-lightning-control-panel">
        <h2>電流エフェクト調整</h2>
        
        <div class="wp-control-group">
            <label for="wp-thickness">太さ (Thickness)<span id="wp-val-thickness" class="wp-value-display">2.5px</span></label>
            <input type="range" id="wp-thickness" min="1" max="10" step="0.5" value="2.5">
        </div>

        <div class="wp-control-group">
            <label for="wp-brightness">明るさ (Brightness)<span id="wp-val-brightness" class="wp-value-display">120%</span></label>
            <input type="range" id="wp-brightness" min="50" max="200" step="5" value="120">
        </div>

        <div class="wp-control-group">
            <label for="wp-hue">色み (Color Hue)<span id="wp-val-hue" class="wp-value-display">195°</span></label>
            <input type="range" id="wp-hue" min="0" max="360" step="1" value="195">
        </div>

        <div class="wp-control-group">
            <label for="wp-diffusion">拡散 (Chaos / Diffusion)<span id="wp-val-diffusion" class="wp-value-display">35</span></label>
            <input type="range" id="wp-diffusion" min="5" max="80" step="1" value="35">
        </div>
    </div>

    <div id="wp-lightning-instructions">画面をドラッグ、またはクリックすると電流の始点・終点を変更できます</div>

    <canvas id="wp-lightningCanvas"></canvas>
</div>

<style>
    /* 記事の幅に合わせて全画面表示、または指定エリアに収めるラッパー */
    .wp-lightning-simulator-container {
        position: relative;
        width: 100%;
        height: 600px; /* WordPress内で扱いやすいように固定高に調整(100vhから変更) */
        background-color: #08090c;
        color: #e0e6ed;
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
        overflow: hidden;
        border-radius: 12px;
        box-shadow: 0 10px 30px rgba(0,0,0,0.3);
        margin: 2em 0;
    }

    /* 他の要素のユーザー選択を邪魔しないようコンテナ内のみ制限 */
    .wp-lightning-simulator-container * {
        box-sizing: border-box;
        user-select: none;
    }

    #wp-lightning-control-panel {
        position: absolute;
        top: 20px;
        left: 20px;
        width: 280px;
        background: rgba(15, 22, 36, 0.85);
        backdrop-filter: blur(12px);
        -webkit-backdrop-filter: blur(12px);
        border: 1px solid rgba(255, 255, 255, 0.1);
        border-radius: 14px;
        padding: 20px;
        box-shadow: 0 15px 35px rgba(0, 0, 0, 0.5);
        z-index: 10;
    }

    #wp-lightning-control-panel h2 {
        font-size: 16px;
        font-weight: 600;
        margin: 0 0 15px 0 !important; /* WPテーマのスタイルを上書き */
        padding: 0 0 8px 0 !important;
        color: #fff !important;
        letter-spacing: 0.5px;
        border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    }

    .wp-control-group {
        margin-bottom: 14px;
    }

    .wp-control-group:last-child {
        margin-bottom: 0;
    }

    .wp-control-group label {
        display: flex;
        justify-content: space-between;
        font-size: 12px;
        color: #a0aec0;
        margin-bottom: 6px;
        font-weight: 500;
    }

    .wp-value-display {
        color: #63b3ed;
        font-family: monospace;
        font-weight: bold;
    }

    .wp-control-group input[type="range"] {
        -webkit-appearance: none;
        appearance: none;
        width: 100%;
        height: 6px;
        background: #2d3748;
        border-radius: 3px;
        outline: none;
        margin: 0;
    }

    .wp-control-group input[type="range"]::-webkit-slider-thumb {
        -webkit-appearance: none;
        appearance: none;
        width: 16px;
        height: 16px;
        border-radius: 50%;
        background: #fff;
        cursor: pointer;
        box-shadow: 0 0 6px rgba(0,0,0,0.5);
        transition: transform 0.1s, background-color 0.1s;
    }

    .wp-control-group input[type="range"]::-webkit-slider-thumb:hover {
        transform: scale(1.2);
        background: #63b3ed;
    }

    #wp-lightningCanvas {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        z-index: 1;
        cursor: crosshair;
        display: block;
    }

    #wp-lightning-instructions {
        position: absolute;
        bottom: 15px;
        right: 15px;
        color: rgba(255, 255, 255, 0.4);
        font-size: 11px;
        pointer-events: none;
        text-align: right;
        z-index: 10;
    }
</style>

<script>
(function() {
    // WordPress内での競合を防ぐため、即時関数(IIFE)でスコープを隔離
    const container = document.querySelector('.wp-lightning-simulator-container');
    const canvas = document.getElementById('wp-lightningCanvas');
    if (!canvas) return;
    const ctx = canvas.getContext('2d');

    const sliders = {
        thickness: document.getElementById('wp-thickness'),
        brightness: document.getElementById('wp-brightness'),
        hue: document.getElementById('wp-hue'),
        diffusion: document.getElementById('wp-diffusion')
    };

    const displays = {
        thickness: document.getElementById('wp-val-thickness'),
        brightness: document.getElementById('wp-val-brightness'),
        hue: document.getElementById('wp-val-hue'),
        diffusion: document.getElementById('wp-val-diffusion')
    };

    let params = {
        thickness: parseFloat(sliders.thickness.value),
        brightness: parseInt(sliders.brightness.value),
        hue: parseInt(sliders.hue.value),
        diffusion: parseInt(sliders.diffusion.value)
    };

    function updateParams() {
        params.thickness = parseFloat(sliders.thickness.value);
        params.brightness = parseInt(sliders.brightness.value);
        params.hue = parseInt(sliders.hue.value);
        params.diffusion = parseInt(sliders.diffusion.value);

        displays.thickness.textContent = params.thickness.toFixed(1) + 'px';
        displays.brightness.textContent = params.brightness + '%';
        displays.hue.textContent = params.hue + '°';
        displays.diffusion.textContent = params.diffusion;
    }

    Object.keys(sliders).forEach(key => {
        sliders[key].addEventListener('input', updateParams);
    });

    function resizeCanvas() {
        // 全画面(window)ではなく、親コンテナのサイズに合わせる
        canvas.width = container.clientWidth;
        canvas.height = container.clientHeight;
    }
    window.addEventListener('resize', resizeCanvas);
    resizeCanvas();

    let startPoint = { x: canvas.width * 0.25, y: canvas.height * 0.5 };
    let endPoint = { x: canvas.width * 0.75, y: canvas.height * 0.5 };
    let isDragging = false;

    function getEventPos(e) {
        const rect = canvas.getBoundingClientRect();
        const clientX = e.touches ? e.touches[0].clientX : e.clientX;
        const clientY = e.touches ? e.touches[0].clientY : e.clientY;
        // スクロールや親要素の位置ズレを補正
        return { 
            x: clientX - rect.left, 
            y: clientY - rect.top 
        };
    }

    function handleStart(e) {
        if (e.target.closest('#wp-lightning-control-panel')) return;
        isDragging = true;
        const pos = getEventPos(e);
        startPoint = pos;
        endPoint = pos;
    }

    function handleMove(e) {
        if (!isDragging) return;
        const pos = getEventPos(e);
        endPoint = pos;
    }

    function handleEnd() {
        isDragging = false;
    }

    // イベントリスナーをwindowではなくcanvas(またはコンテナ)に絞ることで誤作動を防ぐ
    canvas.addEventListener('mousedown', handleStart);
    window.addEventListener('mousemove', handleMove);
    window.addEventListener('mouseup', handleEnd);

    canvas.addEventListener('touchstart', handleStart, { passive: true });
    window.addEventListener('touchmove', handleMove, { passive: true });
    window.addEventListener('touchend', handleEnd);

    window.addEventListener('resize', () => {
        if (!isDragging) {
            startPoint = { x: canvas.width * 0.25, y: canvas.height * 0.5 };
            endPoint = { x: canvas.width * 0.75, y: canvas.height * 0.5 };
        }
    });

    function createLightningPath(startX, startY, endX, endY, displace, minSegmentLength) {
        const dx = endX - startX;
        const dy = endY - startY;
        const distance = Math.sqrt(dx * dx + dy * dy);

        if (distance < minSegmentLength) {
            return [{ x: startX, y: startY }, { x: endX, y: endY }];
        }

        const midX = (startX + endX) / 2;
        const midY = (startY + endY) / 2;

        const nx = -dy / distance;
        const ny = dx / distance;
        const offset = (Math.random() - 0.5) * displace;

        const actualMidX = midX + nx * offset;
        const actualMidY = midY + ny * offset;

        const nextDisplace = displace * 0.52;
        const leftPath = createLightningPath(startX, startY, actualMidX, actualMidY, nextDisplace, minSegmentLength);
        const rightPath = createLightningPath(actualMidX, actualMidY, endX, endY, nextDisplace, minSegmentLength);

        return leftPath.concat(rightPath.slice(1));
    }

    let animationFrameId;
    function animate() {
        ctx.fillStyle = 'rgba(8, 9, 12, 0.4)';
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        let currentEnd = { ...endPoint };
        if (startPoint.x === endPoint.x && startPoint.y === endPoint.y) {
            currentEnd.x += 0.1; 
        }

        const pathsToDraw = [];
        const mainPath = createLightningPath(startPoint.x, startPoint.y, currentEnd.x, currentEnd.y, params.diffusion * 6, 10);
        pathsToDraw.push({ segments: mainPath, type: 'main' });

        if (mainPath.length > 4 && Math.random() < 0.6) {
            const branchIdx = Math.floor(Math.random() * (mainPath.length - 2)) + 1;
            const bStart = mainPath[branchIdx];
            const angle = (Math.random() - 0.5) * Math.PI / 2;
            const length = params.diffusion * (2 + Math.random() * 4);
            
            const bEndX = bStart.x + Math.sin(angle) * length;
            const bEndY = bStart.y + Math.cos(angle) * length;
            
            const branchPath = createLightningPath(bStart.x, bStart.y, bEndX, bEndY, params.diffusion * 2, 8);
            pathsToDraw.push({ segments: branchPath, type: 'branch' });
        }

        const baseHue = params.hue;
        const glowIntensity = params.brightness / 100;

        pathsToDraw.forEach(pathData => {
            const points = pathData.segments;
            const isMain = pathData.type === 'main';

            ctx.lineCap = 'round';
            ctx.lineJoin = 'round';

            // 1. 拡散グロー
            ctx.shadowBlur = 40 * glowIntensity;
            ctx.shadowColor = `hsl(${baseHue}, 100%, 50%)`;
            ctx.strokeStyle = `rgba(${isMain ? 20 : 10}, 80, 255, ${0.05 * glowIntensity})`;
            ctx.lineWidth = params.thickness * (isMain ? 8 : 4);
            
            ctx.beginPath();
            ctx.moveTo(points[0].x, points[0].y);
            for (let i = 1; i < points.length; i++) { ctx.lineTo(points[i].x, points[i].y); }
            ctx.stroke();

            // 2. コアグロー
            ctx.shadowBlur = 15 * glowIntensity;
            ctx.shadowColor = `hsl(${baseHue}, 100%, 60%)`;
            ctx.strokeStyle = `hsl(${baseHue}, 100%, ${65 * glowIntensity}%)`;
            ctx.lineWidth = params.thickness * (isMain ? 3 : 1.5);
            
            ctx.beginPath();
            ctx.moveTo(points[0].x, points[0].y);
            for (let i = 1; i < points.length; i++) { ctx.lineTo(points[i].x, points[i].y); }
            ctx.stroke();

            // 3. ホワイトコア
            ctx.shadowBlur = 4 * glowIntensity;
            ctx.shadowColor = '#fff';
            ctx.strokeStyle = `rgba(255, 255, 255, ${0.9 * Math.min(1, glowIntensity)})`;
            ctx.lineWidth = params.thickness * (isMain ? 1 : 0.5);
            
            ctx.beginPath();
            ctx.moveTo(points[0].x, points[0].y);
            for (let i = 1; i < points.length; i++) { ctx.lineTo(points[i].x, points[i].y); }
            ctx.stroke();
        });

        if (Math.random() < 0.04) {
            ctx.fillStyle = `hsla(${baseHue}, 100%, 50%, ${0.02 * glowIntensity})`;
            ctx.fillRect(0, 0, canvas.width, canvas.height);
        }

        animationFrameId = requestAnimationFrame(animate);
    }

    animate();
})();
</script>

AIになかなか伝わらない雷・サンダーエフェクトのJavascriptです

これを添付すればAIもこれを元につくることができます

ゲーム制作記事で紹介したブラウザゲームです

ゲーム制作記事で紹介した&このページのブラウザゲームのプログラムです

コメント