-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Jovan edited this page Sep 26, 2025
·
12 revisions
A lightweight Unity component that prevents players from staying idle (AFK) for too long.
It provides configurable timers, countdowns, and UnityEvents so you can react to AFK behavior (e.g., skip turns, kick players, show UI warnings).
The Anti-AFK system tracks player input and detects inactivity.
- If a player is idle for too long, a countdown starts.
- During the countdown, the player has a chance to wake up by pressing a key or moving the mouse.
- If the countdown ends, the system either skips the player’s turn or kicks them (depending on settings).
- You can subscribe to UnityEvents to react in your own game logic (UI, networking, player manager, etc.).
The AntiAfk MonoBehaviour exposes the following fields in the Inspector (with Odin Inspector support):
| Setting | Type | Default | Description |
|---|---|---|---|
| Wait For Input Secs | float |
30 |
Time (in seconds) of inactivity before the AFK countdown starts. |
| Countdown Secs | float |
15 |
Duration (in seconds) of the countdown before action is taken. |
| Is Strict | bool |
true |
If enabled, players can eventually be kicked after too many AFK turns. |
| Max AFK Turns | int |
3 |
Number of tolerated AFK turns before a player is kicked (only if strict). |
- Add the AntiAfk component to a GameObject in your scene.
- Call
ToogleAfk(playerId)at the start of a player’s turn to begin AFK checking for that player. - The system will automatically detect inputs (
Input.anyKeyDownor mouse movement).- If input is detected → AFK timer resets.
- If no input → countdown starts, then either skip or kick the player.
- Listen to the UnityEvents to hook into your game’s logic.
In case you want to use the AntiAfk package with network framework like
Photon Fusion(which is possible), clients must communicate with the host when there are considered as AFK, so the host can kick player or just skip the turn.
You can subscribe directly in the Inspector or via code:
| Event | Args | Description |
|---|---|---|
| OnCountdownStarted | (int playerId, float countdownDuration) |
Fired when the countdown starts. |
| OnCountdownTick | (int playerId, float secondsLeft) |
Fired every second while the countdown is running. |
| OnCountdownEnded | (int playerId) |
Fired when the countdown reaches 0. |
| OnPlayerKicked | (int playerId) |
Fired when a player is kicked for being AFK too many times. |
| OnPlayerTurnSkipped | (int playerId) |
Fired when a player’s turn is skipped due to AFK. |
| OnPlayerWakeUp | (int playerId) |
Fired when a player provides input again (keyboard or mouse). |
using TMPro;
using UnityEngine;
using Sirenix.OdinInspector;
/// <summary>
/// This is an example script showing how to use the AntiAfk component to display a countdown UI.
/// In this example, we assume that the AntiAfk component is already present in the scene.
/// Also the UI is very basic and should be adapted to your needs.
/// The AntiAfk UI is client side only, so each player will see their own countdown when they are AFK..
/// </summary>
public class CountdownUIExample : MonoBehaviour
{
#region UI
[Title("UI References")]
[Required("A TextMeshPro need to be assigned, otherwise the anti-AFK cannot display the countdown text.")]
[SerializeField] private TMP_Text _CountdownText;
[Required("A Parent Gameobject need to be assigned, otherwise the coutdown will always be displayed or hidden.")]
[SerializeField] private GameObject _ParentContainer;
#endregion
#region Properties
private AntiAfk _AntiAfk;
#endregion
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
// Get the AntiAfk component from the scene
_AntiAfk = FindFirstObjectByType<AntiAfk>();
if (_AntiAfk == null)
{
Debug.LogError("No AntiAfk component found in the scene. Please add one to use the CountdownUIExample.");
return;
}
// Register to the AntiAfk events to update the UI
_AntiAfk.OnCountdownStarted.AddListener(HandleCountdownStarted);
_AntiAfk.OnCountdownTick.AddListener(HandleCountdownTick);
_AntiAfk.OnCountdownEnded.AddListener(HandleCountdownEnded);
_AntiAfk.OnPlayerWakeUp.AddListener(HandleCountdownInterrupted);
}
private void OnDestroy()
{
if (_AntiAfk != null)
{
// Unregister from the AntiAfk events to avoid memory leaks
_AntiAfk.OnCountdownStarted.RemoveListener(HandleCountdownStarted);
_AntiAfk.OnCountdownTick.RemoveListener(HandleCountdownTick);
_AntiAfk.OnCountdownEnded.RemoveListener(HandleCountdownEnded);
_AntiAfk.OnPlayerWakeUp.RemoveListener(HandleCountdownInterrupted);
}
}
private void HandleCountdownStarted(int playerId, float countdownDuration)
{
if (_ParentContainer != null)
_ParentContainer.SetActive(true);
if (_CountdownText != null)
_CountdownText.text = $"Player {playerId} is AFK! Countdown: {countdownDuration:F0}s";
}
private void HandleCountdownTick(int playerId, float secondsLeft)
{
if (_CountdownText != null)
_CountdownText.text = $"Player {playerId} is AFK! Countdown: {secondsLeft:F0}s";
}
private void HandleCountdownEnded(int playerId)
{
if (_ParentContainer != null)
_ParentContainer.SetActive(false);
if (_CountdownText != null)
_CountdownText.text = string.Empty;
}
private void HandleCountdownInterrupted(int playerId)
{
// int playerId is not used here, but could be used to display a message, or for networking purposes
HandleCountdownEnded(playerId);
}
}using UnityEngine;
public class AntiAfkToogleExample : MonoBehaviour
{
public int playerId = 0;
public AntiAfk AntiAfk;
public void ToggleAfk()
{
AntiAfk.ToogleAfk(playerId); // You just need to access to the AntiAfk GameObject and call the ToogleAfk function using the actual player id
}
}