Będę łagodnie nie zgadzać się ze wszystkimi i powiem, że podejście relacyjne jest tutaj rozsądne. Interesujące jest to, że przedmioty mogą mieć wiele ról. Głównym problemem będzie to, że odwzorowanie między tym układem relacyjnym a układem OO w kodzie nie będzie wydawać się „naturalne”, ale myślę, że po stronie bazy danych wiele ról można wyrazić w czysty sposób (bez dziwnego kodowania lub redundancji, po prostu łączy) .
Pierwszą rzeczą, którą należy podjąć, jest to, jaka część danych jest specyficzna dla elementu i ile jest dzielona przez wszystkie elementy danego typu.
Oto co bym zrobił, gdyby wszystkie dane były specyficzne dla elementu:
// ITEMS table: attributes common to all items
item_id | name | owner | location | sprite_id | ...
1 | Light Saber | 14 (Tchalvek) | 381 (Tchalvek house) | 5663 | ...
// WEAPONS table: attributes for items that are weapons
item_id | damage | damage_type | durability | ...
1 | 5 | sharp | 13 | ...
// LIGHTING table: attributes for items that serve as lights
item_id | radius | brightness | duration | ...
1 | 3 meters | 50 | 8 hours | ...
W tym projekcie każdy element znajduje się w tabeli Przedmioty, wraz z atrybutami, które mają wszystkie (lub większość) przedmiotów. Każda dodatkowa rola, jaką może odgrywać przedmiot, to osobny stół.
Jeśli chcesz użyć go jako broni, poszukaj go w tabeli broni. Jeśli tam jest, to nadaje się jako broń. Jeśli go nie ma, nie można go użyć jako broni. Istnienie zapisu mówi ci, czy to broń. A jeśli tam jest, przechowywane są tam wszystkie atrybuty specyficzne dla broni. Ponieważ te atrybuty są przechowywane bezpośrednio zamiast w jakiejś zakodowanej formie, będziesz mógł wykonywać z nimi zapytania / filtry. (Na przykład, na stronie metryki gry możesz chcieć agregować graczy według typu obrażeń od broni, a możesz to zrobić z niektórymi złączeniami i grupami według typu obrażeń).
Przedmiot może mieć wiele ról i może istnieć w więcej niż jednym stole dla poszczególnych ról (w tym przykładzie zarówno broń, jak i oświetlenie).
Jeśli jest to po prostu boolean typu „czy można to utrzymać”, umieściłbym to w tabeli Przedmioty. Może warto tam umieścić w pamięci podręcznej „czy to broń” itp., Abyś nie musiał sprawdzać broni i innych tabel ról. Dodaje jednak nadmiarowość, dlatego należy zachować ostrożność, aby zachować synchronizację.
Zalecenie Ari, aby mieć dodatkową tabelę według typu, może być również zastosowane w tym podejściu, jeśli niektóre dane nie będą się różnić dla poszczególnych pozycji. Na przykład, jeśli obrażenia od broni nie różnią się w zależności od przedmiotu, ale role wciąż różnią się w zależności od przedmiotu, możesz podzielić dzielone atrybuty broni na tabelę:
// WEAPONS table: attributes for items that are weapons
item_id | durability | weapon_type
1 | 13 | light_saber
// WEAPONTYPES table: attributes for classes of weapons
weapon_type_id | damage | damage_type
light_saber | 5 | energy
Innym podejściem byłoby, gdyby role odgrywane przez przedmioty nie różniły się w zależności od przedmiotu, ale tylko od rodzaju przedmiotu. W takim przypadku umieścisz item_type w tabeli Items i możesz przechowywać właściwości takie jak „czy to broń” i „czy można ją trzymać” oraz „czy to światło” w tabeli ItemTypes. W tym przykładzie sprawiam, że nazwy przedmiotów nie różnią się w zależności od przedmiotu:
// ITEMS table: attributes per item
item_id | item_type | owner | location
1 | light_saber | 14 (Tchalvek) | 381 (Tchalvek house)
// ITEMTYPES table: attributes shared by all items of a type
item_type | name | sprite_id | is_holdable | is_weapon | is_light
light_saber | Light Saber | 5663 | true | true | true
// WEAPONTYPES table: attributes for item types that are also weapons
item_type | damage | damage_type
light_saber | 5 | energy
Jest prawdopodobne, że typy przedmiotów i typy broni nie zmieniają się w trakcie gry, więc możesz po prostu załadować te tabele do pamięci raz i wyszukać te atrybuty w tabeli skrótów zamiast z łączeniem z bazą danych.
Niestety, w takich sytuacjach relacyjne bazy danych (takie jak SQL) są niewystarczające, a nierelacyjne bazy danych (takie jak MongoDB ) przodują. Biorąc to pod uwagę, nie jest niemożliwe modelowanie danych w relacyjnej bazie danych, a ponieważ wydaje się, że twoja baza kodów jest już zależna od SQL, oto model, z którym bym poszedł:
Zamiast tworzyć element i korzystać z tabeli, utwórz tabelę i tabelę referencyjną „[item] _type” dla każdego typu elementu.
Oto kilka przykładów:
To rozwiązanie zapewnia wiele długoterminowej elastyczności i skalowalności, zmniejsza (jeśli nie eliminuje) zmarnowane miejsce i jest samo dokumentujące (w przeciwieństwie do „item_use_data”). Wymaga to nieco większej konfiguracji po stronie programisty, ale moim zdaniem jest to najbardziej eleganckie rozwiązanie dostępne w sytuacjach, w których musisz użyć relacyjnej bazy danych do przechowywania danych. Ogólnie rzecz biorąc, nierelacyjne bazy danych są znacznie lepsze do tworzenia gier, ponieważ są bardziej elastyczne w sposobie modelowania danych, a jednocześnie są bardziej wydajne niż bazy danych oparte na SQL - co czyni je wyborem „win-win”.
Edycja 1: Poprawiono błąd w polu potion_type_id
Edycja 2: Dodano więcej szczegółów na temat nierelacyjnych i relacyjnych baz danych, aby zapewnić dodatkową perspektywę
źródło
Po pierwsze, zrzuć podejście do dziedziczenia obiektowego i przejdź do systemu opartego na komponentach .
Gdy to zrobisz, układ SQL staje się nagle znacznie łatwiejszy. Dla każdego typu elementu masz jedną tabelę ze wspólnym numerem identyfikacyjnym. Jeśli potrzebujesz przedmiotu nr 17, poszukaj „ID przedmiotu 17” w każdej tabeli. Każda tabela z kluczem jest dodawana do komponentu.
Twoja Tabela Przedmiotów zawiera wszystkie wymagane dane dotyczące przedmiotów (nazwa, cena sprzedaży, waga, rozmiar, wszystko inne, które są wspólne dla wszystkich przedmiotów.) Twoja Tabela Broni zawiera wszystkie odpowiednie dane dotyczące broni, twoja Tabela Mikstur zawiera wszystkie odpowiednie dane w przypadku mikstur tabela zbroi zawiera wszystkie odpowiednie dane dla zbroi. Chcesz napierśnika, jest to pozycja w Przedmiotu i pozycja w Zbroi. Chcesz półki na miecze, którą możesz wypić, po prostu umieść wpis w każdym stoliku, i tak dalej.
Pamiętaj, że ten sam wzór nie jest szczególnie specyficzny dla przedmiotu - możesz go użyć do stworzenia, strefy, wszystkiego, czego możesz chcieć. Jest zaskakująco wszechstronny.
źródło
Używanie SQL było poważnym błędem. Absolutnie NIE nadaje się do przechowywania statycznych danych projektowych gier.
Jeśli nie możesz odejść od SQL, rozważenie przechowywania elementów w serializowanym pliku. To znaczy
Oczywiście jest to brzydkie i wyrzuca wszystkie „drobiazgi” SQL przez okno, ale czy naprawdę ich potrzebujesz ? Najprawdopodobniej i tak odczytasz wszystkie swoje dane przedmiotów na początku gry i nigdy
SELECT
przez nic innegoitem_id
.źródło
W zależności od tego, ile cech prawdopodobnie będziesz potrzebować, możesz użyć prostej maski bitowej dla obiektów o różnych bitach odpowiadających różnym cechom:
Następnie możesz wykonać proste testy bitowe, aby sprawdzić, czy obiekt może być użyty do określonej funkcji.
Tak więc „szklana butelka” może mieć wartość 101111, co oznacza, że można ją trzymać, może być używana jako broń, łatwo pęka, można ją wyrzucić i może zawierać płyny.
Każdy edytor utworzony dla elementów może następnie mieć prosty zestaw pól wyboru, aby włączyć / wyłączyć cechy obiektu.
źródło
W naszym projekcie mamy item_attributes dla różnych „dodatkowych danych”, które może mieć element. Przedstawiono coś takiego:
Następnie mamy tabelę atrybutów, która wygląda tak:
Ari Patrick ma rację, ostatecznie relacyjne bazy danych nie są do tego stworzone. Minusem jest to, że mamy dość skomplikowane procedury generowania nowych przedmiotów (wykonywane za pomocą zewnętrznego narzędzia - które bardzo polecam, nie próbuj ręcznie dodawać tych elementów, tylko się pomylisz)
Inną dostępną opcją jest używanie języka skryptowego do tworzenia szablonów elementów, a następnie można je łatwo analizować i używać do tworzenia nowych elementów. Oczywiście nadal musisz zapisać dane pozycji w bazie danych, ale w tym momencie nie musisz się martwić o specyfikę tworzenia nowych elementów, możesz po prostu skopiować stary plik skryptu, zmienić niektóre informacje i jesteś dobry iść.
Szczerze mówiąc, jeśli mielibyśmy przerobić sposób tworzenia nowych elementów statycznych, prawdopodobnie zastosowalibyśmy znacznie prostsze podejście przy użyciu szablonów skryptów elementów.
źródło
To typowa relacja wiele do wielu, nic zbyt ezoterycznego dla jakiejkolwiek zdolnej relacyjnej bazy danych. Masz wiele cech dla jednego obiektu, a każda cecha może być używana przez jeden lub więcej różnych typów obiektów. Modeluj trzy relacje (tabele), przy czym jedna jest relacją powiązania i gotowe. Właściwe indeksowanie zapewni szybkie odczytywanie danych.
Ułóż ORM w kodzie i powinieneś mieć bardzo mało problemów z przechodzeniem między DB a oprogramowaniem pośrednim. Wiele ORM jest w stanie również automatycznie generować klasy, przez co stają się jeszcze bardziej „niewidoczne”.
Jeśli chodzi o bazy danych NoSQL, nie ma powodu, dla którego nie można tego zrobić. Obecnie modne jest dopingowanie tej technologii w szmatach handlowych i blogach, ale pojawiają się one z mnóstwem własnych problemów: stosunkowo niedojrzałe platformy, idealne do prostych, rzadko zmienianych odczytów (jak jeden do wielu twits w profilu), ale słaba dla złożonych dynamicznych odczytów lub aktualizacji, słaba obsługa łańcucha narzędzi, redundantne dane i towarzyszące im problemy z integralnością itp. Jednak oferują atrakcyjność lepszej skalowalności, ponieważ unikają w różnym stopniu możliwości transakcyjnych i ich kosztów ogólnych oraz łatwiejszych modeli dystrybucji / replikacji .
Zwykłym hobgoblinem relacyjnych baz danych vs baz danych NoSQL jest wydajność. Różne RDMBS mają różne stopnie obciążenia, co czyni je mniej preferowanymi do skalowania do poziomów Facebooka lub Twittera. Jednak jest bardzo mało prawdopodobne, że napotkasz te problemy. Nawet wtedy proste systemy serwerowe oparte na SSD mogą sprawić, że debata na temat wydajności będzie bezużyteczna.
Wyjaśnijmy: większość baz danych NoSQL to fundamentalnie dystrybuowane tabele skrótów, które ograniczą cię w taki sam sposób, jak w przypadku kodu skrótu w twoim kodzie, tj. nie wszystkie dane dobrze pasują do tego modelu. Model relacyjny jest znacznie potężniejszy do modelowania relacji między danymi. (Problemem jest fakt, że większość RDBMS to starsze systemy, które są źle dostrojone do wymagań popularnego 0,0001% internetu, a mianowicie Facebooka i in.)
źródło
W tym miejscu uważam, że bardziej przydatne są nowoczesne mapery OR, takie jak Entity Framework 4 i jego pierwsza funkcja kodu (CTP) . Po prostu piszesz klasy i relacje między nimi w zwykłym kodzie i nawet bez ozdabiania ich lub ręcznego uruchamiania narzędzi, EF wygeneruje dla ciebie magazyn kopii zapasowych w formacie SQL, z wymaganymi tabelami linków i wszystkim ... naprawdę uwalnia kreatywność imo ^^
źródło
Oto, co teraz rozważam:
Ponieważ każda „cecha” w zasadzie i tak wymaga zmian w kodzie, postanowiłem po prostu zachować cechy (i wszelkie wymagane dane domyślne) w samym kodzie (przynajmniej na razie).
Na przykład
$traits = array('holdable'=>1, 'weapon'=>1, 'sword'=>array('min_dam'=>1, 'max_dam'=>500));
Następnie elementy otrzymują w bazie danych pole „trait_data”, które użyje
json_encode()
funkcji do zapisania w formacie JSON.Planuje się, że przedmioty odziedziczą wszystkie domyślne statystyki z ich cech nadrzędnych i będą miały tylko cechy zastępowania, które określają różnice w stosunku do tego, co cechy ustawiono jako domyślne.
Minusem pola cech jest to, że chociaż mógłbym ręcznie edytować część json ... ... to nie będzie tak łatwo ani bezpiecznie zrobić z danymi znajdującymi się w bazie danych.
źródło
Jest to trochę zuchwałe i nie daje żadnych gwarancji, że jest to dobry projekt DB; moje lekcje DB były jakiś czas temu i nie przychodzą mi szybko do głowy. Ale jeśli gwarantowane jest, że przedmioty zawierają, powiedzmy, mniej niż 10 przedmiotów, możesz nadać każdemu przedmiotowi pole atrybut1, pole atrybut2 itd. Aż do atrybutu10. Wyeliminowałoby to potrzebę korzystania z tabeli atrybutów wiele do wielu kosztem ograniczenia liczby atrybutów.
Patrząc wstecz na to, jestem pewien, że to okropny projekt, ale oznacza to, że możesz trzymać się wygodnej relacyjnej bazy danych i nie musisz wchodzić na nieznane terytorium. Od Ciebie zależy, czy kompromis jest tego wart.
źródło
Nevermind dał dobrą odpowiedź, ale zastanawiam się, czy potrzebujesz do tego bazy danych? Przechowywanie wszystkich danych w zwykłym pliku i ładowanie ich do programu podczas uruchamiania wydaje się być najmądrzejszym sposobem na zrobienie tego.
Edytować:
, w środowisku bezstanowym opartym na żądaniach lepiej przechowywać dane w bazie danych. Zapisz dane w pliku i napisz fragment kodu, aby przekształcić go w bazę danych typu sugerowanego przez Nevermind.
Alternatywnie, jeśli liczba obiektów nie jest zbyt duża, deklarowanie partii dosłownie w pliku kodu może być najszybszą metodą.
źródło