勇者团子传
by ElectricFlare57163 lines5.8 KB
<!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.