地下城|5大关×10小关 钥匙开宝箱
by PhantomCaptain66254 lines6.9 KB
kidhubb.com/publish<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>地下城|5大关×10小关 钥匙开宝箱</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
html,body{
width:100%;
height:100%;
overflow:hidden;
background:#000;
touch-action:none;
}
#game{
background:#111;
display:block;
width:100vw;
height:100vh;
}
.joystick-wrap{
position:fixed;
left:30px;
bottom:30px;
width:130px;
height:130px;
border-radius:50%;
background:rgba(80,120,200,0.35);
border:3px solid #3f5a8c;
z-index:10;
}
.joystick{
width:54px;
height:54px;
border-radius:50%;
background:#4289e7;
position:absolute;
left:38px;top:38px;
}
.kick-btn{
position:fixed;
right:36px;
bottom:44px;
width:92px;
height:92px;
border-radius:50%;
background:#ff3333;
color:#fff;
font-weight:bold;
font-size:22px;
border:3px solid #7a0000;
z-index:10;
}
.shopBtn{
position:fixed;
left:36px;
top:20px;
padding:8px 14px;
font-size:16px;
border-radius:8px;
background:#22aa22;
color:white;
border:none;
z-index:10;
}
</style>
</head>
<body>
<canvas id="game"></canvas>
<div class="joystick-wrap" id="joyWrap">
<div class="joystick" id="joy"></div>
</div>
<button class="kick-btn" id="kickBtn">踢</button>
<button class="shopBtn" id="shopBtn">商店</button>
<script>
const canvas = document.getElementById('game')
const ctx = canvas.getContext('2d')
function resize(){
canvas.width = window.innerWidth
canvas.height = window.innerHeight
}
resize()
window.addEventListener('resize',resize)
function getGroundY(){
return canvas.height * 0.82
}
const bigLevelConfig = [
{ monsterCount:1, speed:1.15, hp:3 },
{ monsterCount:1, speed:1.30, hp:3 },
{ monsterCount:2, speed:1.45, hp:4 },
{ monsterCount:2, speed:1.70, hp:4 },
{ monsterCount:2, speed:2.00, hp:5 }
]
let bigIndex = 0
let smallIndex = 0
let monsters = []
let box = {}
let keyItem = {}
let haveKey = false
let platforms = []
const playerBase = {speed:4.8,jump:-17,attack:1}
let addSpeed = 0, addJump = 0, addAttack = 0
const player = {
x:70,y:240,w:36,h:48,
gravity:0.72,onGround:true,facing:1,
attacking:false,attackTimer:0,attackCd:0,
hp:4,maxHp:4,invincible:0,
walkFrame:0,walkTimer:0
}
let gold = 0, dead = false, reviveTimer = 0
const reviveWait = 80
let allClear = false, shopOpen = false
function getPlayerStat(){
return {
speed: playerBase.speed + addSpeed,
jumpPower: playerBase.jump + addJump,
attackPower: playerBase.attack + addAttack
}
}
function loadStage(){
const cfg = bigLevelConfig[bigIndex]
const groundY = getGroundY()
monsters = []
platforms = []
const pCount = 2 + Math.floor(Math.random()*2)
for(let i=0;i<pCount;i++){
const px = canvas.width * 0.15 + Math.random()*canvas.width*0.6
const py = canvas.height * 0.55 + Math.random()*canvas.height*0.15
const pw = 70 + Math.random()*50
platforms.push({x:px,y:py,w:pw,h:16})
}
const targetP = platforms[Math.floor(Math.random()*platforms.length)]
keyItem = {x:targetP.x+10, y:targetP.y-30, w:24,h:24}
haveKey = false
for(let i=0;i<cfg.monsterCount;i++){
const mx = canvas.width * 0.22 + i * canvas.width * 0.32
const dir = i%2===0 ? 1 : -1
monsters.push({
x:mx, y:groundY-44, w:40,h:44,
vx: cfg.speed * dir,
minX: Math.max(canvas.width*0.08, mx - 80),
maxX: Math.min(canvas.width*0.92, mx + 80),
hp:cfg.hp, flash:0, maxHp:cfg.hp
})
}
box = {x:canvas.width*0.85,y:groundY-40,w:40,h:40}
player.x=canvas.width*0.04;player.y=groundY-48;player.vx=0;player.vy=0
player.hp=player.maxHp;player.invincible=0;player.walkFrame=0;player.walkTimer=0
dead=false;reviveTimer=0
}
loadStage()
const joyWrap = document.getElementById('joyWrap')
const joy = document.getElementById('joy')
const kickBtn = document.getElementById('kickBtn')
const shopBtn = document.getElementById('shopBtn')
const joyR = 40
let joyDx=0,joyDy=0,touchingJoy=false
shopBtn.onclick = ()=>{shopOpen = !shopOpen}
function updateJoystick(clientX, clientY){
if(dead || shopOpen) return
const stat = getPlayerStat()
const rect = joyWrap.getBoundingClientRect()
const x = clientX - rect.left
const y = clientY - rect.top
let dx = x - 65, dy = y - 65
const dist = Math.hypot(dx,dy)
if(dist>joyR){dx=dx/dist*joyR;dy=dy/dist*joyR}
joy.style.left=(65+dx-27)+"px"
joy.style.top=(65+dy-27)+"px"
joyDx=dx;joyDy=dy
if(joyDx>8) player.facing=1
if(joyDx<-8) player.facing=-1
if(dy<-22 && player.onGround){
player.vy=stat.jumpPower
player.onGround=false
}
}
function resetJoy(){joy.style.left="38px";joy.style.top="38px";joyDx=0;joyDy=0}
joyWrap.addEventListener('touchstart',e=>{touchingJoy=true;updateJoystick(e.touches[0].clientX,e.touches[0].clientY)})
joyWrap.addEventListener('touchmove',e=>{e.preventDefault();if(!touchingJoy)return;updateJoystick(e.touches[0].clientX,e.touches[0].clientY)})
joyWrap.addEventListener('touchend',()=>{touchingJoy=false;resetJoy()})
joyWrap.addEventListener('mousedown',e=>{touchingJoy=true;updateJoystick(e.clientX,e.clientY)})
window.addEventListener('mousemove',e=>{if(!touchingJoy)return;updateJoystick(e.clientX,e.clientY)})
window.addEventListener('mouseup',()=>{touchingJoy=false;resetJoy()})
function kick(){
if(player.attackCd>0 || dead || shopOpen) return
const stat = getPlayerStat()
player.attacking=true;player.attackTimer=14;player.attackCd=22
const attackRange = {
x: player.facing===1 ? player.x+player.w : player.x-44,
y:player.y+8,w:44,h:34
}
for(let m of monsters){
if(m.hp<=0) continue
if(attackRange.x<m.x+m.w && attackRange.x+attackRange.w>m.x && attackRange.y<m.y+m.h && attackRange.y+attackRange.h>m.y){
m.hp -= stat.attackPower
m.flash=12
m.vx=-m.vx
}
}
}
kickBtn.addEventListener('touchstart',e=>{e.preventDefault();kick()})
kickBtn.addEventListener('mousedown',kick)
function rectHit(a,b){
return a.x<b.x+b.w && a.x+a.w>b.x && a.y<b.y+b.h && a.y+a.h>b.y
}
function drawBackground(){
const groundY = getGroundY()
ctx.fillStyle="#221133"
ctx.fillRect(0,0,canvas.width,groundY)
ctx.fillStyle="#110820"
ctx.fillRect(0,groundY,canvas.width,canvas.height-groundY)
}
function drawPlayer(){
const px=player.x, py=player.y
if(player.invincible>0 && Math.floor(player.invincible/4)%2===0) return
ctx.save()
if(player.facing<0){
ctx.translate(px+player.w/2,0)
ctx.scale(-1,1)
ctx.translate(-(px+player.w/2),0)
}
ctx.fillStyle="#1368c7"
ctx.fillRect(px+8,py+14,20,24)
ctx.fillStyle="#ffd9a8"
ctx.fillRect(px+10,py,16,16)
ctx.fillStyle="#111"
ctx.fillRect(px+20,py+4,4,4)
const leftLeg={x:px+10,y:py+38,w:7,h:10}
const rightLeg={x:px+19,y:py+38,w:7,h:10}
ctx.fillStyle="#2f7bd1"
if(!player.onGround){
ctx.fillRect(leftLeg.x+1,leftLeg.y-2,leftLeg.w,8)
ctx.fillRect(rightLeg.x+1,rightLeg.y-2,rightLeg.w,8)
}else if(Math.abs(playGame Source: 地下城|5大关×10小关 钥匙开宝箱
Creator: PhantomCaptain66
Libraries: none
Complexity: complex (254 lines, 6.9 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: 5-10-phantomcaptain66" to link back to the original. Then publish at arcadelab.ai/publish.