9 Kasım 2016 Çarşamba

Unity3D: Save / Load Data(JSON)



Başlangıç

Unity3D de oyun/uygulama içinde veri kaydetmek istediğimizde Unity bize PlayerPrefs gibi bir seçenek sunuyor. Ben bundan farklı olarak .json dosyası oluşturmak ve buradan verileri okumakla ilgili bir örnek paylaşacağım.

Bu şekilde daha organize bir data yapısı kullanabilirsiniz.

Gereksinimler

NewtonSoft.Json : Bu linkden indireceğiniz dll dosyasını Assets>Plugins içine atınız. Yada ReadAndWriteJson.unitypackage linkinden indireceğiniz unitypackage dosyasını açmış olduğunuz boş bir projeye import edip oradan alabilir ve yapmış olduğum örneği inceleyebilirsiniz.


Yaptığım örnekde Scene'e, 2 tane InputField, 2 tane Button, 1 tane Text ekledim. Bir tane boş GameObject ekledim. GameObject için FileOperations adında bir tane script oluşturdum.


FileOperations.cs içinde UI elementleri için field ları oluşturalım.

using UnityEngine;
using System;
using System.Collections.Generic;
using UnityEngine.UI;
using System.IO;
using Newtonsoft.Json;

public class FileOperations : MonoBehaviour
{

    public Button LoadBtn;
    public Button SaveBtn;
    public Text text;

    
    private SaveData saveData;

    public InputField IdText;
    public InputField NameText;
}

Scene içerisinde bulunan ilgili UI elementlerini Hierarchy de FileOperations.cs da bulunan ilgili yerlere sürükleyip bırakalım. UI butonlar için ilgili event listenerları ekleyelim.

public void Awake()
{
    LoadBtn.onClick.AddListener(LoadGameBtnListener);
    SaveBtn.onClick.AddListener(SaveGameBtnListener);        
}

private void LoadGameBtnListener()
{
}

private void SaveGameBtnListener()
{
}

Save ve Read işlemlerini yapmak için Application.persistentDataPath kullanıyoruz. Kullanacağımız data dosyasının ismi SaveData.json olsun. Bu durumda dosya adresimiz aşağıdaki gibi olacak ve bunu Awake içine ekleyelim.

private string fileName;

public void Awake()
{
    .......
    fileName = Application.persistentDataPath + "/SaveData.json";
}

Application.persistentDataPath adresi PC için C:\Users\[UserName]\AppData\LocalLow\[CompanyName]\[ProductName] şeklindedir. Bu adresi kendi PC nizde  Debug.Log(Application.persistentDataPath) yazarak görebilirsiniz.

Şimdi SaveData.json dosyasının önceden yaratılıp yaratılmadığına bakmak için Awake içine ChechDataFile metodu nu çağıralım. Bu metod json dosyanız oyununuz/uygulamanız ilk çalıştırıldığında yaratılmadığı için yaratacak ve sonraki çalıştırmalarda yeni bir dosya yaratılmasını önleyecek.

public void Awake()
{
    .......
    ChechDataFile();
}

private void ChechDataFile()
{
    try
    {
        if(!File.Exists(fileName))
        {
            FileStream fs = File.Create(fileName);
            fs.Close();
        }
    }
    catch (Exception e)
    {
        Debug.Log("Exeption: " + e);
    }
}


Şimdi saklayacağımız veri ile ilgili scriptimiz için SaveData.cs yaratalım. Bu Script'i aşağıdaki gibi yazalım. Json Dosyamız bu scriptler üzerinde Serialize/Deserialize edilecek. Yani içeride kullanacağımız veri yapılarına dönüştürülecek. Burada InventoryItemlar'ı listeleyeceğiz. Her item'ın bir ID ve Name değeri olacak. Bu itemlar bir liste içerisinde tutulacak.

using System.Collections.Generic;

public class SaveData
{
    public List<InventoryItem> Items { get; set; }
}

public class InventoryItem
{
    public int ID { get; set; }
    public string Name { get; set; }
}


