การนำลวดพันสายไฟ (เช่น Worms Ninja Rope) มาใช้ในเครื่องมือฟิสิกส์ 2D


34

เมื่อไม่นานมานี้ฉันได้ทดลองใช้เชือกฟิสิกส์และฉันพบว่าวิธีแก้ปัญหา "มาตรฐาน" - การทำเชือกจากชุดของวัตถุที่พันกันด้วยสปริงหรือข้อต่อ - ไม่น่าพอใจ โดยเฉพาะอย่างยิ่งเมื่อการแกว่งเชือกมีความเกี่ยวข้องกับการเล่นเกม ฉันไม่สนใจเกี่ยวกับความสามารถของเชือกในการห่อหุ้มหรือหย่อนยาน

สำหรับการเล่นเกมสิ่งที่สำคัญคือความสามารถในการพันเชือกเพื่อล้อมรอบสภาพแวดล้อมและจากนั้นก็แกะออก มันไม่จำเป็นต้องทำตัวเหมือนเชือก - "ลวด" ซึ่งประกอบด้วยส่วนของเส้นตรงจะทำ นี่คือภาพประกอบ:

เชือกนินจาล้อมรอบสิ่งกีดขวาง

สิ่งนี้คล้ายกับ "Ninja Rope" จากเกมเวิร์ม

เพราะฉันใช้เครื่องมือฟิสิกส์ 2D - สภาพแวดล้อมของฉันประกอบด้วยรูปหลายเหลี่ยมนูน 2 มิติ (โดยเฉพาะฉันใช้ SAT ใน Farseer)

ดังนั้นคำถามของฉันคือ: คุณจะใช้เอฟเฟกต์ "การห่อ" ได้อย่างไร

ดูเหมือนว่าจะเห็นได้ชัดว่าการวางสายนั้นจะประกอบด้วยชุดของส่วนของเส้นที่ "แยก" และ "เข้าร่วม" และส่วนสุดท้าย (ใช้งานอยู่) ของบรรทัดนั้นซึ่งวัตถุที่กำลังเคลื่อนที่ติดอยู่นั้นจะเป็นรอยต่อที่มีความยาวคงที่

แต่อะไรคือคณิตศาสตร์ / อัลกอริธึมที่เกี่ยวข้องในการพิจารณาว่าเมื่อใดและที่ใดที่จะต้องแบ่งเซกเมนต์บรรทัดที่ใช้งาน และเมื่อมันจะต้องเข้าร่วมกับกลุ่มก่อนหน้า?

(ก่อนหน้านี้คำถามนี้ถามเกี่ยวกับการทำสิ่งนี้เพื่อสภาพแวดล้อมแบบไดนามิกด้วย - ฉันตัดสินใจที่จะแยกมันออกเป็นคำถามอื่น ๆ )

คำตอบ:


18

ในการกำหนดเวลาที่จะแยกเชือกคุณต้องดูบริเวณที่เชือกครอบคลุมแต่ละเฟรม สิ่งที่คุณทำคือคุณทำการตรวจสอบการชนกับพื้นที่ครอบคลุมและเรขาคณิตระดับของคุณ พื้นที่ที่ครอบคลุมการสวิงควรเป็นส่วนโค้ง หากมีการชนกันคุณต้องสร้างเซกเมนต์ใหม่ให้กับเชือก ตรวจสอบมุมที่ชนกับส่วนโค้งที่แกว่ง หากมีหลายมุมที่ชนกับส่วนโค้งสวิงคุณควรเลือกมุมที่ระหว่างเชือกระหว่างเฟรมก่อนหน้าและจุดการชนนั้นน้อยที่สุด

แผนภาพที่เป็นประโยชน์ของสถานการณ์เชือกนินจา

วิธีที่คุณใช้ในการตรวจจับการชนกันคือสำหรับรากของส่วนเชือกปัจจุบัน, O, ตำแหน่งสิ้นสุดของเชือกในเฟรมก่อนหน้า, A, ตำแหน่งสิ้นสุดของเชือกในเฟรมปัจจุบัน, B และแต่ละจุดมุม P ในรูปหลายเหลี่ยม ระดับเรขาคณิตคุณคำนวณ (OA x OP), (OP x OB) และ (OA x OB) โดยที่ "x" หมายถึงการใช้พิกัด Z ของผลิตภัณฑ์ข้ามระหว่างเวกเตอร์สองตัว หากผลลัพธ์ทั้งสามมีเครื่องหมายเหมือนกันลบหรือบวกและความยาวของ OP มีขนาดเล็กกว่าความยาวของส่วนเชือกจุด P จะอยู่ในพื้นที่ที่ครอบคลุมโดยการแกว่งและคุณควรแยกเชือก หากคุณมีจุดเข้าชนหลายจุดคุณจะต้องใช้จุดแรกที่กระทบเชือกซึ่งหมายถึงมุมที่ OA และ OP มีขนาดเล็กที่สุด ใช้ผลิตภัณฑ์ดอทเพื่อหามุม

