Unity’de Yer Çekimi Silahı Yapımı (Raycast & Rigidbody)

Unity’de Yer Çekimi Silahı Yapımı (Raycast & Rigidbody)

12 Kasım 2020 0 Yazar: Ahmet Güler

Merhaba, bir önceki dersimizde Unity ile el feneri yapmıştık.Böylece korku temalı projeleriniz için olmazsa olmaz bir mekaniğin yapımını öğrenmiştik.Bugünkü dersimizde ise, Unity ile yer çekimi silahı yapmayı öğreneceğiz.Yer çekimi silahının temeli Half Life oyununa dayanıyor.Gerçekleştirmiş olduğu işlev ise basit olarak, bir nesneyi çekmek veya itmek olarak özetlenebilir.Bu silahın adı, Gravity Gun olarak geçiyor.Yani yer çekimi silahı.Ancak biz bugün bir Gravity Gloves yapmayı deneyeceğiz.O zaman vakit kaybetmeden projemize başlayalım.

Sahnemizi oluşturmaya başlayalım.

Evet bu projemizde kullanacağımız senaryoya bir bakalım.Sahnemizde yer çekimi silahı ile etkileşime gireceğimiz nesnelerimiz yer alacak ve biz bu nesneleri kontrol etmeye çalışacağız.Şimdi Bu projede Unity‘de yer alan Standart Assets dosyalarını kullanacağız.Bu yüzden eğer yeni bir proje oluşturuyorsanız, menüde yer alan Asset Packages butonuna tıklıyoruz.Daha sonra açılan pencerede Standart Assets seçimini yapıyoruz.Böylece projemiz oluşurken, gerekli dosyalarda eklenmiş oluyor.

Şimdi sahnemize yeni bir Plane nesnesi ekliyoruz.Bu nesne bizim zeminimizi oluşturacak.Daha sonra Hierarchy penceresinde sağ tıklayarak Create Empty seçimini yapıyoruz.Bu nesnemiz bir ebeveyn (parent) nesnesi olacak.Bu nesnemizin içerisinde sahnemizde olan ve etkileşime girebileceğimiz tüm nesneler (child objects) yer alacak.Şimdi bu boş nesnemizin ismini değiştiriyoruz.Daha sonra yeni bir etiket oluşturarak, nesnemize tanımlıyoruz.

Unity-Yer-Çekimi-Silahı-Yapımı-Raycast-Rigidbody-How-to-Make-the-Gravity-Gun-in-Unity

Hierarchy penceresindeki bu boş nesnemizin üzerine sağ tıklayarak sırasıyla, 3D Object > Cube seçimini yapıyoruz.Sonra sahnemizde yer alan bu küp nesnemizi seçiyoruz ve sağ tarafta yer alan Inspector penceresine geliyoruz.Inspector penceresinde en altta inerek, Add Component butonuna basıyoruz.Açılan pencerede yer alan arama kutusuna Rigidbody yazıyoruz ve nesnemize bu bileşeni ekliyoruz.

Çünkü etkileşime geçmek istediğimiz nesnelerde fizik unsurları olması gerektiği için Rigidbody bileşeni eklenmesi gerekiyor.Evet sahnemiz hazır.Şimdi sahnemizde dolaşmak için, FPSController nesnesine ihtiyacımız var.Az önce Unity‘de yeni bir proje oluştururken bize gerecek dosyaları da projemize dahil etmiştik.Project penceresinde sırasıyla, Standard Assets > Characters > FirstPersonCharacter > Prefabs yolunu izliyoruz.Daha sonra FPSController prefab nesnemizi sahnemize sürükleyerek bırakıyoruz.

Unity-Yer-Çekimi-Silahı-Yapımı-Raycast-Rigidbody-How-to-Make-the-Gravity-Gun-in-Unity

3D modelimizi sahnemize ekleyelim.

Şimdi sahnede rahatça dolaşabildiğimize göre bize kullanabileceğimiz bir 3d silah modeli gerekiyor.Ancak yazımım başında da dediğim gibi, ben Gravity Gun yerine Gravity Gloves yapmayı tercih ettim.Bu nedenle de, internetten bulduğumuz bir 3d el modeli yada 3d kol modelini indirip projemize dahil ediyoruz.Bu arada siz bir silah modelini de tercih edebilirsiniz.Sonuçta bu sadece projemize görsel anlamda bir katkı sağlayacaktır.Birde neden sadece el modeli eklemedik diye merak edenler için, kol daha iyi durur diye düşündüm.

