Database
 sql >> Baza danych >  >> RDS >> Database

Gry MMO i projektowanie baz danych

Bądźmy szczerzy:wszyscy uwielbiamy grać w gry, zwłaszcza na naszych komputerach. Dopóki Internet nie stał się powszechny, większość z nas grała w gry komputerowe samodzielnie, zwykle przeciwko oponentom AI. Było fajnie, ale gdy tylko zorientowałeś się, jak działa mechanika rozgrywki, gra straciła większość swojej magii.

Rozwój Internetu przeniósł gry do sieci. Teraz możemy grać przeciwko ludzkim przeciwnikom i testować swoje umiejętności przeciwko ich. Koniec z rozgrywką!

Potem pojawiły się masowe gry online dla wielu graczy (MMO), które zmieniły wszystko. Tysiące graczy znalazło się w tych samych światach gry, rywalizując o zasoby, negocjując, handlując i walcząc. Aby takie gry były możliwe, potrzebna była struktura bazy danych, która mogłaby przechowywać wszystkie istotne informacje.

W tym artykule zaprojektujemy model, który będzie zawierał najczęściej spotykane elementy w grach MMO. Omówimy, jak z niego korzystać, jego ograniczenia i możliwe ulepszenia.

Wprowadzenie do modeli danych w grach MMO

W dzisiejszych czasach istnieje wiele bardzo popularnych gier MMO i dotyczą one wszelkiego rodzaju scenariuszy. Skoncentruję się tutaj na grach strategicznych, takich jak Ogame , Travian , Sparta :Wojna imperiów i Imperia Online . W tych grach chodzi bardziej o planowanie, budowanie i opracowywanie strategii, a mniej o działanie bezpośrednie.

Gry MMO toczą się w różnych światach, różnią się wizualnie i wykorzystują mniej lub bardziej różne opcje rozgrywki. Mimo to niektóre pomysły są takie same. Gracze rywalizują o lokacje, walczą o nie i zawierają sojusze z (i przeciwko) innym graczom. Budują struktury, zbierają surowce i badają technologie. Budują jednostki (takie jak wojownicy, czołgi, handlarze itp.) i używają ich do handlu z sojusznikami lub do walki z przeciwnikami. Wszystko to musi być obsługiwane w naszej bazie danych.

Możemy myśleć o tych grach jako o grach planszowych online z wieloma indeksowanymi kwadratami. Z każdym kwadratem może być powiązanych wiele różnych akcji; niektóre akcje będą obejmować wiele kwadratów – np. kiedy przenosimy jednostki lub zasoby z jednej lokalizacji do drugiej.




Baza danych podzielona jest na pięć głównych obszarów:

  • Players / Users
  • Alliances
  • Locations and Structures
  • Research and Resources
  • Units

Pozostałe siedem niepogrupowanych tabel dotyczy jednostek i opisuje ich pozycje oraz ruchy w grze. Przyjrzymy się każdemu z tych obszarów znacznie bardziej szczegółowo, zaczynając od Graczy i sojusze .

Gracze i sojusze

Bez wątpienia gracze są najważniejszą częścią każdej gry.

player tabela zawiera listę wszystkich zarejestrowanych graczy biorących udział w instancji gry. Będziemy przechowywać nazwy użytkowników, hasła i nazwy ekranowe graczy. Będą one przechowywane w user_name , password i nickname odpowiednio atrybuty.

Nowi użytkownicy będą musieli podać adres e-mail podczas rejestracji. Zostanie wygenerowany i wysłany do nich kod potwierdzający, na który odpowiedzą. Zaktualizujemy confirmation_date atrybut, gdy użytkownik weryfikuje swój adres e-mail. Tak więc ta tabela ma trzy unikalne klucze:user_name , nickname i email .

Za każdym razem, gdy użytkownik się loguje, w login_history stół. Wszystkie atrybuty w tej tabeli nie wymagają wyjaśnień. logout_time jest specyficzny. Może być NULL, gdy bieżąca sesja użytkownika jest aktywna lub gdy użytkownicy wychodzą z gry (bez wylogowania) z powodu problemów technicznych. W login_data atrybut, będziemy przechowywać dane logowania, takie jak lokalizacja geograficzna gracza, adres IP oraz używane urządzenie i przeglądarka.

