创建新工程

This commit is contained in:
moyin
2025-12-05 19:00:14 +08:00
commit ff4fa5fffd
227 changed files with 32804 additions and 0 deletions

550
scripts/GameStatistics.gd Normal file
View 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()