Zwrócenie typu pochodnej, a nie bazy - jak zrobić?

0

Witam! Napisałem klasę GameObject po której dziedziczą inne obiekty w grze. Mam klasę Player, która właśnie dziedziczy po GameObject. Mam też listę obiektów List<GameObject> objects. Tyle, że ta lista jest obsługiwana przez metodę innej klasy. W tej metodzie zwracam GameObject i przez to nie mam dostępu do pól Player. Jak wykonać rzutowanie czy może coś innego, aby zwróciło mi typ taki jaki jest na danej pozycji w liście. Kod:

 
        private List<GameObject> objects;

        public GameObject Get(int index)
        {
                return objects[index];
        }

        // --------------------------------

        GameObjectsManager.Get(123).Name // tu mam tylko pola GameObject, a chcę również pola Player

Co mam zrobić, żeby zwracało typ, o którym pisałem wcześniej (typ obiektu z listy o danym indexie)?

1

Przede wszystkim po co Ci to :|. O to właśnie chodzi w dziedziczeniu że traktujesz obiekty jako klasę bazową. Jeśli chcesz rzutować zwrócony typ na Player to tylko na tej klasie będziesz mógł operować i równie dobrze możesz sobie dać spokój z całą tą obiektowością i dziedziczeniem...

Ale żeby nie było że nie piszę na temat:
Klasycznie:

((Player)GameObjectsManager.Get(123)).Name

Z użyciem operatora as:

(GameObjectsManager.Get(123) as Player).Name
0

Możesz użyć metody generycznej, żeby robiła rzutowanie jeszcze przed zwróceniem wartości. Chyba o takie rozwiązanie Ci chodziło.

0

Ale skąd ja mam wiedzieć, że obiekt o indexie 25953 jest akurat np. Player? Równie dobrze to może być Building itd. I ja chcę właśnie pobrać ten obiekt takim jakim jest, a nie GameObject. Być może, że chodzi o to co post wyżej.

A ogólnie to skoro mam nie dziedziczyć każdego obiektu po GameObject to jak mam to zrobić?? Pisać oddzielnie każdą klasę, dla każdego obiektu, ciekawe jak zrobię z tego listę. Może jest jakiś sposób inny, to proszę o jego przedstawienie.

0

Pytanie moje dlaczego to przechowujesz na jednej liście?

Ale skąd ja mam wiedzieć, że obiekt o indexie 25953 jest akurat np. Player? Równie dobrze to może być Building itd. I ja chcę właśnie pobrać ten obiekt takim jakim jest, a nie GameObject.

Jak chcesz mieć obiekt typu player odebrany z listy, to musisz wiedzieć, który to obiekt, albo zaimplementować w tej metodzie generycznej szukanie tego typu obiektów na liście (operator is)
EDIT:
To co tutaj proponuję jest tylko obejściem w przypadku gdy potrzebujesz trzymać te wszystkie obiekty na liście z innego (sensownego) powodu

1

A ogólnie to skoro mam nie dziedziczyć każdego obiektu po GameObject to jak mam to zrobić?? Pisać oddzielnie każdą klasę, dla każdego obiektu, ciekawe jak zrobię z tego listę. Może jest jakiś sposób inny, to proszę o jego przedstawienie.

Powinieneś dziedziczyć, nie masz rzutować. Po co Ci informacja że dany obiekt jest typu Player czy np. EvilDragon? Jeśli potrzebujesz pola Name to dodaj je do klasy bazowej.

0

