Unity kullanarak oyuncu hareketini 3D nesnenin yüzeyine nasıl kısıtlayabilirim?


16

Mario Galaxy veya Geometry Wars 3'ünkine benzer bir efekt yaratmaya çalışıyorum, burada oyuncu "gezegenin" yerçekimi etrafında yürürken ayarlanıyor ve yerçekimi gibi nesnenin kenarından düşmüyorlar tek yönde sabitlenmiştir.

resim açıklamasını buraya girin
(kaynak: gameskinny.com )

Geometri Savaşları 3

Yerçekimi olması gereken nesnenin ona doğru diğer katı cisimleri çektiği bir yaklaşım kullanarak aradığım şeye yakın bir şey uygulamayı başardım, ancak Unity için yerleşik fizik motorunu kullanarak, AddForce ve benzerleriyle hareket uygulayarak, Hareketin kendini iyi hissetmesini sağlayamadım. Oyuncu, nesnenin yüzeyinden uçmaya başlamadan oyuncunun yeterince hızlı hareket etmesini sağlayamadım ve bunun için uygun kuvvet ve yerçekimi dengesini bulamadım. Şu anki uygulamam, bulunanların uyarlanmasıdır burada

Çözümün, oyuncuyu yüzeyden ayrılmaları halinde, oyuncunun nesneye topraklanması için muhtemelen fizik kullanacağını hissediyorum, ancak oyuncu topraklandıktan sonra, oyuncuyu yüzeye tutmanın ve fiziği kapatmanın bir yolu olurdu ve oynatıcıyı başka yollarla kontrol edebilirim ama gerçekten emin değilim.