สำหรับการเข้าร่วมกลุ่มทำการเปรียบเทียบระหว่างส่วนก่อนหน้าและส่วนโค้งของกลุ่มปัจจุบันของคุณ หากส่วนปัจจุบันหมุนจากด้านซ้ายไปด้านขวาหรือในทางกลับกันคุณควรเข้าร่วมกลุ่ม

สำหรับคณิตศาสตร์สำหรับการรวมกลุ่มเราจะใช้จุดเชื่อมต่อของเซ็กเมนต์เชือกก่อนหน้านี้เช่นเดียวกับกลุ่มที่เรามีสำหรับเคสแยก ตอนนี้คุณต้องเปรียบเทียบเวกเตอร์ QO, OA และ OB หากสัญลักษณ์ของ (QO x OA) แตกต่างจากสัญลักษณ์ของ (QO x OB) เชือกจะข้ามจากซ้ายไปขวาหรือกลับกันและคุณควรเข้าร่วมกลุ่ม สิ่งนี้อาจเกิดขึ้นได้หากเชือกหมุนได้ 180 องศาดังนั้นหากคุณต้องการให้เชือกพันรอบจุดเดียวในอวกาศแทนที่จะเป็นรูปหลายเหลี่ยมคุณอาจต้องการเพิ่มเคสพิเศษให้

แน่นอนว่าวิธีการนี้จะถือว่าคุณกำลังทำการตรวจจับการชนกันของวัตถุที่ห้อยลงมาจากเชือกเพื่อที่ว่ามันจะไม่จบลงในระดับเรขาคณิต


1
ปัญหาด้วยวิธีนี้คือข้อผิดพลาดความแม่นยำจุดลอยตัวทำให้เชือกสามารถผ่าน "ผ่าน" จุดได้
Andrew Russell

16

มันเป็นเวลานานแล้วที่ฉันเล่นเวิร์ม แต่จากสิ่งที่ฉันจำได้เมื่อเชือกพันรอบสิ่งต่าง ๆ มันมีเพียงส่วนเดียว (ตรง) ของเชือกที่เคลื่อนไหวได้ตลอดเวลา เชือกที่เหลือจะคงที่

ดังนั้นจึงมีฟิสิกส์ที่เกี่ยวข้องน้อยมาก ส่วนที่ใช้งานสามารถจำลองเป็นสปริงตัวแข็งที่มีมวลอยู่ด้านท้าย

บิตที่น่าสนใจจะเป็นตรรกะสำหรับการแยก / เข้าร่วมส่วนที่ไม่ได้ใช้งานของเชือกไปยัง / จากส่วนที่ใช้งานอยู่

ฉันคิดว่าจะมีการดำเนินการหลัก 2 อย่าง:

'แยก' - เชือกได้ตัดบางอย่าง แยกที่จุดตัดเป็นส่วนที่ไม่ได้ใช้งานและส่วนใหม่ที่สั้นกว่าและใช้งานอยู่

'เข้าร่วม' - เชือกที่เคลื่อนไหวอยู่ในตำแหน่งที่ไม่มีจุดตัดที่ใกล้ที่สุดอีกต่อไป (นี่อาจเป็นการทดสอบผลิตภัณฑ์มุม / จุดแบบง่าย ๆ ) เข้าร่วม 2 ส่วนอีกครั้งสร้างส่วนใหม่ยาวขึ้นใช้งานอยู่

ในฉากที่สร้างจากรูปหลายเหลี่ยม 2 มิติจุดแยกทั้งหมดควรอยู่ที่จุดยอดสุดของตาข่ายกันการชน การตรวจจับการชนอาจทำให้บางสิ่งบางอย่างลดลงตามเส้นของ 'ถ้าเชือกผ่านจุดสุดยอดในการอัปเดตนี้ให้แยก / เข้าร่วมเชือกที่จุดสุดยอด?


2
ผู้ชายคนนี้อยู่ในจุดที่ ... จริง ๆ แล้วมันไม่ได้เป็นฤดูใบไม้ผลิ "แข็ง" คุณเพียงหมุนเส้นตรงไปรอบ ๆ ...
speeder

