Skip to content

Instantly share code, notes, and snippets.

@shogo-hta
Created October 20, 2010 13:03
Show Gist options
  • Select an option

  • Save shogo-hta/636371 to your computer and use it in GitHub Desktop.

Select an option

Save shogo-hta/636371 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<title>game</title>
<style>
#C{
position:relative;
}
.cell{
position:absolute;
background:#fff;
border:1px solid #999;
}
#O{
background:black;
color:white;
text-align:right;
position:fixed;
top:0px;
right:0px;
font-size:xx-large;
}
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
<script src="http://cdn.socket.io/stable/socket.io.js"></script>
</head>
<body>
<div id="C"></div>
<div id="O">0</div>
<script>
/*
* リアルタイムWebハッカソン(http://atnd.org/events/8626)で書いたコードのクライアント側のコードです
*/
var c = document.getElementById('C');
var o = document.getElementById('O');
// マス目の数
var count = 5;
// マスの大きさ
var size = 50;
// 自機数
var own_limit = 5;
// ポイント
var mypoint = 0;
var cells = [];
var my_cells = [];
// 自機色
var color = 'blue';
// デフォルトカラー
var def_color = 'white';
var to_json = JSON.stringify;
// Socketに接続
var socket = new io.Socket('172.16.128.121',{port:3000});
socket.connect();
socket.on('message', function(data) {
//console.log(data);
data = JSON.parse(data);
var x = data.x;
var y = data.y;
var id = data.id;
if (data.color){
// 相手が埋めたとき、1ポイント
cells[y][x].flip(data.color, 1);
} else {
// 相手が空いたとき
cells[y][x].flip(def_color, 0);
}
// ラインが揃ってるかチェック
check_line();
});
function Cell(x, y){
var that = this;
this.x = x;
this.y = y;
var node = this.node = document.createElement('div');
node.className = 'cell';
node.addEventListener('click',function(){
// すでにチェック済みのセルなら終了
for(var i = 0, len = my_cells.length;i < len;i++){
if (my_cells[i] === that) return;
}
// セルを反転、自機は2ポイント
that.flip(color, 2);
// 自機を配信
socket.send(to_json({x:x,y:y,color:color}));
my_cells.push(that);
if(own_limit < my_cells.length){
// 自機の上限を超えていたら古いものを削除
var d = my_cells.shift();
d.flip(def_color);
socket.send(to_json({x:d.x,y:d.y}));
}
check_line();
},false);
node.style.left = x * size + 'px';
node.style.top = y * size + 'px';
node.style.width = node.style.height = size+'px';
c.appendChild(node);
}
Cell.prototype.flip = function(color, flipPoint){
this.node.style.backgroundColor = color;
this.fliped = flipPoint;
};
Cell.prototype.toString = function(){
return this.fliped || '';
};
Cell.prototype.valueOf = function(){
return this.fliped || 0;
};
function check_line(){
var ds;
for (var i = 0, ok;i < count;i++){
// toStringを定義しているので、joinしたときにflipedの値が連結される
var v = cells[i].join('');
var c = vcells[i].join('');
if (c.length === count || v.length === count){
if (c.length === count){
ok = c;
ds = vcells[i];
} else {
ok = v;
ds = cells[i];
}
if(ok){
// 一列揃った場合はセルをデフォルトに戻し、自機配列からそのセルを削除する
for(var k = 0;k < ds.length;k++){
var d = ds[k];
d.flip(def_color, 0);
var m = my_cells.length;
while(m--){
var co = my_cells[m];
if (co === d){
my_cells.splice(m, 1);
}
}
}
}
break;
}
}
if (ok){
// okにはポイントが入っているので、分割して各値を加算する
for (var i= 0, a=ok.split(''),l=a.length;i < l;i++){
mypoint += +a[i];
}
o.innerHTML = mypoint;
}
}
var vcells = [];
(function(){
for (var y = 0;y < count;y++){
var col = [];
for(var x = 0;x < count;x++){
col.push(new Cell(x, y));
}
cells.push(col);
}
for (var x = 0;x < count;x++){
var col = [];
for(var y = 0;y < count;y++){
col.push(cells[y][x]);
}
vcells.push(col);
}
})();
</script>
</body>
</html>
@mstafajomaa3-web

