เป็นวิธีที่ดีในการจัดเก็บข้อมูล tilemap อะไร


13

ฉันกำลังพัฒนา platformer 2D กับเพื่อน ๆ เราได้ใช้ชุดเริ่มต้น XNA Platformer ซึ่งใช้ไฟล์. txt เพื่อจัดเก็บแผนที่ย่อย ในขณะที่ง่าย แต่ก็ไม่ได้ให้การควบคุมและความยืดหยุ่นเพียงพอกับการออกแบบระดับ ตัวอย่าง: สำหรับเนื้อหาหลายเลเยอร์จำเป็นต้องใช้หลายไฟล์วัตถุแต่ละชิ้นได้รับการแก้ไขบนกริดไม่อนุญาตให้มีการหมุนของวัตถุจำนวนอักขระที่ จำกัด เป็นต้นดังนั้นฉันจึงทำการวิจัยเกี่ยวกับวิธีการจัดเก็บข้อมูลระดับ และไฟล์แผนที่

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

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

เหตุผลสำหรับ JSON / XML: ไฟล์ที่สามารถแก้ไขได้ด้วยสายตาการเปลี่ยนแปลงสามารถติดตามผ่าน SVN ได้ง่ายขึ้นมาก แต่มีเนื้อหาซ้ำแล้วซ้ำอีก

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

ปัจจุบันไฟล์มีลักษณะดังนี้:

....................
....................
....................
....................
....................
....................
....................
.........GGG........
.........###........
....................
....GGG.......GGG...
....###.......###...
....................
.1................X.
####################

1 - จุดเริ่มต้นของผู้เล่น X - ระดับทางออก,. - พื้นที่ว่าง, # - แพลตฟอร์ม, G - อัญมณี


2
คุณกำลังใช้ "รูปแบบ" ใดอยู่ เพียงแค่พูดว่า "ไฟล์ข้อความ" เพียงหมายความว่าคุณไม่ได้บันทึกข้อมูลไบนารี เมื่อคุณพูดว่า "ไม่ได้การควบคุมและความยืดหยุ่นพอที่" ทำอะไรโดยเฉพาะปัญหาที่คุณกำลังทำงานในการ? ทำไมการทอยระหว่าง XML และ SQLite ที่จะเป็นตัวกำหนดคำตอบ blog.stackoverflow.com/2011/08/gorilla-vs-shark
Tetrad

ฉันจะเลือกใช้ JSON เนื่องจากสามารถอ่านได้มากกว่า
Den

3
ทำไมผู้คนถึงคิดถึงการใช้ SQLite สำหรับสิ่งเหล่านี้ มันเป็นฐานข้อมูลเชิงสัมพันธ์ ; ทำไมคนคิดว่าฐานข้อมูลเชิงสัมพันธ์ทำให้รูปแบบไฟล์ที่ดี?
Nicol Bolas

1
@StephenTierney: ทำไมคำถามนี้จึงเปลี่ยนจากการเป็น XML กับ SQLite ไปเป็น JSON กับฐานข้อมูลทุกประเภท? คำถามของฉันคือทำไมมันไม่เพียงแค่ "เป็นวิธีที่ดีในการจัดเก็บข้อมูล tilemap คืออะไร" คำถาม X vs. Y นี้เป็นกฎเกณฑ์และไม่มีความหมาย
Nicol Bolas

1
@Kylotan: แต่มันทำงานได้ไม่ดีนัก มันยากที่จะแก้ไขอย่างไม่น่าเชื่อเพราะคุณสามารถแก้ไขฐานข้อมูลผ่านคำสั่ง SQL เท่านั้น เป็นการยากที่จะอ่านเนื่องจากตารางไม่ใช่วิธีที่มีประสิทธิภาพในการดูแผนที่ย่อยและมีความเข้าใจในสิ่งที่เกิดขึ้น และในขณะที่สามารถทำการค้นหาได้ขั้นตอนการค้นหานั้นซับซ้อนอย่างไม่น่าเชื่อ การทำงานบางอย่างเป็นสิ่งสำคัญ แต่ถ้ามันจะทำให้กระบวนการพัฒนาเกมนั้นหนักขึ้นจริง ๆ มันก็ไม่คุ้มที่จะใช้เส้นทางสั้น ๆ
Nicol Bolas

คำตอบ:


14

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

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

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

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

ทุกอย่างที่พูดมาฉันคิดว่าคุณกำลังกระโดดปืนนิดหน่อย

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

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


