Proszę o radę. Zarządzanie funkcjami zawartości.

Ogólne pytania na temat Unity3D

Proszę o radę. Zarządzanie funkcjami zawartości.

Postprzez Cartoon » 29 Lis 2018, 10:30

Witam,

Chciał bym poprosić Was o radę i pomoc. Dla niektórych będą to podstawy. Niestety ja jestem osobą średnio zaawansowaną, która do tej pory miała solidną styczność tylko z ActionScript.

Stworzyłem sobie klasę „CIvillian” mojej gry. Trzymam ich w klasie CIvillianDatabase. Ich budowa jest bardzo prosta.

Kod: Zaznacz wszystko
public List<Civilian> civ = new List<Civilian>();

void Start()
{   civ.Add(newCivilian("Feniri",1,5,5,5,5,5,100,80,0,Civilian.CitzenType.Dweller,Civilian.CitzenJobe.Engineer));
civ.Add(new Civilian("Mako",1,3,4,3,3,1,100,50,1,Civilian.CitzenType.Dweller, Civilian.CitzenJobe.Farmer));
civ.Add(new Civilian("Akita",1,4,3,4,3,3,150,60,2,Civilian.CitzenType.Enemy, Civilian.CitzenJobe.Outlaw));
    }


Kolejno. Imie, Lvl , Str, Con, Int, Dex, Luck, Życie, Stamina, Id., Rodzaj, Praca.

Posiadam następną klasę "CivilianInventory", w której gracz kupuje mieszkańca/mieszkańców i ich przetrzymuje. Kolejny skrypt steruje pracą mieszkańców w zależności od ich ustawień. Kiedy uruchamiamy skrypt, wszystko działa tak jak sobie to wyobraziłem. Surowce w zależności od wyboru gracza i statystyk mieszkańców spływają do zbioru zasobów, a oni za swój wysiłek pobierają złoto lub żywność, tracąc stamine i życie, o ile gracz nie posiada żywności.

Tutaj zaczyna się mój mały problem. Owszem wszystko działa. Jednak chciał bym aby mogli lvl'ować, tak jak gracz i mieli swoje własne funkcje. U gracza mam już to zrobione. Jednak chciałbym aby podobnie działało to u mieszkańców. Rozmyślam nad tworzeniem classy w stylu:

Kod: Zaznacz wszystko
public class Feniri : Civilian {…}

I tu umieścić skrypt do lvl’u i zmian statystyk i czego sobie tam wymyśle. Jednak teraz nie mam pojęcia, jak to podpiąć do CIvillianDatabase, aby dostało ręce i nogi. Tak samo, w tej chwili stałymi są Życie, Stamina, a mają być zależne od statystyk mieszkańca, a to dopiero mały kawałek.

Rozumiem, że rozwiązanie może być banalne, niestety na chwilę obecną nie mogę znaleźć rozwiązania.
Jak mogę to przebudować lub zmienić. Może ktoś robił tu coś podobnego i potrafi dać dobrą radę lub jakiś link do tutorialu?
Cartoon
 
Posty: 3
Rejestracja: 29 Lis 2018, 07:50
Has thanked: 0 time
Been thanked: 0 time

Re: Proszę o radę. Zarządzanie funkcjami zawartości.

Postprzez Wichur » 29 Lis 2018, 11:13

jeśli dobrze myślę to wszystkie rzeczy do levelowania umieścić w klasie civilian, levelowanie na funkcji virtual/override powinno pomóc
Awatar użytkownika
Wichur
 
Posty: 104
Rejestracja: 19 Gru 2017, 10:33
Miejscowość: Pruszcz Gdański
Has thanked: 0 time
Been thanked: 0 time

Re: Proszę o radę. Zarządzanie funkcjami zawartości.

Postprzez Hostur » 29 Lis 2018, 11:16

Może odpowiedź nie będzie taka oczywista ale to czego szukasz to data oriented programming.
Z jednej strony mógłbyś po prostu rozszerzyć klasę civilian na Civilian + CivilianAttributes np.
Z drugiej strony robisz grę strategiczną jak mniemam i prędzej czy później zderzysz się z problemami wydajności.

Nadaj każdej jednostce pewien unikatowy identyfikator, może to być jej index z CIvillianDatabase.
Użyj tego indexu w osobnych strukturach aby odnieść się do tej samej encji tzn.
Tworzysz np. CivialiansExperience, w którym trzymasz tablicę obiektów CivilianExperience w taki sposób, że odnosząc się do indexu z CivilianDatabase odnosisz się do tej samej encji w innej strukturze która jest w jakiejś relacji do tej encji.

