Bir nesneyi başka bir nesnenin çevresi boyunca nasıl hareket ettirebilirim?


11

O kadar matematiğim acıyor, ama bazılarınız için bu bir parça kek olmalı. Basit bir dairesel yolda, bir nesneyi yaşları veya çevresi boyunca başka bir yere taşımak istiyorum. Şu anda oyun algoritmam bir perdenin bir engelin hemen kenarında nasıl taşınacağını ve konumlandırılacağını biliyor ve şimdi çeşitli koşullara bağlı olarak bir sonraki noktanın hareket etmesini bekliyor.

Buradaki matematiksel problem , Merkezi (cX, cY), nesne konumunu (oX, oY) ve hareket etmek için gereken mesafeyi (d) bildiğimde (aX, aY) ve (bX, bY) konumlarını nasıl elde edeceğinizdir.

resim açıklamasını buraya girin


1
dbir doğrusal uzaklık veya bir ark?
MichaelHouse

Bu piksel cinsinden doğrusal bir mesafedir
Lumis

Vektörlerin ne olduğu ve üzerlerindeki temel işlemler hakkında bilgi sahibi misiniz?
Patrick Hughes

@Patrick Hayır, sanırım Vektörler üzerine bir ders yapmak zorunda kalacağım. Bu kare kare animasyon olduğundan kodun hızlı ve optimize edilmiş olması gerekir.
Lumis

Yanıtlar:


8