Copy link
Copy Markdown
<title>EA-FC Ultimate Champions</title> <style> * { box-sizing: border-box; margin: 0; padding: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; user-select: none; }
    body {
        background: #0f172a;
        color: #fff;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        min-height: 100vh;
        overflow: hidden;
    }

    /* DYNAMIC INTRO SCREEN */
    #intro-screen {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: linear-gradient(135deg, #020617 0%, #1e1b4b 50%, #09090b 100%);
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        z-index: 100;
        transition: opacity 1s ease;
    }

    .logo-box {
        text-align: center;
        animation: pulse 2s infinite alternate;
    }

    .logo-box h1 {
        font-size: 4rem;
        background: linear-gradient(45deg, #38bdf8, #818cf8, #c084fc);
        -webkit-background-clip: text;
        -webkit-text-fill-color: transparent;
        text-shadow: 0 0 30px rgba(99, 102, 241, 0.5);
        margin-bottom: 10px;
    }

    .logo-box p {
        font-size: 1.5rem;
        color: #94a3b8;
        letter-spacing: 2px;
    }

    .btn-start {
        margin-top: 40px;
        padding: 15px 50px;
        font-size: 1.5rem;
        font-weight: bold;
        color: #fff;
        background: linear-gradient(90deg, #10b981, #059669);
        border: none;
        border-radius: 50px;
        cursor: pointer;
        box-shadow: 0 0 20px rgba(16, 185, 129, 0.4);
        transition: all 0.3s ease;
    }

    .btn-start:hover {
        transform: scale(1.1);
        box-shadow: 0 0 35px rgba(16, 185, 129, 0.8);
    }

    /* MAIN MENU SCREEN */
    #menu-screen {
        display: none;
        flex-direction: column;
        align-items: center;
        background: rgba(15, 23, 42, 0.95);
        padding: 30px;
        border-radius: 20px;
        box-shadow: 0 0 40px rgba(0,0,0,0.8);
        border: 1px solid #334155;
        width: 450px;
        z-index: 90;
    }

    .menu-group {
        width: 100%;
        margin-bottom: 20px;
    }

    .menu-group label {
        display: block;
        margin-bottom: 8px;
        color: #cbd5e1;
        font-size: 1.1rem;
    }

    .menu-group select, .menu-group input {
        width: 100%;
        padding: 12px;
        border-radius: 10px;
        border: 1px solid #475569;
        background: #1e293b;
        color: #fff;
        font-size: 1rem;
        outline: none;
    }

    /* GAME UI & SCOREBOARD */
    #game-container {
        display: none;
        flex-direction: column;
        align-items: center;
        position: relative;
    }

    #scoreboard {
        width: 900px;
        background: #1e293b;
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 10px 30px;
        border-top-left-radius: 15px;
        border-top-right-radius: 15px;
        border-bottom: 3px solid #38bdf8;
        font-weight: bold;
    }

    .team-score {
        display: flex;
        align-items: center;
        gap: 15px;
        font-size: 1.5rem;
    }

    .score-badge {
        background: #0f172a;
        padding: 5px 15px;
        border-radius: 8px;
        font-size: 2rem;
        color: #38bdf8;
    }

    #timer {
        font-size: 1.8rem;
        background: #09090b;
        padding: 5px 20px;
        border-radius: 20px;
        color: #f59e0b;
    }

    canvas {
        background: #15803d;
        border-bottom-left-radius: 15px;
        border-bottom-right-radius: 15px;
        box-shadow: 0 10px 30px rgba(0,0,0,0.5);
    }

    /* OVERLAYS & NOTIFICATIONS */
    #goal-overlay {
        position: absolute;
        top: 45%;
        left: 50%;
        transform: translate(-50%, -50%) scale(0);
        font-size: 5rem;
        font-weight: 900;
        color: #facc15;
        text-shadow: 0 0 20px #000, 0 0 40px #f59e0b;
        pointer-events: none;
        transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
        z-index: 50;
    }

    #controls-hint {
        margin-top: 15px;
        color: #94a3b8;
        font-size: 0.9rem;
        text-align: center;
    }

    @keyframes pulse {
        0% { transform: scale(0.98); }
        100% { transform: scale(1.02); }
    }
</style>
<!-- 1. INTRO SCREEN -->
<div id="intro-screen">
    <div class="logo-box">
        <h1>E-FC CHAMPIONS 2026</h1>
        <p>أساطير كرة القدم الحقيقية</p>
    </div>
    <button class="btn-start" onclick="goToMenu()">اضغط للبدء ⚽</button>
</div>

