This page covers mobile game development — touch input, platform-specific features, optimization, monetization, and publishing for iOS and Android.
For engine setup see Godot, Unity, Unreal Engine.
For game design patterns see Game Design. For audio see Game Audio.
History
How: Mobile gaming began with Snake on Nokia phones (1997), exploded with the App Store launch (2008), and became the world’s largest gaming market by revenue by 2016.
Who: Key players — Apple (iOS/App Store), Google (Android/Play Store), Unity Technologies (dominant mobile engine), and studios like Supercell, King, Niantic, and miHoYo.
Why: 3.6 billion mobile gamers worldwide (2024). Mobile is the most accessible gaming platform — no console required, always in pocket.
Mobile Gaming Timeline
timeline
title Mobile Gaming Evolution
1997 : Snake on Nokia
: First mainstream mobile game
2003 : Java ME Games
: Basic 2D games on feature phones
2008 : App Store Launch
: iPhone changes everything
: Touch-first design begins
2010 : Angry Birds Era
: Casual gaming explosion
: Free-to-play model emerges
2013 : Clash of Clans
: Gacha and live service dominance
: Mobile surpasses console revenue
2016 : Pokemon GO
: AR gaming goes mainstream
2019 : Call of Duty Mobile
: AAA quality on mobile
: 120Hz displays arrive
2023 : Genshin Impact Era
: Console-quality mobile games
: Cross-platform standard
Introduction
Mobile game development requires a fundamentally different mindset from PC/console development.
Constraints are severe — limited CPU, GPU, RAM, battery, and storage. Touch input replaces keyboard/mouse/controller.
But the audience is massive and the distribution is global and instant.
Mobile vs PC/Console
Aspect
Mobile
PC / Console
Input
Touch, gyroscope, accelerometer
Keyboard, mouse, controller
Session length
2–10 minutes (casual)
30–120 minutes
CPU
4–8 cores, low clock speed
8–16 cores, high clock speed
GPU
Tile-based (PowerVR, Mali, Adreno)
Immediate mode (NVIDIA, AMD)
RAM
2–8 GB (shared with OS)
16–64 GB
Storage
64–512 GB (shared)
500 GB–4 TB
Battery
Critical constraint
Not a concern
Distribution
App Store / Play Store
Steam, Epic, direct
Monetization
F2P dominant
Premium dominant
Screen size
4–7 inches
24–55 inches
Mobile Game Knowledge Map
mindmap
root((Mobile Game Dev))
Input
Touch Controls
Gestures
Gyroscope
Accelerometer
Platform
iOS Specifics
Android Specifics
Cross-Platform
Optimization
CPU Performance
GPU Performance
Memory Management
Battery Life
Monetization
Free-to-Play
In-App Purchases
Ads
Battle Pass
Publishing
App Store
Google Play
Certification
ASO
Touch Controls
Touch Input Types
Input Type
Description
Game Use Case
Tap
Single finger touch and release
Jump, shoot, select
Double tap
Two taps quickly
Special action, zoom
Long press
Hold finger down
Charge attack, context menu
Swipe
Drag in a direction
Slice, scroll, dodge
Pinch
Two fingers moving together
Zoom out
Spread
Two fingers moving apart
Zoom in
Rotate
Two fingers rotating
Rotate object
Multi-touch
Multiple simultaneous touches
Virtual joystick + button
Drag
Move finger while held
Move character, drag UI
end
```
Virtual Controls Design
graph TD
subgraph Good["✅ Good Virtual Control Design"]
G1["Large touch targets\n(min 44x44 points / 88x88 px)"]
G2["Transparent overlays\ndon't block gameplay"]
G3["Customizable position\nplayer sets layout"]
G4["Visual + haptic feedback\non every press"]
G5["Dead zone on joystick\nprevents drift"]
end
subgraph Bad["❌ Bad Virtual Control Design"]
B1["Tiny buttons\nfinger misses constantly"]
B2["Opaque controls\nblock important gameplay"]
B3["Fixed layout\ndoesn't fit all hand sizes"]
B4["No feedback\nplayer unsure if pressed"]
Control Type
Min Size
Placement
Notes
Movement joystick
120×120 dp
Bottom-left
Allow repositioning
Action buttons
60×60 dp
Bottom-right
3–4 max visible
Jump button
80×80 dp
Bottom-right
Most used — make largest
Menu/pause
44×44 dp
Top corner
Small, out of the way
Touch Input in Unity
using UnityEngine;using UnityEngine.InputSystem;public class TouchInputHandler : MonoBehaviour{ void Update() { // New Input System — recommended if (Touchscreen.current == null) return; var touches = Touchscreen.current.touches; foreach (var touch in touches) { if (touch.phase.ReadValue() == UnityEngine.InputSystem.TouchPhase.Began) { Vector2 pos = touch.position.ReadValue(); HandleTap(pos); } } } // Legacy Input — still widely used void UpdateLegacy() { for (int i = 0; i < Input.touchCount; i++) { Touch touch = Input.GetTouch(i); switch (touch.phase) { case TouchPhase.Began: HandleTouchStart(touch.position, touch.fingerId); break; case TouchPhase.Moved: HandleTouchMove(touch.position, touch.deltaPosition, touch.fingerId); break; case TouchPhase.Ended: case TouchPhase.Canceled: HandleTouchEnd(touch.fingerId); break; } } } void HandleTap(Vector2 screenPos) { // Convert screen position to world position Ray ray = Camera.main.ScreenPointToRay(screenPos); if (Physics.Raycast(ray, out RaycastHit hit)) { Debug.Log("Tapped: " + hit.collider.name); } } void HandleTouchStart(Vector2 pos, int id) { } void HandleTouchMove(Vector2 pos, Vector2 delta, int id) { } void HandleTouchEnd(int id) { }}
Touch Input in Godot
extends Node2Dvar touch_positions: Dictionary = {} # finger_id → positionfunc _input(event: InputEvent) -> void: if event is InputEventScreenTouch: if event.pressed: touch_positions[event.index] = event.position _on_touch_start(event.index, event.position) else: touch_positions.erase(event.index) _on_touch_end(event.index, event.position) elif event is InputEventScreenDrag: touch_positions[event.index] = event.position _on_touch_drag(event.index, event.position, event.relative)func _on_touch_start(finger_id: int, pos: Vector2) -> void: print("Touch started: finger %d at %s" % [finger_id, pos])func _on_touch_drag(finger_id: int, pos: Vector2, delta: Vector2) -> void: # Virtual joystick logic if finger_id == 0: # left thumb var joystick_input = (pos - joystick_origin).normalized() player.velocity = joystick_input * player_speedfunc _on_touch_end(finger_id: int, pos: Vector2) -> void: if finger_id == 0: player.velocity = Vector2.ZERO
Gesture Recognition
# Simple swipe detection in Godotextends Nodevar touch_start: Vector2var touch_start_time: floatconst SWIPE_MIN_DISTANCE: float = 50.0const SWIPE_MAX_TIME: float = 0.3func _input(event: InputEvent) -> void: if event is InputEventScreenTouch: if event.pressed: touch_start = event.position touch_start_time = Time.get_ticks_msec() / 1000.0 else: var elapsed = Time.get_ticks_msec() / 1000.0 - touch_start_time var delta = event.position - touch_start if delta.length() > SWIPE_MIN_DISTANCE and elapsed < SWIPE_MAX_TIME: _on_swipe(delta.normalized())func _on_swipe(direction: Vector2) -> void: if abs(direction.x) > abs(direction.y): if direction.x > 0: print("Swipe RIGHT") else: print("Swipe LEFT") else: if direction.y > 0: print("Swipe DOWN") else: print("Swipe UP")
Gyroscope & Accelerometer
// Unity — gyroscope and accelerometervoid Start() { // Enable gyroscope Input.gyro.enabled = true;}void Update() { // Accelerometer — tilt controls Vector3 tilt = Input.acceleration; // tilt.x = left/right tilt (-1 to 1) // tilt.y = forward/back tilt // tilt.z = face up/down float steerInput = Mathf.Clamp(tilt.x * 2f, -1f, 1f); car.Steer(steerInput); // Gyroscope — rotation rate Vector3 rotationRate = Input.gyro.rotationRate; // Use for aiming, camera control in FPS}
Mobile Optimization
Mobile GPU Architecture
graph TD
subgraph Desktop["🖥️ Desktop GPU — Immediate Mode"]
D1["Draw call submitted"]
D2["Entire frame rendered immediately"]
D3["Results written to VRAM"]
D1 --> D2 --> D3
end
subgraph Mobile["📱 Mobile GPU — Tile-Based Deferred Rendering"]
M1["Draw call submitted"]
M2["Screen divided into tiles\n(16x16 or 32x32 pixels)"]
M3["Each tile rendered\ncompletely in on-chip memory"]
M4["Tile written to RAM\nonly when complete"]
M1 --> M2 --> M3 --> M4
end
Mobile -->|"Benefit"| Benefit["Lower memory bandwidth\nBetter power efficiency"]
Mobile -->|"Implication"| Impl["Avoid reading framebuffer mid-frame\nDeferred rendering is expensive"]