Unity - Bir gemiyi gerçekçi bir şekilde 2D yukarıdan aşağıya oyundaki bir noktaya taşıma


14

fare ile tıkladığım noktaya bir yelkenli gemi taşımak için çalışıyorum. bu hareket gerçekçi olmalıdır (geminin etrafında hareket ettiği yerde kürek), bu nedenle fare tıklaması solda ve geminin önünde, doğru dönüşü sağlamak için gemi daha sonra virajlı bir yolla hareket etmelidir.

Birisi bana bu konuda yardımcı olabilir, sevinirim teşekkürler gemi hareketi


1
Resminiz yelkenleri tasvir ediyor gibi: Rüzgar dikkate alınmalı mı? Bazı manevraların yanlış rüzgarla veya bunların eksikliğiyle yapılması imkansızdır.
Kimse

Daha da önemlisi, gerçekçi görünen yelkenli gemi hareketi gerçekten rüzgarı dikkate almayı gerektirir ; görmezden gelmek, zıplamayı uygularken yer çekimini görmezden gelmek gibidir. Özellikle ayrıntılı bir rüzgar modeline ihtiyacınız yoktur, ancak gemilerinizin yelkenlerindeki rüzgar (ve salmalarına ve dümenlerine karşı su) tarafından itildiğini unutmayın. Özellikle, gemiler doğrudan rüzgârla yelken açamazlar; onlar gerekir dövmek yerine orada kendi yolunu.
Ilmari Karonen

Genellikle bir "goto noktası" dönme fazına ve ileri hareket fazına ayrılabilir. Aynı yaklaşımı alın, ancak dönüşe ileri bir hareket uygulayın. Her dönüş rotasyonu örneği tekneyi y metre ileriye
taşır

Yanıtlar:


7

Bu sayfaya bakın

Gerçekçi Dönüşler Ekleme

Bir sonraki adım, ünitelerimiz için gerçekçi kavisli dönüşler eklemektir, böylece her dönüşlerinde aniden yön değiştirmezler. Basit bir çözüm, ani köşeleri sırayla düzeltmek için bir spline kullanmayı içerir. Bu, estetik kaygıların bazılarını çözerken, yine de çoğu birim için fiziksel olarak çok gerçekçi olmayan bir hareketle sonuçlanır. Örneğin, bir tankın ani bir virajını sıkı bir eğriye dönüştürebilir, ancak kavisli dönüş hala tankın gerçekleştirebileceğinden çok daha sıkı olacaktır.

Daha iyi bir çözüm için bilmemiz gereken ilk şey, ünitemiz için dönüş yarıçapıdır. Dönme yarıçapı oldukça basit bir kavramdır: arabanızda büyük bir park yerinde iseniz ve tekerleği gidebildiği kadar sola döndürün ve bir daire içinde sürmeye devam ediyorsanız, o dairenin yarıçapı dönüyor yarıçapı. Bir Volkswagen Beetle'ın dönüş yarıçapı, büyük bir SUV'nin dönüş yarıçapından önemli ölçüde daha küçük olacak ve bir kişinin dönüş yarıçapı, büyük, kereste bir ayının yarıçapından önemli ölçüde daha az olacaktır.

Diyelim ki bir noktada (başlangıç ​​noktası) ve belirli bir yöne işaret ettiniz ve Şekil 5'te gösterildiği gibi başka bir noktaya (hedefe) gitmeniz gerekiyor. doğrudan hedefe işaret edene kadar bir daireye gidebilir ve daha sonra ileri gidebilir veya sağa dönüp aynı şeyi yapabilirsiniz. Şekil 5: Başlangıç ​​noktasından hedefe olan en kısa yolu belirleme.

Şekil 5'te en kısa yol açıkça alttaki yeşil çizgidir. Bu yol, Şekil 6'da gösterilen bazı geometrik ilişkilerden dolayı hesaplamak için oldukça basittir.

Şekil 6: Yolun uzunluğunu hesaplama.

İlk önce dönüş çemberimizin merkezi olan ve her zaman başlangıç ​​noktasından yarıçapı r olan P noktasının konumunu hesaplıyoruz. Başlangıç ​​yönümüzden sağa dönersek, bu P'nin başlangıç ​​noktasından (başlangıç_dizini - 90) bir açıda olduğu anlamına gelir, yani:

angleToP = initial_direction - 90
P.x = Origin.x + r * cos(angleToP)
P.y = Origin.y + r * sin(angleToP)

Artık merkez nokta P'nin yerini bildiğimize göre, şemada h olarak gösterilen P ile hedefe olan mesafeyi hesaplayabiliriz:

