← All writing

Rewarded ads a player can say no to

An ad is a trade. The game should only offer one it can keep.

Game designMobile adsUnity
Siren Jam’s world map: an island of roads running from a city through snow, a harbour, a fairground and farmland, with an ambulance on the road.
A reward only matters if the player chose to earn it.

Both of my Matterward Labs games are free. Narrow Front is a portrait WWII strategy game, and Siren Jam is a parking puzzle. Both show ads, and both follow the same rule: the only ads are rewarded videos, and the player always chooses to watch one.

There are no interstitials. Nothing plays between levels by itself, and Narrow Front never shows an ad during a battle. This note covers how that works in the code, and the places where I made a trade-off on purpose.

What a video pays

Each game has exactly one placement.

In Narrow Front, a completed video pays a Supply Drop: the basic free crate of cards, a little smaller than the Field Crate you win in battle. It is capped at three a day. The game also sells premium crates in its store, and those are never earned with an ad. A video only ever pays the free crate.

In Siren Jam, a completed video pays 40 coins, at most five times a day. Coins buy power-ups in the shop, or five extra moves for 60 coins when a level runs out. If you cannot afford the moves and a video is ready, a WATCH AD button sits under the offer, so two videos buy the extra moves. Siren Jam has nothing to buy with real money.

In both games the reward and the daily allowance are data, so I can change either without touching the flow.

Only offer what can pay

The rule I care most about is that a watched video always pays. If the player sits through thirty seconds and gets nothing, the game has broken its side of the trade.

So the button exists only when two things are true: an ad is loaded, and the reward rules say a grant would succeed right now. The rules check comes first. In Narrow Front it also covers a full crate rack, since a drop with nowhere to go should never be offered.

Siren Jam’s allowance check is small enough to show in full:

public static int Left(ProgressData p, int day)
{
    if (day > p.adDay) return PerDay;
    if (day < p.adDay) return 0;
    return Math.Max(0, PerDay - p.adsToday);
}

A new UTC day restores the allowance. A day earlier than the last one recorded returns zero, so setting the clock back never reopens a day that was already used.

Demand works the same way. The game tells the ad provider whether a reward could be granted now, at start-up, when the shop, depot or out-of-moves panel opens, and after each video. Narrow Front checks again whenever the player returns to the app. The provider keeps one ad buffered only while there is demand. When the day’s allowance is spent, it stops requesting, which avoids loading ads nobody can use.

When there is no ad

Ads fail all the time: no connection, no fill, a slow network. Both games give that state its own design instead of a disabled button.

Siren Jam’s FREE COINS card in the shop has four states:

State Shows
Ready +40 a video, how many are left today, and a pulsing WATCH
Loading “Finding a video...”
No signal “No videos right now” after two failed loads in a row
Done for today “More videos tomorrow” with a countdown to UTC midnight

Narrow Front draws the same states in its own voice. While loading, the card says it is signalling HQ for a drop. After repeated failures it shows NO SIGNAL. When the day is used, a countdown shows the next drop. In both games, the card redraws itself when an ad finally arrives.

If consent has not been answered yet, or the device cannot request ads, there is no card at all. An offer the player cannot take is just noise.

Settling a show exactly once

The ad SDK reports through callbacks, and they do not always arrive in a tidy order. Both games wrap a show so that it settles exactly once, after the ad has closed:

  • Completed, with a reward ID, only if the reward callback fired.
  • Skipped when the player closed it early. The message says to watch to the end.
  • Failed or Unavailable otherwise.

Two edge cases needed care. The reward callback can arrive just after the close callback, so an unrewarded close waits 0.8 seconds before it counts as skipped. And sometimes the close never arrives at all, so a watchdog settles the show four seconds after the app comes back to the front.

Each reward ID is remembered in the save. Siren Jam keeps the latest sixteen; Narrow Front keeps them with its battle result IDs. One video can never pay twice, even if a callback repeats. While a video is opening, a hold on the interface ignores a second tap (and, in Narrow Front, the Android back button), and game audio pauses.

The ads SDK starts only after Google’s User Messaging Platform says the game may request ads. The consent form appears only where it is required, over the first screen. A choice made in an earlier session starts the SDK straight away. Where the rules require a way back into those choices, Settings shows an AD PRIVACY row.

On iOS, Apple’s tracking prompt comes after the consent step and before the SDK starts, and only once the app is active, because iOS ignores a request made in the background. Development builds always request Google’s test ads, and a store build refuses to build while a sample ID is still configured.

The content ratings differ. Narrow Front’s ads are capped at a teen rating, which suits a WWII strategy game. Siren Jam’s are capped at general audiences, because its audience includes younger players.

The trade-offs I accepted

Both games keep progress on the device, and that shapes what the ad code can promise.

The daily cap follows the device clock. Setting it back does nothing, but setting it forward could farm a few extra rewards. For an offline game I accepted that. Server-side reward verification would close it, at the cost of making every reward depend on a network round trip.

A video interrupted by a killed app pays nothing. If the reward never reached the game, there is nothing to recover without server verification. A Narrow Front crate that was granted but not yet opened is safe, though. It is already saved in the rack and flies in when the player next opens the depot.

One placement per game. I could add more, but every extra offer is another thing asking for the player’s attention. One clear trade, where the player already wants something, is easier to respect.

What I would keep

The parts I would carry into the next game are small: gate the offer on the same rules that grant the reward, settle every show once, give the no-ad state real copy, and let the player find the offer instead of having it pushed at them. None of it is complicated. It just has to be decided before the first ad loads.

Sources & further reading

Another question worth following?

Explore the workGet in touch

A good project often starts here.

Leave me
a what if.

All the places to find me ↗
To Satyajit

There’s an app I wish existed.

Tell me who would use it and what they are trying to do. A rough sketch is plenty to start with.

Talk to me on LinkedInLet’s follow the question.