Unity-Yer-Çekimi-Silahı-Yapımı-Raycast-Rigidbody-How-to-Make-the-Gravity-Gun-in-Unity

Etkileşime geçeceğimiz nesneleri çekerken, bir noktada durması gerekiyor.Bunu çeşitli yöntemler ile sağlayabiliriz.Fakat amacımız en basit yoldan ilerlemek olduğu için, diğer yöntemleri tercih etmeyeceğiz.Şimdi Hierarchy penceresinde yer alan kol modelimize (yada siz hangi modeli kullanıyorsanız) sağ tıklıyoruz ve 3D Object > Cube seçimini yapıyoruz.Burada dikkat etmemiz gereken şey, eklediğimiz Cube nesnemizin sahnemizdeki 3d modelimizin altında (child object) yer alması gerekiyor.

Unity-Yer-Çekimi-Silahı-Yapımı-Raycast-Rigidbody-How-to-Make-the-Gravity-Gun-in-Unity

Eklediğimiz bu Cube nesnemizi, Scene penceremizde istediğimiz konuma getiriyoruz.Konumunu ayarladıktan sonra Inspector penceresine geliyoruz.Burada Mesh Filter ve Mesh Renderer bileşenlerini siliyoruz.Bunu yaptıktan sonra, elimizde boş bir nesne oluyor.Neden başta bir Create Empty eklemedik diyebilirsiniz.Çünkü hizalamayı düzgün bir şekilde yapmak için bize bir 3d model gerekiyordu.Diğer türlü hizalamayı doğru yapmak için uğraşabilirdik.Son olarak bu nesnemiz için yeni bir etiket oluştırıyoruz.

Crosshair ikonumuzu sahnemize ekleyelim.

Hizalama demişken, crosshair ikonumuzu sahnemize eklemeye sıra geldi.Bu ikonu eklemek tamamen sizin tercihinize kalmış durumdadır.Biz raycast ile ışın atacağımız için fare ile tıkladığımız noktaları görmek daha iyi olur diye düşünüyorum.Bu arada projelerimize crosshair ikonu eklemek gayet basit bir işlemdir.Şimdi Hierarchy penceremizde sağ tıklıyoruz.Açılan pencerede sırasıyla UI > Canvas yolunu izliyoruz.

Daha sonra Canvas nesnemizi seçiyoruz ve Inspector penceresine geliyoruz.Burada ilk olarak Canvas Scaler bileşenimize geliyoruz.UI Scale Mode ayarını Scale With Screen Size olarak değiştiriyoruz.Bu seçeneği seçtiğimiz zaman aşağıya yeni bir ayar açılıyor.Reference Resolution ayarında yer alan X ve Y eksenlerine bir değer girmemiz gerekiyor.Buraya 1920 x 1080 değerini giriyoruz.Bunu yapmamızın nedeni ise, UI elemanlarımızın her çözünürlükte sabit kalması içindir.

Unity-Yer-Çekimi-Silahı-Yapımı-Raycast-Rigidbody-How-to-Make-the-Gravity-Gun-in-Unity

Şimdi Hierarchy penceresinde bulunan Canvas nesnemize sağ tıklıyoruz ve UI > Image yolunu izliyoruz.Şu an ekranda sadece bir kare olduğunu görüyoruz.Bunu kendi seçtiğimiz crosshair ikonu ile değiştirmemiz gerekiyor.Bunun için, internette bulduğumuz bir ikonu indiriyoruz ve projemize dahil ediyoruz.Burada dikkat etmeniz gereken şey ise, indirdiğiniz ikonun arkaplanının saydam olmasıdır.

İkonu Project penceremizde seçiyoruz ve Inspector penceremize geliyoruz.Texture Type ayarını Sprite (2D and UI) olarak belirliyoruz.Daha sonra yaptığımız değişikliği kaydediyoruz.Şimdi Hierarchy penceremizde bulunan Image nesnemizi seçiyoruz.Inspector penceremizde bulunan Image bileşeninin altındaki Source Image bölümüne, Project penceremizdeki crosshair ikonunu sürükleyerek bırakıyoruz.

