18 KiB
Analytics Tutorial
Learn how to track player behavior, measure engagement, and make data-driven decisions with AeThex Analytics.
What You'll Build
A complete analytics system that tracks:
- Player events (level complete, item collected, etc.)
- User properties (level, skill, preferences)
- Custom funnels (onboarding, purchases, retention)
- Crash reports and errors
- Performance metrics
Time: 25 minutes
Difficulty: Beginner
Prerequisites: Basic GDScript knowledge
Why Use Analytics?
Analytics helps you:
- Understand players - See what they do in your game
- Improve retention - Find where players drop off
- Optimize monetization - Track purchase funnels
- Fix bugs faster - Get crash reports automatically
- Make data-driven decisions - Know what features to build
Step 1: Connect to Analytics
First, connect to AeThex Cloud and initialize analytics:
# main.gd
extends Node
func _ready():
# Connect to cloud
var result = await AeThexCloud.connect_to_cloud()
if result.success:
print("Connected to AeThex Cloud")
initialize_analytics()
else:
print("Failed to connect: ", result.error)
func initialize_analytics():
# Analytics initialization is automatic
# Track app start
AeThexAnalytics.track_event("app_started", {
"platform": OS.get_name(),
"version": ProjectSettings.get_setting("application/config/version")
})
Step 2: Track Basic Events
Track important game events:
#player.gd
extends CharacterBody2D
var level = 1
var score = 0
func level_complete():
# Track level completion
AeThexAnalytics.track_event("level_complete", {
"level": level,
"score": score,
"time_spent": get_level_time(),
"deaths": death_count
})
level += 1
func collect_item(item_name: String):
# Track item collection
AeThexAnalytics.track_event("item_collected", {
"item_name": item_name,
"level": level,
"timestamp": Time.get_unix_time_from_system()
})
func player_died():
# Track deaths
AeThexAnalytics.track_event("player_died", {
"level": level,
"cause": death_cause,
"position": global_position
})
Step 3: Set User Properties
Store persistent information about users:
# analytics_manager.gd (autoload)
extends Node
func _ready():
# Set up user properties when game starts
setup_user_properties()
func setup_user_properties():
# Basic user info
AeThexAnalytics.set_user_property("platform", OS.get_name())
AeThexAnalytics.set_user_property("game_version", ProjectSettings.get_setting("application/config/version"))
# Load from save file
var save_data = load_save()
if save_data:
AeThexAnalytics.set_user_property("player_level", save_data.level)
AeThexAnalytics.set_user_property("total_playtime", save_data.playtime)
AeThexAnalytics.set_user_property("achievements_unlocked", save_data.achievements.size())
func on_player_level_up(new_level: int):
# Update user property when it changes
AeThexAnalytics.set_user_property("player_level", new_level)
func on_achievement_unlocked(achievement_id: String):
# Increment property
AeThexAnalytics.increment_user_property("achievements_unlocked", 1)
# Track event
AeThexAnalytics.track_event("achievement_unlocked", {
"achievement_id": achievement_id
})
Step 4: Track Screen Views
Monitor which screens players visit:
# base_screen.gd (inherit from this for all screens)
extends Control
var screen_name: String = "Unknown"
var screen_enter_time: float = 0
func _ready():
screen_enter_time = Time.get_ticks_msec() / 1000.0
track_screen_view()
func _exit_tree():
track_screen_duration()
func track_screen_view():
AeThexAnalytics.track_event("screen_view", {
"screen_name": screen_name,
"timestamp": Time.get_unix_time_from_system()
})
func track_screen_duration():
var duration = (Time.get_ticks_msec() / 1000.0) - screen_enter_time
AeThexAnalytics.track_event("screen_duration", {
"screen_name": screen_name,
"duration_seconds": duration
})
Example usage:
# main_menu.gd
extends "res://scripts/base_screen.gd"
func _ready():
screen_name = "main_menu"
super._ready()
Step 5: Track Economy Events
Monitor in-game economy:
# economy_tracker.gd
extends Node
func currency_earned(currency_name: String, amount: int, source: String):
AeThexAnalytics.track_event("currency_earned", {
"currency": currency_name,
"amount": amount,
"source": source, # "quest", "shop", "reward", etc.
"total_balance": get_currency_balance(currency_name)
})
func currency_spent(currency_name: String, amount: int, item: String):
AeThexAnalytics.track_event("currency_spent", {
"currency": currency_name,
"amount": amount,
"item": item,
"remaining_balance": get_currency_balance(currency_name)
})
func item_purchased(item_id: String, price: int, currency: String):
AeThexAnalytics.track_event("item_purchased", {
"item_id": item_id,
"price": price,
"currency": currency,
"source": "shop" # or "chest", "reward", etc.
})
func real_money_purchase(product_id: String, price: float, currency: String):
AeThexAnalytics.track_event("iap_purchase", {
"product_id": product_id,
"price": price,
"currency": currency,
"revenue": price # Important for revenue tracking
})
Step 6: Create Funnels
Track conversion funnels to understand player flow:
# funnel_tracker.gd
extends Node
# Onboarding funnel
func track_onboarding_funnel(step: String):
var funnel_steps = [
"tutorial_start",
"tutorial_complete",
"first_level_start",
"first_level_complete",
"account_created"
]
AeThexAnalytics.track_event("onboarding_funnel", {
"step": step,
"step_number": funnel_steps.find(step) + 1,
"total_steps": funnel_steps.size()
})
# Purchase funnel
func track_purchase_funnel(step: String, product_id: String = ""):
AeThexAnalytics.track_event("purchase_funnel", {
"step": step, # "view_shop", "select_item", "confirm", "complete"
"product_id": product_id
})
# Level progression funnel
func track_level_funnel(step: String, level: int):
AeThexAnalytics.track_event("level_funnel", {
"step": step, # "start", "complete", "fail", "abandon"
"level": level
})
Example usage:
# tutorial.gd
func _ready():
FunnelTracker.track_onboarding_funnel("tutorial_start")
func on_tutorial_complete():
FunnelTracker.track_onboarding_funnel("tutorial_complete")
# shop.gd
func _on_item_clicked(product_id):
FunnelTracker.track_purchase_funnel("select_item", product_id)
func _on_purchase_confirmed(product_id):
FunnelTracker.track_purchase_funnel("confirm", product_id)
Step 7: Track Engagement Metrics
Measure player engagement:
# engagement_tracker.gd (autoload)
extends Node
var session_start_time: float = 0
var total_sessions: int = 0
var events_this_session: int = 0
func _ready():
start_session()
func start_session():
session_start_time = Time.get_ticks_msec() / 1000.0
total_sessions = load_total_sessions() + 1
save_total_sessions(total_sessions)
AeThexAnalytics.track_event("session_start", {
"session_number": total_sessions,
"days_since_install": get_days_since_install()
})
# Set user property
AeThexAnalytics.set_user_property("total_sessions", total_sessions)
func end_session():
var session_duration = (Time.get_ticks_msec() / 1000.0) - session_start_time
AeThexAnalytics.track_event("session_end", {
"duration_seconds": session_duration,
"events_tracked": events_this_session
})
func _notification(what):
if what == NOTIFICATION_WM_CLOSE_REQUEST:
end_session()
func track_engagement_event():
events_this_session += 1
func get_days_since_install() -> int:
var install_date = load_install_date()
if install_date == 0:
install_date = Time.get_unix_time_from_system()
save_install_date(install_date)
return 0
var current_time = Time.get_unix_time_from_system()
var days = (current_time - install_date) / 86400.0 # seconds in a day
return int(days)
Step 8: Track Errors and Crashes
Automatically report errors:
# error_tracker.gd (autoload)
extends Node
func _ready():
# Catch unhandled errors
Engine.get_singleton("ScriptServer").add_global_constant("ErrorTracker", self)
func track_error(error_message: String, stack_trace: String = ""):
AeThexAnalytics.track_event("error_occurred", {
"error_message": error_message,
"stack_trace": stack_trace,
"platform": OS.get_name(),
"version": ProjectSettings.get_setting("application/config/version")
})
func track_crash(crash_reason: String):
AeThexAnalytics.track_event("game_crash", {
"reason": crash_reason,
"platform": OS.get_name(),
"memory_used": Performance.get_monitor(Performance.MEMORY_STATIC),
"fps": Engine.get_frames_per_second()
})
# Catch GDScript errors
func _on_error(error_message: String):
track_error(error_message, get_stack())
# Helper to get stack trace
func get_stack() -> String:
var stack = get_stack_trace()
var result = ""
for frame in stack:
result += "%s:%d in %s()\n" % [frame.source, frame.line, frame.function]
return result
func get_stack_trace() -> Array:
return [] # Implemented in debug builds
Step 9: Performance Tracking
Track game performance metrics:
# performance_tracker.gd (autoload)
extends Node
const SAMPLE_INTERVAL = 5.0 # seconds
var sample_timer: Timer
func _ready():
sample_timer = Timer.new()
sample_timer.timeout.connect(_sample_performance)
sample_timer.wait_time = SAMPLE_INTERVAL
add_child(sample_timer)
sample_timer.start()
func _sample_performance():
var fps = Engine.get_frames_per_second()
var memory_mb = Performance.get_monitor(Performance.MEMORY_STATIC) / 1024.0 / 1024.0
var draw_calls = Performance.get_monitor(Performance.RENDER_TOTAL_DRAW_CALLS_IN_FRAME)
# Track performance event
AeThexAnalytics.track_event("performance_sample", {
"fps": fps,
"memory_mb": memory_mb,
"draw_calls": draw_calls,
"scene": get_tree().current_scene.name if get_tree().current_scene else "unknown"
})
# Alert on low performance
if fps < 30:
track_low_performance(fps, memory_mb)
func track_low_performance(fps: int, memory_mb: float):
AeThexAnalytics.track_event("low_performance", {
"fps": fps,
"memory_mb": memory_mb,
"platform": OS.get_name(),
"scene": get_tree().current_scene.name if get_tree().current_scene else "unknown"
})
Step 10: A/B Testing
Implement A/B tests to compare features:
# ab_testing.gd (autoload)
extends Node
var user_variant: String = ""
func _ready():
assign_variant()
func assign_variant():
# Check if user already has a variant assigned
user_variant = load_user_variant()
if user_variant.is_empty():
# Randomly assign variant (50/50 split)
user_variant = "A" if randf() < 0.5 else "B"
save_user_variant(user_variant)
# Set as user property
AeThexAnalytics.set_user_property("ab_test_variant", user_variant)
# Track assignment
AeThexAnalytics.track_event("ab_test_assigned", {
"variant": user_variant
})
func get_variant() -> String:
return user_variant
func is_variant_a() -> bool:
return user_variant == "A"
func is_variant_b() -> bool:
return user_variant == "B"
func track_conversion(goal: String):
AeThexAnalytics.track_event("ab_test_conversion", {
"variant": user_variant,
"goal": goal
})
Example usage:
# Configure feature based on variant
func _ready():
if ABTesting.is_variant_a():
# Show red button
$Button.modulate = Color.RED
else:
# Show blue button
$Button.modulate = Color.BLUE
func _on_button_pressed():
# Track which variant converted
ABTesting.track_conversion("button_clicked")
Analytics Dashboard
View your analytics data:
- Go to: https://studio.aethex.io/analytics
- Select your project
- View dashboards:
- Overview: DAU, MAU, retention
- Events: All tracked events
- Funnels: Conversion funnels
- User Properties: Audience segments
- Errors: Crash reports
- Performance: FPS, memory, load times
Best Practices
1. Name Events Consistently
# ✓ DO - Use snake_case
AeThexAnalytics.track_event("level_complete", {})
AeThexAnalytics.track_event("item_collected", {})
# ❌ DON'T - Inconsistent naming
AeThexAnalytics.track_event("LevelComplete", {})
AeThexAnalytics.track_event("item-collected", {})
2. Include Context in Events
# ✓ DO - Rich context
AeThexAnalytics.track_event("button_clicked", {
"button_name": "play",
"screen": "main_menu",
"session_time": get_session_time()
})
# ❌ DON'T - No context
AeThexAnalytics.track_event("button_clicked", {})
3. Track the User Journey
# Track entire player flow
AeThexAnalytics.track_event("game_started", {})
AeThexAnalytics.track_event("tutorial_started", {})
AeThexAnalytics.track_event("tutorial_completed", {})
AeThexAnalytics.track_event("first_level_started", {})
AeThexAnalytics.track_event("first_level_completed", {})
4. Avoid PII (Personally Identifiable Information)
# ❌ DON'T - Include PII
AeThexAnalytics.track_event("user_info", {
"email": "user@example.com", # Never track emails
"name": "John Doe", # Never track real names
"ip_address": "192.168.1.1" # Never track IPs
})
# ✓ DO - Use anonymous identifiers
AeThexAnalytics.track_event("user_info", {
"user_id": "anon_123456",
"player_level": 5
})
5. Batch Events if Needed
# For high-frequency events, batch them
var event_batch = []
func track_player_movement():
event_batch.append({
"event": "player_moved",
"position": player.position
})
if event_batch.size() >= 10:
flush_events()
func flush_events():
for event in event_batch:
AeThexAnalytics.track_event(event.event, event)
event_batch.clear()
Common Metrics to Track
Engagement Metrics:
- Daily Active Users (DAU)
- Monthly Active Users (MAU)
- Session length
- Sessions per user
- Day 1/7/30 retention
Monetization Metrics:
- Average Revenue Per User (ARPU)
- Paying User Rate
- Lifetime Value (LTV)
- Conversion rate
Game Metrics:
- Level completion rate
- Tutorial completion rate
- Time to first action
- Churn points
- Player progression
Example: Complete Analytics Setup
# analytics_complete.gd (autoload)
extends Node
signal analytics_ready
var is_initialized = false
func _ready():
initialize()
func initialize():
# Wait for cloud connection
await get_tree().create_timer(1.0).timeout
if not AeThexCloud.is_connected():
await AeThexCloud.connect_to_cloud()
# Set up user properties
setup_user_properties()
# Start session tracking
start_session()
# Connect to game signals
connect_game_signals()
is_initialized = true
analytics_ready.emit()
func setup_user_properties():
AeThexAnalytics.set_user_property("platform", OS.get_name())
AeThexAnalytics.set_user_property("game_version", get_game_version())
AeThexAnalytics.set_user_property("install_date", get_install_date())
func start_session():
AeThexAnalytics.track_event("session_start", {
"platform": OS.get_name(),
"version": get_game_version()
})
func connect_game_signals():
# Connect to various game events
var game = get_tree().root.get_node("Game")
if game:
game.level_completed.connect(on_level_completed)
game.player_died.connect(on_player_died)
game.item_collected.connect(on_item_collected)
func on_level_completed(level: int, score: int):
AeThexAnalytics.track_event("level_complete", {
"level": level,
"score": score
})
func on_player_died(level: int, cause: String):
AeThexAnalytics.track_event("player_died", {
"level": level,
"cause": cause
})
func on_item_collected(item: String):
AeThexAnalytics.track_event("item_collected", {
"item": item
})
func get_game_version() -> String:
return ProjectSettings.get_setting("application/config/version", "1.0.0")
func get_install_date() -> int:
# Load from save or set to current time
var save_file = FileAccess.open("user://install_date.save", FileAccess.READ)
if save_file:
var date = save_file.get_64()
save_file.close()
return date
else:
var date = Time.get_unix_time_from_system()
save_file = FileAccess.open("user://install_date.save", FileAccess.WRITE)
save_file.store_64(date)
save_file.close()
return date
Testing Analytics
Test your analytics implementation:
# test_analytics.gd
extends Node
func _ready():
test_analytics()
func test_analytics():
print("Testing analytics...")
# Test basic event
AeThexAnalytics.track_event("test_event", {
"test": true
})
# Test user property
AeThexAnalytics.set_user_property("test_property", "test_value")
# Check console for confirmation
print("Analytics test complete. Check dashboard for events.")
Next Steps
- Publishing Guide - Deploy your game with analytics
- API Reference - Complete analytics API
- Dashboard: View your data at studio.aethex.io/analytics
Summary
You've learned how to:
✅ Track custom events
✅ Set user properties
✅ Create conversion funnels
✅ Monitor performance
✅ Track errors and crashes
✅ Implement A/B testing
✅ Measure engagement metrics
Analytics gives you the insights to make your game better and understand your players!
Ready to publish? Check out the Publishing Guide! 🚀