köstebeği yakala

Köstebeği Yakala! Oyunu

Mobil Uygulama Geliştirme Ders Notları

# Proje Tanımı

Köstebeği Yakala, ekranda beliren köstebeklere dokunarak puan toplanılan bir oyun.

- Oyunu Android Studio'da Java dili ile geliştireceğiz.
- Oyunun tasarımı ve animasyonları için XML dosyaları kullanılacak.
- Kullanıcı adı ve en yüksek puan Firebase Realtime Database'e kaydedilecek.
- Oyunun sesleri ve titreşim efekti kullanılacak.
- Oyunun başlığı ve butonları animasyonlarla hareketli olacak.
- Oyunun süresi ve puanı gösterilecek.

Oyun 3 ekrandan oluşacak:

LoginActivity:

Oyunun başlık görselinin, oyunun karakteri olan köstebek görselinin ve kullanıcı adı girilen alanının, Kaydet/Başla ve ses açma kapama butonunun bulunduğu ekran.

Ekranın en üstünde oyunun başlığı olacak.

Başlığın altında köstebek olacak ve bu köstebeğe hareketli olacak.
Köstebeğin hareketi için animasyon kullanılacak.
Animasyon her tekrarlandığında bir ses çalacak.
Köstebek tıklandığında vurulma sesi çalacak ve titreşim olacak.
Vurulan köstebeğin resmi değişecek.

Uygulama ilk kez açıldığında kullanıcı adı girilecek ve bu kullanıcı adı Firebase Realtime Database'e daha önce kaydedilmişse uyarı verilecek. Girilen kullanıcı hem SharedPreferences'a hem de Firebase Realtime Database'e kaydedilecek.

Uygulama tekrar açıldığında kullanıcı adı SharedPreferences'tan hatırlanacak ve tekrar düzenlenemeyecek.

Uygulama ilk kez açıldıysa Kaydet butonu, daha önce kaydedilmişse Başla butonu gösterilecek.
Başla butonuna tıklandığında GameActivity'e geçilecek.

Kullanıcının varsa daha önce kaydettiği En yüksek puan da SharedPreferences'ta saklanacak gösterilecek.
Eğer uygulama ilk kez açılıyorsa en yüksek puan olmadığı için gösterilmeyecek.

Ses açma kapama butonu ile oyunun sesi açılıp kapatılacak ve bu durum SharedPreferences ile saklanacak.
Ses durumununa göre butonun resmi değişecek.
Uygulama tekrar açıldığında ses durumu hatırlanacak.

Butonlara tıklandığında küçülüp büyüyecek.

GameActivity:

Ekranın üst kısmında kalan süre gösterilecek.
Süre son 3 saniyede kırmızıya dönecek.
Süre sona erdiğinde oyun sona erecek ve ResultActivity'e geçilecek.

Ortada oyun alanı olacak ve bu alanda köstebekler belirecek. Aynı Login ekranındaki gibi:
Köstebeğin hareketi için animasyon kullanılacak.
Animasyon her tekrarlandığında bir ses çalacak.
Köstebek tıklandığında vurulma sesi çalacak ve titreşim olacak.
Vurulan köstebeğin resmi değişecek.
Puan artacak.

Bazen rastgele köstebek yerine bomba belirecek.
Bomba tıklandığında patlama sesi çalacak ve titreşim olacak.
Puan azalacak.

Ekranın alt tarafında puan gösterilecek.
Puan, köstebek vurulduğunda artacak.
Her puan arttığında Puan bölümü animasyon ile büyüyüp küçülecek.

ResultActivity:

Ekranın üst kısmında kullanıcının adı ve puanı gösterilecek.
Eğer kullanıcı daha önce oynamışsa ve kaydedilen en yüksek puanı geçmişse tebrik mesajı gösterilecek.

En yüksek 3 puan gösterilecek.
Bu puanlar Firebase Realtime Database'den alınacak.

Ekranın alt tarafında Tekrar Oyna butonu olacak.
Bu butona tıklanınca da büyüyüp küçülecek.

# İşlem Adımları


# Projeyi Oluşturma

new-project

API 33 seviyesinde, Java dilinde ve Groovy olarak projeyi oluşturun.
Package name ini düzenlemeyi unutmayın.


# Assets dosyalarını projeye ekleme

assets

Oyun için hazırladığımız assets dosyalarını buraya tıklayarak indirelim.


Şimdi assets klasöründekileri projeye dahil edelim.

- Resim dosyaları: drawable

Diğer ögeler için res klasörüne sağ tık New > Android Resource Directory seçin.

- Ses dosyaları için: Resource type: raw

- Font dosyaları için: Resource type: font


# Uygulama İkonu:

app-icon

Asset Studio ile İkon Oluşturalım:

app-icon

Bu işlem sonucunda res/mipmap klasöründe icon dosyaları oluşur.


# View binding i etkinleştirme

build.gradle (modül: app):
              
          
android {
  ...
  viewBinding {
      enabled = true
  }
}
            
            
          

# Activity’leri Oluşturma

Varsayılan olarak yüklenen Main ekranını silin.

İlk olarak LoginActivity i oluşturun.
Bu ekran uygulamanın ilk açılışında gelecek ekran olacağı için Launcher Activity yi işaretleyin.

Sonra sırayla GameActivity ve ResultActivity ekranlarını oluşturun.

Activity lerde varsayılan olarak gelen Constraintlayout a arkaplan ekleyelim:

              
android:background="@drawable/arkaplan"        

            
          

Durum ve Navigasyon çubuğu arka rengini değiştirme:

              
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  Window window = getWindow();
  window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
  // Durum çubuğu rengi
  window.setStatusBarColor(Color.parseColor("#885218"));
  // Alt navigasyon çubuğu rengi
  window.setNavigationBarColor(Color.parseColor("#885218"));
}
            
          

# Firebase Realtime Database entegrasyonu

1- Firebase Projesini Oluştur

Firebase konsolunu açın.

Yeni bir proje oluşturun (Create a project): KostebegiYakala