Şimdi bu ikonu ekranımızda tam ortaya gelecek şekilde ayarlamamız gerekiyor.Bunu yapmak içinde Rect Transform bileşenine geliyoruz.Hemen altta yer alan hizalama yardımcımıza tıklıyoruz.Açılan pencerede, klavyeden alt tuşuna basılı tutarak, orta noktayı seçiyoruz.Artık crosshair ikonumuz ekranımızın tam ortasında yer alacaktır.

Unity-Yer-Çekimi-Silahı-Yapımı-Raycast-Rigidbody-How-to-Make-the-Gravity-Gun-in-Unity

Kodlarımızı yazmaya başlayalım.

Evet projemizin sahne kurulumu bittiğine göre artık kodlama tarafına geçebiliriz.Project penceresinde yeni bir C# Script dosyası oluştutuyoruz.Oluşturduğumuz bu script dosyasına yeni bir isim veriyoruz ve editörümüzün yardımı ile bu dosyayı açıyoruz.

public Camera AnaKamera;
public GameObject SabitNokta;
public GameObject YakalananNesne;
public Rigidbody NesneninFizigi;

Evet script dosyamızda kullanacağımız değişkenleri oluşturmakla başlıyoruz.İlk satırda bir Camera değişkeni oluşturuyoruz.Böylece raycast ışınları için kameramızı kontrol edebileceğiz.İkinci ve üçüncü satırlarda bir GameObject değişkenleri oluşturuyoruz.İkinci satırda, etkileşime geçeceğimiz nesnelerin geleceği boş nesnemiz yer alırken, üçüncü satırda elimizde tuttuğumuz nesneleri kontrol edebilmek için bir değişkenimiz bulunuyor.Son satırda ise, bir Rigidbody değişkeni yer alıyor.Bu değişkenle ise, elimizdeki nesnelerin Rigidbody bileşenlerini kontrol edebileceğiz.

public int CekmeKuvveti;
public float ItmeKuvveti;
public float MinItmeKuvveti;
public float MakItmeKuvveti;
public Vector3 NesneyiDondur;
public bool NesneAktif;
private GameObject Kutular;

Değişkenlerimizi oluşturmaya devam ediyoruz.İlk satırda int (integer) türünde bir değişken oluşturuyoruz.Bu değişken ile çektiğimiz nesnelerin bize doğru gelme hızını kontrol edebileceğiz.İkinci ve dördüncü satırlar arasında float türünde değişkenler oluşturuyoruz.Bu değişkenler ile, etkileşime gireceğimiz nesnelere karşı uygulayacağımız minimum ve maksimum kuvveti belirleyebileceğiz.

Beşinci satırda Vector3 değişkeni oluşturuyoruz.Bu değişkenle ise, nesneleri çekerken bir titreme efekti yapacağız.Altıncı satırda ise, bool türünde bir değişken oluşturuyoruz.Bu değişkenle, elimizde bir nesne olup olmadığını kontrol edeceğiz.Son satırda ise, GameObject değişkeni oluştuyoruz.Bu değişkeni ise, sahnemizdeki tüm kutuları hiyerarşik olarak kontrol etmek için kullanacağız.

AnaKamera = Camera.main;
SabitNokta = GameObject.FindWithTag("sabit");
ItmeKuvveti = MinItmeKuvveti;
NesneyiDondur = Vector3.zero;
NesneAktif = false;
Kutular = GameObject.FindWithTag("kutular");

Şimdi bu kodları start() fonksiyonumuzun içerisine yazıyoruz.İlk satırda “AnaKamera” değişkenimize, sahnemizdeki kameramızı tanımlıyoruz.İkinci satırda “SabitNokta” değişkenimize, etiketi “sabit” olan nesnemizi tanımlıyoruz.Üçüncü satırda “ItmeKuvveti” değişkenimize, yine oluşturmuş olduğumuz “MinItmeKuvveti” değişkenimizin değerini eşitliyoruz.

Dördüncü satırda “NesneyiDondur” Vector3 değişkenimizin koordinatlarını sıfırlıyoruz.Beşinci satırda “NesneAktif” bool değişkenimizin değerini false olarak belirliyoruz.Son satırda ise “Kutular” değişkenimize, etiketi “kutular” olan nesnemizi tanımlıyoruz.