Większość gier MMO pozwala nam współpracować z innymi graczami. Jedną ze standardowych form współpracy graczy jest sojusz. Gracze dzielą się swoimi „prywatnymi danymi” w grze (statusem online, planami, lokalizacją swoich miast i kolonii itp.) z innymi, aby czerpać korzyści z działań sojuszników i dla czystej zabawy.

alliance tabela przechowuje podstawowe informacje o sojuszach w grze. Każdy z nich ma unikalny alliance_name które będziemy przechowywać. Będziemy też mieli pole date_founded , który przechowuje, kiedy sojusz został założony. Jeśli sojusz zostanie rozwiązany, przechowamy te informacje w date_disbanded atrybut.

alliance_member tabela dotyczy graczy z sojuszami. Gracze mogą dołączać do tego samego sojuszu i opuszczać go więcej niż raz. Z tego powodu player_idalliance_id para nie jest unikalnym kluczem. Będziemy przechowywać informacje o tym, kiedy gracz dołącza do sojuszu i kiedy (jeśli) opuszcza sojusz w date_from i date_to pola. membership_type_id atrybut jest odniesieniem do membership_type słownik; przechowuje aktualny poziom praw graczy w sojuszu.

Prawa graczy w sojuszu mogą z czasem ulec zmianie. membership_actions , membership_type i actions_allowed tabele razem definiują wszystkie możliwe prawa członków sojuszu. Ten model nie pozwala graczom definiować własnych poziomów praw w sojuszu, ale można to łatwo osiągnąć, dodając nowe rekordy w membership_type słownik i przechowywanie informacji o tym, z jakimi sojuszami są powiązani.

Podsumowując:wartości przechowywane w tych tabelach są przez nas definiowane podczas wstępnej konfiguracji; zmienią się tylko wtedy, gdy wprowadzimy nowe opcje.

membership_history tabela przechowuje wszystkie dane dotyczące ról graczy lub uprawnień w sojuszu, w tym zakres, w którym te prawa były ważne. (Na przykład może mieć uprawnienia „nowicjusza” przez miesiąc, a następnie „pełne członkostwo” od tego momentu.) date_to atrybut jest NULL, ponieważ aktualnie aktywne prawa jeszcze się nie zakończyły.

membership_actions Słownik zawiera listę wszystkich akcji, które gracze mogą wykonać w sojuszu. Każda akcja ma swoją własną action_name i logika gry jest zbudowana wokół tych nazw. Możemy oczekiwać wartości takich jak „wyświetl listę członków” , „wyświetl statusy członków” i „wyślij wiadomość” tutaj.

membership_type słownik zawiera unikalne nazwy grup akcji używanych w grze. actions_allowed tabela przypisuje akcje do typów członkostwa. Każdą akcję można przypisać do typu tylko raz. Dlatego membership_action - membership_type para tworzy unikalny klucz dla tej tabeli.

Lokalizacje i struktury

Lokacje gry to obszary, w których gracze zbierają zasoby oraz budują struktury i jednostki. Niektóre gry mają predefiniowany zakres możliwych lokalizacji, podczas gdy inne mogą umożliwiać użytkownikom definiowanie własnych lokalizacji.

W przestrzeni 3D lokalizacje można zdefiniować za pomocą współrzędnych [x:y:z]. Jeśli gra ma wstępnie zdefiniowany zakres, może nie pozwalać graczom na użycie dowolnej lokalizacji spoza zakresu [0:1000] dla wszystkich trzech osi, więc jesteśmy ograniczeni do przestrzeni 1000 * 1000 * 1000.

Z drugiej strony, być może chcemy umożliwić graczom wpisanie dokładnych współrzędnych ich nowej lokalizacji – m.in. [1001:2073:4] – i chcemy, aby gra przetworzyła to za nich.

