🎮ArcadeLab

勇者团子传

by ElectricFlare57
163 lines5.8 KB
▶ Play
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
<title>勇者团子传</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{background:#222;display:flex;justify-content:center;align-items:center;height:100vh;overflow:hidden}
canvas{background:#fff8e8;border-radius:8px;max-width:100%;max-height:100vh}
#ui{position:absolute;top:10px;left:10px;color:#333;font-weight:bold;font-size:18px}
#upgrade{position:absolute;display:none;gap:12px;padding:16px;background:rgba(255,255,255,0.95);border-radius:12px}
.upg-btn{padding:12px 16px;border:none;border-radius:8px;background:#ffd6dd;color:#333;font-size:15px}
</style>
</head>
<body>
<canvas id="game"></canvas>
<div id="ui">❤️生命:<span id="hp">100</span> | ⭐等级:<span id="lv">1</span></div>
<div id="upgrade"></div>
<script>
const canvas=document.getElementById("game");
const ctx=canvas.getContext("2d");
canvas.width=800;canvas.height=600;
const uiHp=document.getElementById("hp");
const uiLv=document.getElementById("lv");
const upgradeBox=document.getElementById("upgrade");
let gameRun=true;
//玩家
let p={x:400,y:300,r:22,hp:100,maxHp:100,atk:12,spd:4,exp:0,level:1,expNeed:40};
let bullets=[];
let enemies=[];
let buffs=[];
let keys={};
let lastShot=0,cd=350;
//强化池
const upgList=[
    {name:"团子弹追踪",desc:"子弹自动追踪敌人",eff:()=>{p.track=true}},
    {name:"攻击+4",desc:"提升攻击力",eff:()=>{p.atk+=4}},
    {name:"移速+0.8",desc:"跑的更快",eff:()=>{p.spd+=0.8}},
    {name:"冷却缩减",desc:"发射间隔变短",eff:()=>{cd=Math.max(120,cd-60)}},
    {name:"生命+25",desc:"增加最大血量",eff:()=>{p.maxHp+=25;p.hp+=25}},
    {name:"多重弹",desc:"一次发射2颗子弹",eff:()=>{p.multi=2}}
];
//生成敌人
function spawnEnemy(){
    const side=Math.floor(Math.random()*4);
    let x,y;
    if(side===0){x=Math.random()*canvas.width;y=-30}
    else if(side===1){x=canvas.width+30;y=Math.random()*canvas.height}
    else if(side===2){x=Math.random()*canvas.width;y=canvas.height+30}
    else{x=-30;y=Math.random()*canvas.height}
    enemies.push({x,y,r:18,hp:22+p.level*4,spd:1.4,color:"#993322"})
}
//升级弹窗
function showUpgrade(){
    gameRun=false;
    upgradeBox.style.display="flex";
    const shuffled=[...upgList].sort(()=>Math.random()-0.5).slice(0,3);
    upgradeBox.innerHTML="";
    shuffled.forEach(u=>{
        const btn=document.createElement("button");
        btn.className="upg-btn";
        btn.innerText=u.name+"\n"+u.desc;
        btn.onclick=()=>{
            u.eff();
            upgradeBox.style.display="none";
            gameRun=true;
        }
        upgradeBox.appendChild(btn);
    })
}
//射击
function shoot(now){
    if(now-lastShot<cd)return;
    lastShot=now;
    let cnt=p.multi||1;
    for(let i=0;i<cnt;i++){
        let ang=Math.atan2(p.y-(enemies[0]?.y||p.y),p.x-(enemies[0]?.x||p.x));
        ang+=(i-(cnt-1)/2)*0.18;
        bullets.push({
            x:p.x,y:p.y,
            vx:Math.cos(ang)*7,vy:Math.sin(ang)*7,
            dmg:p.atk,track:p.track
        })
    }
}
//主循环
function loop(t){
    if(!gameRun){requestAnimationFrame(loop);return}
    //移动
    if(keys["w"]||keys["W"]||keys["ArrowUp"])p.y-=p.spd;
    if(keys["s"]||keys["S"]||keys["ArrowDown"])p.y+=p.spd;
    if(keys["a"]||keys["A"]||keys["ArrowLeft"])p.x-=p.spd;
    if(keys["d"]||keys["D"]||keys["ArrowRight"])p.x+=p.spd;
    p.x=Math.max(p.r,Math.min(canvas.width-p.r,p.x));
    p.y=Math.max(p.r,Math.min(canvas.height-p.r,p.y));
    shoot(t);
    //子弹更新
    for(let i=bullets.length-1;i>=0;i--){
        let b=bullets[i];
        if(b.track&&enemies[0]){
            const ang=Math.atan2(enemies[0].y-b.y,enemies[0].x-b.x);
            b.vx=Math.cos(ang)*7;b.vy=Math.sin(ang)*7;
        }
        b.x+=b.vx;b.y+=b.vy;
        if(b.x<0||b.x>canvas.width||b.y<0||b.y>canvas.height){bullets.splice(i,1);continue}
        //碰撞敌人
        for(let j=enemies.length-1;j>=0;j--){
            const e=enemies[j];
            const dx=b.x-e.x,dy=b.y-e.y,dist=Math.hypot(dx,dy);
            if(dist<e.r){
                e.hp-=b.dmg;
                bullets.splice(i,1);
                if(e.hp<=0){
                    enemies.splice(j,1);
                    p.exp+=12;
                    if(p.exp>=p.expNeed){
                        p.exp-=p.expNeed;
                        p.level++;
                        p.expNeed=Math.floor(p.expNeed*1.35);
                        uiLv.innerText=p.level;
                        showUpgrade();
                    }
                }
                break;
            }
        }
    }
    //敌人移动
    enemies.forEach(e=>{
        const dx=p.x-e.x,dy=p.y-e.y,dist=Math.hypot(dx,dy);
        e.x+=dx/dist*e.spd;
        e.y+=dy/dist*e.spd;
        if(dist<p.r+e.r){
            p.hp-=0.4;
            if(p.hp<=0)alert("游戏结束!刷新重来");
        }
    })
    uiHp.innerText=Math.floor(p.hp);
    //随机刷怪
    if(Math.random()<0.022)spawnEnemy();
    //绘制
    ctx.fillStyle="#fff8e8";ctx.fillRect(0,0,canvas.width,canvas.height);
    //团子主角
    ctx.beginPath();ctx.arc(p.x,p.y,p.r,0,Math.PI*2);ctx.fillStyle="#ffb6c1";ctx.fill();ctx.strokeStyle="#ff7788";ctx.lineWidth=2;ctx.stroke();
    ctx.fillStyle="#333";ctx.beginPath();ctx.arc(p.x-7,p.y-6,3,0,Math.PI*2);ctx.arc(p.x+7,p.y-6,3,0,Math.PI*2);ctx.fill();
    //敌人
    enemies.forEach(e=>{
        ctx.beginPath();ctx.arc(e.x,e.y,e.r,0,Math.PI*2);ctx.fillStyle=e.color;ctx.fill();
    })
    //子弹
    bullets.forEach(b=>{
        ctx.beginPath();ctx.arc(b.x,b.y,6,0,Math.PI*2);ctx.fillStyle="#ffddee";ctx.fill();
    })
    requestAnimationFrame(loop);
}
window.addEventListener("keydown",e=>keys[e.key]=true);
window.addEventListener("keyup",e=>keys[e.key]=false);
requestAnimationFrame(loop);
</script>
</body>
</html>

Game Source: 勇者团子传

Creator: ElectricFlare57

Libraries: none

Complexity: moderate (163 lines, 5.8 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-electricflare57" to link back to the original. Then publish at arcadelab.ai/publish.