ฉันจะลบเอนทิตีออกจากลูปเกมของฉันได้อย่างไรเมื่อมันตาย


16

ตกลงดังนั้นฉันมีรายการใหญ่ของเอนทิตีทั้งหมดของฉันซึ่งฉันวนซ้ำและอัปเดต ใน AS3 ฉันสามารถเก็บสิ่งนี้เป็นแบบ Array (ความยาวแบบไดนามิกไม่พิมพ์) เวกเตอร์ (พิมพ์) หรือรายการที่เชื่อมโยง (ไม่ใช่แบบดั้งเดิม) ตอนนี้ฉันกำลังใช้ Array แต่ฉันวางแผนที่จะเปลี่ยนเป็น Vector หรือรายการที่เชื่อมโยงถ้ามันเร็วขึ้น

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


ความยาวของ Vector ไม่คงที่ แต่พิมพ์และนั่นทำให้เหนือกว่า Array ข้อเสียคือไม่มีไวยากรณ์ด่วนเพื่อกำหนดรายการที่เติมไว้ล่วงหน้า แต่คุณไม่จำเป็นต้องคิด
Bart van Heukelom

คำตอบ:


13

ฉันจะเก็บเพิ่ม / ลบทั้งหมดในรายการแยกต่างหากและดำเนินการเหล่านั้นหลังจากฉันทำซ้ำผ่านห่วงการปรับปรุง


10

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


1
+1 สำหรับ flixel การรีไซเคิลdeadสิ่งที่ช่วยจริงๆด้วยประสิทธิภาพ
Snow Blind

3

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

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

void updateGame()
{
  // updateEntities()
  Entity* pSrcEntity = &mEntities[0];
  Entity* pDstEntity = &mEntities[0];
  newNumEntities = 0;
  for (int i = 0; i < numEntities; i++)
  {
    if (!pSrcEntity->isDead)
    {
       // could be inline but whatever.
       updateEntity(pDstEntity, pSrcEntity);
       // if entity just died, don't update the pDstEntity pointer, 
       // and just let the next entity updated overwrite it.
       if (!pDstEntity->isDead)
       {
          pDstEntity++;
          newNumEntities++;
       }
    }
    pSrcEntity++;
  }
}
numEntities = newNumEntities;

นี่คือลักษณะของรูปแบบนี้:

  • ความเป็นปึกแผ่นตามธรรมชาติของเอนทิตี (แม้ว่าจะมีความล่าช้า 1 เฟรมก่อนที่จะสามารถเรียกคืนเอนทิตีสล็อตได้)
  • ไม่มีปัญหาการสั่งซื้อซ้ำแบบสุ่ม
  • ในขณะที่รายการที่เชื่อมโยงเป็นทวีคูณมีการแทรก / ลบ O (1) แต่เป็นการยากที่จะดึงข้อมูลล่วงหน้าเพื่อซ่อนแคชเวลาแฝงที่เหมาะสม การเก็บไว้ในอาร์เรย์ขนาดกะทัดรัดช่วยให้เทคนิคการดึงข้อมูลล่วงหน้าทำงานได้เป็นอย่างดี
  • ในกรณีที่มีการทำลายวัตถุหลายอย่างคุณไม่จำเป็นต้องทำสำเนากะซ้ำซ้อนเพื่อรักษาความสงบเรียบร้อยและความกะทัดรัด
  • คุณใช้ประโยชน์จากการสัมผัสข้อมูลที่จะต้องมีแคชอยู่แล้วในระหว่างการอัพเดต
  • มันทำงานได้ดีถ้าแหล่งที่มาและปลายทางของเอนทิตีของคุณแยกอาร์เรย์ จากนั้นคุณสามารถบัฟเฟอร์สองครั้งที่อาร์เรย์เอนทิตีของคุณเพื่อใช้ประโยชน์จากมัลติคอร์ / เช่น หนึ่งเธรดที่อัพเดต / การเขียนเอนทิตีสำหรับเฟรม N ในขณะที่เธรดอื่นกำลังแสดงเอนทิตีของเฟรมก่อนหน้าสำหรับเฟรม N-1
  • ความกะทัดรัดหมายถึงการที่ DMA ง่ายขึ้นมากในหน่วยประมวลผลแบบ heterogenous เพื่อให้ CPU ทำงานได้มากขึ้นเช่น SPU หรือ GPU

+1 ฉันชอบสิ่งนี้. แม้ว่าฉันจะไม่ต้องการคำสั่งอัปเดตภายในสระว่ายน้ำ แต่ฉันจะเพิ่มลงในกระเป๋าของสิ่งต่าง ๆ ที่ต้องจำหากฉันพบเจอกับสถานการณ์: o)
Kaj

2

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

เราแค่พูดถึงการรวมทรัพยากรกันในห้องแชทจริงๆแล้ว มันเป็นการฝึกฝนที่ดีมากและดีใจที่ได้ยินว่าคุณกำลังทำเช่นนั้น :)


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

ว้าวจุดที่ดีมาก Kaj! :)
Ricket