FileOperations.cs içerisine yaratılacak json dosyasını okuyup string değerini alacağımız Metodu ekleyelim.

private string saveDataJson;

private void ReadDataFile()
{
    saveDataJson = string.Empty;
    
    try
    {
        if(File.Exists(fileName))
        {
            saveDataJson = File.ReadAllText(fileName);
        }
    }
    catch (Exception e)
    {
        Debug.Log("Exeption: " + e);
    }
}


Load butonuna basıldığı zaman aşağıdaki işlemleri yapsın ve ekrana json dosyamızın içeriğini yazdırsın.
private void LoadGameBtnListener()
{
    ReadDataFile();

    text.text = saveDataJson;
}


Ekran üzerinde ID kısmına rakam ve Name kısmına isim yazdığımızda Save butonuna basacağız ve bu verileri json dosyamıza kaydedeceğiz. Aynı zamanda json dosyamızın son halini ekrana yazdıracağız.

private void SaveGameBtnListener()
{
    ReadDataFile();

    if(IdText.text == string.Empty || NameText.text ==string.Empty)
    {
        return;
    }

    int x = 0;

    if (Int32.TryParse(IdText.text, out x) == false)
    {
        return;
    }

    if (saveDataJson != string.Empty)
    {
        saveData = JsonConvert.DeserializeObject<SaveData>(saveDataJson);
    }
    else
    {
        saveData = new SaveData();
        saveData.Items = new List<InventoryItem>();
    }

    InventoryItem inventoryItem;

    inventoryItem = saveData.Items.Find(item =< item.ID == Int32.Parse(IdText.text));

    if(inventoryItem == null)
    {
        inventoryItem = new InventoryItem();
        inventoryItem.ID = Int32.Parse(IdText.text);
        inventoryItem.Name = NameText.text;
        saveData.Items.Add(inventoryItem);
    }
    else
    {
        //Update Data
        inventoryItem.Name = NameText.text;
    }
        
    saveDataJson = JsonConvert.SerializeObject(saveData);

    try
    {
        File.WriteAllText(fileName, saveDataJson);
    }
    catch (Exception e)
    {
        Debug.Log("Exeption: " + e);
    }

    LoadGameBtnListener();
}

Bu projenin çalışır halini ReadAndWriteJson.unitypackage linkinden indireceğiniz unitypackage dosyasını boş bir Unity projesi içine import edip inceleyebilirsiniz.

10 Eylül 2016 Cumartesi

New from Pixega Studio: "Fruit Picking"

Android : http://goo.gl/fyU2Aw
iOs : <Comming Soon>


“Fruit Picking”
 is an exciting and entertaining fruit collecting game for children.
Free and ad-free – It will always be free.
Easy to play – Just tap the falling fruits and collect them.
The game suits best for 2-6 years old. But no one will blame you if you are -as an adult- already get caught up by the game’s amusement.
Features
The game contains most known 9 fruits which are apple, banana, cherry, grape, orange, pear, pineapple, strawberry and watermelon.
All graphics of the game are colorful and attractive.
The game has joyful background music that is appropriate for toddlers.
Every interaction triggers a special sound effect that excites kids’ attention.
You can enable or disable sounds and music.

Gameplay
At the beginning of every game, a fruit will be determined randomly. The aim is to collect that fruit up until you reach your goal.
The goal will be determined randomly between 10 and 25.
At the beginning of the every game, you will have 5 extra lives.
If you tap on other fruits than the determined one, you will lose one of your extra lives.
When you run out of extra life the game will be over.
You can always restart the game.

Notes to the Parents
This type of games develops your child’s 
- concentration,
- motor skills and
- hand-eye coordination.
Also, your beloved ones will learn about fruits and distinguish between them.
The child's developing skills will be fostered by enthusiastic visual and sound effects like confetti and applause.
Download it for free and let your toddler enjoy this great addicting game. Let us know your thoughts in the comment section.

2 Eylül 2016 Cuma

Watch Your Tap





