Press "Enter" to skip to content

10.1: Tworzenie kolekcji obiektów

Cele lekcji

Po zakończeniu tej lekcji…

  • Będziesz wiedzieć, jak przechowywać grupy obiektów w pojedynczej właściwości lub zmiennej i odczytywać je.

 

Obsługa grup obiektów

W niektórych naszych klasach brakuje właściwości. Podczas opracowywania koncepcji programu wspomniałem, że gracz będzie mógł przechowywać przedmioty w plecaku i wykonywać zadania nagradzane przedmiotami wymaganymi do ich ukończenia, a z potworów będą wypadać łupy.

Aktualnie dysponujemy klasą Quest i Item, ale zadań ani przedmiotów nie możemy przypisać nigdzie w klasach Player ani Monster. Nie utworzyliśmy jeszcze metody przechowywania wielu przedmiotów, a gracz raczej będzie miał w plecaku wiele przedmiotów i będzie miał wiele rozpoczętych zadań.

Ten problem rozwiążemy za pomocą list lub kolekcji.

Jednak zanim utworzymy właściwości list, do przedmiotów i zadań musimy dołączyć dodatkowe informacje.

W przypadku przedmiotów w plecaku będziemy musieli określać ich liczbę. Podobną operację będziemy wykonywać dla klasy Quest, czyli będziemy określać liczbę przedmiotów potrzebnych do ukończenia zadania.

W przypadku zadań potrzebujemy jeszcze miejsca, w którym będziemy zapisywać, czy gracz ukończył zadanie czy nie.

W klasie Monster potrzebujemy listy przedmiotów, które mogą „wypaść” z potwora. To będzie nasza „tabela łupów”, w której będziemy zapisywać procentową szansę na „upuszczenie” łupu przez potwora. Jeśli jakiś przedmiot jest domyślny, to wymagane jeszcze będzie dodatkowe zabezpieczenie, na wypadek gdyby generator liczb losowych nie umożliwił zdobycia danego przedmiotu.

Powyższe zadania można wykonać na wiele sposobów, ale w tej lekcji wykorzystamy metodę opartą na dodatkowych klasach.

Utworzymy więc kilka nowych klas, a następnie dodamy je jako właściwości listy do istniejących klas Player, Monster i Quest.

 

UWAGA: W filmie klasa Quest jest pozbawiona właściwości RewardItem dodanej w poprzedniej lekcji (6:52). Tę właściwość należy zachować w klasie.

 

 

Etap 1: Uruchom aplikację Visual Studio i otwórz swoje rozwiązanie.

 

Etap 2: Utwórz cztery nowe klasy, klikając prawym przyciskiem myszy projekt Engine i wskazując pozycje Add i Class. Tworzonym klasom nadaj następujące nazwy:

 

InventoryItem.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Engine
{
    public class InventoryItem
    {
        public Item Details { get; set; }
        public int Quantity { get; set; }
        public InventoryItem(Item details, int quantity)
        {
            Details = details;
            Quantity = quantity;
        }
    }
}

 

LootItem.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Engine
{
    public class LootItem
    {
        public Item Details { get; set; }
        public int DropPercentage { get; set; }
        public bool IsDefaultItem { get; set; }
        public LootItem(Item details, int dropPercentage, bool isDefaultItem)
        {
            Details = details;
            DropPercentage = dropPercentage;
            IsDefaultItem = isDefaultItem;
        }
    }
}

 

PlayerQuest.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Engine
{
    public class PlayerQuest
    {
        public Quest Details { get; set; }
        public bool IsCompleted { get; set; }
        public PlayerQuest(Quest details)
        {
            Details = details;
            IsCompleted = false;
        }
    }
}

 

QuestCompletionItem.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Engine
{
    public class QuestCompletionItem
    {
        public Item Details { get; set; }
        public int Quantity { get; set; }
        public QuestCompletionItem(Item details, int quantity)
        {
            Details = details;
            Quantity = quantity;
        }
    }
}

 

UWAGA: W dwóch klasach używamy nowego typu danych bool (wartość logiczna). Wartość logiczna pozwala przechowywać dwa stany true (prawda) lub false (fałsz). Przykładowo: w klasie PlayerQuest właściwość IsCompleted będzie zawierać wartość false do momentu ukończenia zadania. Wtedy wartością będzie true.

 

Etap 3: Mamy już nowe klasy, więc możemy utworzyć dla nich właściwości w klasach Player, Quest i Monster.

Zmodyfikujmy klasę Player, klikając ja dwukrotnie w projekcie Engine.

Przyjrzyjmy się wierszowi 2 w klasie Player. Ten wiersz jest wymagany, aby korzystać z list, czyli wartości lub właściwości zawierającej kolekcję obiektów w tej samej klasie. Tutaj program będzie wyszukiwał wszystkie elementy potrzebne do korzystania z kolekcji.