2

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

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

ฉันใช้โครงสร้างข้อมูลหลายเหลี่ยมในหลายโครงการและมีความสุขมากกับพวกเขา

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


1
ฉันคิดว่าคุณแนะนำรายการที่เชื่อมโยงสองครั้งที่นี่หรือไม่ (หน้า / หลัง)? นอกจากนี้: คุณกำลังแนะนำการเรียงลำดับของพูลบนองค์ประกอบลิงก์หรือคุณกำลังจัดสรรเจ้าของพอยน์เตอร์แต่ละตัวในรายการที่ลิงก์หรือไม่?
Simon

ใช่มันจะต้องเป็นรายการที่เชื่อมโยงสองครั้งที่เหมาะที่สุดสำหรับงานนั้น ขอบคุณสำหรับการชี้ให้เห็น! เกี่ยวกับการใช้งานไอเท็มอีกครั้ง: ฉันกำลังคิดถึงคลาสการรวมกำไร / การจัดโครงสร้างข้อมูลแบบพิเศษที่สร้างออบเจ็กต์ใหม่ตามต้องการหรือใช้อินสแตนซ์ที่มีอยู่หากมีบางอย่างในพูล ดังนั้นจึงเป็นการดีที่จะลบรายการ "ตาย" ออกจากรายการและเพิ่มลงในกลุ่มเพื่อใช้ในภายหลัง
bummzack

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

@caspin ใช่แน่นอน หากคุณใช้รายการเดียวที่เชื่อมโยงคุณจะต้องติดตามโหนดก่อนหน้าและเชื่อมโยงnextตัวชี้ไปยังโหนดหลังจากลบแล้ว หากคุณไม่ต้องการให้ยุ่งยากในการทำสิ่งนี้ด้วยตัวเองรายการที่เชื่อมโยงสองครั้งจะเป็น DataStructure ของตัวเลือก
bummzack

1

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

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


0

วิธีแก้ปัญหาที่สะอาดและทั่วไปอย่างหนึ่งที่ฉันพบใน lib i ที่ใช้คือการใช้แผนที่ที่ล็อคได้

คุณมีการปฏิบัติการ 2 อย่างlock()และunlock()ในขณะที่คุณวนซ้ำแผนที่คุณจะต้องlock()จากจุดนี้ทุกการดำเนินการที่เปลี่ยนแผนที่จะไม่มีผลใช้งาน แต่มันจะถูกผลักเข้าไปCommandQueueที่จะทำงานทันทีที่คุณโทรunlock()ที่จะทำงานเมื่อคุณเรียก

ดังนั้นการลบเอนทิตีจะมีรหัสเทียมต่อไปนี้:

void lockableMap::remove(std::string id) {
   if(isLocked) {
       commandQueue.add(new RemoveCommand(id));
   } else {
       //remove element from map
   }

และเมื่อคุณ unlock()

isLocked = false
commandQueue.execute(this);

สิ่งเดียวที่คุณต้องพิจารณาคือคุณจะลบเอนทิตีหลังจากลูปเท่านั้น

แก้ไข: นี่คือทางออกที่เสนอโดย Simon



0

ฉันมีสองวิธี

เมื่อคุณเรียกวัตถุที่จะลบมันจะตั้งค่าสถานะที่สอง:

1.One ที่จะบอกคอนเทนเนอร์ว่าวัตถุถูกลบไปแล้ว

2.One จะบอกว่าวัตถุใดที่ถูกร้องขอให้ลบคอนเทนเนอร์

void object::deleteObject()
{
    container->objectHasBeenDeleted = true;
    isToDelete = true;
}

หนึ่ง ใช้เวกเตอร์ของวัตถุ

std::vector<object*> objects;

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

void container::update()
{
    if (objectHasBeenDeleted)
    {
        std::vector<object*>::iterator ListIterator;
        for(ListIterator=objects.begin(); ListIterator!=objects.end();)
        {
            if( (*ListIterator)->isToDelete )
            {
                ListIterator = objects.erase(ListIterator);
                delete *ListIterator;
            }
            else {
                ++ListIterator;
            }
        }
    objectHasBeenDeleted = false;
    }
}

Two ใช้ a (pointer to a) vector ของวัตถุ

std::vector<object*> *objects;

ในฟังก์ชั่นการอัพเดทหากวัตถุถูกลบให้ทำซ้ำผ่านวัตถุและเพิ่มวัตถุที่ไม่ถูกลบไปยังเวกเตอร์ใหม่ ลบเวกเตอร์วัตถุและตั้งค่าตัวชี้เป็นเวกเตอร์ใหม่

void container::update()
{
    if (objectHasBeenDeleted)
    {
        std::vector<object*> *newVector;
        unsigned long i;
        for (i = 0; i < objects->size(); i++)
        {
            if (!objects->at(i)->isToDelete)
            {
                newVector->push_back(objects->at(i));
            }
        }
        delete objects;
        objects = newVector;
        objectHasBeenDeleted = false;
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.