Proje oluşturulduktan sonra, Android uygulamanı eklemek için:
Firebase konsolunda "Add app" seçeneğini seçin ve Android simgesine tıklayın.

Paket adını girin: com.caglarkarabulut.kostebegiyakala

google-services.json dosyasını indirin ve projenin app klasörüne yapıştırın.


2- Bağımlılıkları Ekleyelim:

build.gradle (Project):

            
plugins {
alias(libs.plugins.android.application) apply false
    id 'com.google.gms.google-services' version '4.4.2' apply false
}      
              
          
          

build.gradle (App):

            
plugins {
  // Google Servisleri
  id 'com.google.gms.google-services'
  
  alias(libs.plugins.android.application)
}     

// diğer kodlar

dependencies {

// diğer kodlar

  // Firebase projeye dahil et
  implementation platform('com.google.firebase:firebase-bom:33.11.0')
  // Firebase bağımlılıkları
  implementation 'com.google.firebase:firebase-analytics'
  implementation 'com.google.firebase:firebase-database' 
}
              
          
          

3- AndroidManifest.xml’e İnternet Erişim İzni Ekleyelim:

AndroidManifest.xml:


<uses-permission android:name="android.permission.INTERNET" />              

                        
          

Değişiklikleri yaptıktan sonra projeyi senkronize et: Sync Project with Gradle Files


# Login Ekranı

# activity_login.xml tasarımı

activity_login.xml i tasarlamalaya başlayalım:

activity_login

Ekranda Neler olacak:

- Oyun adı görseli (ImageView)
- Köstebek ile ilgili görseller (ImageView)
- Kullanıcı adı girilecek alan (EditText)
- En Yüksek Puan gösterilecek alan (TextView)
- Başla butonu (bir Button ile)
- Oyun sesini açan buton (ImageButton)

Bileşenlerin hepsi alt alta olduğu için LinearLayout (vertical) ile bu 6 bileşeni düzenleyebiliriz.

Köstebeğin yuvanın içine girip çıkıyormuş gibi görünmesi için 4 farklı resmi üst üste koyacağız. Bu resimlerin hepsi aynı boyutta olacak. Bu resimlerin hepsini bir RelativeLayout içine koyacağız.
Köstebeği yuva içinde yukarı aşağı hareket ettirerek köstebeğin yuvanın içinde olduğu hissini vereceğiz.

LinearLayout ekleyerek başlayalım:
İçine sırasıyla:

- Oyun adı görseli için ImageView,

- Köstebek için RelativeLayout,

- Köstebek için 4 ImageView,

- Kullanıcı adı için EditText,

- En Yüksek Puan için TextView,

- Başla butonu için ImageButton,

- Oyun sesini açan buton için ImageButton ekleyelim.


LinearLayout Özellikleri:

Genişlik: wrap_content,
Yükseklik: wrap_content,
Yazı hizalaması: center,
Düzen yönü: vertical.
   
android:layout_width="wrap_content"  
android:layout_height="wrap_content"  
android:gravity="center"  
android:orientation="vertical"

                      
          

RelativeLayout Özellikleri (kostebekAlani):

ID: kostebekAlani,
Genişlik: 150dp,
Yükseklik: 150dp,
Parent'e ortalama: true.
  
android:id="@+id/kostebekAlani"  
android:layout_width="150dp"  
android:layout_height="150dp"  
android:layout_centerInParent="true"  

                      
          

RelativeLayout İçindeki ImageView Bileşenleri:

Sırasıyla 4 ImageView bileşeni ekleyelim:

- İlk ImageView de köstebek yuvasının arka tarafı olacak.
- İkinci ImageView bileşeni köstebek olacak. Bu ImageView e id verelim: ivKostebek
- Üçüncü ImageView bileşeni toprak olacak.
- Dördüncü ImageView bileşeni köstebek yuvasının ön yüzü olacak.

Buradaki ivKostebek i aşağı yukarı yönlü hareket ettirerek köstebek yuva içinden çıkıp girecekmiş gibi bir etki vereceğiz.

EditText (Kullanıcı Adı Girin):

layout_width (Genişlik): wrap_content,
layout_height (Yükseklik): 120dp,
layout_marginLeft (Sol margin): 10dp,
layout_marginTop (Üst margin): -30dp,
layout_marginRight (Sağ margin): 10dp,
background (Arka plan): @drawable/et_kullanici_adi,
fontFamily (Yazı tipi): sour_gummy,
gravity (Yazı hizalaması): center,
hint (Yer tutucu yazı): Kullanıcı Adınızı Girin...,
inputType (Giriş tipi): text,
maxLength (Maksimum uzunluk): 15,
shadowColor (Gölge rengi): @color/black,
shadowDx (Gölge X ofseti): 5,
shadowDy (Gölge Y ofseti): 5,
shadowRadius (Gölge yarıçapı): 20,
textColor (Yazı rengi): @color/white,
textColorHint (Yazı rengi (hint)): @android:color/white,
textSize (Yazı boyutu): 26dp,
layout_constraintEnd_toEndOf (Sağ kenara hizalama): parent,
layout_constraintStart_toStartOf (Sol kenara hizalama): parent,
Görünürlük: visible.
 
android:id="@+id/etKullaniciAdi"
android:layout_width="wrap_content"
android:layout_height="120dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="-30dp"
android:layout_marginRight="10dp"
android:background="@drawable/et_kullanici_adi"
android:fontFamily="@font/sour_gummy"
android:gravity="center"
android:hint="Kullanıcı Adınızı Girin..."
android:inputType="text"
android:maxLength="15"
android:shadowColor="@color/black"
android:shadowDx="5"
android:shadowDy="5"
android:shadowRadius="20"
android:textAlignment="center"
android:textColor="@color/white"
android:textColorHint="@android:color/white"
android:textSize="26dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"

                        
          

TextView (En Yüksek Puanın):

