Dodawanie manipulatorów do kabin na podstawie istniejących już komend

Z MaSzyna
Wersja z dnia 18:16, 17 lut 2025 autorstwa Hirek (dyskusja | edycje)
(różn.) ← poprzednia wersja | przejdź do aktualnej wersji (różn.) | następna wersja → (różn.)
Skocz do: nawigacja, szukaj

Wstęp

Każdy przełącznik/dźwignia/przycisk w maszynie to tak naprawdę wskaźnik/miernik/lampka. Bezpośrednie klikanie na takie coś nie daje żadnego efektu. Aby wszystko działało prawidłowo, tzn. aby przyciski się animowały i reagowały na akcje gracza, należy podjąć kilka kroków opisanych w tym poradniku. Został on przygotowany tak, aby osoba znająca język c++ była w stanie samodzielnie dodać swój pierwszy przycisk, na podstawie istniejących już komend.

Definicja przycisku

Zaczynamy od dodania TGauge (czyli naszego wskaźnika) w pliku Train.cpp i Train.h - najlepiej go od razu opisać za co odpowiada, aby ktoś później edytujący kod, wiedział za co dana zmienna odpowiada. Przykładowa definicja: TGauge ggPantValvesUpdate;

W ten sposób tworzymy obiekt naszego przycisku. Trzeba go zainicjalizować. W tym celu w funkcji ```Update(double const Deltatime)``` dodajemy następujące linijki w odpowiednich sekcjach:

// obok wszystkich innych .Update dla innych wskaźników 
ggPantValvesUpdate.Update();

Pozostaje nam dopisać ten przycisk do funkcji przygotowującej kabinę, czyli set_cab_controls(int const Cab)'

if (ggPantValvesUpdate.SubModel != nullptr)
{
    ggPantValvesUpdate.PutValue(0.f);
}

Tym fragmentem kodu przypisujemy wartość 0 dla naszego wskaźnika jednocześnie go inicjalizując. Pozostaje jeszcze funkcja clear_cab_controls() która ma za zadanie zresetować kabinę (używane jest to w momencie gdy zmieniamy kierunek w którym jedziemy, czy przemieszczając się z jednego pojazdu do innego). Oto przykładowy wpis:

ggPantValvesUpdate.Clear();

Teraz dodajemy interakcje za pomocą myszki. Przycisk/przełącznik który chcemy dodać w tym przykładzie chcemy żeby był definiowany w pliku mmd (sekcja cabXdefinition) kluczem pantvalvesupdate_bt:. Otwieramy plik drivermouseinput.cpp i szukamy funkcji default_bindings(). Na samym jej początku znajduje się mapa przypisań kryjąca się pod nazwą m_buttonbindings. Dodajemy do niej jakie komendy chcemy wykonać wciskając lewy lub prawy przycisk myszki. W naszym przykładzie wpis będzie wyglądał następująco:

{ "pantvalvesupdate_bt:", { // string definiujący klucz w mmd przycisku/przełącznika dla którego chcemy wykonywać akcję
user_command::pantographvalvesupdate, // komenda wykonywana wciskając LPM
user_command::none}}, // komenda wykonywana wciskając PPM - w tym przypadku nie podejmuje zadnej akcji

Uwaga: Należy pamiętać o dwukropku na końcu klucza, Lista dostępnych komend zawarta jest w ENUMie user_command. Można również sprawdzić wywoływaną komendę przez istniejący przycisk za pomocą symulatora, klikając na niego, a następnie patrząc w okienko konsoli zobaczyć jaka komenda została wywołana. Jej treść znajduje się pomiędzy nawiasami kwadratowymi [].

Podpowiedzi po najechaniu myszką

Aby graczowi było łatwiej się zorientować, co za co odpowiada, symulator używa systemu podpowiedzi. Jak można się domyślić, naszemu przyciskowi należy również zdefiniować tzw. hint. Robimy to w pliku translation.cpp dodając w funkcji label_cab_control(std::string const &Label) w mapie cabcontrols_labels tekst podpowiedzi. W tym przykładzie będzie to:

{ "pantvalvesupdate_bt:",  // klucz mmd naszego przycisku
 STRN("selected pantographs up") } // tekst wyświetlany po najechaniu na przycisk

Uwaga: Należy pamiętać o dwukropku na końcu klucza

Animacja

Animacja przycisku odbywa się w funkcji wywoływanej komendy. Listę przypisań komend z ENUMa user_command do funkcji można znaleźć w mapie m_commandhandlers w pliku Train.cpp. Gdy znajdziemy funkcję odpowiadającą naszemu przyciskowi, przechodzimy do niej i dopisujemy linijkę aktualizującą nasz przycisk. Zanim jednak to zrobimy warto, omówić budowę funkcji OnCommand_*. Jak można zauważyć przyjmuje ona dwa parametry - Train oraz Command. Train zawiera odnośnik do pojazdu, do jego obiektu TTrain, a Command kontekst w jakim funkcja została wywołana np. czy przycisk jest kliknięty, trzymany, puszczony, parametry, deltatime między wywołaniami, czy wywołano w trybie freefly (kamera F4), czy lokalizacja wywołania. Najważniejszym elementem będzie Command.action ponieważ dzięki niemu zabezpieczamy naszą funkcję przed zbyt dużą ilością wywołań.

Omówienie budowy OnCommand_*

Omówmy przykładową funkcję:

<nowiki>void TTrain::OnCommand_alarmchainenable(TTrain *Train, command_data const &Command)
{

if (Command.action == GLFW_PRESS) {

        // pull
        Train->mvOccupied->AlarmChainSwitch(true);
        // visual feedback
        Train->ggAlarmChain.UpdateValue(1.0);
    }
}

Dla kogoś kto zna podstawy c++ to część może być zrozumiała. Mamy definicję funkcji OnCommand_alarmchainenable w klasie TTrain, mamy parametry Train zawierający pojazd z którego komenda została wywołana, oraz Command zawierający parametry. Linijkę niżej widzimy warunek, który wykonuje się w momencie naciśnięcia klawisza/przycisku myszki, GLFW_PRESS to jedno z kilku dostępnych makr. Możemy skorzystać z:

  • GLFW_PRESS - naciśnięcie
  • GLFW_REPEAT - trzymanie
  • GLFW_RELEASE - puszczenie

Kolejna linijka odnosi się do movera pojazdu wywołującego komendę oraz wywołuje funkcję AlarmChainSwitch, która uruchamia hamulec bezpieczeństa np. w siódemkach klapa Ackermana.

Kolejna lnijka, tak jak komentarz wskazuje, odpowiada za animację przycisku w wywołującym pojeździe. Jest to przycisk podpisany jako ggAlarmChain i ustawia jego wartość na 1.0.

Dodawanie wpisu animacji

Na podstawie przycisku, który chcieliśmy dodać w poprzednich paragrafach, odpowiednią do tego funkcją będzie OnCommand_pantographvalvesupdate. W warunku if (Command.action == GLFW_PRESS) dopisujemy

Train->ggPantValvesButton.UpdateValue(1.0, Train->dsbSwitch);.

Pierwszy parametr to wartość na jaką ustawiamy przycisk, a drugi to sound_source. Na razie nie będziemy się w to zagłębiać co to i jak działa, do przycisków w zupełności wystarczy Train->dsbSwitch. Po tym co zdefiniowaliśmy na ten moment mamy animację samego wciśnięcia. Trzeba dodać jeszcze animację odkliku. W tym celu dodajemy podaną wyżej linijkę, tym razem z parametrem 0.0 do warunku if (Command.action == GLFW_RELEASE) W ten sposób zdefiniowaliśmy w pełni nasz przycisk i będzie on działał w symulatorze.