Ehh.. Myślę, że tak jest w większości gier więc tak zrobiłem. Mam listę obiektów w grze List<GameObject> klasa ma tylko pole i właściwość Name oraz metody abstrakcyjne Update i Draw. Wszystkie inne obiekty dziedziczą po GameObject. Myślałem czy Player'a dziedziczyć czy nie, żeby to było co innego w ogóle, ale postanowiłem dziedziczyć. Walić tego Player'a, bo to jednak trochę inna klasa. Ale co z obiektami np. Building, Vehicle itd. Przecież pojazd będzie miał pola np. prędkość, ilość miejsc itd. I chcę właśnie dostać się do tych pól (czy właściwości) poprzez mój GameObjectManager, który ma listę obiektów i ma metodę Get(int index) i Get(string name), które zwracają obiekt z listy. Jak mam napisać to wszystko, żeby ładnie się dodawało/usuwało/dostawało do tej listy obiektów. Nie zrobię przecież dla każdej klasy Vehicle etc. oddzielnej listy nie? A chcę dostać np. Vehicle (velocity, slots) + pola po GameObject (name) tyle, że jak wyszukuję po nazwie obiektu np. "Mercedes1", to zwraca mi GameObject, a ja chcę typ obiektu, który ma name = "Mercedes1". Czyli wyszukuję "mur_1342" i zwraca mi typ tego obiektu na liście czyli mur to może być building. Panimajesz?

0

Stosując moje obejście to wywołanie wyglądałoby mniej więcej tak:

Vehicle vehicle = objectManager.Get<Vehicle>("Mercedes1");
1

A stosując moje dwa:

Vehicle vehicle = (Vehicle)objectManager.Get("Mercedes1"); 
Vehicle vehicle = objectManager.Get("Mercedes1") as Vehicle;
0

O w sumie dobre, nie pomyślałem (w sumie to chciałem tak zrobić, ale czytaj dalej), że można tak, ale to trochę psuje estetykę ;/ A jak zapobiec teraz, żebym sobie niechcący dał <Vehicle>, a pod "Mercedes1" przypadkowo będzie inny typ?

