基于鸿蒙OS开发打飞机小游戏(24)-防护墙技能

一、防护墙系统总览

墙壁技能激活

在EmojiShooter的Boss技能体系中,防护墙技能(hasWall)是最复杂的空间控制机制。它不仅通过物理障碍限制玩家的移动空间,还通过"掏兜"动画提供战术预警,通过弹飞机制改变玩家的位置,甚至通过屏幕外死亡判定创造了一种全新的致死模式。

防护墙系统包含以下子系统:

子系统 核心功能 设计意图
掏兜动画 墙壁生成前的视觉预警 提供可读的战术信息
表情砌墙 生成物理障碍限制空间 空间否定与路径控制
子弹↔墙碰撞 墙壁阻挡玩家子弹 保护Boss免受伤害
墙↔玩家碰撞 墙壁弹飞玩家 位置改变+生命损失
屏幕外死亡 弹飞后飞出屏幕=死亡 弹飞→死亡的连锁
护盾抗弹飞 护盾消耗一次弹飞 提供一线生机

这些子系统协同工作,创造了一个"空间博弈"的战斗环境——玩家不仅要回避弹幕和射击Boss,还要管理自己的空间位置,避免被墙壁包围和弹飞。

二、掏兜动画系统

2.1 掏兜计时器逻辑

掏兜动画的核心计时器逻辑(Index.ets:1431-1556):

wallPocketTimer++;
threshold = aliveCount === 0 ? wallPocketInterval : wallPocketInterval + 120;
if (wallPocketTimer >= threshold) {
  wallPocketActive = true;
  wallPocketAnimTime = wallPocketAnimMax; // 60
}

关键参数:

参数 说明
wallPocketInterval 240帧 基础掏兜间隔
wallPocketAnimMax 60帧 掏兜动画持续帧数
wallPocketTimer 帧计数器 掏兜计时器
wallPocketActive 布尔值 掏兜动画是否激活
wallPocketAnimTime 帧计数器 动画倒计时

2.2 阈值的动态调整

掏兜触发的阈值根据当前墙壁存活数量动态调整:

条件 阈值 间隔时间(60FPS) 说明
aliveCount === 0 240帧 4秒 无墙时快速生成
aliveCount > 0 240+120=360帧 6秒 有墙时慢速扩展

这种动态调整的逻辑是:

  • 无墙时快速生成——确保场地上始终有墙壁存在,维持空间压力
  • 有墙时慢速扩展——给玩家喘息时间,避免墙壁过快覆盖全场

阈值差异为120帧(2秒),这意味着:

  • 拆除所有墙壁后,2秒后就会开始生成新墙壁
  • 保留部分墙壁,4秒后才会扩展

这创造了一个策略选择:快速拆墙可以获得2秒的额外无墙时间,但需要投入射击资源

2.3 掏兜动画的时序

当wallPocketTimer达到阈值时,掏兜动画开始:

帧数 wallPocketAnimTime 事件 视觉效果
0 60 动画开始 🤚手从Boss位置伸出
1-30 59→30 动画中期 手逐渐伸长+emoji开始出现
31-59 29→1 动画后期 emoji飞向目标位置
60 0 动画结束 墙壁方块生成

60帧的动画时长(1秒)提供了足够的预警时间。在动画的前30帧,玩家可以看到手伸出的方向和emoji的初始轨迹,推断墙壁将出现在哪里。在后30帧,emoji已经飞向目标位置,玩家可以看到墙壁的最终位置。

2.4 掏兜动画作为电报系统

在游戏设计术语中,“电报”(telegraph)是指敌人在发动攻击前的可视化预警。掏兜动画就是一个电报系统——它告诉玩家"墙壁即将出现在某个位置",给玩家时间做出反应。

电报系统的设计遵循以下原则:

原则1:可读性

  • 动画必须足够明显,不会被弹幕或其他特效掩盖
  • 🤚手的视觉效果应该突出且清晰

原则2:可预测性

  • 玩家应该能从动画推断攻击的结果
  • emoji飞向的方向=墙壁出现的位置

原则3:可应对性

  • 预警时间必须足够玩家做出反应
  • 60帧(1秒)的动画时长给了玩家移动或射击的时间
电报属性 掏兜动画 评价
可读性 🤚手+emoji飞行
可预测性 飞行方向=墙壁位置
可应对性 60帧预警时间 中高
一致性 每次掏兜都有相同动画

2.5 掏兜动画的视觉元素

根据代码,掏兜动画包含以下视觉元素:

元素1:危险光环(红色圆圈)

  • 位置:Boss周围
  • 效果:红色的圆形危险指示
  • 作用:提示Boss即将执行墙壁技能

元素2:🤚手动画

  • 位置:从Boss位置伸出
  • 效果:手逐渐伸出+在70%计时器时出现
  • 作用:视觉预警+方向指示

元素3:emoji生成

  • 位置:从手中飞出
  • 效果:emoji飞向目标位置
  • 作用:显示墙壁的类型和位置

这些元素的时序关系:

计时器比例 视觉效果 信息量
0-50% 危险光环+手伸出 低(只知道"有东西要发生")
50-70% 手伸长+准备 中(可以看到方向)
70-100% emoji飞出 高(可以看到目标位置)
100% 墙壁生成 完整(墙壁已就位)

2.6 手动画的70%阈值