คำตอบของคุณถูกต้องทางเทคนิค แต่ฉันคิดว่าการมีส่วนของเส้นแบ่งและแยกและเข้าร่วมพวกเขาชัดเจน ฉันสนใจในอัลกอริทึม / คณิตศาสตร์ที่แท้จริงเพื่อทำสิ่งนั้น ฉันทำให้คำถามของฉันเฉพาะเจาะจงมากขึ้น
Andrew Russell

3

ตรวจสอบว่าเชือกนินจาในGusanosถูกนำไปใช้อย่างไร:

  • เชือกทำตัวเหมือนอนุภาคจนกระทั่งมันยึดติดกับบางสิ่ง
  • เชือกจะใช้แรงกับตัวหนอน
  • การเชื่อมต่อกับวัตถุแบบไดนามิก (เช่นเวิร์มอื่น ๆ ) ยังคงเป็นสิ่งที่ต้องทำ: ในรหัสนี้
  • ฉันจำไม่ได้ว่าสนับสนุนห่อวัตถุหรือมุม ...

ข้อความที่ตัดตอนมาที่เกี่ยวข้องจากninjarope.cpp :


void NinjaRope::think()
{
    if ( m_length > game.options.ninja_rope_maxLength )
        m_length = game.options.ninja_rope_maxLength;

    if (!active)
        return;

    if ( justCreated && m_type->creation )
    {
        m_type->creation->run(this);
        justCreated = false;
    }

    for ( int i = 0; !deleteMe && i < m_type->repeat; ++i)
    {
        pos += spd;

        BaseVec<long> ipos(pos);

        // TODO: Try to attach to worms/objects

        Vec diff(m_worm->pos, pos);
        float curLen = diff.length();
        Vec force(diff * game.options.ninja_rope_pullForce);

        if(!game.level.getMaterial( ipos.x, ipos.y ).particle_pass)
        {
            if(!attached)
            {
                m_length = 450.f / 16.f - 1.0f;
                attached = true;
                spd.zero();
                if ( m_type->groundCollision  )
                    m_type->groundCollision->run(this);
            }
        }
        else
            attached = false;

        if(attached)
        {
            if(curLen > m_length)
            {
                m_worm->addSpeed(force / curLen);
            }
        }
        else
        {
            spd.y += m_type->gravity;

            if(curLen > m_length)
            {
                spd -= force / curLen;
            }
        }
    }
}

1
อืม ... นี่ดูเหมือนจะไม่ตอบคำถามของฉันเลย จุดทั้งหมดของคำถามของฉันคือการพันรอบโลกที่ทำจากรูปหลายเหลี่ยม Gusanos ดูเหมือนจะไม่มีการล้อมรอบและบิตแมปโลก
Andrew Russell

1

ฉันเกรงว่าฉันจะไม่สามารถให้อัลกอริธึมที่เป็นรูปธรรมกับคุณจากหัวของฉันได้ แต่มันเกิดขึ้นกับฉันว่ามีเพียงสองสิ่งเท่านั้นที่สำคัญสำหรับการตรวจจับการชนกันของเชือกนินจา: สิ่งใด ๆ ภายในรัศมีของ "การแบ่ง" ล่าสุดเท่ากับความยาวที่เหลืออยู่ของส่วน และทิศทางการแกว่งในปัจจุบัน (ตามเข็มนาฬิกาหรือทวนเข็มนาฬิกา) หากคุณสร้างรายการมุมชั่วคราวจากจุดยอด "แยก" ไปยังแต่ละจุดยอดที่อยู่ใกล้เคียงอัลกอริทึมของคุณจะต้องใส่ใจว่าส่วนของคุณกำลังจะแกว่งผ่านมุมนั้นสำหรับจุดสุดยอดที่กำหนดหรือไม่ ถ้าเป็นเช่นนั้นคุณจะต้องทำการแยกซึ่งเป็นเรื่องง่ายเหมือนพาย - มันเป็นเพียงแค่เส้นจากจุดสุดยอดการแยกล่าสุดไปยังการแยกใหม่แล้วคำนวณส่วนที่เหลือใหม่

ฉันคิดว่าเฉพาะจุดสูงสุดเท่านั้น หากคุณตกอยู่ในอันตรายจากการชนส่วนหนึ่งระหว่างจุดยอดบนสิ่งกีดขวางจากนั้นการตรวจจับการชนปกติของคุณสำหรับคนที่แขวนอยู่ที่ปลายเชือกควรเตะเข้ากล่าวอีกนัยหนึ่งเชือกของคุณจะเป็นเพียง "อุปสรรค์" บน มุมดังนั้นกลุ่มระหว่างจะไม่สำคัญ