Raycast ışını atmak için fonksiyonumuzu oluşturalım.

void NesneyiBul() {
   RaycastHit hit; 
   Ray ray = AnaKamera.ScreenPointToRay(Input.mousePosition); 
   if ( Physics.Raycast (ray, out hit, 200.0f)) {
      Debug.DrawLine(ray.origin, hit.point, Color.red, 0.1f);
      ...
   }
}

Evet şimdi yeni bir fonksiyon oluşturuyoruz.Bu fonksiyonumuzun amacı, etkileşime geçeceğimiz nesneleri bulmak olacaktır.İkinci satırda bir RaycastHit değişkeni oluşturuyoruz.Önceki derslerimde anlatmıştım fakat yerine geldiği için tekrar belirtmekte fayda var diye düşünüyorum.RaycastHit kısaca, Raycast ışınlarından gelen bilgileri toplamak için kullanılır.

Üçüncü satırda, bir Ray değişkeni oluşturuyoruz.Ve bu Ray değişkenimize ScreenPointToRay fonksiyonunu kullanarak, fare hareketlerimizi (Input.mousePosition) tanımlıyoruz.Tabi attığımız ışınların başlangıç noktası içinde “AnaKamera” değişkenimize tanımlı kameramızı kullanıyoruz.Böylece imlecimizin olduğu yere bir ışın atmamızı sağlıyoruz.

Dördüncü satırda yeni bir koşul oluşturuyoruz.Physics.Raycast ile sahnedeki nesnelerin, ışınımız (Ray) tarafından vurulup vurulmadığını kontrol ediyoruz.Beşinci satırda ise DrawLine fonksiyonunu kullanarak, sahnemizde attığımız ışınların başlangıç ve bitiş noktaları arasına bir çizgi çiziyoruz.Böylece Scene penceresinde ışınlarımızın nasıl bir yol izlediğini görebiliyoruz.

if (hit.collider.CompareTag("kutu")) {
   YakalananNesne = hit.collider.gameObject;
   NesneninFizigi = YakalananNesne.GetComponent<Rigidbody>();
   YakalananNesne.transform.parent = SabitNokta.transform;
   YakalananNesne.transform.LookAt (SabitNokta.transform.position);
   NesneninFizigi.constraints = RigidbodyConstraints.FreezeAll;
   NesneAktif = true;
}

İlk fonksiyonumuzun devamına bu kodları yazarak devam ediyoruz.İlk satırda yeni bir kouş oluşturuyoruz.Eğer ışınımızın çarptığı nesnenin etiketi “kutu” ise koşulumuz true oluyor.Burada gördüğümüz CompareTag fonksiyonu ise, aynı etikete sahip tüm nesneleri kontrol etmemize yarıyor.Yani belirlemiş olduğumuz etiket dışında bir nesneye ışın atarsak, herhangi bir etkileşime geçiyoruz.

İkinci satırda “YakalananNesne” değişkenimize, ışın attığımız nesneyi tanımlıyoruz.Üçüncü satırda “NesneninFizigi” değişkenimize, ışın attıığımız nesnenin Rigidbody bileşenini tanımlıyoruz.Dördüncü satırda “YakalananNesne” değişkenimizde tanımlı nesnemizi, “SabitNokta” değişkenimize tanımlı nesnenin child pozisyonuna getiriyoruz.Yani Hierarchy penceresinde bu nesnemizin alt nesnesi haline geliyor.

Beşinci satırda “YakalananNesne” değişkenimize tanımlı nesnemizi, LookAt fonksiyonu yardımı ile “SabitNokta” değişkenimize tanımlı nesnemize doğru bakmasını sağlıyoruz.Altıncı satırda “NesneninFizigi” değişkenimize tanımlı Rigidbody bileşeninin “constraints” ayarını, tüm eksenler boyunca dönüş ve hareketi kısıtlıyoruz.Bunu yapmak içinde, FreezeAll kodundan yararlanıyoruz.Son satırda ise, “NesneAktif ” bool değişkenimizin değerini true olarak değiştiriyoruz.

Nesneyi çekmek için fonksiyonumuzu oluşturalım.