手的出现在计时器达到70%时(即wallPocketTimer/wallPocketInterval > 0.7且无活跃掏兜时)。这个阈值的设计意义:

  • 70%之前:玩家只知道"掏兜即将发生",但不知道方向
  • 70%之后:玩家可以看到手的方向,推断墙壁位置
  • 70% = 168帧(无墙时)或252帧(有墙时):即2.8秒或4.2秒后手出现

70%的阈值给了玩家约30%的周期(72帧或108帧≈1.2-1.8秒)来看到手并做出反应。这个时间窗口虽然不长,但足以让玩家移动位置或准备射击新生成的墙壁。

三、表情砌墙系统

3.1 addWallBlocks逻辑

当掏兜动画完成(wallPocketAnimTime=0)时,addWallBlocks函数被调用,根据当前墙壁数量决定生成策略:

if (existingCount === 0) {
  // 生成5-block初始环形
  生成5个方块,半径80x/60y
} else if (existingCount < 20) {
  // 扩展+5个方块
  在现有环形基础上增加5个
} else if (existingCount >= 20) {
  // 修复+3个方块
  修复已死亡的墙壁+新增3个
}

三种生成策略的对比:

条件 生成数量 布局 说明
existingCount=0 5 环形(半径80x/60y) 初始墙壁环
0<existingCount<20 +5 扩展环形 逐步扩展
existingCount≥20 +3 修复/补充 维持+修复

3.2 初始环形墙壁

初始环形墙壁的参数:

参数 说明
方块数量 5 环形中的方块数
X方向半径 80px 环形在X方向的半径
Y方向半径 60px 环形在Y方向的半径
中心位置 Boss中心 环形围绕Boss中心

5个方块均匀分布在椭圆形上,角度间隔为72度(360/5):

方块编号 角度 X偏移 Y偏移 相对位置
0 +80 0 右侧
1 72° +25 -57 右上
2 144° -65 -35 左上
3 216° -65 +35 左下
4 288° +25 +57 右下

这个环形布局创造了一个"保护圈"——Boss在中心,5个方块围绕Boss形成一个椭圆形的屏障。玩家必须穿过或绕过这个屏障才能射击Boss。

3.3 扩展策略

当已有1-19个墙壁方块时,每次扩展增加5个:

扩展次数 总方块数 累计扩展角度覆盖 密度变化
初始 5 环形 基础
+1 10 环形扩展 密度增加
+2 15 双环 更密集
+3 20 多环 非常密集

每次扩展的5个方块可能添加在:

  • 现有环形的外圈(扩大半径)
  • 现有环形的间隙(填充空隙)
  • 新的同心环(多层防御)

3.4 修复策略

当方块数≥20时,改为修复+3策略:

修复行为 条件 效果
修复已死亡方块 有alive=false的方块 将其alive设为true, hp恢复
新增方块 所有方块都alive 新增3个方块

修复策略确保了墙壁的完整性——即使玩家拆掉了一些方块,它们会在下一次掏兜时被修复。这使得"拆墙"策略的长期效果有限——除非玩家能在修复前利用无墙间隙对Boss造成足够伤害。

3.5 wallEmojis列表

墙壁方块的emoji列表:

wallEmojis = [🧱📦🔩⛓🛡🔨🚪🏰🧱📦]
索引 emoji 含义 视觉特征
0 🧱 砖块 红色方块
1 📦 包裹 棕色立方体
2 🔩 螺栓 金属质感
3 锁链 连接感
4 🛡 盾牌 防御象征
5 🔨 锤子 攻击/建造
6 🚪 通道/入口
7 🏰 城堡 建筑象征
8 🧱 砖块 红色方块(重复)
9 📦 包裹 棕色立方体(重复)

这些emoji的选择遵循了"建筑/防御"主题——砖块、包裹、螺栓、锁链、盾牌、锤子、门、城堡——都与建造和防御相关。这增强了墙壁系统的视觉叙事:Boss在"建造"防御工事。

四、WallBlock数据结构

4.1 WallBlock属性

每个WallBlock(墙壁方块)有以下属性:

属性 类型 初始值 说明
hp number 3 当前生命值
maxHp number 3 最大生命值
size number 22 方块大小(px)
alive boolean true 是否存活
x number - X坐标
y number - Y坐标
emoji string - 显示的emoji

4.2 HP系统

墙壁方块的HP系统简单明了:

HP值 状态 视觉表现
3 完好 正常显示emoji
2 轻微损伤 可能显示裂纹
1 严重损伤 可能显示严重裂纹
0 破坏 alive=false, 不再显示

3HP的设计意味着每个方块需要3发子弹才能击毁。这在子弹资源上是一个显著的成本——5个初始方块需要15发子弹才能全部清除。

4.3 碰撞半径

碰撞检测基于size参数:

子弹↔墙碰撞: dist < size * 0.9 = 22 * 0.9 = 19.8px
墙↔玩家碰撞: dist < size + player.width/2

0.9的系数使墙壁的碰撞区域略小于视觉区域,这是一个常见的游戏设计技巧——让碰撞区域略小于显示区域,避免"明明没碰到却被弹"的挫败感。

五、子弹↔墙壁碰撞

5.1 碰撞检测逻辑

在updateBossWall函数中,子弹与墙壁的碰撞检测:

for each bullet:
  for each wallBlock (alive=true):
    dist = distance(bullet, wallBlock)
    if (dist < wallBlock.size * 0.9):
      bullet.alive = false  // 子弹被墙壁阻挡
      wallBlock.hp--        // 墙壁受损
      if (wallBlock.hp <= 0):
        wallBlock.alive = false  // 墙壁被击毁

5.2 子弹被阻挡的战术影响

子弹被墙壁阻挡意味着:

  1. 墙壁保护Boss——在墙壁后面的Boss不会被子弹击中
  2. 射击效率降低——部分子弹被墙壁吸收,到达Boss的子弹减少
  3. 需要拆墙——必须先击毁阻挡视线的墙壁

子弹被阻挡的效率损失:

墙壁数量 子弹通过率 说明
0 100% 无墙壁
5 ~60-80% 初始环形,有间隙
10 ~40-60% 扩展后,间隙缩小
15 ~20-40% 密集,少量间隙
20+ ~10-20% 几乎完全封锁

通过率取决于射击角度和墙壁布局。初始的5方块环形有较大的间隙(72度间隔),约60-80%的子弹可以通过。但随着墙壁扩展,间隙被填满,通过率急剧下降。

5.3 拆墙的成本-收益分析

拆墙需要消耗子弹(每个方块3发),但可以恢复射击通道。成本-收益分析:

方块HP 击毁所需子弹 恢复的射击通道 净收益
3→0 3发 该方向的射击通道 正(如果该方向有Boss部件)
3→0 3发 无(该方向无Boss部件) 负(浪费子弹)

最优策略是只拆掉阻挡射击Boss关键部件的墙壁,而不是全部拆除。但考虑到墙壁会在修复阶段被恢复,拆墙的收益是暂时的。

5.4 墙壁作为"子弹吸收器"

从Boss的角度看,墙壁不仅是物理屏障,更是"子弹吸收器"——它们消耗玩家的子弹资源,降低玩家的有效DPS。

假设玩家射速为每6帧1发(10发/秒),Boss的射击间隔为100帧(6发/秒):

墙壁状态 玩家子弹分配 到达Boss的子弹 有效DPS
无墙 全部射向Boss 10发/秒 100%
5墙(拆2) 6发拆墙+4发射Boss 4发/秒 40%
5墙(不拆) 全部被吸收 2发/秒(间隙通过) 20%
10墙 大部分被吸收 1-2发/秒 10-20%

墙壁对DPS的衰减是显著的。5个初始环形墙就能将有效DPS降低到40%(如果拆墙)或20%(如果不拆)。10个扩展墙更是将DPS降到10-20%。

六、墙↔玩家碰撞:弹飞机制

6.1 碰撞检测逻辑

checkWallPlayerCollision函数检测玩家与墙壁的碰撞:

for each wallBlock (alive=true):
  dist = distance(player, wallBlock)
  if (dist < wallBlock.size + player.width/2):
    // 弹飞玩家
    knockForce = 18
    direction = (player - wallBlock) / dist  // 方向:从墙到玩家
    player.knockbackVx = direction.x * knockForce
    player.knockbackVy = direction.y * knockForce
    
    // 护盾检查
    if (player.shield > 0):
      player.shield = 0  // 消耗护盾
      // 不扣生命
    else:
      lives -= 1  // 扣1条生命

6.2 弹飞力学

弹飞的力学参数:

参数 说明
knockForce 18 弹飞力度
knockbackVx direction.x × 18 X方向弹飞速度
knockbackVy direction.y × 18 Y方向弹飞速度
衰减系数 0.92 每帧速度衰减
停止阈值 0.3 速度低于此值停止弹飞

弹飞方向是从墙壁指向玩家的单位向量×力度。这意味着玩家被"推离"墙壁——无论从哪个方向接触墙壁,都会被推向相反方向。

6.3 弹飞速度的帧级衰减

弹飞速度每帧衰减8%(乘以0.92):

帧数 速度 位移(帧) 累计位移
0 18.0 18.0 18.0
1 16.56 16.56 34.56
2 15.24 15.24 49.80
3 14.02 14.02 63.82
4 12.90 12.90 76.72
5 11.87 11.87 88.59
10 7.73 7.73 136.56
15 5.04 5.04 166.42
20 3.28 3.28 185.10
25 2.14 2.14 196.76
30 1.39 1.39 204.27
35 0.91 0.91 209.01
40 0.59 0.59 212.15
45 0.39 0.39 214.12
50 0.25 停止 215.50

弹飞的总位移约为215px,持续约50帧(0.83秒)。这个位移足以将玩家从墙壁附近推到屏幕边缘甚至屏幕外。

6.4 弹飞方向分析

弹飞方向取决于玩家接触墙壁的位置:

接触位置 弹飞方向 结果
墙壁左侧 向左弹飞 可能飞向左屏幕外
墙壁右侧 向右弹飞 可能飞向右屏幕外
墙壁上方 向上弹飞 可能飞向上屏幕外
墙壁下方 向下弹飞 可能飞向下屏幕外
墙壁角落 对角弹飞 可能飞向角落屏幕外

弹飞方向是从墙壁中心指向玩家中心的方向。这意味着:

  • 如果玩家在墙壁左边碰到,被推向更左
  • 如果玩家在墙壁上方碰到,被推向更上
  • 如果玩家被多个墙壁包围,可能被反复弹飞

6.5 连续弹飞的灾难性场景