9
  • XML: ง่ายต่อการแก้ไขด้วยมือ แต่เมื่อคุณเริ่มโหลดข้อมูลจำนวนมากมันจะช้าลง
  • SQLite: วิธีนี้อาจจะดีกว่าถ้าคุณต้องการดึงข้อมูลจำนวนมากในคราวเดียวหรือแม้แต่ชิ้นเล็ก ๆ อย่างไรก็ตามหากคุณไม่ได้ใช้สิ่งนี้ในที่อื่น ๆ ในเกมของคุณฉันคิดว่ามันเกินความเป็นจริงสำหรับแผนที่ของคุณ

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

นอกจากนี้หากคุณไปเส้นทางนี้ฉันขอแนะนำตัวแปรแรกของคุณในไฟล์เป็นหมายเลขรุ่น

ลองพิจารณาตัวอย่างคลาสแผนที่ (ง่ายมาก):

class Map {
    private const short MapVersion = 1;
    public string Name { get; set; }

    public void SaveMap(string filename) {
        //set up binary writer
        bw.Write(MapVersion);
        bw.Write(Name);
        //close/dispose binary writer
    }
    public void LoadMap(string filename) {
        //set up binary reader
        short mapVersion = br.ReadInt16();
        Name = br.ReadString();
        //close/dispose binary reader
    }
}

ตอนนี้สมมติว่าคุณต้องการเพิ่มสถานที่ให้บริการใหม่ลงในแผนที่ของคุณพูดListของPlatformวัตถุ ฉันเลือกสิ่งนี้เพราะมันเกี่ยวข้องกับอีกเล็กน้อย

ก่อนอื่นคุณเพิ่มMapVersionและเพิ่มList:

private const short MapVersion = 2;
public string Name { get; set; }
public List<Platform> Platforms { get; set; }

จากนั้นอัปเดตวิธีการบันทึก:

public void SaveMap(string filename) {
    //set up binary writer
    bw.Write(MapVersion);
    bw.Write(Name);
    //Save the count for loading later
    bw.Write(Platforms.Count);
    foreach(Platform plat in Platforms) {
        //For simplicities sake, I assume Platform has it's own
        // method to write itself to a file.
        plat.Write(bw);
    }
    //close/dispose binary writer
}

จากนั้นและนี่คือที่คุณเห็นประโยชน์จริง ๆ อัปเดตวิธีโหลด:

public void LoadMap(string filename) {
    //set up binary reader
    short mapVersion = br.ReadInt16();
    Name = br.ReadString();
    //Create our platforms list
    Platforms = new List<Platform>();
    if (mapVersion >= 2) {
        //Version is OK, let's load the Platforms
        int mapCount = br.ReadInt32();
        for (int i = 0; i < mapCount; i++) {
            //Again, I'm going to assume platform has a static Read
            //  method that returns a Platform object
            Platforms.Add(Platform.Read(br));
        }
    } else {
        //If it's an older version, give it a default value
        Platforms.Add(new Platform());
    }
    //close/dispose binary reader
}

อย่างที่คุณเห็นLoadMapsวิธีการนี้ไม่เพียง แต่สามารถโหลดแผนที่เวอร์ชั่นล่าสุด แต่ยังเวอร์ชั่นที่เก่ากว่า! เมื่อโหลดแผนที่เก่าคุณจะสามารถควบคุมค่าเริ่มต้นที่ใช้


9

เรื่องสั้น

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

เรื่องยาว

คำถามนี้ทำให้ฉันนึกถึงว่าเมื่อไหร่ที่ Notch รู้สึกอยากจะเข้า Ludum Dare ของเขาเมื่อไม่กี่เดือนก่อนและฉันต้องการแบ่งปันในกรณีที่คุณไม่รู้ว่าเขาทำอะไร ฉันคิดว่ามันน่าสนใจจริงๆ

โดยทั่วไปเขาใช้บิตแมปเพื่อจัดเก็บระดับของเขา แต่ละพิกเซลในบิตแมปตรงกับ "ไทล์" หนึ่งในโลก (ไม่ใช่ไทล์ในกรณีของเขาเพราะมันเป็นเกม raycasted แต่ใกล้พอ) ตัวอย่างของหนึ่งในระดับของเขา:

ป้อนคำอธิบายรูปภาพที่นี่

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

