Firebase Remote Config 在 Unity 中的使用
缓存机制三层架构理解Default → Cached → Remote的优先级Fetch ≠ Activate,必须激活后才生效国内网络适配设置5-10秒超时实现降级方案默认值兜底常见陷阱忘记激活配置缓存时间不当频繁Fetch触发限流初始化时机不对。
快速查找表
| 使用场景 | 推荐方案 | 关键API | 核心注意事项 |
|---|---|---|---|
| 首次集成初始化 | 设置默认值+异步拉取 | SetDefaultsAsync() + FetchAsync() |
国内建议设置5-10秒超时 |
| 获取配置值 | 先检查激活状态 | GetValue(key).StringValue |
使用前确保已Activate |
| 开发环境调试 | 禁用缓存 | FetchAsync(TimeSpan.Zero) |
生产环境必须设置合理缓存时间 |
| 生产环境更新 | 12小时缓存 | FetchAsync(TimeSpan.FromHours(12)) |
平衡实时性与网络消耗 |
| 国内网络适配 | 超时处理+降级方案 | 自定义超时逻辑 | 必须有默认值兜底 |
| 离线使用 | 依赖本地缓存 | 自动使用上次缓存 | 首次运行必须联网 |
| 数据类型获取 | 使用强类型方法 | .BooleanValue/.LongValue |
避免类型转换错误 |
一、核心概念速览
Firebase Remote Config是一个云端配置服务,允许你在不发布新版本的情况下动态修改应用的行为和外观。
1.1 为什么需要Remote Config?
传统方式:修改配置 → 打包 → 发布 → 用户更新 (周期长)
Remote Config:云端修改 → 应用Fetch → 即时生效 (分钟级)
典型应用场景:
- ✅ 游戏数值平衡(不停服调整)
- ✅ AB测试不同UI方案
- ✅ 功能开关(灰度发布)
- ✅ 活动配置(节日主题)
- ✅ 紧急停服维护公告
二、缓存机制完全解析(核心重点)
2.1 三层数据架构原理
Firebase Remote Config的数据获取遵循三层优先级模型:
数据层级说明:
// 第1层:默认值(代码中设置,兜底保障)
var defaults = new Dictionary<string, object>
{
{"game_difficulty", "normal"},
{"max_level", 50},
{"enable_new_feature", false}
};
await remoteConfig.SetDefaultsAsync(defaults);
// 第2层:缓存值(上次成功Fetch的数据,持久化存储)
// 存储位置:Application.persistentDataPath/com.google.Firebase.RemoteConfig
// 自动管理,无需手动操作
// 第3层:远程值(服务端最新配置,需要Fetch+Activate)
await remoteConfig.FetchAsync(); // 拉取到缓存
await remoteConfig.ActivateAsync(); // 激活缓存使其生效
2.2 Fetch流程深度解析
完整的Fetch生命周期:
1. 检查缓存时间 → 2. 决定是否网络请求 → 3. 下载到本地缓存 → 4. 等待Activate → 5. 生效
关键概念:Fetch ≠ 生效
// ❌ 常见错误:Fetch后立即获取,拿到的是旧数据
await remoteConfig.FetchAsync();
var value = remoteConfig.GetValue("key").StringValue; // 仍是旧值!
// ✅ 正确做法1:使用FetchAndActivate(推荐)
await remoteConfig.FetchAndActivateAsync(); // 拉取并激活
var value = remoteConfig.GetValue("key").StringValue; // 新值✓
// ✅ 正确做法2:分步操作(精细控制)
await remoteConfig.FetchAsync(); // 拉取到缓存
await remoteConfig.ActivateAsync(); // 激活缓存
var value = remoteConfig.GetValue("key").StringValue; // 新值✓
2.3 缓存过期策略详解
缓存时间参数的作用:
// TimeSpan参数含义:多久之后缓存过期,需要重新Fetch
await remoteConfig.FetchAsync(TimeSpan.FromHours(12));
执行逻辑:
IF (当前时间 - 上次成功Fetch时间) < 缓存时间
→ 直接使用缓存,不发起网络请求 ⚡
ELSE
→ 发起网络请求获取最新配置 🌐
不同场景的最佳实践:
// 开发调试:禁用缓存,每次都拉取最新
await remoteConfig.FetchAsync(TimeSpan.Zero);
// 生产环境:12小时缓存(推荐)
await remoteConfig.FetchAsync(TimeSpan.FromHours(12));
// 关键活动期:1小时缓存(高时效性需求)
await remoteConfig.FetchAsync(TimeSpan.FromHours(1));
// 静态配置:24小时缓存(降低服务器负载)
await remoteConfig.FetchAsync(TimeSpan.FromHours(24));
2.4 本地缓存存储机制
缓存文件位置:
// Android/iOS
Application.persistentDataPath + "/com.google.Firebase.RemoteConfig"
// 缓存内容包括:
// 1. 已激活的配置(当前生效)
// 2. 已拉取但未激活的配置(待激活)
// 3. 元数据(上次Fetch时间、过期时间等)
缓存持久化特性:
- ✅ 应用重启后缓存依然有效
- ✅ 离线状态下可使用缓存值
- ✅ 首次安装时无缓存,依赖默认值
- ❌ 卸载应用会清空缓存
2.5 激活机制与时机选择
Activate的本质:
将"已拉取但未激活"的配置 → 移动到"已激活"状态
激活时机的权衡:
// 方案1:应用启动时激活(推荐)
// 优点:配置即时生效
// 缺点:可能影响启动速度
async void Start()
{
await remoteConfig.FetchAndActivateAsync();
ApplyConfigs(); // 应用配置
}
// 方案2:延迟激活(适合大型应用)
// 优点:不阻塞启动流程
// 缺点:首次运行使用默认值
async void Start()
{
ShowMainMenu(); // 先显示界面
// 后台异步拉取
_ = FetchConfigInBackground();
}
private async Task FetchConfigInBackground()
{
await remoteConfig.FetchAsync();
// 下次应用重启时激活(非实时)
// 或者检测用户进入特定场景时激活
}
// 方案3:场景切换时激活(精细控制)
// 适合:某些配置只在特定场景使用
async void OnEnterGameScene()
{
await remoteConfig.ActivateAsync(); // 激活之前Fetch的配置
ApplyGameConfigs();
}
三、国内网络环境适配方案(关键重点)
3.1 国内网络问题分析
核心问题:
Firebase依赖Google Cloud服务,国内直连存在以下问题:
1. DNS解析慢/失败 → 连接超时
2. 网络间歇性中断 → Fetch失败
3. 首次初始化耗时长 → 启动卡顿
4. 部分地区完全不可用 → 功能不可用
实测数据对比:
海外环境:Fetch平均耗时 200-800ms ✅
国内直连:Fetch平均耗时 5-30秒 ⚠️
国内失败:完全超时 60秒 ❌
3.2 超时配置最佳实践
Unity Firebase SDK的超时机制:
⚠️ 重要:Unity Firebase SDK默认不支持自定义超时时间!
// ❌ SDK本身无超时参数
await remoteConfig.FetchAsync(); // 默认60秒超时,无法修改
// ✅ 自建超时控制
public async Task<bool> FetchWithTimeout(int timeoutSeconds = 10)
{
var fetchTask = remoteConfig.FetchAndActivateAsync();
var timeoutTask = Task.Delay(timeoutSeconds * 1000);
var completedTask = await Task.WhenAny(fetchTask, timeoutTask);
if (completedTask == timeoutTask)
{
Debug.LogWarning($"RemoteConfig Fetch超时({timeoutSeconds}秒),使用缓存/默认值");
return false; // 超时
}
return await fetchTask; // 成功
}
3.3 完整的容错方案
生产级实现代码:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Firebase.Extensions;
using Firebase.RemoteConfig;
using UnityEngine;
public class RemoteConfigManager : MonoBehaviour
{
private FirebaseRemoteConfig remoteConfig;
private bool isInitialized = false;
// 配置参数
[Header("网络配置")]
[SerializeField] private int fetchTimeoutSeconds = 10; // Fetch超时时间
[SerializeField] private int maxRetryCount = 2; // 最大重试次数
[SerializeField] private float retryDelaySeconds = 2f; // 重试间隔
[Header("缓存配置")]
[SerializeField] private int cacheExpirationHours = 12; // 缓存过期时间
private async void Start()
{
await InitializeRemoteConfig();
}
/// <summary>
/// 初始化Remote Config(带容错处理)
/// </summary>
public async Task<bool> InitializeRemoteConfig()
{
try
{
// 1. 等待Firebase初始化
var dependencyStatus = await Firebase.FirebaseApp.CheckAndFixDependenciesAsync();
if (dependencyStatus != Firebase.DependencyStatus.Available)
{
Debug.LogError($"Firebase初始化失败: {dependencyStatus}");
return false;
}
// 2. 获取RemoteConfig实例
remoteConfig = FirebaseRemoteConfig.DefaultInstance;
// 3. 设置默认值(兜底保障)
SetDefaultValues();
// 4. 配置Fetch设置
var configSettings = new ConfigSettings
{
MinimumFetchIntervalInMilliseconds = (ulong)(cacheExpirationHours * 3600 * 1000)
};
remoteConfig.SetConfigSettingsAsync(configSettings);
// 5. 尝试Fetch远程配置(带重试)
bool fetchSuccess = await FetchRemoteConfigWithRetry();
if (fetchSuccess)
{
Debug.Log("✅ RemoteConfig初始化成功");
}
else
{
Debug.LogWarning("⚠️ RemoteConfig Fetch失败,使用缓存/默认值");
}
isInitialized = true;
return true;
}
catch (Exception ex)
{
Debug.LogError($"RemoteConfig初始化异常: {ex.Message}");
return false;
}
}
/// <summary>
/// 设置默认值(必须执行,作为兜底保障)
/// </summary>
private void SetDefaultValues()
{
var defaults = new Dictionary<string, object>
{
// 游戏配置
{"game_difficulty", "normal"},
{"max_player_level", 100},
{"daily_reward_amount", 100},
// 功能开关
{"enable_new_ui", false},
{"enable_social_system", true},
{"maintenance_mode", false},
// 数值配置
{"coin_exchange_rate", 1.0},
{"exp_multiplier", 1.0},
// 文本配置
{"welcome_message", "欢迎来到游戏!"},
{"maintenance_notice", "服务器维护中,请稍后再试"}
};
remoteConfig.SetDefaultsAsync(defaults).ContinueWithOnMainThread(task =>
{
Debug.Log("✅ 默认值设置完成");
});
}
/// <summary>
/// 带重试机制的Fetch
/// </summary>
private async Task<bool> FetchRemoteConfigWithRetry()
{
for (int attempt = 0; attempt <= maxRetryCount; attempt++)
{
if (attempt > 0)
{
Debug.Log($"🔄 RemoteConfig重试 {attempt}/{maxRetryCount}...");
await Task.Delay((int)(retryDelaySeconds * 1000));
}
bool success = await FetchWithTimeout(fetchTimeoutSeconds);
if (success)
{
return true;
}
}
Debug.LogWarning($"❌ RemoteConfig在{maxRetryCount}次重试后仍然失败");
return false;
}
/// <summary>
/// 带超时控制的Fetch
/// </summary>
private async Task<bool> FetchWithTimeout(int timeoutSeconds)
{
try
{
// 创建Fetch任务
var fetchTask = remoteConfig.FetchAsync(TimeSpan.FromHours(cacheExpirationHours));
// 创建超时任务
var timeoutTask = Task.Delay(timeoutSeconds * 1000);
// 等待任何一个完成
var completedTask = await Task.WhenAny(fetchTask, timeoutTask);
if (completedTask == timeoutTask)
{
Debug.LogWarning($"⏱️ Fetch超时({timeoutSeconds}秒)");
return false;
}
// Fetch成功,激活配置
bool activated = await remoteConfig.ActivateAsync();
Debug.Log(activated
? "✅ 新配置已激活"
: "ℹ️ 激活失败或配置未变化,使用现有配置");
return true;
}
catch (Exception ex)
{
Debug.LogError($"Fetch异常: {ex.Message}");
return false;
}
}
/// <summary>
/// 获取字符串配置
/// </summary>
public string GetString(string key, string defaultValue = "")
{
if (!isInitialized)
{
Debug.LogWarning("RemoteConfig未初始化,返回默认值");
return defaultValue;
}
try
{
return remoteConfig.GetValue(key).StringValue;
}
catch (Exception ex)
{
Debug.LogError($"获取配置失败 [{key}]: {ex.Message}");
return defaultValue;
}
}
/// <summary>
/// 获取整数配置
/// </summary>
public long GetLong(string key, long defaultValue = 0)
{
if (!isInitialized) return defaultValue;
try
{
return remoteConfig.GetValue(key).LongValue;
}
catch
{
return defaultValue;
}
}
/// <summary>
/// 获取布尔配置
/// </summary>
public bool GetBool(string key, bool defaultValue = false)
{
if (!isInitialized) return defaultValue;
try
{
return remoteConfig.GetValue(key).BooleanValue;
}
catch
{
return defaultValue;
}
}
/// <summary>
/// 获取浮点配置
/// </summary>
public double GetDouble(string key, double defaultValue = 0.0)
{
if (!isInitialized) return defaultValue;
try
{
return remoteConfig.GetValue(key).DoubleValue;
}
catch
{
return defaultValue;
}
}
/// <summary>
/// 手动刷新配置(供开发者调用)
/// </summary>
public async Task<bool> RefreshConfig()
{
Debug.Log("🔄 手动刷新RemoteConfig...");
return await FetchRemoteConfigWithRetry();
}
}
3.4 网络状态检测优化
using UnityEngine;
public static class NetworkHelper
{
/// <summary>
/// 检查网络连接状态
/// </summary>
public static bool IsNetworkAvailable()
{
return Application.internetReachability != NetworkReachability.NotReachable;
}
/// <summary>
/// 获取网络类型
/// </summary>
public static string GetNetworkType()
{
switch (Application.internetReachability)
{
case NetworkReachability.ReachableViaCarrierDataNetwork:
return "移动网络";
case NetworkReachability.ReachableViaLocalAreaNetwork:
return "WiFi";
default:
return "无网络";
}
}
/// <summary>
/// 是否应该进行网络请求(避免移动网络大流量)
/// </summary>
public static bool ShouldFetchRemoteConfig()
{
// 无网络时不请求
if (!IsNetworkAvailable())
{
Debug.Log("无网络连接,跳过RemoteConfig Fetch");
return false;
}
// WiFi环境下总是请求
if (Application.internetReachability == NetworkReachability.ReachableViaLocalAreaNetwork)
{
return true;
}
// 移动网络下,可以根据用户偏好设置决定
// 这里简化处理,移动网络也允许
return true;
}
}
// 使用示例
if (NetworkHelper.ShouldFetchRemoteConfig())
{
await remoteConfigManager.InitializeRemoteConfig();
}
else
{
Debug.Log("当前网络环境不适合Fetch,使用缓存配置");
}
四、常见陷阱与解决方案
4.1 陷阱1:忘记激活配置
问题表现:
// ❌ 错误代码
await remoteConfig.FetchAsync();
var value = remoteConfig.GetValue("new_feature").BooleanValue;
// 即使Fetch成功,拿到的仍是旧值!
原因分析:
Fetch只是将配置下载到缓存
必须Activate后才会替换当前生效的配置
解决方案:
// ✅ 方案1:使用FetchAndActivate(推荐)
await remoteConfig.FetchAndActivateAsync();
// ✅ 方案2:分步操作
await remoteConfig.FetchAsync();
await remoteConfig.ActivateAsync();
// ✅ 方案3:检查激活结果
var info = remoteConfig.Info;
if (info.LastFetchStatus == LastFetchStatus.Success)
{
bool activated = await remoteConfig.ActivateAsync();
Debug.Log(activated ? "新配置已激活" : "配置未变化");
}
4.2 陷阱2:缓存时间设置不当
问题表现:
// ❌ 开发环境忘记禁用缓存
await remoteConfig.FetchAsync(TimeSpan.FromHours(12));
// 修改后台配置后,应用内不生效(缓存未过期)
// ❌ 生产环境设置为0
await remoteConfig.FetchAsync(TimeSpan.Zero);
// 每次启动都请求,服务器压力大,启动慢
最佳实践:
public class RemoteConfigSettings
{
public static TimeSpan GetCacheExpiration()
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
// 开发环境:禁用缓存
return TimeSpan.Zero;
#else
// 生产环境:12小时缓存
return TimeSpan.FromHours(12);
#endif
}
}
// 使用
await remoteConfig.FetchAsync(RemoteConfigSettings.GetCacheExpiration());
4.3 陷阱3:数据类型转换错误
问题表现:
// 后台配置:app_version = "1.2.3" (字符串)
// ❌ 错误获取方式
int version = (int)remoteConfig.GetValue("app_version").LongValue;
// 结果:0(类型不匹配,返回默认值)
// ❌ 字符串当数字用
string maxLevel = "100"; // 后台设置
int level = remoteConfig.GetValue("max_level").LongValue;
// 如果后台设置为字符串"100",这里会返回0
解决方案:
// ✅ 使用正确的类型方法
string version = remoteConfig.GetValue("app_version").StringValue;
// ✅ 后台设置数字时不加引号
// 正确:max_level = 100
// 错误:max_level = "100"
int maxLevel = (int)remoteConfig.GetValue("max_level").LongValue;
// ✅ 需要转换时手动处理
string levelStr = remoteConfig.GetValue("max_level").StringValue;
if (int.TryParse(levelStr, out int level))
{
// 使用level
}
// ✅ 封装类型安全的获取方法
public static class RemoteConfigExtensions
{
public static int GetInt(this FirebaseRemoteConfig config, string key, int defaultValue = 0)
{
try
{
var value = config.GetValue(key);
// 尝试直接获取Long
if (value.Source != ValueSource.StaticValue)
{
return (int)value.LongValue;
}
// 尝试字符串转换
if (int.TryParse(value.StringValue, out int result))
{
return result;
}
return defaultValue;
}
catch
{
return defaultValue;
}
}
}
4.4 陷阱4:初始化时机不当
问题表现:
// ❌ 在Awake中同步初始化
void Awake()
{
// 阻塞主线程,导致启动卡顿
InitializeFirebase().Wait();
}
// ❌ 在场景加载时才初始化
void OnEnterGameScene()
{
// 每次进入场景都初始化,浪费资源
InitializeRemoteConfig();
}
推荐方案:
// ✅ 方案1:单例模式,应用启动时初始化一次
public class RemoteConfigManager : MonoBehaviour
{
private static RemoteConfigManager instance;
public static RemoteConfigManager Instance
{
get
{
if (instance == null)
{
var go = new GameObject("RemoteConfigManager");
instance = go.AddComponent<RemoteConfigManager>();
DontDestroyOnLoad(go);
}
return instance;
}
}
private void Awake()
{
if (instance != null && instance != this)
{
Destroy(gameObject);
return;
}
instance = this;
DontDestroyOnLoad(gameObject);
// 异步初始化,不阻塞启动
_ = InitializeAsync();
}
private async Task InitializeAsync()
{
await InitializeRemoteConfig();
}
}
// ✅ 方案2:启动场景中初始化
// 创建一个AppStartup场景,在其中完成所有初始化
public class AppStartup : MonoBehaviour
{
async void Start()
{
// 显示加载界面
ShowLoadingScreen();
// 等待RemoteConfig初始化
await RemoteConfigManager.Instance.InitializeRemoteConfig();
// 隐藏加载界面,进入主界面
LoadMainScene();
}
}
4.5 陷阱5:频繁Fetch导致限流
问题表现:
// ❌ 每次需要配置时都Fetch
void OnButtonClick()
{
await remoteConfig.FetchAndActivateAsync(); // 错误!
var value = remoteConfig.GetValue("key").StringValue;
}
// 结果:触发Firebase限流(每小时最多5次)
// 错误信息:FetchFailedException: Fetch throttled
Firebase限流规则:
默认限流:每小时最多5次Fetch
开发模式:可以减少到0(无限制)
解决方案:
// ✅ 应用启动时Fetch一次
async void Start()
{
await remoteConfig.FetchAndActivateAsync();
}
// ✅ 之后直接使用GetValue
void OnButtonClick()
{
// 不需要再Fetch
var value = remoteConfig.GetValue("key").StringValue;
}
// ✅ 定时刷新(可选)
private async void StartPeriodicRefresh()
{
while (true)
{
await Task.Delay(3600 * 1000); // 每小时
await remoteConfig.FetchAndActivateAsync();
}
}
// ✅ 开发模式禁用限流
var configSettings = new ConfigSettings
{
MinimumFetchInternalInMilliseconds = 0 // 开发时设置为0
};
remoteConfig.SetConfigSettingsAsync(configSettings);
4.6 陷阱6:国内网络未做超时处理
问题表现:
// ❌ 使用默认超时(60秒)
async void Start()
{
// 国内网络差时,用户等待60秒,体验极差
await remoteConfig.FetchAsync();
LoadMainScene();
}
真实场景影响:
理想情况:Fetch成功 → 800ms → 进入游戏 ✅
国内4G网络:Fetch超时 → 60秒 → 用户强退应用 ❌
国内WiFi较好:Fetch成功 → 5秒 → 体验下降 ⚠️
推荐方案(已在3.2章节详述):
- 设置5-10秒超时
- 超时后使用缓存/默认值
- 后台继续尝试Fetch
五、生产环境最佳实践
5.1 完整的初始化检查清单
/// <summary>
/// 生产级RemoteConfig初始化检查清单
/// </summary>
public class RemoteConfigCheckList
{
public static async Task<bool> ValidateSetup(FirebaseRemoteConfig config)
{
var passed = true;
// ✅ 检查1:Firebase初始化
var app = Firebase.FirebaseApp.DefaultInstance;
if (app == null)
{
Debug.LogError("❌ Firebase未初始化");
passed = false;
}
else
{
Debug.Log("✅ Firebase已初始化");
}
// ✅ 检查2:默认值已设置
var testValue = config.GetValue("test_key");
if (testValue.Source == ValueSource.StaticValue)
{
Debug.LogWarning("⚠️ 建议设置默认值作为兜底");
}
else
{
Debug.Log("✅ 默认值已配置");
}
// ✅ 检查3:网络连接
if (Application.internetReachability == NetworkReachability.NotReachable)
{
Debug.LogWarning("⚠️ 无网络连接,将使用缓存配置");
}
else
{
Debug.Log($"✅ 网络已连接 ({NetworkHelper.GetNetworkType()})");
}
// ✅ 检查4:缓存配置合理性
var settings = config.Info;
Debug.Log($"ℹ️ 缓存过期时间: {settings.Settings.MinimumFetchInternalInMilliseconds / 1000 / 60}分钟");
// ✅ 检查5:上次Fetch状态
switch (settings.LastFetchStatus)
{
case LastFetchStatus.Success:
Debug.Log("✅ 上次Fetch成功");
break;
case LastFetchStatus.Failure:
Debug.LogWarning("⚠️ 上次Fetch失败");
break;
case LastFetchStatus.Pending:
Debug.Log("ℹ️ Fetch进行中");
break;
case LastFetchStatus.NoFetchYet:
Debug.Log("ℹ️ 尚未Fetch");
break;
}
return passed;
}
}
5.2 监控指标建议
/// <summary>
/// RemoteConfig性能监控
/// </summary>
public class RemoteConfigMetrics
{
private static DateTime fetchStartTime;
public static void RecordFetchStart()
{
fetchStartTime = DateTime.Now;
Debug.Log($"[Metrics] Fetch开始: {fetchStartTime:HH:mm:ss.fff}");
}
public static void RecordFetchEnd(bool success)
{
var duration = (DateTime.Now - fetchStartTime).TotalMilliseconds;
Debug.Log($"[Metrics] Fetch{(success ? "成功" : "失败")}, 耗时: {duration}ms");
// 发送到分析平台
// Analytics.LogEvent("remote_config_fetch", new Dictionary<string, object>
// {
// {"success", success},
// {"duration_ms", duration},
// {"network_type", NetworkHelper.GetNetworkType()}
// });
}
}
// 使用
RemoteConfigMetrics.RecordFetchStart();
bool success = await FetchWithTimeout(10);
RemoteConfigMetrics.RecordFetchEnd(success);
5.3 配置项命名规范
/// <summary>
/// 配置键命名规范(建议遵循)
/// </summary>
public static class RemoteConfigKeys
{
// 功能开关:enable_xxx
public const string ENABLE_NEW_UI = "enable_new_ui";
public const string ENABLE_SOCIAL = "enable_social_system";
// 数值配置:xxx_value / xxx_amount
public const string MAX_LEVEL = "max_player_level";
public const string DAILY_REWARD = "daily_reward_amount";
// 倍率配置:xxx_multiplier / xxx_rate
public const string EXP_MULTIPLIER = "exp_multiplier";
public const string COIN_RATE = "coin_exchange_rate";
// 文本配置:xxx_text / xxx_message
public const string WELCOME_TEXT = "welcome_message";
public const string MAINTENANCE_MSG = "maintenance_notice";
// 状态标识:is_xxx / has_xxx
public const string IS_MAINTENANCE = "is_maintenance_mode";
public const string HAS_EVENT = "has_special_event";
}
// 使用
bool newUI = remoteConfig.GetBool(RemoteConfigKeys.ENABLE_NEW_UI);
5.4 完整使用示例
using UnityEngine;
using Firebase.RemoteConfig;
using System.Threading.Tasks;
public class GameManager : MonoBehaviour
{
private RemoteConfigManager rcManager;
async void Start()
{
// 1. 初始化RemoteConfig
rcManager = RemoteConfigManager.Instance;
bool initSuccess = await rcManager.InitializeRemoteConfig();
if (!initSuccess)
{
Debug.LogWarning("RemoteConfig初始化失败,使用默认配置启动游戏");
}
// 2. 应用配置到游戏
ApplyGameConfigs();
// 3. 加载主场景
LoadMainScene();
}
void ApplyGameConfigs()
{
// 获取各类配置
int maxLevel = (int)rcManager.GetLong(RemoteConfigKeys.MAX_LEVEL, 100);
bool enableNewUI = rcManager.GetBool(RemoteConfigKeys.ENABLE_NEW_UI, false);
double expMultiplier = rcManager.GetDouble(RemoteConfigKeys.EXP_MULTIPLIER, 1.0);
string welcomeMsg = rcManager.GetString(RemoteConfigKeys.WELCOME_TEXT, "欢迎!");
// 应用到游戏系统
GameConfig.MaxLevel = maxLevel;
GameConfig.ExpMultiplier = expMultiplier;
UIManager.Instance.SetUITheme(enableNewUI ? UITheme.New : UITheme.Classic);
Debug.Log($"游戏配置应用完成:最高等级={maxLevel}, 经验倍率={expMultiplier}");
}
// 可选:提供手动刷新功能(给测试或GM使用)
public async void OnRefreshConfigButton()
{
Debug.Log("手动刷新配置...");
bool success = await rcManager.RefreshConfig();
if (success)
{
ApplyGameConfigs();
Debug.Log("配置刷新成功,已重新应用");
}
else
{
Debug.Log("配置刷新失败");
}
}
}
六、总结与建议
6.1 核心要点回顾
-
缓存机制三层架构
- 理解Default → Cached → Remote的优先级
- Fetch ≠ Activate,必须激活后才生效
-
国内网络适配
- 设置5-10秒超时
- 实现降级方案
- 默认值兜底
-
常见陷阱
- 忘记激活配置
- 缓存时间不当
- 频繁Fetch触发限流
- 初始化时机不对
6.2 开发流程建议
1. 设计配置项 → 2. 设置默认值 → 3. 后台配置 → 4. 客户端集成 → 5. 测试验证
6.3 测试验证清单
- 首次安装(无缓存)是否使用默认值
- 无网络环境下是否使用缓存
- Fetch超时是否正常降级
- 配置更新后是否正确激活
- 不同数据类型是否正确解析
- 国内网络环境下启动速度是否可接受
6.4 推荐配置模板
// 开发环境
FetchTimeout: 5秒
CacheExpiration: 0秒(禁用)
RetryCount: 1
DefaultValues: 完整配置
// 生产环境
FetchTimeout: 10秒
CacheExpiration: 12小时
RetryCount: 2
DefaultValues: 完整配置(兜底)
七、参考资源
最后提醒:
⚠️ 国内环境下,Firebase Remote Config的稳定性受网络影响较大,建议:
- 必须设置合理的超时时间
- 必须提供完整的默认值
- 关键配置不要完全依赖Remote Config
- 考虑混合方案(Remote Config + 自建服务)
💡 最佳实践:将Remote Config用于非关键配置(如UI主题、活动文案等),核心游戏逻辑保留本地配置作为主要方案。
这里是一个专注于游戏开发的社区,我们致力于为广大游戏爱好者提供一个良好的学习和交流平台。我们的专区包含了各大流行引擎的技术博文,涵盖了从入门到进阶的各个阶段,无论你是初学者还是资深开发者,都能在这里找到适合自己的内容。除此之外,我们还会不定期举办游戏开发相关的活动,让大家更好地交流互动。加入我们,一起探索游戏开发的奥秘吧!
更多推荐



所有评论(0)