当玩家被弹飞后,如果弹飞路径上还有其他墙壁,就会发生连续弹飞:

玩家碰到墙A → 被弹飞 → 飞向墙B → 碰到墙B → 被再次弹飞 → 飞向墙C → ...

每次碰撞都会:

  1. 重置弹飞速度为18(方向改变)
  2. 扣1条生命(或消耗护盾)
  3. 改变弹飞方向

如果玩家被3面墙壁包围,可能发生3次连续弹飞,损失3条生命,最终被弹向屏幕外。

碰撞次数 生命损失 累计位移方向 结果
1 -1 向外1次 可能安全
2 -2 向外2次 危险
3 -3 向外3次 极可能屏幕外
4+ -4+ 不确定 几乎必然屏幕外

连续弹飞是防护墙系统最致命的组合——一次失误可能导致连锁反应,瞬间损失多条生命。

七、屏幕外死亡机制

7.1 死亡判定条件

在updatePlayer函数中,屏幕外死亡的判定:

if (player.x < -player.width || player.x > canvasWidth + player.width ||
    player.y < -player.height || player.y > canvasHeight + player.height) {
  // 屏幕外死亡
  lives -= 1; // 或直接game over
}

判定条件:玩家中心超出画布边界一个玩家宽度的距离。

方向 死亡边界 说明
x < -width 飞出左边界
x > canvasWidth + width 飞出右边界
y < -height 飞出上边界
y > canvasHeight + height 飞出下边界

7.2 弹飞→屏幕外死亡的数学分析

弹飞的总位移约为215px。画布尺寸假设为400×700px。玩家初始位置假设在画布中心(200, 350)。

弹飞方向 到边界的距离 是否致死(215px位移)
向左 200+width/2≈220 是(215≈220)
向右 200+width/2≈220 是(215≈220)
向上 350+height/2≈370 否(215<370)
向下 350+height/2≈370 否(215<370)

在画布中心位置,水平弹飞(215px)几乎刚好到达屏幕边缘(220px),可能致死;垂直弹飞(215px)不足以到达边缘(370px),通常不致死。

但如果玩家已经靠近边缘:

玩家位置 弹飞方向 到边界距离 是否致死
中心(200,350) 220 边缘(可能)
偏左(100,350) 120
偏右(300,350) 120
偏上(200,150) 170
边角(50,50) 左上 70/70 是(两个方向)

结论:玩家越靠近屏幕边缘,弹飞致死的风险越高。 墙壁的存在迫使玩家在有限的区域内移动,而区域边缘就是死亡陷阱。

7.3 屏幕外死亡与弹幕回避的矛盾

屏幕外死亡创造了一个新的回避维度:

回避类型 方向 风险
弹幕回避 向弹幕稀疏区域移动 可能靠近墙壁→被弹飞
墙壁回避 远离墙壁 可能靠近弹幕→被击中
边界回避 远离屏幕边缘 被压缩到中心区域→空间更小

三种回避方向可能互相矛盾——回避弹幕可能靠近墙壁,回避墙壁可能靠近弹幕,回避边界可能被压缩到中心。这种三重矛盾是防护墙系统创造的核心空间博弈。

八、护盾抗弹飞机制

8.1 护盾消耗逻辑

当玩家拥有护盾(shield > 0)时,墙壁弹飞可以被护盾抵挡:

if (player.shield > 0):
  player.shield = 0  // 消耗护盾
  // 不扣生命
  // 但弹飞仍然发生

注意:护盾只抵消生命损失,不抵消弹飞效果。玩家仍然会被弹飞,只是不扣生命。

8.2 护盾的价值评估

护盾在防护墙Boss战中的价值极高:

场景 无护盾 有护盾 差异
碰到1面墙 -1生命 0生命(消耗护盾) 1条生命
碰到2面墙(连续) -2生命 -1生命(第1次消耗护盾) 1条生命
碰到3面墙(连续) -3生命 -2生命 1条生命

护盾只抵消第一次碰撞的生命损失,后续碰撞仍然正常扣生命。但这1条生命的差距可能是生死之别——如果护盾让玩家在lives=1时免于死亡,那就是从"游戏结束"到"继续战斗"的区别。

8.3 护盾使用策略

面对防护墙Boss,护盾的使用策略:

策略1:保守使用

  • 保留护盾到最危急时刻
  • 平时通过走位避免碰墙
  • 护盾作为最后的保险

策略2:主动使用

  • 有护盾时更积极地接近Boss射击
  • 碰墙时用护盾抵消,换取更好的射击位置
  • 护盾作为进攻资源

策略3:循环获取

  • 如果击败Boss可以获得护盾,则可以循环使用
  • 每次Boss被击败→获得护盾→下次碰墙消耗→再击败Boss…

策略2在防护墙Boss战中可能更有效——因为墙壁限制了射击角度,有时必须靠近Boss才能射击。有护盾时可以更自由地移动,不受墙壁碰撞的恐惧制约。

九、防护墙的空间否定分析

9.1 空间否定的概念

“空间否定”(Spatial Denial)是游戏设计中的一个核心概念——通过放置障碍物或危险区域,使某些空间变得不可进入或不可通过。防护墙系统是一个典型的空间否定机制。

空间状态 可用性 原因
无墙区域 完全可用 可以自由移动和射击
墙壁位置 不可进入 碰到墙壁→弹飞+生命损失
墙壁后方 部分可用 可以射击但子弹被阻挡
墙壁包围区域 极度危险 可能被连续弹飞