Będziemy przechowywać listę wszystkich lokalizacji używanych w instancji naszej gry w location stół. Każda lokalizacja ma swoją własną nazwę, ale nazwy nie są unikatowe. Z drugiej strony coordinates atrybut musi zawierać tylko unikalne wartości. Współrzędne lokalizacji są przechowywane jako wartości tekstowe, więc możemy przechowywać współrzędne dla gier 3D jako [112:72:235]. Współrzędne gier 2D mogą być przechowywane jako <1102:98>.

W niektórych grach lokalizacje będą miały pewną liczbę kwadratów, które służą do przechowywania struktur lub jednostek. Będziemy przechowywać te informacje w dimension atrybut, który jest polem tekstowym. Wymiarem może być po prostu liczba kwadratów w siatce 2D lub 3D. player_id atrybut przechowuje informacje o aktualnym właścicielu tej lokalizacji. Może być NULL, gdy lokalizacje są wstępnie zdefiniowane, a gracze rywalizują o ich zajęcie.

structure tabela zawiera listę wszystkich konstrukcji, które możemy zbudować w różnych lokalizacjach gry. Struktury reprezentują ulepszenia, które pozwalają nam produkować lepsze jednostki, przeprowadzać nowe rodzaje badań, produkować więcej zasobów itp. Każda struktura używana w grze ma swoją własną, unikalną structure_name . Niektóre możliwe structure_name wartości to „farma”, „kopalnia rudy”, „elektrownia słoneczna” i „centrum badawcze”.

Możemy oczekiwać, że każda struktura będzie wielokrotnie ulepszana, więc będziemy również przechowywać informacje o jej aktualnym poziomie. Każde ulepszenie zwiększa wydajność struktur, dzięki czemu wytwarza więcej zasobów lub pozwala korzystać z nowych funkcji w grze. Nie możemy z góry określić maksymalnego poziomu ulepszeń, więc wszystkie rzeczy związane z poziomem (koszty, czas ulepszeń i produkcja) zdefiniujemy za pomocą formuł. Wszystkie formuły przechowywane w bazie danych stanowią rdzeń mechaniki gry, a ich dostosowanie ma kluczowe znaczenie dla równowagi gry i ogólnie rozgrywki.

Tak jest również w przypadku upgrade_time_formula atrybut. Przykładowa wartość tego pola to * 30 min” , gdzie reprezentuje poziom, do którego chcemy uaktualnić.

W większości przypadków istnieją wymagania, które muszą zostać spełnione, zanim gracze podejmą określone działania. Być może musimy wykonać określoną ilość badań, zanim będziemy mogli budować nowe struktury lub odwrotnie. Przechowamy poziom badań potrzebny do zbudowania struktur w prerequisite_research stół. Relacje i poziom struktury potrzebny do rozpoczęcia różnych badań są przechowywane w prerequisite_structure stół. W obu tabelach klucze obce research_id i structure_id są sparowane, tworząc unikalny klucz. level_required jedyną wartością jest atrybut.

Te dwie tabele, prerequisite_research i prerequisite_structure , również stanowią rdzeń gry.

Dla każdej struktury zdefiniujemy listę wymagań wstępnych:inne struktury i ich minimalne poziomy, które gracze muszą mieć, aby rozpocząć budowę. Będziemy przechowywać te dane w structure_required stół. Tutaj structure_id reprezentuje strukturę, którą chcemy zbudować; structure_required_id jest odniesieniem do struktur wymagań wstępnych i level to wymagany poziom.

structure_built tabela przechowuje informacje o aktualnych poziomach konstrukcji w danej lokalizacji. upgrade_ongoing atrybut zostanie ustawiony tylko wtedy, gdy aktualizacja jest w toku, podczas gdy upgrade_end_time atrybut będzie zawierał znacznik czasu po zakończeniu aktualizacji.

structure_formula tabela dotyczy struktur i zasobów. Para kluczy obcych do tej tabeli tworzy jej unikalny klucz. Ta tabela zawiera również dwa atrybuty tekstowe zawierające formuły z parametrem . Zdefiniujemy te formuły, jedną na koszty, a drugą na generowanie zasobów, w bazie danych. Będą podobne do upgrade_time_formula . Potrzebujemy ich, ponieważ musimy określić zasoby wydawane na budowę każdej struktury. Musimy również zdefiniować produkcję zasobów po aktualizacji, jeśli struktura generuje jakiekolwiek zasoby (np. kopalnia rudy wyprodukuje * 20 rudy dziennie).