layout_width (Genişlik): wrap_content,
layout_height (Yükseklik): wrap_content,
layout_marginTop (Üst margin): 10dp,
fontFamily (Yazı tipi): sour_gummy,
shadowColor (Gölge rengi): @color/black,
shadowDx (Gölge X ofseti): 7,
shadowDy (Gölge Y ofseti): 7,
shadowRadius (Gölge yarıçapı): 7,
text (Yazı): En Yüksek Puanın: 5,
textAlignment (Yazı hizalaması): center,
textColor (Yazı rengi): @color/white,
textSize (Yazı boyutu): 30dp,
layout_constraintEnd_toEndOf (Sağ kenara hizalama): parent,
layout_constraintStart_toStartOf (Sol kenara hizalama): parent,
layout_constraintTop_toTopOf (Üst kenara hizalama): parent,
Görünürlük: visible.
 
android:id="@+id/tvEnYuksekPuan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:fontFamily="@font/sour_gummy"
android:shadowColor="@color/black"
android:shadowDx="7"
android:shadowDy="7"
android:shadowRadius="7"
android:text="En Yüksek Puanın: 5"
android:textAlignment="center"
android:textColor="@color/white"
android:textSize="30dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"

                        
          

ImageButton (Kaydet ve Başla):

layout_width (Genişlik): wrap_content,
layout_height (Yükseklik): 130dp,
layout_marginTop (Üst margin): 10dp,
background (Arka plan): @android:color/transparent,
scaleType (Ölçek türü): fitCenter,
srcCompat (Resim kaynağı): @drawable/btn_kaydet,
layout_constraintEnd_toEndOf (Sağ kenara hizalama): parent,
layout_constraintStart_toStartOf (Sol kenara hizalama): parent,
Görünürlük: visible.
 
android:id="@+id/btnKaydetBasla"
android:layout_width="wrap_content"
android:layout_height="130dp"
android:layout_marginTop="10dp"
android:background="@android:color/transparent"
android:scaleType="fitCenter"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@drawable/btn_kaydet"

                        
          

ImageButton (Ses Aç/Kapat):

layout_width (Genişlik): wrap_content,
layout_height (Yükseklik): 100dp,
layout_marginBottom (Alt margin): 50dp,
background (Arka plan): @android:color/transparent,
scaleType (Ölçek türü): fitCenter,
srcCompat (Resim kaynağı): @drawable/btn_ses_acik,
layout_constraintEnd_toEndOf (Sağ kenara hizalama): parent,
layout_constraintStart_toStartOf (Sol kenara hizalama): parent,
Görünürlük: visible.
 
android:id="@+id/btnSesAcKapat"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:layout_marginBottom="50dp"
android:background="@android:color/transparent"
android:scaleType="fitCenter"
app:srcCompat="@drawable/btn_ses_acik"

                        
          

Buton Animasyonları:

Butonlara tıkladığında küçülüp büyüme efekti eklemek için animasyon tanımlayalım:

res klasörüne sağ tıklayın.
New > Android Resource Directory seçin. "Resource type" olarak anim seçin ve "OK" deyin.

res/anim klasörüne sağ tıklayın ve New > Animation Resource File seçin.
Dosya adı: button_tiklama_animation.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Küçülme animasyonu (1.0'dan 0.9'a) -->
    <scale
        android:fromXScale="1.0"
        android:toXScale="0.9"
        android:fromYScale="1.0"
        android:toYScale="0.9"
        android:pivotX="50%"
        android:pivotY="50%"
        android:duration="100" />

    <!-- Büyüme animasyonu (0.9'dan 1.0'a) -->
    <scale
        android:startOffset="100"
        android:fromXScale="0.9"
        android:toXScale="1.0"
        android:fromYScale="0.9"
        android:toYScale="1.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:duration="100" />
</set>          

                        
          

# LoginActivity.java

Şimdi LoginActivity.java da kod yazmaya başlayalım:
Önce binding tanımalamasını yapalım:

  
private ActivityLoginBinding binding;

                  
          

View Binding ile layout'u inflate edelim:

  
// View Binding ile layout'u inflate etme
binding = ActivityLoginBinding.inflate(getLayoutInflater()); 
setContentView(binding.getRoot());

# Button Tıklama Animasyonu

Butonlara tıkladığında küçülüp büyüme efekti ekleyelim:

onCreate() metodu içinde animasyonu tanımlayalım:

  
// Animasyonu yükle  
Animation buttonTiklamaAnimation = AnimationUtils.loadAnimation(this,
R.anim.button_tiklama_animation);

          

btnKaydet için Listener tanımlayıp animasyonu başlatacak kodu ekleyelim:

           
// Button Animasyonunu başlat  
v.startAnimation(buttonTiklamaAnimation);

                        
          

Çalıştırıp test edelim.
Kaydet butonuna tıklayınca küçülüp büyüyecek.

Aynı şekilde btnSesAcKapat için de Listener tanımlayıp animasyonu başlatacak kodu ekleyelim:

# Köstebek Animasyonu

Köstebek görselini aşağı yukarı hareket ettirerek köstebek yuva içinde çıkıp giriyormuş gibi bir etki vereceğiz.

- vurulduMu: köstebeğin vurulup vurulmadığını kontrol eder.
- handler: köstebeğin düzenli aralıklarla çıkmasını sağlar.
- animUp ve animDown: köstebeğin yukarı çıkma ve aşağı inme animasyonlarını oluşturur.

           
private boolean vurulduMu = false;

private final Handler handler = new Handler(Looper.getMainLooper());

private final TranslateAnimation animUp = createTranslateAnimation(150, 0, 300);  // Yukarı çıkma animasyonu
private final TranslateAnimation animDown = createTranslateAnimation(0, 150, 300);  // Aşağı inme animasyonu

                        
          

