Làm thế nào để di chuyển một đối tượng dọc theo chu vi của một đối tượng khác?


11

Tôi không giỏi toán đến nỗi đau, nhưng đối với một số bạn đây nên là một miếng bánh. Tôi muốn di chuyển một vật thể xung quanh vật thể khác theo độ tuổi hoặc chu vi của nó trên một đường tròn đơn giản. Hiện tại, thuật toán trò chơi của tôi biết cách di chuyển và định vị một sprite chỉ ở rìa của một chướng ngại vật và bây giờ nó chờ điểm tiếp theo di chuyển tùy thuộc vào các điều kiện khác nhau.

Vì vậy, vấn đề toán học ở đây là làm thế nào để có được vị trí (aX, aY)(bX, bY) , khi tôi biết Trung tâm (cX, cY), vị trí đối tượng (oX, oY) và khoảng cách cần thiết để di chuyển (d)

nhập mô tả hình ảnh ở đây


1
dmột khoảng cách tuyến tính hay là một vòng cung?
MichaelHouse

Đó là một khoảng cách tuyến tính tính bằng pixel
Lumis

Bạn có quen thuộc với các vectơ và các hoạt động cơ bản trên chúng không?
Patrick Hughes

@Patrick Không, tôi đoán tôi sẽ phải học một khóa về vectơ. Vì đây là khung hình theo khung hình động nên mã phải nhanh và tối ưu.
Lumis

Câu trả lời:


8

( Nên biết trước: Tôi đang sử dụng hai xấp xỉ ở đây: người đầu tiên mất d như chiều dài hồ quang, và lần thứ hai mất nó như là một chiều dài trực giao Cả hai xấp xỉ nên được tốt cho các giá trị tương đối nhỏ của d, nhưng họ không thực hiện đầy đủ. câu hỏi chính xác như được làm rõ trong các ý kiến.)

Toán học về điều này, may mắn thay, là tương đối đơn giản. Trước hết, chúng ta có thể tìm thấy vectơ tương đối từ vị trí trung tâm của chúng ta đến vị trí hiện tại của chúng ta:

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

Và một khi chúng ta có vectơ tương đối này, thì chúng ta có thể biết bán kính của vòng tròn chúng ta đang làm việc bằng cách tìm độ dài của nó:

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

Hơn nữa, từ vectơ tương đối của chúng ta, chúng ta có thể tìm thấy góc chính xác mà đường thẳng từ cX đến oX nằm ở:

curTheta = atan2(deltaX, deltaY);

Bây giờ mọi thứ trở nên phức tạp hơn một chút. Trước hết, hãy hiểu rằng chu vi của một vòng tròn - nghĩa là 'chiều dài cung' của một cung có số đo góc là 2π - là 2πr. Nói chung, độ dài cung của một cung có số đo góc dọc theo vòng tròn bán kính r chỉ là θr. Nếu chúng ta sử dụng d trong sơ đồ của bạn làm độ dài cung và vì chúng ta biết bán kính, chúng ta có thể tìm thấy sự thay đổi trong theta để đưa chúng ta đến vị trí mới bằng cách chia ra:

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

Đối với trường hợp d cần một khoảng cách tuyến tính, mọi thứ phức tạp hơn một chút, nhưng may mắn là không nhiều. Ở đó, d là một cạnh của một tam giác isocele có hai cạnh còn lại là bán kính của đường tròn (từ cX / cY đến oX / oY và aX / aY) và chia đôi tam giác isocele này cho chúng ta hai tam giác vuông, mỗi cạnh có d / 2 là một bên và bán kính là cạnh huyền; điều này có nghĩa là sin của một nửa góc của chúng ta là (d / 2) / bán kính, và do đó, góc đầy đủ chỉ là hai lần này:

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

Lưu ý làm thế nào nếu bạn lấy asin ra khỏi công thức này và hủy 2 giây, đây sẽ giống như công thức cuối cùng; điều này giống như nói rằng sin (x) xấp xỉ x đối với các giá trị nhỏ của x, đây là một xấp xỉ hữu ích cần biết.