Do klasy Player dodaj następujące właściwości:

public List<InventoryItem> Inventory { get; set; }
public List<PlayerQuest> Quests { get; set; }

 

Te dwie nowe właściwości pozwalają przechowywać listy zawierające obiekty InventoryItem i PlayerQuest.

W kodzie konstruktora dodaj następujące wiersze:

Inventory = new List<InventoryItem>();
Quests = new List<PlayerQuest>();

 

Te dwa wiersze pozwalają ustawić puste listy jako wartości nowych właściwości. Jeśli nie ustawilibyśmy tych opcji, wartością właściwości byłoby null, czyli nic. Ustawienie pustej listy pozwala nam na późniejsze dodanie elementów, ponieważ do pustej listy można dodawać obiekty, ale wartość nic (null) już nie umożliwia wykonania tej operacji.

 

Player.cs (uzupełniony, ze zmianami)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Engine
{
    public class Player : LivingCreature
    {
        public int Gold { get; set; }
        public int ExperiencePoints { get; set; }
        public int Level { get; set; }
        public List<InventoryItem> Inventory { get; set; }
        public List<PlayerQuest> Quests { get; set; }
        public Player(int currentHitPoints, int maximumHitPoints, int gold, int experiencePoints, int level) : base(currentHitPoints, maximumHitPoints)
        {
            Gold = gold;
            ExperiencePoints = experiencePoints;
            Level = level;
            Inventory = new List<InventoryItem>();
            Quests = new List<PlayerQuest>();
        }
    }
}

 

Etap 4: Zmodyfikuj klasę Quest i dodaj nową właściwość:

public List<QuestCompletionItem> QuestCompletionItems { get; set; }

 

W konstruktorze dodaj ten nowy wiersz, aby przygotować listę QuestCompletionItems do przyjmowania obiektów:

QuestCompletionItems = new List<QuestCompletionItem>();

 

Quest.cs (uzupełniony, ze zmianami)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Engine
{
    public class Quest
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public int RewardExperiencePoints { get; set; }
        public int RewardGold { get; set; }
        public List<QuestCompletionItem> QuestCompletionItems { get; set; }
        public Item RewardItem { get; set; }
        public Quest(int id, string name, string description, int rewardExperiencePoints, int rewardGold)
        {
            ID = id;
            Name = name;
            Description = description;
            RewardExperiencePoints = rewardExperiencePoints;
            RewardGold = rewardGold;
            QuestCompletionItems = new List<QuestCompletionItem>();
        }
    }
}

 

Etap 5: Zmodyfikuj klasę Monster, dodając następującą właściwość:

public List<LootItem> LootTable { get; set; }

 

A do konstruktora dodaj następujący wiersz, aby nowa właściwość nie miała wartości null:

LootTable = new List<LootItem>();

 

UWAGA 1: W przypadku właściwości ciąg, liczba całkowita i logiczna nie trzeba ustawiać wartości domyślnej, ponieważ te typy danych zawierają wbudowane wartości domyślne. W przypadku list domyślnie określona jest wartość null (nic), więc należy ustawić wartość pustą (nowy obiekt listy niezawierający wartości).

UWAGA 2: W konstruktorze nie utworzyliśmy parametrów przeznaczonych do uzupełnienia wartościami w przypadku nowych właściwości. Mogliśmy to zrobić, ale uzupełnimy je w inny sposób — prostszy dla osób rozpoczynających przygodę z programowaniem.

 

Monster.cs (uzupełniony, ze zmianami)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Engine
{
    public class Monster : LivingCreature
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int MaximumDamage { get; set; }
        public int RewardExperiencePoints { get; set; }
        public int RewardGold { get; set; }
        public List<LootItem> LootTable { get; set; }
        public Monster(int id, string name, int maximumDamage, int rewardExperiencePoints, int rewardGold, int currentHitPoints, int maximumHitPoints)
            : base(currentHitPoints, maximumHitPoints)
        {
            ID = id;
            Name = name;
            MaximumDamage = maximumDamage;
            RewardExperiencePoints = rewardExperiencePoints;
            RewardGold = rewardGold;
            LootTable = new List<LootItem>();
        }
    }
}

 

Podsumowanie

Niemal wszystkie programy, które napisałem zawierały listy lub kolekcje. W prawdziwym świecie zazwyczaj obsługujemy kilka elementów (np. wyświetlanie wszystkich wpłat i wypłat na rachunku bankowym).

Niedługo dowiesz się, jak dodawać i opracowywać obiekty na listach.

 

Łącza do tej lekcji

Kod źródłowy w serwisie GitHub (nowe lekcje) (zaktualizowane lekcje)

Kod źródłowy w serwisie Dropbox (nowe lekcje) (zaktualizowane lekcje)

    Leave a Reply

    Your email address will not be published. Required fields are marked *