Badania i zasoby

Badania (lub technologie) w grach są zwykle niezbędne do stworzenia innych funkcji. Bez pewnych poziomów badań nie można budować nowych struktur ani typów jednostek. Badania również mogą mieć swoje własne wymagania. Jednym z najczęstszych jest poziom danej struktury, zwykle nazywany „laboratorium badawczym”. A może gracze muszą ukończyć pewien poziom badań, zanim będą mogli rozpocząć nowe badania. Wszystkie te wymagania zostaną omówione w tej sekcji. Poniżej znajduje się model danych dla badań i zasobów:

research tabela zawiera listę wszystkich możliwych akcji badawczych w naszej grze. Używa tej samej logiki, co structure stół. research_name atrybut jest unikalnym kluczem tabeli, natomiast upgrade_time_formula pole zawiera tekstową reprezentację formuły wymagań czasowych badania, której parametrem jest . Wszelkie zasoby wymagane do aktualizacji są zdefiniowane w upgrade_formula przechowywane w research_formula tabela.

Podobnie jak w przypadku struktur, zdefiniujemy listę wszystkich innych badań i ich poziomy, które należy ukończyć, zanim będziemy mogli rozpocząć kolejny rodzaj badań. Będziemy przechowywać te dane w research_required tabela, gdzie research_id reprezentuje pożądane badania; research_required_id jest odniesieniem do badania wymagań wstępnych i level to wymagany poziom.

Badania dotyczą poszczególnych graczy, a dla każdego gracza – badania W parze musimy przechowywać aktualny poziom badań gracza i wszelkie bieżące statusy ulepszeń. Będziemy przechowywać te informacje przy użyciu research_level w ten sam sposób, w jaki użyliśmy structure_built tabela.

Zasoby takie jak drewno, ruda, klejnoty i energia są wydobywane lub zbierane i wykorzystywane później do budowy struktur i innych ulepszeń. Przechowamy listę wszystkich zasobów w grze w resource słownik. Jedynym atrybutem tutaj jest resource_name pole i jest to również unikalny klucz tabeli.

Aby śledzić aktualną ilość zasobów w każdej lokalizacji, użyjemy resources_on_location stół. Ponownie para kluczy obcych (resource_id i location_id ) tworzy unikalny klucz tabeli, natomiast number atrybut przechowuje aktualne wartości zasobów.

Jednostki i ruchy

Surowce są wykorzystywane do produkcji jednostek. Jednostki mogą być używane do transportu surowców, atakowania innych graczy lub ogólnie grabieży i palenia.

Lista typów jednostek używanych w naszej grze jest przechowywana w unit słownik z tylko jedną wartością, unit_name; ten atrybut jest unikalnym kluczem tej tabeli. Niektóre popularne jednostki w grze to „szermierz”, „krążownik”, „gryf”, „myśliwiec”, „czołg” itp.

Każdą jednostkę musimy opisać za pomocą określonych cech. Lista wszystkich możliwych cech jest przechowywana w characteristic słownik. characteristic_name pole zawiera unikalną wartość. Wartości w tym polu mogą obejmować:„atak”, „obronę” i „punkty wytrzymałości”. Przypiszemy cechy jednostkom za pomocą unit_characteristic relacja. Para kluczy obcych unit_id i characteristic_id tworzą unikalny klucz tabeli. Użyjemy tylko jednego atrybutu, value , aby zapisać żądaną wartość.

research_unit tabela zawiera listę wszystkich czynności badawczych, które należy zakończyć zanim będziemy mogli rozpocząć produkcję danego typu jednostki. unit_cost tabela określa zasoby potrzebne do wyprodukowania pojedynczej jednostki. Obie tabele mają unikalne klucze złożone z pary kluczy obcych (research_id lub resources_id w połączeniu z unit_id ) i jedno pole wartości (cost i level_required ).