9.2 空间压缩的动态过程

随着墙壁数量增加,可用空间逐步压缩:

阶段 墙壁数量 可用空间比例 移动自由度
初始 0 100% 完全自由
5墙 5 ~85% 大部分自由
10墙 10 ~70% 受限
15墙 15 ~55% 严重受限
20墙 20 ~40% 极度受限
25墙 25 ~30% 几乎无法移动

可用空间的压缩直接影响玩家的回避能力——空间越小,回避弹幕的余地越小。这创造了一种"空间绞杀"效应:墙壁逐步压缩空间,弹幕密度不变但回避空间减少,等效于弹幕密度增加。

9.3 空间否定的双重性

防护墙的空间否定具有双重性——它同时否定了玩家的移动空间和射击空间:

移动空间否定

  • 墙壁占据的区域不可进入
  • 墙壁之间的通道变窄
  • 被弹飞的风险增加

射击空间否定

  • 墙壁阻挡子弹
  • 射击角度被限制
  • 必须通过间隙射击Boss

这两个否定协同工作——移动空间减少意味着玩家更难找到好的射击角度,射击空间减少意味着玩家更难对Boss造成伤害。

十、Boss14的技能组合分析

10.1 Boss14综合参数

参数 说明
hasWall true 拥有防护墙技能
wallPocketInterval 240 掏兜间隔
wallEmojis [🧱📦🔩⛓🛡🔨🚪🏰🧱📦] 墙壁emoji列表
vx 0.8 水平移动速度
shootInterval 100 射击间隔

10.2 墙壁+射击的协同效应

Boss14同时拥有防护墙和射击技能,两者产生协同效应:

协同1:墙壁保护Boss

  • 玩家的子弹被墙壁阻挡
  • Boss在墙壁后面安全射击
  • 相当于Boss拥有"物理护盾"

协同2:墙壁压缩空间+弹幕覆盖空间

  • 墙壁减少了可用移动空间
  • 弹幕覆盖了剩余空间
  • 玩家的回避选择越来越少

协同3:弹飞+弹幕的双重威胁

  • 被弹飞后,玩家处于失控状态
  • 在失控期间无法回避弹幕
  • 可能被弹幕击中+弹飞的双重伤害

10.3 Boss14的战斗节奏

Boss14的战斗节奏由掏兜周期决定:

时间 事件 墙壁数量 威胁等级
0-4s 首次掏兜预警 0→5 低→中
4-10s 5墙阶段 5
10-14s 扩展掏兜 5→10 中→高
14-20s 10墙阶段 10
20-24s 扩展掏兜 10→15 高→极高
24-30s 15墙阶段 15 极高
30-34s 扩展掏兜 15→20 极高
34+ 修复阶段 20(修复) 极高

如果不拆墙,约30秒后墙壁数量达到20,进入修复阶段。此时可用空间仅约40%,战斗变得极其困难。

10.4 拆墙vs射击Boss的策略权衡

玩家面临的核心策略权衡:拆墙还是射击Boss?

策略 子弹分配 优点 缺点
全力拆墙 100%拆墙 恢复空间和射击通道 Boss不受伤害
全力射击Boss 100%射Boss BossHP下降 墙壁越来越多
平衡分配 50%拆墙+50%射Boss 兼顾空间和伤害 两边都不够

最优策略可能是"阶段性切换":

  1. 墙壁少时(0-5个):全力射击Boss
  2. 墙壁中等时(5-10个):拆关键墙壁+射击Boss
  3. 墙壁多时(10+个):优先拆墙恢复空间
  4. 墙壁极多时(20+个):全力拆墙,避免空间绞杀

十一、掏兜动画的视觉设计深度分析

11.1 掏兜的叙事含义

“掏兜”(Pocket Pull)动画的叙事含义非常直观——Boss从"口袋"中掏出建筑材料来建造墙壁。这个动画有以下叙事功能:

  1. 赋予Boss"建造者"的身份——Boss不是凭空生成墙壁,而是从口袋中取出材料
  2. 提供视觉逻辑——墙壁的来源有了可视化的解释
  3. 增加Boss的拟人化——“掏兜"是一个人类的动作,使Boss更有"角色感”

11.2 🤚手的动画设计

🤚手动画在70%计时器时出现,这是一个精心设计的时机:

计时器阶段 手的状态 信息揭示
0-70% 无手 只有危险光环,不知道方向
70-85% 手伸出 可以看到大致方向
85-95% 手完全伸出 方向明确
95-100% emoji从手中飞出 目标位置确定

70%的出现时机确保了:

  • 早期不确定——0-70%期间玩家只知道"有东西要发生",但不知道是什么方向
  • 后期可预测——70-100%期间玩家可以看到手的方向和emoji的飞行轨迹
  • 总预警时间约1.2-1.8秒——30%的周期(72-108帧)是可见预警时间

11.3 emoji飞行动画

emoji从手中飞向目标位置的动画有以下功能:

  1. 显示墙壁类型——emoji的样式暗示了墙壁的emoji(🧱、📦等)
  2. 显示目标位置——emoji飞向的方向=墙壁出现的位置
  3. 增加动态感——飞行的emoji增加了视觉趣味性
  4. 提供最后预警——emoji到达目标前的几帧是最后的移动机会