Köstebeğin ekranda belirli aralıklarla yukarı çıkmasını ve aşağı inmesini sağlayan metot:

           
private void startKostebek() {
  handler.postDelayed(new Runnable() {
      @Override
      public void run() {
          // Köstebek vurulmadıysa ve ekranda görünmüyorsa, görünür hale gel ve yukarı hareket et
          if (!vurulduMu && binding.ivKostebek.getVisibility() != View.VISIBLE) {
              binding.ivKostebek.setVisibility(View.VISIBLE);  // Göster
              binding.ivKostebek.startAnimation(animUp);  // Yukarı hareket et

              // 1000 ms sonra köstebek hala vurulmadıysa aşağı kay ve görünmez ol
              binding.ivKostebek.postDelayed(() -> {
                  if (binding.ivKostebek.getVisibility() == View.VISIBLE && !vurulduMu) {
                      binding.ivKostebek.startAnimation(animDown);  // Aşağı kayma animasyonu
                      binding.ivKostebek.setVisibility(View.INVISIBLE);  // Görünmez ol
                  }
              }, 1000);  // 1 saniye sonra çalış
          }
          // Aynı işlemleri 1500 ms sonra tekrarlamak için kendini yeniden çağır
          handler.postDelayed(this, 1500);
      }
  }, 1500);  // İlk başlangıçta da 1.5 saniye gecikme ile başla
}

                        
          

Hareket animasyonlarını (yukarı ve aşağı) yaratan metot:


private TranslateAnimation createTranslateAnimation(float fromY, float toY, int duration) {
    TranslateAnimation animation = new TranslateAnimation(0, 0, fromY, toY);
    animation.setDuration(duration);
    return animation;
}

                        
          

Şimdi onCreate() metodu içinde köstebek animasyonunu başlatalım:


// Köstebek animasyonunu başlat
startKostebek();

                        
          

# Köstebek Vurulduğunda Resminin Değişmesi

Köstebek görseli tıklandığında köstebek vurulmuş gibi göstermek için ivKostebek için bir Listener tanımlayıp köstebek vuruldu mu kontrolünü yapalım:

Eğer köstebek görünüyorsa (View.VISIBLE) ve vurulmadıysa (!vurulduMu):
- vurulduMu true olacak (vuruldu).
- Köstebek resmi kostebek_vurulan ile değişecek.
- Aşağı kayma animasyonu (animDown) başlayacak.
- 300ms sonra köstebek kaybolur, resmi eski haline dönecek (kostebek) ve tekrar vurulabilir olacak (vurulduMu = false).


// Köstebek ekranda görünüyorsa ve henüz vurulmadıysa:
if (binding.ivKostebek.getVisibility() == View.VISIBLE && !vurulduMu) {
    vurulduMu = true;  // Köstebeğin vurulduğunu işaretleriz.

    // Köstebek resmi vurulmuş resmi ile değiştir.
    binding.ivKostebek.setImageResource(R.drawable.kostebek_vurulan);

    // Aşağı kayma animasyonu başlat(vurulduktan sonra aşağı kay).
    binding.ivKostebek.startAnimation(animDown);

    // 300 ms sonra köstebek tekrar kaybolsun ve eski haline dönsün.
    binding.ivKostebek.postDelayed(() -> {
        binding.ivKostebek.setVisibility(View.INVISIBLE);  // Köstebek görünmez yapılır.
        binding.ivKostebek.setImageResource(R.drawable.kostebek);  // Resim eski haline döner.
        vurulduMu = false;  // Tekrar vurulabilir hale gelir.
    }, 300);  // 300 milisaniyelik gecikme
}

                        
          

# Ses açma kapama butonu

Oyunda sesi açıp kapatabilmek için bir ses açma kapama butonu ekleyelim:

Ses açık mı kapalı mı olduğunu uygulamanın diğer açılışlarında hatırlamak için cihaz hafızasına kaydetmeliyiz.
Cihaz hafızasına kaydetmek için SharedPreferences kullanacağız. ses açma kapama butonu için bir değişken tanımlayalım:

 
private SharedPreferences prefs;
private boolean sesAcikMi;   

                      
          

onCreate() metodu içinde SharedPreferences başlatalım ve ses açık mı kapalı mı olduğununa göre ses açma kapama butonunun resmini değiştirelim:

  
// SharedPreferences ve ses ayarlarını başlat
prefs = getSharedPreferences("GamePrefs", MODE_PRIVATE);
sesAcikMi = prefs.getBoolean("isSoundOn", true); // Varsayılan olarak ses açık
sesButonuResmiGuncelle(); // Butonun görselini güncelle

                      
          

Uygulama açıldığında ses açık mı kapalı mı durumuna göre ses açma kapama butonunun resmini değiştiren bir metot yazalım:

             
private void sesButonuResmiGuncelle() {
  if (sesAcikMi) {
      binding.btnSesAcKapat.setImageResource(R.drawable.btn_ses_acik);
  } else {
      binding.btnSesAcKapat.setImageResource(R.drawable.btn_ses_kapali);
  }
}

          
          

# Köstebekin Çıktığında ve Vurulduğunda Ses Çalma

             
private SoundPool soundPool; // Sesleri çalmak için SoundPool
private int cikmaSesiId; // Çıkma sesinin ID'si
private int vurulmaSesiId; // Vurulma sesinin ID'si

            
          

Titreşim Efekti

Önce titreşim izni almalıyız. AndroidManifest.xml dosyasına izin ekleyelim:

             

            
          

Köstebek vurulduğunda titreşim efekti ekleyelim:

             

            
          

# Kullanıcı Adını Kaydetme

...

             
private DatabaseReference database;

            
          

onCreate() metodu içinde Firebase veritabanını başlatalım ve kullanıcı adını kontrol edelim:

             
// Firebase Realtime Database'i başlat
database = FirebaseDatabase.getInstance().getReference("users");

// Kullanıcı adını kontrol et
String savedUsername = prefs.getString("username", null);
if (savedUsername != null) {
    // Kullanıcı adı daha önce kaydedilmişse
    binding.etKullaniciAdi.setText(savedUsername);
    binding.etKullaniciAdi.setEnabled(false); // Düzenlenemez
    binding.btnKaydetBasla.setImageResource(R.drawable.btn_basla); // Başla butonu göster
} else {
    // İlk açılış, Kaydet butonu göster
    binding.btnKaydetBasla.setImageResource(R.drawable.btn_kaydet);
}
            
          