Czyli jeśli na 0 indexie CivilianDatabase masz encję X, to jej CivilianExperience znajdzie się na indexie 0 w strukturzę CiviliansExperiense. Tym sposobem nie tylko masz łatwiejsze utrzymanie kodu, ale wykorzystujesz w danym framencie logiki tylko ten fragment, który Cie interesuje. Np levelując postać odnosisz się tylko do CiviliansExperience bo może inne informacje na temat tej jednostki Cie nie interesują.
Jak już załapiesz o co chodzi to możesz sobie rozbić logicznie dane na tema jednostek na różne typy (najlepiej struktury) i trzymać je w osobnych klasach z założeniem takim, że znajc jedynie identyfikator czy index danej jednostki możesz odnieść go do różnych klas wyciągając różne informacje na jej temat.
Hostur
 
Posty: 663
Rejestracja: 05 Sie 2015, 07:36
Has thanked: 0 time
Been thanked: 0 time

Re: Proszę o radę. Zarządzanie funkcjami zawartości.

Postprzez Hostur » 29 Lis 2018, 11:26

Aby Ci trochę ułatwić, załóż sobie teoretyczny limit jednostek a tablice w których będziesz trzymać dane na temat tych jednostek niech będą rzeczywiście tablicami (a nie listami). W konstruktorach alokujesz od razu potrzebną ilość abyś później mógł uzupełniać te struktury na odpowiednim indexie a nie poprzez List.Add(T)

Kod: Zaznacz wszystko
public struct CivilianExperience
{
  public int Experience { get; set; }
  public bool Assigned { get; }
  public int Level => Experience / 100; // N
  public CivilianExperience(int defaultExperience)
  {
    Experience = defaultExperience;
    Assigned = true;
  }
}
public class CiviliansExperience
{
  public CivilianExperience[] Experience { get; }
  public CiviliansExperience(IGameConfiguration configuration)
  {
    Experience= new CivilianExperience[configuration.MaxCivilians];
  }

  public void AddExperience(int id, int experience)
  {
    var exp = Experience[id];
    // Tak
    if(!exp.Assigned) throw new Exception("Cannot add experience to not assigned entity.");
    // lub
    if(!exp.Assigned)
    {
      exp = new CivilianExperience(experience);
      Experience[id] = exp;
      return;
    }

    exp.Experience = exp.Experience + experience;
    Experience[id] = exp;
  }
}
Hostur
 
Posty: 663
Rejestracja: 05 Sie 2015, 07:36
Has thanked: 0 time
Been thanked: 0 time

Re: Proszę o radę. Zarządzanie funkcjami zawartości.

Postprzez Cartoon » 30 Lis 2018, 10:32

Dziękuje za odpowiedzi,

Analizując to co napisaliście i wpatrując się w swój kod. Zrozumiałem jaka mało zorientowany jestem w tym co robię.

Jeśli potrafię w kodzie odczytywać po statystykach, ile zapasów mają zebrać i z braku żywności zabierać im życie. To dlaczego nie mógł bym im dodać doświadczenia, a to owszem mogę zrobić. Niestety to działa dla różnych jednostek, nie tych samych.

W moich klasa „Civilian” i „CivilianDatabase” dodałem wartość z expem. Wszystko co robię później to operuje na „CivilianInventory” przypiętym do gracza. Każdy mieszkaniec wykupiony zajmuje miejsce w ekwipunku, a ilość mieszkańców jest zależna od pojemności inwentarza gracza. Zaczyna mi się tu jednak mały problem. Muszę to inaczej poustawiać, bo zmiany zachodzą również w moim "CivilianDatabase" a to miało służyć mi jako kalka. Dodam dodatkowo sloty i zobaczę jak z nimi to gra, jednak może okazać się niewypałem.

To jest taki inwentarz bez potek i mieczy, tylko z żywymi mieszkańcami.

Tak jak Hostur napisałeś. Stworzyłem sobie klasę dodatkową CivilianExperience. Jednak muszę poczytać coś więcej o public struct „nazwa” bo nigdy z tego nie korzystałem. A wychodzi na to, że będę mógł spokojnie taki kod w skryptach trzymać, a nie na scenie. Chyba, że źle na to patrzę .