11.4 危险光环(红色圆圈)

危险光环在掏兜预警期间出现:

属性 说明
颜色 红色 危险/警告色
形状 圆形 围绕Boss
透明度 可能随计时器变化 早期低,后期高
持续时间 整个预警期 从70%计时器到掏兜触发

危险光环的作用是"全局预警"——即使玩家没有注意到手的动画,红色光环也能吸引注意力,提示"Boss正在准备什么"。

十二、墙壁布局的空间分析

12.1 初始环形的战术空间

5方块初始环形创造了一个椭圆形的保护区域:

        [方块2]
    [方块3]  Boss  [方块1]
        [方块4]
              [方块0]

从玩家的视角,这个环形的影响:

方向 墙壁阻挡 间隙 可射击角度
右侧 方块0 被完全阻挡
右上 方块1 少量 大部分阻挡
左上 方块2 少量 大部分阻挡
左下 方块3 少量 大部分阻挡
右下 方块4 少量 大部分阻挡

72度的间隔意味着每个间隙约72度宽,但间隙的宽度取决于距离——距离Boss越远,间隙越宽。

12.2 环形半径的影响

80x/60y的椭圆半径决定了环形的大小:

半径方向 到Boss中心的距离 间隙宽度(72°间隔)
X方向 80px 80px ~100px(2×80×sin36°)
Y方向 60px 60px ~75px(2×60×sin36°)

X方向的间隙(100px)比Y方向(75px)更宽,这意味着从左右方向射击更容易通过间隙。

12.3 多层环形的嵌套

当墙壁扩展到10个以上时,可能形成多层环形:

外环: [方块5][方块6][方块7][方块8][方块9]
内环: [方块0][方块1][方块2][方块3][方块4]
          Boss

双层环形的间隙对齐情况:

情况 内外环间隙对齐 射击通道
完全对齐 内外间隙重叠 有直通通道
部分对齐 间隙部分重叠 有狭窄通道
完全错位 内外间隙不重叠 无直通通道

如果扩展时新方块恰好填充了内环的间隙,则双层环形几乎没有射击通道。这要求玩家必须在扩展前拆除关键墙壁,维持射击通道。

十三、弹飞与屏幕外死亡的连锁分析

13.1 弹飞→弹幕→死亡的连锁

弹飞后的连锁效应:

碰到墙壁 → 被弹飞(失控0.83秒) → 无法回避弹幕 → 被弹幕击中(-1生命) → 弹飞结束(可能在屏幕边缘) → 碰到另一面墙 → 再次弹飞 → 屏幕外 → 死亡

这个连锁的每个环节都有概率发生,整体致死概率:

连锁环节 发生概率 累计概率
碰到墙壁 取决于位置 变量
弹飞期间被弹幕击中 ~30%(假设) P×0.3
弹飞后碰到另一面墙 ~40%(假设) P×0.3×0.4
第二次弹飞后屏幕外 ~50%(假设) P×0.3×0.4×0.5

假设初始碰到墙壁的概率为P=0.5,则完整连锁的概率约为0.5×0.3×0.4×0.5=3%。虽然概率不高,但一旦发生就是灾难性的——从1次墙壁碰撞到死亡,中间可能损失2-3条生命。

13.2 墙壁包围的致死陷阱

当玩家被墙壁包围时,致死概率急剧上升:

  [墙][墙][墙]
  [墙] 玩家 [墙]
  [墙][墙][墙]

在这种布局中,任何方向的弹飞都会碰到另一面墙,产生连续弹飞。每次碰撞-1生命,3次碰撞后lives可能归零。

包围程度 可逃出方向 连续弹飞概率 致死风险
半包围(3面) 1-2个方向 中高
大半包围(4面) 1个方向
完全包围(5+面) 0个方向 极高 极高

完全包围是一个"死亡陷阱"——玩家无法通过正常移动逃脱,每次碰墙都被弹飞到另一面墙,形成无限循环直到死亡。

13.3 护盾在连锁中的关键作用

护盾在弹飞连锁中的作用是"断链"——消耗一次护盾,避免1次生命损失:

连锁状态 无护盾 有护盾 差异
第1次碰墙 -1生命 0(消耗护盾) 1条生命
第2次碰墙 -1生命 -1生命 0
第3次碰墙 -1生命 -1生命 0

护盾只在第1次碰撞时有效,但这次"断链"可能打破连锁——如果第1次弹飞后的位置不在其他墙壁附近,连锁就此终止。

十四、防护墙与Boss的协同战术

14.1 墙壁保护Boss的效率

墙壁保护Boss的效率取决于墙壁的布局和玩家的射击角度:

射击角度 墙壁阻挡数 到达Boss的子弹比例 保护效率
正面(0°) 1-2 30-50%
侧面(45°) 0-1 60-80%
对角(90°) 1-2 30-50%
后方(180°) 2-3 10-30%

后方射击(从Boss背后射击)的保护效率最高,因为墙壁通常集中在Boss前方。但如果Boss面向玩家,后方射击通常不可行。

14.2 Boss在墙壁保护下的射击

Boss在墙壁后面持续射击,创造了一种"堡垒"战术:

[弹幕]  [弹幕]  [弹幕]
  ↓       ↓       ↓
[墙][墙][Boss][墙][墙]

Boss从墙壁间隙中发射弹幕,同时墙壁保护Boss不受子弹伤害。这种"堡垒"布局使Boss在安全输出弹幕的同时,玩家难以有效反击。