ขออภัยฉันไม่ได้เป็นรูปธรรม แต่หวังว่าจะทำให้คุณได้ในที่ที่คุณต้องการเป็นแนวคิดเพื่อทำให้สิ่งนี้เกิดขึ้น :)


1

นี่คือโพสต์ที่มีลิงก์ไปยังเอกสารเกี่ยวกับการจำลองประเภทเดียวกัน (ในบริบททางวิศวกรรม / เชิงวิชาการมากกว่าสำหรับเกม): https://gamedev.stackexchange.com/a/10350/6398

ฉันพยายามอย่างน้อยสองวิธีที่แตกต่างกันในการตรวจจับการชน + การตอบสนองสำหรับการจำลอง "wire" แบบนี้ (ตามที่เห็นในเกม Umihara Kawase); อย่างน้อยฉันคิดว่านี่คือสิ่งที่คุณตามมา - ดูเหมือนจะไม่มีคำเฉพาะสำหรับการจำลองแบบนี้ฉันแค่เรียกว่า "ลวด" มากกว่า "เชือก" เพราะดูเหมือนว่าคนส่วนใหญ่ คิดว่า "เชือก" มีความหมายเหมือนกันกับ "ห่วงโซ่ของอนุภาค" และถ้าคุณต้องการพฤติกรรม stick-ish ของเชือกนินจา (เช่นสามารถกดและดึงได้) นี่เป็นเหมือนลวดที่แข็งกว่าเชือก อย่างไรก็ตาม..

คำตอบของ Pekuja นั้นดีคุณสามารถใช้การตรวจจับการชนกันอย่างต่อเนื่องโดยการแก้ปัญหาเมื่อพื้นที่ที่เซ็นชื่อของทั้งสามจุดเป็น 0