void NesneyiCek() {
   if (NesneAktif) {
      YakalananNesne.transform.position = Vector3.Lerp (YakalananNesne.transform.position, SabitNokta.transform.position, CekmeKuvveti * Time.deltaTime);
      float xEkseni = Random.Range(-1.5f, 1.5f);
      float yEkseni = Random.Range(-1.5f, 1.5f);
      float zEkseni = Random.Range(-1.5f, 1.5f);
      NesneyiDondur = new Vector3(xEkseni, yEkseni, zEkseni);
      YakalananNesne.transform.Rotate(NesneyiDondur);
   }
}

Evet şimdi yeni bir fonksiyon daha oluşturuyoruz.Bu fonksiyonumuzun görevi ise, attığımız Raycast ışınları doğrultusunda yakaladığımız nesneleri çekmek olacaktır.İkinci satırda yeni bir koşul oluşturuyoruz.Eğer “NesneAktif” bool türündeki değişkenimizin değeri true ise, koşulumuz aktif oluyor.Üçüncü satırda “YakalananNesne” değişkenimize tanımlı nesnemizi, Lerp fonksiyonu yardımı ile hedef noktaya doğru ilerletiyoruz.

Parantez içerisinde aldığı parametrelere bakacak olursak, hedef noktanın “SabitNokta” değişkenine tanımlı nesne olduğunu görüyoruz.Bu hedef sahnemizde nerede olursa olsun, ışın ile yakaladığımız nesneler bunu referans alacaktır.Dördüncü ve altıncı satırlar arasında yeni float değişkenleri oluşturuyoruz.Bu değişkenlerin değerlerine, Random fonksiyonu yardımı ile belirtilen sınırlar içerisinde rastgele bir tanesinin tanımlıyoruz.

Yedinci satırda ise “NesneyiDondur” Vector3 değişkenimize, rastgele oluştuduğumuz bu eksen değerlerini tanımlıyoruz.Sekizinci satırda “YakalananNesne” değişkenimize tanımlı nesnemizi, Rotate fonksiyonu yardımı ile parantez içerisindeki koordinatlara göre döndürüyoruz.Tabi burada bir ayrıntı bulunuyor.Koşulumuz true değerini aldığı süre boyunca x, y ve z eksenlerinin değerleri sürekli değişeceği için, çektiğimiz nesnenin de rotate değerleri sürekli değişecektir.Böylece bir titreme efekti yapmış olacağız.

Nesneyi bırakma ve fırlatma fonksiyonlarımızı oluşturalım.

void NesneyiBirak() {
   YakalananNesne.transform.parent = Kutular.transform;
   YakalananNesne = null;
   NesneAktif = false;
   NesneninFizigi.constraints = RigidbodyConstraints.None;
}

Evet şimdi yeni bir fonksiyon daha oluşturuyoruz.Bu fonksiyonumuzun amacı, yakalamış olduğumuz nesneleri elimizden bırakmak olacaktır.İkinci satırda “YakalananNesne” değişkenimizde tanımlı olan nesnemizi, “Kutular” değişkenimize tanımlı nesnenin child pozisyonuna getiriyoruz.Yani Hierarchy penceresinde bu nesnemizin alt nesnesi haline geliyor.

Üçüncü satırda, “YakalananNesne” değişkenimize tanımlı nesnemizi kaldırıyoruz.Yani değişkenimizin değerini null yapıyoruz.Dördüncü satırda, “NesneAktif” bool türündeki değişkenimizin değerini false yapıyoruz.Böylece artık yeni nesneler çekebileceğiz.Beşinci satırda ise, “NesneninFizigi” değişkenimize tanımlı Rigidbody bileşeninin “constraints” ayarını, tüm eksenler boyunca dönüşünü ve hareketini serbest bırakıyoruz.

void NesneyiFirlat() {
   ItmeKuvveti = Mathf.Clamp(ItmeKuvveti, MinItmeKuvveti, MakItmeKuvveti);
   NesneninFizigi.AddForce(AnaKamera.transform.forward * ItmeKuvveti, ForceMode.Impulse);
   NesneyiBirak();
}

Şimdi nesneyi fırlatmak için bir fonksiyon oluşturuyoruz.İkinci satırda “ItmeKuvveti” değişkenimizin değerini, “MinItmeKuvveti” değişkenimizin değeri ile “MakItmeKuvveti” değişkenimizin değeri arasında sınırlıyoruz.Bunu yapmak için, Mathf.Clamp fonksiyonundan yararlanıyoruz.Üçüncü satırda “NesneninFizigi” değişkenimize tanımlı Rigidbody bileşenimizi kullanarak, elimizdeki nesnemize belirli bir yonde ve hızde kuvvet uyguluyoruz.