14.3 反堡垒策略

面对Boss的堡垒战术,玩家可以采用以下反制策略:

策略1:拆墙开缺口

  • 集中火力拆除一面墙壁
  • 通过缺口射击Boss
  • 优点:创造持久的射击通道
  • 缺点:拆墙耗时,期间Boss持续输出

策略2:绕墙射击

  • 移动到墙壁间隙的正对方向
  • 通过间隙射击Boss
  • 优点:不需要拆墙
  • 缺点:间隙可能很窄,命中率低

策略3:穿墙弹幕

  • 如果有穿墙武器或技能
  • 直接穿过墙壁射击Boss
  • 优点:无视墙壁
  • 缺点:可能不存在此类武器

策略4:引诱Boss移动

  • 通过移动引诱Boss改变位置
  • 利用Boss移动时墙壁滞后的时间窗口
  • 优点:利用动态间隙
  • 缺点:需要精确的移动控制

十五、防护墙系统的代码实现详解

15.1 掏兜计时器逻辑(Index.ets:1431-1456)

if (boss.hasWall) {
  boss.wallPocketTimer++;
  let threshold = aliveWallCount === 0 ? 
    boss.wallPocketInterval : boss.wallPocketInterval + 120;
  if (boss.wallPocketTimer >= threshold) {
    boss.wallPocketActive = true;
    boss.wallPocketAnimTime = boss.wallPocketAnimMax; // 60
  }
  if (boss.wallPocketActive) {
    boss.wallPocketAnimTime--;
    if (boss.wallPocketAnimTime <= 0) {
      boss.wallPocketActive = false;
      addWallBlocks(boss);
    }
  }
}

15.2 addWallBlocks函数

function addWallBlocks(boss) {
  let existingCount = boss.wallBlocks.filter(w => w.alive).length;
  if (existingCount === 0) {
    // 5-block环形
    for (let i = 0; i < 5; i++) {
      let angle = (i / 5) * Math.PI * 2;
      boss.wallBlocks.push({
        x: boss.x + Math.cos(angle) * 80,
        y: boss.y + Math.sin(angle) * 60,
        hp: 3, maxHp: 3, size: 22, alive: true,
        emoji: boss.wallEmojis[i % boss.wallEmojis.length]
      });
    }
  } else if (existingCount < 20) {
    // 扩展+5
    for (let i = 0; i < 5; i++) {
      let angle = (i / 5) * Math.PI * 2 + offset;
      let radius = 80 + layer * 20;
      boss.wallBlocks.push({
        x: boss.x + Math.cos(angle) * radius,
        y: boss.y + Math.sin(angle) * (radius * 0.75),
        hp: 3, maxHp: 3, size: 22, alive: true,
        emoji: boss.wallEmojis[i % boss.wallEmojis.length]
      });
    }
  } else {
    // 修复+3
    let deadWalls = boss.wallBlocks.filter(w => !w.alive);
    for (let i = 0; i < Math.min(3, deadWalls.length); i++) {
      deadWalls[i].alive = true;
      deadWalls[i].hp = deadWalls[i].maxHp;
    }
    // 如果所有都存活,新增3个
    if (deadWalls.length < 3) {
      for (let i = 0; i < 3 - deadWalls.length; i++) {
        // 新增方块...
      }
    }
  }
}

15.3 子弹↔墙碰撞(updateBossWall函数)

for (let bullet of bullets) {
  if (!bullet.alive) continue;
  for (let wall of boss.wallBlocks) {
    if (!wall.alive) continue;
    let dist = distance(bullet, wall);
    if (dist < wall.size * 0.9) {
      bullet.alive = false;
      wall.hp--;
      if (wall.hp <= 0) {
        wall.alive = false;
      }
      break; // 一颗子弹只碰一面墙
    }
  }
}

15.4 墙↔玩家碰撞(checkWallPlayerCollision函数)

for (let wall of boss.wallBlocks) {
  if (!wall.alive) continue;
  let dist = distance(player, wall);
  if (dist < wall.size + player.width / 2) {
    let knockForce = 18;
    let dx = player.x - wall.x;
    let dy = player.y - wall.y;
    let len = Math.sqrt(dx * dx + dy * dy);
    player.knockbackVx = (dx / len) * knockForce;
    player.knockbackVy = (dy / len) * knockForce;
    
    if (player.shield > 0) {
      player.shield = 0;
    } else {
      lives -= 1;
    }
    break; // 一次只碰一面墙
  }
}

15.5 弹飞衰减(updatePlayer函数)

if (player.knockbackVx !== 0 || player.knockbackVy !== 0) {
  player.x += player.knockbackVx;
  player.y += player.knockbackVy;
  player.knockbackVx *= 0.92;
  player.knockbackVy *= 0.92;
  if (Math.abs(player.knockbackVx) < 0.3) player.knockbackVx = 0;
  if (Math.abs(player.knockbackVy) < 0.3) player.knockbackVy = 0;
}

15.6 屏幕外死亡判定

if (player.x < -player.width || player.x > canvasWidth + player.width ||
    player.y < -player.height || player.y > canvasHeight + player.height) {
  // 屏幕外死亡
  lives -= 1;
  // 或 game over
}

十六、防护墙系统的数值参考表

16.1 核心参数