Kullanıcı adını kaydeden metodu yazalım. Kullanıcı adı daha önce kaydedilmişse Firebase veritabanında kontrol edelim:

             
// Kullanıcı adını Firebase ve SharedPreferences'a kaydetme
private void saveUsername() {
    String username = binding.etKullaniciAdi.getText().toString().trim();
    if (username.isEmpty()) {
        Toast.makeText(this, "Kullanıcı adı boş olamaz!", Toast.LENGTH_SHORT).show();
        return;
    }

    // Firebase'de kullanıcı adını kontrol et
    database.child(username).addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot snapshot) {
            if (snapshot.exists()) {
                // Kullanıcı adı zaten var
                Toast.makeText(LoginActivity.this, "Bu kullanıcı adı zaten alınmış!", Toast.LENGTH_SHORT).show();
            } else {
                // Kullanıcı adını kaydet
                prefs.edit().putString("username", username).apply(); // SharedPreferences'a kaydet
                database.child(username).child("highScore").setValue(0); // Firebase'e kaydet (varsayılan puan 0)
                binding.etKullaniciAdi.setEnabled(false); // Düzenlemeyi kapat
                binding.btnKaydetBasla.setImageResource(R.drawable.btn_basla); // Başla butonuna geç
                Toast.makeText(LoginActivity.this, "Kullanıcı adı kaydedildi!", Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        public void onCancelled(DatabaseError error) {
            Toast.makeText(LoginActivity.this, "Hata: " + error.getMessage(), Toast.LENGTH_SHORT).show();
        }
    });
}
            
          






ve son olarak da onDestroy() metodu tanımlayıp SoundPool ve binding kaynaklı bellek sızıntılarını önleyelim:

 
@Override
protected void onDestroy() {
    super.onDestroy();
    // SoundPool kaynaklarını temizle
    if (soundPool != null) {
        soundPool.release();
        soundPool = null;
    }
    binding = null; // Bellek sızıntısını önlemek için
}
          
                      
          

# Oyun Ekranı

# activity_game.xml tasarımı

activity_login

Oyun ekranı tasarımı için RelativeLayout kullanacağız.

Ekranın üst kısmında "Kalan Süre" bilgisini gösteren bir TextView olacak.

Ortada oyun alanı için bir GridLayout olacak.

GridLayout içinde 3x5 grid olacak ve her bir grid alanında bir köstebek görseli olacak.

Köstebek ve yuva görsellerini bir arada tutmak için bir RelativeLayout kullanacağız. (Login Ekranında olduğu gibi)

Toplamda 15 adet köstebek yuvası olması için 15 adet RelativeLayout ekleyeceğiz.

Böylece ekranda 15 köstebek yuvası görseli olacak.

Ekranın alt kısmında ise "Puan" bilgisini gösteren bir TextView olacak.

RelativeLayout:


layout_width (Genişlik): match_parent,
layout_height (Yükseklik): match_parent.
 
android:layout_width="match_parent" 
android:layout_height="match_parent" 

                        
          

TextView (Kalan Süre: 10):


id (Tanımlayıcı): tvSure,
layout_width (Genişlik): wrap_content,
layout_height (Yükseklik): wrap_content,
layout_centerHorizontal (Yatayda Ortala): true,
layout_marginTop (Üst Margin): 20dp,
fontFamily (Yazı Tipi): sour_gummy,
gravity (Hizalama): center,
shadowColor (Gölge Rengi): @color/black,
shadowDx (Gölge X Ofseti): 5,
shadowDy (Gölge Y Ofseti): 5,
shadowRadius (Gölge Yarıçapı): 20,
text (Yazı): "Kalan Süre: 10",
textColor (Yazı Rengi): @color/white,
textSize (Yazı Boyutu): 35dp.
 
android:id="@+id/tvSure"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:fontFamily="@font/sour_gummy"
android:gravity="center"
android:shadowColor="@color/black"
android:shadowDx="5"
android:shadowDy="5"
android:shadowRadius="20"
android:text="Kalan Süre: \n10 "
android:textColor="@color/white"
android:textSize="35dp" 

                      
          

GridLayout (Oyun Alanı 3x5):


id (Tanımlayıcı): oyunAlani,
layout_width (Genişlik): match_parent,
layout_height (Yükseklik): wrap_content,
layout_above (Üst Kenar): @id/tvPuan,
layout_below (Alt Kenar): @id/tvSure,
layout_margin (Margin): 16dp,
columnCount (Kolon Sayısı): 3,
rowCount (Satır Sayısı): 3.
 
android:id="@+id/oyunAlani"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/tvPuan"
android:layout_below="@id/tvSure"
android:layout_margin="16dp"
android:columnCount="3"
android:rowCount="5"

                      
          

Köstebek ve yuva görsellerini bir arada tutmak için RelativeLayout kullanalım.
Bir tanesini yapıp kopyalayarak diğerlerini oluşturalım.

RelativeLayout (1. Köstebek Alanı):

layout_width (Genişlik): 110dp,
layout_height (Yükseklik): 110dp,
layout_margin (Margin): 6dp.
 
android:layout_width="110dp" 
android:layout_height="110dp" 
android:layout_margin="6dp" 

                      
          

İçindeki ImageView (Köstebek Görseli) için sırasıyla şu tanımlamaları yapalım:

İlk ImageView (Yuvanın Arka Tarafı):

layout_width (Genişlik): match_parent,
layout_height (Yükseklik): match_parent,
src (Kaynak): @drawable/yuva_arka.
 
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/yuva_arka" 

                      
          

İkinci ImageView (Köstebek 1):

android:id (ID): @+id/kostebek1,
layout_width (Genişlik): match_parent,
layout_height (Yükseklik): match_parent,
src (Kaynak): @drawable/kostebek,
visibility (Görünürlük): invisible (görünmez).
 
android:id="@+id/kostebek1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/kostebek"
android:visibility="invisible" 

                      
          

Üçüncü ImageView (Toprak):

layout_width (Genişlik): match_parent,
layout_height (Yükseklik): match_parent,
src (Kaynak): @drawable/toprak.
 
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/toprak" 

                      
          

Dördüncü ImageView (Yuvanın Ön Tarafı):