Bunu yapmak içinde, AddForce fonksiyonumuzu kullanıyoruz.Parantez içerisinde yer alan ForceMode.Impulse kodu ise, fırlatmış olduğumuz nesnenin kütlesi kadar bir kuvvet uygulamak için kullanıyor.Dördüncü satırda ise, “NesneyiBirak()” fonksiyonumuzu yazıyoruz.Bu fonksiyonu eğer yazmaksak bu sefer nesneyi fırlattığımız zaman hem “constraints” değerleri kilitli kalır hemde diğer mekanikler doğru düzgün çalışmaz.

if (Input.GetMouseButtonDown(0) && !NesneAktif) {
   NesneyiBul();
}
if (Input.GetMouseButtonUp(0) && NesneAktif) {
   NesneyiBirak();
}

Şimdi bize gerekli olan fonksiyonları oluşturduk.Şimdi bunları kullanmamız gerekiyor.Bunun içinde update() fonksiyonumuzun içerisine bu kodları yazıyoruz.İlk satırda yeni bir koşul oluşturuyoruz.Eğer faremizin sol tuşuna basarsak ve “NesneAktif” değişkenimizin değeri false olursa koşulumuz aktif oluyor.İkinci satıra bakacak olursak, “NesneyiBul()” fonksiyonumuzu çağırıyoruz.

Dördüncü satırda yeninden bir koşul oluşturuyoruz.Eğer faremizin sol tuşunu bırakırsak ve “NesneAktif” değişkenimizin değeri true olursa koşulumuz aktif oluyor.Beşinci satıra bakacak olursak, bu sefer “NesneyiBirak()” fonksiyonumuzu çağırıyoruz.

      if (Input.GetMouseButton(1) && NesneAktif) {
         ItmeKuvveti += 0.5f;
      }
      if (Input.GetMouseButtonUp(1) && NesneAktif) {
         NesneyiFirlat();
         ItmeKuvveti = MinItmeKuvveti;
      }
      NesneyiCek();

Şimdi bu kodları update() fonksiyonumuzun devamına yazıyoruz.İlk satırda yeninden bir koşul oluşturuyoruz.Eğer faremizin sağ tuşunu basılı tutarsak ve “NesneAktif” değişkenimizin değeri true olursa koşulumuz aktif oluyor.İkinci satırda “ItmeKuvveti” değişkenimizin değerini, sağ tuşa basılı tuttuğumuz sürece “0.5f” arttırıyoruz.Böylece ne kadar basılı tutarsak, elimizdeki nesneyi o kadar kuvvetli fırlatmış oluyoruz.

Dördüncü satırda yeninden bir koşul oluşturuyoruz.Eğer faremizin sağ tuşunu bırakırsak  ve “NesneAktif” değişkenimizin değeri true olursa koşulumuz aktif oluyor.Beşinci satırda “NesneyiFirlat()” fonksiyonumuzu çağırıyoruz.Altıncı satırda ise “ItmeKuvveti” değişkenimizin değerini, “MinItmeKuvveti” değişkenimizin değerine eşitliyoruz.Böylece sağ tuşu her bıraktığımızda fırlatma kuvvetimiz eski değerine dönüyor.Sekizinci satırda ise, “NesneyiCek()” fonksiyonumuzu çağırıyoruz.Böylece Raycast ışınlarımızı göndermeye hazır oluyoruz.

Evet projemizin sonuna gelmiş bulunuyoruz.Şimdi Project penceresinde oluşturmuş olduğumuz C# Script dosyamızı sürükle, Hierarchy penceresinde bulunan “FPSController” nesnemizin üzerine bırakıyoruz.Daha sonra Inspector penceresinde script üzerindeki “CekmeKuvveti”, “MinItmeKuvveti” ve “MakItmeKuvveti” bölümlerini istediğimiz gibi dolduruyoruz.Artık test edebiliriz.Evet bir sonraki yazıda görüşmek üzere…

Unity-Yer-Çekimi-Silahı-Yapımı-Raycast-Rigidbody-How-to-Make-the-Gravity-Gun-in-Unity