dx = Destination.x - P.x
dy = Destination.y - P.y
h = sqrt(dx*dx + dy*dy)

Bu noktada, hedefin daire içinde olmadığını kontrol etmek istiyoruz, çünkü öyleyse, asla ulaşamayız:

if (h < r)
    return false

Şimdi d segmentinin uzunluğunu hesaplayabiliriz, çünkü sağ üçgenin diğer iki tarafının uzunluklarını zaten biliyoruz, yani h ve r. Sağ üçgen ilişkisinden açıyı da belirleyebiliriz:

d = sqrt(h*h - r*r)
theta = arccos(r / h)

Son olarak, daireyi terk edip düz çizgide başlamak için Q noktasını bulmak için toplam açıyı + bilmemiz gerekir ve P'den hedefe açı olarak kolayca belirlenir:

phi = arctan(dy / dx) [offset to the correct quadrant]
Q.x = P.x + r * cos(phi + theta)
Q.y = P.y + r * sin(phi + theta)

Yukarıdaki hesaplamalar sağa dönüş yolunu temsil eder. Soldaki yol tam olarak aynı şekilde hesaplanabilir, ancak angleToP'yi hesaplamak için initial_direction öğesine 90 ekleriz ve daha sonra + yerine - kullanırız. Her ikisini de hesapladıktan sonra, hangi yolun daha kısa olduğunu görüyoruz ve bunu kullanıyoruz.

Bu algoritmayı ve onu takip edenleri uygulamamızda, her biri ya düz ya da kavisli olmak üzere en fazla dört farklı "çizgi parçası" saklayan bir veri yapısı kullanıyoruz. Burada açıklanan kavisli yollar için sadece iki segment kullanılır: bir ark ve ardından düz bir çizgi. Veri yapısı, parçanın bir yay mı yoksa düz bir çizgi mi olduğunu, parçanın uzunluğunu ve başlangıç ​​konumunu belirten üyeler içerir. Segment düz bir çizgi ise, veri yapısı da açıyı belirtir; yaylar için dairenin merkezini, dairenin başlangıç ​​açısını ve yayın tarafından kapsanan toplam radyanı belirtir.

İki nokta arasında almak için gerekli olan eğri yolu hesapladıktan sonra, Liste 2'de gösterildiği gibi, konumumuzu ve yönümüzü herhangi bir anda kolayca hesaplayabiliriz.

LISTING 2. Belirli bir zamanda konumun ve yönün hesaplanması.

distance = unit_speed * elapsed_time
loop i = 0 to 3:
    if (distance < LineSegment[i].length)
        // Unit is somewhere on this line segment
        if LineSegment[i] is an arc
            //determine current angle on arc (theta) by adding or
            //subtracting (distance / r) to the starting angle
            //depending on whether turning to the left or right
            position.x = LineSegment[i].center.x + r*cos(theta)
            position.y = LineSegment[i].center.y + r*sin(theta)
        //determine current direction (direction) by adding or
        //subtracting 90 to theta, depending on left/right
        else
            position.x = LineSegment[i].start.x 
              + distance * cos(LineSegment[i].line_angle)
            position.y = LineSegment[i].start.y
              + distance * sin(LineSegment[i].line_angle)
        direction = theta
        break out of loop
    else
        distance = distance - LineSegment[i].length

4
Bu cevap gerçekten gemilerin fiziğine bakmıyor. Ayrıca temelde başka bir web sitesine bir bağlantı ve uzun bir alıntı olduğunu sorunlu buluyorum (yasallık konusunda emin değilim).
Kimse

En son sorulan soruya bir çözüm olan mevcut bir kaynak sağladığımda, hedef sitenin sona ermesi durumunda bağlantının içeriğini eklemem istendi. Şimdi içeriği eklememem isteniyor. Zihnini yarat.
Draco18s artık SE

3
@ Draco18s: Yapmanız gereken , bağlantılı materyalin temel noktalarını kendi kelimelerinizle özetlemektir. (Ya da daha iyisi, soruyu kendi deneyiminize dayanarak yanıtlayın ve bağlantıları sadece destekleyici materyal olarak veya daha fazla okuma için kullanın.) Kısa alıntılar, özellikle gerçekten önlenemedikleri durumlarda genellikle iyidir (örneğin, birisinin tam olarak alıntılanması) gerçekten bir şey söylediklerini göstermek için kelimeler), ancak bir makalenin önemli bir bölümünü alıntılamak gerçekten adil kullanımın ötesine geçiyor .
Ilmari Karonen

Nokta dairenin içindeyse biraz dışarı çıkıp geri dönebilirsiniz.
user253751