I ogólnie brzydkie rozwiązanie, bo muszę pamiętać, jakiego typu jest to, to i tamto :( Żeby się kurde dało zwykłe Get i zwraca to co powinno ;/ Hehe:

public T Get(int index)
{
    return objects[i] as (object[i].GetType());
}
1

A jak zapobiec teraz, żebym sobie niechcący dał <Vehicle>, a pod "Mercedes1" przypadkowo będzie inny typ?

Nie da się.

0

To chyba zostanę przy rzutowaniu ;/

0

Możesz jedynie łapać wyjątek, albo sprawdzić zgodność typu przed rzutowaniem.

I ogólnie brzydkie rozwiązanie, bo muszę pamiętać, jakiego typu jest to, to i tamto Żeby się kurde dało zwykłe Get i zwraca to co powinno ;/ Hehe:

A jakbyś chciał to odbierać?:

? obiekt = manager.Get(index);

co wstawisz zamiast ?

0

Jeśli bardzo zależy ci na bezpieczeństwie,to do klasy bazowej dodaj metodę:

public class GameObject
{
    public virtual string getType(void)=0;
}

która ci będzie zwracała string "Car","Building","Player" etc i na tej podstawie będziesz dokonywał odpowiedniego rzutowania

2
  1. Czemu wszystkie obiekty mają mieć jedną wspólną klasę bazową? W grze są różne typy obiektów, te które tworzą jakieś grupy może jest sens żeby miały wspólną klasę bazową, chociaż ja poszedłbym raczej w interfejsy. Np. masz obiekty ruchome, które wymagają m.in. zmiany pozycji, więc tworzysz IObiektRuchomy z polem Pozycja i metodą ZmieńPozycję(Point nowaPozycja). Teraz samochód będzie implementował ten interfejs, a budynek nie.
    Dalej, jeśli masz obiekty, które wymagają narysowania, niech implementują IRysuj, z metodą Rysuj(Ekran ekranGry). Zależy od gry, ale w cześci gier gracz nie jest bytem rysowalnym, np. w starategiach, karciankach, więc on ani się nie przemieszcza, ani nie rysuje, ale może ma inne cechy wpływające na grę, czy wymagające gdzieś prezentacji (doświadczenie, liczba gotówki).
  2. Ponowię pytanie, czemu wszystkie obiekty są na jednej liście. Gracze mogą stanowić jedną listę, budynki kolejną etc. Nie wiem jaką grę tworzysz (typ) więc nie wiem jaka będzie logika gry, czy jakaś pętla przetwarzająca tury etc. Ale wg mnie też nie ma sensu aby wszystko było na jednej liście, chyba że GameObject ma metodę WykonajTurę/Przetwarzaj i wchodzi tu polimorfizm i obiekt wie co ma z sobą zrobić.
  3. Gry najczęściej wymagają tego aby były wydajne, bo muszą posługiwać się dużą liczbą zasobów (grafika, animacja, ...), więc wszystkie dodatkowe rzutowania, boxing-unboxing, wyszukiwanie po nazwie, jeśli tylko nie jest konieczne należałoby wyeliminować.
0

@MasterBLB:
Jak potem zwrócić typ? getType() { return this.GetType().ToString(); } ?

@massther: Sam nie wiem co to będzie, bo to pierwszy mój taki projekt, więc liczę tylko na dobrą zabawę i naukę, a pomysł mam na prostego FPS'a tyle, że porządnie zbudowanego, aby w przyszłości można go rozwijać, a nie w kółko przepisywać kod na nowo. Interfejsy to dobry pomysł, ale z tymi oddzielnymi listami to mnie nie pasuje, bo (chyba, że oddzielne listy, ale jedna metoda Get, która przeszukuje wszystkie listy - to miało by sens?), a w sumie nie zaszkodzie spróbować, ale szukam najlepszej metody i proszę byście powiedzieli, która by była wg Was najlepsza z tych, które wymieniliście.

0
xeo545x39 napisał(a)

@MasterBLB:
Jak potem zwrócić typ? getType() { return this.GetType().ToString(); } ?

E nie,takie coś miałem na myśli:

public class Vehicle : GameObject
{
  public virtual string getType(void)
  {
      return "Vehicle";
  }
}
0
xeo545x39 napisał(a)

Interfejsy to dobry pomysł, ale z tymi oddzielnymi listami to mnie nie pasuje, bo (chyba, że oddzielne listy, ale jedna metoda Get, która przeszukuje wszystkie listy - to miało by sens?)

Dlaczego nie oddzielne listy? Dlaczego tak trzymasz się tej jednej?
To, co chcesz zrobić, od początku nie ma żadnego sensu. Po co wpychać różne obiekty na jedną listę, skoro i tak do wykonania jakiejś operacji trzeba je z niej wyciągać?
A tak w ogóle, to czy gwarantujesz, że różne obiekty zawsze mają różne Name?

0

Możliwe jest dodanie nowego obiektu tylko, gdy takiej nazwy obiektu nie ma na liście. Przemyślałem sprawę i zrobię już tak jak będzie dobrze. Proszę teraz żebyście wytłumaczyli jak zrobić na oddzielnych listach (no może to akurat nie problem) i z tym interfejsami, bo coś próbowałem, ale nic z tego nie wyszło. Chcę, żeby każdy obiekt miał właściwość Name i ewentualnie każdy inna klasa implementowała potrzebny jej interfejs np. ICharacter. Tyle, że wtedy nie wiem jak to trzymać na liście, jakiego typu były te moje obiekty itd. itd. Chodzi o to, żebyście przybliżyli najlepsze i najwygodniejsze rozwiązanie. Piszę sobie klasę pojazdu, to ma to co pojazd + to co obiekt poruszalny (prędkość, pozycja) + jak każdy obiekt w grze właściwość Name. Piszę sobie klasę mutanta to mam to co postać + obiekt poruszalny + Name. I nie wiem jak to zrobić, pisać dla każdej klasy oddzielną listę??

1

Może coś takiego? Każdy obiekt w liście jego klasy i dodatkowa lista dla interfejsu rysowania.

    interface IDraw
    {
        void Draw();
    }

    class Car : IDraw
    {
        public string Name { get; set; }
        public void Draw()
        {
            System.Console.WriteLine("Rysowanie auta {0}",Name);
            //rysowanie auta
        }
    }

    class Person : IDraw
    {
        public string FirstName { get; set; }
        public string SecondName { get; set; }

        public void Draw()
        {
            System.Console.WriteLine("Rysowanie osoby {0} {1}",FirstName,SecondName);
            //rysowanie osoby
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            Car car1 = new Car() { Name = "Mercedes" };
            Car car2 = new Car() { Name = "Nissan" };
            Car car3 = new Car() { Name = "Opel" };
            Person person1 = new Person() { FirstName = "Radek", SecondName="Ktos" };
            Person person2 = new Person() { FirstName = "Ola", SecondName = "KtosInny" };
            Person person3 = new Person() { FirstName = "Alfred", SecondName = "ZupelnieKtosInny" };
            
            List<IDraw> drawList = new List<IDraw>();
            drawList.Add(car1);
            drawList.Add(car2);
            drawList.Add(car3);
            drawList.Add(person1);
            drawList.Add(person2);
            drawList.Add(person3);

            List<Car> carList = new List<Car>();
            carList.Add(car1);
            carList.Add(car2);
            carList.Add(car3);

            List<Person> personList = new List<Person>();
            personList.Add(person1);
            personList.Add(person2);
            personList.Add(person3);

            Console.ReadKey();

        }
    }
0

Moim zdaniem to złe rozwiązanie i musi się dać inaczej. Nie wyobrażam sobie tego dla 1000 obiektów w grze. 1000 na jednej liście, 1000 na drugiej itd. A Name musi być dla każdego obiektu w grze, nie chodzi mi tu o nazwę samochodu np. "Żuk", a nazwę obiektu dla danego Żuka np. "zuk_pod_plotem" czy "zuk1". I co jeżeli, a na pewno będę musiał skorzystać z kilku interfejsów dla jednej klasy? Mam do każdej listy dodawać obiekt? Nie no, jakoś musi się dać prościej po ludzku, a w ogóle wie ktoś jak takie rzeczy się w grach robi?

0
  1. Po co lista? Musi być lista? Ja bym użył np hash-mapy i indeksował po polu Name. Oczywiście o ile rozmiar mapy jest w miarę stały. Jeżeli się co chwilę zmienia w dużym stopniu, albo jeżeli jest potrzeba przeiterowania po mapie to trzeba wybrać tree map.
  2. Po co wiele list? Przecież zamiast sprawdzać po kolei w każdej liście czy w niej znajduje się obiekt, można zrobić jedną i zrobić sprawdzanie typów + rzutowanie.
  3. Wyjątki w C# są wolne, zamiast łapać wyjątki przy rzutowaniu, sprawdzaj jaki jest typ przed rzutowaniem.
  4. Nie zapominajcie też o wzorcu odwiedzający: http://en.wikipedia.org/wiki/Visitor_pattern#Java_example Jak widać w przykładzie, lista zawiera elementy różnych typów, nie ma nigdzie rzutowania, a dla każdego typu elementu jest inny rodzaj działania.
  5. Z punktu widzenia wydajności, rzutowanie powinno być (niemal) tak samo szybkie jak wywoływanie metody wirtualnej, przy założeniu, że rzutowanie nie rzuci wyjątku. Wywołanie metody wirtualnej zawiera w sobie implicite rzutowanie, ale bez możliwości rzucenia wyjątku.
  6. C# 4.0 ma multiple dispatch za pomocą słówka kluczowego dynamic ( http://blogs.msdn.com/b/laurionb/archive/2009/08/13/multimethods-in-c-4-0-with-dynamic.aspx ), ale myślę, że to będzie miało niższą wydajność niż wzorzec odwiedzający.
1

Nie wyobrażam sobie tego dla 1000 obiektów w grze. 1000 na jednej liście, 1000 na drugiej itd.

Razem całe 12 kilobajtów.

Któryś raz pytasz się 'w ogóle wie ktoś jak takie rzeczy się w grach robi'. Spodziewasz się że wszystkie gry stosują się do jakiegoś Jedynego Słusznego Standardu Dzielenia Obiektów Na Listy? Każda gra robi to inaczej, tak jak jej jest wygodnie.

Więc teraz coś prywatnie ode mnie - po co ci ta właściwość 'name' w każdym obiekcie? Dostanie się do obiektu po nazwie będzie wymagało O(n) operacji (i to mógłby być dopiero problem wydajnościowy) i nazwy będą zajmować większość pamięci obiektów. Ale to mały problem bo podczas pisania gry możliwość dostania się do obiektu po jego 'nazwie' i tak nigdzie się nie przydaje.
Kwestia kluczowa, od której zależy wszystko i której jeszcze nie wyjaśniłeś - po co Ci w ogóle w grze wiedzieć jaki jest typ jednostki, skoro wszystkie udostępniają taki sam interfejs (idzDo(), atakuj(), rysuj())? Każda jednostka to powiedzmy IObiect i niezbyt cię obchodzi czy jest samochodem, czarodziejem, czołgiem czy chodzącym drzewem.
A jeśli chcesz do tego dodać jeszcze np. budynki które udostępniają INNY interfejs, to musisz je dodać na INNĄ listę i nie unikniesz tego jak bardzo byś nie chciał.

0

Kurde... Ja już sam nic nie wiem. A jak bez Name będę wiedział, którym obiektem na liście jest jakis tam ch#$? Indexów nie zapamiętam, a nazwy tak, chyba, że jest jakiś inny sposób na to. Dla mnie najlepsze rozwiązanie było to, że wszystkie klasy dziedziczą po GameObject, który ma samo Name, Update() i Draw(), ale niestety chyba się nie da zwrócić nie GameObject a to czym faktycznie jest obiekt (no da się rzutowaniem, ale tak za każdym razem rzutować i pamiętać nazwy klas na które chcę - chyba trochę do d***)

I jeszcze raz to samo trochę bardziej zrozumiale opisane na przykładzie. Mam edytor map 3D. Dodaję sobie obiekty na mapę i każdy z nich ma swój typ (czyli inne pola i metody), ale każdy z obiektów ma zawsze swoją nazwę i 2 metody - Update() i Draw() - oczywiście abstrakcyjne, bo każdy obiekt co innego będzie w nich robił. Podsumowanie: jak może być to ładnie zrobione?

0

No to w czym problem? Masz zbiór obiektów typu GameObject, iterujesz po zbiorze i wywołujesz Update() lub Draw(). Czego chcesz więcej?

0

Ok, ale ja chcę to co ma inny obiekt pochodny od GameObject, czyli dla pojazdu np. prędkość. Czyli co bez rzutowania nie da rady? Chyba, że oddzielne listy, ale wtedy bym miał problem z metodami managera - Add() - co ma zwracać, jak nie wie czego będzie szukać?

0

Jeśli chciałbyś użyć tego chorego pomysłu z wieloma listami, to metoda Add() musiałaby mieć tak czy siak od groma rzutowań.

Jeśli metoda chce operować na obiektach typu np Samochód (gdzie Samochód dziedziczy po GameObject), to co to za problem dla tej metody sobie zrzutować ten obiekt?

0

Dobra wiele list = @#$%
Co jeszcze, ktoś tam wspomniał o jakichś hash-mapach, o co z nimi chodzi?
A skoro nie to ani wiele list, to co jeszcze można?

0

Ja wspomniałem. http://pl.wikipedia.org/wiki/Tablica_asocjacyjna Mapy to podstawa, z takimi pytaniami to do newbie.

0
xeo545x39 napisał(a)

Co jeszcze, ktoś tam wspomniał o jakichś hash-mapach, o co z nimi chodzi?

http://msdn.microsoft.com/en-us/library/xfhwa508.aspx
Słowniki pozwoliłyby Ci na szybkie wyszukiwanie obiektów po ich nazwie. Ale to chyba naprawdę nie jest Ci do niczego potrzebne.

Mam wrażenie, że próbujesz sobie utrudnić życie. A pomysłów więcej raczej nie będzie... Zastosuj rzutowanie i idź do przodu z projektem, bo inaczej do końca życia będziesz myślał, co teraz zrobić.

1 użytkowników online, w tym zalogowanych: 0, gości: 1