Używam teraz pętli po „CivilianWork” aby sprawdzić wszystkich mieszkańców i przeliczam ich doświadczenie, jeśli otrzymał już wystarczającą liczbę doświadczenia, przechodzi na wyższy poziom, zarazem zwiększam zapotrzebowanie na kolejny. Działa o ile jednostki są inne, takie same się kumulują.

Kod: Zaznacz wszystko
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CivilianExperiens : MonoBehaviour {

    public Kontroler player;

    //aktualny lvl
    public int vLevel;
    //posiadany exp
    public int vCurrExp;
    //exp potrzebny do lvl 1
    public int vExpBase = 10;
    //exp pozostały do levelu
    public int vExpLeft = 10;
    //modifikator zapotrzebowania do następnego lvl.
    public float vExpMod = 1.15f;

    //do testowania
    void Update()
    {

    }

    public void CheckExp()
    {
        for (int k = 0; k < player.civInventory.settler.Count; k++)
        {
            if (player.civInventory.settler[k].civName != null)
            {
                vCurrExp = player.civInventory.settler[k].civExp;
                vLevel = player.civInventory.settler[k].civLvl;

                if (vCurrExp >= vExpLeft)
                {
                    player.civInventory.settler[k].civExp -= vExpLeft;
                    player.civInventory.settler[k].civLvl++;
                    float t = Mathf.Pow(vExpMod, vLevel);
                    vExpLeft = (int)Mathf.Floor((vExpBase + vLevel * 5) * t);
                }
            }
        }
    }


Teraz głównym celem jest, aby podobny mieszkaniec w inwentarzu gracza był różny. Dodawanie kolejnego podobnego kończy się kalką, różnica to tylko ilość zbiorów ( chyba, że zrezygnuje z duplikatów). Pobawię się jeszcze z this.settlers[i], jednak powątpiewam aby to działało.
Cartoon
 
Posty: 3
Rejestracja: 29 Lis 2018, 07:50
Has thanked: 0 time
Been thanked: 0 time

Re: Proszę o radę. Zarządzanie funkcjami zawartości.

Postprzez Hostur » 30 Lis 2018, 11:20

"public struct „nazwa” bo nigdy z tego nie korzystałem."
To mnie martwi :)
Zacznij od podstaw, różnic między typami referncyjnymi a wartościowymi i trochę dziedziczenia.
Używasz tylko "public class Nazwa : MonoBehaviour" ?
Jeśli tak, to różnica jest taka, że każda klasa jaką tworzysz jest komponentem gameobjectu, tzn możesz ją dodać na jakiś obiekt na scenie.

Backend, który właśnie piszesz nie ma nic wspólnego z gameobjectem. MonoBehaviour zapewnia Ci tylko podpinanie tego na obiekt na scenie.

W paktyce im mniej gameobjectów tym lepiej, postaram Ci się to zobrazować.
Jeśli masz grę, w której np. jedziesz autem i patrzysz z perspektywy kierowcy to biorąc pod lupę obsługę deski rozdzielczej mógłbyś mieć to rozbite mniej więcej w taki sposób:

LicznikPrędkości [Canvas jako parent i odpowiednie elementy UI pod nim]
Ten licznik prędkości będzie reprezentowany przez pojedynczy Component, czyli klasę dziedziczącą po MonoBehaviour.
Dzięki temu dodajesz ten komponent np na canvas, i podpinasz mu odpowiednie elementy ui w serializowanych polach tej klasy
Kod: Zaznacz wszystko
public class Speedo : MonoBehaviour
{
  [SerializeField]
  private Text _speedText;
}

Dzięki temu możesz umieścić na canvasie text, podpiąć go do skryptu Speedo i spiąć tym sposobem "grę z jej kodem"

Jednak nie znaczy to, że logika gry ma się odbywać na componentach.
W tym przypadku kategorycznie nie powinno tak być, skrypt odpowiedzialny za UI nie powinien posiadać logiki.
Kierowanie autem odbywałoby się także poprzez Component dlatego, że służyłby on do zbierania inputów, z faktu posiadania funkcji Update() i dostępu do Time.deltaTime;

