원클릭으로
flutter-game-expert
Flutter ile oyun geliştirme (Flame Engine vb.) ve karmaşık, büyük ölçekli mimariler kurma rehberi.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Flutter ile oyun geliştirme (Flame Engine vb.) ve karmaşık, büyük ölçekli mimariler kurma rehberi.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Bun runtime: HTTP server, file I/O, SQLite, test runner, package manager, bundler — all-in-one JS toolchain.
Clerk: Drop-in auth UI, Organizations, User management, JWT templates, webhooks, Next.js middleware integration.
Gelişmiş masaüstü, tarayıcı ve işletim sistemi kontrol yeteneği. Görsel (koordinat tabanlı) fare/klavye otomasyonu, DOM manipülasyonu, pencere yönetimi, gelişmiş dosya, ağ ve süreç yönetimini kapsar.
Drizzle ORM: Schema definition, type-safe queries, migrations, relations, Postgres/SQLite/MySQL support.
Expo Router v3: File-based navigation, layouts, tabs, modals, deep linking, API routes, typed routes.
LLM Integration: Anthropic Claude API, OpenAI, streaming, tool use, prompt caching, structured output, fallback strategies, cost optimization.
| name | flutter-game-expert |
| description | Flutter ile oyun geliştirme (Flame Engine vb.) ve karmaşık, büyük ölçekli mimariler kurma rehberi. |
Bu doküman, Flutter kullanarak sıfırdan ileri seviye, performanslı, karmaşık, ölçeklenebilir ve yönetilebilir oyun (Game Development) ve gelişmiş uygulama mimarileri oluşturmak için hazırlanmış en kapsamlı başvuru kılavuzudur.
CustomPaint ve AnimationController kullanılabilir.Component yapısını (Flame Component System) kullanarak nesneleri (Player, Enemy, Background) modüler hale getirin.bloc entegrasyonuyla tutulmalıdır.Flame.images.loadAll, FlameAudio.audioCache.loadAll). Aksi takdirde jank (takılma) oluşur.Update döngüsünde ağır hesaplamalardan kaçının. Karmaşık hesaplamaları Isolate'lere (arka plan threadleri) taşıyın.update(double dt) her karede çalışır. İçinde yeni nesneler üretmek GC'yi yorarak FPS düşürür.SpriteBatch kullanın.Büyük ölçekli bir oyun veya uygulama geliştirirken kodun sürdürülebilirliği en önemli faktördür. Oyun geliştirme genellikle "Spagetti Kod" üretmeye çok müsaittir. Bu yüzden katmanlı mimari şarttır.
lib/
├── core/
│ ├── errors/ (Hata yönetimi)
│ ├── utils/ (Yardımcı fonksiyonlar, matematik kütüphaneleri)
│ └── constants/ (Oyun içi sabitler, renkler, physics timestep vb.)
├── data/
│ ├── models/ (API'den veya local DB'den gelen veri modelleri)
│ ├── repositories/ (Veri çekme/yazma mantığı)
│ └── datasources/ (Local SQLite/Hive/Isar veya Remote API)
├── domain/
│ ├── entities/ (Saf iş/oyun mantığı objeleri)
│ ├── repositories/ (Repository interfaceleri)
│ └── usecases/ (Örn: SaveGameUseCase, LoadLevelUseCase)
├── game/
│ ├── components/ (Player, Enemy, Bullet, Environment)
│ ├── core/ (Oyunun ana FlameGame sınıfı)
│ ├── mixins/ (Collidable, HasGameRef gibi paylaşılan özellikler)
│ ├── systems/ (Spawners, AI Controllers, Level Managers)
│ └── states/ (Oyun içi durumlar, FSM - Finite State Machine)
├── presentation/
│ ├── hud/ (Can barı, skor ekranı, menüler - Flutter Widget'ları)
│ ├── screens/ (Ana menü, Ayarlar, Oyun ekranı)
│ └── bloc/ (Riverpod veya BLoC dosyaları)
Oyundaki nesnelerin state'leri (Can, Mermi Sayısı) "Domain" katmanında yaşarken, ekranda nasıl çizileceği (Render) "Game" katmanında (Flame Component) yaşar. Bir mermi hasar verdiğinde Component doğrudan veritabanına yazmaz; UseCase çağırır. UseCase, Repository üzerinden veritabanına kaydeder.
Flame'in kalbi update(double dt) ve render(Canvas canvas) fonksiyonlarıdır.
update(dt): Mantıksal hesaplamaların yapıldığı yerdir. Fiziği hesapla, pozisyonu güncelle. dt (delta time) son kareden bu yana geçen saniyedir. Her hareketi dt ile çarpmak frame-rate bağımsız (fps-independent) hareket sağlar.render(canvas): Sadece çizim yapılmalıdır. Asla mantıksal kontrol (if (isDead)) veya değişken ataması burada yapılmamalıdır.Flame, çoklu kalıtım (multiple inheritance) gibi çalışan Mixin sistemini yoğun kullanır.
HasGameRef<MyGame>: Component içinden ana oyun sınıfına (gameRef) erişmeyi sağlar. Ekran boyutunu okumak veya global oyun fonksiyonlarını çağırmak için.CollisionCallbacks: Çarpışmaları (onCollision, onCollisionStart, onCollisionEnd) dinlemek için.TapCallbacks, DragCallbacks: Dokunmatik ekran girdilerini dinlemek için.Eğer ekranda binlerce mermi veya partikül (yağmur, kar) varsa, her biri için ayrı SpriteComponent oluşturmak FPS'i 60'tan 10'a düşürebilir. Bunun yerine SpriteBatchComponent kullanmalısınız. SpriteBatch tek bir draw call (çizim çağrısı) ile binlerce sprite çizebilir.
final batch = await SpriteBatch.load('spritesheet.png');
batch.add(source: const Rect.fromLTWH(0, 0, 32, 32), offset: Vector2(100, 100));
Flutter saniyede 60 veya 120 kare çizmeye çalışır. Her kare için sadece 16ms (60fps için) veya 8ms (120fps için) vaktiniz vardır. Bu süreyi aşarsanız "Jank" (takılma) oluşur.
Dart dilinde hafızada tutulan ve referansı kaybolan nesneler çöp toplayıcı (GC) tarafından temizlenir. GC çalıştığında oyun anlık donar. Çözüm: Nesne havuzlama (Object Pooling) kullanın. Diyelim ki silah mermi atıyor. Saniyede 50 mermi yaratıp yok etmek yerine, 100 mermilik bir "Pool" oluşturun. Ekrana giren mermiyi aktif (visible = true) yapın, ekrandan çıkanı pasif (visible = false) yapın. Böylece hiç yeni obje "new"lenmez, GC çalışmaz.
Oyun sırasında arka planda büyük bir haritanın prosedürel üretilmesi (Procedural Generation) veya düşman yapay zekasının (A*) yol bulması gerekiyorsa, bunu Main Thread'de yapmayın.
Dart'ın Isolate yapısını veya compute() fonksiyonunu kullanın.
// Kötü
final path = calculatePathfinding(start, end, grid); // Ekran donar
// İyi
final path = await compute(calculatePathfinding, PathData(start, end, grid)); // Arka planda hesaplar
Flame kullanmadan sadece Flutter ile oyun yapmak isterseniz CustomPainter hayat kurtarıcıdır.
canvas.drawRect, canvas.drawCircle, canvas.drawPath kullanırken gereksiz obje yaratmaktan kaçının. Paint objelerini her seferinde render içinde oluşturmak yerine sınıf seviyesinde bir kere oluşturup tekrar kullanın (caching Paint objects).Basit AABB (Axis-Aligned Bounding Box) kutu çarpışmaları Flame ile kolaydır. Ancak yerçekimi, sürtünme (friction), esneklik (restitution), dönüş (torque) ve karmaşık şekiller arası çarpışma gerekiyorsa Forge2D kullanmalısınız (Box2D'nin Dart portu).
Forge2D dünyasında uzunluk birimi piksel değil metredir. Ekranda çizim yaparken piksel/metre dönüşümü yapılmalıdır (örn: 10 piksel = 1 metre).
Oyunun içindeki mantık ile dışındaki Flutter Widget'larını senkronize etmek zor olabilir.
Oyunun üzerine duraklatma menüsü, can barı gibi Flutter Widget'ları çizmek için GameWidget.overlays kullanın.
GameWidget(
game: myGame,
overlayBuilderMap: {
'PauseMenu': (context, game) => PauseMenuWidget(game: game),
'Hud': (context, game) => HudWidget(game: game),
},
initialActiveOverlays: const ['Hud'],
)
Flame objelerinin Riverpod veya BLoC dinlemesi mümkündür. StreamSubscription kullanarak state değişikliklerini dinleyip Component'e yansıtabilirsiniz. Ancak dinleyicileri (listener) Component ekrandan kalktığında (onRemove metodunda) iptal etmeyi (cancel) unutmayın. Aksi halde Memory Leak (Hafıza Kaçağı) yaşanır.
Flutter 3.x ve sonrasında Fragment Shaders (GLSL) desteği geldi. Gölgeler, su dalgalanmaları, parlamalar (bloom), CRT ekran efektleri gibi şeyleri CPU yerine GPU'da hesaplamak için shader kullanılır.
.frag dosyanızı yazın (GLSL dilinde).flutter.shaders olarak pubspec.yaml'a ekleyin.FragmentProgram.fromAsset ile yükleyin.Paint()..shader = shader kullanarak canvas.drawRect ile ekrana basın.Multiplayer oyunlar için HTTP Request uygun değildir. WebSocket, gRPC stream veya UDP tabanlı sistemler (örn: Nakama) kullanılmalıdır. Oyun sunucusu otoriter (Server Authoritative) olmalıdır. Client sadece tuş basımlarını sunucuya yollar (örn: Sağa basıldı). Sunucu yeni koordinatı hesaplar ve client'lara yayınlar.
Oyun verileri sürekli ve hızlı bir şekilde güncelleniyorsa SharedPreferences yetersiz kalır.
Ses dosyaları mobil cihazlarda RAM'i çok hızlı tüketebilir.
FlameAudio.audioCache.loadAll ile önceden yüklenmelidir (Preload). Aksi takdirde ses çalma anında I/O gecikmesi nedeniyle FPS drop yaşanır.Oyun yaparken Vector ve Matrix matematiği hayati önem taşır.
sqrt() yavaştır. Eğer sadece iki nesnenin birbirine olan yakınlığını kıyaslıyorsanız karesini (distanceToSquared) kullanın.atan2(y, x) fonksiyonu kullanılır.Telefonların en boy oranları (aspect ratio) birbirinden çok farklıdır (16:9, 18:9, iPad için 4:3).
FixedResolutionViewport kullanın (Kenarlara siyah bant atar).CameraComponent ile UI öğelerini kenarlara (Anchor.topLeft, Anchor.bottomRight) sabitleyin. Asla mutlak piksel değerleri (x=100, y=500) kullanmayın, her zaman ekran genişliğinin (size.x) oranına göre (size.x * 0.1) pozisyonlama yapın.Görsel zenginlik katmak için patlamalar, yağmur, kar, sihir efektleri gibi sistemlerde Particle System kullanılır.
Flame'in yerleşik ParticleSystemComponent yapısı mevcuttur. Ancak karmaşık sistemlerde binlerce partikül oluşturulacaksa objeleri tek tek oluşturmak yerine Compute Shader'lara (Custom Shaders) devredilmesi performansı büyük ölçüde artırır.
Düşman davranışları, patron savaşları (Boss Fights) veya NPC hareketleri için basit if-else blokları yerine FSM (Finite State Machine) kullanılmalıdır.
StateMachine kullanılabilir.Geniş açık dünya (Open World) veya platform (Platformer) oyunlarında seviyeleri elle kodlamak imkansızdır.
.tmx veya .json formatında dışarı aktarılan haritaları kullanın.TiledComponent olarak oyuna entegre edebilirsiniz.Flame'de 2 tip animasyon yaygındır:
Kameranın sadece karakteri merkeze alması her zaman iyi bir deneyim sunmaz.
ParallaxComponent kullanarak kolayca entegre edilebilir.Flutter ile tek kod tabanında iOS, Android, Web ve Masaüstü oyunları çıkarabilirsiniz. Ancak kontroller platforma göre değişmelidir.
gamepads) kullanılmalı ve donanım düğmeleri oyun içi aksiyonlara map edilmelidir (Key Mapping).Oyunlar çok fazla yan etkiye (side-effect) sahip oldukları için test edilmeleri zordur.
flame_test paketini kullanarak özel component'lerin yüklendiğini, başlangıç durumlarının doğruluğunu test edebilirsiniz.Eğer oyununuz rekabetçi veya skor tabloları (Leaderboards) içeriyorsa, hileye karşı önlem almanız şarttır.
DateTime.now() üzerinden değil, güvenilir bir NTP (Network Time Protocol) sunucusundan alın.Müzik veya ritim tabanlı (Rhythm Games) oyunlarda, sesin kare hızına (frame-rate) tam olarak senkronize olması gereklidir.
currentPosition) bakarak görsel nesneleri o süreye göre pozisyonlandırın. Eğer cihazda saniyelik drop (FPS düşüşü) olursa objeler doğru yere ışınlanmış olur ve senkron bozulmaz.Oyun geliştirme sürecinde sürekli olarak derleme (build) almak geliştiriciyi çok yorar.
GameWidget kendi içinde oldukça karmaşık bir lifecycle'a sahiptir. Etrafındaki widget ağacı sürekli rebuild oluyorsa GameWidget sıfırlanabilir. Etrafına RepaintBoundary ekleyin.setState ile saniyede 60 kere ekranı yenilemeye (TickerProviderStateMixin ile) çalışmak, saf Flame (Canvas) kadar optimize değildir. UI kısımları reaktif olmalı ama render döngüsünü meşgul etmemelidir.sprite = await loadSprite('img.png'); yaparken objeyi onLoad içinde hazırlayın. Obje henüz hazır olmadan render metodunda çizmeye çalışmak NullPointerException veya kırmızı ekran hatası fırlatır.size: Vector2(32, 32) yaparak küçültmek belleği felç eder. Orijinal dosyayı ihtiyacınız olan boyuta yakın tutun.flutter run --profile veya --release ile yapın.position.x += 5; yazmak yanlıştır. Eğer ekran 120Hz ise karakter 60Hz olana göre iki kat hızlı gider. Doğrusu: position.x += speed * dt; olmalıdır.if (enemyType == "Goblin") şeklinde kontrol etmek hataya açıktır. Her zaman enum veya sınıf hiyerarşisi kullanın.Bu belge, bir Flutter projesinde oyun veya ultra karmaşık bir grafiksel sistem geliştirileceği zaman birincil başvuru kaynağıdır. İhtiyaç duyduğunuz anda, bu best-practice'lere bağlı kalarak kodu şekillendiriniz.