(Ps. Meta hakkındaki bu iki soruya da bakınız. )
Ilmari

7

Basit bir çözüm olarak, bir yorumda söylediğim gibi, bu yaklaşımı deneyebilirsiniz:

gemiyi hedef yöne doğrulttuğunuz bir fazı düşünün, bu aşamada yuduma bir dönüş uygularsınız, aynı zamanda ileriye doğru bir hareket uygularsınız. Gemi zaten hedefe baktığında tam bir ileri hız uygulayabilirsiniz. Love2d'de bir test düzenledim, burada gemi güncelleme yöntemini takip edin.

turnAngSpeed = 0.4 --direction changing speed
ForwordSpeed = 40 -- full forward speed
turnForwordSpeed = ForwordSpeed *0.6 -- forward speed while turning
function ent:update(dt)
            dir = getVec2(self.tx-self.x,self.ty-self.y) -- ship --> target direction (vec2)
            dir = dir.normalize(dir) --normalized                               
            a= dir:angle() - self.forward:angle() --angle between target direction e current forward ship vector
            if (a<0) then
             a=a+math.pi *2 -- some workaround to have all positive values
            end

            if a > 0.05 then -- if angle difference 
                if a < math.pi then
                    --turn right
                    self.forward = vec2.rotate(self.forward,getVec2(0,0),turnAngSpeed * dt)
                else
                    --turn left
                    self.forward = vec2.rotate(self.forward,getVec2(0,0),-turnAngSpeed * dt)
                end             
                --apply turnForwordSpeed
                self.x = self.x+ self.forward.x * turnForwordSpeed * dt
                self.y = self.y+ self.forward.y * turnForwordSpeed * dt
            else 
                --applly ForwordSpeed
                self.x = self.x+ self.forward.x * ForwordSpeed * dt
                self.y = self.y+ self.forward.y * ForwordSpeed * dt
            end
end

resim açıklamasını buraya girin

Örnek animasyon (son döngü), geminin hedefe ulaşamadığı bir durumu gösterir, çünkü dönüş ve ileri hızın kombinasyonu çok büyük bir dönüş yarıçapını tanımlar, bu durumlarda arı " turnForwordSpeed" değerini azaltabilir veya daha iyi yapabilir açı mesafesine ( a) ve hedef mesafeye bağlıdır.


Bu güzel bir cevaptır, ancak OP için yeterince gerçekçi olabilir veya olmayabilir. Diyelim ki, arabaların aksine, gemilerin gerçekten bir "dönüş yarıçapı" yoktur: yelkenli gemiler rüzgara bağlıyken, kendinden (motor / insan) güç alan çoğu gemi esasen bir kuruşa dönüşebilir ve aslında olumsuz bir etkili dönüşe sahip olabilir rüzgâr siperi yarıçapı, yani rüzgarın sola dönmesi geminin sağa kaymasına neden olabilir. Gemilerin sahip oldukları atalettir (ve sürükler): anında dönemezler veya hareket edemezler ve hareket ettikten veya döndükten sonra durmak için biraz zaman ve güç harcarlar. Yine de +1.
Ilmari Karonen

Cevabınız için çok teşekkür ederim!!! :) efendim sen benim kahramanımsın!
DavidT

@DavidT Daha sonra probleminizi tatmin edici bir şekilde çözebiliyorsa, cevabını kabul edilen cevap olarak işaretlemeyi düşünün. :)
15'te

-2

Unity Nav mesh sistemi, muhtemelen nav agent değerleri ile biraz oynayarak istediğinizi yapar.

Nav Mesh'lerin kullanımı oldukça basittir. Ve yalnızca yukarıdan aşağıya kurulumda kullanılabilir (veya en azından sadece x / z hareketi için kullanılabilir)

Birlik kılavuzu Gezinme ağının ayarlanmasıyla ilgili sayfa

Temel olarak bir gezinme alanı oluşturmak için herhangi bir şekil kafesi kullanabilir ve nesnelerinize gezinme aracıları ekleyebilir ve bir gezinme ağının etrafındaki yollarını bulmalarını sağlayabilirsiniz.


Draco18s'in cevabının da bu konuda eksik olduğunu düşünüyorum. Ancak, sizinki gerçek bir cevap ve daha fazla yorum değildir.
Kimse

2
Bu iyi bir öneridir, ancak iyi bir cevap olabilmek için uygulama konusunda destek ve bilgiye ihtiyaç duymaktadır. Bunun iyi bir yanıt olması için nav mesh'lerin yapılandırılmasıyla ilgili bazı bilgiler ekleyin. Bence yukarıdaki yorumcular söylemeye çalışıyorlar :)
sirdank
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.