ปัญหา C# เกี่ยวกับแผนที่ภาคพื้นอย่างง่ายสำหรับเอ็นจิ้นโร๊คไลค์

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

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

public class Tile
{
    public int X { get; set; }
    public int Y { get; set; }
    public bool IsWall { get; set; }
}

แล้วเติมรายการโดยทำสิ่งนี้

private static void PopulateTiles()
    {
        for (int x = 1; x < 159; x++)
            for (int y = 3; y < 45; y++)
                Tiles.Add(new Tile { X = x, Y = y });
    }

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

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

    static void DrawObstacles()
    {
        foreach (Tile tile in World.Tiles)
        {
            if (tile.IsWall)
            {
                Console.SetCursorPosition(tile.X, tile.Y);
                Console.Write("#");
            }
        }
    }

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

    //Tried this
    private static void SetWalls()
    {
        int[] walls = new int[] { };
        walls += Tiles.GetRange(0, 7); //Fails because I cannot use tiles in an int[]... of course
    }

    //This
    List<Tile> walls = new List<Tile>();
    List<Tile> walls = Tiles.GetRange(0, 7);
    List<Tile> walls = Tiles.GetRange(19, 7; //Doesn't work either but I've no idea why here

    //And finally
    List<Tile> walls = new List<Tile>();
    walls.Add(Tiles[Tiles.GetRange(0, 7)]); //Gives me an error saying that System.Collections.Generic.List<Engine.Tile> cannot be converted to int

ตอนนี้ ฉันหลงทางไปเล็กน้อยเนื่องจากฉันต้องการวนซ้ำรายการ แทนที่จะป้อนทุกพิกัดด้วยตนเองในอาร์เรย์ 2 มิติ... แล้ววนซ้ำเพื่อ "สร้าง" กำแพง

คำถามของฉันคือ: เป็นไปได้หรือไม่ที่จะดึงข้อมูลเพียงบางส่วนของรายการ (เช่นดัชนี 0 - 7, 19 - 26, 29, 31, 35 - 46) จากนั้นวนซ้ำรายการหลักของไทล์เพื่อใช้ "รายการดัชนีใหม่นี้ " เพื่อเปลี่ยนคุณสมบัติ ISWALL ของไทล์ที่ดัชนีเหล่านั้น... ถ้านั่นสมเหตุสมผล

ขอบคุณล่วงหน้าและอย่าลังเลที่จะบอกฉันหากฉันทำผิดพลาดในโพสต์


person Schaezar    schedule 24.04.2017    source แหล่งที่มา
comment
ฉันหลงทางในการอ่านสิ่งนี้ คำถามของคุณคืออะไรกันแน่?   -  person Smartis    schedule 24.04.2017
comment
โดยพื้นฐานแล้ว ฉันต้องการที่จะวนซ้ำรายการไทล์และเลือกเพียงบางส่วนเท่านั้นเพื่อตั้งค่าคุณสมบัติ ISWALL เป็น TRUE ฉันแค่ต้องเลือกช่วงบางช่วงเท่านั้น เช่น ไทล์ 0 - 7, 19 - 26 และอื่นๆ ถ้ามันสมเหตุสมผล   -  person Schaezar    schedule 24.04.2017
comment
คุณไม่ได้จัดเก็บไทล์ของคุณในรูปแบบ 80x25 หรือไม่? เลียนแบบเอาต์พุตหน้าจอใช่ไหม นั่นจะทำให้ชีวิตของคุณง่ายขึ้น.. ฉันคิดว่าคุณกำลังพยายามสร้างห้อง atm ใช่ไหม? (ฉันใช้เวลาทั้งชีวิตไปกับเกมโร๊คไลค์มากเกินไป)   -  person BugFinder    schedule 24.04.2017
comment
ฉันไม่แน่ใจว่าฉันเข้าใจสิ่งที่คุณหมายถึงโดยการจัดเก็บไทล์ในรูปแบบ 80*25 แต่จริงๆ แล้วอาร์เรย์ไทล์คือ 158*41 เนื่องจากฉันเปลี่ยนขนาดของคอนโซลเช่นกัน มันคือ (160, 60)   -  person Schaezar    schedule 24.04.2017


คำตอบ (1)


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

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

ตัวอย่างเช่น คุณสามารถเริ่มต้นทุกอย่างได้ดังนี้:

    List<GameObject> GameObjects { get; set; }
    List<GameObject> Walls { get; set; }
    List<GameObject> Items { get; set; }

    public Map()
    {
        PopulateTiles();
        StructureCollections();
    }

    //Small processing loop
    private void DrawObstacles()
    {
        foreach(var wall in Walls)
        {
            Console.SetCursorPosition(tile.X, tile.Y);
            Console.Write("#");
        }
    }

    //Called only on startup
    private  void PopulateTiles()
    {
        for (int x = 1; x < 159; x++)
            for (int y = 3; y < 45; y++)
                GameObjects.Add(new GameObject() { X = x, Y = y });
    }

    //Called only on starup
    private void StructureCollections()
    {
        foreach(GameObject gameObj in GameObjects)
        {
            if (gameObj.IsWall)
                Walls.Add(gameObj);

            if (gameObj.IsItem)
                Items.Add(gameObj);
        }
    }

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

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

public class GameObject
{
    public int X { get; set; }
    public int Y { get; set; }
    public bool IsWall { get; set; }

    public GameObject(int x, int y)
    {
        X = x;
        Y = y;
    }

}

public class Wall : GameObject
{
    public Wall(int x, int y) : base(x,y)
    {
        IsWall = true;
    }
}

ในกรณีนี้ คุณเพียงแค่ต้องเรียก new Wall(x, y); ใหม่ซึ่งจะจัดการทุกสิ่งที่จำเป็นในการทำงานเป็นกำแพง โดยไม่ต้องใช้พารามิเตอร์เพิ่มเติม

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

person Toskr    schedule 24.04.2017
comment
นั่นคือสิ่งที่ฉันต้องการทำ แต่ฉันไม่รู้วิธีเปลี่ยนคุณสมบัติเป็น IsWall (นอกเหนือจากการป้อนด้วยตนเองด้วยคำสั่งเช่นนี้: gameObjects.Add(new GameObject(X, Y, true) สำหรับกระเบื้องทุกแผ่นที่เป็นผนัง... ซึ่งจะไม่แห้งมากนัก และสุดท้ายต้องใช้รหัส 100 บรรทัดสำหรับแต่ละข้อความ - person Schaezar; 24.04.2017