消消方块阵换皮表情
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

319 lines
12 KiB

1 week ago
import MTools from '../../../mx/tools/MTools';
import { sys } from 'cc';
import { sdkConfig } from '../../sdk/sdkConfig';
import { MiniGamePlatform } from '../../sdk/platform/MiniGamePlatform';
import { SmartPreload } from '../../game/SmartPreload';
/** 堆叠高度分档:仅影响 VFX/SFX 表现,不改伤害/逻辑 */
enum StackPerfTier {
Normal = 0,
/** ≥30m */
Mid = 1,
/** ≥60m */
High = 2,
}
/**
* /+
* - iOS +
* - Static unfreeze
* - VFX/SFX/UI
* - triggerGC
*/
export class BattlePerformance {
/** 与左侧高度尺 30m 里程碑一致 */
static readonly HIGH_STACK_M = 30;
/** 与左侧高度尺 60m 里程碑一致 */
static readonly ULTRA_HIGH_STACK_M = 60;
/** 平台 onMemoryWarning 升档:0=无 1=轻 2=中 3=重 */
private static _memPressureLevel = 0;
/** 微信小游戏 + iOS:需要重度性能节流 */
static isWeChatIos(): boolean {
if (sys.isBrowser) {
const p = sdkConfig.browserBattlePerfProfile;
if (p === 'ios') return true;
if (p === 'android') return false;
return false;
}
return sys.platform === sys.Platform.WECHAT_GAME && sys.os === sys.OS.IOS;
}
/** 抖音小游戏:轻量战斗节流(主投放渠道) */
static isDouyinMiniGame(): boolean {
if (sys.isBrowser) {
return sdkConfig.browserBattlePerfProfile === 'douyin';
}
return sys.platform === sys.Platform.BYTEDANCE_MINI_GAME;
}
/** 抖音鸿蒙(OpenHarmony):与抖音同档并略加强节流 */
static isDouyinHarmonyOS(): boolean {
if (!this.isDouyinMiniGame()) return false;
MiniGamePlatform.init();
return MiniGamePlatform.isHarmonyOS;
}
/** 抖音系(含鸿蒙)是否启用轻量节流 */
private static _isDouyinPerfHost(): boolean {
return this.isDouyinMiniGame();
}
/** 平台内存告警回调:只升档节流,不触发 GC */
static onMemoryPressure(warnLevel: number): void {
if (warnLevel >= 15) {
this._memPressureLevel = 3;
} else if (warnLevel >= 10) {
this._memPressureLevel = Math.max(this._memPressureLevel, 2);
} else if (warnLevel >= 5) {
this._memPressureLevel = Math.max(this._memPressureLevel, 1);
}
console.warn(`[BattlePerf] memory pressure → level=${this._memPressureLevel} (warn=${warnLevel})`);
SmartPreload.onMemoryPressure(warnLevel);
}
static resetMemoryPressure(): void {
this._memPressureLevel = 0;
}
static memoryPressureLevel(): number {
return this._memPressureLevel;
}
/** 内存压力对特效保留率的乘数(越小越抽稀) */
private static _memPressureVfxMul(): number {
switch (this._memPressureLevel) {
case 3: return 0.45;
case 2: return 0.62;
case 1: return 0.78;
default: return 1;
}
}
/** 当前生效的性能档位(日志/调试) */
static activePerfProfile(): 'ios' | 'android' | 'douyin' | 'douyin-harmony' {
if (this.isWeChatIos()) return 'ios';
if (this.isDouyinHarmonyOS()) return 'douyin-harmony';
if (this.isDouyinMiniGame()) return 'douyin';
return 'android';
}
/** 浏览器切换性能档位;进战斗前切换更稳妥 */
static setBrowserPerfProfile(profile: 'auto' | 'ios' | 'android' | 'douyin'): void {
if (!sys.isBrowser) {
console.warn('[BattlePerf] setBrowserPerfProfile 仅浏览器预览有效');
return;
}
sdkConfig.browserBattlePerfProfile = profile;
const map = gg.game?.CurentBattle?.TetraMap;
if (map?.isValid) {
map.recalcInterval = this.tetriRecalcIntervalSec();
}
console.log(`[BattlePerf] browserBattlePerfProfile=${profile} → active=${this.activePerfProfile()}`);
}
/** 全平台:落稳后冻 Static,撞击时仍会 unfreeze(试塌塔/减算力) */
static shouldFreezeTetriPhysics(): boolean {
return true;
}
/** 落稳后至少等待多久(ms)才允许冻结;buried=塔顶活跃带以下可更快冻 */
static tetriFreezeMinSettledMs(buried: boolean = false): number {
if (buried) return this.isWeChatIos() ? 700 : 450;
return this.isWeChatIos() ? 1600 : 1200;
}
/** 落稳后连续低速帧数才冻结(约 60fps) */
static tetriFreezeSleepStreak(buried: boolean = false): number {
if (buried) return this.isWeChatIos() ? 50 : 36;
return this.isWeChatIos() ? 120 : 90;
}
/**
* px
* 3 / Dynamic
*/
static tetriFreezeActiveBandPx(): number {
return 120;
}
/** 冻结前合成速度上限 */
static tetriFreezeMaxSpeed(): number {
return 0.35;
}
/** 冻结前 |角速度| 上限 */
static tetriFreezeMaxAng(): number {
return 0.2;
}
/** 冻结前 |vy| 上限 */
static tetriFreezeMaxVy(): number {
return 0.35;
}
/** 倾斜超过此值(相对 90° 网格)禁止冻 Static,避免「翘着冻住」 */
static tetriFreezeMaxTiltFromGridDeg(): number {
return 8;
}
/** 安静窗口内角度漂移超过此值则重新计时(慢倾覆) */
static tetriFreezeMaxQuietAngleDriftDeg(): number {
return 2.5;
}
/** 白线全表重算间隔(秒) */
static tetriRecalcIntervalSec(): number {
if (this.isWeChatIos()) return 0.3;
if (this.isDouyinHarmonyOS()) return 0.22;
if (this._isDouyinPerfHost()) return 0.18;
if (this._memPressureLevel >= 2) return 0.2;
return 0.1;
}
/** 武器 CD 倍率(>1 发射更慢)。仅极端机型启用,禁止把抖音常态当“优化”改战斗数值 */
static weaponCdMul(): number {
if (this.isWeChatIos()) return 1.4;
return 1;
}
/** 同武器两次发射最小间隔(ms);仅微信 iOS 启用 */
static weaponFireMinIntervalMs(): number {
if (this.isWeChatIos()) return 150;
return 0;
}
/** 秒伤 UI 刷新最小间隔(ms) */
static dpsUiMinIntervalMs(): number {
if (this.isWeChatIos()) return 450;
if (this.isDouyinHarmonyOS()) return 400;
if (this._isDouyinPerfHost()) return 350;
if (this._memPressureLevel >= 1) return 280;
return 0;
}
/** 伤害飘字单帧上限 */
static hurtFloatMaxPerFrame(): number {
let max = 15;
if (this.isWeChatIos()) max = 8;
else if (this.isDouyinHarmonyOS()) max = 6;
else if (this._isDouyinPerfHost()) max = 8;
if (this._memPressureLevel >= 3) max = Math.min(max, 5);
else if (this._memPressureLevel >= 2) max = Math.min(max, 6);
else if (this._memPressureLevel >= 1) max = Math.min(max, 7);
return max;
}
static getTier(): StackPerfTier {
const b = gg.game?.CurentBattle;
if (!b || b.IsGameOver) return StackPerfTier.Normal;
const h = b.TetriStackHeightMeters;
if (h >= this.ULTRA_HIGH_STACK_M) return StackPerfTier.High;
if (h >= this.HIGH_STACK_M) return StackPerfTier.Mid;
return StackPerfTier.Normal;
}
static isHighStackMode(): boolean {
return this.getTier() >= StackPerfTier.Mid;
}
static isUltraHighStackMode(): boolean {
return this.getTier() >= StackPerfTier.High;
}
/** 一次性爆炸/受击特效:高堆叠时抽样显示 */
static rollShowCombatVfx(customRate?: number): boolean {
const tier = this.getTier();
const memMul = this._memPressureVfxMul();
if (tier === StackPerfTier.Normal) {
if (this.isWeChatIos() && Math.random() > 0.62 * memMul) return false;
if (this.isDouyinHarmonyOS() && Math.random() > 0.68 * memMul) return false;
if (this._isDouyinPerfHost() && Math.random() > 0.75 * memMul) return false;
if (this._memPressureLevel >= 1 && Math.random() > memMul) return false;
return true;
}
const tierRate = tier === StackPerfTier.High ? 0.28 : 0.5;
const rate = (customRate != null ? Math.min(customRate, tierRate) : tierRate) * memMul;
if (this.isWeChatIos()) {
return Math.random() < rate * 0.75;
}
if (this.isDouyinHarmonyOS()) {
return Math.random() < rate * 0.78;
}
if (this._isDouyinPerfHost()) {
return Math.random() < rate * 0.85;
}
return Math.random() < rate;
}
static vfxScaleMul(): number {
let mul: number;
switch (this.getTier()) {
case StackPerfTier.High: mul = 0.58; break;
case StackPerfTier.Mid: mul = 0.72; break;
default: mul = 1; break;
}
if (this._memPressureLevel >= 2) mul *= 0.88;
return mul;
}
static adjustVfxScale(base: number): number {
return base * this.vfxScaleMul();
}
/** 怪物受击特效节流(ms) */
static hitVfxCooldownMs(): number {
let ms: number;
switch (this.getTier()) {
case StackPerfTier.High: ms = 650; break;
case StackPerfTier.Mid: ms = 420; break;
default: ms = 200; break;
}
if (this.isWeChatIos()) ms = Math.floor(ms * 1.75);
else if (this.isDouyinHarmonyOS()) ms = Math.floor(ms * 1.5);
else if (this._isDouyinPerfHost()) ms = Math.floor(ms * 1.35);
if (this._memPressureLevel >= 2) ms = Math.floor(ms * 1.25);
else if (this._memPressureLevel >= 1) ms = Math.floor(ms * 1.12);
return ms;
}
static isWeaponBattleSfx(path: string, name: string): boolean {
if (path.includes('武器音效')) return true;
return /命中|受击|子弹|飞弹|无人机攻击|溅水|冲击波/.test(name);
}
/**
* /
* MTools.beforeTimes true
* return beforeTimes
*/
static shouldPlayWeaponSfx(path: string, name: string): boolean {
if (!this.isWeaponBattleSfx(path, name)) return true;
// 低堆叠且内存压力不高:不节流,保证前几波音效完整
const needThrottle = this.isHighStackMode()
|| this.isWeChatIos()
|| this.isDouyinHarmonyOS()
|| this._memPressureLevel >= 2;
if (!needThrottle) return true;
let minIntervalMs = 80;
if (this.isHighStackMode()) {
minIntervalMs = this.getTier() === StackPerfTier.High ? 140 : 90;
}
if (this.isWeChatIos()) {
minIntervalMs = Math.max(minIntervalMs, 160);
} else if (this.isDouyinHarmonyOS()) {
minIntervalMs = Math.max(minIntervalMs, 150);
} else if (this._isDouyinPerfHost()) {
minIntervalMs = Math.max(minIntervalMs, 120);
}
if (this._memPressureLevel >= 2) {
minIntervalMs = Math.max(minIntervalMs, 180);
}
// 按音效名分别节流,避免所有武器抢同一把锁
return !MTools.beforeTimes(minIntervalMs, `battle_sfx_${name || path}`);
}
}