หลังจากคำถามนี้ฉันต้องการความช่วยเหลือเพิ่มเติม
ฉันจะทราบได้อย่างไรว่าการชนกันของสี่เหลี่ยมด้านใดมาและตอบสนองได้อย่างไร
ลูกศรสีน้ำเงินเป็นเส้นทางที่วัตถุวงกลมบางตัวติดตามหากก่อนและหลังการชนกับกล่อง
ฉันจะคำนวณสิ่งนี้ได้อย่างไร
หลังจากคำถามนี้ฉันต้องการความช่วยเหลือเพิ่มเติม
ฉันจะทราบได้อย่างไรว่าการชนกันของสี่เหลี่ยมด้านใดมาและตอบสนองได้อย่างไร
ลูกศรสีน้ำเงินเป็นเส้นทางที่วัตถุวงกลมบางตัวติดตามหากก่อนและหลังการชนกับกล่อง
ฉันจะคำนวณสิ่งนี้ได้อย่างไร
คำตอบ:
เนื่องจากสิ่งนี้เป็นไปตามคำถามอื่นของคุณฉันจะให้คำตอบเมื่อสี่เหลี่ยมถูกจัดแนวแกน
ขั้นแรกให้คุณสร้างสี่เหลี่ยมผืนผ้าของวัตถุปัจจุบันด้วยค่าต่อไปนี้:
int boxLeft = box.X;
int boxRight = boxLeft + box.Width;
int boxTop = box.Y;
int boxBottom = boxTop + box.Height;
ถัดไปคุณจะต้องมีตำแหน่งของวัตถุเก่า (ซึ่งคุณสามารถจัดเก็บในแต่ละวัตถุหรือส่งต่อไปยังฟังก์ชัน) เพื่อสร้างสี่เหลี่ยมของวัตถุเก่า (เมื่อไม่ได้ชนกัน):
int oldBoxLeft = box.OldX;
int oldBoxRight = oldBoxLeft + box.Width;
int oldBoxTop = box.OldY;
int oldBoxBottom = oldBoxTop + box.Height;
ทีนี้เพื่อทราบว่าการชนมาจากไหนคุณต้องหาด้านที่ตำแหน่งเดิมไม่ได้อยู่ในพื้นที่ปะทะและตำแหน่งใหม่ของมัน เพราะเมื่อคุณคิดว่ามันเป็นสิ่งที่เกิดขึ้นเมื่อคุณชนกัน: ด้านที่ไม่ชนเข้าสู่สี่เหลี่ยมผืนผ้าอื่น
นี่คือวิธีที่คุณสามารถทำได้ (ฟังก์ชั่นเหล่านี้สมมติว่ามีการชนกันของข้อมูลพวกเขาไม่ควรถูกเรียกหากไม่มีการชนกัน):
bool collidedFromLeft(Object otherObj)
{
return oldBoxRight < otherObj.Left && // was not colliding
boxRight >= otherObj.Left;
}
Rince และทำซ้ำ
bool collidedFromRight(Object otherObj)
{
return oldBoxLeft >= otherObj.Right && // was not colliding
boxLeft < otherObj.Right;
}
bool collidedFromTop(Object otherObj)
{
return oldBoxBottom < otherObj.Top && // was not colliding
boxBottom >= otherObj.Top;
}
bool collidedFromBottom(Object otherObj)
{
return oldBoxTop >= otherObj.Bottom && // was not colliding
boxTop < otherObj.Bottom;
}
ตอนนี้สำหรับการใช้งานจริงกับการตอบสนองการชนจากคำถามอื่น:
if (collidedFromTop(otherObj) || collidedFromBottom(otherObj))
obj.Velocity.Y = -obj.Velocity.Y;
if (collidedFromLeft(otherObj) || collidedFromRight(otherObj))
obj.Velocity.X = -obj.Velocity.X;
อีกครั้งนี่อาจไม่ใช่ทางออกที่ดีที่สุด แต่นั่นเป็นวิธีที่ฉันมักจะไปตรวจหาการชนกันของข้อมูล
เนื่องจากคำถามนี้บางส่วนเหมือนคำถามนี้ฉันจะใช้คำตอบบางส่วนอีกครั้งเพื่อลองตอบคำถามของคุณ
ให้กำหนดบริบทและตัวแปรบางอย่างเพื่อให้คำอธิบายต่อไปนี้เข้าใจได้ง่ายขึ้น แบบฟอร์มการเป็นตัวแทนที่เราจะใช้ที่นี่อาจไม่ตรงกับรูปแบบของข้อมูลของคุณเอง แต่มันควรจะง่ายกว่าที่จะเข้าใจวิธีนี้ (แน่นอนคุณสามารถใช้วิธีการต่อไปนี้โดยใช้การรับรองแบบอื่น ๆ เมื่อคุณเข้าใจหลักการ)
ดังนั้นเราจะพิจารณาแกนชิด bounding กล่อง (หรือOriented กล่องขอบเขต ) และย้าย Entity
กล่อง Bounding ประกอบด้วย 4 ด้านและเราจะนิยามแต่ละอันดังนี้:
Side1 = [x1, y1, x2, y2] (สองจุด [x1, y1] และ [x2, y2])
ย้าย Entity ถูกกำหนดให้เป็นเวกเตอร์ความเร็ว (ตำแหน่ง + ความเร็ว):
ตำแหน่ง[posX, Posy]และความเร็ว[SpeedX รวดเร็ว]
คุณสามารถกำหนดได้ว่าเวกเตอร์ AABB / OBB ใดจะถูกโจมตีโดยใช้วิธีการต่อไปนี้:
1 / ค้นหาจุดตัดระหว่างเส้นอนันต์ที่ผ่านแม้ว่าทั้งสี่ด้านของ AABB และเส้นไม่สิ้นสุดผ่านตำแหน่งเอนทิตี (การชนกันล่วงหน้า) ซึ่งใช้เวกเตอร์ความเร็วเอนทิตีเป็นตบ (คุณสามารถค้นหาจุดชนหรือหมายเลขที่ไม่ได้กำหนดซึ่งสอดคล้องกับแนวหรือเส้นที่ทับซ้อนกัน)
2 / เมื่อคุณทราบจุดตัดกัน (ถ้ามี) คุณสามารถค้นหาสิ่งที่อยู่ในขอบเขตกลุ่ม
3 / สุดท้ายหากยังมีหลายจุดในรายการ (เวกเตอร์ความเร็วสามารถผ่านหลาย ๆ ด้าน) คุณสามารถค้นหาจุดที่ใกล้ที่สุดจากจุดกำเนิดเอนทิตีโดยใช้ขนาดของเวกเตอร์จากจุดตัดไปยังจุดกำเนิดเอนทิตี
จากนั้นคุณสามารถกำหนดมุมของการชนโดยใช้ผลิตภัณฑ์แบบจุด
รายละเอียดเพิ่มเติม:
1 / ค้นหาทางแยก
a / หาค่าอนันต์ไลน์ (Ax + Bx = D) โดยใช้รูปแบบพารามิเตอร์ (P (t) = Po + tD)
จุดกำเนิด: Po = [posX, posY]
เวกเตอร์ทิศทาง: D = [speedX, speedY]
A = Dy = speedY
B = -Dx = -speedX
D = (Po.x * Dy) - (Po.y * Dx) = (posX speedY) - (posY speedX)
ขวาน + โดย = D <====> (SpeedY x) + (-speedX y) = (posX speedY) - (posY speedX)
ฉันใช้ค่าจุดเอนทิตีเพื่อแสดงให้เห็นถึงวิธีการ แต่นี่เป็นวิธีเดียวกันในการกำหนดเส้นไม่สิ้นสุด 4 ด้านของกล่องขอบเขต (ใช้ Po = [x1, y1] และ D = [x2-x1; y2-y1] แทน).
b / ถัดไปเพื่อหาจุดตัดของสองอนันต์เราสามารถแก้ปัญหาระบบดังต่อไปนี้:
A1x + B1x = D1 <== บรรทัดผ่านจุดเอนทิตีที่มีเวกเตอร์ความเร็วเป็นสลาฟ
A2x + B2x = D2 <== หนึ่งในบรรทัดที่ผ่านด้าน AABB
ซึ่งให้พิกัดต่อไปนี้สำหรับการสกัดกั้น:
การสกัดกั้นx = (( B 2 * D 1) - ( B 1 * D 2)) / (( A 1 * B 2) - ( A 2 * B 1)) การ
สกัดกั้นy = (( A 1 * D 2) - ( A 2 * D 1)) / (( A 1 * B 2) - ( A 2 * B 1))
หากตัวส่วน ((A1 * B2) - (A2 * B1)) เท่ากับศูนย์ดังนั้นทั้งสองเส้นจะเป็นแนวหรือทับกันมิฉะนั้นคุณควรหาจุดตัด
2 / ทดสอบขอบเขตของกลุ่ม เนื่องจากตรงไปตรงมาเพื่อยืนยันจึงไม่จำเป็นต้องมีรายละเอียดเพิ่มเติม
3 / ค้นหาจุดที่ใกล้ที่สุด หากยังมีหลายจุดในรายการเราสามารถค้นหาว่าด้านใดที่อยู่ใกล้จุดกำเนิดเอนทิตีมากที่สุด
a / กำหนดเวกเตอร์ที่ไปจากจุดตัดถึงจุดกำเนิดเอนทิตี
V = Po - Int = [Po.x - Int.x; Po.y - Int.y]
b / คำนวณขนาดเวกเตอร์
|| || V = sqrt (V.x² + V.y²)
4 / ตอนนี้คุณรู้แล้วว่าจะตีด้านไหนคุณสามารถกำหนดมุมโดยใช้ผลิตภัณฑ์ดอท
a / Let S = [x2-x1; y2-y1]เป็นเวกเตอร์ด้านข้างที่จะถูกตีและE = [speedX; speedY]เป็นเวกเตอร์ velocity ของเอนทิตี
การใช้กฎผลิตภัณฑ์จุดเวกเตอร์เรารู้ว่า
S · E = Sx Ex + Sy Ey
และ
S · E = || S || || || E เพราะ
ดังนั้นเราสามารถหาθได้โดยใช้สมการนี้สักหน่อย ...
cos θ = (S · E) / (|| S || || E ||)
θ = acos ((S · E) / (|| S || || E ||)
กับ
S · E = Sx * Ex + Sy * Ey
|| S || = sqrt (Sx² + Sy²)
|| E || = sqrt (Ex² + Ey²)
หมายเหตุ: ดังที่ฉันพูดในหัวข้อคำถามอื่น ๆ นี่อาจไม่ใช่วิธีที่มีประสิทธิภาพมากที่สุดหรือวิธีที่ง่ายที่สุดที่จะทำนี่เป็นเพียงสิ่งที่อยู่ในใจและบางส่วนของคณิตศาสตร์อาจช่วยได้
ฉันไม่ได้ตรวจสอบด้วยตัวอย่าง OBB ที่เป็นรูปธรรม (ฉันทำกับ AABB) แต่ก็ควรใช้งานได้เช่นกัน
วิธีง่ายๆคือการแก้ไขการชนจากนั้นแปลกล่องการชนกันของวัตถุที่เคลื่อนที่โดยหนึ่งพิกเซลในแต่ละทิศทางในทิศทางหันและดูว่าคนที่ทำให้เกิดการปะทะกัน
ถ้าคุณต้องการที่จะทำมัน "ถูกต้อง" และด้วยรูปทรงการชนกันหมุนหรือรูปหลายเหลี่ยมโดยพลการฉันขอแนะนำให้อ่านทฤษฎีการแยกแกน ซอฟต์แวร์ Metanet (ผู้เล่นที่สร้างเกม N) มีบทความเกี่ยวกับ SAT ที่ยอดเยี่ยม พวกเขายังหารือเกี่ยวกับฟิสิกส์ที่เกี่ยวข้อง
วิธีหนึ่งคือหมุนโลกรอบ ๆ สี่เหลี่ยมผืนผ้าของคุณ "โลก" ในกรณีนี้เป็นเพียงวัตถุที่คุณสนใจ: สี่เหลี่ยมผืนผ้าและลูกบอล คุณหมุนสี่เหลี่ยมผืนผ้ารอบ ๆ ศูนย์กลางของมันจนกระทั่งขอบเขตนั้นอยู่ในแนวเดียวกับแกน x- / y แล้วคุณหมุนลูกบอลด้วยจำนวนเดียวกัน
จุดสำคัญที่นี่คือคุณหมุนลูกบอลรอบจุดศูนย์กลางของสี่เหลี่ยมผืนผ้าไม่ใช่ของตัวเอง
จากนั้นคุณสามารถทดสอบการชนได้อย่างง่ายดายเหมือนกับสี่เหลี่ยมที่ไม่หมุนอื่น ๆ
อีกทางเลือกหนึ่งคือการปฏิบัติต่อสี่เหลี่ยมเป็นสี่ส่วนของเส้นตรงที่แตกต่างกันและทดสอบการชนกันของแต่ละเส้นแยกกัน สิ่งนี้จะช่วยให้คุณทดสอบการชนกันและหาว่าชนด้านใดในเวลาเดียวกัน
ฉันใช้มุมคงที่ในการคำนวณของฉัน แต่สิ่งนี้จะช่วยคุณได้บ้าง
void Bullet::Ricochet(C_Rect *r)
{
C_Line Line;
//the next two lines are because I detected
// a collision in my main loop so I need to take a step back.
x = x + ceil(speed * ((double)fcos(itofix(angle)) / 65536));
y = y + ceil(speed * ((double)fsin(itofix(angle)) / 65536));
C_Point Prev(x,y);
//the following checks our position to all the lines will give us
// an answer which line we will hit due to no lines
// with angles > 90 lines of a rect always shield the other lines.
Line = r->Get_Closest_Line(Prev);
int langle = 0;
if(!Line.Is_Horizontal()) //we need to rotate the line to a horizontal position
{
langle = Line.Get_Point1().Find_Fixed_Angle(Line.Get_Point2());
angle = angle - langle; //to give us the new angle of approach
}
//at this point the line is horizontal and the bullet is ready to be fixed.
angle = 256 - angle;
angle += langle;
}