This commit is contained in:
NIGGER SLAYER 2026-04-16 16:32:54 +08:00
parent 17cf24564f
commit 3b6ba4804e
9 changed files with 181 additions and 92 deletions

View File

@ -60,10 +60,10 @@
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationLandscapeLeft</string>

View File

@ -188,6 +188,28 @@ class BaishunJsBridge {
});
if (!window.__baishunNetworkDebugReady) {
window.__baishunNetworkDebugReady = true;
if (typeof document !== 'undefined' && typeof document.addEventListener === 'function' && !window.__baishunInputDebugReady) {
window.__baishunInputDebugReady = true;
window.__baishunInputDebugCount = 0;
['pointerdown', 'touchstart', 'mousedown', 'click'].forEach(function(eventName) {
document.addEventListener(eventName, function(event) {
if ((window.__baishunInputDebugCount || 0) >= 24) {
return;
}
window.__baishunInputDebugCount = (window.__baishunInputDebugCount || 0) + 1;
const target = event && event.target;
const touch = event && event.touches && event.touches.length ? event.touches[0] : null;
postDebug('input.' + eventName, {
x: touch ? touch.clientX : (event && event.clientX),
y: touch ? touch.clientY : (event && event.clientY),
pointerType: event && event.pointerType,
targetTag: target && target.tagName,
targetId: target && target.id,
targetClass: target && target.className
});
}, true);
});
}
if (typeof window.addEventListener === 'function') {
window.addEventListener('error', function(event) {
const target = event && event.target;

View File

@ -1,6 +1,8 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
@ -32,6 +34,10 @@ class BaishunGamePage extends StatefulWidget {
class _BaishunGamePageState extends State<BaishunGamePage> {
final RoomGameRepository _repository = RoomGameRepository();
final Set<Factory<OneSequenceGestureRecognizer>> _webGestureRecognizers =
<Factory<OneSequenceGestureRecognizer>>{
Factory<OneSequenceGestureRecognizer>(() => EagerGestureRecognizer()),
};
late final WebViewController _controller;
Timer? _bridgeBootstrapTimer;
Timer? _loadingFallbackTimer;
@ -51,6 +57,16 @@ class _BaishunGamePageState extends State<BaishunGamePage> {
String _lastJsCallback = '';
String _lastConfigSummary = '';
int get _launchOrientationValue {
if (widget.launchModel.entry.orientation != 0) {
return widget.launchModel.entry.orientation;
}
if (widget.game.orientation != 0) {
return widget.game.orientation;
}
return 1;
}
@override
void initState() {
super.initState();
@ -185,7 +201,7 @@ class _BaishunGamePageState extends State<BaishunGamePage> {
final entryUrl = widget.launchModel.entry.entryUrl.trim();
_appendDebugLog(
'launch',
'load entry launchMode=${widget.launchModel.entry.launchMode} url=$entryUrl',
'load entry launchMode=${widget.launchModel.entry.launchMode} orientation=$_launchOrientationValue url=$entryUrl',
);
if (entryUrl.isEmpty || entryUrl.startsWith('mock://')) {
final html = await rootBundle.loadString(
@ -569,29 +585,14 @@ class _BaishunGamePageState extends State<BaishunGamePage> {
}
await _closeAndExit();
},
child: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24.w),
topRight: Radius.circular(24.w),
),
child: Container(
width: ScreenUtil().screenWidth,
height: ScreenUtil().screenHeight * 0.8,
color: const Color(0xFF081915),
child: Scaffold(
backgroundColor: const Color(0xFF081915),
body: SafeArea(
bottom: false,
child: Column(
children: [
SizedBox(height: 10.w),
Container(
width: 34.w,
height: 4.w,
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.28),
borderRadius: BorderRadius.circular(99.w),
),
),
SizedBox(height: 6.w),
Padding(
padding: EdgeInsets.symmetric(horizontal: 10.w),
padding: EdgeInsets.fromLTRB(10.w, 6.w, 10.w, 6.w),
child: Row(
children: [
IconButton(
@ -623,12 +624,18 @@ class _BaishunGamePageState extends State<BaishunGamePage> {
child: Stack(
children: [
Positioned.fill(
child: WebViewWidget(controller: _controller),
child: WebViewWidget(
controller: _controller,
gestureRecognizers: _webGestureRecognizers,
),
),
if (_errorMessage != null) _buildErrorState(),
if (_isLoading && _errorMessage == null)
const BaishunLoadingView(
message: 'Waiting for gameLoaded...',
const IgnorePointer(
ignoring: true,
child: BaishunLoadingView(
message: 'Waiting for gameLoaded...',
),
),
Positioned(
left: 12.w,
@ -640,7 +647,8 @@ class _BaishunGamePageState extends State<BaishunGamePage> {
gameId: widget.game.gameId,
gameSessionId: widget.launchModel.gameSessionId,
entryUrl: widget.launchModel.entry.entryUrl,
launchMode: widget.launchModel.entry.launchMode,
launchMode:
'${widget.launchModel.entry.launchMode} / ori=$_launchOrientationValue',
loading: _isLoading,
pageFinished: _didFinishPageLoad,
receivedBridgeMessage: _didReceiveBridgeMessage,

View File

@ -6,7 +6,6 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:provider/provider.dart';
import 'package:yumi/modules/room_game/data/models/room_game_models.dart';
import 'package:yumi/modules/room_game/data/room_game_repository.dart';
import 'package:yumi/shared/tools/sc_lk_dialog_util.dart';
import 'package:yumi/modules/room_game/views/baishun_game_page.dart';
import 'package:yumi/services/audio/rtc_manager.dart';
import 'package:yumi/ui_kit/components/custom_cached_image.dart';
@ -14,10 +13,7 @@ import 'package:yumi/ui_kit/components/sc_tts.dart';
import 'package:yumi/ui_kit/components/text/sc_text.dart';
class RoomGameListSheet extends StatefulWidget {
const RoomGameListSheet({
super.key,
required this.roomContext,
});
const RoomGameListSheet({super.key, required this.roomContext});
final BuildContext roomContext;
@ -92,15 +88,34 @@ class _RoomGameListSheetState extends State<RoomGameListSheet> {
if (!widget.roomContext.mounted) {
return;
}
showBottomInBottomDialog(
widget.roomContext,
BaishunGamePage(
roomId: _roomId,
game: game,
launchModel: launchModel,
await Navigator.of(widget.roomContext).push(
PageRouteBuilder<void>(
transitionDuration: const Duration(milliseconds: 260),
reverseTransitionDuration: const Duration(milliseconds: 220),
pageBuilder:
(context, animation, secondaryAnimation) => BaishunGamePage(
roomId: _roomId,
game: game,
launchModel: launchModel,
),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
final curved = CurvedAnimation(
parent: animation,
curve: Curves.easeOutCubic,
reverseCurve: Curves.easeInCubic,
);
return FadeTransition(
opacity: curved,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 0.05),
end: Offset.zero,
).animate(curved),
child: child,
),
);
},
),
barrierColor: Colors.black54,
barrierDismissible: false,
);
} catch (error) {
SCTts.show('Launch failed');
@ -184,12 +199,16 @@ class _RoomGameListSheetState extends State<RoomGameListSheet> {
return Padding(
padding: EdgeInsets.only(bottom: 12.w),
child: Row(
children: List.generate(_itemsPerRow, (columnIndex) {
children: List.generate(_itemsPerRow, (
columnIndex,
) {
final itemIndex =
rowIndex * _itemsPerRow + columnIndex;
return Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 4.w),
padding: EdgeInsets.symmetric(
horizontal: 4.w,
),
child:
itemIndex < items.length
? _RoomGameTile(
@ -197,7 +216,10 @@ class _RoomGameListSheetState extends State<RoomGameListSheet> {
loading:
_launchingGameId ==
items[itemIndex].gameId,
onTap: () => _openGame(items[itemIndex]),
onTap:
() => _openGame(
items[itemIndex],
),
)
: const SizedBox.shrink(),
),
@ -329,10 +351,6 @@ class _RoomGameTile extends StatelessWidget {
borderRadius: 24.w,
);
}
return Icon(
Icons.sports_esports_outlined,
size: 22.w,
color: Colors.white,
);
return Icon(Icons.sports_esports_outlined, size: 22.w, color: Colors.white);
}
}

View File

@ -54,9 +54,9 @@ class _RoomBottomWidgetState extends State<RoomBottomWidget> {
children: [
_buildChatEntry(inputWidth),
giftAction,
messageAction,
_buildMicAction(rtcProvider),
menuAction,
messageAction,
],
)
: Row(
@ -66,9 +66,9 @@ class _RoomBottomWidgetState extends State<RoomBottomWidget> {
const Spacer(),
giftAction,
SizedBox(width: 14.w),
messageAction,
SizedBox(width: 14.w),
menuAction,
SizedBox(width: 14.w),
messageAction,
],
),
);
@ -145,9 +145,9 @@ class _RoomBottomWidgetState extends State<RoomBottomWidget> {
builder: (_, allUnReadCount, __) {
final action = RoomBottomCircleAction(
child: Image.asset(
"sc_images/room/sc_icon_botton_message.png",
width: 20.w,
height: 20.w,
"sc_images/room/sc_icon_botton_message_custom.png",
width: 22.w,
height: 22.w,
fit: BoxFit.contain,
),
);
@ -206,8 +206,8 @@ class _RoomBottomWidgetState extends State<RoomBottomWidget> {
child: RoomBottomCircleAction(
child: Image.asset(
"sc_images/room/${provider.isMic ? 'sc_icon_botton_mic_close' : 'sc_icon_botton_mic_open'}.png",
width: 20.w,
height: 20.w,
width: 24.w,
height: 24.w,
fit: BoxFit.contain,
gaplessPlayback: true,
),

View File

@ -70,8 +70,10 @@ class _SCSvgaAssetWidgetState extends State<SCSvgaAssetWidget>
if (!mounted || widget.assetPath != assetPath) {
return;
}
_loadedAssetPath = assetPath;
_controller.videoItem = movieEntity;
setState(() {
_loadedAssetPath = assetPath;
_controller.videoItem = movieEntity;
});
_syncPlayback(restartIfActive: true);
} catch (error) {
debugPrint('[SCSVGA] load failed asset=$assetPath error=$error');

Binary file not shown.

After

Width:  |  Height:  |  Size: 827 B

View File

@ -85,26 +85,57 @@ def append_common_flutter_args(command: list[str], args: argparse.Namespace) ->
command.extend(["--dart-define-from-file", item])
def should_build_ios(platform: str) -> bool:
return platform in {"all", "ios"}
def should_build_android(platform: str) -> bool:
return platform in {"all", "android", "android-google-play", "android-local-arm64"}
def android_build_profile(platform: str) -> str:
if platform == "android-google-play":
return "google-play"
if platform == "android-local-arm64":
return "local-arm64"
return "full"
def build_android(args: argparse.Namespace, manifest: dict[str, object]) -> None:
android_symbols_dir = ROOT / "build" / "symbols" / "android"
android_output_dir = args.output_dir / "android"
profile = android_build_profile(args.platform)
appbundle_cmd = ["flutter", "build", "appbundle"]
append_common_flutter_args(appbundle_cmd, args)
appbundle_cmd.append(f"--split-debug-info={android_symbols_dir.relative_to(ROOT)}")
run_command(appbundle_cmd)
if profile in {"full", "google-play"}:
appbundle_cmd = ["flutter", "build", "appbundle"]
append_common_flutter_args(appbundle_cmd, args)
appbundle_cmd.append(f"--split-debug-info={android_symbols_dir.relative_to(ROOT)}")
run_command(appbundle_cmd)
apk_cmd = [
"flutter",
"build",
"apk",
"--split-per-abi",
"--target-platform",
"android-arm,android-arm64,android-x64",
]
append_common_flutter_args(apk_cmd, args)
apk_cmd.append(f"--split-debug-info={android_symbols_dir.relative_to(ROOT)}")
run_command(apk_cmd)
if profile == "full":
apk_cmd = [
"flutter",
"build",
"apk",
"--split-per-abi",
"--target-platform",
"android-arm,android-arm64,android-x64",
]
append_common_flutter_args(apk_cmd, args)
apk_cmd.append(f"--split-debug-info={android_symbols_dir.relative_to(ROOT)}")
run_command(apk_cmd)
elif profile == "local-arm64":
apk_cmd = [
"flutter",
"build",
"apk",
"--split-per-abi",
"--target-platform",
"android-arm64",
]
append_common_flutter_args(apk_cmd, args)
apk_cmd.append(f"--split-debug-info={android_symbols_dir.relative_to(ROOT)}")
run_command(apk_cmd)
google_play_dir = android_output_dir / "google-play"
local_dir = android_output_dir / "local"
@ -112,23 +143,28 @@ def build_android(args: argparse.Namespace, manifest: dict[str, object]) -> None
artifact_prefix = f"{args.package_name}-v{args.build_name}-b{args.build_number}"
aab_src = ROOT / "build" / "app" / "outputs" / "bundle" / "release" / "app-release.aab"
arm64_src = ROOT / "build" / "app" / "outputs" / "flutter-apk" / "app-arm64-v8a-release.apk"
armv7_src = ROOT / "build" / "app" / "outputs" / "flutter-apk" / "app-armeabi-v7a-release.apk"
x64_src = ROOT / "build" / "app" / "outputs" / "flutter-apk" / "app-x86_64-release.apk"
artifacts: dict[str, object] = {}
artifacts = {
"googlePlayAab": copy_file(aab_src, google_play_dir / f"{artifact_prefix}-google-play.aab"),
"localArm64Apk": copy_file(arm64_src, local_dir / f"{artifact_prefix}-arm64-v8a.apk"),
"localArmeabiV7aApk": copy_file(armv7_src, local_dir / f"{artifact_prefix}-armeabi-v7a.apk"),
"testingX64Apk": copy_file(x64_src, testing_dir / f"{artifact_prefix}-x86_64-test.apk"),
}
if profile in {"full", "google-play"}:
aab_src = ROOT / "build" / "app" / "outputs" / "bundle" / "release" / "app-release.aab"
artifacts["googlePlayAab"] = copy_file(aab_src, google_play_dir / f"{artifact_prefix}-google-play.aab")
if profile in {"full", "local-arm64"}:
arm64_src = ROOT / "build" / "app" / "outputs" / "flutter-apk" / "app-arm64-v8a-release.apk"
artifacts["localArm64Apk"] = copy_file(arm64_src, local_dir / f"{artifact_prefix}-arm64-v8a.apk")
if profile == "full":
armv7_src = ROOT / "build" / "app" / "outputs" / "flutter-apk" / "app-armeabi-v7a-release.apk"
x64_src = ROOT / "build" / "app" / "outputs" / "flutter-apk" / "app-x86_64-release.apk"
artifacts["localArmeabiV7aApk"] = copy_file(armv7_src, local_dir / f"{artifact_prefix}-armeabi-v7a.apk")
artifacts["testingX64Apk"] = copy_file(x64_src, testing_dir / f"{artifact_prefix}-x86_64-test.apk")
if android_symbols_dir.exists():
copy_tree(android_symbols_dir, google_play_dir / "symbols")
symbols_parent_dir = google_play_dir if profile in {"full", "google-play"} else local_dir
copy_tree(android_symbols_dir, symbols_parent_dir / "symbols")
artifacts["dartSymbolsDir"] = {
"path": str((google_play_dir / "symbols").relative_to(ROOT)),
"sizeBytes": sum(path.stat().st_size for path in (google_play_dir / "symbols").rglob("*") if path.is_file()),
"path": str((symbols_parent_dir / "symbols").relative_to(ROOT)),
"sizeBytes": sum(path.stat().st_size for path in (symbols_parent_dir / "symbols").rglob("*") if path.is_file()),
}
manifest["android"] = artifacts
@ -191,7 +227,7 @@ def create_argument_parser() -> argparse.ArgumentParser:
)
parser.add_argument(
"--platform",
choices=["all", "android", "ios"],
choices=["all", "ios", "android", "android-google-play", "android-local-arm64"],
default="all",
help="Select which platform artifacts to build.",
)
@ -253,9 +289,9 @@ def main() -> int:
"outputDir": str(args.output_dir.relative_to(ROOT)),
}
if args.platform in {"all", "android"}:
if should_build_android(args.platform):
build_android(args, manifest)
if args.platform in {"all", "ios"}:
if should_build_ios(args.platform):
build_ios(args, manifest)
manifest_path = args.output_dir / "build_manifest.json"

View File

@ -19,10 +19,11 @@
- 已修复语言房顶部左侧房间榜单入口出现 `right overflowed` 的问题:当前贡献值区域已改为约束宽度 + 自适应缩放文本,不再因为积分位数变长把右箭头顶出容器。
- 已根据最新确认回退首页 Party 区的 `recommend_rank_top` 三个素材替换:财富榜、房间榜、魅力榜前三头像现已恢复为原先版本,不再在该位置叠加 `recommend_rank_top` 动态头像框;相关素材仍保留在工程内并已按规范重命名,后续待确认真正使用位置。
- 已新增通用本地 SVGA 资源组件 `lib/ui_kit/widgets/svga/sc_svga_asset_widget.dart`,用于统一处理本地 assets 解码、内存缓存、单次播放/循环播放和失败兜底;本轮底部 tab、语言房 game 按钮和麦位声波均复用这套实现,避免后续重复写播放器逻辑。
- 已按最新语言房底部栏视觉需求完成 UI 重构:底部 5 个入口现改为同一水平基线布局,移除礼物按钮原先的中间悬浮 `Stack` 结构,并最终按“1、2、5、3、4”顺序排列也就是“输入入口、礼物、消息、麦克风、菜单”麦克风隐藏时继续保留占位保证整体间距平均、视觉对齐稳定
- 已按最新语言房底部栏视觉需求完成 UI 重构:底部 5 个入口现改为同一水平基线布局,移除礼物按钮原先的中间悬浮 `Stack` 结构,并按最新顺序调整为“输入入口、礼物、麦克风、菜单、消息”;其中消息入口已移动到最右侧,且在麦克风隐藏时,礼物、菜单、消息 3 个入口会自动向右收拢成一组,避免中间留空
- 已将语言房左侧输入入口改为独立组件:当前已撤回自绘聊天气泡图标方案,直接恢复使用项目原有的 `sc_images/room/icon_room_input_t.png` 资源作为左侧输入 icon并继续放入约三分之一屏宽的圆角矩形中右侧保留多语言问候文案 `roomBottomGreeting`,避免继续在自绘气泡尾巴形态上反复调整。
- 已将语言房礼物入口拆成独立组件:礼物图标缩小后当前尺寸只略大于右侧 3 个圆按钮,并保留常驻轻量摇晃动画;根据最新反馈,当前已移除礼物按钮的粒子特效,并进一步去掉礼物入口外层 `container`,直接使用与原先按钮壳同尺寸的礼物图片本体作为可点击 UI避免额外底壳干扰观感。
- 已统一语言房其余四个入口的按钮壳样式:输入入口、消息、菜单、麦克风现统一使用淡色半透明背景 container消息红点计数、菜单弹窗、消息跳转和麦克风开关等原有功能逻辑保持不变本轮只调整 UI 展示与布局层。
- 已继续强化语言房右侧消息与麦克风入口的识别度:消息图标已替换为项目内更明显的信封样式资源 `sc_icon_send_user_message.png`,麦克风图标则在不改变按钮壳尺寸的前提下放大到更接近其它入口的可视大小,减少“图标偏小、不够醒目”的问题。
- 已按最新交互要求调整麦克风隐藏态布局:当第 4 个图标(麦克风)可见时,底部栏保持当前 5 个槽位的位置不变;当麦克风隐藏时,不再为其保留空槽位,礼物、消息、菜单 3 个入口会自动向右收拢成一组,避免右侧中间出现空位。
- 已排查语言房“双设备、不同账号进入同一房间,上麦后无声”的直接原因:当前 RTC 进房时统一以 Agora `Audience` 身份加入频道,见 `lib/services/audio/rtc_manager.dart``joinChannel()``clientRoleType: clientRoleAudience`;而本地麦克风开关 `isMic` 默认值为 `true`(当前语义实际是“闭麦”),上麦 `shangMai()` 后只有在 `!isMic` 时才会切到 `Broadcaster` 并取消本地静音,所以“只上麦、不点底部麦克风按钮”时不会发声。
- 已补充语言房当前真实交互结论:现有实现里“上麦”和“开麦”是两个动作。用户点空麦位后只是占麦;还需要再点击底部麦克风按钮,触发 `lib/ui_kit/widgets/room/room_bottom_widget.dart` 中的角色切换,才能真正开始向房间发送音频。
@ -42,6 +43,8 @@
- 已根据最新真机日志继续收敛 BAISHUN 问题边界:当前调试面板已确认 `ConfigSend=1``language=2(raw=en)``getConfig_1_complete``gameLoaded_1_complete` 都已正常走通,说明 Flutter 侧桥接、语言字段和“一次性 code 重复下发”目前都不是主要矛盾;同时 `Recent Logs` 里未出现浏览器侧 `fetch/XHR` 失败,但仍出现 `get user info failed` 弹窗与 H5 内部 `InvalidStateError`,因此当前更倾向于 BAISHUN 服务端继续调用商户侧 `/v1/api/get_sstoken` / `/v1/api/get_user_info` 的链路失败或其返回内容不符合文档要求H5 页面里的报错更像后续症状而非首因。
- 已继续增强 BAISHUN 临时调试能力用于下一轮真机排障:当前除了原有 `fetch/XHR/window.onerror/unhandledrejection` 外,还会额外回传 H5 的 `console.log/info/warn/error` 与资源加载失败信息,并把本地日志保留条数放宽到 `80` 条、面板日志区适当加高;这些都只服务当前联调,不属于正式功能,问题定位完成后需要一并删除。
- 已通过 2026-04-16 真机最新控制台日志拿到更直接的证据H5 侧明确打印了 `{"msgId":"Connect","errCode":1015,"errMsg":"1015-SSToken接口错误","data":null}`,并伴随 `WebSocket Error / WebSocket Close / Reconnect` 日志,说明当前问题已可进一步收敛为百顺游戏在建立长连接前获取 `SSToken` 失败;这表明主要矛盾已经不在 Flutter 容器桥接,而在百顺服务端访问商户侧 `/v1/api/get_sstoken` 的链路、该接口的业务结果、或其返回格式与文档约定不一致。
- 已继续定位“游戏画面可见但触摸无响应”的 Flutter 侧原因:当前实现此前一直把 BAISHUN H5 页面放在 `showGeneralDialog` 底部弹层里承载,同时 App 启动时又全局锁定了 `portraitUp`;对于 `FishingStar` 这类横版游戏,这会叠加出“平台视图在弹层容器中的命中不稳定 + iPhone 端实际不支持横屏”的问题。现已改为用独立路由承载 BAISHUN 游戏页,并按启动数据里的 `orientation` 动态切换横竖屏;同时 iOS `Info.plist` 已补充手机横屏支持,便于继续验证横版游戏触摸是否恢复。上述调整仍属于本轮 BAISHUN 联调修正,不是新的正式产品交互。
- 已按最新联调反馈回撤 BAISHUN 页面的横屏适配:当前后续不会接横屏游戏,因此已去掉游戏页按 `orientation` 切换系统方向的逻辑,并撤回 iPhone 端新增的横屏声明,只保留独立路由承载 BAISHUN 页面这一项 Flutter 容器修正。同时已继续增强临时调试,在 H5 侧补充 `pointerdown / touchstart / mousedown / click` 事件回传,用于下一轮真机直接确认“点击是否真的进到了 H5 DOM”这些输入事件日志依旧只用于本轮排障后续需要删除。
- 创建并持续维护进度跟踪文件。
- 已继续排查语言房 gift 动画链路:确认送礼后会同时走本地房间消息、滚屏礼物条和大额礼物全局飘屏三条路径,并修复动画管理器在控制器尚未绑定完成时提前消费队列导致后续动画不再播放的问题。
- 已定位并修复语言房礼物飘屏资源引用错误:代码里误写 `sc_icon_gift_flosc_bg` / `sc_icon_luck_gift_flosc_*`,实际资源文件名为 `float`,导致点击送礼后飘屏背景图加载失败,相关动画无法正常显示。