参数 单位 说明
wallPocketInterval 240 基础掏兜间隔
wallPocketAnimMax 60 动画持续帧数
WallBlock.hp 3 HP 方块生命值
WallBlock.maxHp 3 HP 方块最大生命值
WallBlock.size 22 px 方块大小
knockForce 18 px/帧 弹飞力度
衰减系数 0.92 - 每帧速度衰减
停止阈值 0.3 px/帧 弹飞停止速度

16.2 时间参数

参数 帧数 秒数(60FPS) 说明
无墙掏兜间隔 240 4.0 无墙时的间隔
有墙掏兜间隔 360 6.0 有墙时的间隔
掏兜动画 60 1.0 动画时长
手出现时机 70%计时器 变化 预警开始
弹飞持续 ~50 ~0.83 弹飞持续时间
墙壁生成到修复 ~30s 30 达到20墙的时间

16.3 空间参数

参数 说明
初始环形X半径 80px 椭圆X方向
初始环形Y半径 60px 椭圆Y方向
方块碰撞半径 19.8px size×0.9
弹飞总位移 ~215px 单次弹飞
弹飞最大速度 18px/帧 初始弹飞速度

16.4 Boss14综合参数

参数 说明
hasWall true 拥有防护墙技能
wallPocketInterval 240 掏兜间隔
wallEmojis [🧱📦🔩⛓🛡🔨🚪🏰🧱📦] 墙壁emoji
vx 0.8 水平移动速度
shootInterval 100 射击间隔

十七、防护墙系统的设计哲学

17.1 空间作为资源

防护墙系统将"空间"从免费的背景资源转化为有限的、可消耗的战斗资源。在没有墙壁的情况下,玩家的移动空间是"无限的"(受画布边界限制),可以自由选择任何位置。但墙壁的存在使空间变成了需要管理和保护的资源。

这种"空间资源化"的设计增加了游戏的策略深度:

  • 空间不足时,玩家的选择减少
  • 拆墙可以回收空间,但消耗子弹
  • 不拆墙会导致空间持续压缩
  • 空间管理成为除回避和射击外的第三维策略

17.2 物理障碍的情感效应

物理障碍(墙壁)比弹幕(投射物)产生更强的心理压迫:

威胁类型 心理效应 持续性 对抗方式
弹幕 即时威胁 短暂(飞过后消失) 回避
墙壁 持续威胁 持久(直到被击毁) 拆除
弹飞 位置威胁 中等(0.83秒) 无法对抗(已发生后)

弹幕是"流动的威胁"——它们经过后就不会再造成伤害。墙壁是"固定的威胁"——它们一直在那里,持续影响玩家的移动和射击。这种持续性使墙壁的心理压迫更加持久。

17.3 掏兜动画的"公平警告"哲学

掏兜动画体现了游戏设计中"公平警告"(Fair Warning)的哲学——任何对玩家有重大影响的事件都应该有可读的预警。

没有预警 有预警
墙壁突然出现 掏兜动画60帧后墙壁出现
玩家感到不公平 玩家有反应时间
死亡感觉随机 死亡感觉是可避免的
游戏体验差 游戏体验好

60帧的掏兜动画+70%计时器时的手出现,确保了玩家有1-1.8秒的预警时间。这足以让玩家移动位置或准备射击新生成的墙壁。

十八、总结与核心要点

防护墙技能是EmojiShooter中最复杂、最多层次的空间控制机制。它通过物理障碍、弹飞力学、屏幕外死亡和护盾抗弹飞四个子系统,创造了一个完整的"空间博弈"战斗环境。

核心要点:

  1. 掏兜动画作为电报系统——60帧动画+70%计时器手出现,提供1-1.8秒预警
  2. 动态间隔——无墙240帧/有墙360帧,引导拆墙或保留的策略选择
  3. 三种砌墙策略——初始5环/扩展+5/修复+3,根据墙壁数量动态调整
  4. WallBlock——3HP/22px,每方块需3发子弹击毁
  5. 子弹↔墙碰撞——子弹被阻挡+墙壁受伤,墙壁作为Boss的物理护盾
  6. 弹飞力学——力度18/衰减0.92/总位移215px/持续50帧
  7. 屏幕外死亡——弹飞后飞出画布=死亡,创造弹飞→死亡的连锁
  8. 连续弹飞——碰到多面墙的连锁反应,可能瞬间损失多条生命
  9. 护盾抗弹飞——消耗护盾抵消1次生命损失,但不抵消弹飞效果
  10. 空间否定——墙壁同时否定移动空间和射击空间,创造"空间绞杀"
  11. 墙壁+射击协同——墙壁保护Boss+弹幕覆盖剩余空间+弹飞使玩家失控
  12. 拆墙vs射击Boss的策略权衡——资源分配的核心决策
  13. 修复机制——20墙后修复死亡方块,使拆墙的长期效果有限
  14. 建筑主题emoji——🧱📦🔩⛓🛡🔨🚪🏰增强视觉叙事
  15. 公平警告哲学——掏兜动画确保玩家有反应时间,避免不公平感

防护墙系统的设计体现了EmojiShooter对"空间博弈"的深刻理解——它不仅是一个障碍物系统,更是一个完整的空间经济学模型,将空间从背景资源转化为需要管理的战斗资源,将弹幕游戏从"回避+射击"的二维模式推向"回避+射击+空间管理"的三维模式。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