Bây giờ chúng ta có thể tìm thấy góc mới bằng cách thêm hoặc bớt:

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

Khi chúng ta có góc mới, sau đó chúng ta có thể sử dụng một số trig cơ bản để tìm vectơ tương đối được cập nhật của mình:

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

và từ vị trí trung tâm và vectơ tương đối của chúng ta, cuối cùng chúng ta có thể tìm thấy điểm mục tiêu:

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

Bây giờ, với tất cả điều này, có một số lớn hãy cẩn thận phải nhận thức được. Đối với một người, bạn sẽ nhận thấy rằng toán học này chủ yếu là dấu phẩy động, và trên thực tế nó gần như phải như vậy; cố gắng sử dụng phương pháp này để cập nhật trong một vòng lặp và làm tròn trở lại các giá trị số nguyên ở mỗi bước có thể làm mọi thứ từ làm cho vòng tròn của bạn không đóng (xoắn ốc vào trong hoặc ra ngoài mỗi khi bạn đi vòng vòng) để không bắt đầu trong vòng đầu tiên địa điểm! (Nếu d của bạn quá nhỏ, thì bạn có thể phát hiện ra rằng các phiên bản tròn của aX / aY hoặc bX / bY chính xác là vị trí bắt đầu của bạn oX / oY.) Đối với người khác, điều này rất tốn kém, đặc biệt là những gì nó đang cố gắng làm; nói chung, nếu bạn biết nhân vật của mình sẽ di chuyển theo hình vòng cung, bạn nên lên kế hoạch trước cho toàn bộ cung và khôngđánh dấu nó từ khung này sang khung khác như thế này, vì nhiều tính toán đắt nhất ở đây có thể được tải trước để cắt giảm chi phí. Một cách tốt khác để cắt giảm chi phí, nếu bạn thực sự muốn cập nhật tăng dần như thế này, là không sử dụng trig ở nơi đầu tiên; nếu d nhỏ và bạn không cần chính xác nhưng chỉ cần rất gần, thì bạn có thể thực hiện một 'mẹo' bằng cách thêm một vectơ có chiều dài d vào oX / oY, trực giao với vectơ về phía trung tâm của bạn (lưu ý rằng vectơ trực giao với (dX, dY) được cho bởi (-dY, dX)), và sau đó thu nhỏ nó xuống đúng chiều dài. Tôi sẽ không giải thích mã này từng bước một, nhưng hy vọng nó sẽ có ý nghĩa với những gì bạn đã thấy cho đến nay. Lưu ý rằng chúng tôi 'thu nhỏ' vectơ delta mới ở bước cuối cùng,

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 Tôi nghĩ rằng tôi sẽ thử tính gần đúng trước vì đây chỉ là một trò chơi quan trọng để cảm thấy tự nhiên và thú vị hơn là chính xác. Ngoài ra vấn đề tốc độ. Cảm ơn bạn cho hướng dẫn dài và tốt này!
Lumis

Wow, Steven gần đúng của bạn đang làm việc như một giấc mơ! Bạn có thể cho tôi biết làm thế nào để thay đổi mã của bạn để có được bX, bY. Tôi chưa rõ về khái niệm trực giao của bạn ...
Lumis

2
Chắc chắn rồi! Bạn sẽ thực sự muốn hiểu toán học vectơ tại một số điểm, và một khi bạn làm tôi nghi ngờ đây sẽ là bản chất thứ hai; để có được bX / bY, bạn chỉ cần đi 'theo cách khác' - nói cách khác, thay vì thêm vectơ trực giao (cụ thể), chỉ cần trừ nó. Về mặt mã ở trên, đây sẽ là 'newDeltaX = deltaX-orthoX; newDeltaY = deltaY-orthoY; ', theo sau là cùng một tính toán của newLpm, và sau đó' bX = cX + newDeltaX radius / newLdrops; bY = cY + bán kính newDeltaY / newLpm; '.
Steven Stadnicki

