制裁白子
by WildOwl28214 lines6.7 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 {overflow: hidden; background: #222; touch-action: none;}
#gameCanvas {display: block; background: #7ccd7c;}
#gameOver {
position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
background: red; color: #fff; font-size: 32px;
display: none; flex-direction: column;
align-items: center; justify-content: center;
gap: 40px; z-index: 99;
}
#restartBtn {
padding: 14px 36px; font-size: 22px; cursor: pointer;
border: none; border-radius: 8px; background: #fff; color: red;
}
.joystick {
position: fixed; bottom: 30px; left: 30px;
width: 120px; height: 120px;
background: rgba(255,255,255,0.2); border-radius: 50%;
touch-action: none;
}
.stick {
width: 50px; height: 50px; border-radius: 50%;
background: rgba(255,255,255,0.4);
position: absolute; left: 50%; top: 50%;
transform: translate(-50%,-50%);
}
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<div id="gameOver">
<div>你已经被吃了游戏结束(你暗恋方向盘)</div>
<button id="restartBtn">重新开始</button>
</div>
<div class="joystick" id="joy">
<div class="stick" id="stick"></div>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const gameOverEl = document.getElementById('gameOver');
const restartBtn = document.getElementById('restartBtn');
const joy = document.getElementById('joy');
const stick = document.getElementById('stick');
function resize(){
canvas.width = innerWidth;
canvas.height = innerHeight;
}
resize();
window.addEventListener('resize', resize);
//游戏数据
let gameState = 'play';
let player = {x:0,y:0,size:24,speed:4};
let heldItem = null;
let knifeSpawned = false;
let trees = [];
let enemies = [];
let items = [];
let dragonTimer = 0;
let rainEffect = false;
let enemyFreeze = 0;
let camera = {x:0,y:0};
let lastDragonTime = 0;
let worldExtendDist = 800;
//摇杆
let joyActive = false, joyVec={x:0,y:0};
joy.addEventListener('touchstart',e=>{joyActive=true;updateJoy(e.touches[0]);});
document.addEventListener('touchmove',e=>joyActive&&updateJoy(e.touches[0]));
document.addEventListener('touchend',()=>{joyActive=false;joyVec={x:0,y:0};stick.style.transform='translate(-50%,-50%)';});
function updateJoy(touch){
const rect = joy.getBoundingClientRect();
const cx = rect.left+rect.width/2, cy=rect.top+rect.height/2;
let dx = touch.clientX - cx, dy = touch.clientY - cy;
const maxR = rect.width/2 - 25;
const dist = Math.hypot(dx,dy);
if(dist>maxR){dx=dx/dist*maxR; dy=dy/dist*maxR;}
stick.style.left = (rect.width/2+dx)+'px';
stick.style.top = (rect.height/2+dy)+'px';
joyVec = {x:dx/maxR, y:dy/maxR};
}
//生成树木
function spawnTree(px,py){
const type = Math.random()>0.5 ? 'tree1' : 'tree2';
trees.push({x:px+(Math.random()-0.5)*worldExtendDist, y:py+(Math.random()-0.5)*worldExtendDist, type, size:28+Math.random()*12});
}
//生成敌人(数量少、移速慢)
function spawnEnemy(px,py){
const types = ['devil','ghost','alien'];
const t = types[Math.floor(Math.random()*types.length)];
const angle = Math.random()*Math.PI*2;
const dist = 450+Math.random()*600;
enemies.push({
x: px+Math.cos(angle)*dist,
y: py+Math.sin(angle)*dist,
type:t, speed:1.1, size:26
});
}
//道具
function spawnItem(px,py){
const list = ['dragon','lobster','water','boom','bomb','spin','sleep','knife'];
let t;
if(!knifeSpawned && Math.random()<0.12){
t='knife'; knifeSpawned=true;
}else{
t = list.filter(v=>v!=='knife')[Math.floor(Math.random()*7)];
}
const ang = Math.random()*Math.PI*2;
const d = 300+Math.random()*500;
items.push({
x:px+Math.cos(ang)*d,
y:py+Math.sin(ang)*d,
type:t, size:22
});
}
function initGame(){
gameState='play';
gameOverEl.style.display='none';
player={x:0,y:0,size:24,speed:4};
heldItem=null;
knifeSpawned=false;
trees=[];enemies=[];items=[];
dragonTimer=0;rainEffect=false;enemyFreeze=0;
camera={x:0,y:0};
lastDragonTime=Date.now();
for(let i=0;i<35;i++) spawnTree(0,0);
for(let i=0;i<4;i++) spawnEnemy(0,0);
for(let i=0;i<9;i++) spawnItem(0,0);
}
restartBtn.onclick = initGame;
initGame();
//键盘
const keys={};
document.addEventListener('keydown',e=>keys[e.key.toLowerCase()]=true);
document.addEventListener('keyup',e=>keys[e.key.toLowerCase()]=false);
function getMoveDir(){
let x=0,y=0;
if(keys['w']||keys['arrowup'])y-=1;
if(keys['s']||keys['arrowdown'])y+=1;
if(keys['a']||keys['arrowleft'])x-=1;
if(keys['d']||keys['arrowright'])x+=1;
if(joyActive){x+=joyVec.x; y+=joyVec.y;}
const len = Math.hypot(x,y);
if(len>0) return {x:x/len, y:y/len};
return {x:0,y:0};
}
//碰撞
function circleCollision(a,b){
const d = Math.hypot(a.x-b.x, a.y-b.y);
return d < a.size/2 + b.size/2;
}
//合成系统
function combineItems(itemA,itemB){
if(itemA==='dragon'&&itemB==='lobster' || itemA==='lobster'&&itemB==='dragon'){
alert('你是龙啊,你是虾子');
enemyFreeze = 3000;
setTimeout(()=>{
enemies.forEach(e=>e.speed=1.1);
},3000);
return true;
}
if(itemA==='water'&&itemB==='boom' || itemA==='boom'&&itemB==='water'){
alert('下雨了!');
rainEffect=true;
enemies.forEach(e=>{
const knock = {x:(e.x-player.x)*0.25, y:(e.y-player.y)*0.25};
e.x+=knock.x*12; e.y+=knock.y*12;
});
setTimeout(()=>rainEffect=false,800);
return true;
}
return false;
}
function gameLoop(){
ctx.fillStyle='#7ccd7c';
ctx.fillRect(0,0,canvas.width,canvas.height);
if(gameState!=='play'){
requestAnimationFrame(gameLoop);
return;
}
const move = getMoveDir();
player.x += move.x * player.speed;
player.y += move.y * player.speed;
camera.x = player.x - canvas.width/2;
camera.y = player.y - canvas.height/2;
//每10秒刷龙
const now = Date.now();
if(now - lastDragonTime > 10000){
const ang = Math.random()*Math.PI*2;
const dis = 350+Math.random()*400;
items.push({
x:player.x+Math.cos(ang)*dis,
y:player.y+Math.sin(ang)*dis,
type:'dragon', size:22
Game Source: 制裁白子
Creator: WildOwl28
Libraries: none
Complexity: complex (214 lines, 6.7 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-wildowl28" to link back to the original. Then publish at arcadelab.ai/publish.