From f21b7e24faa262eb354756ec1a18c8a3dfb691fa Mon Sep 17 00:00:00 2001 From: JOJO <1498581755@qq.com> Date: Thu, 6 Aug 2026 01:04:28 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A6=E6=89=8B=E6=89=AB=E5=87=BB=E7=A7=BB?= =?UTF-8?q?=E6=A4=8Ddemo=E9=AA=8C=E8=AF=81=E7=89=88=EF=BC=9A=E8=A7=92?= =?UTF-8?q?=E5=BA=A6=E5=88=B660=C2=B0=E6=89=87=E5=BD=A2=E6=8C=A5=E9=9E=AD?= =?UTF-8?q?=EF=BC=88=E5=89=8D=E6=91=87=E5=90=8E=E6=8B=89=E8=93=84=E5=8A=9B?= =?UTF-8?q?+=E5=8F=91=E5=85=89=E8=81=9A=E8=83=BD/=E5=8A=A0=E9=80=9F?= =?UTF-8?q?=E6=8C=A5=E5=87=BA+=E6=AE=8B=E5=BD=B1/=E9=A1=BF=E6=8C=AB/?= =?UTF-8?q?=E6=94=B6=E5=9B=9E=EF=BC=89=EF=BC=8C=E8=A7=A6=E6=89=8B=E5=8D=B3?= =?UTF-8?q?=E7=A2=B0=E6=92=9E=E4=BD=93=EF=BC=88=E7=82=B9-=E7=BA=BF?= =?UTF-8?q?=E6=AE=B5=E8=BF=9E=E7=BB=AD=E5=88=A4=E5=AE=9A=EF=BC=8C=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E5=88=A4=E5=AE=9A=E5=90=8C=E6=BA=90=EF=BC=89=EF=BC=8C?= =?UTF-8?q?=E5=88=80=E5=B0=96=E6=9C=9D=E5=90=91=E8=B7=9F=E9=9A=8F=E5=88=87?= =?UTF-8?q?=E7=BA=BF=EF=BC=8C=E5=88=A0=E9=A2=84=E8=AD=A6=E5=B8=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 0 -> 6148 bytes demo/tentacle-sweep.html | 136 +++++++++++++++++++++++++++++++++++++++ js/enemies.js | 42 +++++++++--- js/render.js | 33 ++++++---- 4 files changed, 191 insertions(+), 20 deletions(-) create mode 100644 .DS_Store create mode 100644 demo/tentacle-sweep.html diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..1d69658916bf1ca55a138e2611333c4da8c0a8e2 GIT binary patch literal 6148 zcmeHK%}T>S5Z<-XrW7Fug&r5Y7ECPk;w8lT0!H+pQWFw1m}X0p+CwSitS{t~_&m<+ zZp31}ir5+0{pNQ!`$6`HF~);=;xXnh#%ySa9F+}%?v0@xlZ?o5jCfH_<6OpIznR!y z2mE%6B`jqVCcl1vJk7JRa@}{n)3SE=+IHLS+IQijT!d9r&C4K~-Jo?TWga(r6klcO zV(c88$)bv~BAuy%EJ-2d_BtyPxd`OENJ>>3>VVy`J7cG}Tn^p7@Z6JCUo5?WH|UGu z&|R%M_Tka->BaadpA_;%6Uu>aCHn>&cm?HiJWz9zrPBP0fh0b+m{*j@(A znP7LfmjYTjF+dFbzyR(K0ve)ku-2%y4(RaujByJQ1$2B%APR%N!CE6kK)5ai)TP`! zF}N-Vzc6{e!CIp(XI#w; + + + +泰伦触手扫击 · 动作设计 Demo v2 + + + + +
右侧触手:前摇向右后拉蓄力(发光+聚能粒子)→ 从右下向左下 60° 扇形挥鞭(残影轨迹)→ 顿挫收回 · 判定=根部圆心扇形
+ + + diff --git a/js/enemies.js b/js/enemies.js index 4a0380c..4856495 100644 --- a/js/enemies.js +++ b/js/enemies.js @@ -36,6 +36,25 @@ function spawnEnemy(type,x,y,elite){ if(e.beh==="spin"){e.vx=rnd(-0.5,0.5);e.rot=rnd(0,TAU);} G.enemies.push(e); } +/* ---- 泰伦触手扫击:角度制扇形动作(demo验证版移植,渲染与判定同源) ---- */ +function tentPose(tn,side,tB){ // 返回{ang(0=正下,side向为正),stretch,glow} + const idle=Math.sin(tB*0.06)*0.07*side; + if(tn.warn>0){const f=1-tn.warn/50,e=f*f; // 前摇:向扫击反方向后拉蓄力 + return{ang:idle*(1-f)+side*0.73*e+Math.sin(tB*0.9)*0.012*e,stretch:1+e*0.15,glow:f};} + if(tn.act>17){const f=(45-tn.act)/28,e=f*f*f; // 扫击:加速挥出(右下→左下60°扇形) + return{ang:side*(0.73-e*1.31),stretch:1.15+e*0.45,glow:1};} + if(tn.act>0){return{ang:side*(-0.58)+Math.sin(tB*1.2)*0.05*(tn.act/17),stretch:1.6,glow:1};} // 顿挫 + const f=1-tn.rec/40,e=1-(1-f)*(1-f); // 收回 + return{ang:side*(-0.58)+(idle-side*(-0.58))*e,stretch:1.6-0.6*e,glow:1-e};} +function tentSegs(b,side,opF){ // 触手7节局部坐标(相对Boss中心),逐节延迟形成鞭弧 + const pts=[];opF=opF===undefined?1:opF; + for(let i=0;i<7;i++){ + let ang=Math.sin((b.t-i*2.2)*0.06)*0.07*side,stretch=1,glow=0; + if(b.tent){const s=tentPose(b.tent,side,b.t-i*2.2);ang=s.ang;stretch=s.stretch;glow=s.glow;} + const f=i/6,L=40*stretch*f*opF; + pts.push({x:side*9+Math.sin(ang)*L,y:24+Math.cos(ang)*L,glow}); + } + return pts;} /* ---- 敌机行为 ---- */ function updateEnemy(e){ e.t++; if(e.flash>0)e.flash--; if(e.hpt>0)e.hpt--; @@ -293,13 +312,20 @@ function updateBoss(){ if(b.plT===90){b.warpChg=0;bossBeam(b.x,b.y+6,b.x,H+8,b.phase>=1?24:20,"#5cff9d",10,80,30,{b,ox:0,oy:6,followX2:true,sweep:1,amp:b.phase>=1?70:50});} } } - if(b.idx===10&&b.tent){const tn=b.tent; // 泰伦:触手扫击(预警→横扫扇形,扫至中段判定) - if(tn.warn>0){tn.warn--;if(tn.warn<=0){tn.act=30;tn.hitDone=false;}} + if(b.idx===10&&b.tent){const tn=b.tent; // 泰伦:触手扫击(前摇→横扫→顿挫→收回;触手即碰撞体) + if(tn.warn>0){tn.warn--;if(tn.warn<=0){tn.act=45;tn.hitDone=false;}} else if(tn.act>0){tn.act--; - if(!tn.hitDone&&tn.act<=15){tn.hitDone=true; + if(!tn.hitDone){ // 扫击+顿挫全期:玩家与触手各节线段做点-线段碰撞 const p=G.p; - if(p.dead<=0&&Math.abs(p.x-(b.x+tn.side*30))<34&&p.y>b.y+10&&p.y0){tn.rec--;if(tn.rec<=0)b.tent=null;} } const lck=(b.laserT||0)>0; // 巨宽核心激光期间压制其他攻击 // —— 主攻击 —— @@ -462,10 +488,10 @@ function bossMinor(b,mk,fm){ case "sporeBloom": // 泰伦:孢子囊投放(慢飘碍走位,死亡酸液环爆) for(let i=0;i<2+b.phase;i++)spawnEnemy("sporeT",b.x+rnd(-20,20),b.y+rnd(2,16),false); b.minorT=Math.round(280/fm);break; - case "tentSweep": // 泰伦:触手扫击(P2+):预警后伸长横扫身前一带 + case "tentSweep": // 泰伦:触手扫击(P2+):前摇后拉蓄力→60°扇形挥鞭(触手即碰撞体) if(b.phase<1){b.minorT=80;break;} - if(!b.tent){b.tent={side:ap2.x0){ctx.globalAlpha=0.13+0.09*Math.sin(t*0.3);ctx.fillStyle="#a8ff5c"; // 触手扫击预警带 - ctx.fillRect(b.tent.side*30-34,10,68,78);ctx.globalAlpha=1;} for(const sd of[-1,0,1]){const sw=Math.sin(t*0.055+sd*1.8)*3; // 尾须缓摆 poly([sd*8-2.5,-36,sd*8+2.5,-36,sd*11+sw,-52,sd*9+sw-1.5,-52],"#3d2f4a"); px(sd*11+sw-1,-54,2,3,"#e8dcc0");} @@ -188,16 +186,27 @@ function drawBoss(){ poly([0,20,sd*9,14,sd*8,28,sd*3,36,0,34],"#241a30"); poly([0,22,sd*6,17,sd*5,27,sd*2,32,0,31],"#3d2f4a"); ctx.restore();} - if(op2>0){ // 触手:2长+4小(P2从裂口伸出,P3加粗;扫击侧伸长) - const tent=(x0,y0,len,wd,phs,tipW,swF,swS)=>{const sw2=Math.sin(t*0.06+phs)*3.5; - const bx=swF!=null?(swF*2-1)*30*(swS||1):0; // 扫击:尖端横扫偏转 - for(let i2=0;i2<4;i2++){const f=i2/4; - circ(x0+sw2*f*f*2.2+bx*f*f,y0+len*f,wd*(1-f*0.45)*(1+op3*0.3),i2%2?"#5a3a5a":"#7a4a6a");} - const fx2=x0+sw2*2.2+bx,fy=y0+len; - poly([fx2-2*tipW,fy-2,fx2+2*tipW,fy-2,fx2,fy+6],"#e8dcc0");}; - for(const sd of[-1,1]){const swp=b.tent&&b.tent.act>0&&b.tent.side===sd?1-b.tent.act/30:null; // 扫击侧:横扫扇形动画 - tent(sd*9*op2,24,(26+op3*8)*(swp!=null?1.8:1)*op2,3.2,sd*1.7,1,swp,sd);} - for(const sd of[-1,1]){tent(sd*15*op2,16,12*op2,1.8,sd*2.9+1,0.6,null,0);tent(sd*4*op2,32,14*op2,1.6,sd*4.1+2,0.6,null,0);} + if(op2>0){ // 触手:2长(角度制扇形摆荡,与判定同源)+4小 + for(const sd of[-1,1]){ + const pts=tentSegs(b,sd,op2),gl=pts[3].glow||0,sweeping=b.tent&&b.tent.side===sd; + if(sweeping&&b.tent.act>17){b.tent.trail=b.tent.trail||[];b.tent.trail.push(pts.map(p=>({x:p.x,y:p.y})));if(b.tent.trail.length>10)b.tent.trail.shift();} // 扫击记录残影 + else if(sweeping&&b.tent.trail&&b.tent.trail.length)b.tent.trail.shift(); + if(sweeping&&b.tent.trail){const tr=b.tent.trail; + for(let hi=0;hi=0;i--){const p=pts[i],f=i/6,r=Math.max(0.5,3.2*(1-f*0.45)*(1+op3*0.3)); + if(gl>0.15){ctx.globalAlpha=gl*0.28;circ(p.x,p.y,r+2,"#a8ff5c");ctx.globalAlpha=1;} + circ(p.x,p.y,r,i%2?"#7a4a6a":"#5a3a5a");} + const p1=pts[5],p2=pts[6],ta=Math.atan2(p2.y-p1.y,p2.x-p1.x); // 骨白刀尖:朝向跟随末端切线 + ctx.save();ctx.translate(p2.x,p2.y);ctx.rotate(ta-Math.PI/2); + poly([-2.5,-1.5,2.5,-1.5,0,6],"#e8dcc0"); + ctx.restore(); + } + if(b.tent&&b.tent.warn>0&&G.time%3===0){const pts2=tentSegs(b,b.tent.side,op2); // 前摇聚能粒子 + G.parts.push({x:b.x+pts2[4].x+rnd(-24,24),y:b.y+pts2[4].y+rnd(-14,24),vx:0,vy:0,life:18,col:"#a8ff5c",sz:1,suck:{x:b.x+pts2[4].x,y:b.y+pts2[4].y}});} + for(const sd of[-1,1]){const tentS=(x0,y0,len,wd,phs)=>{const sw2=Math.sin(t*0.06+phs)*3; // 4条小触手 + for(let i2=0;i2<3;i2++){const f=i2/3;circ(x0+sw2*f*f*2,y0+len*f,wd*(1-f*0.4)*(1+op3*0.3),i2%2?"#5a3a5a":"#7a4a6a");}}; + tentS(sd*15*op2,16,12*op2,1.8,sd*2.9+1);tentS(sd*4*op2,32,14*op2,1.6,sd*4.1+2);} } if(op3>0){const mw=9*op3; // 环齿嘴(P3展开,利齿缓慢旋动) circ(0,30,mw,"#4a1a1a");circ(0,30,mw*0.55,"#2a0d0d");