พวกเขาจะทำอย่างไร: กระเบื้องนับล้านใน Terraria


45

ฉันได้สร้างเอ็นจิ้นเกมคล้ายกับTerrariaซึ่งส่วนใหญ่เป็นสิ่งที่ท้าทายและในขณะที่ฉันคิดว่าส่วนใหญ่แล้วฉันไม่สามารถคาดเดาได้ว่าพวกเขาจัดการกับกระเบื้องที่สามารถโต้ตอบ / เก็บเกี่ยวได้นับล้าน เกมมีในครั้งเดียว การสร้างไทล์ประมาณ 500,000 แผ่นนั่นคือ 1 ใน 20 ของสิ่งที่เป็นไปได้ในTerrariaในเครื่องยนต์ของฉันทำให้อัตราเฟรมลดลงจาก 60 เหลือประมาณ 20 ถึงแม้ว่าฉันจะยังคงแสดงเฉพาะไพ่ในมุมมองเท่านั้น ใจคุณฉันไม่ได้ทำอะไรกับกระเบื้องเพียงแค่ทำให้พวกเขาอยู่ในความทรงจำ

อัปเดต : รหัสถูกเพิ่มเพื่อแสดงว่าฉันทำสิ่งต่าง ๆ อย่างไร

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

...
    public void Draw(SpriteBatch spriteBatch, GameTime gameTime)
    {
        foreach (Tile tile in this.Tiles)
        {
            if (tile != null)
            {
                if (tile.Position.X < -this.Offset.X + 32)
                    continue;
                if (tile.Position.X > -this.Offset.X + 1024 - 48)
                    continue;
                if (tile.Position.Y < -this.Offset.Y + 32)
                    continue;
                if (tile.Position.Y > -this.Offset.Y + 768 - 48)
                    continue;
                tile.Draw(spriteBatch, gameTime);
            }
        }
    }
...

นอกจากนี้นี่คือวิธี Tile.Draw ซึ่งสามารถทำได้ด้วยการอัปเดตเนื่องจากไทล์แต่ละอันใช้การเรียกสี่ครั้งไปยังเมธอด SpriteBatch.Draw นี่เป็นส่วนหนึ่งของระบบการหมุนอัตโนมัติของฉันซึ่งหมายถึงการวาดแต่ละมุมขึ้นอยู่กับกระเบื้องข้างเคียง texture_ * เป็นรูปสี่เหลี่ยมตั้งครั้งเดียวที่การสร้างระดับไม่ใช่การอัปเดตแต่ละครั้ง

...
    public virtual void Draw(SpriteBatch spriteBatch, GameTime gameTime)
    {
        if (this.type == TileType.TileSet)
        {
            spriteBatch.Draw(this.texture, this.realm.Offset + this.Position, texture_tl, this.BlendColor);
            spriteBatch.Draw(this.texture, this.realm.Offset + this.Position + new Vector2(8, 0), texture_tr, this.BlendColor);
            spriteBatch.Draw(this.texture, this.realm.Offset + this.Position + new Vector2(0, 8), texture_bl, this.BlendColor);
            spriteBatch.Draw(this.texture, this.realm.Offset + this.Position + new Vector2(8, 8), texture_br, this.BlendColor);
        }
    }
...

คำติชมหรือคำแนะนำเกี่ยวกับรหัสของฉันยินดีต้อนรับ

อัปเดต : เพิ่มโซลูชันแล้ว

นี่คือวิธี Level.Draw สุดท้าย กระบวนการ Level.TileAt วิธีการตรวจสอบค่าอินพุตเพื่อหลีกเลี่ยงข้อยกเว้น OutOfRange

...
    public void Draw(SpriteBatch spriteBatch, GameTime gameTime)
    {
        Int32 startx = (Int32)Math.Floor((-this.Offset.X - 32) / 16);
        Int32 endx = (Int32)Math.Ceiling((-this.Offset.X + 1024 + 32) / 16);
        Int32 starty = (Int32)Math.Floor((-this.Offset.Y - 32) / 16);
        Int32 endy = (Int32)Math.Ceiling((-this.Offset.Y + 768 + 32) / 16);

        for (Int32 x = startx; x < endx; x += 1)
        {
            for (Int32 y = starty; y < endy; y += 1)
            {
                Tile tile = this.TileAt(x, y);
                if (tile != null)
                    tile.Draw(spriteBatch, gameTime);

            }
        }
    }
