ball 项目提交
This commit is contained in:
@@ -0,0 +1,281 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using DataEyeAnalytics;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BallKingdomCrush
|
||||
{
|
||||
public class BIManager : MonoBehaviour
|
||||
{
|
||||
private static BIManager _instance;
|
||||
private static readonly object _initLock = new object();
|
||||
|
||||
// 状态标志
|
||||
private bool _initialized = false; // 仅在初始化成功后为 true
|
||||
private bool _initializing = false; // 防止并发多次尝试初始化
|
||||
|
||||
|
||||
private string ntpServer = "time.windows.com";
|
||||
private string defaultChannel = "gp";
|
||||
private int maxInitRetries = 6;
|
||||
private float retryIntervalSeconds = 0.5f;
|
||||
|
||||
public static BIManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
var go = new GameObject("BIManager");
|
||||
DontDestroyOnLoad(go);
|
||||
_instance = go.AddComponent<BIManager>();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// 单例保护:如果场景里已有实例,销毁重复的
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
else if (_instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// Start 调用 InitBI 是安全的:InitBI 本身是幂等且线程安全的
|
||||
InitBI();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 对外统一初始化入口(幂等、线程安全)
|
||||
/// </summary>
|
||||
public void InitBI()
|
||||
{
|
||||
// 快速检查
|
||||
if (_initialized) return;
|
||||
|
||||
lock (_initLock)
|
||||
{
|
||||
if (_initialized || _initializing)
|
||||
{
|
||||
// 已经初始化或正在初始化,直接返回
|
||||
return;
|
||||
}
|
||||
|
||||
_initializing = true;
|
||||
}
|
||||
|
||||
// 使用协程执行初始化(必要时可以做重试)
|
||||
StartCoroutine(DoInitCoroutine());
|
||||
}
|
||||
|
||||
private IEnumerator DoInitCoroutine()
|
||||
{
|
||||
try
|
||||
{
|
||||
int attempt = 0;
|
||||
Exception lastException = null;
|
||||
|
||||
while (attempt < maxInitRetries)
|
||||
{
|
||||
attempt++;
|
||||
try
|
||||
{
|
||||
// 这里不主动调用 DataEyeAnalyticsAPI.Init(...) 假设你使用的是场景里的 DataEyeAnalytics 预制体,
|
||||
// SDK 会在预制体 Awake/Start 里初始化。如果你的流程是手动 Init,请在这里调用 Init(appId)。
|
||||
|
||||
// 1) 校准时间(默认实例)
|
||||
DataEyeAnalyticsAPI.CalibrateTimeWithNtp(ntpServer);
|
||||
|
||||
// 2) 设置超参数
|
||||
var superProperties = new Dictionary<string, object>
|
||||
{
|
||||
{ "channel", defaultChannel }
|
||||
};
|
||||
DataEyeAnalyticsAPI.SetSuperProperties(superProperties);
|
||||
|
||||
// 3) 开启自动采集
|
||||
DataEyeAnalyticsAPI.EnableAutoTrack(AUTO_TRACK_EVENTS.ALL);
|
||||
|
||||
// 如果上面没有抛异常,认为初始化成功
|
||||
_initialized = true;
|
||||
Debug.Log($"[BIManager] InitBI succeeded on attempt {attempt}");
|
||||
yield break;
|
||||
}
|
||||
catch (ArgumentNullException ane)
|
||||
{
|
||||
// 典型:DataEye 内部 instance 还没创建(appId 为 null) -> retry
|
||||
lastException = ane;
|
||||
Debug.LogWarning(
|
||||
$"[BIManager] DataEye not ready (attempt {attempt}), will retry. msg={ane.Message}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 其他异常,记录并决定是否重试
|
||||
lastException = ex;
|
||||
Debug.LogWarning($"[BIManager] InitBI attempt {attempt} threw: {ex.Message}");
|
||||
}
|
||||
|
||||
// 等待后重试
|
||||
yield return new WaitForSeconds(retryIntervalSeconds);
|
||||
}
|
||||
|
||||
// 重试用尽仍失败 —— 打日志(不再抛异常以免影响游戏流程)
|
||||
Debug.LogError("[BIManager] InitBI failed after retries. Last exception: " +
|
||||
(lastException?.ToString() ?? "null"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock (_initLock)
|
||||
{
|
||||
_initializing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ================= 通用埋点 =================
|
||||
public void TrackEvent(string eventName, Dictionary<string, object> properties = null)
|
||||
{
|
||||
if (properties == null)
|
||||
properties = new Dictionary<string, object>();
|
||||
|
||||
DataEyeAnalyticsAPI.Track(eventName, properties);
|
||||
DataEyeAnalyticsAPI.Flush();
|
||||
|
||||
Debug.Log($"[BI] 事件上报: {eventName}, 属性={JsonConvert.SerializeObject(properties)}");
|
||||
}
|
||||
|
||||
// ================= 预制事件封装 =================
|
||||
|
||||
/// <summary>
|
||||
/// 上报广告事件
|
||||
/// </summary>
|
||||
/// <param name="eventName"></param>
|
||||
/// <param name="adType">Native,RewardedVideo,Banner,Interstitial,Splash</param>
|
||||
/// <param name="placementId">聚合广告位id</param>
|
||||
/// <param name="networkFirmId">广告网络ID</param>
|
||||
public void TrackAdEvent(string eventName, string adType, string placementId = null,
|
||||
string networkFirmId = null, double revenue = 0, string scene = null, string currency = "USD")
|
||||
{
|
||||
var props = new Dictionary<string, object>
|
||||
{
|
||||
{ "ad_type", adType }
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(placementId))
|
||||
props["PlacementId"] = placementId;
|
||||
|
||||
if (!string.IsNullOrEmpty(networkFirmId))
|
||||
props["NetworkFirmId"] = networkFirmId;
|
||||
|
||||
if (revenue > 0)
|
||||
{
|
||||
props["Revenue"] = revenue;
|
||||
props["Ecpm"] = revenue * 1000;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(scene))
|
||||
props["scene"] = scene;
|
||||
|
||||
if (!string.IsNullOrEmpty(currency))
|
||||
props["Currency"] = currency;
|
||||
|
||||
|
||||
TrackEvent(eventName, props);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上报内购事件
|
||||
/// </summary>
|
||||
/// <param name="revenue">收入</param>
|
||||
/// <param name="currency">币种</param>
|
||||
/// <param name="payType">支付方式 0=商店支付,1=三方支付</param>
|
||||
/// <param name="itemId">sku</param>
|
||||
/// <param name="status">order:下单 paid:付费成功 paid_err:扣款失败</param>
|
||||
/// <param name="itemName">商品名称</param>
|
||||
/// <param name="msg">如果遇到异常或相关情况,将err_msg进行上报</param>
|
||||
public void TrackPurchase(double revenue, string currency, string payType, string itemId,
|
||||
string status, string itemName = null, string msg = null)
|
||||
{
|
||||
var props = new Dictionary<string, object>
|
||||
{
|
||||
{ "Revenue", revenue },
|
||||
{ "Currency", currency },
|
||||
{ "type", payType },
|
||||
{ "item_id", itemId },
|
||||
{ "purchase_status", status }
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(itemName))
|
||||
props["item_name"] = itemName;
|
||||
if (!string.IsNullOrEmpty(msg))
|
||||
props["msg"] = msg;
|
||||
|
||||
TrackEvent(BIEvent.PURCHASE, props);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上报页面浏览
|
||||
/// </summary>
|
||||
public void TrackPageView(string pageName)
|
||||
{
|
||||
var props = new Dictionary<string, object>
|
||||
{
|
||||
{ "page_name", pageName } // 例如 "loading"、"game"
|
||||
};
|
||||
|
||||
TrackEvent(BIEvent.PAGE_VIEW, props);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AB 分组埋点
|
||||
/// </summary>
|
||||
public void TrackABConfig(int responseTime)
|
||||
{
|
||||
var props = new Dictionary<string, object>
|
||||
{
|
||||
{ "response_time", responseTime }
|
||||
};
|
||||
|
||||
TrackEvent(BIEvent.AB_CONFIG, props);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class BIEvent
|
||||
{
|
||||
// 自动采集(SDK 开启自动采集即可,不需要手动埋点)
|
||||
public const string APP_INSTALL = "app_install";
|
||||
public const string APP_START = "app_start";
|
||||
public const string APP_END = "app_end";
|
||||
public const string APP_VIEW = "app_view";
|
||||
public const string APP_CRASH = "app_crash";
|
||||
|
||||
// 内部预制事件
|
||||
public const string AD_REQUEST = "ad_request"; // 广告请求
|
||||
public const string AD_INVENTORY = "ad_inventory"; // 广告填充的时候
|
||||
public const string AD_IMP = "ad_imp"; // 广告展示
|
||||
public const string AD_CLICK = "ad_click"; // 广告点击
|
||||
|
||||
public const string PURCHASE = "purchase"; // 内购
|
||||
|
||||
// 页面相关
|
||||
public const string PAGE_VIEW = "page_view_customize";
|
||||
|
||||
// 其他业务事件(AB 测试等)
|
||||
public const string AB_CONFIG = "config"; // AB
|
||||
}
|
||||
Reference in New Issue
Block a user