Are you ready for the competition about being the fastest tapper in the wild west?
Warm up your fingers!
Try to be on the top of the leaderboard!

31 Ağustos 2016 Çarşamba

Color Bars Launch Trailer



IOS: http://goo.gl/uMimqe
Android: http://goo.gl/pMmjuZ

Color Bars is an addictive arcade game. 
It is easy to play and fun! How many bars can you match? 
Try your best to be the first on the leader board!

28 Ağustos 2016 Pazar

Design Patterns in Unity3D MVC



Merhabalar.

Unity3D içerisinde development yaparken karşımıza çıkabilecek sorunları ve bu sorunların çözüm yollarını MVC içerisinde nasıl çözebileceğimizi kısaca anlatmaya çalıştım. Bu bilgiler yine bir miktar ileri programlama bilgisi ve terminoloji hakkında bilgi sahibi olmayı gerektiriyor. Bu konuları dilimin döndüğünce sizlere anlatmaya çalıştım. Hatam olduysa affola. Umarım sizlere faydası olacak bilgilerdir. Zaman ayırdığınız için teşekkürler.

Aşağıda faydalı olabileceğini düşündüğüm linleri paylaşıyorum.

Observer Pattern
Unity Design Pattern
Design Pattern
Singleton Pattern
Observer Pattern 2

27 Ağustos 2016 Cumartesi

MVC Nedir?



Unity3D projelerinde ileri programlama yapmak için bir dizi eğitim videosu oluşturmaya karar verdim. vakit buldukça bu eğitimleri hazırlayıp buradan paylaşacağım.
İleride paylaşacağım eğitimlerde Unity3D için kendi geliştirmiş olduğum MVC Framework'ü uGaMa'yı ve uGaMa ile nasıl oyun programlayacağınız örnek kodlarla anlatmaya, kaynak kodları buradan paylaşmaya çalışağım.
Hazırlayacağım bu seri Unity3D de veya C# da ileri programlama bilgisinesahip olanlara hitap etmektedir.

uGaMa hakkında daha detaylı bilgiye buradan ulaşabilirsiniz.
Ayrıca MVC için burayıda incelebilirsiniz. Güzel anlatılmış.

13 Ağustos 2016 Cumartesi

Script Execution Order

Unity3D bazen yazdığınız Scriptin diğerlerinden daha önce çalışmasını isteyebilirsiniz. Bunu yapmanın iki yolu vardır. Bu iki yolu sırasıyla adım adım yapalım.

A. Unity Editor üzerinden elle ayarlama:

Diyelim ki 5 tane ayrı scriptiniz var bunlar ScriptA, ScriptB, ScriptC, ScriptD, ScriptE.

Bunların şu sırayla çalışmasını istiyorum ScriptA, ScriptC, ScriptD, ScriptB, ScriptE.


  1. İlk önce Unity içinden şu sırayla Script Execution Order özelliklerine ulaşalım.
    Edit > Project Settings > Script Execution Order
    Karşımıza Inspector içinde aşağıdaki resimde olduğu gibi bir alan açılacak.
  2. Burada "+" işaretine tıkladığımızda projemizde yazılmış olan Scriptler listelenir. Açılan Listeden ScriptA yı seçelim. Ardından tekrar "+" ya tıklayıp listeden ScriptC yi seçelim ve istediğimiz sırayı oluşturana kadar bu işlemi tekrarlayalım ve "Apply" butonuna tıklayalım. Eğer sıralamayı değiştirmek isterseniz "=" butonuna tıklayıp ilgili sıraya sürekleyip bırakarak sıralamayı değiştirebilirsiniz. Eğer bu sıralamanın dışında olmasını isdeğiniz bir Script varsa
    "-" ye basarak listeden çıkarabilirsiniz. Yapmış olduğunuz değişiklikleri kaybetmemek için "Apply" butonuna basmayı unutmayın. Yazmış olduğunuz bütün scritleri buraya eklemenize gerek yok. Burada ki sıralamanın amacı özel olarak çalışmasını istediğimiz scritleri ve sırasını belirtmek. Listede Gördüğünüz gibi yanlarında rakamlar bulunmakta bunları isterseniz değiştirebilirsiniz. Listede ilk sırada bulunan Script ilk çalışır son bulunan Script en son çalışır.


