Bagaimana cara memindahkan objek di sepanjang keliling objek lain?

11

Saya sangat keluar dari matematika sehingga sakit, tetapi bagi sebagian dari Anda ini harus menjadi sepotong kue. Saya ingin memindahkan objek di sekitar yang lain sepanjang umurnya atau kelilingnya pada jalur melingkar sederhana. Saat ini algoritma permainan saya tahu cara bergerak dan memposisikan sprite tepat di tepi rintangan dan sekarang menunggu titik berikutnya untuk bergerak tergantung pada berbagai kondisi.

Jadi masalah matematika di sini adalah bagaimana untuk mendapatkan (aX, aY) dan (bX, bY) posisi, ketika saya tahu Center (cX, cY), posisi objek (OX, Oy) dan jarak yang dibutuhkan untuk bergerak (d)

masukkan deskripsi gambar di sini

Lumis
sumber
1
Apakah djarak linear atau busur?
MichaelHouse
Ini adalah jarak linear dalam piksel
Lumis
Apakah Anda akrab dengan vektor apa dan operasi dasar pada mereka?
Patrick Hughes
@ Patrick Tidak, saya kira saya harus melakukan kursus tentang Vektor. Karena ini adalah animasi frame demi frame, kode harus cepat dan dioptimalkan.
Lumis

Jawaban:

8

( CAVEAT: Saya menggunakan dua pendekatan di sini: yang pertama mengambil d sebagai panjang busur, dan yang kedua menganggapnya sebagai panjang ortogonal. Kedua perkiraan ini harus baik untuk nilai relatif kecil dari d, tetapi mereka tidak memenuhi pertanyaan yang tepat seperti yang dijelaskan dalam komentar.)

Matematika tentang ini, untungnya, relatif mudah. Pertama-tama, kita dapat menemukan vektor relatif dari posisi tengah kita ke posisi kita saat ini:

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

Dan begitu kita memiliki vektor relatif ini, maka kita dapat mengetahui jari-jari lingkaran yang sedang kita kerjakan dengan menemukan panjangnya:

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

Terlebih lagi, dari vektor relatif kita, kita dapat menemukan sudut yang tepat bahwa garis dari cX ke oX berada di:

curTheta = atan2(deltaX, deltaY);

Sekarang segalanya menjadi sedikit lebih rumit. Pertama-tama, pahami bahwa keliling lingkaran - yaitu, 'panjang busur' dari busur dengan ukuran sudut 2π - adalah 2πr. Secara umum, panjang busur dari busur dengan ukuran sudut θ sepanjang lingkaran jari-jari r hanya θr. Jika kita menggunakan d dalam diagram Anda sebagai panjang busur, dan karena kita tahu jari-jarinya, kita dapat menemukan perubahan dalam theta untuk membawa kita ke posisi baru dengan hanya membagi:

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

Untuk kasus di mana d perlu jarak linear, hal-hal sedikit lebih rumit, tetapi untungnya tidak banyak. Di sana, d adalah satu sisi dari segitiga isoceles yang dua sisi lainnya adalah jari-jari lingkaran (dari cX / cY ke oX / oY dan aX / aY masing-masing), dan membagi dua segitiga isoceles ini memberi kita dua segitiga siku-siku, masing-masing memiliki d / 2 sebagai satu sisi dan jari-jari sebagai sisi miring; ini berarti bahwa sinus setengah sudut kita adalah (d / 2) / radius, dan sudut penuh hanya dua kali ini:

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

Perhatikan bagaimana jika Anda mengeluarkan asin dari rumus ini, dan membatalkan 2s, ini akan sama dengan rumus terakhir; ini sama dengan mengatakan bahwa dosa (x) kira-kira x untuk nilai-nilai kecil x, yang merupakan perkiraan yang berguna untuk diketahui.

Sekarang kita dapat menemukan sudut baru dengan hanya menambahkan atau mengurangi:

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

Setelah kami memiliki sudut baru, maka kami dapat menggunakan beberapa trigonometri dasar untuk menemukan vektor relatif kami yang diperbarui:

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

dan dari posisi tengah kami dan vektor relatif kami, kami dapat (akhirnya) menemukan titik target:

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