layout_width (Genişlik): match_parent,
layout_height (Yükseklik): match_parent,
src (Kaynak): @drawable/yuva_on.
    
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/yuva_on" 

                      
          

Oluşturduğumuz RelativeLayout i kopyalayarak diğer köstebek alanlarını oluşturalım.

Puan TextView:

Sırasıyla;
layout_width (Genişlik): wrap_content,
layout_height (Yükseklik): wrap_content,
layout_alignParentBottom (Alt kenara hizalama): true,
layout_centerHorizontal (Yatayda ortalama): true,
layout_marginBottom (Alt margin): 20dp,
fontFamily (Yazı tipi): @font/sour_gummy,
gravity (Yazı hizalaması): center,
shadowColor (Gölge rengi): @color/black,
shadowDx (Gölge X ofseti): 5,
shadowDy (Gölge Y ofseti): 5,
shadowRadius (Gölge yarıçapı): 20,
text (Metin): Puan: 0 ,
textColor (Yazı rengi): @color/white,
textSize (Yazı boyutu): 45dp.
 
android:id="@+id/tvPuan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="20dp"
android:fontFamily="@font/sour_gummy"
android:gravity="center"
android:shadowColor="@color/black"
android:shadowDx="5"
android:shadowDy="5"
android:shadowRadius="20"
android:text="Puan: 0 "
android:textColor="@color/white"
android:textSize="45dp"

                        
          





GameActivity.java

Kodlama tarafına başlayalım.

Binding, kullanıcı adı ve puan için sırasıyla şu tanımlamaları yapalım:

- binding (View Binding),
- SharedPreferences (Kullanıcı adını kaydetmek için),
- PREF_NAME (SharedPreferences adı),
- KEY_USERNAME (Kullanıcı adını kaydetmek için),
- KEY_HIGH_SCORE (En Yüksek Puanı kaydetmek için),
- puan.

 
private ActivityGameBinding binding;
 
private SharedPreferences preferences;
private static final String PREF_NAME = "KostebegiYakalaPrefs";
private static final String KEY_HIGH_SCORE = "enYuksekPuan";
private int puan = 0;

                    
          

View Binding ile layout'u inflate edelim:

  
// View Binding ile layout'u inflate etme
binding = ActivityGameBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
        
                    
          

onCreate() Metodunda SharedPreferences başlatalım:

  
// SharedPreferences'ı başlatma
preferences = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
        
                    
          

Geri sayım için timer ve toplamSure yi tanımlayalım:

  
private CountDownTimer gameTimer;
private static final long toplamSure = 10000; // 10 saniye (milisaniye cinsinden)

                      
          

onCreate() metodu içinde zamanlayıcıyı başlatma metodu çağıralım:

1
  
// Zamanlayıcıyı başlatma
startZamanlayici();

                      
          

startZamanlayici() metodu:

Zamanlayıcıyı başlatmak için CountDownTimer sınıfını kullanacağız.
onTick metodu her saniye çalışacak ve süreyi güncelleyecek.
onFinish metodu süre bittiğinde çalışacak ve oyunu bitirecek.
endGame() metodu oyunu bitirecek.

 
private void startZamanlayici() {
  gameTimer = new CountDownTimer(toplamSure, 1000) {
      @Override
      public void onTick(long millisUntilFinished) {
          // Her saniye güncellenecek
          long secondsRemaining = millisUntilFinished / 1000;
          binding.tvSure.setText("Kalan Süre: " + secondsRemaining + " Saniye");
      }

      @Override
      public void onFinish() {
          // Süre bittiğinde
          binding.tvSure.setText("Süre Bitti!");
          // oyun sonu
      }
  }.start();
}
                      
          

Köstebeğin rastgele konumlarda belirmesini sağlayalım:

Sırasıyla şu tanımlamaları yapalım:
- Handler (Köstebek hareketlerini kontrol etmek için),
- Random (Köstebek rastgele konumda belirsin),
- kostebekHizi (Köstebek her 0.75 saniyede bir yer değiştirsin)

Random sınıfını java.util paketiyle import edelim.
Handler sınıfını android.os paketiyle import edelim.

 
private Handler handler;
private Random random;
private static final long kostebekHizi = 750; // Köstebek her 0.75 saniyede bir yer değiştirir

                      
          

onCreate() metodunda köstebek hareketini başlatan metodu çağıralım:

 
// Köstebek hareketini başlatma
kostebegiHareketlendir();

                      
          

Köstebeği her 0.75 saniyede bir rastgele bir konuma taşıyalım ve görünür hale getirelim; bu işlemi oyun devam ettiği sürece tekrarlayalım.

kostebegiHareketlendir() metodu:

 
private void kostebegiHareketlendir() {
  handler.postDelayed(new Runnable() {
      @Override
      public void run() {
          if (gameTimer != null) { // Oyun devam ediyorsa
              kostebegiRastgeleBaskaYerdeCikar();
              binding.btnKostebek.setVisibility(View.VISIBLE);
              handler.postDelayed(this, kostebekHizi); // Tekrar çalıştır
          }
      }
  }, kostebekHizi);
}

                      
          

GameActivity'de oyunAlani içinde köstebeğin rastgele konumlarda belirmesini sağlayalım. Bunun için köstebeğin (btnKostebek) pozisyonunu rastgele değiştirecek bir mekanizma ve belirli aralıklarla görünür/gizli olmasını sağlayacak bir zamanlayıcı ekleyelim:

kostebegiRastgeleBaskaYerdeCikar() metodu:

 
private void kostebegiRastgeleBaskaYerdeCikar() {
  // oyunAlani'nın boyutlarını al
  int areaWidth = binding.oyunAlani.getWidth();
  int areaHeight = binding.oyunAlani.getHeight();

  // Köstebeğin boyutlarını al
  int moleWidth = binding.btnKostebek.getWidth();
  int moleHeight = binding.btnKostebek.getHeight();

  // Rastgele koordinatlar hesapla (köstebek alanın dışına çıkmasın)
  int maxX = areaWidth - moleWidth;
  int maxY = areaHeight - moleHeight;

  if (maxX > 0 && maxY > 0) { // Boyutlar hesaplanana kadar bekle
      int newX = random.nextInt(maxX);
      int newY = random.nextInt(maxY);

      // Köstebeğin pozisyonunu güncelle
      binding.btnKostebek.setX(newX);
      binding.btnKostebek.setY(newY);
  }
}

                      
          

