# 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<br/>• level_complete<br/>• item_collected<br/>• player_died]
    Properties[User Props<br/>• level<br/>• skill<br/>• 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.io/analytics](https://studio.aethex.io/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.io/analytics](https://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](../PUBLISHING_GUIDE.md)! 🚀
