创建新工程
This commit is contained in:
550
scripts/GameStatistics.gd
Normal file
550
scripts/GameStatistics.gd
Normal file
@@ -0,0 +1,550 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user