...

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

5
อัตราเฟรมลดลงจาก 60 เป็น 20 fps เพียงเพราะหน่วยความจำที่จัดสรรหรือไม่ ไม่น่าเป็นไปได้อย่างยิ่งที่จะต้องมีสิ่งผิดปกติ มันคือแพลตฟอร์มอะไร ระบบจะเปลี่ยนหน่วยความจำเสมือนเป็นแผ่นดิสก์หรือไม่
Maik Semder

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

3
อุ๊ย! ทำซ้ำทั้งหมดกระเบื้องและโทรวาดสี่ครั้งในแต่ละหนึ่งที่ในมุมมอง มีแน่นอนการปรับปรุงบางอย่างที่เป็นไปได้ที่นี่ ....
thedaian

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

คำตอบ:


56

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

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

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

ในที่สุดและอาจเป็นตัวเลือกที่ดีที่สุด (เกมระดับโลกส่วนใหญ่ทำสิ่งนี้) คือการแบ่งภูมิประเทศของคุณออกเป็นภูมิภาค แบ่งโลกออกเป็นส่วน ๆ พูดขนาด 512x512 แผ่นและโหลด / ยกเลิกการโหลดภูมิภาคเมื่อผู้เล่นเข้าใกล้หรืออยู่ห่างจากภูมิภาค นอกจากนี้ยังช่วยให้คุณไม่ต้องวนซ้ำแผ่นที่อยู่ไกลออกไปเพื่อทำการติ๊ก 'update' ทุกประเภท

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


5
ส่วนของภูมิภาคนั้นน่าสนใจถ้าฉันจำได้ถูกต้องนั่นคือสิ่งที่ minecraft ทำ แต่มันไม่ได้ทำงานร่วมกับวิธีที่ Terraria วิวัฒนาการขึ้นมาแม้ว่าคุณจะไม่ได้อยู่ใกล้มันเช่นการแพร่กระจายของหญ้า แก้ไข : ยกเว้นกรณีที่พวกเขาใช้เวลาที่ผ่านไปตั้งแต่ครั้งล่าสุดที่ภูมิภาคมีการใช้งานแล้วทำการเปลี่ยนแปลงทั้งหมดที่จะเกิดขึ้นในช่วงเวลานั้น นั่นอาจใช้งานได้จริง
William Mariager

2
Minecraft ใช้พื้นที่อย่างแน่นอน (และเกม Ultima ในภายหลังได้และฉันค่อนข้างมั่นใจว่าเกม Bethesda หลายเกมทำ) ฉันไม่ค่อยแน่ใจว่า Terraria จัดการกับภูมิประเทศอย่างไร แต่พวกเขาอาจใช้เวลาที่ผ่านไปกับภูมิภาค "ใหม่" หรือเก็บแผนที่ทั้งหมดไว้ในหน่วยความจำ หลังต้องการการใช้ RAM สูง แต่คอมพิวเตอร์ที่ทันสมัยส่วนใหญ่สามารถจัดการกับมันได้ อาจมีตัวเลือกอื่น ๆ ที่ไม่ได้กล่าวถึงเช่นกัน
thedaian

2
@MindWorX: ทำการอัปเดตทั้งหมดเมื่อมันโหลดใหม่จะไม่ทำงานเสมอไป - สมมติว่าคุณมีพื้นที่แห้งแล้งทั้งกลุ่ม คุณหายไปนานแล้วเดินไปที่หญ้า เมื่อบล็อกที่มีหญ้าโหลดมันจะถูกจับขึ้นมาและมันจะกระจายในบล็อกนั้น แต่มันจะไม่กระจายเข้าไปในบล็อกที่อยู่ใกล้กว่าที่โหลดไว้แล้ว สิ่งดังกล่าวมีความคืบหน้ามากช้ากว่าเกมแม้ว่า - ตรวจสอบเพียงส่วนย่อยเล็ก ๆ ของกระเบื้องในรอบการปรับปรุงใดคนหนึ่งและคุณสามารถทำให้ภูมิประเทศที่ห่างไกลสดโดยไม่ต้องโหลดลงระบบ
Loren Pechtel

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