<!-- 2. MAIN MENU -->
<div id="menu-screen">
    <h2 style="margin-bottom: 25px; color: #38bdf8;">إعدادات المباراة</h2>
    
    <div class="menu-group">
        <label>وضع اللعب:</label>
        <select id="game-mode">
            <option value="pve">لاعب ضد الكمبيوتر (1P vs AI)</option>
            <option value="pvp">لاعب ضد لاعب (1P vs 2P)</option>
        </select>
    </div>

    <div class="menu-group">
        <label>مستوى الصعوبة (للكمبيوتر):</label>
        <select id="difficulty">
            <option value="easy">سهل (Easy)</option>
            <option value="medium" selected>متوسط (Medium)</option>
            <option value="hard">محترف (Hard)</option>
        </select>
    </div>

    <div class="menu-group">
        <label>مدة المباراة (بالدقائق):</label>
        <select id="match-time">
            <option value="60">دقيقة واحدة</option>
            <option value="120" selected>دقيقتان</option>
            <option value="180">3 دقائق</option>
        </select>
    </div>

    <button class="btn-start" style="width: 100%; margin-top: 10px;" onclick="startGame()">انطلاق المباراة 🚀</button>
</div>

<!-- 3. GAME FIELD & UI -->
<div id="game-container">
    <div id="scoreboard">
        <div class="team-score">
            <span style="color: #ef4444;">الفريق الأحمر</span>
            <span id="score1" class="score-badge">0</span>
        </div>
        <div id="timer">02:00</div>
        <div class="team-score">
            <span id="score2" class="score-badge">0</span>
            <span style="color: #3b82f6;">الفريق الأزرق</span>
        </div>
    </div>

    <canvas id="pitch" width="900" height="500"></canvas>
    <div id="goal-overlay">GOAL!!! ⚽</div>

    <div id="controls-hint">
        <b>اللاعب 1 (الأحمر):</b> W, A, S, D للحركة | <b>Space</b> للتسديد والقوة <br>
        <b>اللاعب 2 / الكمبيوتر (الأزرق):</b> الأسهم للحركة | <b>Enter / Num 0</b> للتسديد
    </div>
</div>

