Event Ideas for Action Games
Once the basics are in (app_start, game_over, submit_score — see the Dodge the Creeps Walkthrough), the question is what to track next. This recipe is a curated list of eight events that earn their bandwidth for a small action game, grouped by what question they answer.
Every event below is wired live in examples/dodge-the-creeps/. Open the demo, play a few rounds, and check the dashboard pages noted at the bottom of each section to see real data flow.
1 · Core Loop & Retention
Section titled “1 · Core Loop & Retention”These answer how much your game gets played.
game_start
Section titled “game_start”Already covered in the basic walkthrough. Worth re-stating: every round opens with one.
# main.gd:new_game()QuestData.track("game_start")game_over with score and duration
Section titled “game_over with score and duration”Same — the leaderboard event from the walkthrough. The point of repeating it: score and duration_seconds belong on the same event, not split across two. If you only ship score, you can’t tell whether high scores come from skill or from grinding.
# main.gd:game_over()var duration = Time.get_ticks_msec() / 1000.0 - _game_start_timeQuestData.track("game_over", { "score": score, "duration_seconds": snappedf(duration, 0.1),})player_respawn — the stickiness signal
Section titled “player_respawn — the stickiness signal”Fired only on rounds after the first. If a player’s first round ends and they immediately start another, that’s the strongest engagement signal you’ll find — they’re hooked.
# main.gd:new_game()if _total_games > 0: QuestData.track("player_respawn", { "previous_best_score": _best_score, "games_played": _total_games, })What this answers: “Of all players who reached a Game Over, what fraction started another round?” Build it in the dashboard as a funnel: game_over → player_respawn. Anything below ~40% means your game-over screen has friction (or your loop isn’t satisfying).
2 · Gameplay & Balancing
Section titled “2 · Gameplay & Balancing”These answer whether your game is too easy, too hard, or wrong-shaped.
mob_spawned — density at death
Section titled “mob_spawned — density at death”Don’t fire one per spawn — that’s noise. Fire once at game over with the count of enemies on the field at that moment.
# main.gd: counter in _on_MobTimer_timeout_mobs_spawned_this_round += 1var alive_now = get_tree().get_nodes_in_group("mobs").size()if alive_now > _peak_mobs_alive: _peak_mobs_alive = alive_now
# main.gd:game_over()var mobs_at_death = get_tree().get_nodes_in_group("mobs").size()QuestData.track("mob_spawned", { "count_at_death": mobs_at_death, "peak_alive_this_round": _peak_mobs_alive, "total_spawned": _mobs_spawned_this_round, "score_at_death": score,})What this answers: “Is the difficulty curve reasonable?” If 80% of deaths happen at count_at_death > 20, your spawn rate is too aggressive at that score. Dial mob_spawn_interval (Remote Config) until the death-density distribution looks healthy.
score_milestone — the drop-off curve
Section titled “score_milestone — the drop-off curve”Fire at fixed score thresholds (10, 50, 100, …). Aggregating these in the dashboard gives you a drop-off chart: how many runs cleared each milestone.
# main.gd:_on_ScoreTimer_timeoutif score % 10 == 0 and score > 0: QuestData.track("score_milestone", {"score": score})What this answers: “Where do players give up?” If 90% reach score 10 but only 5% reach score 50, you have a difficulty cliff between them. Tune.
movement_pattern — sparse heatmap
Section titled “movement_pattern — sparse heatmap”Send the player’s position every 5 seconds, not every frame. 5s is sparse enough to be cheap, dense enough to show camping vs roaming.
# player.gd:_process(delta)_movement_timer += deltaif _movement_timer >= 5.0: _movement_timer = 0.0 QuestData.track("movement_pattern", { "_x": position.x, "_y": position.y, "level": "dodge_the_creeps", })The _x and _y underscore prefix tells the backend to project these into spatial columns — they show up on the Heatmap page automatically.
What this answers: “Do all players hide in the corner?” If yes, your map design is failing — central positions need to be more attractive. Or the corner is too safe; nerf it.
3 · Technical Performance
Section titled “3 · Technical Performance”These answer whether your game runs well on real hardware.
fps_drop — perf incident
Section titled “fps_drop — perf incident”Fire once per round when FPS falls below 30. The dedup flag is important — without it, a sustained dip would fire dozens of events per second.
# player.gd:_process(delta)if not _fps_warning_sent: var fps := Engine.get_frames_per_second() if fps < 30: _fps_warning_sent = true QuestData.track("fps_drop", { "fps": fps, "resolution": "%dx%d" % [int(screen_size.x), int(screen_size.y)], })Reset _fps_warning_sent = false at the start of each round (in Player.start()).
What this answers: “Which devices struggle?” The SDK auto-attaches OS, CPU, and GPU info to every event — slice the fps_drop count by GPU model in the Performance dashboard. If a specific GPU model dominates, that’s your support-ticket source.
screen_resolution — already automatic
Section titled “screen_resolution — already automatic”You don’t need a track("screen_resolution") call. The SDK captures screen_size (and OS, CPU, GPU, memory) automatically on the first event of each session — visible in the Performance dashboard under the device-info breakdown.
If you want resolution as a property of a specific event (e.g. paired with fps_drop like above), include it in that event’s payload instead of as its own event.
Dashboard payoff
Section titled “Dashboard payoff”Once these are flowing, you have answers for the four questions every action game has to answer in its first month:
| Question | Answer comes from |
|---|---|
| Are players coming back? | player_respawn count vs game_over count |
| Is the difficulty right? | mob_spawned.count_at_death distribution + score_milestone drop-off |
| Where do players spend their time? | movement_pattern heatmap |
| Does it run on real hardware? | fps_drop sliced by GPU + auto device info |
That’s a useful base for any small action game. Add more only when a specific question can’t be answered with what’s already flowing.
Where to go next
Section titled “Where to go next”- Event Tracking — full event API + properties
- Spatial Tracking —
_x/_yconventions for heatmaps - Bullet Shower Walkthrough — the perf-heavy version of
fps_drop(usestrack_errorfor production-grade alerting) - 2D Platformer Walkthrough — heatmap with proper world-coordinate bounds