AA Game Demo — Unity
Github repository: https://github.com/SerkanTarakci/AA-Game-Demo
Android apk: Google Drive
Unity plaformunu öğrenmek amacıyla geliştirdiğim 2 boyutlu AA oyunu.
İçerik:
- Klasör yapısı
- Ortada dönen çemberi oluşturma
- Fırlatılacak iğneleri oluşturma
- İğneleri fırlatan küçük çemberi oluşturma
- Oyun yöneticisi oluşturma
- Ana menü yapımı
- Kalan iğne kontrolü ve sonrasında seviye geçişi
- Kayıt sistemi
- Ekran görüntüleri
Oyunumuzun klasör yapısı şu şekilde:
1. Aşama: Ortada dönen çemberi oluşturma
İlk olarak ortada dönen çemberimizi oluşturacağız. Dönen çemberimiz için boş obje oluşturduk ve buna bir SpriteRenderer ekledik. Sprite olarak Knob seçeneğini kullandık.
Çembere dönmesi için kod ekliyoruz.
SpinningCode:
public class SpinningCode : MonoBehaviour
{
public float speed;
void Update()
{
transform.Rotate(0,0, speed*Time.deltaTime);
}
}
Burada sadece çemberi z ekseninde verdiğimiz speed değeri kadar sabit bir hızda döndürme işlemini yapıyoruz.
RotatingCircle Yapısı:
Dönen çembere RotatingCircleTag etiketini ekledik. Çarpışma kontrolü için Circle Collider 2D ekledik.
2. Aşama: Fırlatılacak iğneleri oluşturma
İğne yapmak için bu Needle isimli küçük çember oluşturuyoruz. Sprite Renderer ekliyoruz. Sprite olarak Knob kullanıyoruz. İğnenin gövde kısmı için Needle nesnesine bir alt nesne ekliyoruz. Bu sefer uzun ince bir yapı olması için sprite olarak InputFieldBackground kullandık. Needle objesine(yani parent objeye) hareket için Rigidbody 2D, iğnenin baş kısmı için Circle Collider 2D ve iğnenin gövdesi için Box Collider 2D ekliyoruz ve Is Trigger kutucuğunu işaretliyoruz. İğnenin hareketi için Needle objesine kod ekliyoruz.
public class NeedleScript : MonoBehaviour
{
Rigidbody2D physiscs;
public float speed;
bool moveControl = false;
void Start()
{
physiscs = GetComponent<Rigidbody2D>();
}// Update is called once per frame
void FixedUpdate()
{
if (!moveControl)
{
physiscs.MovePosition(physiscs.position + Vector2.up * speed * Time.deltaTime);
}
}
void OnTriggerEnter2D(Collider2D col)
{
if (col.tag == "RotatingCircleTag")
{
transform.SetParent(col.transform);
moveControl = true;
}
}
}
Burada öncelikle Rigidbody2D türünde physics isimli bir değişken oluşturuyoruz ve Unity’deki Rigidbody2D componentimizi bu değişkene eşitliyoruz.
Çarpışma olduğu zaman hareketi durdurmak için boolean türünde moveControl isimli bir değişken oluşturuyoruz. Bu değer başta false. OnTriggerEnter2D metodunda eğer iğne, “RotatingCircleTag” etiketine sahip bir nesneye çarparsa moveControl değeri true olacak yani iğne hareket etmeyecek. “transform.SetParent(col.transform);” satırı ise çarpıştıkları zaman çemberi iğnenin parentı yapıyor ve iğnenin çemberle birlikte dönmesini sağlıyor.
Daha sonra bu objeyi tekrar tekrar kullanabilmek için Prefabs dosyamıza prefab olarak taşıyoruz.
3. Aşama: İğneleri fırlatan küçük çemberi oluşturma
İğnelerin fırlatılacağı noktayı temsil etmesi için yeni bir obje oluşturuyoruz. Sprite Renderer ekliyoruz ve yine Sprite olarak Knob kullanıyoruz. Throwing Circle objemize, ekrana tıklandığı zaman iğneleri fırlatması için script ekliyoruz.
ThrowingCircleScript:
public class ThrowingCircleScript : MonoBehaviour
{
public GameObject needle;
private void Start()
{
gameController = GameObject.FindGameObjectWithTag("GameControllerTag");
}
void Update()
{
if(Input.GetButtonDown("Fire1"))
{
ThrowNeedle();
}
}
void ThrowNeedle()
{
Instantiate(needle,transform.position,transform.rotation);
}
}
4.Aşama: Oyun yöneticisi oluşturma
Oyunun bittiğini kontrol etmek gibi, bir sonraki bölüme geçişi kontrol etmek gibi oyunla ilgili genel kontrol ve operasyonları gerçekleştirmek adına GameController adında boş bir nesne yaratıyoruz ve buna GameControllerScript ekliyoruz. Nesnemize GameControllerTag etiketini veriyoruz.
Oyunu bitirme kontrolü:
İki iğne birbirine çarptığı zaman oyunu sonlandırmamız gerekiyor. İğnelerin hareketi ile ilgili operasyonlar NeedleScript içerisindeydi. Oyun bittiği zaman gerçekleşecek işlemler ile ilgili GameOver fonksiyonumuz ise GameControllerScript içerisinde. Biz NeedleScript içerisinde iki iğne birbiri ile temas ettiği zaman GameControllerScript içerisindeki GameOver fonksiyonunu çağıracağız. Bu yüzden NeedleScript içerisinden GameControllerScript içerisindeki bir metoda ulaşmak için bir bağlantı kurmamız gerekecek.
GameControllerScript, GameController diye bir nesnenin içinde duruyor. O yüzden NeedleScript içerisinde:
GameObject gameController;
komutu ile GameObject türünde bir değişken oluşturuyoruz. Start metodu içerisinde:
gameController = GameObject.FindGameObjectWithTag(“GameControllerTag”);
komutu ile bunu “GameControllerTag” etiketine sahip olan objeye yani GameController nesnemize bağlıyoruz. Artık bu obje içindeki scripte ve onun metotlarına ulaşabiliriz. NeedleScript içerisindeki OnTriggerEnter2D metodunda:
if(col.tag == “NeedleTag”)
{
gameController.GetComponent<GameControllerScript>().GameOver();
}
komutu ile eğer bir iğne etiketi “NeedleTag” olan bir nesneye çarparsa, gameController içerisinde bulunan GameControllerScript içerisindeki GameOver metodu çağırılacak.
(NeedleScript son hali için tıklayınız)
Oyun bittiği zaman daha önce hazırladığımız basit bir bitiş animasyonunun çalışmasını istiyoruz ve animasyonun ardından ana ekrana dönülmesini istiyoruz. Eğer animasyon oynatma kodunu ve ana ekrana dönüş kodunu alt alta yazarsak geçişler çok hızlı olacağı için animasyon oynamayacak. Bu yüzden ana ekrana geçmeden önce kodumuzu bekleteceğiz.
GameControllerScript:
...
public void GameOver()
{
StartCoroutine(CalledGameOver());
}IEnumerator CalledGameOver()
{
roundedCircle.GetComponent<SpinningCode>().enabled = false;
clickCircle.GetComponent<ThrowingCircleScript>().enabled = false;
animator.SetTrigger("GameOverTrigger");
goNewLevel = false;
yield return new WaitForSeconds(1.5f);
SceneManager.LoadScene("MainMenu");
}
...
Kodumuzu Coroutine yapısı ile bekletiyoruz. Oyun bittiği zaman gerçekleşecek aksiyonları IEnumerator türündeki CalledGameOver() adında bir metodun içine yazdık. MainMenu sahnesine geçmeden önce kodumuzu “yield return new WaitForSeconds(1.5f);” komutu ile 1.5 saniye kadar beklettik ve bu sayede animasyonumuz oynamaya başladı.
5.Aşama: Ana menü yapımı
Bir ana menü dizaynı yapıyoruz. Sahneye Play — Exit butonlarını koyuyoruz. Bu işlemleri kontrol etmek için Bir MainMenuController objesi oluşturup içerisine MainMenuControlScript ekliyoruz.
MainMenuControlScript:
public void Play()
{
SceneManager.LoadScene("1");
}public void Exit()
{
Application.Quit();
}
- Sahneleri File — Build Settings — Scenes In Build alanına eklemeyi unutmamalıyız.
Daha sonra editöre geliyoruz ve Play butonundaki On Click() metodunun içine MainMenuController objesini sürüklüyoruz ve sağ tarafta MainMenuControlScript içerisindeki Play metodunu seçiyoruz. Aynı şekilde Exit butonu için de scriptimizdeki Exit metodunu seçiyoruz.
6.Aşama: Kaç adet iğne kaldığını görme ve iğneler bittiği zaman diğer bölümlere geçme
Bu işlemleri GameControllerScript içinde yapacağız. Oyun editöründe iki adet text objesi oluşturuyoruz. Sonra kodumuza gelip:
public Text levelText,needleLeftText;
public int howManyNeedle;
komutu ile editördeki objeleri kodumuza tanıtıyoruz. howManyNeedle değişkeni ile hangi seviyede kaç adet iğne olacağını oyun editörü üzerinde belirleyebiliriz. Start metodu içerisinde kodumuzdaki değişkenleri oyun editörü ile aşağıdaki komutlar sayesinde bağlıyoruz:
levelText.text = SceneManager.GetActiveScene().name;
needleLeftText.text = howManyNeedle + “”;
Sahne isimlerini 1,2,3… şeklinde vermiştik. Bu sayede hangi seviyedeysek dönen çemberin üzerinde o seviyenin adı yazacak.
Kalan iğne kontrolü ve sonrasında seviye geçişi:
Kalan iğne kontrolü için GameControllerScript içinde ShowHowManyLeft adında bir metot oluşturduk:
public void ShowHowManyLeft()
{
howManyNeedle — ;
needleLeftText.text = howManyNeedle + “”;
if(howManyNeedle==0)
{
StartCoroutine(newLevel());
}
}
Bu metoda yine ThrowingCircleScript üzerinden ulaşılacak ve iğne sayısı 0 olduğu zaman bir sonraki seviyeye geçilecek. Geçiş sırasında yine animasyon oynatacağımız için newLevel metodunu Coroutine içerisinde çağırıyoruz.
...
IEnumerator newLevel()
{
roundedCircle.GetComponent<SpinningCode>().enabled = false;
clickCircle.GetComponent<ThrowingCircleScript>().enabled = false;
yield return new WaitForSeconds(1.5f);
if (goNewLevel)
{
animator.SetTrigger("NewLevelTrigger");
yield return new WaitForSeconds(1.5f);
SceneManager.LoadScene(int.Parse(SceneManager .GetActiveScene().name) + 1);
}
}
...
Sahne isimlerimizi 1,2,3… şeklinde verdiğimiz için bir sonraki sahneyi yüklerken, mevcut sahne adının integer türünden değeri + 1 dememiz yeterli oluyor.
Şimdi iğneleri fırlattığımız küçük çember üzerinde kalan iğne sayısını göstermek ve sayı 0 olduğunda GameControllerScript içerisindeki newLevel metodunu çağırmak için ThrowingCircleScript ile GameControllerScript scriptlerini bir obje üzerinden bağlayacağız.
ThrowingCircleScript içerisinde:
GameObject gameController;
komutu ile bir gameController değişkeni oluşturduk.
Start metodunda:
gameController = GameObject.FindGameObjectWithTag(“GameControllerTag”);
komutu ile gameController değişkenini, Unity’de “GameControllerTag” etiketine sahip olan nesneye bağladık. Artık istediğimiz zaman bu objenin scriptlerine erişebiliriz. İğneleri attığımız metodumuzu şu şekilde güncelliyoruz:
ThrowNeedle:
void ThrowNeedle()
{
Instantiate(needle,transform.position,transform.rotation);
gameController.GetComponent<GameControllerScript>().ShowHowManyLeft();
}
7.Aşama: Kayıt sistemi
Hangi bölümde olduğumuzu kaydetmek için Unity’nin kendi kayıt sistemi olan PlayerPrefs yapısını kullanıyoruz. Bu yapı ile herhangi bir sınıf içerisinde bir anahtar sözcük kullanılarak kaydedilen değere diğer sınıflardan ulaşılabilir. Hangi bölümde olduğumuzu kaydetmek için GameControllerScript sınıfımızın Start metoduna aşağıdaki satırı ekliyoruz:
PlayerPrefs.SetInt(“Record”, int.Parse(SceneManager.GetActiveScene().name));
Bu komut sayesinde en son hangi bölümdeysek o bölüm Record adında bir integer değer olarak hafızaya kaydediliyor. Daha sonra oyunu açtığımızda ilk açılan sahnede yani MainMenu sahnesinde herhangi bir kayıt olup olmadığını kontrol edeceğiz. Eğer bir kayıt varsa Play butonuna bastığımız zaman oyuna kayıtlı bölümden başlayacağız.
MainMenuControlScript:
public class MainMenuControlScript : MonoBehaviour
{
public void Play()
{
if(PlayerPrefs.GetInt("Record")==0)
{
SceneManager.LoadScene("1");
}
else
{
SceneManager.LoadScene(PlayerPrefs.GetInt("Record"));
}
}public void Exit()
{
PlayerPrefs.DeleteAll();
Application.Quit();
}
}
Burada Exit butonuna bastığımız zaman PlayerPrefs’te bulunan tüm değerleri siliyor. Kaydetme ve silme işlemleri için ayrı butonlar da yapabilirdik tabi ki.