<script>
    // Web Audio API Sound Effects Generator
    const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
    function playSound(type) {
        if (audioCtx.state === 'suspended') audioCtx.resume();
        const osc = audioCtx.createOscillator();
        const gain = audioCtx.createGain();
        osc.connect(gain);
        gain.connect(audioCtx.destination);

        if (type === 'kick') {
            osc.type = 'triangle';
            osc.frequency.setValueAtTime(120, audioCtx.currentTime);
            osc.frequency.exponentialRampToValueAtTime(30, audioCtx.currentTime + 0.15);
            gain.gain.setValueAtTime(1, audioCtx.currentTime);
            gain.gain.linearRampToValueAtTime(0, audioCtx.currentTime + 0.15);
            osc.start(); osc.stop(audioCtx.currentTime + 0.15);
        } else if (type === 'whistle') {
            osc.type = 'sine';
            osc.frequency.setValueAtTime(2500, audioCtx.currentTime);
            osc.frequency.setValueAtTime(3000, audioCtx.currentTime + 0.1);
            gain.gain.setValueAtTime(0.5, audioCtx.currentTime);
            gain.gain.linearRampToValueAtTime(0, audioCtx.currentTime + 0.4);
            osc.start(); osc.stop(audioCtx.currentTime + 0.4);
        } else if (type === 'goal') {
            osc.type = 'sawtooth';
            osc.frequency.setValueAtTime(400, audioCtx.currentTime);
            osc.frequency.linearRampToValueAtTime(800, audioCtx.currentTime + 0.5);
            gain.gain.setValueAtTime(0.7, audioCtx.currentTime);
            gain.gain.linearRampToValueAtTime(0, audioCtx.currentTime + 0.8);
            osc.start(); osc.stop(audioCtx.currentTime + 0.8);
        }
    }

    // NAVIGATION
    function goToMenu() {
        playSound('whistle');
        document.getElementById('intro-screen').style.opacity = '0';
        setTimeout(() => {
            document.getElementById('intro-screen').style.display = 'none';
            document.getElementById('menu-screen').style.display = 'flex';
        }, 500);
    }

    // GAME LOGIC
    const canvas = document.getElementById('pitch');
    const ctx = canvas.getContext('2d');

    let gameMode = 'pve';
    let difficulty = 'medium';
    let timeRemaining = 120;
    let timerInterval = null;
    let isGameRunning = false;

    let score1 = 0;
    let score2 = 0;

    const keys = {};
    window.addEventListener('keydown', e => keys[e.code] = true);
    window.addEventListener('keyup', e => keys[e.code] = false);

    // Entities
    const p1 = { x: 200, y: 250, radius: 18, color: '#ef4444', speed: 4, vx: 0, vy: 0 };
    const p2 = { x: 700, y: 250, radius: 18, color: '#3b82f6', speed: 4, vx: 0, vy: 0 };
    const ball = { x: 450, y: 250, radius: 10, color: '#ffffff', vx: 0, vy: 0, friction: 0.981 };

    function startGame() {
        gameMode = document.getElementById('game-mode').value;
        difficulty = document.getElementById('difficulty').value;
        timeRemaining = parseInt(document.getElementById('match-time').value);

        document.getElementById('menu-screen').style.display = 'none';
        document.getElementById('game-container').style.display = 'flex';

        score1 = 0;
        score2 = 0;
        updateScoreboard();
        resetPositions();

        isGameRunning = true;
        playSound('whistle');

        clearInterval(timerInterval);
        timerInterval = setInterval(() => {
            if (!isGameRunning) return;
            timeRemaining--;
            let mins = Math.floor(timeRemaining / 60).toString().padStart(2, '0');
            let secs = (timeRemaining % 60).toString().padStart(2, '0');
            document.getElementById('timer').innerText = `${mins}:${secs}`;

            if (timeRemaining <= 0) {
                endGame();
            }
        }, 1000);

        requestAnimationFrame(gameLoop);
    }

    function resetPositions() {
        p1.x = 250; p1.y = canvas.height / 2; p1.vx = 0; p1.vy = 0;
        p2.x = 650; p2.y = canvas.height / 2; p2.vx = 0; p2.vy = 0;
        ball.x = canvas.width / 2; ball.y = canvas.height / 2; ball.vx = 0; ball.vy = 0;
    }

    function showGoal() {
        playSound('goal');
        const overlay = document.getElementById('goal-overlay');
        overlay.style.transform = 'translate(-50%, -50%) scale(1)';
        setTimeout(() => {
            overlay.style.transform = 'translate(-50%, -50%) scale(0)';
            resetPositions();
        }, 1500);
    }

    function updateScoreboard() {
        document.getElementById('score1').innerText = score1;
        document.getElementById('score2').innerText = score2;
    }

    function endGame() {
        isGameRunning = false;
        clearInterval(timerInterval);
        playSound('whistle');
        let winner = "تعادل!";
        if (score1 > score2) winner = "الفريق الأحمر هو الفائز! 🎉";
        else if (score2 > score1) winner = "الفريق الأزرق هو الفائز! 🎉";
        
        alert(`انتهت المباراة!\nالنتيجة: ${score1} - ${score2}\n${winner}`);
        document.getElementById('game-container').style.display = 'none';
        document.getElementById('menu-screen').style.display = 'flex';
    }

    // PLAYER CONTROLS & AI
    function handleInput() {
        // Player 1 (WASD)
        p1.vx = 0; p1.vy = 0;
        if (keys['KeyW'] || keys['Keyw']) p1.vy = -p1.speed;
        if (keys['KeyS'] || keys['Keys']) p1.vy = p1.speed;
        if (keys['KeyA'] || keys['Keya']) p1.vx = -p1.speed;
        if (keys['KeyD'] || keys['Keyd']) p1.vx = p1.speed;

        // Kick action P1
        if (keys['Space']) {
            let dist = Math.hypot(ball.x - p1.x, ball.y - p1.y);
            if (dist < p1.radius + ball.radius + 10) {
                let angle = Math.atan2(ball.y - p1.y, ball.x - p1.x);
                ball.vx = Math.cos(angle) * 12;
                ball.vy = Math.sin(angle) * 12;
                playSound('kick');
            }
        }

        // Player 2 or AI
        if (gameMode === 'pvp') {
            p2.vx = 0; p2.vy = 0;
            if (keys['ArrowUp']) p2.vy = -p2.speed;
            if (keys['ArrowDown']) p2.vy = p2.speed;
            if (keys['ArrowLeft']) p2.vx = -p2.speed;
            if (keys['ArrowRight']) p2.vx = p2.speed;

            if (keys['Enter'] || keys['Numpad0']) {
                let dist = Math.hypot(ball.x - p2.x, ball.y - p2.y);
                if (dist < p2.radius + ball.radius + 10) {
                    let angle = Math.atan2(ball.y - p2.y, ball.x - p2.x);
                    ball.vx = Math.cos(angle) * 12;
                    ball.vy = Math.sin(angle) * 12;
                    playSound('kick');
                }
            }
        } else {
            // AI Logic
            let aiSpeed = difficulty === 'easy' ? 2 : (difficulty === 'medium' ? 3.2 : 4.5);
            let targetX = ball.x;
            let targetY = ball.y;

            let dx = targetX - p2.x;
            let dy = targetY - p2.y;
            let dist = Math.hypot(dx, dy);

            if (dist > 5) {
                p2.vx = (dx / dist) * aiSpeed;
                p2.vy = (dy / dist) * aiSpeed;
            }

            // AI Kicking
            if (dist < p2.radius + ball.radius + 5) {
                let angle = Math.atan2(canvas.height / 2 - p2.y, 0 - p2.x); // Shoot towards P1 Goal
                ball.vx = Math.cos(angle) * (difficulty === 'hard' ? 13 : 10);
                ball.vy = Math.sin(angle) * (difficulty === 'hard' ? 13 : 10);
                playSound('kick');
            }
        }

        // Move players
        p1.x += p1.vx; p1.y += p1.vy;
        p2.x += p2.vx; p2.y += p2.vy;

        // Boundaries
        [p1, p2].forEach(p => {
            p.x = Math.max(p.radius, Math.min(canvas.width - p.radius, p.x));
            p.y = Math.max(p.radius, Math.min(canvas.height - p.radius, p.y));
        });
    }

    function updatePhysics() {
        // Ball motion
        ball.x += ball.vx;
        ball.y += ball.vy;
        ball.vx *= ball.friction;
        ball.vy *= ball.friction;

        // Ball & Player Collision
        [p1, p2].forEach(p => {
            let dx = ball.x - p.x;
            let dy = ball.y - p.y;
            let dist = Math.hypot(dx, dy);
            if (dist < p.radius + ball.radius) {
                let angle = Math.atan2(dy, dx);
                let force = 5;
                ball.vx = Math.cos(angle) * force + p.vx * 0.5;
                ball.vy = Math.sin(angle) * force + p.vy * 0.5;
                playSound('kick');
            }
        });

        // Ball Pitch Wall Collision
        if (ball.y - ball.radius <= 0 || ball.y + ball.radius >= canvas.height) {
            ball.vy *= -1;
        }

        // Goal Posts Check (Goal Height: 180px to 320px)
        const goalTop = 170;
        const goalBottom = 330;

        if (ball.x - ball.radius <= 0) {
            if (ball.y > goalTop && ball.y < goalBottom) {
                score2++;
                updateScoreboard();
                showGoal();
            } else {
                ball.vx *= -1;
                ball.x = ball.radius;
            }
        }

        if (ball.x + ball.radius >= canvas.width) {
            if (ball.y > goalTop && ball.y < goalBottom) {
                score1++;
                updateScoreboard();
                showGoal();
            } else {
                ball.vx *= -1;
                ball.x = canvas.width - ball.radius;
            }
        }
    }

    function render() {
        // Draw Field
        ctx.fillStyle = '#15803d';
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        // Field Lines
        ctx.strokeStyle = '#ffffff';
        ctx.lineWidth = 4;

        // Outer Line
        ctx.strokeRect(10, 10, canvas.width - 20, canvas.height - 20);
        
        // Center Line & Circle
        ctx.beginPath();
        ctx.moveTo(canvas.width / 2, 10);
        ctx.lineTo(canvas.width / 2, canvas.height - 10);
        ctx.stroke();

        ctx.beginPath();
        ctx.arc(canvas.width / 2, canvas.height / 2, 70, 0, Math.PI * 2);
        ctx.stroke();

        // Goals (Left & Right)
        ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
        ctx.fillRect(0, 170, 15, 160);
        ctx.fillRect(canvas.width - 15, 170, 15, 160);

        // Draw Players
        [p1, p2].forEach(p => {
            ctx.beginPath();
            ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
            ctx.fillStyle = p.color;
            ctx.fill();
            ctx.lineWidth = 3;
            ctx.strokeStyle = '#fff';
            ctx.stroke();
        });

        // Draw Ball
        ctx.beginPath();
        ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
        ctx.fillStyle = ball.color;
        ctx.fill();
        ctx.lineWidth = 2;
        ctx.strokeStyle = '#000';
        ctx.stroke();
    }

    function gameLoop() {
        if (!isGameRunning) return;
        handleInput();
        updatePhysics();
        render();
        requestAnimationFrame(gameLoop);
    }
</script>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment