Unity - Nesneleri Hareket Ettirme

Unity - Nesneleri Hareket Ettirme

2023-02-22    

Unity de genellikle bir nesnenin ölçeğini, dönüşünü ve konumunu yönetmek için Transform bileşeni kullanılır.

Unity - Nesneleri Hareket Ettirme

Fakat bunu yapmanın birçok farklı yolu vardır.

Birçok durumda Transform bileşeni üzerinden bunu gerçekleştirebilsek bile aslında yapmak istediğimiz hareketin türüne göre bu durum değişebilir.

Nesne Nasıl Taşınır?

Bunu nesnenin Transfrom bileşenindeki konum özelliğine yeni bir değer ekleyerek yapabiliriz.

// Nesneyi belirtilen konuma taşır.
transform.position = new Vector3(4, 0, 2);

Konum özelliğine yeni bir değer ekleyerek nesneyi belirli bir yönde hareket ettirebiliriz.

// Nesneyi 1 birim yukarı taşır.
transform.position += new Vector3(0, 1, 0);

Trasnform.Translate() Nasıl Kullanılır?

Unity de transform.Translate() metodu bir nesneyi mevcut konumu ve yönüne göre belirtilen değerde hareket sağlar.

void Start()
{
    // Nesneyi dört birimi ileri taşır.
    transform.Translate(0,0,4);
}

Burada bilmemiz gereken bu metod ile kendi yerel alanına (local position) göre haraket sağlanır.

Her iki yöntemi kullanırken bilmemiz gereken bir durum söz konusudur. cihazlarda farklı kare hızlarından dolayı farklı hareket hızı oluşmaması için aşağıdaki yöntem kullanılır. Time.deltaTime son kareden bu yana geçen zaman değerini verir.

transform.position = Vector3.forward * Time.deltaTime;

Tabi çoğu durumda hızı ve hareket vektörünü ayrı ayrı kontrol etmek daha kullanışlı bir yöntemdir.

transform.position = Vector3.forward * speed * Time.deltaTime;

Unity de kullanıma hazır birim vektör veya normalleştirilmiş vektör'ler vardır. Bunlar, dünya uzayındaki ortak yönlerle ilgilidir.

Vector3.forward;   // (0, 0,  1)
Vector3.back;      // (0, 0, -1)
Vector3.up;        // (0, 1,  0)
Vector3.down;      // (0, -1, 0)
Vector3.right;     // (1,  0, 0)
Vector3.left;      // (-1, 0, 0)

Bir nesnenin yerel konumu ve dönüşüne göre normalleştirilmiş yön vektörleride vardır.

transform.forward:  // forward
-transform.forward; // back
transform.up;       // up
-transform.up;      // down
transform.right;    // right
-transform.right;   // left

Bir nesneyi halihazırdaki yönü ile hareket vermek için aşağıdaki gibi yöntem uygulabilir.

transform.position += transform.forward * speed * Time.deltaTime;

Klavye ile Taşıma?

Bir nesneyi klavye veya başka herhangi bir giriş aygıtıyla (joystick, gamepad vs) hareket ettirmek için uygulamak istediğiniz hareket yönünü giriş aygıtından alınan vektör (Input.Axis) ile çarparız.

Her Input Axis, -1 ile 1 arasında bir değer döndürür. Döndürülen bu değeri bir hareket vektörü oluşturmak için kullanabileceğiniz anlamına gelir.

public float speed = 2;
void Update()
{
    float x = Input.GetAxis("Horizontal");
    float z = Input.GetAxis("Vertical");
    Vector3 movement = new Vector3(x, 0, z);
    transform.Translate(movement * speed * Time.deltaTime);
}

Clamp Magnitude Nedir?

Bir nesneyi yatay ve dikey eksenler kullanarak hareket ettirirken, tek bir eksenin hareket hızından daha hızlı çapraz hareket oluşturmak mümkündür.

Bunun nedeni, 1'lik bir ileri vektör ve 1'lik bir yan vektörden oluşturulan vektörün uzunluğunun, her ikisinden de daha uzun yani yaklaşık 1,4 olmasıdır.

Örneğin bir klavyede ileri ve sağa tuşa aynı anda basmanın, oynatıcıyı yalnızca ileri veya yanlara doğru hareket etmekten 1,4 kat daha hızlı hareket ettirerek düzensiz 8 yönlü harekete neden olacağı anlamına gelir.

Bu durumu oluşan vektörü normalleştirerek düzeltebilirisin.

Vector3 movement = new Vector3(x, 0, z).normalized;

Bu işe yarasa da vektörün uzunluğunun her zaman bir olduğu, yani analog hareketler veya Unity'nin dijital eksen girişlerine uyguladığı kademeli hızlanma gibi birden küçük herhangi bir değerin her zaman bire yuvarlandığı anlamına gelir.

Bu durumda 1'den küçük değerleri de desteklemek için bunun yerine Clamp Magnitude kullanarak vektörün uzunluğunu sınırlayabilirsiniz .

public float speed = 2;
void Update()
{
    float x = Input.GetAxis("Horizontal");
    float z = Input.GetAxis("Vertical");
    Vector3 movement = new Vector3(x, 0, z);
    movement = Vector3.ClampMagnitude(movement, 1);
    transform.Translate(movement * speed * Time.deltaTime);
}

Bu vektörün uzunluğunu bir ile sınırlayacak ve daha düşük değerleri olduğu gibi bırakarak tüm yönlerde bile analog 8 yollu hareket oluşturmanıza izin verecektir.

Kameraya Göre Hareket?

Nesnenin ileri vektörü yerine kameranın ileri vektörünü kullanarak Unity'de bir nesneyi kameranın konumuna göre hareket ettirmek mümkündür.

public float speed = 2;
void Update()
{
    Transform camTransform = Camera.main.transform;
    Vector3 forwardMovement = camTransform.forward * Input.GetAxis("Vertical");
    Vector3 horizontalMovement = camTransform.right * Input.GetAxis("Horizontal");
    Vector3 movement = Vector3.ClampMagnitude(forwardMovement + horizontalMovement,1);
    transform.Translate(movement * speed * Time.deltaTime, Space.World);
}

Hareket kameranın konumuna göre olduğundan bunun çalışması için Translate() işlevindeki Relative To parametresinin Space.World olarak ayarlanması gerekir.

Ancak bir sorun var.

Kameranın ileri vektörü, bazı durumlarda, oyuncuya dik bir açıyla bakıyor olabilir; bu durumda uygulanan herhangi bir hareketin beklediğiniz gibi düz bir düzlem yerine aşağı doğru bir hareket oluşturacağı anlamına gelir.

Bunu düzeltmek için göz ardı etmek istediğiniz yönü devre dışarıda bırakarak kamera ile nesne arasındaki yönü manuel olarak hesaplamanız gerekir.

Bir yönü hesaplamak için tek yapmanız gereken kamernaın konumunu hedefin konumundan çıkarmak ve ardından sonucu normalleştirmek.

Vector3 direction = (transform.position - Camera.main.transform.position).normalized;

Burada dikkat etmeniz gereken nesne ile kamera yükseklik farklarının eşitlemek olmalıdır.

Transform camTransform = Camera.main.transform;
Vector3 camPosition = new Vector3(camTransform.position.x,transform.position.y,camTransform.position.z);
Vector3 direction = (transform.position - camPosition).normalized;

Böylelikle düz bir düzlemde kameraya göre hareketi hesaplamak için oluşturulan düzeltilmiş yönü kullanabilirsiniz.

public float speed = 2;
void Update()
{
    Transform camTransform = Camera.main.transform;
    Vector3 camPosition = new Vector3(camTransform.position.x, transform.position.y, camTransform.position.z);
    Vector3 direction = (transform.position - camPosition).normalized;
    Vector3 forwardMovement = direction * Input.GetAxis("Vertical");
    Vector3 horizontalMovement = camTransform.right * Input.GetAxis("Horizontal");
    Vector3 movement = Vector3.ClampMagnitude(forwardMovement + horizontalMovement, 1);
    transform.Translate(movement * speed * Time.deltaTime, Space.World);
}

Nesneyi Unity'de Bir Konuma Taşıma?

Genel olarak konuşursak, bir nesneyi belirli bir konuma taşımanın iki farklı yolu vardır. Kullanacağınız yöntem, nesnenin hareketini zamana veya hıza göre nasıl kontrol etmek istediğinize bağlı olacaktır.

  • Hız; nesnenin belirli bir hızda bir hedefe doğru hareket ettirerek
  • Zaman; İki nokta arasında belirli bir zamana göre haraket ettirerek

Belirli bir hız ile bir konuma taşıma

MoveToward() işlevini kullanarak bir nesneyi başka bir nesneye veya sahnedeki belirli bir konuma taşımak mümkündür.