A teraz zabawna część. Produkcja jest fajna, ale przenoszenie jednostek i podejmowanie akcji jest jeszcze lepsze. Wprowadziliśmy już unit tabeli, ale zachowamy ją tutaj ze względu na to, jak odnosi się do innych tabel.

Jednostki stacjonują w lokacji lub przemieszczają się pomiędzy lokacjami. Dodawanie player_id pole określa, kto jest właścicielem lokalizacji lub grupy, która przemieszcza się między lokalizacjami.

Jeśli jednostki właśnie stacjonują w danej lokalizacji, zapiszemy tę lokalizację i liczbę jednostek tam stacjonujących. Aby to zrobić, użyjemy units_on_location tabela.

Kiedy jednostki nie stacjonują, poruszają się. Będziemy musieli zapisać ich punkt wyjścia i miejsce docelowe. Ponadto musimy zdefiniować możliwe działania podczas ruchów. Wszystkie takie działania są przechowywane w movement_type słownik. type_name atrybut jest unikalny, podczas gdy allows_wait atrybut określa, czy akcja pozwala na oczekiwanie w punkcie docelowym.

Możemy przenieść jeden typ jednostek, ale prawie w każdym przypadku przeniesiemy wiele jednostek kilku różnych typów. Ta grupa będzie udostępniać wspólne dane, a my będziemy je przechowywać w group_movement stół. W tej tabeli zdefiniujemy następujące pozycje:

  • gracz, który zainicjował to działanie
  • rodzaj akcji
  • punkt wyjścia
  • punkt docelowy
  • arrival_time w miejscu docelowym
  • return_time do punktu wyjścia
  • wait_time w miejscu docelowym

return_time atrybut może mieć wartość NULL, jeśli jest to podróż w jedną stronę, a wait_time jest definiowany przez gracza. Jednostki należące do grupy są definiowane przez wartości przechowywane w units_in_group stół. Para kluczy obcych units_id i group_moving_id tworzy unikalny klucz stołu. Liczba jednostek tego samego typu w grupie jest zdefiniowana w number atrybut.

Każdy ruch może przenosić surowce z jednego miejsca do drugiego. Dlatego zdefiniujemy relację wiele-do-wielu między group_movement i resources tabele. Oprócz kluczy podstawowych i obcych, resources_in_group tabela zawiera tylko number atrybut. To pole przechowuje ilość zasobów przenoszonych przez graczy z punktu początkowego do miejsca docelowego.

W większości przypadków gracze mogą wezwać innych do przyłączenia się do ich przygody. Aby to wesprzeć, użyjemy dwóch tabel:allied_movement i allied_groups . Jeden gracz zainicjuje wspólne działanie, które utworzy nowy rekord w allied_movement stół. Wszystkie grupy jednostek, które biorą udział w sojuszniczej akcji, są definiowane przez wartości przechowywane w allied_groups stół. Każda grupa może być przypisana do sojuszniczej akcji tylko raz, więc klucze obce tworzą unikalny klucz tej tabeli.

Model ten daje nam podstawową strukturę potrzebną do zbudowania gry strategicznej MMO. Zawiera najważniejsze cechy gry:lokalizacje, struktury, zasoby, badania i jednostki. Łączy je również, pozwala nam zdefiniować wymagania wstępne w bazie danych, a także przechowuje większość logiki gry w bazie danych.

Po wypełnieniu tych tabel większość logiki gry jest zdefiniowana i nie spodziewalibyśmy się dodania nowych wartości. Prawie każda tabela ma unikalną wartość klucza, nazwę funkcji lub parę kluczy obcych. Zmiana charakterystyk jednostek i formuł produkcji/kosztów pozwoli nam zmienić balans gry w warstwie bazy danych.

Jak byś zmienił ten model? Co lubisz, a co byś zrobił inaczej? Powiedz nam w sekcji komentarzy!


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Algebra relacyjna

  2. Praca nad pominiętymi optymalizacjami

  3. Podzbiór bazy danych – jak to zrobić w IRI Voracity

  4. ScaleGrid uruchamia obsługę Google Cloud Platform (GCP) dla hostingu zarządzanej bazy danych

  5. Uszkodzenie bazy danych