1
สำหรับทุกคนที่สงสัย Terraria เก็บข้อมูลไทล์ทั้งหมดไว้ในอาร์เรย์ 2 มิติ (ขนาดสูงสุดคือ 8400x2400) จากนั้นใช้คณิตศาสตร์ง่าย ๆ ในการตัดสินใจเลือกส่วนของไทล์
FrenchyNZ

4

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

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

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

struct TileUpdate
{
public byte health;
public byte type;
public byte damage;
}

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

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


1
ใช่ภายหลังการทำซ้ำของเครื่องยนต์ของฉันพัฒนาขึ้นเพื่อใช้งาน ส่วนใหญ่เพื่อลดการใช้หน่วยความจำและเพื่อเพิ่มความเร็ว ชนิด ByRef ช้าเมื่อเปรียบเทียบกับอาร์เรย์ที่เต็มไปด้วยโครงสร้าง ByVal อย่างไรก็ตามมันดีที่คุณเพิ่มคำตอบลงไป
William Mariager

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

3
คุณไม่จำเป็นต้องอย่างใดอย่างหนึ่งหรือhealth damageคุณสามารถเก็บบัฟเฟอร์เล็ก ๆ ของตำแหน่งไทล์ที่เลือกล่าสุดและความเสียหายในแต่ละตำแหน่ง หากมีการเลือกไทล์ใหม่และบัฟเฟอร์เต็มแล้วให้เลื่อนตำแหน่งที่เก่าแก่ที่สุดก่อนที่จะเพิ่มไทล์ใหม่ นี่เป็นการ จำกัด จำนวนครั้งที่คุณสามารถขุดได้ทีละก้อน แต่มีข้อ จำกัด ที่แท้จริงในเรื่องนี้ (คร่าว ๆ#players * pick_tile_size) คุณสามารถเก็บรายการนี้ต่อผู้เล่นหากทำได้ง่ายขึ้น ขนาดไม่เรื่องของความเร็ว ขนาดที่เล็กลงหมายถึงไทล์มากขึ้นในแต่ละ cacheline ของ CPU
Sean Middleditch

@SeanMiddleditch คุณพูดถูก แต่นั่นไม่ใช่ประเด็น จุดคือการหยุดและคิดว่าสิ่งที่คุณต้องวาดกระเบื้องบนหน้าจอและทำการคำนวณของคุณ เนื่องจาก OP ใช้คลาสทั้งหมดฉันทำตัวอย่างง่าย ๆ Simple ไปไกลในความก้าวหน้าการเรียนรู้ ตอนนี้ปลดล็อกหัวข้อของฉันสำหรับความหนาแน่นของพิกเซลโปรดคุณเป็นโปรแกรมเมอร์พยายามดูคำถามนั้นด้วยสายตาของศิลปิน CG เนื่องจากไซต์นี้มีไว้สำหรับ CG สำหรับเกม afaik
Madmenyo

2

มีเทคนิคการเข้ารหัสที่แตกต่างกันที่คุณสามารถใช้ได้

RLE: ดังนั้นคุณเริ่มต้นด้วยพิกัด (x, y) แล้วนับว่ามีไพ่เรียงกันกี่อันที่มีเคียงข้างกัน (ความยาว) ตามแกนใดแกนหนึ่ง ตัวอย่าง: (1,1,10,5) จะหมายถึงว่าเริ่มต้นที่การประสานงาน 1,1 มี 10 แผ่นเรียงต่อกันของชนิดเรียงต่อกัน 5

อาเรย์ขนาดใหญ่ (บิตแมป): แต่ละองค์ประกอบของอาเรย์จะยึดติดกับประเภทไทล์ที่อยู่ในพื้นที่นั้น

แก้ไข: ฉันเพิ่งเจอคำถามที่ยอดเยี่ยมนี้ที่นี่: ฟังก์ชั่นเมล็ดแบบสุ่มสำหรับการสร้างแผนที่?

เครื่องกำเนิดเสียงรบกวน Perlin ดูเหมือนจะเป็นคำตอบที่ดี


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

1
การใช้การเข้ารหัสที่เหมาะสม (เช่นเสียง perlin) คุณจะไม่ต้องกังวลกับการถือไทล์เหล่านั้นทั้งหมดในหน่วยความจำในครั้งเดียว .. แทนที่จะถามตัวเข้ารหัส (ตัวสร้างเสียงรบกวน) เพื่อบอกสิ่งที่ควรจะปรากฏในพิกัดที่กำหนด มีความสมดุลที่นี่ระหว่างรอบการทำงานของ CPU และการใช้หน่วยความจำและตัวสร้างสัญญาณรบกวนสามารถช่วยในการปรับสมดุลนั้น
Sam Axe

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

ความคิดที่ดีมากเช่นเดียวกับ thedaian ภูมิประเทศถูกโต้ตอบโดยผู้ใช้ซึ่งหมายความว่าฉันไม่สามารถดึงข้อมูลทางคณิตศาสตร์ได้ ฉันจะดูเสียงเพอร์ลินเพื่อสร้างภูมิประเทศพื้นฐาน
William Mariager

1

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

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


+1 สำหรับ "การปรับให้เหมาะสมก่อนเวลาอันควรเป็นรากของความชั่วทั้งหมด" ฉันมีความผิดในเรื่องนี้ :(
thedaian

16
-1 quadtree หรือไม่ มากไปหน่อย เมื่อไทล์ทั้งหมดมีขนาดเท่ากันในเกม 2D เช่นนี้ (ซึ่งใช้สำหรับ Terraria) มันง่ายมากที่จะหาว่าไทล์ช่วงใดที่อยู่บนหน้าจอ - เป็นเพียงซ้ายสุดบน (บนหน้าจอ) ไปยัง กระเบื้องล่างขวาสุด
BlueRaja - Danny Pflughoeft

1

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

เกี่ยวกับ aditing แบบไดนามิก ... บางทีทรี quad อาจเป็นความคิดที่ไม่ดี สมมติว่ากระเบื้องถูกใส่ลงในโหนดใบไม้และโหนดที่ไม่ใช่ใบไม้นั้นเป็นเพียงแบตช์ตาข่ายจากลูกของมันรูทควรมีกระเบื้องทั้งหมดที่แบตช์ไว้ในตาข่ายเดียว การลบไทล์หนึ่งต้องการการอัพเดตโหนด (mesh rebatching) จนถึงรูท แต่ในทุกระดับของต้นไม้มีเพียง 1 ใน 4 ของตาข่ายที่ทำการรีบูตซึ่งไม่ควรจะมากขนาดนั้น 4 * tree_height mesh coins?

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

แค่ความคิดของฉันไม่มีรหัสบางทีมันไร้สาระ


0

@arrival ถูกต้อง ปัญหาคือรหัสการวาด คุณกำลังสร้างอาร์เรย์ของ 4 * 3000 + วาดคำสั่งรูปสี่เหลี่ยม (24000+ คำสั่งรูปหลายเหลี่ยมที่วาด ) ต่อเฟรม จากนั้นคำสั่งเหล่านี้จะถูกประมวลผลและส่งไปยัง GPU มันค่อนข้างแย่

มีวิธีแก้ปัญหาบางอย่าง

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

0

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

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

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


-2

ฉันกำลังคิดเกี่ยวกับวิธีการจัดการกับ zillions ของบล็อกที่มากและสิ่งเดียวที่อยู่ในหัวของฉันคือรูปแบบการออกแบบ Flyweight หากคุณไม่รู้ฉันขอแนะนำให้อ่านอย่างลึกซึ้ง: อาจช่วยได้มากเมื่อพูดถึงการประหยัดหน่วยความจำและการประมวลผล: http://en.wikipedia.org/wiki/Flyweight_pattern


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