如何实现轮盘(幸运大转盘)的平滑减速调度
实现幸运大转盘的平滑减速效果
我明白你现在的痛点——分段配置tick-duration虽然能实现减速,但每段之间的转速突变太明显,体验很生硬。咱们可以用连续的数学缓动函数来替代分段配置,让轮盘的转速随着转动次数增加自然、平滑地降低,完全没有突兀的变化。
核心思路
我们的目标是:从第0次转动到第maxRuns次转动,让轮盘的转动间隔(也就是你的tick-duration)从最快的50ms(对应tick-duration=1)平滑增加到一个最慢间隔(比如500ms,对应tick-duration=10)。这里推荐用**二次缓出(Ease Out Quad)**函数,它的特点是前期减速平缓,后期减速越来越快,完美贴合现实中转盘的物理减速规律。
对应的计算公式很简单:
// 计算当前转动进度(0到1之间) double progress = runs / (double) runData.getMaxRuns(); // 二次缓出处理,让后期减速更明显 double easedProgress = Math.pow(progress, 2); // 计算当前的tick-duration,从MIN到MAX平滑过渡 int currentTickDuration = MIN_TICK_DURATION + (int) Math.round((MAX_TICK_DURATION - MIN_TICK_DURATION) * easedProgress);
其中:
MIN_TICK_DURATION:最快转速对应的间隔次数,设为1(对应每50ms转一次)MAX_TICK_DURATION:最慢转速对应的间隔次数,你可以根据需求调整(比如10就是500ms转一次)runs:当前已经完成的转动次数
修改后的完整代码
我帮你重构了原来的HologramWheelTask,去掉了生硬的分段查找逻辑,换成了平滑的数学计算:
package com.dbsoftwares.dangerwheel.wheel.hologram; import com.dbsoftwares.dangerwheel.utils.objects.CircleColor; import com.dbsoftwares.dangerwheel.utils.objects.WheelRunData; import com.dbsoftwares.dangerwheel.wheel.WheelCircle; import com.dbsoftwares.dangerwheel.wheel.WheelManager; import com.gmail.filoghost.holographicdisplays.api.Hologram; import lombok.RequiredArgsConstructor; import org.bukkit.scheduler.BukkitRunnable; import java.util.List; @RequiredArgsConstructor public class HologramWheelTask extends BukkitRunnable { private final WheelManager manager; private final Hologram hologram; private final WheelCircle circle; private final WheelRunData runData; private final int startIdx; private int ticks = 0; private int runs = 0; // 可配置的减速参数:建议放到WheelRunData里让外部配置 private static final int MIN_TICK_DURATION = 1; // 最快:每50ms转动一次 private static final int MAX_TICK_DURATION = 10; // 最慢:每500ms转动一次 @Override public void run() { final int currentTickDuration = calculateCurrentTickDuration(); if (ticks < currentTickDuration) { ticks++; return; } ticks = 0; runs++; // 执行轮盘转动的核心逻辑(更新全息显示等) updateWheelDisplay(); if (runs >= runData.getMaxRuns()) { cancel(); // 这里可以添加轮盘停止后的逻辑,比如触发奖励、通知玩家等 } } private int calculateCurrentTickDuration() { final int maxRuns = runData.getMaxRuns(); if (maxRuns == 0) { return MIN_TICK_DURATION; } // 二次缓出公式,实现平滑减速 final double progress = runs / (double) maxRuns; final double easedProgress = Math.pow(progress, 2); // 计算当前的tick间隔,取整保证整数类型 return MIN_TICK_DURATION + (int) Math.round((MAX_TICK_DURATION - MIN_TICK_DURATION) * easedProgress); } private void updateWheelDisplay() { // 根据当前转动次数更新全息显示的内容 final List<CircleColor> currentColors = circle.getColorsForRotation(startIdx + runs); hologram.clearLines(); hologram.appendTextLine(buildLine(currentColors)); } private String buildLine(final List<CircleColor> colors) { final StringBuilder line = new StringBuilder(); colors.forEach(color -> line.append(color.getAsText())); return line.toString(); } }
关键改进说明
- 去掉生硬的分段逻辑:不再需要
WheelRun的分段配置,用一行数学计算就能实现完全平滑的减速曲线 - 缓动函数可选:如果你觉得二次缓出的效果不符合预期,可以换其他缓动函数:
- 线性缓动:
easedProgress = progress,减速匀速,效果比较平淡 - 三次缓出:
Math.pow(progress, 3),后期减速比二次更明显 - 对数缓动:
1 - Math.log10(9*progress +1)/Math.log10(10),前期几乎不减速,后期快速减速
- 线性缓动:
- 可配置化:把
MIN_TICK_DURATION和MAX_TICK_DURATION移到WheelRunData中,让你可以在外部配置不同的减速参数,适配不同场景 - 代码更简洁高效:原来的流式查找逻辑被替换成简单的数学运算,性能更好,也更容易维护
额外优化提示
如果想要更贴近真实物理的转盘效果,可以考虑加入目标位置校准:比如计算出需要转动到目标位置的准确次数,然后调整减速曲线,确保最终刚好停在正确的位置,而不是严格按照maxRuns停止(这需要额外的逻辑来计算目标位置和剩余转动次数)。
内容的提问来源于stack exchange,提问作者Dieter Blancke
相关产品推荐
相关产品推荐