B. Kod ile Dinamik Script Execution Order oluşturma:

Yine aynı sıralamayı kod ile yapalım.


  1. Bunun için Hazırlamış olduğumuz ScriptA, B... E Scriptlerine bir göz atalım.

  2. using UnityEngine;
    
    public class ScriptA : MonoBehaviour {
    
    }
    
    using UnityEngine;
    
    public class ScriptB : MonoBehaviour {
    
    }
    
    using UnityEngine;
    
    public class ScriptC : MonoBehaviour {
    
    }
    
    using UnityEngine;
    using System.Collections;
    
    public class ScriptD : MonoBehaviour {
    
    }
    
    using UnityEngine;
    
    public class ScriptE : MonoBehaviour {
    
    }

  3. Yazmış olduğıumuz bu Scriptlere sıralama özelliğini verecek Scriptimizi yazalım.

  4. using System;
    using UnityEngine;
    
    public class ScriptOrder : Attribute
    {
        [HideInInspector]
        public int order;
            
        public ScriptOrder(int order)
        {
            this.order = order;
            Debug.Log("+ ScriptOrder : " + this.order);
        }
    }
    

  5. Yazmış olduğumuz bu Scriptlerin Orderlarını (çalışma sıralarını) belirtelim.

  6. using UnityEngine;
    
    [ScriptOrder(-1000)]
    public class ScriptA : MonoBehaviour {
    
    }
    
    using UnityEngine;
    
    [ScriptOrder(-970)]
    public class ScriptB : MonoBehaviour {
    
    }
    
    using UnityEngine;
    
    [ScriptOrder(-990)]
    public class ScriptC : MonoBehaviour {
    
    }
    
    using UnityEngine;
    using System.Collections;
    
    [ScriptOrder(-980)]
    public class ScriptD : MonoBehaviour {
    
    }
    
    using UnityEngine;
    
    [ScriptOrder(-960)]
    public class ScriptE : MonoBehaviour {
    
    }

  7. Şimdi ise bu sıralamaları Unity Editore aktaracak Editor Scriptimizi yazalım. Bunun için Asset klasörü altında yeni bir klasör oluşturup ismini Editor olarak değiştirelim. Ve yeni bir Script oluşturalım. İsmine de ScriptOrderManager diyelim. ScriptOrderManager Scriptinin içeriği aşağıdaki gibi olacak.

  8. using System;
    using UnityEditor;
    
    [InitializeOnLoad]
    public class ScriptOrderManager
    {
    
        static ScriptOrderManager()
        {
            foreach (MonoScript monoScript in MonoImporter.GetAllRuntimeMonoScripts())
            {
                if (monoScript.GetClass() != null)
                {
                    foreach (var a in Attribute.GetCustomAttributes(monoScript.GetClass(), typeof(ScriptOrder)))
                    {
                        var currentOrder = MonoImporter.GetExecutionOrder(monoScript);
                        var newOrder = ((ScriptOrder)a).order;
                        if (currentOrder != newOrder)
                            MonoImporter.SetExecutionOrder(monoScript, newOrder);
                    }
                }
            }
        }
    }
    
  9. Unity de yine Edit > Project Settings > Script Execution Order şeklinde Inspectordan Execution Order penceresini açıtığınızda otomatik olarak sıralamanın kodda belirttiğimiz gibi değiştiğini göreceksiniz. Bu sıralamayı artık bu pencere üzerinden değiştiremezsiniz. Değiştirmek istediğinizde sırasını kod üzerinden değiştirmeniz gerekecektir. Çünkü Unity her değişiklik yaptığınızda ScriptOrderManager scriptini çalıştırıp sıralamayı kodda belirttiğiniz şekilde yapacaktır. Bu sayede kazara değerleri silmenize ve/veya değiştirmenize karşı korumaya almış olursunuz.