Về cơ bản, mã đó sẽ trỏ newDeltaX / newDeltaY theo hướng bX / bY (thay vì theo hướng aX / aY), sau đó cắt để phù hợp và thêm vào trung tâm giống như bạn sẽ có được aX / aY.
Steven Stadnicki

9

Tạo thành một hình tam giác bằng hai cạnh bạn đã có (một mặt là từ 'c' đến 'o', mặt kia từ 'o' đến 'a') và mặt thứ ba đi từ 'a' đến 'c'. Bạn không biết 'a' ở đâu chưa, hãy tưởng tượng bây giờ có một điểm ở đó. Bạn sẽ cần lượng giác để tính góc của góc đối diện với cạnh 'd'. Bạn có chiều dài của các cạnh c <-> o và c <-> a, vì cả hai đều là bán kính của vòng tròn.

Bây giờ bạn có độ dài ba cạnh của tam giác này mà bạn chưa thể nhìn thấy, bạn có thể xác định góc đối diện với cạnh 'd' của tam giác. Đây là công thức SSS (side-side-side) nếu bạn cần: http://www.teacherschoice.com.au/maths_l Library / trigonometry/solve_trig_sss.htmlm

Sử dụng công thức SSS, bạn có góc (mà chúng ta sẽ gọi là 'j') đối diện với cạnh 'd'. Vì vậy, bây giờ chúng ta có thể tính toán (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) );

Đảm bảo các góc bạn tính toán luôn tính bằng radian.

Nếu bạn cần tính bán kính của vòng tròn, bạn có thể sử dụng phép trừ vectơ, trừ điểm 'c' khỏi điểm 'o', sau đó lấy độ dài của vectơ kết quả.

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

float radius = Math.sqrt(lengthSquared);

Một cái gì đó như thế này nên làm, tôi tin. Tôi không biết Java, vì vậy tôi đoán theo cú pháp chính xác.

Đây là hình ảnh được cung cấp bởi người dùng Byte56để minh họa hình tam giác này có thể trông như thế nào: tam giác cao


1
Tôi đã làm cho một câu trả lời, nhưng đây là nó. Bạn được hoan nghênh sử dụng hình ảnh tôi đã tạo :) i.imgur.com/UUBgM.png
MichaelHouse

@ Byte56: Cảm ơn, tôi không có trình xử lý hình ảnh nào để minh họa.
Nic Foster

Lưu ý rằng bán kính cũng phải được tính toán; nên có nhiều cách nhận j đơn giản hơn so với tính toán SSS đầy đủ, vì chúng ta có tam giác isocele.)
Steven Stadnicki

Vâng, điều đó có vẻ đơn giản, ngay cả với tôi! Android không có Vector2 nên tôi đoán tôi chỉ có thể sử dụng các giá trị riêng biệt. Tình cờ tôi thấy lớp Vector2 được tạo thủ công cho Android tại đây: code.google.com/p/beginning-android-games-2/source/browse/trunk/ phỏng
Lumis

(Tôi đã điều chỉnh câu trả lời của riêng tôi để tìm ra tuyến tính khoảng cách chính xác - tính toán thứ hai của deltaTheta ở đó, như 2 * asin (d / (2 * radius)), là cách bạn sẽ tìm thấy j ở đây.)
Steven Stadnicki

2

Để làm cho obj2 xoay quanh obj1, có thể thử:

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;

Điều này không chỉ ra cách lấy góc ở vị trí đầu tiên và bạn dường như đang chỉ ra cách quay quanh quỹ đạo, thay vì tìm tọa độ như câu hỏi.
MichaelHouse

1
Lewis, cảm ơn vì đã chỉ ra cách quay quanh một vật thể quanh một điểm. Nó có thể hữu ích ...
Lumis
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.