( CAVEAT: Burada iki yaklaşım kullanıyorum: birincisi d'yi bir yay uzunluğu olarak, ikincisi bunu dikey bir uzunluk olarak alır. Bu yaklaşımların her ikisi de nispeten küçük d değerleri için iyi olmalıdır, ancak yerine getirmezler yorumlarda açıklandığı gibi kesin soru.)

Bununla ilgili matematik, neyse ki, nispeten basittir. Her şeyden önce, merkez konumumuzdan mevcut konumumuza göreli vektörü bulabiliriz:

deltaX = oX-cX;
deltaY = oY-cY;

Ve bu göreli vektöre sahip olduktan sonra, üzerinde çalıştığımız dairenin yarıçapını, uzunluğunu bularak öğrenebiliriz:

radius = sqrt(deltaX*deltaX+deltaY*deltaY);

Dahası, göreceli vektörümüzden cX'ten oX'a kadar olan çizginin tam açısını bulabiliriz:

curTheta = atan2(deltaX, deltaY);

Şimdi işler biraz daha zorlaşıyor. Her şeyden önce, bir dairenin çevresinin - yani 2π açısal ölçüsü olan bir yayın 'ark uzunluğunun' 2πr olduğunu anlayın. Genel olarak, r yarıçapı dairesi boyunca θ açısal ölçüsü olan bir yayın yay uzunluğu sadece justr'dir. Diyagramınızdaki d'yi yay uzunluğu olarak kullanacak olsaydık ve yarıçapı bildiğimizden, bizi bölerek yeni konuma götürmek için tetadaki değişikliği bulabiliriz:

deltaTheta = d/radius; // treats d as a distance along the arc

D'nin doğrusal bir mesafe olması gerektiği durumda, işler biraz daha karmaşıktır, ama neyse ki fazla değildir. Burada d, diğer iki tarafı dairenin yarıçapı olan (sırasıyla cX / cY'den oX / oY ve aX / aY'ye) olan bir izoceles üçgeninin bir tarafıdır ve bu izoceles üçgeninin ikiye bölünmesi, her biri iki sağ üçgen verir. bir tarafı olarak d / 2 ve hipotenüs olarak yarıçapı vardır; bu, açımızın yarısının sinüsünün (d / 2) / yarıçap olduğu anlamına gelir ve bu nedenle tam açı bunun sadece iki katıdır:

deltaTheta = 2*asin(d/(2*radius)); // treats d as a linear distance

Eğer asini bu formülden çıkarır ve 2'leri iptal ederseniz, bu son formülle nasıl olur; bu sin (x) 'in küçük x değerleri için yaklaşık x olduğunu söylemekle aynıdır, ki bu da yararlı bir yaklaşımdır.

Şimdi yeni açıyı sadece ekleyerek veya çıkararak bulabiliriz:

newTheta = curTheta+deltaTheta; // This will take you to aX, aY. For bX/bY, use curTheta-deltaTheta

Yeni açıya sahip olduktan sonra, güncellenmiş göreli vektörümüzü bulmak için bazı temel trigileri kullanabiliriz:

newDeltaX = radius*cos(newTheta);
newDeltaY = radius*sin(newTheta);

ve merkez konumumuzdan ve bağıl vektörünüzden (sonunda) hedef noktayı bulabiliriz:

aX = cX+newDeltaX;
aY = cY+newDeltaY;

Şimdi, tüm bunlarla birlikte, farkında olmak için bazı büyük uyarılar var . Birincisi, bu matematiğin çoğunlukla kayan nokta olduğunu ve aslında neredeyse olması gerektiğini fark edeceksiniz; bir döngüde güncellemek ve her adımda tamsayı değerlere geri dönmek için bu yöntemi kullanmaya çalışmak, çemberinizi yakınlaştırmamaktan (döngüde her gittiğinizde içe veya dışa doğru sarılmaktan) ilk başlamamaya kadar her şeyi yapabilir yer! (D'niz çok küçükse, o zaman aX / aY veya bX / bY'nin yuvarlatılmış sürümlerinin tam olarak oX / oY başlangıç ​​konumunuz olduğunu keşfedebilirsiniz.) Bir diğeri için, bu özellikle pahalıya çalıştığı şey için çok pahalı yapmak; Eğer karakter bir çember yayı hareketli olacak biliyorsanız genel olarak, tüm yay önceden ve dışarı planlamalısınız değilburadaki en pahalı hesaplamaların birçoğu maliyetleri azaltmak için önden yüklenebileceğinden, çerçeveden çerçeveye böyle işaretleyin. Maliyetleri azaltmanın başka bir iyi yolu, eğer gerçekten bu şekilde artımlı olarak güncellemek istiyorsanız, ilk etapta trig kullanmamaktır; d küçükse ve tam ancak çok yakın olmasına ihtiyacınız yoksa, oX / oY'ye d uzunluğunda bir vektör ekleyerek merkezinize doğru dik bir 'hile' yapabilirsiniz (a. (dX, dY) ile dik vektör (-dY, dX) ile verilir ve sonra doğru uzunluğa küçültülür. Bu kodu adım adım açıklamayacağım, ancak umarım şimdiye kadar gördükleriniz göz önüne alındığında mantıklı olacaktır. Son adımda yeni delta vektörünü örtük olarak 'küçülttüğümüzü',

deltaX = oX-cX; deltaY = oY-cY;
radius = sqrt(deltaX*deltaX+deltaY*deltaY);
orthoX = -deltaY*d/radius;
orthoY = deltaX*d/radius;
newDeltaX = deltaX+orthoX; newDeltaY = deltaY+orthoY;
newLength = sqrt(newDeltaX*newDeltaX+newDeltaY*newDeltaY);
aX = cX+newDeltaX*radius/newLength; aY = cY+newDeltaY*radius/newLength;

1
Steven Sanırım ilk önce yaklaşımı deneyeceğim, çünkü bu sadece doğal ve ilginç hissetmenin kesin olmaktan daha önemli olduğu bir oyun. Ayrıca hız önemlidir. Bu uzun ve iyi öğretici için teşekkürler!
Lumis

Vay canına, Steven yaklaşımın bir rüya gibi çalışıyor! Bana bX almak için kodunuzu nasıl değiştireceğinizi söyleyebilir misiniz, bY. Ortogonal konseptinizde henüz net değilim ...
Lumis

2
Elbette! Bir noktada vektör matematiğini gerçekten anlamak isteyeceksiniz ve bunu yaptıktan sonra bunun ikinci doğa olacağından şüpheleniyorum; bX / bY elde etmek için sadece 'diğer yöne' gitmeniz gerekir - başka bir deyişle, (belirli) dikey vektör eklemek yerine, sadece çıkartın. Yukarıdaki kod açısından, bu 'newDeltaX = deltaX-orthoX; newDeltaY = deltaY-orthoY; ', ardından newLength ile aynı hesaplama ve' bX = cX + newDeltaX radius / newLength; bY = cY + newDeltaY yarıçapı / newLength; '.
Steven Stadnicki

Temel olarak, bu kod newDeltaX / newDeltaY'yi bX / bY yönünde (aX / aY yönünde değil) gösterecek, sonra aX / aY'yi alacağınız gibi merkeze sığacak şekilde kırpacak ve ekleyecektir.
Steven Stadnicki

9

Zaten sahip olduğunuz iki tarafı kullanarak bir üçgen oluşturun (bir tarafı 'c' ila 'o', diğeri 'o' ila 'a' arasındadır) ve üçüncü taraf 'a' ila 'c' arasındadır. Henüz 'a' nın nerede olduğunu bilmiyorsun, sadece şimdilik bir nokta olduğunu hayal et. 'D' tarafına ters olan açının açısını hesaplamak için trigonometriye ihtiyacınız olacaktır. Kenarların uzunluğu c <-> o ve c <-> a'dır, çünkü ikisi de dairenin yarıçapıdır.

Artık bu üçgenin henüz göremediğiniz üç kenarının uzunluğuna sahip olduğunuza göre, üçgenin 'd' tarafına zıt olan açıyı belirleyebilirsiniz. Gerekirse SSS (yan taraf) formülü şöyledir: http://www.teacherschoice.com.au/maths_library/trigonometry/solve_trig_sss.htm

SSS formülünü kullanarak 'd' tarafına zıt bir açıya ('j' diyeceğiz) sahip olursunuz. Şimdi hesaplayabiliriz (aX, aY).

// This is the angle from 'c' to 'o'
float angle = Math.atan2(oY - cY, oX - cX)

// Add the angle we calculated earlier.
angle += j;

Vector2 a = new Vector2( radius * Math.cos(angle), radius * Math.sin(angle) );

Hesapladığınız açıların daima radyanda olduğundan emin olun.

Dairenin yarıçapını hesaplamanız gerekiyorsa, vektör çıkarma, 'c' noktasını 'o' noktasından çıkarabilir, sonra elde edilen vektörün uzunluğunu alabilirsiniz.

float lengthSquared = ( inVect.x * inVect.x
                      + inVect.y * inVect.y
                      + inVect.z * inVect.z );

float radius = Math.sqrt(lengthSquared);

Böyle bir şey yapmalı, inanıyorum. Java bilmiyorum, bu yüzden tam sözdizimi tahmin.

İşte Byte56bu üçgenin nasıl görünebileceğini göstermek için kullanıcı tarafından verilen görüntü : cao üçgeni


1
Bir cevap veriyordum, ama bu kadar. Çektiğim
MichaelHouse

@ Byte56: Teşekkürler, gösterecek herhangi bir resim düzenleyici tutamaçım yoktu.
Nic Foster

Yarıçapın da hesaplanması gerektiğini unutmayın; j'yi elde etmenin tam SSS hesaplamasından daha basit yolları olmalı, çünkü bir izoceles üçgene sahibiz.)
Steven Stadnicki

Evet, bu benim için bile basit görünüyor! Android Vector2'ye sahip değil, bu yüzden değerleri ayrı ayrı kullanabileceğimi tahmin ediyorum. İlginçtir ki burada Android için manuel olarak oluşturulmuş Vector2 sınıfını buldum: code.google.com/p/beginning-android-games-2/source/browse/trunk/…
Lumis

(Doğru doğrusal mesafeyi bulmak için kendi cevabımı ayarladım - deltaTheta'nın ikinci hesaplaması, 2 * asin (d / (2 * yarıçap)) olarak, burada
j'yi

3

Obj2'nin obj1 etrafında dönmesini sağlamak için şunu deneyin:

float angle = 0; //init angle

//call in an update
obj2.x = (obj1.x -= r*cos(angle));
obj2.y = (obj1.y += r*sin(angle));
angle-=0.5;

Bu, ilk etapta açıyı nasıl elde edeceğinizi göstermez ve soru sorulduğu gibi koordinatları bulmak yerine nasıl yörüngeye gireceğinizi gösterirsiniz.
MichaelHouse

1
Lewis, bir nesnenin bir noktaya nasıl yörüngesinde kaldığını gösterdiğin için teşekkürler. Faydalı gelebilir ...
Lumis
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.