28 Temmuz 2016 Perşembe

Observer Pattern in Unity3D

Observer Pattern Nedir?


Observer Pattern Object Oriented Programing içerisinde tanımlı design modellerinden biridir.
Kısaca bir nesnede olan değişikliklerden diğer nesnelerin haberdar olmasını sağlar. 
3 bileşeni vardır:

  1. Subject: Üzerinde oluşan değişikliklerin izleneceği nesnedir.Bu nesneyi izleyecek olana nesneler daha önceden bildirilir. İzleyici nesneler hangi değişiklikleri izlediğinide bildirmek zorundadır.
  2. Observer: Hangi değişikliklerin kimlere gönderileceğini kendi içerisinde tutar.
  3. Subscriber : Subject üzerinde olan değişikliklerden haberdar olmak isteyen nesnelerdir. Subscriberlar, Observer a izlemek istediği değişikliklere dair kendini kaydettirir.
Burda dikkat edeceğiniz üzere Subject ve Subscriberların bir birinden haberi yoktur. Birbirlerinden habersiz olarak Subject, Subscriberlara değişiklikleri gönderir ve kimlerin bu değişiklikleri dinlediğiyle ilgilenmez. Sonuç olarak Subject'in içerisinde Subscriberla ilgili bir değişken yaratmaya gerek kalmaz.

Unity için bir örnek üzerinden anlatmaya devam edelim:

Diyelimli sahnemizde bir GameObject nesnesi var ve buna "boss" diyelim. Bir alan içerisinde bunun hareket ettiğini ve duvarlara temas ettiğinde yön değiştirdiğini faredelim. "boss" un sahip olduğu 4 tane de koruması "guard" ı temsil eden GameObjectler olduğunu düşünelim. Bu "guard"ların "boss"un çevresinde onu korumak için hareket ettiğini farz edelim. Bu durumda "boss" ObserverPattern içerisinde "Subject", "guar"lar ise yine ObserverPattern içerisinde "Subscriber" olarak tanımlarız. 

ObserverPattern kullanmıyor olsaydık en basit yoldan bu senaryoyu gerçekleştirmek için "boss" için yarattığımız scrit içerisinde kaç tane guard varsa bunları tanımlamamız gerekirdi. Diyelim daha fazla guar olacak bunlarıda bir şekilde bu script e eklemek veya bir liste içerisinde tutmak gerekecekti. 
ObserverPattern kullandığımız zaman ise "boss" ve "guard"lar birbirinden habersiz olacak ve guardlar Observer dan "boss" un hareketi ile ilgili bilgi isteyecek. "boss" ise Observer a hareketi ile ilgili bilgi gönderecek.

Şimdi yavaş yavaş Unity için Observer Pattern'ımızı kuralım.

İlk olarak Observer bileşenimizi yazalım:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

public class ObservParam
{
    public object data;
    public object key;
}

public class Observer
{
    public static Dictionary<object, Dictionary<Action<ObservParam>, BaseBehaviour>> observList = new Dictionary<object, Dictionary<Action<ObservParam>, BaseBehaviour>>();

    public static void AddListener(object key, BaseBehaviour obj, Action<ObservParam> callback)
    {
        if(!observList.ContainsKey(key))
        {
            Dictionary<Action<ObservParam>, BaseBehaviour> actions = new Dictionary<Action<ObservParam>, BaseBehaviour>();
            actions.Add(callback, obj);
            observList.Add(key, actions);
        }
        else
        {
            Dictionary<Action<ObservParam>, BaseBehaviour> actions = observList[key];
            actions.Add(callback, obj);
        }
    }

    public static void SendMessage(object key)
    {
        if(observList.ContainsKey(key))
        {
            ObservParam observParam = new ObservParam();
            observParam.data = null;
            observParam.key = key;
            Dictionary<Action<ObservParam>, BaseBehaviour> actions = observList[key];

            for (int i = 0; i < actions.Count; i++)
            {
                BaseBehaviour tmpBehavior = actions.Values.ElementAt(i);
                tmpBehavior.OnHandlerMessage(observParam, actions.Keys.ElementAt(i));
            }
        }
    }