Sekarang, dengan semua ini, ada beberapa peringatan besar yang harus diperhatikan. Untuk satu, Anda akan melihat bahwa matematika ini sebagian besar adalah floating-point, dan sebenarnya hampir pasti; mencoba menggunakan metode ini untuk memperbarui dalam satu lingkaran dan kembali ke nilai integer pada setiap langkah dapat melakukan segalanya mulai dari membuat lingkaran Anda tidak tertutup (baik berputar ke dalam atau ke luar setiap kali Anda memutar lingkaran) untuk tidak memulainya di awal tempat! (Jika d Anda terlalu kecil, maka Anda mungkin menemukan bahwa versi bulat aX / aY atau bX / bY persis di mana posisi awal Anda oX / oY berada.) Untuk yang lain, ini sangat mahal, terutama untuk apa yang berusaha untuk melakukan; secara umum, jika Anda tahu karakter Anda akan bergerak dalam busur melingkar, Anda harus merencanakan seluruh busur di muka dan tidaktandai dari bingkai ke bingkai seperti ini, karena banyak perhitungan paling mahal di sini dapat dimuat di depan untuk mengurangi biaya. Cara lain yang bagus untuk memangkas biaya, jika Anda benar-benar ingin memperbarui secara bertahap seperti ini, adalah dengan tidak menggunakan trigonometri di tempat pertama; jika d kecil dan Anda tidak perlu tepat tetapi hanya sangat dekat, maka Anda dapat melakukan 'trik' dengan menambahkan vektor panjang d ke oX / oY, ortogonal ke vektor menuju pusat Anda (perhatikan bahwa a vektor orthogonal ke (dX, dY) diberikan oleh (-dY, dX)), dan kemudian mengecilkannya ke panjang yang tepat. Saya tidak akan menjelaskan kode ini selangkah demi selangkah, tetapi mudah-mudahan masuk akal mengingat apa yang telah Anda lihat sejauh ini. Perhatikan bahwa kita 'menyusutkan' vektor delta baru secara implisit pada langkah terakhir,

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;
Steven Stadnicki
sumber
1
Steven Saya pikir saya akan mencoba pendekatan pertama karena ini hanya permainan di mana lebih penting untuk merasa alami dan menarik daripada tepat. Juga mempercepat masalah. Terima kasih untuk tutorial yang panjang dan bagus ini!
Lumis
Wow, Steven perkiraanmu bekerja seperti mimpi! Bisakah Anda memberi tahu saya cara mengubah kode Anda untuk mendapatkan bX, oleh. Saya belum jelas tentang konsep ortogonal Anda ...
Lumis
2
Tentu! Anda akan benar-benar ingin memahami matematika vektor di beberapa titik, dan setelah Anda melakukannya saya curiga ini akan menjadi sifat kedua; untuk mendapatkan bX / bY Anda hanya perlu pergi 'sebaliknya' - dengan kata lain, daripada menambahkan vektor ortogonal (khusus), kurangi saja. Dalam hal kode di atas, ini akan menjadi 'newDeltaX = deltaX-orthoX; newDeltaY = deltaY-orthoY; ', diikuti oleh perhitungan newLength yang sama, dan kemudian' bX = cX + newDeltaX radius / newLength; olehY = cY + newDeltaY radius / newLength; '.
Steven Stadnicki
Pada dasarnya, kode itu akan menunjuk newDeltaX / newDeltaY ke arah bX / bY (alih-alih ke arah aX / aY), lalu potong agar pas dan tambahkan ke tengah seperti halnya Anda akan mendapatkan aX / aY.
Steven Stadnicki
9

Bentuk segitiga menggunakan dua sisi yang sudah Anda miliki (satu sisi dari 'c' ke 'o', yang lain dari 'o' ke 'a'), dan sisi ketiga bergerak dari 'a' ke 'c'. Anda tidak tahu di mana 'a' dulu, bayangkan saja ada titik di sana untuk saat ini. Anda perlu trigonometri untuk menghitung sudut sudut yang berlawanan dengan sisi 'd'. Anda memiliki panjang sisi c <-> o dan c <-> a, karena keduanya merupakan jari-jari lingkaran.

Sekarang Anda memiliki panjang tiga sisi segitiga ini yang belum dapat Anda lihat, Anda dapat menentukan sudut yang berlawanan dengan sisi 'd' dari segitiga. Inilah rumus SSS (sisi-sisi) jika Anda memerlukan: http://www.teacherschoice.com.au/maths_library/trigonometry/solve_trig_sss.htm

Menggunakan rumus SSS Anda memiliki sudut (yang akan kita sebut 'j') yang berlawanan dengan sisi 'd'. Jadi, sekarang kita bisa menghitung (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) );

Pastikan sudut yang Anda hitung selalu dalam radian.

Jika Anda perlu menghitung jari-jari lingkaran, Anda dapat menggunakan pengurangan vektor, kurangi titik 'c' dari titik 'o', lalu dapatkan panjang vektor yang dihasilkan.

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

float radius = Math.sqrt(lengthSquared);

Sesuatu seperti ini harus dilakukan, saya percaya. Saya tidak tahu Java, jadi saya menebak sintaks yang tepat.

Berikut gambar yang diberikan oleh pengguna Byte56untuk menggambarkan seperti apa bentuk segitiga ini: segitiga cao

Nic Foster
sumber
1
Saya sedang membuat jawaban, tapi ini dia. Anda bebas menggunakan gambar yang saya buat :) i.imgur.com/UUBgM.png
MichaelHouse
@ Byte56: Terima kasih, saya tidak punya pegangan editor gambar untuk menggambarkan.
Nic Foster
Perhatikan bahwa jari-jari harus dihitung juga; harus ada cara yang lebih mudah untuk mendapatkan j daripada perhitungan SSS penuh, karena kita memiliki segitiga isoceles.)
Steven Stadnicki
Ya, itu tampak sederhana, bahkan bagi saya! Android tidak memiliki Vector2 jadi saya kira saya bisa menggunakan nilai secara terpisah. Menariknya saya menemukan kelas Vector2 secara manual dibuat untuk Android di sini: code.google.com/p/beginning-android-games-2/source/browse/trunk/…
Lumis
(Saya telah mengubah jawaban saya sendiri untuk menemukan jarak linear yang benar - perhitungan kedua deltaTheta di sana, sebanyak 2 * asin (d / (radius 2 *)), adalah bagaimana Anda akan menemukan j di sini.)
Steven Stadnicki
2

Untuk membuat objek2 diputar di sekitar objek1, cobalah:

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;
Lewis
sumber
Ini tidak menunjukkan bagaimana mendapatkan sudut di tempat pertama, dan Anda tampaknya menunjukkan cara mengorbit, alih-alih menemukan koordinat seperti pertanyaan yang diajukan.
MichaelHouse
1
Lewis, terima kasih telah menunjukkan cara mengorbit objek di sekitar titik. Mungkin bermanfaat ...
Lumis