Zakładając, że mamy jakiś GameObject, reprezentujący samochód i mamy na nim skrypt CarMovementInputsHandler : MonoBehaviour, który to przekazuje nasze inputy do logiki kierującej pojazdem (lub dla uproszczenia po prostu kieruje pojazdem na podstawie naszych inputów) to wystawiamy z takiego skryptu prędkość, którą pozyskaliśmy w ramach jakiegoś wzoru zastosowanego w logice jazdy.

Kod: Zaznacz wszystko
public class CarMovementInputsHandler : MonoBehaviour
{
  public static float Speed { get; private set; }
  private void Update()
  { 
    ...
    Speed = ???
  }
}


Wystawiłem Speed jako statyczną propercję z publicznym getem aby już nie mieszać Ci w głowie z dependency injection ale tym sposobem możesz z innego skryptu odwołać się do tego Speeda

Kod: Zaznacz wszystko
public class Speedo : MonoBehaviour
{
  [SerializeField]
  private Text _speedText;

  [Range(0, 1)]
  [SerializeField]
  private float _uiRefreshFrequency = 0.1f;

  private void Awake()
  {
    //Refreshing
    InvokeRepeating("RefreshSpeed", 0, _uiRefreshFrequency);
  }

  private void RefreshSpeed()
  {
    _speedText.text = CarMovementInputsHandler.Speed.ToString();
  }
}


Jak widzisz w klasie wyświetlającej prędkość nie ma logiki jako takiej, jest po prostu wołana funkcja, która bierze aktualną prędkość i wpisuje ją do pola textowego na ekranie.

MonoBehaviour nie jest po to, żebyś w nim procedował logikę swojej gry, udostępnia on kilka istotnych funkcji ale nie powinien być podstawą twojego kodu.
Logika powinna dziać się w Twoich klasach, na Twoich strukturach a MonoBehaviour powinien Ci służyć do tego, żeby zbierać inputy do tej logiki i poprawnie ją wyświetlać graczowi
Hostur
 
Posty: 663
Rejestracja: 05 Sie 2015, 07:36
Has thanked: 0 time
Been thanked: 0 time

Re: Proszę o radę. Zarządzanie funkcjami zawartości.

Postprzez Cartoon » 02 Gru 2018, 13:44

Dzięki Hostur za wyjaśnienie. Postaram się w przyszłości, bardziej na to zwracać uwagę.

Zawsze robiłem to na swój fikuśny sposób. Nie wywalało błędów to sądziłem, że jest dobrze i mogę tak robić cały czas, aż do gotowego projektu. Dlatego cieszę się, że mogę to poprawić i czegoś się nauczyć. Władam czystą prostotą. Bardziej zaawansowanych skryptów i klas muszę się nauczyć.

Jednak co do MonoBehaviour. To, aż tak cały czas nie korzystam.

Moja klasa Civilian nie jest MonoBehaviourem. Za to klasa CivilianDatabase jest, tak jak i CilivlianInventory i posiadam je na scenie. Inventory będzie później dostępne dla gracza, gdy tylko stworze sloty.

Klasa Player i Supply też jest na scenie. Player ma połączenie do Supply oraz Civilian Inventory. Możliwe, że źle to rozumowałem. Jednak na stole mam dodatkowo spięte w Canvas panel z informacjami o playerze i ilościach materiałów. A w tym panelu spinałem Playera. Dodatkowo jeszcze, posiadam dwa obiekty "navigacja" i "nextday" gdzie mam podpięte dwa skrypty do obsługi playera i civilian posiadanych w inventory. Są niestety na scenie, wpięte do obiektów. A co do klasy experiens w dwóch wydaniach, to przesadziłem, bo nie maiłem wiedzy na ten temat.

Zresztą to jest projekt przejściowy. Gdzie testuje funkcje które mogę później przenieś do mojego głównego projektu. Mogę go spokojnie udostępnić. Może dzięki temu zrozumiem lepiej co źle robię i w jaki sposób mam dalej postępować. Przy okazji inni mogą skorzystać. Zarazem nie proszę o pisanie żadnych skryptów, tylko wytłumaczenie/podpowiedzenie co mogę zrobić i jak.

https://drive.google.com/open?id=14MG0zq20zLbL65FcUmsDsuNzF_6dz67T
Cartoon
 
Posty: 3
Rejestracja: 29 Lis 2018, 07:50
Has thanked: 0 time
Been thanked: 0 time


Wróć do Wsparcie Unity

Kto jest na forum

Użytkownicy przeglądający to forum: Bing [Bot] oraz 11 gości