    public static void SendMessage(object key, object param)
    {
        if (observList.ContainsKey(key))
        {
            ObservParam observParam = new ObservParam();
            observParam.data = param;
            observParam.key = key;
            Dictionary<Action<ObservParam>, BaseBehaviour> actions = observList[key];

            for (int i = 0; i < actions.Count; i++)
            {
                BaseBehaviour tmpBehavior = actions.Values.ElementAt(i);
                tmpBehavior.OnHandlerMessage(observParam, actions.Keys.ElementAt(i));
            }
        }
    }

    public static void RemoveListener(object key, BaseBehaviour obj, Action<ObservParam> callback)
    {
        if(observList.ContainsKey(key))
        {
            Dictionary<Action<ObservParam>, BaseBehaviour> actions = observList[key];
            for (int i = 0; i < actions.Count; i++)
            {
                if(actions.Keys.ElementAt(i) == callback && actions.Values.ElementAt(i) == obj)
                {
                    actions.Remove(callback);
                }
            }
        }
    }

    public static void RemoveAllListeners(BaseBehaviour obj)
    {
        foreach (KeyValuePair<object, Dictionary<Action<ObservParam>, BaseBehaviour>> item in observList)
        {
            Dictionary<Action<ObservParam>, BaseBehaviour> actions = item.Value;
            if (actions.ContainsValue(obj))
            {
                for (int i = 0; i < actions.Count; i++)
                {
                    if (actions.Values.ElementAt(i) == obj)
                    {
                        actions.Remove(actions.Keys.ElementAt(i));
                    }
                }
            }
        }
    }
}
İkinci olarak da Subject ve Subscriberlar için yani "boss" ve "guard"lar için temel oluşturacak Scriptimizi yazalım. Bu Script MonoBehaviour dan türeyecek ve bu sayede Observer'dan gelecek olan güncelemeleri dinleyebileceğiz.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;

public class BaseBehaviour : MonoBehaviour {

 internal void OnHandlerMessage(ObservParam observParam, Action<observparam> value)
    {
        value(observParam);
    }
}
Üçüncü olarak "Boss" un hareketleri esnasında göndereceği ve "Guard"ların "Observer"'a bu updateler ile ilgili kayıt oluşturacığı "key"leri oluşturalım.:
public enum BossUpdateKey
{
    MOVE_FORWARD,
    MOVE_BACKWARD
}
Dördüncü olarak "Boss" ve "Guard" ile ilgili Scriptlerimizi yazalım.
using UnityEngine;
using System.Collections;
using System;

public class Boss : BaseBehaviour
{
    private void HomeListener(ObservParam obj)
    {
        int myParam = (int)obj.data;
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.UpArrow))
        {
            MoveForward();
        }
        if (Input.GetKey(KeyCode.DownArrow))
        {
            MoveBackward();
        }
    }

    void MoveForward()
    {
        Observer.SendMessage(BossUpdateKey.MOVE_FORWARD, "ileri gidiyorum");
    }
    void BackForward()
    {
        Observer.SendMessage(BossUpdateKey.MOVE_BACKWARD, 5);
    }
}
using UnityEngine;
using System.Collections;
using System;

public class Guard : BaseBehaviour
{

    // Use this for initialization
    void Awake()
    {
        Observer.AddListener(BossUpdateKey.MOVE_FORWARD, this, MoveForwardHandler);
        Observer.AddListener(BossUpdateKey.MOVE_BACKWARD, this, MoveBackwardHandler);
    }

    private void MoveForwardHandler(ObservParam obj)
    {
        string myParam = obj.data as string;
        Debug.Log("Boss İleri Gitti");
        Debug.Log("Boss: "+ myParam);
    }

    private void MoveBackwardHandler(ObservParam obj)
    {
        int myParam = (int)obj.data;
        Debug.Log("Boss Geri Gitti");
        Debug.Log("Boss: "+ myParam);
    }
}