(ฉันจำ OTOH ไม่เต็มที่ แต่คุณสามารถเข้าใกล้มันได้ดังนี้: หาเวลา t เมื่อจุด a อยู่ในบรรทัดที่ผ่าน b, c (ฉันคิดว่าฉันทำสิ่งนี้โดยการหาจุดเมื่อ (ab, cb) = 0 เพื่อค้นหาค่าของ t) จากนั้นให้เวลาที่ถูกต้อง 0 <= t <1 ค้นหาตำแหน่งพารามิเตอร์ s ของ a บนเซ็กเมนต์ bc เช่น a = (1-s) b + s c และถ้า a อยู่ระหว่าง b และ c (เช่นถ้า 0 <= s <= 1) เป็นการชนที่ถูกต้อง

AFAICR คุณสามารถเข้าใกล้มันได้เช่นกัน (เช่นแก้หา s แล้วเสียบเข้าไปหา t) แต่มันใช้งานง่ายกว่ามาก (ฉันขอโทษถ้าสิ่งนี้ไม่สมเหตุสมผลฉันไม่มีเวลาขุดบันทึกของฉันและมันก็ไม่กี่ปีที่ผ่านมา!)

ดังนั้นตอนนี้คุณสามารถคำนวณทุกครั้งที่เกิดเหตุการณ์ (เช่นควรใส่หรือลบโหนดเชือก) ประมวลผลเหตุการณ์แรกสุด (แทรกหรือลบโหนด) จากนั้นทำซ้ำ / เรียกคืนจนกว่าจะไม่มีเหตุการณ์เพิ่มเติมระหว่าง t = 0 และ t = 1

หนึ่งคำเตือนเกี่ยวกับวิธีการนี้: หากวัตถุที่เชือกสามารถพันเป็นแบบไดนามิก (โดยเฉพาะอย่างยิ่งถ้าคุณกำลังจำลองพวกเขาและผลกระทบที่มีต่อเชือกและในทางกลับกัน) อาจมีปัญหาหากวัตถุเหล่านั้นตัด / ผ่าน อื่น ๆ - ลวดสามารถพันกัน และมันจะเป็นเรื่องท้าทายอย่างยิ่งที่จะป้องกันไม่ให้การโต้ตอบ / การเคลื่อนไหว (มุมของวัตถุลื่นไถลกัน) ในการจำลองฟิสิกส์แบบ box2d .. การเจาะระหว่างวัตถุจำนวนเล็กน้อยเป็นพฤติกรรมปกติในบริบทนั้น

(อย่างน้อย .. นี่เป็นปัญหากับหนึ่งในการติดตั้ง "wire" ของฉัน)

วิธีการแก้ปัญหาที่แตกต่างกันซึ่งมีเสถียรภาพมากขึ้น แต่พลาดการชนในบางเงื่อนไขคือการใช้การทดสอบแบบคงที่ (เช่นไม่ต้องกังวลเกี่ยวกับการสั่งซื้อตามเวลาเพียงแค่แบ่งการเรียกซ้ำแต่ละส่วนในการชนกัน แข็งแกร่งมากขึ้น - ลวดจะไม่พันกันที่มุมและการเจาะเล็กน้อยจะไม่เป็นไร

ฉันคิดว่าวิธีการของ Pekuja ก็ใช้ได้เช่นกัน แต่ก็มีวิธีอื่นเช่นกัน วิธีการหนึ่งที่ฉันใช้คือการเพิ่มข้อมูลการชนกันของข้อมูล: ในแต่ละจุดยอด v ในโลก (เช่นมุมของรูปร่างที่เชือกสามารถพันได้) เพิ่มจุด u เพื่อสร้างส่วนของเส้นกำกับที่ uv จุด "ข้างในมุม" (เช่นภายในโลก, "หลัง" v; ในการคำนวณคุณสามารถโยนรังสีเข้าจาก v ไปตามปกติและหยุดระยะหนึ่งหลังจาก v หรือก่อนที่รังสีตัดกับขอบโลกและ ออกจากพื้นที่ที่เป็นของแข็งหรือคุณสามารถวาดส่วนต่างๆในโลกด้วยตนเองโดยใช้เครื่องมือแก้ไข / แสดงผลระดับภาพ)

อย่างไรก็ตามตอนนี้คุณมีชุด "corner linesegs" uv; สำหรับแต่ละ uv และแต่ละส่วน ab ในสายตรวจสอบ ab และ uv intersect (เช่น static, บูลีน lineseg-lineseg query intersection); ถ้าเป็นเช่นนั้น recurse (แยก lineseg ab เป็น av และ vb, เช่น insert v), บันทึกทิศทางของเชือกที่งอที่ v. จากนั้นสำหรับคู่ของแต่ละคู่ที่อยู่ติดกัน ab, bc ในสาย, ทดสอบว่าทิศทางโค้งงอที่ b เป็นเช่นเดียวกับเมื่อสร้างข (การทดสอบ "ทิศทางโค้ง" ทั้งหมดนี้เป็นเพียงการทดสอบพื้นที่ที่มีลายเซ็น) ถ้าไม่รวมทั้งสองส่วนเป็น ac (เช่นลบ b)

หรือบางทีฉันอาจรวมแล้วแยกฉันลืม - แต่มันใช้งานได้อย่างน้อยหนึ่งในสองคำสั่งที่เป็นไปได้! :)

เมื่อพิจารณาจากส่วนของเส้นลวดทั้งหมดที่คำนวณสำหรับเฟรมปัจจุบันคุณสามารถจำลองข้อ จำกัด ระยะทางระหว่างจุดปลายสายทั้งสอง (และคุณสามารถเกี่ยวข้องกับจุดตกแต่งภายในได้เช่นจุดเชื่อมต่อระหว่างเส้นลวดและโลก )

อย่างไรก็ตามหวังว่านี่จะเป็นการใช้งาน ... เอกสารในโพสต์ที่ฉันเชื่อมโยงด้วยควรให้แนวคิดบางอย่างแก่คุณ


0

วิธีการหนึ่งในการทำเช่นนี้คือการจำลองเชือกเป็นอนุภาคที่สามารถชนกันได้ซึ่งเชื่อมต่อกันด้วยสปริง (คนที่ค่อนข้างแข็งอาจเป็นเพียงแค่กระดูกแทน) อนุภาคชนกับสิ่งแวดล้อมทำให้แน่ใจว่าเชือกพันรอบสิ่งของ

นี่คือตัวอย่างของแหล่งข้อมูล: http://www.ewjordan.com/rgbDemo/

(เลื่อนไปทางขวาในระดับแรกมีเชือกสีแดงที่คุณสามารถโต้ตอบได้)


2
เอ่อ - นี่คือสิ่งที่ฉันไม่ต้องการโดยเฉพาะ (ดูคำถาม)
Andrew Russell

อา นั่นไม่ชัดเจนจากคำถามเดิม ขอขอบคุณที่สละเวลาชี้แจงให้ชัดเจน (ไดอะแกรมที่ยอดเยี่ยม!) ฉันยังคงมีชุดของข้อต่อคงที่ที่เล็กมากซึ่งต่างจากการแยกแบบไดนามิก - ยกเว้นว่ามันเป็นปัญหาประสิทธิภาพการทำงานขนาดใหญ่ในสภาพแวดล้อมของคุณมันง่ายกว่าในการเขียนโค้ด
Rachel Blum
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.