# Analytics Tutorial
Learn how to track player behavior, measure engagement, and make data-driven decisions with AeThex Analytics.
> [!TIP]
> Analytics is your window into player behavior. Use it to understand what players love, what confuses them, and where they get stuck.
---
## 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
```mermaid
graph LR
Game[🎮 Game Events] -->|Track| SDK[AeThex SDK]
SDK -->|Batch| Cloud[☁️ Cloud]
Cloud -->|Process| Pipeline[Data Pipeline]
Pipeline -->|Store| DB[(Database)]
DB -->|Query| Dashboard[📊 Dashboard]
Dashboard -->|Insights| You[👤 You]
Events[Events
• level_complete
• item_collected
• player_died]
Properties[User Props
• level
• skill
• device]
style Game fill:#00ffff22,stroke:#00ffff
style Cloud fill:#ff00ff22,stroke:#ff00ff
style Dashboard fill:#00ffff22,stroke:#00ffff
```
---
## 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
> [!NOTE]
> AeThex Analytics automatically batches events to minimize network usage and battery drain. No need to worry about performance impact.
---
## Step 1: Connect to Analytics
First, connect to AeThex Cloud and initialize analytics:
> [!WARNING]
> Don't track personally identifiable information (PII) like email addresses or real names in analytics events. Use anonymous user IDs instead.
```gdscript
# 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:
```gdscript
#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:
```gdscript
# 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:
```gdscript
# 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:**
```gdscript
# 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:
```gdscript
# 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:
```gdscript
# 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:**
```gdscript
# 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:
```gdscript
# 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:
```gdscript
# 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:
```gdscript
# 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:
```gdscript
# 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:**
```gdscript
# 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:
1. **Go to:** [https://studio.aethex.dev/analytics](https://studio.aethex.dev/analytics)
2. **Select your project**
3. **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**
```gdscript
# ✓ 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**
```gdscript
# ✓ 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**
```gdscript
# 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)**
```gdscript
# ❌ 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**
```gdscript
# 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
```gdscript
# 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:
```gdscript
# 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](../PUBLISHING_GUIDE.md)** - Deploy your game with analytics
- **[API Reference](../API_REFERENCE.md#aethexanalytics-singleton)** - Complete analytics API
- **Dashboard:** View your data at [studio.aethex.dev/analytics](https://studio.aethex.dev/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](../PUBLISHING_GUIDE.md)! 🚀