และที่ดีที่สุดคือคุณไม่จำเป็นต้องมีตัวประมวลผลเนื้อหาที่กำหนดเองเพราะคุณสามารถโหลดมันลงใน XNA เหมือนกับ Texture2D อื่น ๆ และอ่านพิกเซลกลับมาเป็นวง

IIRC เขาใช้จุดสีแดงบริสุทธิ์บนแผนที่เพื่อส่งสัญญาณศัตรูและขึ้นอยู่กับค่าของช่องอัลฟ่าสำหรับพิกเซลนั้นมันจะกำหนดประเภทของศัตรู (เช่นค่าอัลฟา 255 อาจเป็นค้างคาวในขณะที่ 254 จะเป็น ซอมบี้หรืออย่างอื่น)

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

อาจมีวิธีการมัลติเพล็กซ์ข้อมูลมากยิ่งขึ้นใน 4 ช่องทางเหล่านั้น หากใครมีความคิดเกี่ยวกับเรื่องนี้แจ้งให้เราทราบ :)


3

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

หากคุณต้องการทำสิ่งที่ "ถูกต้อง" คุณสร้างรูปแบบไบนารีตามที่ Drackir แนะนำ แต่ถ้าคุณเลือกอีกทางเลือกหนึ่งด้วยเหตุผลโง่ ๆ มันอาจจะไม่กลับมากัดคุณได้


1
ใช่มันขึ้นอยู่กับขนาดอย่างแน่นอน ฉันมีแผนที่ประมาณ 161,000 แผ่น ซึ่งแต่ละชั้นมี 6 ชั้น ฉันเคยมีมันใน XML แต่มันเป็น 82MB และโหลดตลอดไป ตอนนี้ฉันบันทึกในรูปแบบไบนารีและประมาณ 5mb ก่อนการบีบอัดและโหลดในประมาณ 200ms :)
Richard Marskell - Drackir

2

ดูคำถามนี้: 'Binary XML' สำหรับข้อมูลเกม? ฉันจะเลือกใช้ JSON หรือ YAML หรือแม้แต่ XML แต่นั่นมีค่าใช้จ่ายค่อนข้างมาก สำหรับ SQLite ฉันจะไม่ใช้สิ่งนั้นในการจัดเก็บระดับ ฐานข้อมูลเชิงสัมพันธ์มีประโยชน์ถ้าคุณคาดว่าจะเก็บข้อมูลจำนวนมากที่เกี่ยวข้อง (เช่นมีลิงก์เชิงสัมพันธ์ / คีย์ต่างประเทศ) และถ้าคุณคาดว่าจะถามคำถามที่แตกต่างกันจำนวนมากการจัดเก็บ tilemaps ดูเหมือนจะไม่ทำเช่นนี้ สิ่งต่าง ๆ และจะเพิ่มความซับซ้อนที่ไม่จำเป็น


2

ปัญหาพื้นฐานของคำถามนี้คือมันทำให้เกิดแนวคิดสองอย่างที่ไม่เกี่ยวข้องกับสิ่งอื่น:

  1. กระบวนทัศน์การจัดเก็บไฟล์
  2. การแสดงข้อมูลในหน่วยความจำ

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

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

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

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

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

แก้ไขปัญหา.

ฐานข้อมูลเชิงสัมพันธ์ (RDB) มีอยู่เพื่อแก้ไขปัญหาของการค้นหาที่ซับซ้อนผ่านชุดข้อมูลขนาดใหญ่ที่มีเขตข้อมูลจำนวนมาก การค้นหาประเภทเดียวที่คุณเคยทำในแผนที่ย่อยคือ "รับไทล์ที่ตำแหน่ง X, Y" อาร์เรย์ใด ๆ สามารถจัดการได้ การใช้ฐานข้อมูลเชิงสัมพันธ์เพื่อจัดเก็บและดูแลรักษาข้อมูลแผนที่ย่อยจะเป็นเรื่องที่เกินความเป็นจริงและไม่คุ้มค่าอะไร

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


1
  • XML: ใช้งานง่ายมาก แต่ค่าใช้จ่ายจะน่ารำคาญเมื่อโครงสร้างมีความลึก
  • SQLite: สะดวกกว่าสำหรับโครงสร้างขนาดใหญ่

สำหรับข้อมูลที่ง่ายจริงๆฉันอาจไปตามเส้นทาง XML หากคุณคุ้นเคยกับการโหลดและการแยกวิเคราะห์ XML อยู่แล้ว

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.