550 lines
16 KiB
GDScript
550 lines
16 KiB
GDScript
extends Node
|
||
class_name GameStatistics
|
||
## 游戏统计和分析功能
|
||
## 收集和分析游戏整体运行数据和玩家统计信息
|
||
|
||
# 统计数据类型
|
||
enum StatType {
|
||
PLAYER_COUNT, # 玩家数量
|
||
SESSION_DURATION, # 会话时长
|
||
FEATURE_USAGE, # 功能使用率
|
||
PERFORMANCE, # 性能指标
|
||
ERROR_RATE, # 错误率
|
||
ENGAGEMENT, # 参与度
|
||
RETENTION, # 留存率
|
||
SOCIAL_ACTIVITY # 社交活动
|
||
}
|
||
|
||
# 统计数据结构
|
||
class GameStat:
|
||
var stat_type: StatType
|
||
var value: float
|
||
var timestamp: float
|
||
var metadata: Dictionary = {}
|
||
|
||
func _init(type: StatType, val: float, meta: Dictionary = {}):
|
||
stat_type = type
|
||
value = val
|
||
timestamp = Time.get_unix_time_from_system()
|
||
metadata = meta.duplicate()
|
||
|
||
# 数据存储
|
||
var statistics_history: Array[GameStat] = []
|
||
var daily_statistics: Dictionary = {} # date_string -> Dictionary
|
||
var feature_usage_stats: Dictionary = {}
|
||
var performance_metrics: Dictionary = {}
|
||
var player_statistics: Dictionary = {}
|
||
|
||
# 配置
|
||
var max_history_entries: int = 10000
|
||
var statistics_enabled: bool = true
|
||
var collection_interval: float = 300.0 # 5分钟收集一次
|
||
|
||
# 引用其他系统
|
||
var user_behavior_analytics: UserBehaviorAnalytics
|
||
var social_manager: SocialManager
|
||
|
||
# 数据持久化
|
||
var stats_file_path: String = "user://game_statistics.json"
|
||
|
||
# 信号
|
||
signal statistic_recorded(stat_type: StatType, value: float)
|
||
signal daily_report_generated(date: String, report: Dictionary)
|
||
signal performance_alert(metric: String, value: float, threshold: float)
|
||
|
||
func _ready():
|
||
"""初始化游戏统计系统"""
|
||
_load_statistics_data()
|
||
|
||
# 设置定时统计收集
|
||
var collection_timer = Timer.new()
|
||
collection_timer.wait_time = collection_interval
|
||
collection_timer.timeout.connect(_collect_periodic_statistics)
|
||
collection_timer.autostart = true
|
||
add_child(collection_timer)
|
||
|
||
# 设置每日报告生成
|
||
var daily_timer = Timer.new()
|
||
daily_timer.wait_time = 86400.0 # 24小时
|
||
daily_timer.timeout.connect(_generate_daily_report)
|
||
daily_timer.autostart = true
|
||
add_child(daily_timer)
|
||
|
||
print("GameStatistics initialized")
|
||
|
||
## 设置系统引用
|
||
func set_system_references(uba: UserBehaviorAnalytics, sm: SocialManager) -> void:
|
||
"""
|
||
设置其他系统的引用
|
||
@param uba: 用户行为分析系统
|
||
@param sm: 社交管理器
|
||
"""
|
||
user_behavior_analytics = uba
|
||
social_manager = sm
|
||
|
||
# 连接信号
|
||
if user_behavior_analytics:
|
||
user_behavior_analytics.behavior_recorded.connect(_on_behavior_recorded)
|
||
|
||
if social_manager:
|
||
social_manager.friend_activity.connect(_on_social_activity)
|
||
|
||
## 记录统计数据
|
||
func record_statistic(stat_type: StatType, value: float, metadata: Dictionary = {}) -> void:
|
||
"""
|
||
记录统计数据
|
||
@param stat_type: 统计类型
|
||
@param value: 统计值
|
||
@param metadata: 元数据
|
||
"""
|
||
if not statistics_enabled:
|
||
return
|
||
|
||
var stat = GameStat.new(stat_type, value, metadata)
|
||
statistics_history.append(stat)
|
||
|
||
# 限制历史记录数量
|
||
if statistics_history.size() > max_history_entries:
|
||
statistics_history.pop_front()
|
||
|
||
# 更新当日统计
|
||
_update_daily_statistics(stat)
|
||
|
||
# 检查性能警报
|
||
_check_performance_alerts(stat_type, value)
|
||
|
||
# 发射信号
|
||
statistic_recorded.emit(stat_type, value)
|
||
|
||
print("Statistic recorded: ", StatType.keys()[stat_type], " = ", value)
|
||
|
||
## 记录玩家数量
|
||
func record_player_count(online_count: int, total_count: int) -> void:
|
||
"""
|
||
记录玩家数量统计
|
||
@param online_count: 在线玩家数
|
||
@param total_count: 总玩家数
|
||
"""
|
||
record_statistic(StatType.PLAYER_COUNT, online_count, {
|
||
"total_players": total_count,
|
||
"online_ratio": float(online_count) / max(total_count, 1)
|
||
})
|
||
|
||
## 记录功能使用情况
|
||
func record_feature_usage(feature_name: String, usage_count: int, user_count: int) -> void:
|
||
"""
|
||
记录功能使用统计
|
||
@param feature_name: 功能名称
|
||
@param usage_count: 使用次数
|
||
@param user_count: 使用用户数
|
||
"""
|
||
# 更新功能使用统计
|
||
if not feature_usage_stats.has(feature_name):
|
||
feature_usage_stats[feature_name] = {
|
||
"total_usage": 0,
|
||
"unique_users": {},
|
||
"daily_usage": {}
|
||
}
|
||
|
||
var feature_stat = feature_usage_stats[feature_name]
|
||
feature_stat.total_usage += usage_count
|
||
|
||
# 记录到统计历史
|
||
record_statistic(StatType.FEATURE_USAGE, usage_count, {
|
||
"feature": feature_name,
|
||
"user_count": user_count,
|
||
"usage_rate": float(usage_count) / max(user_count, 1)
|
||
})
|
||
|
||
## 记录性能指标
|
||
func record_performance_metric(metric_name: String, value: float, threshold: float = 0.0) -> void:
|
||
"""
|
||
记录性能指标
|
||
@param metric_name: 指标名称
|
||
@param value: 指标值
|
||
@param threshold: 警报阈值
|
||
"""
|
||
# 更新性能指标历史
|
||
if not performance_metrics.has(metric_name):
|
||
performance_metrics[metric_name] = []
|
||
|
||
var metric_history = performance_metrics[metric_name]
|
||
metric_history.append({
|
||
"value": value,
|
||
"timestamp": Time.get_unix_time_from_system()
|
||
})
|
||
|
||
# 限制历史记录长度
|
||
if metric_history.size() > 100:
|
||
metric_history.pop_front()
|
||
|
||
# 记录统计
|
||
record_statistic(StatType.PERFORMANCE, value, {
|
||
"metric": metric_name,
|
||
"threshold": threshold
|
||
})
|
||
|
||
## 记录参与度指标
|
||
func record_engagement_metric(session_duration: float, actions_count: int, features_used: int) -> void:
|
||
"""
|
||
记录用户参与度指标
|
||
@param session_duration: 会话时长
|
||
@param actions_count: 操作次数
|
||
@param features_used: 使用功能数
|
||
"""
|
||
var engagement_score = _calculate_engagement_score(session_duration, actions_count, features_used)
|
||
|
||
record_statistic(StatType.ENGAGEMENT, engagement_score, {
|
||
"session_duration": session_duration,
|
||
"actions_count": actions_count,
|
||
"features_used": features_used
|
||
})
|
||
|
||
## 计算参与度分数
|
||
func _calculate_engagement_score(duration: float, actions: int, features: int) -> float:
|
||
"""
|
||
计算参与度分数
|
||
@param duration: 会话时长(秒)
|
||
@param actions: 操作次数
|
||
@param features: 使用功能数
|
||
@return: 参与度分数(0-100)
|
||
"""
|
||
# 时长分数(最多30分钟满分)
|
||
var duration_score = min(duration / 1800.0, 1.0) * 40.0
|
||
|
||
# 操作频率分数
|
||
var action_rate = actions / max(duration / 60.0, 1.0) # 每分钟操作数
|
||
var action_score = min(action_rate / 10.0, 1.0) * 30.0
|
||
|
||
# 功能多样性分数
|
||
var feature_score = min(features / 5.0, 1.0) * 30.0
|
||
|
||
return duration_score + action_score + feature_score
|
||
|
||
## 定期收集统计数据
|
||
func _collect_periodic_statistics() -> void:
|
||
"""定期收集系统统计数据"""
|
||
if not statistics_enabled:
|
||
return
|
||
|
||
# 收集性能数据
|
||
var fps = Engine.get_frames_per_second()
|
||
var memory = OS.get_static_memory_usage_by_type()
|
||
|
||
record_performance_metric("fps", fps, 30.0)
|
||
record_performance_metric("memory_mb", memory / 1024.0 / 1024.0, 512.0)
|
||
|
||
# 收集用户行为数据
|
||
if user_behavior_analytics:
|
||
var behavior_stats = user_behavior_analytics.get_realtime_statistics()
|
||
record_statistic(StatType.SESSION_DURATION, behavior_stats.get("current_session_duration", 0.0))
|
||
|
||
# 收集社交活动数据
|
||
if social_manager:
|
||
var social_stats = social_manager.get_statistics()
|
||
_record_social_statistics(social_stats)
|
||
|
||
## 记录社交统计
|
||
func _record_social_statistics(social_stats: Dictionary) -> void:
|
||
"""记录社交活动统计"""
|
||
if social_stats.has("friend_system"):
|
||
var friend_stats = social_stats.friend_system
|
||
record_statistic(StatType.SOCIAL_ACTIVITY, friend_stats.get("total_friends", 0), {
|
||
"type": "friends",
|
||
"pending_requests": friend_stats.get("pending_requests", 0)
|
||
})
|
||
|
||
if social_stats.has("community_events"):
|
||
var event_stats = social_stats.community_events
|
||
record_statistic(StatType.SOCIAL_ACTIVITY, event_stats.get("active_events", 0), {
|
||
"type": "events",
|
||
"total_events": event_stats.get("total_events", 0)
|
||
})
|
||
|
||
## 更新每日统计
|
||
func _update_daily_statistics(stat: GameStat) -> void:
|
||
"""更新每日统计数据"""
|
||
var date_string = Time.get_date_string_from_unix_time(stat.timestamp)
|
||
|
||
if not daily_statistics.has(date_string):
|
||
daily_statistics[date_string] = {
|
||
"date": date_string,
|
||
"stats_by_type": {},
|
||
"total_records": 0,
|
||
"performance_summary": {},
|
||
"feature_usage_summary": {}
|
||
}
|
||
|
||
var daily_data = daily_statistics[date_string]
|
||
daily_data.total_records += 1
|
||
|
||
# 按类型统计
|
||
var type_name = StatType.keys()[stat.stat_type]
|
||
if not daily_data.stats_by_type.has(type_name):
|
||
daily_data.stats_by_type[type_name] = {
|
||
"count": 0,
|
||
"total_value": 0.0,
|
||
"min_value": stat.value,
|
||
"max_value": stat.value
|
||
}
|
||
|
||
var type_stats = daily_data.stats_by_type[type_name]
|
||
type_stats.count += 1
|
||
type_stats.total_value += stat.value
|
||
type_stats.min_value = min(type_stats.min_value, stat.value)
|
||
type_stats.max_value = max(type_stats.max_value, stat.value)
|
||
|
||
## 生成每日报告
|
||
func _generate_daily_report() -> void:
|
||
"""生成每日统计报告"""
|
||
var yesterday = Time.get_date_string_from_unix_time(Time.get_unix_time_from_system() - 86400)
|
||
|
||
if not daily_statistics.has(yesterday):
|
||
return
|
||
|
||
var daily_data = daily_statistics[yesterday]
|
||
var report = _create_daily_report(daily_data)
|
||
|
||
daily_report_generated.emit(yesterday, report)
|
||
print("Daily report generated for: ", yesterday)
|
||
|
||
## 创建每日报告
|
||
func _create_daily_report(daily_data: Dictionary) -> Dictionary:
|
||
"""创建每日报告数据"""
|
||
var report = daily_data.duplicate()
|
||
|
||
# 计算平均值
|
||
for type_name in daily_data.stats_by_type:
|
||
var type_stats = daily_data.stats_by_type[type_name]
|
||
type_stats["average_value"] = type_stats.total_value / max(type_stats.count, 1)
|
||
|
||
# 添加趋势分析
|
||
report["trends"] = _analyze_daily_trends(daily_data.date)
|
||
|
||
# 添加关键指标摘要
|
||
report["key_metrics"] = _extract_key_metrics(daily_data)
|
||
|
||
return report
|
||
|
||
## 分析每日趋势
|
||
func _analyze_daily_trends(date: String) -> Dictionary:
|
||
"""分析每日数据趋势"""
|
||
var trends = {}
|
||
|
||
# 获取前一天数据进行对比
|
||
var prev_date_time = Time.get_unix_time_from_datetime_string(date + "T00:00:00") - 86400
|
||
var prev_date = Time.get_date_string_from_unix_time(prev_date_time)
|
||
|
||
if daily_statistics.has(prev_date):
|
||
var current_data = daily_statistics[date]
|
||
var prev_data = daily_statistics[prev_date]
|
||
|
||
# 比较总记录数
|
||
var current_total = current_data.get("total_records", 0)
|
||
var prev_total = prev_data.get("total_records", 0)
|
||
|
||
if prev_total > 0:
|
||
trends["activity_change"] = (float(current_total - prev_total) / prev_total) * 100.0
|
||
|
||
# 比较各类型统计
|
||
trends["type_changes"] = {}
|
||
for type_name in current_data.get("stats_by_type", {}):
|
||
var current_count = current_data.stats_by_type[type_name].get("count", 0)
|
||
var prev_count = prev_data.get("stats_by_type", {}).get(type_name, {}).get("count", 0)
|
||
|
||
if prev_count > 0:
|
||
trends.type_changes[type_name] = (float(current_count - prev_count) / prev_count) * 100.0
|
||
|
||
return trends
|
||
|
||
## 提取关键指标
|
||
func _extract_key_metrics(daily_data: Dictionary) -> Dictionary:
|
||
"""提取每日关键指标"""
|
||
var metrics = {}
|
||
|
||
var stats_by_type = daily_data.get("stats_by_type", {})
|
||
|
||
# 性能指标
|
||
if stats_by_type.has("PERFORMANCE"):
|
||
metrics["performance"] = {
|
||
"average": stats_by_type.PERFORMANCE.get("average_value", 0.0),
|
||
"min": stats_by_type.PERFORMANCE.get("min_value", 0.0),
|
||
"max": stats_by_type.PERFORMANCE.get("max_value", 0.0)
|
||
}
|
||
|
||
# 参与度指标
|
||
if stats_by_type.has("ENGAGEMENT"):
|
||
metrics["engagement"] = {
|
||
"average_score": stats_by_type.ENGAGEMENT.get("average_value", 0.0),
|
||
"peak_score": stats_by_type.ENGAGEMENT.get("max_value", 0.0)
|
||
}
|
||
|
||
# 社交活动指标
|
||
if stats_by_type.has("SOCIAL_ACTIVITY"):
|
||
metrics["social"] = {
|
||
"activity_count": stats_by_type.SOCIAL_ACTIVITY.get("count", 0),
|
||
"average_activity": stats_by_type.SOCIAL_ACTIVITY.get("average_value", 0.0)
|
||
}
|
||
|
||
return metrics
|
||
|
||
## 检查性能警报
|
||
func _check_performance_alerts(stat_type: StatType, value: float) -> void:
|
||
"""检查性能警报条件"""
|
||
if stat_type == StatType.PERFORMANCE:
|
||
# FPS过低警报
|
||
if value < 20.0:
|
||
performance_alert.emit("low_fps", value, 20.0)
|
||
|
||
# 内存使用过高警报(假设单位是MB)
|
||
if value > 1000.0:
|
||
performance_alert.emit("high_memory", value, 1000.0)
|
||
|
||
## 获取统计摘要
|
||
func get_statistics_summary(days: int = 7) -> Dictionary:
|
||
"""
|
||
获取统计摘要
|
||
@param days: 统计天数
|
||
@return: 统计摘要
|
||
"""
|
||
var summary = {}
|
||
var current_time = Time.get_unix_time_from_system()
|
||
var start_time = current_time - (days * 86400)
|
||
|
||
# 获取时间范围内的统计
|
||
var recent_stats = statistics_history.filter(func(stat): return stat.timestamp >= start_time)
|
||
|
||
summary["period_days"] = days
|
||
summary["total_records"] = recent_stats.size()
|
||
summary["start_date"] = Time.get_date_string_from_unix_time(start_time)
|
||
summary["end_date"] = Time.get_date_string_from_unix_time(current_time)
|
||
|
||
# 按类型汇总
|
||
var type_summary = {}
|
||
for stat in recent_stats:
|
||
var type_name = StatType.keys()[stat.stat_type]
|
||
if not type_summary.has(type_name):
|
||
type_summary[type_name] = {
|
||
"count": 0,
|
||
"values": []
|
||
}
|
||
|
||
type_summary[type_name].count += 1
|
||
type_summary[type_name].values.append(stat.value)
|
||
|
||
# 计算统计指标
|
||
for type_name in type_summary:
|
||
var values = type_summary[type_name].values
|
||
if values.size() > 0:
|
||
type_summary[type_name]["average"] = _calculate_average(values)
|
||
type_summary[type_name]["min"] = values.min()
|
||
type_summary[type_name]["max"] = values.max()
|
||
|
||
summary["by_type"] = type_summary
|
||
|
||
return summary
|
||
|
||
## 计算平均值
|
||
func _calculate_average(values: Array) -> float:
|
||
"""计算数组平均值"""
|
||
if values.is_empty():
|
||
return 0.0
|
||
|
||
var sum = 0.0
|
||
for value in values:
|
||
sum += float(value)
|
||
|
||
return sum / values.size()
|
||
|
||
## 信号处理函数
|
||
func _on_behavior_recorded(event_type: UserBehaviorAnalytics.EventType, data: Dictionary):
|
||
"""处理用户行为记录事件"""
|
||
# 将用户行为转换为游戏统计
|
||
match event_type:
|
||
UserBehaviorAnalytics.EventType.LOGIN:
|
||
record_statistic(StatType.PLAYER_COUNT, 1.0, {"action": "login"})
|
||
UserBehaviorAnalytics.EventType.UI_ACTION:
|
||
var element = data.get("element", "unknown")
|
||
record_feature_usage(element, 1, 1)
|
||
|
||
func _on_social_activity(activity_type: String, data: Dictionary):
|
||
"""处理社交活动事件"""
|
||
record_statistic(StatType.SOCIAL_ACTIVITY, 1.0, {
|
||
"activity_type": activity_type,
|
||
"data": data
|
||
})
|
||
|
||
## 保存统计数据
|
||
func _save_statistics_data() -> void:
|
||
"""保存统计数据到本地文件"""
|
||
var data = {
|
||
"statistics_history": [],
|
||
"daily_statistics": daily_statistics,
|
||
"feature_usage_stats": feature_usage_stats,
|
||
"performance_metrics": performance_metrics,
|
||
"saved_at": Time.get_unix_time_from_system()
|
||
}
|
||
|
||
# 序列化统计历史(只保存最近的数据)
|
||
var stats_to_save = statistics_history.slice(max(0, statistics_history.size() - 1000), statistics_history.size())
|
||
|
||
for stat in stats_to_save:
|
||
data.statistics_history.append({
|
||
"stat_type": stat.stat_type,
|
||
"value": stat.value,
|
||
"timestamp": stat.timestamp,
|
||
"metadata": stat.metadata
|
||
})
|
||
|
||
var file = FileAccess.open(stats_file_path, FileAccess.WRITE)
|
||
if file:
|
||
var json_string = JSON.stringify(data)
|
||
file.store_string(json_string)
|
||
file.close()
|
||
print("Game statistics saved: ", stats_to_save.size(), " records")
|
||
else:
|
||
print("Failed to save game statistics")
|
||
|
||
## 加载统计数据
|
||
func _load_statistics_data() -> void:
|
||
"""从本地文件加载统计数据"""
|
||
if not FileAccess.file_exists(stats_file_path):
|
||
print("No game statistics file found, starting fresh")
|
||
return
|
||
|
||
var file = FileAccess.open(stats_file_path, FileAccess.READ)
|
||
if file:
|
||
var json_string = file.get_as_text()
|
||
file.close()
|
||
|
||
var json = JSON.new()
|
||
var parse_result = json.parse(json_string)
|
||
|
||
if parse_result == OK:
|
||
var data = json.data
|
||
|
||
# 加载统计历史
|
||
if data.has("statistics_history"):
|
||
for stat_data in data.statistics_history:
|
||
var stat = GameStat.new(
|
||
stat_data.get("stat_type", StatType.PERFORMANCE),
|
||
stat_data.get("value", 0.0),
|
||
stat_data.get("metadata", {})
|
||
)
|
||
stat.timestamp = stat_data.get("timestamp", 0.0)
|
||
statistics_history.append(stat)
|
||
|
||
# 加载其他数据
|
||
daily_statistics = data.get("daily_statistics", {})
|
||
feature_usage_stats = data.get("feature_usage_stats", {})
|
||
performance_metrics = data.get("performance_metrics", {})
|
||
|
||
print("Game statistics loaded: ", statistics_history.size(), " records")
|
||
else:
|
||
print("Failed to parse game statistics JSON")
|
||
else:
|
||
print("Failed to open game statistics file")
|
||
|
||
func _exit_tree():
|
||
"""节点退出时保存数据"""
|
||
_save_statistics_data() |