Müzikçaları nesnelerin yüzeyine tutturmak için ne tür bir yaklaşım izlemeliyim? Çözümün 3B alanda (2D'nin aksine) çalışması ve Unity'nin ücretsiz sürümü kullanılarak uygulanabilmesi gerektiğini unutmayın.



Duvarlarda yürümeyi aramayı bile düşünmemiştim. Bir göz atacağım ve bunların yardımcı olup olmadığını göreceğim.
SpartanDonut

1
Bu soru özellikle bunu Unity'de 3D olarak yapmakla ilgiliyse, düzenlemelerle daha açık hale getirilmelidir. (O zaman mevcut olanın tam bir kopyası olmazdı .)
Anko

Bu benim de genel hislerim - bu çözümü 3D'ye adapte edip edemeyeceğimi ve bir cevap olarak gönderip gönderemeyeceğimi göreceğim (ya da bir başkası beni yumrukla yenebiliyorsa ben de iyiyim). Daha net olmak için sorumu güncellemeye çalışacağım.
SpartanDonut

Potansiyel olarak yararlı: youtube.com/watch?v=JMWnufriQx4
ssb

Yanıtlar:


7

Öncelikle bu blog gönderisinin yardımıyla ihtiyaç duyduğum şeyi başardım bulmacanın yüzey yakalama parçası için yazısının ve oyuncu hareketi ve kamera için kendi fikirlerimi buldum.

Oynatıcıyı Nesnenin Yüzeyine Yapıştırma

Temel kurulum, her ikisi de küre çarpıştırıcıları bağlı olan büyük bir küreden (dünya) ve daha küçük bir küreden (oynatıcıdan) oluşur.

Yapılan işin büyük kısmı aşağıdaki iki yöntemdeydi:

private void UpdatePlayerTransform(Vector3 movementDirection)
{                
    RaycastHit hitInfo;

    if (GetRaycastDownAtNewPosition(movementDirection, out hitInfo))
    {
        Quaternion targetRotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
        Quaternion finalRotation = Quaternion.RotateTowards(transform.rotation, targetRotation, float.PositiveInfinity);

        transform.rotation = finalRotation;
        transform.position = hitInfo.point + hitInfo.normal * .5f;
    }
}

private bool GetRaycastDownAtNewPosition(Vector3 movementDirection, out RaycastHit hitInfo)
{
    Vector3 newPosition = transform.position;
    Ray ray = new Ray(transform.position + movementDirection * Speed, -transform.up);        

    if (Physics.Raycast(ray, out hitInfo, float.PositiveInfinity, WorldLayerMask))
    {
        return true;
    }

    return false;
}

Vector3 movementDirectionBeni ilk başta anlamaya yönelik parametre sesler gibi, biz bu örnekte nispeten basit sona erdi ederken, bu çerçevede bizim oyuncu hareketli ve bu vektörü hesaplanması olacak yönü biraz zor oldu. Daha sonra daha fazlası, ancak oyuncunun bu kareyi hareket ettirdiği yönde normalleştirilmiş bir vektör olduğunu unutmayın.

Adım attığımızda, yaptığımız ilk şey, oyunculara vektör (-transform.up) doğru yönlendirilen varsayımsal gelecekteki pozisyondan kaynaklanan bir ışının, komut dosyasının genel LayerMask özelliği olan WorldLayerMask'ı kullanarak dünyaya çarpıp çarpmadığını kontrol etmektir. Daha karmaşık çarpışmalar veya birden fazla katman istiyorsanız, kendi katman maskenizi oluşturmanız gerekir. Raycast bir şeye başarılı bir şekilde vurursa, hitInfo, oynatıcının nesnenin üzerinde olması gereken yeni konumunu ve dönüşünü hesaplamak için normal ve vuruş noktasını almak için kullanılır. Söz konusu oyuncu nesnesinin boyutuna ve kaynağına bağlı olarak, oynatıcının konumunun dengelenmesi gerekebilir.

Son olarak, bu gerçekten sadece test edildi ve muhtemelen sadece küreler gibi basit nesneler üzerinde iyi çalışıyor. Çözümümün önerilerini temel aldığım blog yazısı olarak, daha karmaşık arazilerde hareket ederken çok daha hoş bir geçiş elde etmek için muhtemelen birden fazla rekor kırmak ve konumunuz ve rotasyonunuz için ortalamak isteyeceksiniz. Bu noktada düşünmediğim başka tuzaklar da olabilir.

Kamera ve Hareket

Oyuncu nesnenin yüzeyine yapıştıktan sonra, ele alınması gereken bir sonraki görev hareketti. Başlangıçta oyuncuya göre hareketle başladım ama kürenin direklerinde, yönlerin aniden değiştiği ve oyuncularımın direkleri geçmeme izin vermediği yönünü hızla değiştirdi. Yaptığım şey oyuncularımı kameraya göre hareket ettirmekti.

İhtiyaçlarım için iyi çalışan şey, oyuncuları sadece oyuncuların konumuna göre kesinlikle takip eden bir kameraya sahip olmaktı. Sonuç olarak, kamera teknik olarak dönmesine rağmen, yukarı basmak her zaman oynatıcıyı ekranın üst kısmına, aşağıya doğru ve sola ve sağa doğru hareket ettirdi.

Bunu yapmak için, hedef nesnenin oynatıcı olduğu kamerada aşağıdakiler yürütüldü:

private void FixedUpdate()
{
    // Calculate and set camera position
    Vector3 desiredPosition = this.target.TransformPoint(0, this.height, -this.distance);
    this.transform.position = Vector3.Lerp(this.transform.position, desiredPosition, Time.deltaTime * this.damping);

    // Calculate and set camera rotation
    Quaternion desiredRotation = Quaternion.LookRotation(this.target.position - this.transform.position, this.target.up);
    this.transform.rotation = Quaternion.Slerp(this.transform.rotation, desiredRotation, Time.deltaTime * this.rotationDamping);
}

Son olarak, oynatıcıyı hareket ettirmek için, ana kameranın dönüşümünü kullandık, böylece kontrollerimiz yukarı hareket ettiğinde, aşağı hareket ettiğinde vb. Ve burada konumumuzu dünya nesnesine yapıştıracak olan UpdatePlayerTransform'u çağırıyoruz.

void Update () 
{        
    Vector3 movementDirection = Vector3.zero;
    if (Input.GetAxisRaw("Vertical") > 0)
    {
        movementDirection += cameraTransform.up;
    }
    else if (Input.GetAxisRaw("Vertical") < 0)
    {
        movementDirection += -cameraTransform.up;
    }

    if (Input.GetAxisRaw("Horizontal") > 0)
    {
        movementDirection += cameraTransform.right;
    }
    else if (Input.GetAxisRaw("Horizontal") < 0)
    {
        movementDirection += -cameraTransform.right;
    }

    movementDirection.Normalize();

    UpdatePlayerTransform(movementDirection);
}

Daha ilginç bir kamera uygulamak için, ancak kontroller burada sahip olduğumuzla hemen hemen aynı olacak şekilde, işlenmemiş bir kamerayı veya hareketi temel almak için sadece başka bir kukla nesneyi kolayca uygulayabilir ve daha sonra Oyunun benzemesini istersiniz. Bu, kontrolleri bozmadan nesnelerin etrafında dolaşırken güzel kamera geçişlerine izin verecektir.


3

Bu fikrin işe yarayacağını düşünüyorum:

Gezegen ağının CPU tarafı kopyasını saklayın. Kafes köşe noktalarına sahip olmak, gezegendeki her nokta için normal vektörlere sahip olduğunuz anlamına gelir. Daha sonra normal bir vektörün tam tersi yönde bir kuvvet uygulamak yerine tüm varlıklar için yerçekimini tamamen devre dışı bırakın.

Şimdi, gezegenin normal vektörü hangi noktaya göre hesaplanmalıdır?

En kolay cevap (ki gayet iyi çalışacağından eminim) Newton'un yöntemine benzer şekilde yaklaşmaktır : Nesneler ilk ortaya çıktığında, gezegendeki tüm başlangıç ​​konumlarını bilirsiniz. Her bir nesnenin upvektörünü belirlemek için bu başlangıç ​​konumunu kullanın . Açıkçası yerçekimi zıt yönde olacaktır (doğru down). Bir sonraki çerçevede, yerçekimi uygulamadan önce, nesnenin yeni konumundan eski downvektörüne doğru bir ışın verin . upVektörü belirlemek için bu rayın gezegenle kesişimini yeni referans olarak kullanın . Işının hiçbir şeye çarpmadığı nadir durum, bir şeyin korkunç bir şekilde yanlış gittiğini ve nesnenizi önceki çerçevede olduğu yere geri taşımanız gerektiği anlamına gelir.

Ayrıca, bu yöntemi kullanarak, daha fazla oyuncu başlangıç ​​noktasının gezegenden geldiğini, yaklaşımın daha da kötüleştiğini unutmayın. Bu nedenle, her oyuncunun ayaklarının etrafında bir yerde kökenleri olarak kullanmak daha iyidir. Tahmin ediyorum, ama bence ayakları başlangıç ​​noktası olarak kullanmak oyuncunun daha kolay kullanılmasına ve navigasyonuna neden olacak.


Son not: Daha iyi sonuçlar için aşağıdakileri bile yapabilirsiniz: oyuncunun her karedeki hareketini takip edin (örn. Kullanarak current_position - last_position). Ardından, movement_vectornesnenin uzunluğunun upsıfır olması için kenetleyin. Bu yeni vektör diyelim reference_movement. Önceki taşı reference_pointile reference_movementve ışın izleme kökenli olarak bu yeni noktayı kullanın. Işın gezegene reference_pointçarptıktan sonra (ve eğer), o vuruş noktasına gidin. Son olarak, upbu vektörden yeni vektörü hesaplayın reference_point.

Özetlemek için bazı sözde kod:

update_up_vector(player:object, planet:mesh)
{
    up_vector        = normalize(planet.up);
    reference_point  = ray_trace (object.old_position, -up_vector, planet);
    movement         = object.position - object.old_position;
    movement_clamped = movement - up_vector * dot (movement, up_vector);

    reference_point  += movement_clamped;
    reference_point  = ray_trace (reference_point, -up_vector, planet);
    player.up        = normal_vector(mesh, reference_point);
}

2

Bu yayın yardımcı olabilir. Bunun özü, karakter denetleyicilerini kullanmamanız, ancak fizik motorunu kullanarak kendinizin yapmasıdır. Ardından, oynatıcının altında algılanan normalleri, kafesin yüzeyine yönlendirmek için kullanırsınız.

İşte tekniğe güzel bir genel bakış . Web arama terimleri "3d nesneler mario galakside birlik yürüyüş" gibi daha birçok kaynak vardır.

Ayrıca, bir asker ve bir köpekle ve sahnelerden birinde yürüyen bir motoru olan birlik 3.x'ten bir demo projesi de var. Galaxy tarzı 3d nesneler üzerinde yürüyen gösterdi . Buna runevision tarafından hareket sistemi denir .


1
İlk bakışta demo çok iyi çalışmıyor ve Unity paketinde derleme hataları var. Bu işi yapıp yapamayacağımı göreceğim, ancak daha eksiksiz bir cevap takdir edilecektir.
SpartanDonut

2

rotasyon

Answer.unity3d.com (aslında kendim sordum) ile ilgili bu sorunun cevabına göz atın . Alıntı:

İlk görev, tanımlayan bir vektör elde etmektir. Çiziminizden iki yoldan biriyle yapabilirsiniz. Gezegene bir küre gibi davranabilir ve kullanabilirsiniz (object.position - planet.position). İkinci yol Collider.Raycast () kullanmak ve döndürülen 'hit.normal' kullanmaktır.

İşte bana önerdiği kod:

var up : Vector3 = transform.position - planet.position;
transform.rotation = Quaternion.FromToRotation(transform.up, up) * transform.rotation;

Her güncellemeyi çağırın ve çalışmasını sağlayın. (kodun UnityScript'te olduğunu unutmayın ). C # ile
aynı kod :

Vector3 up = transform.position - planet.position;
transform.rotation = Quaternion.FromToRotation(transform.up, up) * transform.rotation;

Yerçekimi

Yerçekimi için, sorumda tarif ettiğim "gezegen fiziği tekniğimi" kullanabilirsiniz, ancak gerçekten optimize edilmedi. Sadece aklımda olan bir şeydi.
Yerçekimi için kendi sisteminizi yaratmanızı öneririm.

İşte Sebastian Lague'un bir Youtube öğreticisi . Bu çok iyi çalışan bir çözüm.

DÜZENLEME: birlik olarak, gidin Edit > Proje Ayarlar > Fizik ve tüm değerleri ayarlamak Gravity 0 (Ya kaldırmak rijit cisim bütün nesnelerden) önlemek için dahili yerçekimi ile çatışma (sadece oyuncu aşağı çeker ki) özel çözüm. (kapatmak)


Oyuncuyu bir gezegenin merkezine gravürlemek (ve buna göre döndürmek) neredeyse küresel gezegenler için iyi çalışır, ancak roket şeklindeki Mario'nun ilk GIF'de atladığı gibi daha az küresel nesneler için gerçekten yanlış gittiğini hayal edebiliyorum.
Anko

Sadece X ekseni etrafında, örneğin küresel olmayan nesnenin içinde hareket etmesi kısıtlanmış bir küp oluşturabilir ve oynatıcı hareket ettiğinde taşıyabilir, böylece her zaman oynatıcının altında düz durabilir ve sonra oynatıcıyı çekebilirsiniz. aşağı küp. Dene.
Daniel Kvist
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.