Köstebek için Listener tanımlayalım ve köstebek vurulduğunda resmini değiştirelim (kostebek_vurulmus) ve puanı artırıp tvPuan a yazdıralım:

 
// Köstebek tıklama olayı
binding.btnKostebek.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        binding.btnKostebek.setImageResource(R.drawable.kostebek_vurulmus); // Vurulmuş resmi
        puan++;
        binding.tvPuan.setText("Puan: " + puan);
    }
});

                      
          

Köstebek çıkma ve vurulma sesini çalalım:

LoginActivity'de kullandığınız SoundPool yapısını temel alarak, köstebeğin çıkma ve vurulma seslerini entegre edeceğiz.
Seslerin oynatılmasını ses açık/kapalı durumuna ( SharedPreferences ile kontrol edilen KEY_SOUND) bağlı olarak ayarlayalım:

























Şimdi GameActivity.java’da kod yazmaya başlayalım:
Önce binding tanımalamasını yapalım:

 
private ActivityGameBinding binding; 

                      
          

View Binding ile layout'u inflate edelim:

 
// View Binding ile layout'u inflate etme binding = ActivityGameBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); 

                      
          

Değişkenleri ve sabitleri tanımlayalım:

             

            
          

Sesler için SoundPool’u tanımlayalım:

             

            
          

onCreate() metodu içinde gerekli başlatmaları yapalım:

             

            
          

Köstebek için Listener tanımlayalım ve tıklama olayını ekleyelim:

             

            
          

kostebegiHareketEttir() metodunu yazalım:
Bu metod, köstebeği rastgele bir konuma yerleştirir, görünür yapar, çıkma sesini çalar ve 1 saniye sonra kaybolup tekrar hareket etmesini sağlar.

             

            
          

Oyun süresini kontrol etmek için startZamanlayici() metodunu ekleyelim:
Bu metod, 10 saniyelik geri sayımı başlatır ve her saniye kalan süreyi gösterir, süre bitince oyunu sonlandırır.

             

            
          

Oyun bittiğinde çalışacak endGame() metodunu tanımlayalım:
Bu metod, hareketleri durdurur, en yüksek puanı kaydeder ve ResultActivity’ye geçiş yapar.

             

            
          

Kaynakları temizlemek için onDestroy() metodunu ekleyelim:

             
            
           
            
          

Çalıştırıp test edelim.
Oyun başladığında köstebek 1 saniye görünür, kaybolur ve tekrar başka yerde çıkar. Tıklandığında vurulma efekti 200 ms sürer ve puan artar. Süre bittiğinde ResultActivity’ye geçer.





















endGame() metodu:

 
private void endGame() {
  handler.removeCallbacksAndMessages(null);

  // Mevcut en yüksek puanı al ve yeni puanla karşılaştır
  int kayitliEnYuksekSkor = preferences.getInt(KEY_HIGH_SCORE, 0);
  if (puan > kayitliEnYuksekSkor) {
      SharedPreferences.Editor editor = preferences.edit();
      editor.putInt(KEY_HIGH_SCORE, puan);
      editor.apply();
  }

  // Oyun bittiğinde ResultActivity'ye geçiş
  Intent intent = new Intent(GameActivity.this, ResultActivity.class);
  intent.putExtra("score", 0); // Şimdilik puan 0, köstebek yakalama eklendiğinde güncellenecek
  startActivity(intent);
  finish(); // GameActivity'yi kapat
}

                      
          

Daha önce startZamanlayici() metodu içinde onFinish() metodu içinde oyunu bitirme işlemlerini yapmıştık.
// oyun sonu açıklama satırını silip endGame() metodu çağırarak oyunu bitirelim.

startZamanlayici() metodu (Son Hali):

 
private void startZamanlayici() {
  gameTimer = new CountDownTimer(toplamSure, 1000) {
      @Override
      public void onTick(long millisUntilFinished) {
          // Her saniye güncellenecek
          long secondsRemaining = millisUntilFinished / 1000;
          binding.tvSure.setText("Kalan Süre: " + secondsRemaining + " Saniye");
      }

      @Override
      public void onFinish() {
          // Süre bittiğinde
          binding.tvSure.setText("Süre Bitti!");
          endGame(); // Oyunu bitir
      }
  }.start();
}
                      
          

ve son olarak da onDestroy() metodu tanımlayıp binding ve handler kaynaklı bellek sızıntılarını önleyelim:

 
@Override
protected void onDestroy() {
    super.onDestroy();
    if (gameTimer != null) {
        gameTimer.cancel();
        gameTimer = null;
    }
    if (handler != null) {
        handler.removeCallbacksAndMessages(null);
        handler = null;
    }
    binding = null;
}
                      
          

GameActivity bitti. Şimdi de sonuçları gösteren ResultActivity'e geçiş yapalım.

# Sonuç Ekranı

# activity_result.xml tasarımı

activity_login.xml i tasarlamalaya başlayalım:

activity_result

Ekranda Neler olacak:

- Tebrik mesajı (TextView)
- Kullanıcı adı (TextView)
- Puan (TextView)
- Lider Tablosu (ImageView)
- En Yüksek 5 Puan (TextView)
- Tekrar Oyna butonu (Button)

Bileşenlerin hepsi alt alta olduğu için LinearLayout (vertical) kullanabiliriz.

Lider Tablosu görseli üzerinde en yüksek puanları göstermek için RelativeLayout ekleyebiliriz.

LinearLayout ekleyerek başlayalım:

İçine sırasıyla:

- Tebrik mesajı (TextView) tvTebrikMesaji,

- Kullanıcı adı (TextView) tvKullaniciAdi,

- Puan (TextView) tvPuan,

- Lidertablosunu göstermek için RelativeLayout,

RelativeLayout içine sırasıyla:

- Lider Tablosu (ImageView) ivLiderTablosu,

- En Yüksek 5 Puan (TextView) tvLiderTablosu,

- Tekrar Oyna butonu (Button) btnTekrarOyna


LinearLayout (Dikey Düzen):

layout_width (Genişlik): 0dp,
layout_height (Yükseklik): 0dp,
gravity (Yerleşim): center,
orientation (Yön): vertical,
padding (İç boşluk): 10dp,
layout_constraintBottom_toBottomOf (Alt kenara hizalama): parent,
layout_constraintEnd_toEndOf (Sağ kenara hizalama): parent,
layout_constraintStart_toStartOf (Sol kenara hizalama): parent,
layout_constraintTop_toTopOf (Üst kenara hizalama): parent.

android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center"
android:orientation="vertical"
android:padding="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"

                  
          

TextView (Tebrik Mesajı):

id: tvTebrikMesaji,
layout_width (Genişlik): wrap_content,
layout_height (Yükseklik): wrap_content,
layout_marginBottom (Alt boşluk): 20dp,
fontFamily (Yazı tipi): sour_gummy,
gravity (Yazı hizalaması): center,
shadowColor (Gölge rengi): @color/black,
shadowDx (Gölge X ofseti): 5,
shadowDy (Gölge Y ofseti): 5,
shadowRadius (Gölge yarıçapı): 10,
text (Metin): "Tebrikler!\nYeni En Yüksek Puan",
textColor (Yazı rengi): @android:color/holo_red_light,
textSize (Yazı boyutu): 34sp,
visibility (Görünürlük): visible.

android:id="@+id/tvTebrikMesaji"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:fontFamily="@font/sour_gummy"
android:gravity="center"
android:shadowColor="@color/black"
android:shadowDx="5"
android:shadowDy="5"
android:shadowRadius="10"
android:text="Tebrikler!\nYeni En Yüksek Puan"
android:textColor="@android:color/holo_red_light"
android:textSize="34sp"
android:visibility="visible" 
        
                      
          

TextView (Kullanıcı Adı):

id: tvKullaniciAdi,
layout_width (Genişlik): wrap_content,
layout_height (Yükseklik): wrap_content,
layout_marginBottom (Alt boşluk): 10dp,
fontFamily (Yazı tipi): sour_gummy,
shadowColor (Gölge rengi): @color/black,
shadowDx (Gölge X ofseti): 3,
shadowDy (Gölge Y ofseti): 3,
shadowRadius (Gölge yarıçapı): 5,
text (Metin): "CaglarKarabulut",
textColor (Yazı rengi): @color/white,
textSize (Yazı boyutu): 35sp.

android:id="@+id/tvKullaniciAdi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:fontFamily="@font/sour_gummy"
android:shadowColor="@color/black"
android:shadowDx="3"
android:shadowDy="3"
android:shadowRadius="5"
android:text="CaglarKarabulut"
android:textColor="@color/white"
android:textSize="35sp" 

                      
          

RelativeLayout (Lider Tablosu Alanı):

layout_width (Genişlik): wrap_content,
layout_height (Yükseklik): wrap_content,
layout_marginBottom (Alt margin): 30dp,
     
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="30dp" 

                        
          

ImageView (Lider Tablosu Görseli):

layout_width (Genişlik): wrap_content,
layout_height (Yükseklik): 450dp,
scaleType (Ölçekleme tipi): fitCenter,
src (Kaynak): @drawable/lider_tablosu,
         
android:id="@+id/ivLiderTablosu"
android:layout_width="wrap_content"
android:layout_height="450dp"
android:scaleType="fitCenter"
android:src="@drawable/lider_tablosu"

                      
          

TextView (Lider Tablosu Metni):

layout_width (Genişlik): wrap_content,
layout_height (Yükseklik): wrap_content,
layout_centerInParent (Ortada mı?): true,
layout_marginTop (Üst margin): 50dp,
fontFamily (Yazı tipi): @font/sour_gummy,
gravity (Yazı hizalaması): center,
shadowColor (Gölge rengi): @color/black,
shadowDx (Gölge X ofseti): 2,
shadowDy (Gölge Y ofseti): 2,
shadowRadius (Gölge yarıçapı): 3,
text (Metin): 1. Oyuncu1: 100\n2. Oyuncu2: 80\n3. Oyuncu3: 50\n4. Oyuncu4: 40\n5. Oyuncu5: 30,
textColor (Yazı rengi): @color/white,
textSize (Yazı boyutu): 30sp,
tools:visibility (Tasarım zamanı görünürlüğü): visible,

android:id="@+id/tvLiderTablosu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginTop="50dp"
android:fontFamily="@font/sour_gummy"
android:gravity="center"
android:shadowColor="@color/black"
android:shadowDx="2"
android:shadowDy="2"
android:shadowRadius="3"
android:text="1. Oyuncu1: 100\n2. Oyuncu2: 80\n3. Oyuncu3: 50\n4. Oyuncu4: 40\n5. Oyuncu5: 30"
android:textColor="@color/white"
android:textSize="30sp"
tools:visibility="visible"

                      
          

ImageButton (Tekrar Oyna Butonu):

layout_width (Genişlik): wrap_content,
layout_height (Yükseklik): 150dp,
layout_marginTop (Üst margin): 0dp,
background (Arka plan): @android:color/transparent,
scaleType (Ölçekleme tipi): fitCenter,
app:srcCompat (Kaynak): @drawable/btn_tekrar_oyna,

android:id="@+id/btnTekrarOyna"
android:layout_width="wrap_content"
android:layout_height="150dp"
android:layout_marginTop="0dp"
android:background="@android:color/transparent"
android:scaleType="fitCenter"
app:srcCompat="@drawable/btn_tekrar_oyna"

                      
          




















# ResultActivity.java

# Kullanıcı adı ve puanı gösterme

# Tebrik mesajı

# Kullanıcının puanı Firebase e kaydetme

# En yüksek 3 puanı gösterme

# Tekrar Oyna butonu

Yorum Yap:

Henüz yorum yok...