public Vector3 targetPosition;
public float speed=10;
void Update()
{
    transform.position = Vector3.MoveTowards(transform.position, targetPosition, speed * Time.deltaTime);
}

Hareket başlarken ve biterken hareketi yumuşatan SmoothDamp() işlevi kullanılabilir.

public Vector3 targetPosition;
public float smoothTime = 0.5f;
public float speed = 10;
Vector3 velocity;
void Update()
{
    transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref velocity, smoothTime, speed);
}

Bu nesnenin konumunu SmoothDamp() işlevinin Vector 3 sonucuna ayarlayarak hedefi geçerli konumu ve işlevin çerçeveler (frame) arasında nesnenin hızını işlemek için kullandığı bir referans Vector 3 değerini ileterek çalışır.

Bu oyuncuyu bir kamerayla takip ederken olduğu gibi hedef konumun bir andan diğerine değişebileceği düzgünleştirilmiş sürekli hareket için yararlı olabilir.

Kamera takip betiği

public Transform player;
public float cameraDistance = 5;
public float cameraHeight = 3;
public float smoothTime = 0.5f;
public float speed = 10;
Vector3 velocity;
void Update()
{
    transform.LookAt(player.transform);
    Vector3 offset = (Camera.main.transform.position - player.position).normalized * cameraDistance;
    Vector3 targetPosition = player.position + offset;
    targetPosition.y = cameraHeight;
    transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref velocity, smoothTime, speed);
}

Bu örnekte kamera 3 birim yükseklikte kalırken ve 5 birim uzaklıkta kalmaya çalışırken oyuncuya doğru yumuşak bir şekilde hareket edecek ve onunla yüzleşmek için dönecektir.

MoveTowards() bir nesneyi dinamik olarak bir konuma doğru hareket ettirmenin bir yolu olarak harika çalışıyor.

Bunun nedeni, hareketin hıza dayalı olması ve hedef konum hareket etse veya bilinmese bile kontrol edilebilmesidir.

Belirli bir süre ile bir konuma taşıma

Lerp veya Lineer Enterpolasyon sıfır ile bir arasında kayan nokta olan 't' konum değerine dayalı olarak minimum ve maksimum arasında bir değer bulmak için kullanılır .

Döndürülen değer t'nin değerine bağlıdır; burada t 0 ise minimum değer, 1 ise maksimum değeri döndürür.

0 ile 1 arasındaki diğer herhangi bir değer ölçeğin minimum ve maksimum uçları arasında temsili bir değer döndürür.

float lerpedValue = Mathf.Lerp(float minValue, float maxValue, float t);

Tipik olarak Lerp yeni bir değer döndürmek için her karede t'yi artırarak bir değeri belirli bir süre boyunca değiştirmek için kullanılır .

Bu bir rengi değiştirmek, bir ses kaynağını soldurmak veya bir nesneyi iki nokta arasında taşımak için kullanılabilir.

Lerp sırasında geçen süreyi toplam süreye bölerek hesaplanır. Bu t değeri için kullanılabilecek bir 0-1 kayan nokta döndürür ve sadece ne kadar sürmesini istediğinizi seçerek Lerp hareketinin uzunluğunu kontrol etmenize olanak tanır.

float timeElapsed;
float lerpDuration = 3;
float lerpedValue;
void Update()
{
    if (timeElapsed < lerpDuration)
    {
        lerpedValue = Mathf.Lerp(0, 100, timeElapsed / lerpDuration);
        timeElapsed += Time.deltaTime;
    }
}

Vector3.Lerp() metodu bir kayan nokta döndürmek yerine t değerine bağlı olarak dünyada iki nokta arasında bir nokta döndürmesi dışında aynı şekilde çalışır.

Bu açık ve kapalı durumdaki bir kapı gibi bir nesneyi iki farklı konum arasında hareket ettirmek için yararlı olabilir.

public float openHeight = 4.5f;
public float duration = 1;
bool doorOpen;
Vector3 closePosition;
void Start()
{
    // Kapının ilk konumunu kapalı pozisyon olarak ayarlar.
    closePosition = transform.position;
}
void OperateDoor()
{
    StopAllCoroutines();
    if (!doorOpen)
    {
        Vector3 openPosition = closePosition + Vector3.up * openHeight;
        StartCoroutine(MoveDoor(openPosition));
    }
    else
    {
        StartCoroutine(MoveDoor(closePosition));
    }
    doorOpen = !doorOpen;
}
IEnumerator MoveDoor(Vector3 targetPosition)
{
    float timeElapsed = 0;
    Vector3 startPosition = transform.position;
    while (timeElapsed < duration)
    {
        transform.position = Vector3.Lerp(startPosition, targetPosition, timeElapsed / duration);
        timeElapsed += Time.deltaTime;
        yield return null;
    }
    transform.position = targetPosition;
}

Bu bir konumdan diğerine doğrusal bir hareket oluşturur ancak SmoothStep() işlevini kullanarak Lerp'in hareketini yumuşatmak da mümkündür.

Bunu t değerini Lerp'e geçirmeden önce SmoothStep() işleviyle değiştirerek yapılabilir.

Lerp'in başında ve sonunda nesnenin hareketini kolaylaştıracaktır.

float t = Mathf.SmoothStep(0, 1, timeElapsed / moveDuration);

Bonus- Animasyon ile bir konuma taşıma

Örneğin yüzen bir platform gibi bir nesneyi iki nokta arasında taşımak için animasyonu kullanabilirsiniz.

Bunun çalışması için biri başlangıç ​​konumuna diğeri platformun bitiş konumuna ve son olarak tekrar platformun bulunduğu nokta olmak üzere "Anahtar Kare" eklenir.

Hız ve zamana göre yapılan hareketler için kullanılan bu yöntemler Unity'de kontrollü hassas hareketler oluşturmak için iyi çalışır. Ancak bir nesnenin hareketini tam olarak kontrol etmek istemezseniz ne olur?

Bunun yerine fiziksel güçleri kullanarak hareket yaratmak için bir nesneyi itmeyi, çekmeyi veya fırlatmayı tercih ederseniz ne yapmanız gerekir?

Fizik ile Taşıma

Unity'deki kullanılan nesnelerin çoğu onlara fiziksel bir varlık sağlayan bir çarpıştırıcı bileşenine (Collider) sahiptir.

Bununla birlikte yalnızca çarpıştırıcıya sahip bir nesnenin statik olduğu kabul edilir yani genel olarak konuşursak hareket etmemesi gerekir.

Bir nesneyi fizik simülasyonu altında fiilen hareket ettirmek için ona yerçekimi gibi fiziksel kuvvetler tarafından hareket ettirilmesine ve hareket ettirilmesine izin veren bir Rigidbody bileşeni eklemeniz gerekir .

Bir fizik nesnesinin Rigidbody bileşenini kullanarak AddForce() işlevi ile hareket ettirebilirsiniz.

Rigidbody.AddForce(Vector3 force);

Bu parametre olarka verdiğimiz vektörün bir hareket miktarı değil fiziksel bir güç olması dışında transform.Translate() işlevine benzer şekilde çalışır.

Sonuç olarak nesnenin ne kadar hareket edeceği kütle, sürtünme ve yerçekimi gibi fiziksel özelliklere bağlı olacaktır.

Bir nesneye fiziksel kuvvet uygulamanın iki ana yolu vardır.

Ya sürekli olarak kuvvet uygulayabilir zaman içinde ivme ve hız kazanabilirsiniz ya da bir nesneyi hareket ettirmek için vurmak gibi bir anda bir dürtüyle birdenbire uygulayabilirsiniz.

Varsayılan olarak, AddForce() işlevi bir roketi kademeli olarak kaldıran bir itici gibi sürekli bir kuvvet uygular.

public Rigidbody rb;
public float forceAmount = 10;
void FixedUpdate()
{
    rb.AddForce(Vector3.up * forceAmount);
}

Nesneye kuvvet uygulamak için Update() metodunu değil, FixedUpdate() metodu kullandığıma dikkat edin. Bunun nedeni, FixedUpdate() metodunun genellikle daha hızlı olan ve kareden kareye değişebilen Update() metodundan farklı bir frekansta çalışan fizik sistemiyle senkronize olarak çağrılmasıdır.

Bu şekilde kullanmak kuvvet uygulamasının etkilediği fizik sistemi ile senkronize olduğu anlamına gelir.

Alternatif olarak, "Impulse Force" modunu kullanarak tek bir patlamada da kuvvet uygulayabilirsiniz .

public Rigidbody rb;
public float forceAmount = 10;
void Start()
{
    rb.AddForce(Vector3.up * forceAmount, ForceMode.Impulse);
}