Kodowanie Twojego pierwszego projektu
Roblox to najpopularniejsza na świecie platforma do tworzenia gier. Ludzie wszystkich typów spotykają się, aby tworzyć niesamowite wirtualne doświadczenia: artyści, muzycy i - jak się domyślacie - programiści. Kodowanie umożliwia graczom interakcję ze światem, który widzą. W Roblox używanym językiem kodowania jest Lua. Lua jest jednym z najłatwiejszych do nauczenia się języków programowania, a gdy jest używany z Roblox Studio, możesz szybko zobaczyć wyniki swojego kodu. Na przykład, chcesz stworzyć ogromną eksplozję o ogromnym promieniu wybuchu? Możesz to zrobić za pomocą zaledwie kilku linii Lua. Roblox Studio to narzędzie, w którym tworzone są wszystkie gry Roblox, a po sparowaniu z Lua oferuje bezproblemowy dostęp do serwerów dla wielu graczy, systemów fizyki i oświetlenia, narzędzi do budowania świata, systemów monetyzacji i nie tylko. I chociaż Roblox zapewnia środowisko, w którym działa Twój program, to Ty kontrolujesz wizję. Jesteś twórcą i artystą. Roblox daje ci płótno i farby, a Lua pędzle i akcje. Ale ty, mając kilka dobrze umieszczonych fragmentów kodu, możesz stworzyć swoje arcydzieło. Ta pierwsza Część dotyczy konfigurowania Roblox Studio, tworzenia pierwszego skryptu i testowania kodu.
Instalacja Roblox Studio
Zanim zaczniesz, upewnij się, że masz zainstalowane Roblox Studio. Działa w systemach Windows i MacOS, a kopię można pobrać pod adresem https://roblox.com/create. Kliknij Rozpocznij tworzenie, aby rozpocząć. Musisz utworzyć konto Roblox, jeśli jeszcze go nie masz
Wybierzmy się na wycieczkę
Roblox Studio zapewnia wszystko, czego potrzebujesz do tworzenia gier. Obejmuje zasoby, takie jak modele postaci, przedmioty do umieszczenia w świecie, grafiki nieba, ścieżki dźwiękowe i wiele innych. Śmiało uruchom Roblox Studio, aby zobaczyć okno pokazane na rysunku.
br>
Wprowadź dane logowania do konta utworzonego podczas rejestracji na stronie internetowej Roblox i kliknij Zaloguj się. Gdy po raz pierwszy otworzysz Studio, zobaczysz szablony. To są miejsca startowe, które możesz wykorzystać do swoich doświadczeń. Najprostszym punktem wyjścia dla każdego projektu jest szablon Baseplate. Kliknij szablon Baseplate, jak pokazano na rysunku
Zacznijmy od krótkiego przeglądu głównych części ekranu z rysunku
, a następnie przejdźmy od razu do pierwszej linii kodu:
1. Oferty na wstążce paska narzędzi zmieniają się zgodnie z wybraną kartą menu.
2. Zestaw narzędzi zawiera istniejące zasoby, które możesz dodać do swojej gry. Możesz także tworzyć własne zasoby za pomocą programu do modelowania 3D, takiego jak Blender3D, a Studio zawiera zestaw narzędzi do edycji siatki, aby dostosować już dostępne modele 3D.
3. Edytor 3D zapewnia widok świata. Przytrzymaj prawy przycisk myszy, aby obrócić widok, i użyj klawiszy WASD, aby zmienić położenie kamery. Tabela opisuje różne elementy sterujące służące do poruszania kamerą.
Klawisz : Ruch
W A S D : Przesuń kamerę w górę, w lewo, w dół lub w prawo
E : Przesuń kamerę
P: Opuść aparat
Shift: Przesuwaj kamerę wolniej
Prawy przycisk myszy (przytrzymaj i przeciągnij mysz) Obróć kamerę
Środkowy przycisk myszy : przeciągnij kamerę
Kółko przewijania myszy: przybliżanie lub oddalanie kamery
F : Ustaw ostrość na wybranym obiekcie
4. Okno Eksploratora zapewnia wygodny dostęp do każdego kluczowego zasobu lub systemu w grze. Używasz tego do wstawiania obiektów do swojego doświadczenia.
5. Użyj okna Właściwości, aby wprowadzić zmiany w obiektach w grze, takie jak kolor, skala, wartość i atrybuty. Wybierz obiekt w Eksploratorze, aby zobaczyć dostępne właściwości.
Istnieje wiele sposobów konfiguracji tego ekranu głównego, w tym ukrywanie różnych sekcji, zmiana ich położenia w celu wygodniejszego i zmiana ich rozmiaru. Roblox Studio to bardzo kompletne środowisko do tworzenia gier, które znacznie wykracza poza Lua.
Otwieranie okna danych wyjściowych
Okno Dane wyjściowe w Studio nie jest domyślnie otwarte, ale jest to potrzebne przed kontynuowaniem, aby można było zobaczyć błędy i komunikaty związane z Twoim kodem. Wykonaj następujące czynności, aby wyświetlić okno danych wyjściowych:
1. Kliknij kartę Widok.
Jeśli kiedykolwiek zamkniesz okno i będziesz musiał je ponownie otworzyć, znajdziesz je tutaj.
2. Kliknij Output ,
aby wyświetlić okno Output na dole ekranu, jak pokazano na rysunku
Pisanie pierwszego skryptu
Do kodowania! Potrzebujesz czegoś do przechowywania kodu, a to jest skrypt. Możesz wstawiać skrypty bezpośrednio do obiektów w świecie. W tym przypadku wstawiasz skrypt do części. Wstaw skrypt do części Część jest podstawowym budulcem Roblox. Części mogą mieć rozmiary od bardzo małych do bardzo dużych. Mogą to być różne kształty, takie jak kula lub klin, lub można je łączyć w bardziej złożone kształty.
1. Wróć do zakładki Strona główna i kliknij Część.
Część pojawi się w Edytorze 3D na środku widoku z kamery.
2. Aby dodać skrypt, w Eksploratorze najedź kursorem na część i kliknij symbol +, a następnie wybierz opcję Skrypt z rozwijanego menu.
WSKAZÓWKA: Szybkie znajdowanie przedmiotów. Wpisanie pierwszej litery (w tym przypadku S) lub dwóch dodawanych elementów filtruje listę, dzięki czemu można szybko zlokalizować ten element. Skrypt otworzy się automatycznie. U góry widać słowa znane każdemu programiście: "Helloworld!".
Pisanie kodu
Od lat 70. "Witaj, świecie!" był jednym z pierwszych fragmentów kodu, których ludzie się nauczyli. Tutaj jest używany w funkcji drukowania. Funkcje to fragmenty kodu służące do określonego celu. Gdy nauczysz się kodować, będziesz korzystać z gotowych funkcji, takich jak print(), która wyświetla komunikaty w oknie Dane wyjściowe. Oczywiście nauczysz się również, jak tworzyć własne funkcje. print() wyświetla ciąg, który jest typem danych zwykle używanym z literami i cyframi, które muszą pozostać razem. W tym przypadku drukujesz "Witaj, świecie!":
1. Dostosuj ten kod do siebie, zmieniając wiadomość w cudzysłowie na to, co chcesz dziś zjeść na kolację. Oto przykład:
print("Chcę dużo makaronu")
2. Aby przetestować kod, w zakładce Strona główna kliknij Odtwórz .
Twój awatar spadnie na świat, aw oknie Wyjście zobaczysz swoje sny o kolacji wraz z notatką o tym, z jakiego skryptu pochodzi ta wiadomość
.
3. Aby zatrzymać test, kliknij przycisk Zatrzymaj.
4. Wróć do swojego skryptu, klikając zakładkę nad Edytorem 3D, jak pokazano na rysunku
Zaszyfruj eksplozję
Kod oczywiście może zrobić więcej niż tylko wyświetlać komunikaty w oknie danych wyjściowych. Może całkowicie zmienić sposób interakcji graczy ze światem i ożywić go. Weźmy nieco dłuższy fragment kodu i sprawmy, by blok w szablonie Baseplate niszczył wszystko, czego dotknie:
1. Użyj narzędzia Przesuń,
aby przesunąć blok z ziemi i od punktu odrodzenia. Kod, który zamierzasz napisać, zniszczy wszystko, czego dotknie, a nie chcesz, aby zadziałał przedwcześnie.
2. W oknie Właściwości przewiń do opcji Zachowanie i upewnij się, że wybrana jest opcja Zakotwiczony , aby blok nie spadł po kliknięciu Odtwórz.
3. W skrypcie pod funkcją print dodaj następujący kod:
print("I want lots of pasta!")
-- Destroys whatever touches the part
local trap = script.Parent
local function onTouch(partTouched)
partTouched:Destroy()
end
trap.Touched:Connect(onTouch)
UWAGA: pola kodów. Pola kodu dla tej książki będą prezentowane w trybie jasnym, chyba że wyraźnie zwrócę uwagę na Studio UX.
4. Kliknij Odtwórz, podbiegnij i dotknij części.
Rezultatem powinno być to, że twoja postać się zepsuje lub części twojego awatara zostaną zniszczone. Możesz zauważyć, że ten kod niszczy tylko to, co go bezpośrednio dotyka, na przykład twoje stopy. Spróbuj wskoczyć na blok lub otrzeć się o niego tylko ręką. Zobaczysz tylko, że część twojego awatara jest zniszczona. Powodem jest to, że kod robi tylko to, co mu każesz, i powiedziałeś części, aby niszczyła tylko to, czego dotyka i nic więcej. Musisz mu powiedzieć, jak zniszczyć resztę warstwy. Z tej książki dowiesz się, jak napisać dodatkowe instrukcje, aby kod mógł obsłużyć więcej scenariuszy, takich jak ten. W części 4, "Parametry i argumenty", dowiesz się, jak upewnić się, że zniszczy to całą postać gracza.
Komunikaty o błędach
Co jeśli kod nie zadziałał? Prawda jest taka, że wszyscy inżynierowie popełniają błędy w swoim kodzie. To nic wielkiego, a edytor i okno wyników mogą pomóc Ci wykryć błędy i je naprawić. Spróbuj popełnić kilka błędów, aby później nauczyć się je lepiej dostrzegać:
1. Usuń drugi nawias z funkcji print. Pod lokalnymi pojawia się czerwona linia.
W edytorze czerwone linie oznaczają problem.
2. Najedź kursorem na czerwoną linię, a edytor poda ci wskazówkę, co poszło nie tak, jak pokazano na rysunku.
Ale nie naprawiaj jeszcze błędu.
3. Kliknij Play, co spowoduje wyświetlenie komunikatu o błędzie w oknie Output, jak pokazano na rysunku.
Kliknij czerwony błąd, a Studio przeniesie Cię tam, gdzie według niego występuje problem.
Zatrzymaj test gry i napraw problem.
WSKAZÓWKA: Zmiany wprowadzone podczas testowania gry nie są trwałe . Zachowaj ostrożność podczas wprowadzania zmian podczas testu gry, ponieważ praca, którą wykonałeś, nie jest automatycznie zapisywana. Jeśli dokonasz zmian, pamiętaj, aby kliknąć Zachowaj zmiany po zatrzymaniu testu gry.
Pozostawianie sobie komentarzy
W poprzednim kodzie możesz zauważyć zdanie -- Niszczy wszystko, co dotknie części. To jest komentarz. Komentarze zaczynają się od dwóch myślników. Wszystko w tej samej linii, co kreski, nie wpływa na skrypt. Programiści używają komentarzy, aby zostawiać sobie i innym notatki na temat tego, co robi kod. Zaufaj nam: jeśli od miesięcy nie przeglądałeś fragmentu kodu, bardzo łatwo jest zapomnieć, do czego on służy. Poniższy kod pokazuje, jak mogłoby wyglądać dodanie komentarza na początku skryptu napisanego wcześniej w ciągu tej godziny:
-- Co chcę na obiad?
print("Chcę dużo makaronu!")
Streszczenie
Przebyłeś długą drogę, szczególnie jeśli zdarzyło ci się to po raz pierwszy kodować lub używać Roblox Studio. Obejmowało to utworzenie konta i otwarcie Robloxa po raz pierwszy. Za pomocą przycisku + można było wstawić skrypt do części, a następnie dodać kod, który zamieniał część w pułapkę dla każdego, kto jej dotknął. Ponadto nauczyłeś się testować kod za pomocą przycisku Odtwórz i korzystać z wbudowanego wykrywania błędów w edytorze skryptów i oknie danych wyjściowych, aby pomóc w rozwiązywaniu problemów, gdy coś pójdzie nie tak. Wreszcie dowiedziałeś się o komentarzach, które są czytelne tylko w edytorze skryptów i mogą służyć do pozostawiania notatek o przeznaczeniu kodu.
Pytania i odpowiedzi
P. Czy możesz używać Studio na Chromebooku?
O. Aby utworzyć, Studio musi być uruchomione na komputerze z systemem MacOS lub Windows. Po opublikowaniu gry można w nią grać na urządzeniach z systemem Android, Apple, Mac, PC, Chrome, a potencjalnie nawet na XBox Live.
P. Jak ponownie otworzyć skrypt, jeśli go zamknę?
O. Jeśli zamkniesz edytor skryptów, możesz go ponownie otworzyć, klikając dwukrotnie obiekt skryptu w Eksploratorze.
P. Jak zapisać swoją pracę?
O. Przejdź do Plik, Publikuj w Roblox, aby zapisać w chmurze, dzięki czemu Twoja gra będzie dostępna z dowolnego komputera.
P. Gdzie mam się udać, jeśli chcę uzyskać dodatkowe informacje o tym, jak działa Roblox Studio?
O. Możesz odwiedzić stronę developer.roblox.com, aby znaleźć dokumentację dotyczącą wszystkich funkcji Studio i interfejsu API.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Roblox używa języka kodowania ________.
2. Aspekty obiektu, takie jak kolor, obrót i zakotwiczenie, można znaleźć w oknie ______.
3. Obiekty gry znajdują się w oknie ________.
4. Aby włączyć okno Wyjście, w którym wyświetlane są komunikaty kodu i błędy, włącz je w zakładce ________.
5. Prawda czy fałsz: Komentarze zmieniają kod, aby umożliwić nową funkcjonalność.
6. Aby zmusić części do pozostania na miejscu, muszą być ________.
Odpowiedzi
1.Lua
2. Properties
3. Explorer
4. View
5. Fałsz. Komentarze nie mają wpływu na kod i służą do pozostawienia notatek dla siebie i innych programistów co do celu skryptu.
6. Zakotwiczone
Ćwiczenia
Zanim przejdziesz dalej, poświęć chwilę na eksperymentowanie z narzędziami do tworzenia, tworząc mini tor przeszkód. Mogą to być pojedyncze części, których gracz musi unikać, lub podłoga z lawy, taka jak ta pokazana na rysunku
Porady
• Twórz więcej części i manipuluj nimi za pomocą narzędzi Przesuń, Przetłumacz i Skaluj znajdujących się na karcie Narzędzia główne.
Możesz także zmienić wygląd części za pomocą opcji Materiał i Kolor.
• Użyj jednej dużej części i wstaw skrypt tak jak wcześniej, aby zamienić ją w lawę.
• Dodatkowe modele można znaleźć w Przyborniku; pamiętaj tylko, że niektóre modele mogą już zawierać skrypty.
• Nie zapomnij zakotwiczyć wszystkich części i modeli.
• Jeśli wiesz, jak korzystać z narzędzi terenowych, możesz wykorzystać je również na swoim torze przeszkód.
Właściwości i zmienne
Hierarchia obiektów
Jeśli chcesz wpływać na obiekty za pomocą kodu, musisz wiedzieć, gdzie te obiekty znajdują się w hierarchii gry. Gdy spojrzysz w Eksploratorze, możesz zobaczyć, że niektóre obiekty gry są zagnieżdżone w innych. Na przykład na rysunku
widać, że obiekt Baseplate jest zagnieżdżony w przestrzeni roboczej. To sprawia, że Baseplate jest dzieckiem Workspace, co czyni Workspace obiektem nadrzędnym. I chociaż nie widać tego w Eksploratorze, Workspace jest dzieckiem Game. W kodzie możesz poruszać się po hierarchii gry za pomocą operatora kropki - na przykład game.Workspace.Baseplate. W ten sposób podajesz wskazówki w skrypcie, aby powiedzieć kodowi, z jakim obiektem ma pracować.
Znajdź i zniszcz
Użyj operatora kropki, aby wyszukać kasetę bazową w obszarze roboczym i użyj funkcji Destroy(), której użyto również w Godzinie 1, aby pozbyć się kasety bazowej.
1. Wstaw nowy skrypt do Baseplate. Zmień nazwę skryptu DestroyBaseplate, klikając go dwukrotnie lub naciskając klawisz F2.
WSKAZÓWKA
Zmienianie nazw skryptów i obiektów w projekcie jest ważne dla zachowania porządku.
2. W skrypcie wpisz game.Workspace.Baseplate:Destroy().
3. Przetestuj grę, a płyta konstrukcyjna zostanie zniszczona, być może nawet przed załadowaniem postaci.
Słowa kluczowe
Porozmawiajmy teraz o słowach kluczowych. Słowa kluczowe można traktować jako słowa, które składają się na język kodowania. Każde słowo kluczowe służy specjalnemu celowi. Lua ma mniej słów kluczowych niż większość języków kodowania, co czyni go jednym z najłatwiejszych do nauczenia. Niektóre słowa kluczowe są automatycznie wbudowane w Lua, a niektóre zostały dodane przez Roblox, aby ułatwić. Jedno słowo kluczowe w Roblox Lua to obszar roboczy, pisane małymi literami, ponieważ game.Workspace było wpisywane tak często, że przemyślani inżynierowie Roblox postanowili podać słowo kluczowe, aby je skrócić. Wróćmy teraz do hierarchii. Operator kropki umożliwia dostęp nie tylko do dzieci obiektów, ale także do obiektów nadrzędnych. Tym razem użyj słowa kluczowego script, które zawsze reprezentuje obiekt Script bez względu na jego nazwę, i użyj operatora kropki, aby uzyskać dostęp do elementu nadrzędnego.
Użyj słowa kluczowego workspace
Zaktualizuj kod, który właśnie napisałeś, dodając słowo kluczowe workspace zamiast game.Workspace.
1. W swoim poprzednim kodzie zastąp game.Works tempem obszarem roboczym.
WSKAZÓWKA
Właściwa wielkość liter jest ważna
W słowach kluczowych rozróżniana jest wielkość liter, więc upewnij się, że obszar roboczy jest pisany małymi literami.
2. Przetestuj i sprawdź, czy kod nadal działa.
Skróć kod
W rzeczywistości możesz jeszcze bardziej skrócić kod i pozbyć się płytki bazowej, używając funkcji Destroy() ze skryptem. Rodzic:
1. W tym samym skrypcie co poprzednio zastąp swój kod skryptem.Parent:Destroy().
WSKAZÓWKA
Skorzystaj z autouzupełniania
Podczas pisania może pojawić się sugerowany kod. Możesz zaakceptować sugestię, naciskając Enter. Pozwoli to zaoszczędzić czas na pisanie i zminimalizuje ryzyko popełnienia literówek.
2. Przetestuj i zweryfikuj swój kod.
Nieruchomości
Oprócz poruszania się po hierarchii operator kropki umożliwia również dostęp do właściwości obiektu. Czym więc są właściwości? Wyjaśnię na przykładzie: spójrz na kwiat.
Jak byś to komuś opisał? Może zacząłbyś od stwierdzenia, że to roślina. Naciskany, aby uzyskać więcej informacji, możesz powiedzieć, że jest to zielona roślina z żółtymi płatkami. Inżynier może dodać dodatkowe szczegóły, na przykład zieloną roślinę z żółtymi płatkami, mającą trzy jednostki wysokości i dwie szerokości. Ktoś inny mógłby wspomnieć, że się pali.
Znajdowanie właściwości i typów danych
Kiedy klikniesz obiekt w Eksploratorze, trafnie nazwane okno Właściwości wypełnia się różnymi właściwościami obiektu, które można zmieniać. Różne formaty, w których właściwości śledzą wartości, to typy danych. Niektóre ważne typy danych, od których warto zacząć, to:
• Liczba: Dowolna liczba rzeczywista - na przykład 11,9.
• Ciąg znaków: zbiór liter i/lub cyfr ujętych w cudzysłowy. Dobre do przechowywania czytelnych informacji. print() akceptuje wartości łańcuchowe - na przykład "99 bananów".
• Boolean: Wartości prawda i fałsz. Właściwości, które mają stany takie jak włączenie/wyłączenie lub zaznaczenie/odznaczenie, są często wartościami boolowskimi.
• Tabele: zestaw informacji - na przykład {Am y, Bill, Cathleen}.
Tworzenie zmiennych
Teraz, gdy wiesz już, jak znajdować obiekty w hierarchii i jak każda właściwość ma swój własny format wartości zwany typem danych, możesz przystąpić do tworzenia zmiennych. Zmienne są symbolami zastępczymi dla informacji. Można ich używać do śledzenia obiektów i typów danych do wykorzystania w kodzie. Po utworzeniu niektóre zmienne mogą być używane tylko w określonych skryptach lub fragmentach kodu. Są to tak zwane zmienne lokalne. Inne zmienne są zaprojektowane tak, aby można było ich używać w szerszym zakresie w różnych skryptach. Są to tak zwane zmienne globalne. Jeśli nie masz dobrego powodu, prawie zawsze chcesz używać zmiennych lokalnych. Twój kod działa szybciej ze zmiennymi lokalnymi i jest mniej prawdopodobne, że w kodzie pojawią się sprzeczne nazwy zmiennych. Prawie wszystkie zmienne, które utworzysz w tej książce, będą zmiennymi lokalnymi. Aby utworzyć zmienną lokalną, wpisz local, a następnie żądaną nazwę zmiennej, na przykład:
local baseplate
Po utworzeniu zmiennej możesz przypisać lub ustawić wartość zmiennej za pomocą znaku równości, na przykład:
local baseplate = script.Parent
W twojej głowie możesz myśleć o znaku równości takim, jakim jest to słowo. Tak więc poprzednia zmienna odczytałaby basePlate jako script.Parent. Po utworzeniu zmiennej możesz uzyskać dostęp do przechowywanych informacji tyle razy, ile chcesz, za pomocą samej nazwy, na przykład:
local basePlate = script.Parent
basePlate.Transparency = 0.5
Zmienne można aktualizować tak często, jak chcesz. Jeśli więc prowadzisz wynik w grze, za każdym razem, gdy gracz zdobywa nowy punkt, możesz nadal używać tej samej zmiennej i przypisywać jej zaktualizowany wynik, na przykład:
local playerScore = 10
print("playerScore is " .. playerScore)
local playerScore = playerScore + 1 -- Add one to current player score
print( "new playerScore is " .. playerScore)
Na wyjściu powinny pojawić się komunikaty drukowania podobne do pokazanego na rysunku
WSKAZÓWKA
Łączenie ciągów znaków i zmiennych
print() może akceptować zarówno ciągi, jak i zmienne, ale y musi być połączone z dwiema kropkami. Łączenie wartości nazywa się konkatenacją.
Stwórz NPC
Mając tylko wiedzę, którą posiadasz do tej pory, możesz stworzyć przewodnik NPC, który dostarczy graczowi ostrzeżenie o zbliżającym się polu lawy. To ćwiczenie pomoże ci przećwiczyć poruszanie się po hierarchii i właściwościach za pomocą operatora kropki, a także używanie zmiennych i typów danych. Najpierw musisz stworzyć NPC:
1. Użyj rozwijanego menu Część, aby utworzyć kulę lub dowolny inny typ części.
2. Zmień nazwę części na GuideNPC.
3. Wstaw skrypt do sfery i zmień jego nazwę.
4. Wstaw obiekt Dialog do GuideNPC. Nie zmieniaj nazwy.
Zakoduj skrypt
W tym przykładzie tworzysz dwie różne zmienne. Pierwsza zmienna prowadzi do części nadrzędnej, a druga zawiera wiadomość, którą przewodnik duchowy wita gracza, gdy zostanie o to poproszony. Dodajesz także trochę kodu, aby dostosować wygląd NPC.
1. Zastąp domyślny kod w NPCScript nową zmienną lokalną o nazwie guideNPC, która wskazuje na rodzica skryptu.
local guide = script.Parent
2. Utwórz drugą zmienną zawierającą wiadomość przewodnika z wartością ciągu. Wiadomość może być dowolna, o ile jest to ciąg znaków.
local guideNPC = script.Parent
local message = "Danger ahead, stay on the rocks!"
3. Spraw, by NPC był bardziej upiorny, otwierając jego właściwości i ustawiając przezroczystość na 0,5.
local guideNPC = script.Parent
local message = "Danger ahead, stay on the rocks!"
guideNPC.Transparency = 0.5
4. Uzyskaj dostęp do potomnego obiektu Dialog i jego właściwości InitialPrompt. Ustaw InitialPrompt na wiadomość.
local guideNPC= script.Parent
local message = "Danger ahead, stay on the rocks!"
guideNPC.Transparency = 0.5
guideNPC.Dialog.InitialPrompt = message
Playtest i kliknij znak zapytania nad głową NPC, aby zobaczyć wiadomość.
WSKAZÓWKA
Konwencje nazewnictwa obiektów
Dla zachowania spójności, obiekty w grze są nazywane przy użyciu CamelCase z pierwszą literą pisaną wielką literą, a zmienne nazwane ich imionami są nazywane pascalCased, z pierwszą literą pisaną małą literą.
Zmiana właściwości koloru
Właściwością często zmienianą w kodzie jest właściwość koloru obiektu. Aby zmienić kolor, musisz zrozumieć, jak działa światło. Każdy kolor na ekranie jest w rzeczywistości produktem zaledwie trzech rodzajów światła; czerwony, zielony i niebieski. Intensywność każdego koloru mieści się w zakresie od 0 do 255. Wszystkie trzy kolory o pełnej mocy (255, 255, 255) są wyświetlane na ekranie jako białe. Każdy pasek odwrócony do końca (0, 0, 0) jest czarny. Czysta czerwień to (255, 0, 0), a czysta zieleń to (0, 255, 0). Jak myślisz, czym jest czysty niebieski? Zmień swojego NPC na fioletowy, mieszając trochę czerwieni z dużą ilością niebieskiego:
guideNPC.Color = Color3.fromRGB(40, 0, 160)
WSKAZÓWKA
Użyj próbnika kolorów, aby znaleźć właściwe wartości
Podczas pisania pojawi się małe koło kolorów .
Jeśli go klikniesz, możesz wybrać żądany kolor i kliknąć OK, aby automatycznie ustawić prawidłową wartość RGB.
Instancje
Ostatnim tematem są instancje. Instancje to kopie obiektów gry, takich jak części, skrypty i błyskotki. Zamiast używać przycisku +, jak dotychczas, instancje można tworzyć za pomocą funkcji Instance.new(), jak pokazano tutaj:
local part = Instance.new("Part")
Po utworzeniu części możesz uzyskać dostęp do wszystkich jej właściwości w normalny sposób. Wprowadź żądane zmiany, a następnie ustaw je jako nadrzędne w obszarze roboczym.
Utwórz nowe wystąpienie części
Zamiast wstawiać część bezpośrednio do Eksploratora, użyj kodu do utworzenia części, zmień kolor części, a następnie umieść ją w obszarze roboczym, gdzie będzie widoczna.
1. W ServerScriptService dodaj nowy skrypt.
2. Utwórz wystąpienie części; następnie ustaw kolor i wreszcie rodzica:
local part = Instance.new("Part")
part.Color = Color3.fromRGB(40, 0, 160)
part.Parent = workspace
Możesz nawet pójść o krok dalej, tworząc instancje wewnątrz instancji:
local part = Instance.new("Part")
local particles = Instance.new("ParticleEmitter")
part.Color = Color3.fromRGB(40, 0, 160)
particle.Parent = part
part.Parent = workspace
WSKAZÓWKA
Nowe wystąpienie części pojawia się w środku świata
Kiedy nowe części są tworzone za pomocą kodu, pojawiają się w samym centrum świata, gdzie znajduje się domyślny punkt odradzania. Jeśli nie widzisz swojej części podczas testowania, spróbuj przesunąć punkt odradzania, a następnie przetestuj ponownie.
Streszczenie
Każdy obiekt w grze ma właściwości, takie jak kolor, skala i przezroczystość, które określają wygląd i zachowanie obiektu w grze. Każda właściwość używa wartości sformatowanych w określony sposób zwany typem danych. Kilka typowych typów danych to ciągi znaków, wartości logiczne i liczby. W kodzie notacja kropkowa służy do uzyskiwania dostępu do właściwości obiektu, a także do znajdowania obiektu w hierarchii Eksploratora. Gdy zrozumiesz właściwości obiektu i sposób uzyskiwania do nich dostępu w hierarchii gry, możesz zacząć wprowadzać zmiany za pomocą kodu. Zmiennych można używać jako symboli zastępczych dla informacji, z którymi skrypt ma pracować. Istnieją dwa główne typy zmiennych, globalne i lokalne. Z tych dwóch zawsze należy używać zmiennych lokalnych, chyba że istnieje konkretny powód, aby tego nie robić. Obiekty gry, takie jak Parts, Scripts, Dialogs i ParticleEmitter s, można tworzyć w działającym skrypcie za pomocą funkcji Instance.new(), która akceptuje nazwę typu obiektu jako ciąg znaków.
Pytania i odpowiedzi
P. Skąd wiesz, jaki typ danych akceptuje obiekt?
O. Możesz wyszukać obiekt gry, jego właściwości i odpowiadające im typy na stronie developer.Roblox.com. Na przykład w wyszukiwarce wpisz Roblox Dialog Properties i poszukaj wyników API w domenie Roblox. Na rysunku
możesz zobaczyć część strony Dialog API. Zawiera krótki opis i listę właściwości wraz z odpowiadającymi im typami danych. Możesz kliknąć każdą właściwość i typ danych, aby dowiedzieć się więcej o tym, jak z nich korzystać.
P. Dlaczego nie należy ustawiać zmiennej tak, aby zawierała właściwość, którą chcesz zmienić? Jak lokalny PartCo lor = workspace.Part.Color?
O. Informacje o hierarchii i informacje o właściwościach to dwa różne typy danych i nie można ich mieszać.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Warsztat
1. Jaki typ danych akceptuje tylko wartości prawda lub fałsz?
2. Zmienne służą _____ do informacji.
3. Gdybym przechowywał imię gracza, jaki typ danych byłby dobry: string, boolean, enum czy float?
4. Aby uzyskać dostęp do skryptu nadrzędnego, użyj ________.
5. Obiekt Dialog wstawiony do części jest ______ części?
6. Proces łączenia wartości do użycia przez print() nazywa się _____.
Odpowiedzi
1. Logiczne
2. Symbole zastępcze
3. Ciąg
4. skrypt.Rodzic
5. Dziecko
6. Konkatenacja
Ćwiczenia
Twarz sprawiłaby, że twój NPC byłby bardziej przystojny. Można go łatwo dodać, wkładając naklejkę. W przypadku tekstury możesz użyć tej z podanego linku lub przesłać własną.
Porady
• Nadaj duchowi twarz ,
wstawiając instancję kalkomanii i aktualizując właściwość tekstury następującym ciągiem: "http://www.roblox.com/asset?id=494290547"
• Być może będziesz musiał obrócić NPC, aby skierował go we właściwą stronę. Lub możesz spróbować zaktualizować właściwość twarzy kalkomanii, aby zmienić położenie kalkomanii.
• Znajdź rozwiązanie kodowe w załączniku.
• W drugim ćwiczeniu sprawdź, czy możesz utworzyć przewodnika duchowego od początku do końca tylko przy użyciu kodu.
Tworzenie i używanie funkcji
W Częściach 1 i 2 korzystałeś z gotowych funkcji print(), destroy() i new(). Ta Część mówi więcej o tym, czym właściwie są funkcje, jak tworzyć własne funkcje i jak uruchamiać swoje funkcje przy użyciu zdarzeń, które mają miejsce na świecie. Druga połowa godziny poświęcona jest trochę o organizacji kodu, dzięki czemu można lepiej zrozumieć, jakie znaczenie ma umieszczenie w skrypcie, aby upewnić się, że kod będzie działał.
Tworzenie i wywoływanie funkcji
Funkcje to spakowane bity kodu przeznaczone do określonych celów, których można używać w razie potrzeby i tak często, jak to konieczne. Kod, którego użyłeś w ciągu ostatniej godziny do stworzenia swojego NPC, został uruchomiony zaraz po rozpoczęciu sesji testowej. Ale co, jeśli nie chcesz, aby kod działał od razu? Powiedz, czy chcesz, aby NPC pojawiał się tylko po kliknięciu przez gracza przycisku lub ukończeniu zadania. Albo co, jeśli chcesz stworzyć wielu NPC, ale nie chcesz ponownie pisać tego samego kodu. Tego typu scenariusze doskonale nadają się do funkcji. Możesz napisać kod tak samo, jak to zrobiłeś, spakować go w funkcję i uruchomić kiedykolwiek chcesz.
1. Aby utworzyć funkcję, wpisz nazwę funkcji lokalnejofFunction.
2. Naciśnij Enter, aby automatycznie zamknąć funkcję z końcem. Twój kod będzie wyglądał następująco:
local function nameOfFunction()
end
3. Wewnątrz funkcji dodaj swój kod we wciętym wierszu. W tym przypadku użyliśmy print() tylko do celów testowych. Cały kod funkcji musi zostać wpisany przed końcem:
local function nameOfFunction()
print("Function Test")
end
WSKAZÓWKA
Wcięcie kodu
Kod będzie działał, jeśli nie zostanie odpowiednio wcięty. Mimo to odpowiednie wcięcia znacznie ułatwiają czytanie kodu Tobie i innym osobom, dlatego zdecydowanie zalecamy stosowanie wcięć w kodzie.
4. Po utworzeniu funkcji pozostaje tylko wydać polecenie jej uruchomienia. Aby to zrobić, musisz wywołać funkcję, wpisując nazwę funkcji, po której następuje nawias:
local function nameOfFunction()
print("Function Test")
end
nameOfFunction()
Jeśli nie wywołasz tej funkcji, nie zostanie ona uruchomiona.
5. Funkcja uruchomi się tyle razy, ile ją wywołasz. Spróbuj wywołać go jeszcze kilka razy:
local function nameOfFunction()
print("Function Test")
end
nameOfFunction()
nameOfFunction()
nameOfFunction()
Nazwa funkcji może być dowolna, pod warunkiem, że następuje po niej znak (), ale poświęćmy chwilę na zastanowienie się nad prawidłowym nazywaniem funkcji. Oto kilka wskazówek, których należy przestrzegać:
• Nazwy powinny informować o tym, co robi funkcja. Na przykład zniszczenie() wyraźnie niszczy rzeczy.
• Nazwy funkcji w Lua są zazwyczaj pascalCased. Zaczynają się małą literą, a każde nowe słowo jest pisane wielką literą.
• Nie używaj spacji ani znaków specjalnych w nazwach funkcji. Spowoduje to użycie błędów.
WSKAZÓWKA
Metoda: inna nazwa funkcji
Funkcje, które są prekompilowane lub należą do istniejących obiektów świata, takie jak print(), wait() i destroy() są często określane jako metody w innych językach kodowania. Użytkownicy Lua zwykle mówią po prostu funkcja, niezależnie od tego, czy można ją nazwać metodą.
Zrozumienie zakresu
Wspomniano tylko, że żaden kod, który nie znajduje się między pierwszym a ostatnim wierszem funkcji, nie zostanie uruchomiony, gdy funkcja zostanie wywołana. Kod znajdujący się poza funkcją jest poza zakresem. Zakres to informacje, które określony fragment kodu, taki jak funkcja, może zobaczyć i uzyskać do niego dostęp. Jeśli uruchomisz poniższy kod, funkcja print wewnątrz funkcji uruchomi się trzy razy, a funkcja print na zewnątrz tylko raz:
local function scopeTest()
print("This is in scope")
end
print("This is out of scope")
scopeTest()
scopeTes t()
scopeTest()
Używanie zdarzeń do wywoływania funkcji
Jednym ze sposobów wywołania funkcji jest samo wpisanie nazwy funkcji, co sprawdza się, gdy chcesz, aby została wywołana w określonym miejscu skryptu. Czasami jednak nie wiadomo z góry, kiedy funkcja powinna zostać wywołana. Chcesz, aby funkcja była uruchamiana, gdy w doświadczeniu wydarzy się coś szczególnego. Oto kilka przykładów:
* Dawanie użytkownikowi miecza po kliknięciu skrzyni z łupami
* Przypisywanie gracza do drużyny po dołączeniu do gry
* Zniszczenie kawałka mostu, gdy gracz go dotknął
W przypadku tego rodzaju scenariuszy nie wiesz z góry, kiedy się pojawią, ale wiesz, jaki kod chcesz uruchomić, kiedy to nastąpi. To, na co czekasz, to konkretne wydarzenie. Kiedy zdarzenie ma miejsce, wyzwalany jest sygnał, którego można użyć do nakazania wykonania kodu. Aby wywołać funkcję za każdym razem, gdy zdarzenie zostało uruchomione, użyj funkcji Connect() i podaj nazwę funkcji, która ma zostać uruchomiona, ale pomiń znak (). Oto przykład:
partName.Touched:Connect(functionName)
Zdarzenie Touched jest wbudowane w części, więc można uzyskać do niego dostęp za pomocą operatora kropki, tak jak inne elementy podrzędne. Dwukropek jest następnie używany do uzyskania dostępu do funkcji o nazwie Connect().
Stwórz znikający most
Obiekty części mają kilka wbudowanych zdarzeń, z których jednym z najbardziej użytecznych jest Dotknięcie. Zdarzenie Touched jest uruchamiane za każdym razem, gdy dojdzie do kolizji z jego częścią nadrzędną. Wykorzystajmy zdarzenie Touched do stworzenia mostu, w którym elementy stają się przezroczyste pół sekundy po dotknięciu przez odkrywcę:
1. Utwórz fragment mostu
z części lub modeli. Upewnij się, że zakotwiczyłeś część na miejscu.
2. Wstaw skrypt do części i zmień nazwę skryptu BridgeScript
3. Przypisz część nadrzędną do zmiennej lokalnej: local bridgePart = script.Parent.
4. Utwórz nową funkcję lokalną o nazwie onTouch:
local bridgePart = script.Parent
local function onTouch()
end
WSKAZÓWKA
Funkcje nazewnictwa używane ze zdarzeniami
Powszechnym wzorcem nazewnictwa funkcji wywoływanych ze zdarzeniami jest onBlank, gdzie Pusty jest nazwą zdarzenia. Kolejny sposób na ułatwienie czytania własnego kodu, gdy trzeba go zaktualizować po roku.
5. Połącz funkcję ze zdarzeniem Touched części. Gdy to zrobisz, możesz użyć print() do przetestowania swojego kodu:
local bridgePart = script.Parent
local function onTouch()
print("Touch event fired!")
end
bridgePart.Touched:Connect(onTouch)
6. Wewnątrz funkcji dodaj kod, który powinien zostać uruchomiony po uruchomieniu zdarzenia. Tutaj część stanie się przezroczysta, a za 0,5 sekundy każdy, kto stanie na moście, zostanie upuszczony. Jeśli dodałeś instrukcję drukowania w ostatnim kroku, śmiało ją usuń:
local bridgePart = script.Parent
local function onTouch()
bridgePart.Transparency = 0.5
wait(0.5)
bridgePart.CanCollide = false
end
bridgePart.Touched:Connect(onTouch)
WSKAZÓWKA
Korzystanie z wartości logicznych
CanCollide jest wartością logiczną. Kiedy to prawda, ten obiekt może wchodzić w interakcje z rzeczami na świecie. Kiedy fałsz, nie może. Tak więc w tym przypadku, gdy wartość jest fałszywa, most nie obsługuje już użytkownika.
Zrozumienie kolejności i rozmieszczenia
Podczas tworzenia zmiennych i funkcji należy pamiętać, że miejsce ich umieszczenia w skrypcie ma znaczenie. Skrypty uruchamiają kod wiersz po wierszu, zaczynając od góry i kierując się ku dołowi. Jeśli więc spróbujesz użyć zmiennej lub funkcji, zanim zostanie ona utworzona w skrypcie, napotkasz problemy .
Spójrz na właśnie ukończony skrypt BridgeScript:
local bridgePart = script.Parent
local function onTouch()
bridgePart.Transparency = 0.5
wait(0.5)
bridgePart.CanCollide = false
end
bridgePart.Touched:Connect(onTouch)
Jeśli przesuniesz pierwszą linię na dół, poprzednie wzmianki o zmiennej będą teraz zawierać błędy. W obu tych przykładach błędy są spowodowane tym, że skrypt próbuje wywołać coś, czego jeszcze nie ma. Teraz, gdy wiesz, że kolejność ma znaczenie, porozmawiajmy o zmiennych tworzonych wewnątrz funkcji. Zacznij od tego podstawowego zestawu trzech zmiennych: jednej przed funkcją, jednej w środku i jednej poniżej:
local above = "above"
local function scopePractice()
local inside = "inside"
end
local below = "below"
Na dole skryptu spróbuj wydrukować wszystkie trzy zmienne. Co się dzieje? inside wystąpi błąd,
mimo że został wcześniej przypisany. To dlatego, że zmienne lokalne wewnątrz a nie można uzyskać dostępu do funkcji z zewnątrz. Aby zrozumieć, dlaczego to nie działa, musisz zrozumieć, że skrypt to seria zagnieżdżonych bloków kodu. Za każdym razem, gdy tworzysz nową funkcję, tworzysz nowy blok. Rysunek ilustruje, w jaki sposób te bloki mogą się nakładać. Pierwszy blok, blok A, to sam skrypt. Wewnątrz znajduje się funkcja, pokazana jako blok B.
W ramach funkcji może znajdować się więcej bloków utworzonych przez instrukcje warunkowe i inne rzeczy, o których dowiesz się za kilka godzin. Każdy blok może uzyskiwać dostęp do lokalnych zmiennych/funkcji w swoim bloku nadrzędnym, ale nie do tych w blokach podrzędnych:
• Blok B może uzyskać dostęp do zmiennej lokalnej w bloku A.
• Blok C może uzyskać dostęp do lokalnych funkcji/zmiennych w blokach A i B.
• Blok A nie ma dostępu do lokalnych funkcji/zmiennych w blokach B lub C.
• Blok B nie może uzyskać dostępu do zmiennej lokalnej w bloku C.
Reaktywuj most
Jedną z rzeczy, o których należy pamiętać w przypadku Roblox, jest to, że znajdują się one na serwerach na żywo i są z natury przeznaczone dla wielu użytkowników, co oznacza, że wiele osób może jednocześnie przebywać na tym samym serwerze. Z tego powodu nie chcesz, aby zepsuty most, taki jak ten na rysunku ,
na stałe pojawiał się w twoich doświadczeniach po przejściu przez niego gracza. Pamiętaj o tym, czego dowiedziałeś się o zakresie i utwórz drugą funkcję, która ponownie aktywuje most.
1. W tym samym skrypcie, którego użyłeś we wcześniejszej części Wypróbuj sam, utwórz nową funkcję o nazwie aktywujBridge() nad funkcją onTouch:
local bridgePart = script.Parent
local function activateBridge()
end
local function onTouch()
bridgePart.Transparency = 0.5
wait(0.5)
bridgePart.CanCollide = false
end
bridgePart.Touched:Connect(onTouch)
2. W activeBridge() odwróć zmiany w CanCollide i Transparency:
local function activateBridge()
bridgePart.Transparency = 0
bridgePart.CanCollide = true
end
3. Wewnątrz funkcji onTouch() po krótkim czasie wywołaj funkcję activeBridge():
local bridgePart = script.Parent
local function activateBridge()
bridgePart.Transparency = 0
bridgePart.CanCollide = true
end
local function onTouch()
bridgePart.Transparency = 0.5
wait(0.5)
bridgePart.CanCollide = false
wait(3.0)
activateBridge()
end
bridge Part.Touched:Connect(onTouch)
WSKAZÓWKA
Zwróć uwagę na kolejność swoich funkcji
ac tivateBridge() musiało znajdować się przed onTouch(), aby zachować zasięg.
Streszczenie
Funkcje to fragmenty kodu wielokrotnego użytku, których można używać wielokrotnie. Po zdefiniowaniu można je wywołać, wpisując po prostu functionName(). Lub alternatywnie, jeśli nie wiesz dokładnie, kiedy funkcja będzie potrzebna, możesz połączyć je ze zdarzeniem. W ten sposób funkcja będzie wywoływana za każdym razem, gdy zdarzenie zostanie wywołane. Podczas tworzenia skryptu ważne jest, aby pamiętać, do jakich informacji ma dostęp fragment kodu. Zmienne i funkcje muszą znajdować się w zakresie używanego fragmentu kodu. Fragmenty kodu mogą uzyskiwać dostęp do informacji we własnym fragmencie oraz w fragmencie nadrzędnym. Próba uzyskania dostępu do informacji, które są poza zakresem, powoduje błędy w skrypcie. Dobrym sposobem na ćwiczenie zakresu jest wzięcie działającego fragmentu kodu, takiego jak skrypt pomostowy, i poruszanie się po funkcjach i zmiennych, aby zobaczyć, kiedy coś się psuje.
Pytania i odpowiedzi
P. Czy możesz utworzyć zmienną bez przypisywania jej wartości, jeśli jeszcze jej nie znasz?
O. Tak, zmienną można utworzyć wcześniej i później przypisać jej wartość.
P. Czy skrypt może mieć więcej niż jedną funkcję?
O. Tak, dość często będziesz mieć wiele funkcji utworzonych w skrypcie.
P. Dlaczego nie uczynić wszystkiego globalnym i nie martwić się o zakres?
O. Oprócz tego, że zmienne globalne działają wolniej niż zmienne lokalne, jest dużo czasu, który będziesz musiał poświęcić na utworzenie wielu funkcji w tym samym skrypcie. Wszystkie te funkcje będą wymagały własnych zmiennych. Jeśli nie ustawisz swoich zmiennych jako lokalnych, bardzo łatwo jest przypadkowo nadpisać zmienną, gdy zamierzasz utworzyć nową.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Jak inaczej można nazwać polecenie uruchomienia funkcji?
2. Jaka funkcja jest używana do uruchamiania funkcji po uruchomieniu zdarzenia?
3. Dostęp do zdarzeń obiektu uzyskuje się za pomocą ____________.
4. Jakie są dwa powody używania zmiennych lokalnych zamiast zmiennych globalnych?
5. Prawda czy fałsz: jeśli zmienna lokalna znajduje się wewnątrz funkcji, dostęp do niej mają wszystkie funkcje w dalszej części skryptu.
6. Jaki symbol służy do uruchomienia funkcji związanej z obiektem? Podpowiedź, pomyśl o tym, jak użyto Connect() i Destroy().
Odpowiedzi
1. Call
2. connect()
3. Notacja kropkowa
4. Zmienne lokalne działają szybciej i zapobiegają przypadkowemu nadpisaniu wartości w przypadku zduplikowanych nazw.
5. Fałsz. Zmienne są dostępne tylko w ich własnych blokach kodu i blokach kodu potomnego.
6. Dwukropek jest używany do wywołania funkcji powiązanej z obiektem - na przykład part:Destroy().
Ćwiczenia
Zamiast zapadać się mostu po dotknięciu, spróbuj stworzyć element mostu lub toru, który zestala się, gdy gracz dotknie przycisku, a następnie zresetuje się. Utwórz jedną funkcję, która aktywuje mostek po dotknięciu, i drugą funkcję, która go dezaktywuje
Porady
• Łatwiej będzie użyć pojedynczej części mostka niż wielu części.
• Umieść skrypt w przycisku, aby skorzystać z wydarzenia Touched.
• Użyj funkcji wait(), aby kontrolować, jak długo most jest aktywny.
• Zmień kolor przycisku na zielony, gdy most jest aktywny.
• Upewnij się, że fragment mostu jest wyłączony, aby skrypt mógł go włączyć.
Praca z parametrami i argumentami
Funkcje mogą nie tylko wykonywać zadania; mogą działać jak małe maszyny fabryczne, które przyjmują rzeczy, przekształcają je, a następnie zwracają wyniki. Ta część dotyczy informacji zawartych w nawiasach - parametrów i argumentów - oraz tego, co funkcja może zrobić z tymi informacjami.
Udostępnianie informacji do wykorzystania przez funkcje
Funkcje nie muszą być samodzielnymi fragmentami kodu; mogą faktycznie pobierać informacje z zewnątrz, aby je wykorzystać. Widzieliście już, jak to się robi z print("Hello"), które pobiera komunikaty do wyświetlenia, i wait(3), które zajmuje kilka sekund, aby wstrzymać skrypt. Wartości, które są przekazywane do funkcji przez nawiasy, nazywane są argumentami. Podczas tworzenia własnych funkcji, do których będą przekazywane informacje, należy utworzyć symbole zastępcze dla argumentów. Te symbole zastępcze są nazywane parametrami. Aby utworzyć własne parametry, podczas definiowania funkcji dodaj nazwę zmiennej w nawiasach, na przykład:
local function functionName(parameterName)
end
Parametr może być następnie używany w funkcji, tak jak każda inna zmienna.
Utwórz funkcję malowania
Strona budynku na rysunku
zostanie zmieniona przez utworzenie nowej funkcji o nazwie paint(), która będzie miała parametr określający kolor ściany, którą należy przemalować. Zamiast korzystać z takiego budynku, możesz poćwiczyć ze zwykłą starą częścią lub modelem bez tekstury.
1. Wewnątrz części lub modelu dodaj nowy skrypt o nazwie Paint.
2. Utwórz nową zmienną lokalną przypisaną do części macierzystej, a następnie nową funkcję lokalną named paint():
local wall = script.Parent
local function paint()
end
3. Utwórz parametr o nazwie paintColor, który będzie pełnił rolę symbolu zastępczego dla wybranego koloru any pomalować ścianę:
local wall = script.Parent
local function paint(paintColor)
end
4. Wewnątrz funkcji ustaw kolor ściany na symbol zastępczy paintColor:
local wall = script.Parent
local function paint(paintColor)
wall.Color = paintColor
end
5. Dodaj zmienną lub dwie z różnymi kolorami RGB, którymi możesz chcieć pomalować ścianę:
local wall = script.Parent
local blue = Color3.fromRGB(29, 121, 160)
local yellow = Color3.fromRGB(219, 223, 128)
local function paint(paintColor)
wall.Color = paintColor
end
6. Wywołaj funkcję paint i podaj jedną ze zmiennych koloru:
local wall = script.Parent
local blue = Color3.fromRGB(29, 121, 160)
local yellow = Color3.fromRGB(219, 223, 128)
local function paint(paintColor)
wall.Color = paintColor
end
paint(blue)
Przetestuj to, a jakakolwiek część, którą pomalowałeś, będzie miała wybrany kolor!
WSKAZÓWKA
Zmienne rozmieszczenie
Być może już zauważyłeś, ale zmienne zwykle znajdują się na górze skryptu lub fragmentu kodu, do którego należą.
Praca z wieloma parametrami i argumentami
Poprzednia wersja "Spróbuj sam" pozwalała ci podać kolory, którymi chciałeś malować, ale obiekt do malowania był zakodowany na stałe. Innymi słowy, kod działałby tylko z tym konkretnym obiektem. Twarde kodowanie naprawdę ogranicza korzystanie z tej funkcji, chyba że planujesz często zmieniać kolor tej jednej ściany. Na szczęście do funkcji można przekazać więcej niż jeden argument. Wszystko, co musisz zrobić, to utworzyć więcej niż jeden parametr. Podczas definiowania funkcji wiele nazw parametrów można oddzielić przecinkami, jak pokazano tutaj:
local function functionName(firstParameter,secondParameter)
print(firstParameter .." and ".. secondParameter)
end
WSKAZÓWKA
Ile to za dużo?
Nie ma technicznych ograniczeń co do liczby parametrów, które możesz mieć, ale większość ludzi zgadza się, że nie więcej niż trzy to dobra praktyczna zasada.
Argumenty, które są przekazywane, zawsze wypełniają parametry w kolejności. Pierwszy argument zawsze przechodzi przez pierwszy parametr, a drugi argument zawsze przechodzi przez drugi parametr:
local first = "first"
local second = "second"
local function practice(firstParameter,secondParameter)
print(firstParameter .. " and " .. secondParameter)
end
practice(first, second) -- Prints "first and second"
practice(second, first) -- Prints "seco nd and first"
Podaj jaki kolor i jaki przedmiot
Spraw, aby funkcja malowania była bardziej użyteczna, tworząc zmienną, która przyjmuje zarówno obiekt do malowania, jak i kolor do malowania. Rysunek
pokazuje samochód i budynek obecnie pomalowane na biało, co jest niesamowicie nudne i nie pasuje do sceny. Weź ten sam kod malarza co poprzednio, ale utwórz go tak, aby można było przekazać obiekt, który chcesz pomalować. W ten sposób kod może być użyty zarówno na budynku, jak i na samochodzie:
1. W ServerScriptService utwórz nowy skrypt.
2. Przypisz zmienne do dwóch różnych kolorów i dwóch różnych obiektów:
-- Available colors
local red = Color3.fromRGB(170, 0, 0)
local olive = Color3.fromRGB(151, 15, 156)
-- Objects to paint
local car = workspace.Car
local resta urant = workspace.Buildings.Restaurant
WSKAZÓWKA
Znajdowanie osadzonych obiektów
Zwróć uwagę, że w przypadku drugiego obiektu w przykładzie restauracja znajdowała się w folderze, więc zastosowano notację kropkową, aby przejść o jeden stopień w dół hierarchii.
3. Utwórz funkcję, która ma parametr do pobrania obiektu do pomalowania i kolor do pomalowania:
-- Paints objects
local function painter(objectToPaint, paintColor)
objectToPaint.Color = paintColor
end
4. Wywołaj funkcję i podaj w niej, który obiekt ma zostać pomalowany i na jaki kolor:
-- Available colors
local red = Color3.fromRGB(170, 0, 0)
local olive = Color3.fromRGB(151, 15, 156)
-- Objects to paint
local car = workspace.Car
local restaurant = workspace.Buildings.Restaurant
-- Paints objects
local function painter(objectToPaint, paintColor)
objectToPaint.Color = paintColor
end
painter(restaurant, olive)
painter(car, red)
5. Przetestuj swój kod. Zamiast Graj, z rozwijanego menu wybierz Uruchom ,
jeśli chcesz zobaczyć zmiany w świecie bez grania w to doświadczenie. Rysunek przedstawia gotowy samochód i budynek, które nie są już zaskakująco białe.
Zwracanie wartości z funkcji
Wartości można nie tylko przekazywać do funkcji, ale także przekazywać z powrotem. Klasycznym przykładem jest kalkulator, taki sam jak ten w telefonie. Wartości są wprowadzane, a wynik jest zwracany. W poniższym przykładzie kodu funkcja jest przypisana do zmiennej. Kiedy zmienna jest używana, funkcja jest uruchamiana, a wynik jest odsyłany za pomocą słowa kluczowego return:
-- Adds any two numbers together
local function add(firstNumber, secondNumber)
local sum = firstNumber + secondNumber
return sum -- Sends sum back to where the function was called
end
-- Some numbers to use
local rent = 3500
local electricity = 128
-- Use add() to add rent and electricity and return the result
local costOfLiving = add(rent, electricity)
print("Rent in New York is " .. costOfLiving)
Zwracanie wielu wartości
Czasami możesz chcieć zwrócić wiele wartości z funkcji. Przykładem może być zwrócenie liczby wygranych, przegranych i remisów użytkownika. Aby zwrócić wiele wartości, użyj funkcji return jak zwykle i oddziel wartości przecinkami.
Zwróć wygrane, przegrane i remisy graczy
Postępuj zgodnie z instrukcjami, aby utworzyć niestandardową funkcję, która po wywołaniu zwraca wygrane, przegrane i remisy gracza. Przypisz zwrócone wartości do zmiennej:
1. Utwórz niestandardową funkcję ze zmiennymi dla wygranych, przegranych i remisów.
2. Wpisz return, a następnie żądaną zmienną. Użyj przecinka, aby je rozdzielić:
local function getWinRate()
local wins = 4
local losses = 0
local ties = 1
return wins, losses, ties
end
3. Zamiast tworzyć zmienne dla każdej odebranej wartości w oddzielnych wierszach, utwórz je w tym samym wierszu, jak w poniższym przykładzie. Zostaną one wypełnione w kolejności zwróconymi wartościami:
local function getWinRate()
local wins = 4
local losses = 0
local ties = 1
return wins, losses, ties
end
local userWins, userLosses, userTies = getWinRate()
4. Wydrukuj zmienne, aby zobaczyć wyniki.
local function getWinRate()
local wins = 4
local losses = 0
local ties = 1
return wins, losses, ties
end
local userWins, userLosses, userTies = getWinRate()
print("Your win s, losses, and ties are: " .. userWins .. " , " .. userLosses .. " , " .. userTies)
Zwracanie Nil
Zero oznacza, że czegoś nie można znaleźć lub nie istnieje. Jeśli zamiast oczekiwanego wyniku zostanie wyświetlony komunikat nil, wykonaj następujące czynności:
* Sprawdź, czy liczba otrzymanych wartości jest taka sama, jak zwrócona.
* Sprawdź, czy zwracane i odbierane wartości są oddzielone przecinkami.
* Potwierdź, że nic innego nie jest nie tak z funkcją.
Zwracanie czegoś, co nie istnieje
Jeśli spróbujesz użyć zmiennej lub funkcji, która nie istnieje, zobaczysz w danych wyjściowych słowo kluczowe nil oraz miejsce wystąpienia błędu.
1. W dowolnym skrypcie przekaż fałszywą nazwę zmiennej, np. DoestExist, do funkcji print().
2. Uruchom kod i sprawdź dane wyjściowe. Powinieneś zobaczyć zero obok nazwy skryptu i numeru wiersza, w którym nie można znaleźć zmiennej, na przykład błąd pokazany na rysunku
.
Radzenie sobie z niedopasowanymi argumentami i parametrami
Ważne jest, aby mieć świadomość, co się stanie, jeśli do funkcji zostanie przekazana lub zwrócona niewłaściwa liczba wartości. Podanie błędnej liczby może spowodować błąd i zawieszenie kodu. Jeśli do funkcji zostaną przekazane niewystarczające argumenty, wystąpi błąd, gdy funkcja osiągnie wartość zero:
local function whoWon(first, second)
print("First place is " .. first .. "Second place is ")
end
whoWon("AngelicaIsTheBest") -- wystąpi błąd, ponieważ nie ma drugiej wartości
Jeśli z powrotem zostanie przekazanych więcej wartości niż dostępnych zmiennych, wartości wypełnią się w kolejności, a wszelkie pozostałe zmienne zostaną usunięte i utracone. W tym przykładzie przekazywane są trzy wartości, ale są tylko spacje dla dwóch:
local a = "Apple"
local b = "Banana"
local c = "Carrot"
return a, b, c
end
local a, b = giveBack() -- c is lost
print(a, b, c) -- Will print Apple, Banana, nil
"Carrot" istnieje tylko w zakresie funkcji i nigdy nie jest zwracana, więc dla trzeciej wartości wypisywana jest nil.
Praca z funkcjami anonimowymi
Funkcje anonimowe są, jak sama nazwa wskazuje, funkcjami. To, co czyni je wyjątkowymi, to to, że kiedy są po raz pierwszy zdefiniowane, pozostają bez nazwy. Oznacza to, że można je zdefiniować w tym samym miejscu, w którym zostały wywołane. Porównaj następujące dwa przykłady kodu dla naszej znanej prostej pułapki połączonej ze zdarzeniem Touched. Zdarzenie Touched zwraca nazwę części wyzwalającej, która następnie jest niszczona. Po pierwsze, oto skrypt, w którym tworzona jest nazwana funkcja, a następnie wywoływana za każdym razem, gdy uruchamiane jest zdarzenie Touched:
Przykład nazwanej funkcji
local trap = script.Parent
local function onTouch(otherPart)
otherPart:Destroy()
end
trap.Touched:Connect(onTouch)
Oto kod, który robi to samo, ale funkcja jest tworzona w tym samym miejscu, w którym jest wywoływana:
Przykład funkcji anonimowej
local part = script.Parent
part.Touched:Connect(function(otherPart)otherPart:Destroy() end)
Jeśli miałbyś uruchomić oba fragmenty kodu, robią dokładnie to samo: niszczą wszystko, co dotyka rodzica skryptu. Dlaczego więc nie miałbyś użyć anonimowej funkcji? W tabeli przedstawiono niektóre zalety i wady funkcji nienazwanych.
Za I przeciw
Szybsze pisanie. : Trudniejsze do odczytania.
Może być używany z funkcjami, które inaczej nie zwracają wartości. : Trudniej aktualizować i ponownie używać. Nie można ich wezwać z innego miejsca, ponieważ nie mają imienia, którym można by ich nazwać.
WSKAZÓWKA
Nazwane funkcje ułatwiają współpracę
Roblox Lua Style Guide odradza korzystanie z funkcji anonimowych, gdy nie jest to konieczne, ponieważ większość projektów będzie miała wielu programistów, a funkcje anonimowe znacznie utrudniają odczytanie i aktualizację kodu.
Streszczenie
Funkcje mogą być używane i ponownie wykorzystywane na wiele różnych sposobów. Można ich użyć do stworzenia czegoś, na przykład NPC w Godzinie 2. Można ich użyć do wprowadzenia zmian w obiekcie poprzez aktualizację właściwości lub nawet całkowite ich zniszczenie, jak w przypadku części pułapki. W tym celu mogą pobierać wartości spoza funkcji, przekazując te wartości przez parametry. Rzeczywiste fragmenty informacji, które są przekazywane przez parametry, nazywane są argumentami. Kiedy funkcja zakończy swoją pracę, informacja może zostać przekazana z powrotem i wykorzystana przez skrypt. Klasycznym przykładem zwracanych informacji jest kalkulator w telefonie. Jeśli użyjesz kalkulatora do dodania dwóch liczb, przekaże on odpowiedź. Innym przykładem, który widzieliśmy, jest to, że za każdym razem, gdy uruchamiane jest zdarzenie Touched, Touched przekazuje z powrotem nazwę obiektu, który spowodował jego uruchomienie. Czasami, jeśli nie ma nic do zwrócenia, możesz użyć anonimowej funkcji i utworzyć ją w tym samym miejscu, w którym została wywołana. Może to być wygodne, ale znacznie utrudnia czytanie kodu, co prawdopodobnie oznacza spowolnienie pracy członków zespołu pracujących nad skryptem. Może nawet sprawić, że będzie to dla Ciebie trudniejsze, jeśli chcesz później wprowadzić aktualizacje kodu.
Pytania i odpowiedzi
P. Czy istnieje maksymalna liczba parametrów, jakie może mieć funkcja?
O. Nie ma ścisłego maksimum, ale przez większość czasu będziesz chciał ograniczyć go do zaledwie trzech. Im więcej masz parametrów, tym trudniej zapamiętać, do czego służy każdy z nich i łatwiej pomylić kolejność.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Przekazywanie informacji z zewnątrz funkcji do wewnątrz nazywa się ______.
2. Jakie słowo kluczowe umożliwia przekazanie wartości po wykonaniu funkcji?
3. Symbole zastępcze dla wartości, które będą później używane przez funkcję, nazywane są ________.
4. Rzeczywiste wartości używane przez funkcje nazywane są ______.
5. Słowem kluczowym używanym, gdy wartości nie można znaleźć lub nie istnieją, jest ___.
Odpowiedzi
1. Przejście
2. return
3. Parametry
4. Argumenty
5. 0bnil
Ćwiczenia
Wszyscy programiści czasami badają, jak inni ludzie coś stworzyli. Jednak kod znaleziony w Internecie może nie działać dokładnie tak, jak chcesz, lub być sformatowany w sposób ułatwiający czytanie członkom zespołu. Ważne jest, aby poświęcić czas na zbadanie pożyczonego kodu i wprowadzenie ulepszeń tam, gdzie to możliwe. W tym ćwiczeniu przećwicz, biorąc anonimową funkcję i próbując sformatować ją jako funkcję nazwaną:
script.Parent.Touched:Connect(function(otherPart) local fire = Instance.new"Fire"
fire.Parent = otherPart end)
Struktury warunkowe
Czy kiedykolwiek powiedziałeś komuś, że coś zrobisz - pod jednym warunkiem? Na przykład pomożesz im przenieść się do nowego miejsca, ale oni muszą pomóc ci w nauce do matury. To jest struktura warunkowa. Zrobisz coś, jeśli wydarzy się coś innego. To samo może się zdarzyć w skryptach. Możesz skonfigurować kod tak, aby działał tylko wtedy, gdy coś innego jest prawdą. Rysunek przedstawia schemat działania struktury warunkowej.
Instrukcje if/then
Najpowszechniejszą strukturą warunkową jest prawdopodobnie instrukcja if/then. Jeśli coś jest prawdą, kod coś zrobi.
Oto kilka przykładów:
• Jeśli klucz zostanie znaleziony, można zbadać nowy obszar.
• Jeśli zadanie zostanie zakończone, użytkownik otrzyma darmowego zwierzaka.
• Jeśli ktoś powie wszystkiego najlepszego na czacie, zrób serię balonów na ekranie. W kodzie wygląda to tak:
if somethingIsTrue then
-- Do something
print("It's true!")
end
Jeśli pierwszy wiersz jest prawdziwy, zostanie uruchomione polecenie print w kodzie z wcięciem. Instrukcje warunkowe mogą używać operatorów do oceny, czy coś jest prawdziwe. Operatory to symbole, które dają wskazówki, jak coś ocenić. Tabela przedstawia niektóre z najczęstszych operatorów
Operator : Opis : Przykłady bycia prawdziwym
== :Jest równe : Jeżeli 3 == 3 to wtedy
+ : Dodawanie : Jeśli 3 + 3 == 6 : wtedy
- : Odejmowanie : Jeśli 3 - 3 == 0 to
* : Mnożenie : Jeśli 3 * 3 == 9 to
Zwróć szczególną uwagę na podwójny znak równości używany jako operator w porównaniu z pojedynczym znakiem równości używanym do przypisania wartości do zmiennej. Podwójny znak równości == służy do sprawdzania, czy coś jest równe.
Poniższe wartości są prawdziwe i kod zostanie uruchomiony:
local health = 10
if health == 10 then
print("You're at full health")
end
Poniższy kod zostanie oceniony jako fałszywy, a kod nie zostanie uruchomiony:
local health = 5
if health == 10 then
print("You're at full health")
end
Co jeśli gracz ma tymczasowe dodatkowe zdrowie? Możesz to sprawdzić za pomocą operatora dla większego niż lub równego (>=):
local health = 12
if health >= 10 then
print("You're at full health")
end
Można również sprawdzić obecność wartości, jeśli nie jest używany żaden operator. Poniższy fragment kodu sprawdza, czy dach się pali:
local roof = script.Parent
local fire = roof:FindFirstChildWhichIsA("Fire")
if fire then -- Checks if fire is not nil
print("The roof is on fire!")
fire:Destroy()
end
Używa FindFirstChildWhichIsA() do sprawdzenia, czy któryś z obiektów podrzędnych dachu jest obiektem Fire. FindFirstChildWhichIsA() pobiera tylko pierwszy znaleziony obiekt pasujący do wyszukiwania.
Przedstawiamy humanoidy
Lawa w Części 1 miała poważną wadę: niszczyła tylko to, co jej bezpośrednio dotknęło, co oznacza, że użytkownicy mogliby biegać bez nóg i rąk, gdyby tylko otarli się o pułapkę, jak na rysunku
Aby całkowicie zresetować użytkownika, musisz znaleźć obiekt kontrolujący zdrowie użytkownika. W Roblox domyślnie jest to obiekt Humanoid. Jeśli użyjesz Humanoida, aby ustawić zdrowie użytkownika na 0, będzie on zmuszony do odrodzenia się - nogi, stopy, ręce i wszystko inne.
1. Utwórz część i wstaw nowy skrypt. Możesz użyć swojej lawy z części 1, o ile usuniesz stary skrypt.
2. Utwórz zmienną przypisaną do samej części pułapki.
3. Uruchom funkcję o nazwie onTouch z parametrem dla otherPart.
4. Wewnątrz funkcji utwórz zmienną o nazwie znak, aby znaleźć rodzica otherPart, jeśli go posiada:
local trap = script.Parent
local function onTouch(otherPart)
local character = otherPart.Parent
local humanoid = character:FindFirstChildWhichIsA("Humanoid")
end
5. Kolejnym krokiem jest sprawdzenie, czy postać posiada Humanoida. Jeśli tak, najprawdopodobniej jest to użytkownik lub NPC:
local trap = script.Parent
local function onTouch(otherPart)
local character = otherPart.Parent
local humanoid = character:FindFirstChildWhichIsA("Humanoid")
if humanoid then
end
end
6. Ustaw zdrowie użytkownika na 0:
local trap = script.Parent
local function onTouch(otherPart)
local character = otherPart.Parent
local humanoid = character:FindFirstChildWhichIsA("Humanoid")
if humanoid then
humanoid.Health = 0
end
end
7. Połącz onTouch ze zdarzeniem Touched w pułapce:
local trap = script.Parent
local function onTouch(otherPart)
local character = otherPart.Parent
local humanoid = character:FindFirstChildWhichIsA("Humanoid")
if humanoid then
humanoid.Health = 0
end
end
trap.Touched:Connect(onTouch)
elseif
OK, ale co, jeśli chcesz, aby kod sprawdzał więcej niż jeden scenariusz? Na przykład chcesz, aby kod wykonywał jedną rzecz, jeśli zdrowie użytkownika jest pełne, a inną, jeśli zdrowie użytkownika nie jest pełne. W tych scenariuszach dodaj drugi tryb warunkowy wywołany ze słowem kluczowym elseif:
local health = 5
if health >= 10 then
print ("You're at full health")
elseif health < 10 then -- Check if health is less than 10
print("Find something to eat to regain health!")
end
elseif jest nadal częścią tego samego bloku kodu, co if/then. Nie ma własnego końca.
Operatory logiczne
Kilku operatorów specjalnych nie jest symbolami; zamiast tego operatorami logicznymi są słowa i, lub, i nie. i i/lub umożliwiają jednoczesne sprawdzenie wielu warunków. nie pozwala upewnić się, że coś nie jest czymś innym. Tabela wyjaśnia, w jaki sposób oceniane są te operatory.
Operator: Opis
and : Ocenia jako prawdziwe tylko wtedy, gdy oba warunki są prawdziwe.
or : Ocenia jako prawdziwy, jeśli którykolwiek z warunków jest prawdziwy.
not : Ocenia jako przeciwieństwo warunku.
Operatorzy ci uznają zarówno false, jak i nil za "fałsz", a wszystko inne za "prawdę". W poniższym fragmencie kodu i służy do sprawdzania zakresu, a nie pojedynczej wartości. W tym scenariuszu wyobrażamy sobie, że użytkownik ma maksymalnie 10 punktów zdrowia i przy 0 punktach zdrowia odradza się. Więc użytkownik musi jeść tylko wtedy, gdy nie jest w pełni zdrowy:
local health = 1
ssssssssss
if health >= 10 then
print ("You're at full health")
elseif health >= 1 and health < 10 then -- Check if health is in a specific range
print("Find something to eat to regain health!")
end
W razie potrzeby możesz nadal tworzyć kod dla określonych scenariuszy za pomocą dodatkowych instrukcji elseif:
local health = 1
if health >= 10 then
print ("You're at full health") -- Runs if health is 10 or higher
elseif health >= 5 and health < 10 then -- Runs if health is 5 - 9
print("Find something to eat to regain health!")
elseif health >= 1 and health <= 4 then -- Runs if health is 1 - 4
print("You are very hungry, better eat soon!")
end
else
Wreszcie, zawsze mądrze jest powiedzieć scenariuszowi, co ma zrobić, jeśli nie zostaną spełnione żadne inne warunki. Użyj słowa kluczowego else, aby zaznaczyć, co należy zrobić, jeśli nie są spełnione żadne inne warunki:
local health = 0
if health >= 10 then
print ("You're at full health") -- Runs if health is 10 or higher
elseif health >= 5 and health < 10 then -- Runs if health is 5 - 9
print("Find something to eat to regain health!")
elseif health >= 1 and health <= 4 then -- Runs if health is 1 - 4
print("You are very hungry, better eat soon!")
else -- Runs if none of the conditions have been true.
print("You ran out of food, you'll need to restart")
end
Ponownie, else nie jest własnym blokiem kodu; cały okres warunkowy powinien używać słowa kluczowego end tylko raz.
Utwórz portal z atrybutami i usługami
Poćwicz używanie instrukcji if/then i elseif, tworząc portal, który pozwoli graczom przejść do tuneli po drugiej stronie tylko wtedy, gdy aktywują w pobliżu specjalny kamień kluczowy. Aby utworzyć portal, zapoznaj się z usługą ProximityPromptService i atrybutami niestandardowymi. Najpierw musisz skonfigurować portal i kamień kluczowy, używając części lub modeli. Na rysnku
wykorzystano model łuku portalu, ale sam portal to tylko czarna część działająca jako bariera. Łuk jest tylko na pokaz. Zarówno portal, jak i kamień kluczowy będą wymagały nowego atrybutu, aby skrypt działał. Atrybuty to właściwości niestandardowe, którym można nadać nazwę i ustawić typ wartości. Dla każdej części zostanie utworzony atrybut o nazwie Aktywowany, aby śledzić, czy klucz został znaleziony i czy można użyć portalu:
1. Skonfiguruj części lub siatki o nazwach Portal i KeyStone.
2. Wybierz Portal i wstaw ProximityPrompt.
ProximityPrompts umożliwiają użytkownikom klikanie i interakcję z częściami, a nie tylko bieganie i dotykanie ich.
3. Gdy Portal jest nadal wybrany, przewiń do końca we Właściwościach i kliknij Dodaj atrybut .
4. Nadaj atrybutowi nazwę Activated, ustaw typ na boolean, a następnie kliknij przycisk Save
5. Wybierz KeyStone, utwórz kolejny nowy atrybut o nazwie Activated i ustaw typ na boolowski.
OSTRZEŻENIE
Pozostaw nowe atrybuty niezaznaczone
Upewnij się, że oba nowe atrybuty są wyłączone, ponieważ nie są zaznaczone. Włączone/zaznaczone atrybuty i właściwości są prawdziwe, natomiast wyłączone/niezaznaczone są fałszywe. Następnie utwórz dwa skrypty: jeden dla klucza i jeden dla portalu.
Pobieranie atrybutów w skrypcie KeyStone
Atrybut KeyStone, Activated, powinien obecnie mieć wartość false. Dopóki klucz nie jest aktywowany, portal nie przepuszcza ludzi. Skrypt KeyStone służy do włączania klawisza po dotknięciu i ustawiania Activated na true:
1. Wybierz KeyStone i wstaw nowy skrypt.
2. Utwórz zmienną, która odwołuje się do rodzica skryptu i funkcji o nazwie onTouch połączonej z zdarzeniem dotknięcia KeyStone. Uwzględnij parametr dotykającej części.
3. Sprawdź, czy dana osoba nie dotknęła części, szukając humanoida. Nie chcesz, aby kod został uruchomiony przez dotknięcie płytki bazowej lub coś podobnego. Zapoznaj się z kodem wcześniej w ciągu godziny, jeśli nie pamiętasz, jak to zrobić.
4. Wewnątrz funkcji użyj SetAttribute(), aby przekazać Activated i zmień wartość na true, jak pokazano:
local keyStone = script.Parent
local function onTouch(otherPart)
local character = otherPart.Parent
local humanoid = character:FindFirstChildWhichIsA("Humanoid")
if humanoid then
keyStone:SetAttribute("Activated", true)
end
end
keyStone.Touched:Connect(onTouch)
5. Zmień materiał KeyStone na Neon, aby pokazać użytkownikowi, że KeyStone został aktywowany:
local keyStone = script.Parent
local function onTouch(otherPart)
local character = otherPart.Parent
local humanoid = character:FindFirstChildWhichIsA("Humanoid")
if humanoid then
keyStone:SetAttribute("Activated", true)
keyStone.Material = Enum.Material.Neon
end
end
keyStone.Touched:Connect(onTouch)
WSKAZÓWKA
Alternatywy dla części teksturowanych
Jeśli część ma teksturę, zmiany materiałów i kolorów nie będą widoczne. Możesz usunąć teksturę, aby te właściwości pokazywały lub włączały cząsteczkę po aktywacji. Ważną rzeczą jest tutaj, aby zawsze pokazywać użytkownikom, kiedy wchodzą w interakcję z częścią.
6. Przetestuj i upewnij się, że część zmieni kolor na neonowy, jak pokazano na rysunku
Skrypt portalu
Powrót do samego portalu: gdy użytkownik podchodzi do portalu, ProximityPrompt wyświetla komunikat informujący, że można wejść w interakcję z barierą, jak pokazano na rysunku
ProximityPrompts mają wiele powiązanych funkcji, których można użyć, ale nie są one automatycznie dołączane do funkcji normalnie dostępnych w skrypcie. Możemy udostępnić te funkcje, dodając do skryptu usługę ProximityPromptService. Usługi to opcjonalne zestawy kodu, które udostępniają dodatkowe funkcje do użycia w skrypcie. Te zestawy kodów można udostępnić do użytku, przypisując je do zmiennej za pomocą funkcji GetService(). Oto przykład:
local ProximityPromptService= game:GetService("ProximityPromptService")
WSKAZÓWKA
Używanie dwukropków z metodami
Dla przypomnienia, podczas uzyskiwania dostępu do metod - czyli funkcji powiązanych z obiektem - używasz dwukropków. Tutaj GetService() jest powiązany z obiektem najwyższego poziomu, grą:
1. Wybierz Portal i wstaw nowy skrypt.
2. Utwórz zmienną, aby uzyskać usługę ProximityPromptService.
3. Utwórz zmienne odnoszące się do Portalu, KeyStone i ProximityPrompt.
4. Utwórz nową funkcję o nazwie onPromptTriggered:
local ProximityPromptService = game:GetService("ProximityPromptService")
local portal = script.Parent
local keyStone = workspace.KeyStone
local proximityPrompt = portal.ProximityPrompt
local function onPromptTriggered()
end
WSKAZÓWKA
Funkcje nazewnictwa
Być może zauważyłeś, że formuła on i nazwa zdarzenia to powszechny sposób nadawania nazw funkcjom.
5. Połącz funkcję ze zdarzeniem PromptTriggered, które jest dostarczane z usługą ProximityPromptService:
local function onPromptTriggered()
end
ProximityPromptService.PromptTriggered:Connect(onPromptTriggered)
6. Wewnątrz funkcji pobierz aktualną wartość atrybutu KeyStone, Activated:
local function onPromptTriggered()
local KeyActivated = keyStone:GetAttribute("Activated")
end
7. Jeśli KeyStone jest aktywowany, uczyń część przezroczystą i wyłącz CanCollide:
local function onPromptTriggered()
local KeyActivated = keyStone:GetAttribute("Activated")
if KeyActivated == true then
portal.Transparency = 0.8
portal.CanCollide = false
print("Come on through")
end
end
8. W przeciwnym razie spraw, aby drzwi migały na czerwono:
local ProximityPromptService = game:GetService("ProximityPromptService")
local portal = script.Parent
local keyStone = workspace.KeyStone
local proximityPrompt = portal.ProximityPrompt
local originalColor = portal.Color
local function onPromptTriggered()
local KeyActivated = keyStone:GetAttribute("Activated")
if KeyActivated == true then
portal.Transparency = 0.8
portal.CanCollide = false
print("Come on through")
else
portal.Color = Color3.fromRGB(255, 0, 0)
wait(1)
portal.Color = originalColor
print("Activate the key stone to pass through the portal")
end
end
ProximityPromptService.PromptTriggered:Connect(onPromptTriggered)
WSKAZÓWKA
Uwzględnianie interakcji wielu graczy
Zmienna służy do uzyskania oryginalnego koloru portalu na górze skryptu zamiast w funkcji. W przeciwnym razie, jeśli spamujesz interakcję, funkcja może zostać uruchomiona, gdy portal jest nadal czerwony. Zmiennej zostanie wtedy przypisany kolor czerwony zamiast oryginalnego koloru. Skrypt jest gotowy! Przetestuj i upewnij się, że ludzie mogą korzystać z portalu. W oknie Właściwości możesz nawet dostosować tekst ProximityPrompt
oraz określić, jak daleko muszą znajdować się gracze.
Streszczenie
Używanie instrukcji warunkowych może naprawdę ożywić twój świat, umożliwiając ustawienie przyczynowo-skutkowych reakcji na świecie. Jeśli ludzie dotkną czegoś niebezpiecznego, tracą zdrowie. Jeśli dotkną czegoś innego, mogą otrzymać magiczną moc lub otworzyć nowe drzwi. Słowa kluczowe if/then, elseif i else umożliwiają utworzenie schematu blokowego dla tego, jaki kod powinien działać w jakich okolicznościach. Skrypt sprawdza każdy warunek, zaczynając od góry, a jeśli warunek jest prawdziwy, uruchamiany jest kod dla tej sekcji. Reszta kodu w instrukcji if/ then jest pomijana. Jeśli nic nie jest prawdą, można użyć else, aby powiedzieć, co powinien zrobić kod. Konfigurując te interakcje, zawsze myśl o ludziach, którzy będą doświadczać świata, który tworzysz. Dołącz wskazówki wizualne, takie jak zmiany kolorów lub efekty specjalne, aby upewnić się, że użytkownik rozumie, że obiekt działa zgodnie z przeznaczeniem.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania
Kartkówka
1. Podwójny znak równości (==) oznacza ____ _____.
2. Co jest nie tak z poniższym fragmentem kodu?
local health = 5
if health >= 10 then
print ("You're at full health")
elseif health < 10 then
print("Find something to eat to regain health!")
end
end
3. Udostępnij dodatkowe zestawy kodu w swoim skrypcie za pomocą ______.
4. Jaki operator oznacza mniej niż lub równo?
5. Operatory, które nie są symbolami, nazywane są ______.
6. Co robi operator?
Odpowiedzi
1. Jest równy
2. elseif nie powinien być własnym fragmentem kodu. Powinien znajdować się na tym samym poziomie wcięcia, co gdyby, i powinien być tylko jeden koniec.
3. GetService()
4. <=
5. Operatory logiczne
6. Ocenia jako prawdziwy, jeśli którykolwiek z warunków jest prawdziwy.
Ćwiczenia
Daj graczowi super moce, sprawiając, że poruszają się szybko, gdy dotkną przyspieszacza prędkości! Domyślna właściwość WalkSpeed Humanoida to 16. Nie jest tak źle, ale o wiele fajniej byłoby jechać dużo szybciej. Stwórz część, która tymczasowo pozwala użytkownikowi iść znacznie szybciej, a następnie przywraca pierwotną prędkość po kilku sekundach. Aby to zrobić, możesz użyć wzorca onTouch, z którym pracowałeś, oraz niektórych instrukcji if/then. Jako dodatkowy zwrot akcji, użyj obiektu ParticleEmitter, aby przesyłać strumieniowo iskierki za nimi, gdy są włączone
Odbijanie i debugowanie
Teraz, gdy już wiesz, czym jest humanoid i jak go sprawdzić za pomocą if, możesz zacząć tworzyć kod, który nie tylko niszczy rzeczy lub ustawia stan zdrowia użytkownika aż do zera. Zamiast tego możesz zacząć wprowadzać rzeczy w życie stopniowo - to znaczy tylko o określoną ilość na raz. Zamiast sprowadzać zdrowie użytkownika do zera, możesz sprawić, że spadnie ono tylko częściowo lub bogactwo użytkownika może wzrosnąć o jedno złoto za każdym razem, gdy wydobędzie kawałek rudy. Druga połowa tej godziny pokazuje metody sprawdzania i ulepszania istniejącego kodu. Użyjesz instrukcji łańcuchowych, aby sprawdzić, gdzie Twój kod mógł pójść źle, zobaczyć, jak skonfigurować systemy, które zapobiegają interakcjom poszczególnych użytkowników ze spamem, i dowiedzieć się, jak zacząć wprowadzać więcej procesu projektowania do swojego kodowania.
Nie niszcz, odrzucaj
Przyjrzyjmy się konfigurowaniu pułapki, która usuwa użytkownikowi 10 punktów zdrowia na raz. Domyślne maksymalne zdrowie humanoida to 100. Korzystając z tego, co wiesz, najłatwiejszym sposobem na ustawienie pułapki, która zabiera aktualne zdrowie gracza i odejmuje 10, może być następująca:
local trap = script.Parent
local function damageUser(otherPart)
local partParent = otherPart.Parent
local humanoid = partParent:FindFirstChildWhichIsA("Humanoid")
if humanoid then
humanoid.Health = humanoid.Health - 10
print("Ouch! Current health is " .. humanoid.Health)
end
end
trap.Touched:Connect(damageUser)
Problem polega na tym, że ze względu na sposób, w jaki silnik fizyki radzi sobie z kolizjami, kod wywoła wiele niemal równoczesnych zdarzeń i spowoduje więcej szkód, niż zamierzano. Na rysunku
widać ze znacznika czasu po lewej stronie, że obecny stan zdrowia tej osoby bardzo szybko się pogorszył. Nie chcemy, aby kod działał tak wiele razy tak szybko. Chcemy mieć pewność, że uruchomi się tylko raz i nie będzie działać ponownie, dopóki nie powiemy, że może. Zapewnienie, że akcja jest uruchamiana tylko raz, podczas gdy w przeciwnym razie byłaby uruchamiana wiele razy, jest znane jako odrzucanie. Oto poprzedni fragment kodu, ale z systemem debounce, który dezaktywuje pułapkę na określony czas:
local trap = script.Parent
local RESET_SECONDS = 1 -- How long the trap will be disabled
local enabled = true -- Needs to be true to damage user
local function damageUser(otherPart)
local partParent = otherPart.Parent
local humanoid = partParent:FindFirstChildWhichIsA("Humanoid")
if humanoid then
if enabled == true then -- Check that trap is currently enabled
enabled = false -- Set variable to false to disable the trap
humanoid.Health = humanoid.Health - 10
print("OUCH!")
wait(RESET_SECONDS) -- Wait for reset time duration
enabled = true -- Re-arms trap
end
end
end
trap.Touched:Connect(damageUser)
W tym systemie gracz zostanie skrzywdzony tylko wtedy, gdy włączona ma wartość prawda.
Symulator górnictwa
Doskonałym przykładem tego, kiedy nie chcesz, aby kod działał częściej niż zamierzono, jest dawanie użytkownikom złota lub punktów. Tutaj tworzysz symulator górnictwa, w którym użytkownicy otrzymują złoto za każdym razem, gdy wydobywają stos rudy, taki jak ten na rysunku.
To Wypróbuj sam używa ProximityPrompts dla mechanika wydobycia i tablicy wyników, w której ludzie mogą zobaczyć, ile złota zebrali do tej pory.
Ustaw tablicę wyników
Będziesz korzystać z tabeli liderów wbudowanej w Roblox. To tabela liderów, którą widzisz w prawym górnym rogu wielu gier Roblox
.
Może być używany do śledzenia nie tylko wyników. Można go również wykorzystać do śledzenia, na jakim poziomie znajduje się gracz, ile posiada zasobów lub w jakiej drużynie jest. Za każdym razem, gdy gracz wchodzi do gry, powinien zostać dodany do tabeli liderów. Można to zrobić w następujący sposób:
1. W ServerScriptService dodaj nowy skrypt.
2. Pobierz usługę Players i połącz funkcję ze zdarzeniem PlayerAdded:
local Players = game:GetService("Players")
local function leaderboardSetup(player)
end
-- Connect the "leaderboardSetup()" function to the "PlayerAdded" event
Players.PlayerAdded:Connect(leaderboardSetup)
3. Wewnątrz połączonej funkcji utwórz nową instancję folderu, nazwij ją leaderstats i przydziel ją odtwarzaczowi:
local function leaderboardSetup(player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
end
WSKAZÓWKA
Upewnij się, że nazwa to Leaderstats.
Bardzo ważne jest, aby folder nazywał się Leaderstats (wszystkie małe litery). Roblox nie doda gracza do tabeli liderów, jeśli zostanie użyta inna odmiana imienia.
4. Ustaw rzeczywistą statystykę, którą widzisz w rogu ekranu. Staraj się postępować zgodnie z instrukcjami bez patrzenia na pole kodu:
A. Użyj zmiennej lokalnej o nazwie gold, aby utworzyć nową instancję IntValue.
B. Nazwij IntValue Gold. To, co tu wpiszesz, będzie dokładnie widoczne dla użytkowników.
C. Ustaw właściwość Value IntValue na 0.
D. Nadrzędna wartość IntValue dla statystyk liderów.
local Players = game:GetService("Players")
local function leaderboardSetup(player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local gold = Instance.new("IntValue")
gold.Name = "Gold"
gold.Value = 0
gold.Parent = leaderstats
end
Players.PlayerAdded:Connect(leaderboardSetup)
WSKAZÓWKA
Obiekty IntValue mogą pomóc w śledzeniu wartości.
IntValue to specjalne obiekty, które akceptują tylko liczby całkowite - czyli liczby całkowite. W ten sposób nie uzyskasz przypadkowo czegoś w rodzaju 6,7 punktu
Przygotuj obiekt rudy złota
W przypadku rudy złota możesz użyć części lub siatki. Pamiętaj, że zawsze możesz skopiować siatki widoczne w dowolnym szablonie Roblox Studio i wkleić je do swojego pliku. Podobnie jak w przypadku portalu z ostatniej godziny, używasz ProximityPrompt, aby umożliwić ludziom interakcję z rudą i tworzony jest nowy atrybut. Jedną z fajnych rzeczy w ProximityPrompts jest to, że możesz je modyfikować, aby zawierały własne odbicia, zwiększając właściwość HoldDuration:
1. Wybierz część lub siatkę, która posłuży jako złoże rudy złota.
2. Wstaw ProximityPrompt.
3. Nazwij ProximityPrompt GoldOre. Jest to ważne, ponieważ będziemy używać nazwy, aby sprawdzić, czy mamy odpowiedni monit o bliskość.
4. We właściwościach ProximityPrompt zmień następujące ustawienia, jak pokazano na rysunku :
* ActionText: Mine
* HoldDuration: 1 (jest to czas, przez jaki użytkownicy muszą wstrzymać interakcję, aby wydobyć rudę.)
* Tekst obiektu: Gold Ore
5. Wybierz część rudy złota i dodaj nowy atrybut o nazwie ResourceType, ustawiony na ciąg.
6. Ustaw ResourceType na Gold, jak pokazano na rysunku
WSKAZÓWKA
Atrybuty mogą sprawić, że Twój kod będzie nadawał się do ponownego użycia.
Używanie atrybutu do tagowania ResourceType oznacza, że możesz użyć tego samego skryptu dla innych obiektów kolekcjonerskich.
Skonfiguruj scenariusz rudy złota
Następnym krokiem jest skonfigurowanie interaktywności dla ProximityPrompt, ale tym razem umieścisz skrypt w ServerScriptService. To pozwoli ci mieć wiele kopalni złota, które używają tego samego skryptu. Dzięki zdarzeniu PromptTriggered możesz stwierdzić, czy gracz przytrzymał przycisk przez wymaganą ilość czasu:
1. Wstaw nowy skrypt do ServerScriptService.
2. W górnej części skryptu pobierz ProximityPromptService. Następnie utwórz zmienną określającą, jak długo monit będzie wyłączony po jego użyciu. Sprawdź, czy pamiętasz, jak to zrobić bez odwoływania się do przykładowego kodu w kroku 3.
3. Utwórz nową funkcję połączoną ze zdarzeniem PromptTriggered z parametrami dla podpowiedzi i dla odtwarzacza, w podanej kolejności. W ten sposób wiesz, kiedy użytkownik skończy przytrzymywać przycisk:
local Players = game:GetService("Players")
local ProximityPromptService = game:GetService("ProximityPromptService")
local isEnabled = true -- Debounce variable
local DISABLED_DURATION = 4
local function onPromptTriggered(prompt, player)
end
Proxim ityPromptService.PromptTriggered:Connect(onPromptTriggered)
WSKAZÓWKA
Musisz uwzględnić oba argumenty.
Po wyzwoleniu monitu zwracany jest zarówno konkretny monit, który go wywołał, jak i gracz, który go uruchomił. Musisz tylko znać gracza, ale pamiętaj, że zwracane wartości są zawsze zwracane w kolejności. Więc jeśli chcesz drugą zwróconą wartość, potrzebujesz dwóch symboli zastępczych.
4. W twojej grze może być wiele monitów o bliskość, więc znajdź rodzica monitu i sprawdź, czy ma on atrybut o nazwie ResourceType:
local ProximityPromptService = game:GetService("ProximityPromptService")
local DISABLED_DURATION = 4
local function onPromptTriggered(prompt, player)
local node = prompt.Parent
local resourceType = node:GetAttribute("ResourceType")
end
ProximityPromptService.PromptTriggered:Connect(onPromptTriggered)
5. Jeśli istnieje typ zasobu, a wartość prompt.Enabled jest równa true, wyłącz monit:
local function onPromptTriggered(prompt, player)
local node = prompt.Parent
local resourceType = node:GetAttribute("ResourceType")
if resourceType and prompt.Enabled then
prompt.Enabled = false
end
end
6. Znajdź statystyki liderów gracza, a następnie użyj resourceType, aby zaktualizować statystyki tabeli liderów, jak pokazano:
local function onPromptTriggered(prompt, player)
local node = prompt.Parent
local resourceType = node:GetAttribute("ResourceType")
if resourceType and prompt.Enabled then
prompt.Enabled = false
local leaderstats = player.leaderstats
local resourceStat = leaderstats:FindFirstChild(resourceType)
resourceStat.Value += 1
end
end
7. Po upływie określonego czasu ponownie włącz monit, aby można było go ponownie użyć:
local ProximityPromptService = game:GetService("ProximityPromptService")
local DISABLED_DURATION = 4
local function onPromptTriggered(prompt, player)
local node = prompt.Parent
local resourceType = node:GetAttribute("ResourceType")
if resourceType and prompt.Enabled then
prompt.Enabled = false
local leaderstats = player.leaderstats
local resourceStat = leaderstats:FindFirstChild(resourceType)
resourceStat.Value += 1
wait(DISABLED_DURATION)
prompt.Enabled = true
end
end
ProximityPromptService.PromptTriggered:Connect(onPromptTriggered)
Skończyłeś! Dodaj wizualne wskaźniki, gdy ruda jest wyłączona, na przykład zmiana przezroczystości lub koloru rudy.
.
Kiedy działa tak, jak chcesz, śmiało duplikuj rudę tyle razy, ile chcesz. Fajną rzeczą jest to, że ponieważ masz tylko jeden skrypt w ServerScriptService, jeśli chcesz później wprowadzić zmiany w skrypcie, jest to naprawdę łatwe, bez względu na to, ile kopii rudy dodasz do swojej gry.
WSKAZÓWKA
Zapisywanie danych gracza
Z kodem, który posiadasz do tej pory, użytkownik musi zaczynać od nowa za każdym razem, gdy dołącza do gry. Godzina 17 pokazuje, jak zapisywać dane użytkowników między sesjami.
Dowiedzieć się, gdzie coś idzie nie tak
Wszyscy popełniamy błędy. Nawet profesjonalni programiści Roblox, którzy kodują od lat, popełniają błędy każdego dnia. Kluczem jest wypracowanie detektywistycznego podejścia zarówno do tego, co poszło nie tak z twoim kodem, jak i do tego, jak kod może napotkać nieoczekiwane sytuacje, gdy użytkownicy wchodzą z nim w interakcję w tworzonym przez ciebie doświadczeniu. W drugiej połowie tej godziny omówimy kilka technik, których możesz użyć do przetestowania kodu i oceny go, aby zapewnić lepsze wrażenia osobom odwiedzającym Twoje światy Roblox.
Korzystanie z debugowania łańcuchów
Wraz ze wzrostem umiejętności kodowania i ciągłym podejmowaniem wyzwań często nie będziesz mieć pewności, dlaczego Twój kod nie zadziałał przy pierwszej próbie. Najbardziej oczywistą rzeczą do sprawdzenia w pierwszej kolejności są podkreślone błędy w edytorze i oknie Wyjście. Czasami jednak to nie wystarczy. Następnym krokiem w ustaleniu, gdzie coś poszło nie tak, jest próba znalezienia miejsca, w którym kod nie działał zgodnie z oczekiwaniami. Być może funkcja nie została faktycznie wywołana lub podane wartości nie były zgodne z oczekiwaniami. Jednym ze sposobów zawężenia zakresu jest połączenie instrukcji print ze znajomością zakresu. Użyj instrukcji print, aby sprawdzić, czy zmienne są zgodne z oczekiwaniami, a kod działa w oczekiwanym czasie. Na przykład, jeśli chcesz się upewnić, że funkcja została wywołana, umieść instrukcję print bezpośrednio na początku funkcji:
local speedBoost = script.Parent
local function onTouch(otherPart)
print("onTouch was called!")
-- Code Body
end
speedBoost.Touched:Connect(onTouch)
Jeśli z jakiegoś powodu nie widzisz komunikatu "onTouch został wywołany!" w oknie wyjściowym wiesz, że funkcja nigdy nie została wywołana. Być może zdarzenie nie zostało uruchomione lub nie jest połączone z funkcją. Jeśli zobaczysz komunikat, musisz sprawdzić, czy problem dotyczy następnego fragmentu kodu i sprawdzić, czy fragmenty kodu działają zgodnie z oczekiwaniami. Poniższy fragment kodu służy do tworzenia przyspieszenia. Kod można umieścić w skrypcie wstawionym do części. Instrukcja print służy do weryfikacji prędkości marszu użytkownika przed wykonaniem warunku, kiedy prędkość marszu powinna zostać zmieniona i po przywróceniu jej do normy. W ten sposób możesz sprawdzić, czy kod działa, a WalkSpeed zmienia się zgodnie z oczekiwaniami:
local speedBoost = script.Parent
local function onTouch(otherPart)
print("onTouch was called!")
local character = otherPart.Parent
local humanoid = character:FindFirstChildWhichIsA ("Humanoid")
if humanoid and humanoid.WalkSpeed <= 16 then
-- Checks for Humanoid without speed boost
print("Original walk speed is " .. humanoid.WalkSpeed)
humanoid.WalkSpeed = 30
print("New walk speed is " .. humanoid.WalkSpeed)
wait(1) -- Duration of boost
humanoid.WalkSpeed = 16
print("Walk speed is returned to " .. humanoid.WalkSpeed)
end
end
speedBoost.Touched:Connect(onTouch)
Po zakończeniu testowania kodu zawsze wracaj i usuwaj wszystkie niepotrzebne instrukcje print. Każda uruchomiona linia kodu powoduje, że skrypt jest nieco wolniejszy, ponieważ skrypt ma więcej do zrobienia. Usunięcie niepotrzebnego kodu sprawia, że wszystko działa tak szybko, jak to możliwe.
Przenoszenie wartości dla łatwiejszego testowania
Nawet jeśli Twój kod działa idealnie, nadal możesz potrzebować poprawek. Być może w ostatnim fragmencie kodu nie jesteś do końca pewien, jak szybko gracz ma działać lub jak długo powinno trwać wzmocnienie. Doskonałą praktyką jest przenoszenie ważnych zmiennych, które mają wpływ na wrażenia użytkownika, na początek skryptu, co ułatwia Tobie i członkom zespołu wprowadzanie odpowiednich poprawek. Ten fragment kodu robi to samo, co poprzedni fragment, ale u góry utworzono zmienne określające, jak szybko gracze będą się poruszać i jak długo będzie trwało wzmocnienie:
local speedBoost = script.Parent
local BOOSTED_SPEED = 20
local BOOST_DURATION = 1
local function onTouch(otherPart)
local character = otherPart.Parent
local humanoid = character:FindFirstChildWhichIsA ("Humanoid")
if humanoid and humanoid.WalkSpeed <= 16 then
print("Original walk speed is " .. humanoid.WalkSpeed)
humanoid.WalkSpeed = BOOSTED_SPEED
print("New walk speed is " .. humanoid.WalkSpeed)
wait(BOOST_DURATION) -- Duration of boost
humanoid.WalkSpeed = 16
print("Walk speed is returned to " .. humanoid.WalkSpeed)
end
end
speedBoost.Touched:Connect(onTouch)
W bardzo długim skrypcie oszczędza to dużo czasu na aktualizowanie podczas eksperymentowania w celu znalezienia odpowiedniej wartości do użycia - zwłaszcza jeśli ta sama wartość jest używana w wielu miejscach. Zmienne, takie jak te utworzone dla NEW_SPEED i BOOST_DURATION, które nigdy nie zmieniają wartości przez cały skrypt, nazywane są stałymi. W przeciwieństwie do zwykłych zmiennych, są one wpisywane ALL_CAPS ze słowami oddzielonymi podkreśleniem (_).
Popraw SpeedBoost
Weź poprzedni fragment kodu i dostosuj wartości, aż poczujesz, że prędkość i czas trwania są odpowiednie. Jedną z technik, których możesz użyć podczas eksperymentowania z wartościami, jest podwojenie i zmniejszenie o połowę. Jest to szczególnie dobre, jeśli nie masz pewności, jakiej liczby użyć.
1. Dodaj nową część lub siatkę i wstaw do niej skrypt, jak pokazano na rysunku
2. Daj ludziom tymczasowe zwiększenie prędkości po dotknięciu części. Możesz użyć fragmentów kodu z wcześniejszej części tego rozdziału lub, jeśli zrobiłeś to sam w godzinie 5, użyj tego i zmodyfikuj kod, aby używał stałych.
3. Eksperymentuj z wartościami BOOSTED_SPEED i BOOST_DURATION, podwajając wartości BOOSTED_SPEED i BOOST_DURATION.
4. Przetestuj i zobacz wyniki. Jeśli wydaje się, że nie jest wystarczająco szybki lub wzmocniony przez wystarczająco długi czas, ponownie podwój ilość. Jeśli wydaje ci się, że to za dużo, odejmij połowę tego, co dodałeś.
Sprawdzanie wartości atrybutów
Większość wartości zmiennych można wydrukować, ale atrybuty zachowują się nieco inaczej. Najpierw musisz przypisać wartość atrybutu do zmiennej:
local activated Value = weapon:GetAttribute("Activated")
print(activatedValue)
Należy o tym pamiętać podczas potwierdzania wartości atrybutów.
Uzyskiwanie właściwych typów wartości
Należy również uważać na to, jakie typy wartości są przekazywane z powrotem do funkcji. Dobry kod bierze pod uwagę, że przy przekazywaniu nieprawidłowych typów wartości zdarzają się błędy. Jeśli spróbujesz przekazać ciąg znaków do funkcji wait(), łańcuch jest ignorowany i używana jest wbudowana wartość domyślna wynosząca jedną trzydziestą sekundy :
local part = script .Parent
wait("twenty") -- Will use default value because strings aren′t accepted
part.Color = Color3.fromRGB(170, 0, 255)
Streszczenie
Istnieje wiele sposobów, aby upewnić się, że kod działa tylko raz przy użyciu różnych typów systemów odrzucania. Jednym ze sposobów, z których mogłeś korzystać wcześniej, jest usuwanie części, gdy tylko coś jej dotknie. Dwa inne sposoby używane w tej godzinie to ustawienie zmiennej debounce i użycie zachęty zbliżeniowej z długim przytrzymaniem. Bez względu na to, jaki sposób zostanie użyty, zawsze myśl o tym, jak Twoje wybory wpłyną na użytkownika końcowego. Dużą częścią bycia programistą jest myślenie o wszystkich możliwych scenariuszach, które mogą się pojawić, i próba stworzenia kodu, który się nie zepsuje, a jednocześnie zapewni użytkownikom najlepsze możliwe wrażenia. Oczywiście to normalne, że coś pójdzie nie tak. Zdarza się to najlepszym programistom - nawet tym, którzy tworzą Twoje ulubione doświadczenia związane z Robloxem. Jeśli coś nie idzie zgodnie z oczekiwaniami, możesz wykorzystać swoją wiedzę na temat zakresu, funkcji i zdarzeń, aby zawęzić problem. Kilka dobrze umieszczonych instrukcji print może pomóc w sprawdzeniu, czy funkcje są wywoływane i czy wartości są zgodne z oczekiwaniami.
Pytania i odpowiedzi
P. Czy w rankingach można używać wartości innych niż IntValues?
O. Tak, możesz tworzyć inne typy wartości. Na przykład na rysunku
użyto zmiennej StringValue do wyświetlenia nazwy frakcji postaci.
P. Czy istnieje maksymalna liczba statystyk, które można wyświetlić?
O. Można wyświetlić maksymalnie cztery statystyki, chociaż nadal można śledzić dodatkowe statystyki.
P: Czy możesz tworzyć własne niestandardowe tabele liderów?
O. Możesz! W późniejszych godzinach dowiesz się więcej o tym, jak wyświetlać informacje poszczególnym osobom i wszystkim na serwerze jako całości.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Jak to się nazywa, gdy upewniasz się, że kod może zostać uruchomiony tylko raz, a nie wiele razy?
2. Jak nazywają się zmienne, których wartości nie zmieniają się podczas działania skryptu?
3. Jak sformatowane są zmienne w poprzednim pytaniu w porównaniu z innymi zmiennymi?
4. Jaka jest łatwa technika ustalania, jakich liczb wartości użyć podczas poprawiania kodu w celu lepszego doświadczenia użytkownika?
5. Jak wydrukować wartość atrybutu?
Odpowiedzi
1. Odbicie
2. Stałe
3. ALL_CAPS, ze znakiem podkreślenia (_) między słowami
4. Podwajanie i zmniejszanie o połowę
5. Najpierw musisz przypisać atrybut do zmiennej, a następnie wydrukować zmienną:
local armorValue = Helm:GetAttribute("Armor")
print(armorValue)
Ćwiczenia
Dużą częścią pracy każdego inżyniera jest myślenie zarówno o tym, co może pójść nie tak, jak i o tym, jak poprawić sytuację. Pomyśl o kodzie, który stworzyłeś przez wszystkie godziny do tej pory, i w tym pierwszym ćwiczeniu zapisz co najmniej trzy sposoby, w jakie kod mógłby być lepszy. Mogą to być rzeczy, które mogą pójść nie tak z kodem lub funkcje, które pozwolą ludziom lepiej cieszyć się Twoim doświadczeniem. Być może nie jesteś jeszcze w stanie zaprogramować rozwiązań, ale powinieneś wyrobić sobie nawyk krytycznego podejścia do tworzonego kodu. W załączniku znajdziesz możliwe odpowiedzi. W drugim ćwiczeniu wykonaj dwa podbicia: jeden, który cię zmniejszy, i drugi, który cię powiększy
.
Zamiast ustawiać trzy określone rozmiary, użyj mnożnika, aby zmienić aktualną skalę awatara. Pamiętaj, aby utworzyć zmienną debounce, aby kontrolować, jak szybko osoba może rosnąć i kurczyć się. Jest to jeden przypadek, w którym jeśli funkcja jest uruchamiana częściej niż zamierzono, Twoje działanie ulegnie awarii.
Porady
&bul; Możesz zmodyfikować domyślną skalę użytkownika za pomocą następujących właściwości:
- Humanoid.HeadScale: Skala głowy awatara.
- Humanoid.BodyDepthScale: Skala głębokości ciała.
- Humanoid.BodyWidthScale: Skala szerokości ciała.
-Humanoid.BodyHeightScale: Skala wzrostu ciała.
- Skonfiguruj proste odrzucanie przed eksperymentowaniem ze skalą awatara, aby uniknąć awarii.
- Zapisz swoją pracę przed testowaniem! Jeśli skala awatara stanie się zbyt duża, spowoduje to awarię.
Pętle while
Czy masz czasem wrażenie, że utknąłeś w pętli, w której ciągle robisz to samo? Wstań, zjedz śniadanie, pracuj ciężko, idź do łóżka, a następnego dnia to samo. Widzimy pętle w całym naszym świecie. Minuty na naszych zegarach zapętlają się przez 60 minut, a godziny zapętlają się 24 razy, tworząc dzień. Skrypty też mają pętle. Kiedy znajdują się w pętli, wykonują to samo zadanie, dopóki coś ich nie zatrzyma. Ta godzina dotyczy tylko jednego rodzaju pętli, którą można zawrzeć w kodzie:
pętle while.
Powtarzaj na zawsze, podczas gdy prawda
Pierwszym rodzajem pętli w tej godzinie jest pętla while. Te pętle są zwykle używane do sprawdzania stanu czegoś i działają w nieskończoność, dopóki warunek nie zostanie spełniony. Mogą nawet biec w nieskończoność! Poniższy fragment kodu pokazuje, jak sformatowana jest pętla while:
local isHungry = true
while isHungry == true do
print("I should eat something")
wait(2.0)
end
Główne słowa kluczowe to while i do. W środku tych słów kluczowych znajduje się warunek sprawdzania pętli while. Tak długo, jak ten warunek jest spełniony, kod działa. W rzeczywistości, jeśli chcesz, aby kod działał w nieskończoność, możesz po prostu ustawić warunek na true:
while true do
print(count)
count = count + 1
wait(1.0)
end
Poprzedni przykład liczył co sekundę i wyświetlał wynik w danych wyjściowych, dopóki nie zatrzymasz odtwarzania.
O czym warto pamiętać
Podczas pracy z pętlami while należy pamiętać o kilku rzeczach. Jednym z nich jest to, że każda pętla while powinna zawierać funkcję oczekiwania. Jeśli tego nie zrobisz, istnieje duża szansa, że pętla będzie działać tak szybko, że Twoje doświadczenie zakończy się spowolnieniem lub awarią. Inną rzeczą, o której należy pamiętać, jest to, że następna pętla jest uruchamiana, gdy tylko zakończy się poprzednia pętla.
Stwórz dyskotekowy parkiet taneczny
Weźmy szybki przykład, w którym tworzysz parkiet do tańca dyskotekowego i chcesz, aby elementy podłogi przechodziły przez serię określonych kolorów - w tym przypadku wzór niebieski i pomarańczowy:
1. Użyj części, aby działała jako sekcja podłogi i wstaw skrypt z następującym kodem:
local discoPiece = script.Parent
while true do
discoPiece.Color = Color3.fromRGB(0, 0, 255)
wait(1.0)
discoPiece.Color = Color3.fromRGB(255, 170, 0)
end
2. Uruchom kod. Jedynym kolorem, który widzisz podczas działania kodu, jest niebieski. Ponieważ następna pętla rozpoczyna się natychmiast, pomarańcza miga tak szybko, że nawet jej nie widać.
3. Napraw to, dodając drugą funkcję oczekiwania po zmianie koloru:
local discoPiece = script.Parent
while true do
discoPiece.Color = Color3.fromRGB(0, 0, 255)
wait(1.0)
discoPiece.Color = Color3.fromRGB(255, 170, 0)
wa it(1.0)
end
Jeśli cała pętla wymaga tylko jednej funkcji oczekiwania, można ją zastosować w warunku. Jest to zademonstrowane w poniższym fragmencie kodu, który co sekundę przypisuje nowy losowy kolor podłodze, takiej jak ta na rysunk:
local discoPiece = script.Parent
while wait(1.0) do
-- Get random values for RGB
local red = math.random(0, 255)
local green = math.random(0, 255)
local blue = math.random(0, 255)
-- Assigns color values
discoPiece.Color = Color3.fromRGB(red, green, blue)
end
Utrzymaj płonące ognisko
Ta gra Try It wykorzystuje pętlę while do śledzenia liczby płonących kłód paliwa. Ogień jest wyłączony, dopóki ktoś nie użyje ProximityPrompt, aby dolać paliwa. Potem ogień płonie przez chwilę przed ponownym wyjściem.
Set up
Najpierw ustaw ogień i ProximityPrompt. Gdy wszystko jest już gotowe, ogień można skopiować do dowolnego środowiska lub modelu:
1. Użyj niewidzialnej części, aby utrzymać ogień.
2. Dodaj nowy atrybut do części CampFire:
Nazwa: Fuel
Typ: Number
3. Włóż emiter cząstek o nazwie Fire i ProximityPrompt o nazwie AddFuel.
WSKAZÓWKA
Projektowanie ognia
W przypadku cząstek ognia ustawienie właściwości Texture na 4797593940 i Speed na 0 pomoże uzyskać cząsteczkę podobną do tej pokazanej w przykładzie. Następnie spróbuj poeksperymentować z wartościami koloru, przeciągnięcia i czasu życia.
4. We właściwościach ParticleEmitter odznacz opcję Enabled, ponieważ zostanie ona włączona w skrypcie.
5. We właściwościach ProximityPrompt zmień HoldDuration na 2.
Scenariusz
Po wyzwoleniu ProximityPrompt paliwo jest dodawane do ognia i ogień jest włączony. Pętla while zużywa paliwo co sekundę, a gdy paliwo osiągnie 0, ogień zostaje wyłączony:
1. W ServerScriptService dodaj nowy skrypt.
2. Pobierz ProximityPromptService i skonfiguruj funkcję, która jest wywoływana po uruchomieniu monitu. Wewnątrz upewnij się, że monit jest włączony i potwierdź, że monit wyzwalający to "AddFuel":
local ProximityPromptService = game:GetService("ProximityPromptService")
local BURN_DURATION = 3
local function onPromptTriggered(prompt, player)
if prompt.Enabled and prompt.Name == "AddFuel" then
end
end
ProximityPromptService.PromptTriggered:Connect(onPromptTriggered)
3. Utwórz stałą kontrolującą, jak długo ogień będzie się palił; wewnątrz instrukcji if utwórz zmienne dla części ogniska i cząstek ognia:
local ProximityPromptService = game:GetService("ProximityPromptService")
local BURN_DURATION = 3
local function onPromptTriggered(prompt, player)
if prompt.Enabled and prompt.Name == "AddFuel" then
local campfire = prompt.Parent
local fire = campfire.Fire -- This should be the particle emitter
end
end
ProximityPromptService.PromptTriggered:Connect(onPromptTriggered)
4. Uzyskaj aktualną wartość atrybutu Paliwo i dodaj 1:
local function onPromptTriggered(prompt, player)
if prompt.Enabled and prompt.Name == "AddFuel" then
local campfire = prompt.Parent
local fire = campfire.Fire -- This should be the particle emitter
local currentFuel = campfire:GetAttribute("Fuel")
campfire:SetAttribute("Fuel", currentFuel + 1)
end
end
5. Użyj innego if, aby sprawdzić, czy cząsteczki są wyłączone, a jeśli tak, włącz cząsteczki:
local function onPromptTriggered(prompt, player)
if prompt.Enabled and prompt.Name == "AddFuel" then
local campfire = prompt.Parent
local fire = campfire.Fire -- This should be the particle emitter
local currentFuel = campfire:GetAttribute("Fuel")
campfire:SetAttribute("Fuel", currentFuel + 1)
if not fire.Enabled then
fire.Enabled = true
end
end
end
6. Spalaj po jednym kawałku paliwa za pomocą pętli while, a następnie wyłącz cząstki:
local ProximityPromptService = game:GetService("ProximityPromptService")
local BURN_DURATION = 3
local function onPromptTriggered(prompt, player)
if prompt.Enabled and prompt.Name == "AddFuel" then
local campfire = prompt.Parent
local fire = campfire.Fire -- This should be the particle emitter
local currentFuel = campfire:GetAttribute("Fuel")
campfire:SetAttribute("Fuel", currentFuel + 1)
if not fire.Enabled then
fire.Enabled = true
while campfire:GetAttribute("Fuel") > 0 do
local currentFuel = campfire:GetAttribute("Fuel")
campfire:SetAttribute("Fuel", currentFuel - 1)
wait(BURN_DURATION)
end
fire.Enabled = false
end
end
end
ProximityPromptService.PromptTriggered:Connect(onPromptTriggered)
Sprawdź swoją pracę. Jeśli interfejs użytkownika przeszkadza w dostrzeżeniu pożaru, możesz przenieść go wyżej we właściwościach zachęty, używając UIOffset
Gdy już wiesz, że ognisko działa zgodnie z przeznaczeniem, możesz dodać je do bardziej wyszukanych środowisk, takich jak te pokazane na rysunku
Jeśli chcesz to rozwinąć, możesz poprosić graczy, aby zbierali drewno z pobliskich drzew, zanim będą mogli rozpalić ogień.
Pętle while i zakres
Ostatnią rzeczą, o której musisz wiedzieć o pętlach while, jest to, że żaden kod znajdujący się pod pętlą while nigdy się nie uruchomi, chyba że pętla zostanie przerwana:
print("The loop hasn't started yet") -- Will run once
while wait(1.0) do
print("while loop has looped") -- Will run until the server stops
end
print("T he while loop has stopped looping ") -- Will never run
Streszczenie
Tworząc więcej środowisk, znajdziesz więcej przypadków, w których chcesz, aby kod powtarzał się w nieskończoność lub w określonych okolicznościach. Niektóre pętle będą małe i szybkie, jak pętla tworząca migoczące światło. Inne pętle będą dłuższe i kontrolują cały przebieg gry - na przykład pętle występujące w grach opartych na rundach, w których ludzie czekają w lobby przez określony czas, a następnie są przenoszeni do miejsca, w którym odbywa się akcja. Pod koniec rundy wszystko jest czyszczone, ludzie są odsyłani z powrotem do lobby, a następnie pętla zaczyna się od nowa. Oczywiście przy korzystaniu z pętli while należy pamiętać o kilku rzeczach. Ponieważ pętla while działa w nieskończoność, kod znajdujący się pod pętlą nigdy nie zostanie osiągnięty, chyba że pętla się zatrzyma. Jest to również własny fragment kodu, więc musisz pamiętać, jak to wpływa na zakres. Jeśli nie chcesz, aby pętla rozpoczynała się zaraz po uruchomieniu serwera, zawsze możesz zawinąć pętlę while w funkcję, jeśli chcesz kontrolować moment jej uruchomienia.
Pytania i odpowiedzi
P. Co zrobić, jeśli chcesz, aby fragment kodu powtarzał się tylko określoną liczbę razy?
O. Jeśli chcesz, aby fragment kodu powtarzał się określoną liczbę razy - na przykład, jeśli chcesz utworzyć dokładnie dziesięć drzew - możesz użyć tak zwanej pętli for. pętle for są omówione w części 8.
P. Co zrobić, jeśli chcesz, aby pętla działała, gdy coś jest fałszywe, a nie gdy jest prawdziwe?
O. Jeśli chcesz, aby fragment kodu działał, gdy warunek jest fałszywy, masz kilka opcji. Po pierwsze, możesz ustawić warunek, taki jak while NumberOfPlayers ~= 0 do. Tutaj fragment kodu działa tak długo, jak długo liczba graczy nie jest równa zeru. Alternatywnie możesz użyć akcji powtarzania do (warunek), która nakazuje fragmentowi kodu powtarzanie w nieskończoność, aż warunek stanie się prawdziwy.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Jak długo będzie działać pętla while?
2. Co zawsze musi być zawarte w pętli while i dlaczego?
3. Jak często poniższa pętla będzie drukować hello?
while wait(1.0) do
print("hello")
wait(1.0)
end
4. Na ile kolorów zmieni się discoFloor, o którym mowa w tym kodzie?
local discoFloor = script.Parent
while wait(2.0) do
print("hello")
end
while true do
discofloor.Color = Color3.fromRGB(0, 0, 255) -- Blue
wait(1.0)
discofloor.Color = Color3.fromRGB(255, 255, 0) -- Yellow
end
discofloor.Color = Color3.fromRGB(255, 0, 127) - Pink
Odpowiedzi
1. Dopóki dany warunek nie jest fałszywy.
2. Zawsze musi być dołączona funkcja oczekiwania; w przeciwnym razie pętla kodu będzie działać szybciej niż silnik może obsłużyć i ulec awarii.
3. Hello będzie drukowane co 2 sekundy. Warunek zawiera jednosekundowe oczekiwanie, aw pętli jednosekundowe oczekiwanie. Drugie oczekiwanie nie jest jednak potrzebne. To może być tylko dwie sekundy oczekiwania w tym stanie.
4. Podłoga nigdy nie zmieni kolorów. Pierwsza pętla uniemożliwia uruchomienie drugiej pętli. Jednak gdyby go tam nie było, byłby niebieski. Żółty migałby zbyt szybko, aby go zobaczyć, a różowy jest poza zakresem pętli.
Ćwiczenia
W tym pierwszym ćwiczeniu zmodyfikuj kod tak, aby ludzie musieli zbierać drewno na opał, zamiast po prostu podejść do ognia i go rozpalić
Porady
• Użyj tabeli liderów, aby śledzić, ile drewna ma gracz. Możesz użyć prawie dokładnego kodu i konfiguracji, które zostały użyte dla rudy w ciągu ostatniej godziny, aby zebrać dzienniki.
• Zmodyfikuj skrypt ogniska, aby pobierał kłody od gracza do wykorzystania jako paliwo. Uniwersalna prawda dotycząca kodowania i projektowania jest taka, że później będziesz chciał coś zaktualizować. Im więcej masz kopii czegoś w swojej grze, tym trudniej jest wprowadzać aktualizacje, niezależnie od tego, czy chodzi o skrypty, cząsteczki czy modele. W drugim ćwiczeniu spróbuj zaktualizować skrypt ognia, aby zamiast włączać istniejący emiter cząstek, wstawiał sklonowany emiter cząstek do ogniska.
• Nadal potrzebujesz części do przytrzymania ProximityPrompt.
• Sprawdź, czy pamiętasz, jak sklonować rzeczy z ReplicatedStorage.
Pętla for
Do tej pory omówiliśmy jeden rodzaj pętli - pętlę while, która może trwać wiecznie, zawsze i zawsze, jeśli tego właśnie chcesz. Jeśli chcesz mieć pewność, że kod aktualizuje się tylko określoną liczbę razy, użyj innego rodzaju pętli: pętli for. W przeciwieństwie do pętli while, pętle for powtarzają się określoną liczbę razy, aż do osiągnięcia celu. Rysunek przedstawia pętlę for używaną do odliczania do oczekiwanej kolizji meteorytu.
Utwórz odliczanie
Przetestuj tę prostą pętlę for, która odlicza do 0. Poszczególne części kodu zostaną wyjaśnione w następnej sekcji:
1. W dowolnym skrypcie skopiuj następujące elementy:
for countDown = 10, 0, -1 do
print(countDown)
wait(1.0)
end
2. Uruchom kod. W oknie Dane wyjściowe powinno być widoczne odliczanie takie jak na rysunku
Jak działają pętle for
Pętla for używa trzech wartości do kontrolowania liczby uruchomień, które są sformatowane tak, jak pokazano na rysunku
* Zmienna kontrolna: Śledzi bieżącą wartość. Przypisana wartość wyznacza miejsce startu. Zmienną kontrolną może być dowolna akceptowalna nazwa zmiennej. Podobnie jak inne nazwy zmiennych, nazwa zmiennej sterującej powinna być jasna i opisowa, co robi pętla for.
* Wartość końcowa lub docelowa: Wartość, przy której pętla powinna przestać działać. Skrypt porównuje zmienną kontrolną z wartością końcową przed rozpoczęciem następnej pętli.
* Wartość przyrostu: Wartość, o którą zmienna kontrolna zmienia się za każdym razem. Dodatnie wartości przyrostu zliczają się; odliczanie ujemnych wartości przyrostu.
Rozpoczynając od wartości początkowej zmiennej kontrolnej, pętla for liczy w kierunku końcowej wartości celu i zatrzymuje się po osiągnięciu wartości celu:
1. Pętla for porównuje zmienną kontrolną z wartością końcową
2. Po uruchomieniu kodu wartość przyrostu jest dodawana do zmiennej sterującej. Następnie pętla sprawdza zmienną kontrolną i zaczyna od nowa.
3. Gdy zmienna kontrolna przekroczy wartość końcową, pętla zostanie zatrzymana. Na przykład, jeśli pętla ma wartość końcową równą 10, gdy zmienna sterująca przekroczy 10, pętla for zostanie zatrzymana
Przyjrzyjmy się jeszcze raz wynikom pokazanym w Wypróbuj sam, pokazanym na rysunku
Pętla, która była uruchamiana za każdym razem, gdy wypisano liczbę, nazywana jest iteracją. Iteracja to pełny proces sprawdzania wartości kontrolnej, uruchamiania kodu i aktualizowania wartości przyrostu. Ponieważ liczenie rozpoczęło się od 0 i zakończyło po 10, kod faktycznie przeszedł jedenaście iteracji. Należy o tym pamiętać podczas projektowania pętli. Jeśli ważne jest, aby liczba przeszła określoną liczbę razy, prawdopodobnie będziesz chciał, aby wartość początkowa wynosiła 1 zamiast 0.
Przyrosty są opcjonalne
Jeśli wartość przyrostu nie jest uwzględniona, używana jest wartość domyślna 1. Fragment kodu zaczyna się od 0 i liczy w górę do 10:
for countUp = 0, 10 do
print(countUp)
wait(1.0)
end
Inne przykłady pętli for
Zmiana wartości zmiennej sterującej, celu końcowego i przyrostu zmienia sposób działania pętli. Pętla for, którą właśnie napisałeś, mogłaby zamiast tego liczyć do 10 lub odliczać liczby nieparzyste. Poniżej przedstawiono przykłady pętli for z różnymi wartościami początkowymi, końcowymi i przyrostowymi.
Liczenie przez jeden
for count = 0, 5, 1 do
print(count)
wait(1.0)
end
Liczenie w liczbach parzystych
for count = 0, 10, 2 do
print(count)
wait(1.0)
end
Uważaj, aby nie odwrócić wartości początkowej i docelowej, na przykład:
for count = 10, 0, 1 do
print(count)
wait(1.0)
end
Jeśli zmienna sterująca zaczyna przekraczać wartość końcową, tak jak we wcześniejszym przykładzie, pętla for w ogóle się nie uruchamia. W tym przypadku pętla for zlicza w górę i sprawdza, czy liczba jest większa niż 0. Kiedy pętla for wykonuje pierwsze sprawdzenie, widzi, że 10 jest większe niż 0, więc zatrzymuje pętlę bez drukowania czegokolwiek.
Odliczanie w świecie
Do tej pory komunikaty były wyświetlane tylko w oknie Wyjście. Teraz nadszedł czas, aby zacząć przekazywać informacje ludziom w Twoim otoczeniu. W tym Wypróbuj sam używasz graficznego interfejsu użytkownika (GUI) do wyświetlania informacji w miejscu, w którym każdy może je zobaczyć. GUI są jak etykiety z naklejkami, których można używać do wyświetlania informacji na całym świecie.
Konfiguracja
W celu konfiguracji tworzysz SurfaceGui i TextLabel i dopasowujesz je do części, aby wyświetlić odliczanie. Ponieważ jest to książka o kodowaniu, nie będziemy zbytnio zagłębiać się w to, jak one działają. Jeśli chcesz dowiedzieć się więcej, możesz znaleźć bardziej szczegółowe wyjaśnienia w Roblox Developer Hub:
1. Utwórz nową część.
2. Wstaw obiekt SurfaceGui do części. Nic oczywistego się nie dzieje, ale obiekty SurfaceGUI działają jak pojemniki na wszystko, co chcesz wyświetlić.
3. Wybierz SurfaceGui i wstaw obiekt TextLabel. Spowoduje to wyświetlenie rzeczywistego tekstu.
WSKAZÓWKA
Znalezienie TextLabel
Jeśli nie widzisz etykiety TextLabel, prawdopodobnie pojawiła się ona po innej stronie części. Możesz obrócić część lub zmienić właściwość SurfaceGui Face, aby to naprawić.
4. Wybierz TextLabel. We Właściwościach rozwiń Rozmiar. W polu Skala X wpisz 1, aw polu Przesunięcie wpisz 0. Zrób to samo dla Y. W ten sposób TextLabel powinien zająć cały bok części.
5. Pozostając we właściwościach TextLabel, przewiń prawie do samego dołu do TextScaled i włącz go
Zakoduj odliczanie
Używasz skryptu, aby zmienić to, co wyświetla TextLabel:
1. Wybierz część znaku i wstaw nowy skrypt.
2. Użyj zmiennych, aby odwołać się do rodzica skryptu i TextLabel. Podpowiedź: możesz kilkakrotnie zejść w dół hierarchii.
3. Utwórz nową pętlę for, która odlicza co sekundę:
local sign = script.Parent
local textLabel = sign.SurfaceGui.TextLabel
for countDown = 10, 1, -1 do
print(countDown)
wait(1.0)
end
4. W pętli ustaw właściwość Text TextLabel na bieżącą wartość odliczania:
local sign = script.Parent
local textLabel = sign.SurfaceGui.TextLabel
for countDown = 10, 1, -1 do
textLabel.Text = countDown
print(countDown)
wait(1.0)
end
5. Przetestuj swój kod.
WSKAZÓWKA
Uwaga dotycząca czasów ładowania
Możesz zauważyć, że czasami liczenie wydaje się zaczynać od środka. Dzieje się tak dlatego, że skrypt zaczął się, zanim Twoja postać i kamera załadowały się do końca. Możesz sprawdzić, czy odliczanie przebiegło prawidłowo, używając instrukcji print lub opóźnić rozpoczęcie odliczania za pomocą małej pauzy na początku skryptu. Gdy tworzysz więcej skryptów, zaczynasz częściej brać pod uwagę czas ładowania.
Pętle zagnieżdżone
Pętle mogą być używane w pętlach. Jednym z najczęstszych sposobów, w jaki można to zrobić, jest umieszczenie pętli for wewnątrz pętli while. W ten sposób możesz powtarzać wydarzenia, które powtarzają się co jakiś czas, takie jak pokazy sztucznych ogni:
while true do
for countDown = 10, 1, -1 do
textLabel.Text = countDown
print(countDown)
wait(1.0)
end
print("Launch the rockets!")
wait(2.0)
end
Kiedy pętle są zagnieżdżone, skrypt zaczyna się od górnej linii i idzie w dół. Gdy zostanie osiągnięta nowa pętla, ta pętla jest wykonywana do końca przed kontynuowaniem z następnymi wierszami kodu.
Wyrywanie się z pętli
Jeśli z jakiegoś powodu musisz opuścić pętlę, użyj słowa kluczowego break:
local goodToGo = true
while wait(1.0) do
if goodToGo == true then
print("Keep going")
else
break -- will stop loop if goodToGo changes to false
end
end
Streszczenie
Pętle są wszędzie w kodzie. Mogą działać w nieskończoność lub określoną liczbę razy; zależy to tylko od rodzaju używanej pętli. pętle while działają dalej, chyba że warunek początkowy stanie się fałszywy lub zostanie użyte słowo kluczowe break. Ten typ pętli jest używany do takich rzeczy, jak cykl dnia i nocy, który kończy się dopiero wtedy, gdy kończy się świat. Z drugiej strony pętle for najlepiej sprawdzają się, gdy próbujesz osiągnąć określoną wartość, na przykład odliczanie do północy w sylwestra.
Pytania i odpowiedzi
P. Dlaczego niektórzy ludzie wpisują po prostu i?
O. Istnieje trochę kontrowersji co do tego, co dokładnie oznacza i, ale powszechna teoria głosi, że pierwotnie oznaczało to liczbę całkowitą. i był po raz pierwszy używany jako zastępstwo dla nieznanych liczb przez starożytnych matematyków, a następnie przez wczesnych programistów komputerowych, którzy musieli utrzymywać swój kod bardzo, bardzo krótki. Krótko mówiąc, jest to po prostu zwykła nazwa zmiennej sterującej, dlatego czasami możesz zobaczyć pętle, które wyglądają tak:
for i = 1, 10 do
print(i)
end
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Jak długo będzie działać pętla for?
2. Co to jest przyrost?
3. Ile razy ten kod zapętli się (wartości początkowe wstecz)?
for count = 10, 0, 1 do
print(count)
end
4. Prawda czy fałsz: wartości przyrostu są opcjonalne.
Odpowiedzi
1. Do momentu osiągnięcia zadanego warunku.
2. Kwota, o którą zmienia się wartość.
3. Zero razy. Wartość początkowa 10 jest większa niż wartość docelowa 0.
4. Prawda. Wartość domyślna 1 jest używana, jeśli nie podano wartości przyrostu.
Ćwiczenia
Koncepcja obrażeń w czasie (DoT) jest używana w wielu doświadczeniach. Dzięki DoT ludzie otrzymują ciągłe obrażenia przez określony czas, zamiast przyjmować je wszystkie na raz. Typowe przykłady obejmują napotkanie trucizny lub poparzenie Ponieważ masz już ogień z wcześniejszego Spróbuj sam, w tym pierwszym ćwiczeniu użyj tego samego modelu, aby tymczasowo zadać obrażenia od oparzeń każdemu, kto go dotknie. Obrażenia po dotknięciu ognia.
Porady
* Użyj tego samego ognia, który stworzyłeś wcześniej, lub użyj części, aby działać jako zastępca.
* Wstaw nową niewidoczną część o nazwie HitBox. Skaluj go tak, aby obejmował ogień.
* Jeśli ktoś dotknie HitBox, użyj pętli for, aby zadać 10 punktów obrażeń co sekundę przez trzy sekundy.
Niewidoczne pudełko służy do wyznaczania granic ognia. W drugim ćwiczeniu poświęć chwilę na zastanowienie się nad co najmniej pięcioma innymi sposobami wykorzystania pętli for i while w doświadczeniach 3D. Nie martw się, czy wiesz, jak utworzyć kod. Ważne jest, aby móc zacząć rozpoznawać, gdzie można je znaleźć.
Praca z tablicami
Teraz nadszedł czas, aby pracować z wieloma obiektami jednocześnie, dzięki czemu możesz na przykład dać każdemu członkowi zespołu nową, lśniącą broń lub zmodyfikować każdy element w folderze. Wykonujesz takie zadania za pomocą tabel. Tabele umożliwiają organizowanie wielu fragmentów danych lub obiektów w grupy, takie jak grupy graczy lub lista wymagań dotyczących elementów przepisu. Ta godzina obejmuje pierwszy z dwóch różnych typów tabel: tablice. Dowiesz się, jak wprowadzać zmiany w całym folderze pełnym obiektów, włączając wiele świateł jednocześnie, zamiast zmuszać ludzi do włączania ich pojedynczo.
Co to są tablice?
Tablice tworzą numerowaną listę elementów, których można używać do śledzenia informacji, takich jak kto jest na pierwszym miejscu lub folder pełen różnych części. Każda pozycja na liście ma przypisany określony numer, zwany indeksem. Gdybyś miał listę zakupów, mogłaby ona wyglądać mniej więcej tak, jak w poniższej tabeli:
Lista zakupów
Indeks : 1 : 2 : 3
Wartość : Jabłka : Banany : Marchewki
Tworzenie tablicy jest tym samym, co tworzenie innych zmiennych; jedyną różnicą jest to, że przypisuje się mu nawiasy klamrowe, na przykład:
local myArray = {}
Nawiasy klamrowe sprawiają, że jest to typ danych tabeli. Elementy można dodawać do tablicy, wymieniając je w nawiasach, chociaż należy pamiętać o oddzieleniu wartości przecinkami. Numer indeksu jest przypisywany automatycznie w kolejności dodawania wartości. Oto przykład tablicy składającej się z trzech elementów:
local groceryList = {"Apples", "Bananas", "Carrots"}
Tablice mogą zawierać dowolny typ wartości - nawet inne tablice. Trzecia tablica w poniższym przykładzie zawiera dwie pierwsze tablice, a czwarta nienazwana tablica jest przypisana do indeksu 3:
local firstArray = {1, 2, 3}
local secondArray = {"first", "second", "third"}
local thirdArray = {firstArray, secondArray, {"unnamed array "}}
Dodawanie elementów później
Możesz dodać element do już utworzonej tablicy, używając table.insert(array, valueTo-Insert). Tak więc dodanie nowego elementu do poprzedniej tablicy wygląda następująco:
local groceryList = {"Apples", "Bananas", "Carrots"}
table.insert(groceryList, "Mangos")
print(groceryList)
Nowe elementy są dodawane na końcu tablicy.
Uzyskiwanie informacji z określonego indeksu
Listę można przetestować, drukując kilka zindeksowanych wartości. Aby użyć wartości w określonym indeksie, dodaj indeks po nazwie tablicy bez spacji, na przykład array_Name[1]:
local groceryList = {"Apples", "Bananas", "Carrots"}
table.insert(groceryList, "Mangos")
print(groceryList[1], groceryList[4], groceryList[5])
Jak widać na rysunku,
zarówno wartość pod indeksem 1, jak i wartość pod indeksem 4, która została później dodana do tabeli, zostały wydrukowane. Nie znaleziono wartości w indeksie 5, więc zwrócono zero.
Drukowanie całej listy za pomocą ipairs()
Najłatwiejszym sposobem wydrukowania całej listy jest użycie specjalnego typu pętli for, która wykorzystuje funkcję ipairs(). Wzór wygląda tak:
for index, value in ipairs(arrayName) do
-- Do something
end
Elementy wzoru są następujące:
> index: Odwołuje się do bieżącego indeksu, przez który przechodzi pętla. Może to być dowolna poprawna nazwa zmiennej. Ludzie często używają po prostu małej litery i.
> wartość: odwołuje się do wartości bieżącego indeksu. Może to być również dowolna poprawna nazwa zmiennej.
> in ipairs(arrayName): in jest słowem kluczowym i nie można go zmienić. ipairs() przyjmuje nazwę tablicy, z którą chcesz pracować.
Jeśli więc masz listę nazwisk graczy i chcesz wydrukować je w kolejności, może ona wyglądać tak:
local players = {"Ali", "Ben", "Cammy"}
for playerIndex, playerName in ipairs(players) do
print(playerIndex .. " is " .. playerName)
end
WSKAZÓWKA
Pętle ogólne
Czasami zobaczysz ten typ pętli określany jako pętla ogólna.
Foldery i ipary()
Naprawdę wygodnym sposobem użycia ipairs() jest modyfikacja wszystkiego w folderze. Możesz uzyskać listę wszystkich obiektów w folderze, w kolejności, używając GetChildren(), która zwraca tablicę. Załóżmy, że masz folder pełen części i chcesz, aby każda część w tym folderze zmieniła kolor na inny. Możesz użyć czegoś takiego jak ten fragment kodu:
local folder = workspace.Folder -- Make sure to use the name of your folder
local arrayTest = folder:GetChildren() -- GetChildren() returns an array
for index, value in ipairs(arrayTest) do
if value:IsA("BasePart") then -- checks to see it′s a part
value.Color = Color3.fromRGB(0, 0, 255)
print( "Object " .. index .. " is now blue")
end
end
Włącz światła w kuchni
W tym Wypróbuj sam masz kilka świateł w kuchni
, które powinny włączać się za pomocą tego samego przełącznika. Nauczyłeś się już wcześniej, że umieszczanie skryptu w każdym z tych świateł powoduje bałagan i utrudnia aktualizację. Możesz umieścić podpowiedź bliskości w każdym świetle, ale wtedy ludzie musieliby chodzić i włączać wszystkie światła jeden po drugim. Jednym ze sposobów organizowania obiektów jest umieszczenie ich w folderze i użycie pętli for w celu jednoczesnej aktualizacji wszystkiego w tym folderze, gdy ktoś przełączy przełącznik.
1. Znajdź część, która będzie działać jak światło. Na rysunku
mały szklany cylinder pełni rolę podpory dla soczewki światła. Wstaw SpotLight do części.
2. Aby zmodyfikować kierunek, w którym świeci SpotLight, w oknie Właściwości > Twarz użyj menu rozwijanego, aby wybrać właściwą powierzchnię, przy której światło będzie świecić w dół. W tym przykładzie jest to lewica.
Twój może być inny.
3. Przy wciąż zaznaczonym Reflektorze, we Właściwościach zwiększ Jasność i Zasięg, aż będą odpowiednie dla sceny.
4. Zduplikuj światło wokół swojej sceny. Możesz nawet użyć różnych modeli. Na rysunku
dysk został skopiowany do oświetlenia szynowego wokół sufitu tej kuchni.
5. Utwórz nowy folder o nazwie Lights i przenieś do niego wszystkie żarówki
Włącz i wyłącz światła
Ten skrypt przechodzi przez każdy obiekt w folderze Lights i sprawdza, czy ma reflektor. Jeśli znajdzie światło reflektorów, skrypt włącza je lub wyłącza.
1. W ServerScriptService utwórz nowy skrypt.
2. Utwórz nową zmienną odwołującą się do folderu Lights.
3. Utwórz drugą zmienną, aby uzyskać tablicę wszystkich elementów podrzędnych folderu:
local lightsFolder = workspace.Lights
local lights = lightsFolder:GetChildren()
4. Utwórz nową pętlę for za pomocą ipairs() i przekaż tablicę:
local lightsFolder = workspace.Lights
local lights = LightsFolder:GetChildren()
for index, lightBulb in ipairs(lights) do
end
5. Wewnątrz pętli for użyj FindFirstChildWhichIsA(), aby znaleźć SpotLight zagnieżdżone w żarówce:
local lightsFolder = workspace.Lights
local lights = LightsFolder:GetChildren()
for index, lightBulb in ipairs(lights) do
local spotLight = lightBulb:FindFirstChildWhichIsA("SpotLight")
end
WSKAZÓWKA
Świecący reflektor
Jeśli używasz części, możesz również zmienić materiał na neonowy, aby wyglądał na świecący.
6. Ustaw następujące trzy warunki:
A. Jeśli reflektor zostanie znaleziony, a światło jest wyłączone, włącz funkcję SpotLight.
B. Jeśli reflektor zostanie znaleziony, a światło jest włączone, wyłącz SpotLight.
C. Jeśli pętla znajdzie w folderze coś, co nie ma SpotLight, wypisz "Not a lightbulb".
Spróbuj to zrobić samodzielnie, zanim spojrzysz na poniższy kod:
local lightsFolder = workspace.Lights
local lights = LightsFolder:GetChildren()
for index, lightBulb in ipairs(lights) do
local spotLight = lightBulb:FindFirstChildWhichIsA("SpotLight")
if spotLight and not spotLight.Enabled then
spotLight.Enabled = true
lightBulb.Material = Enum.Material.Neon -- Makes it look glowy
elseif spotLight and spotLight.Enabled then
lightBulb.Material = Enum.Material.Glass
spotLight.Enabled = false
else
print ("Not a light")
end
end
Przetestuj swój kod, włączając niektóre światła, wyłączając inne i wrzucając losową część do folderu Lights. Jeśli działa zgodnie z przeznaczeniem, umieść swój kod obok funkcji, która będzie uruchamiana, gdy ktoś wejdzie w interakcję z monitem o bliskość, jak pokazano w ciągu ostatnich kilku godzin. Jeśli nie pamiętasz, jak to zrobić, spójrz na swój poprzedni kod.
Wyszukiwanie wartości na liście i drukowanie indeksu
Załóżmy, że masz grupę klientów czekających w kolejce na swój stolik. Jeden z nich podchodzi i chce poznać swoje miejsce w kolejce. Znasz nazwisko klienta, ale nie znasz jego numeru. W tym przypadku lista oczekujących to po prostu kolejna tablica. Możesz ponownie użyć ipairs, aby wyszukać miejsce klienta, sprawdzając pasującą wartość:
local waitingList = {"Ana", "Bruce", "Casey"}
-- Let's find Casey's place in line
for placeInLine, customer in ipairs(waitingLi st) do
if customer == "Casey" then
print(customer .. " is " .. placeInLine)
end
end
Usuwanie wartości z tablicy
Aby usunąć wartość, na przykład jeśli gracz użył przedmiotu lub ktoś z listy aktywnych graczy opuścił grę, użyj table.remove(arrayName, index). Ta funkcja albo usuwa ostatnią wartość z tabeli, albo usuwa ją w określonym indeksie, w zależności od tego, czy oba parametry są używane.
local playerInventory = {}
table.insert(playerInventory, "Health Pack")
table.insert(playerInventory, "Stamina Booster")
table.insert(playerInventory, "Cell Key")
table.remove(playerInventory) -- No index, so last item will be removed
table.remove(playerInventory, 2) -- Will remove the second item
Drugi parametr dla table.remove() akceptuje tylko indeks numeryczny. Wpisanie czegoś takiego jak table.remove(playerItems, "Health Pack") zwraca błąd. Możesz spróbować wydrukować wyniki z tabeli, aby potwierdzić, że wszystko działa zgodnie z oczekiwaniami. Gdy element zostanie usunięty z tablicy, pozostałe wartości zostaną przesunięte, aby wypełnić lukę. Możesz to przetestować, drukując tablicę przed i po usunięciu elementu. Oczywiście nie chcemy wpisywać kodu do drukowania tablicy więcej niż raz, więc w poniższym fragmencie kodu jest to część funkcji, którą można wywoływać tak często, jak chcesz:
local function printArray(arrayToPrint)
for index, value in ipairs(arrayToPrint) do
print("Index " .. index .. " is " .. value)
end
end
local playerInventory = {"Health Pack", "Stamina Booster", "Cell Key"}
printArray(playerInventory)
table.remove(playerInventory, 2) -- Will remove the second item
printArray(playerInventory)
Na rysunku
widać, że pierwotnie indeks 2 to Stamina Booster, ale po usunięciu tej wartości indeks 2 staje się Cell Key.
Numeryczne dla pętli i tablic
Wspomniano wcześniej, że jedna nazwa pętli używającej ipairs() jest ogólną pętlą for. Typ pętli for, którego użyłeś w Godzinie 8, nazywa się numeryczną pętlą for. Jeśli pomoże ci to zapamiętać, która jest która, pamiętaj, że numeryczne pętle for używają liczb do kontrolowania momentu rozpoczęcia i zakończenia. Pętle numeryczne for mogą być łatwo używane również z tablicami. Przeanalizujmy kilka przykładów.
Znajdowanie i usuwanie wszystkich wartości za pomocą pętli for
Podczas gdy poprzedni kod mógł usunąć tylko pierwsze wystąpienie znalezionej wartości, ten fragment kodu znajdzie i usunie wszystkie wystąpienia wartości z tablicy. Pamiętaj, że usunięcie elementów powoduje przesunięcie późniejszych indeksów. Zamiast zaczynać od początku tablicy, zacznij od końca, aby uniknąć przypadkowego pominięcia wartości. Rozpoczynając od ostatniego indeksu, nie zmienisz indeksów wartości przed nim. Rozmiar tablicy można znaleźć za pomocą #arrayName i użyć jako początkowego numeru indeksu:
local playerInventory = {"Gold Coin", "Health Pack", "Stamina Booster", "Cell Key",
"Gold Coin", "Gold Coin"}
for index = #playerInventory, 1, -1 do
if play erInventory[index] == "Gold Coin" then
table.remove(playerInventory, index)
end
end
print(playerInventory)
Przeszukiwanie tylko sekcji tablicy
Innym razem, gdy prawdopodobnie chcesz użyć numerycznej pętli for, jest sytuacja, w której chcesz przejść tylko do części tablicy. Załóżmy, że musisz znaleźć nazwy pierwszych trzech statków, które przekroczą linię mety wyścigu kosmicznego:
local shipsRaced = {"A Bucket of Bolts", "Blue Moon", "Cats In Space",
"DarkAvenger12"}
local fastestThree = {}
for index = 1, 3 do
table.insert(fastestThree, shipsRaced[index])
end
print(fastestThree)
Powyższy fragment kodu pobiera pierwsze trzy wartości z tagushipRaced i dodaje je do fastThree.
Streszczenie
Tabele, z których jednym typem są tablice, pozwalają uporządkować doświadczenie. Dzięki tablicom możesz sporządzić listę wszystkich graczy w swojej grze i dać każdemu z nich nowy element awatara lub broń. Możesz także użyć tablic, aby utworzyć listę każdego elementu w folderze, który wymaga wprowadzenia w nim zmian. Gdy masz już wszystkie potrzebne elementy w tablicy, możesz użyć pętli for do iteracji po tablicy w dowolnym celu. Możesz wydrukować nazwy na liście, możesz zaktualizować kolor każdego obiektu w tablicy lub wykonać znacznie bardziej skomplikowany kod. Istnieją dwa rodzaje pętli for, których można używać z tablicą. Pętla for, której użyłeś w ciągu ostatniej godziny, nazywana jest numeryczną pętlą for. Jest to przydatne, gdy chcesz wprowadzić zmiany tylko w części tablicy lub pracujesz z bardzo dużymi tablicami. Drugi rodzaj pętli for nazywany jest ogólną pętlą for. W tym przypadku ipairs() służy do przeglądania całej tablicy w określonej kolejności.
Pytania i odpowiedzi
P. Jakie są inne powody używania numerycznej pętli for?
O. Na bardzo długich listach obiektów pętle numeryczne for działają nieco szybciej. Jeśli musisz iterować przez setki części, warto o tym pamiętać.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Tablice są rodzajem ________.
2. Numer przypisany do elementu w tablicy to _____ liczba.
3. W Lua indeks zaczyna się od numeru ___.
4. GetChildren() zwraca ___.
5. Czy ipairs() służy do tworzenia pętli ogólnej lub numerycznej?
Odpowiedzi
1. Tabeli
2. Indeks
3. Jeden. Zamiast tego inne języki kodowania mogą zaczynać się od 0.
4. Tablicę
5. Ogólne
Ćwiczenia
Jednym ze sposobów, w jaki programiści sprawiają, że ich doświadczenia są bardziej związane z prawdziwym światem, jest aktualizowanie zasobów w miarę upływu sezonów. W tym pierwszym ćwiczeniu sprawdź, czy potrafisz wymyślić, jak zmienić kolor sosny na rysunku z letniej zieleni na zimową biel.
Porady
* Tę sosnę można łatwo znaleźć w ToolBox
Przeszukaj drzewo, jeśli go nie widzisz. Wybraliśmy go do tego ćwiczenia, ponieważ jest to znany dobry model złożony z kilku części, więc nie musisz się martwić zamianą tekstur lub dodatkowymi skryptami w środku.
* Załóżmy, że w twojej grze jest cały las drzew.
* Będziesz potrzebować więcej niż jednej pętli.
Praca ze słownikami
Drugi rodzaj tabel, których się tutaj nauczysz, to słowniki. Ten typ tabeli umożliwia gromadzenie informacji w grupach i oznaczanie poszczególnych wpisów czymś innym niż tylko liczbą, co otwiera cały świat możliwości. Ta godzina obejmuje tworzenie słowników, dodawanie i usuwanie wartości oraz przeglądanie słowników za pomocą funkcji pairs(). Jednym ze sposobów korzystania ze słowników w tej godzinie jest śledzenie, kto ma najwięcej głosów w symulatorze głosowania. Osoba z największą liczbą głosów zostanie wyrzucona z wyspy. Pozwoli ci to ćwiczyć korzystanie zarówno z tablic, jak i słowników, aby śledzić uczestników i jakie są ich głosy.
Wprowadzenie do słowników
Słowniki to obiekty tabeli, które używają klucza do identyfikowania wartości zamiast indeksów numerowanych. Kluczem może być numer identyfikacyjny osoby, właściwości, takie jak zdrowie lub wytrzymałość, lub dowolny prawidłowy typ danych. Poniższa tabela przedstawia, jak może wyglądać słownik nazwisk graczy i ich wyników.
Słownik aktywnego gracza
Imię gracza jako klucz: Agatha: Billie: Mary Sue
Wynik jako wartość : 1000 : 150 : 1200
W formie słownikowej ta sama tabela może wyglądać następująco:
local activePlayer = {
Agatha = 1000,
Billie = 150,
["Mary Sue"] = 1200,
}
Używaj słowników, gdy chcesz oznaczyć wartości etykietami, a nie tylko wyświetlać je w określonej kolejności, jak robi to tablica.
Kodowanie słownika
Podobnie jak tablice, słowniki są tworzone za pomocą nawiasów klamrowych ({}). Kiedy tworzysz nowy słownik, często widzisz nawiasy rozdzielone, aby ludzie mogli odróżnić go od tablic, jak pokazano w poniższym fragmencie:
local newDictionary = {
}
Pary klucz-wartość są przechowywane w osobnych wierszach, po których następuje przecinek. Klucze i wartości mogą być dowolnym typem danych, w tym łańcuchami, liczbami, instancjami i innymi tabelami. Poniższy słownik używa ciągów znaków jako kluczy:
local inventory = {
Batteries = 4,
["Ammo Packs"] = 1,
["Emergency Rations"] = 0,
}
Formatowanie kluczy
Sposób formatowania klucza zależy od tego, czy jest to ciąg znaków, instancja czy coś innego. Jeśli ciągi są używane jako klucz, nie muszą być w nawiasach, chyba że w ciągu znajdują się spacje. Następnie należy je ująć w cudzysłowy i nawiasy:
local seedInventory = {
-- String keys with no spaces
Wheat = 1,
Rice = 4,
-- String key with spaces
["Sweet Potatoes"] = 3,
}
Jeśli jednak klucze są instancjami, takimi jak część lub ktoś w grze, należy to zaznaczyć w nawiasach. W poniższym przykładzie słownik używa wartości boolowskich do śledzenia, czy wszystkie wymagane kamienie portalu są aktywowane przed otwarciem portalu głównego:
local eastStone = workspace.EastStone
local westStone = workspace.WestStone
local northStone = workspace.NorthStone
local southStone = workspace.SouthStone
-- Each portal stone is an instance of a part, so it′s marked in brackets
local requiredPortalStones = {
[eastStone] = true,
[westStone] = true,
[northStone] = true,
[southStone] = false,
}
Słowniki są często używane do organizowania informacji o postaci lub obiekcie, gdzie są używane do etykietowania właściwości, takich jak nazwa lub poziom. W tym przypadku ani nawiasy, ani cudzysłowy nie są potrzebne. Poniższy przykład wykorzystuje słownik do śledzenia imienia i poziomu postaci:
local hero = {
Name = "Maria",
Level = 1000,
}
OSTRZEŻENIE
Nie mieszaj kluczy i indeksów.
Po utworzeniu tabeli pamiętaj o stosowaniu par klucz-wartość lub wartości indeksowanych. Nigdy nie używaj obu w tej samej tabeli. Łączenie kluczy i indeksów w tej samej tabeli może prowadzić do błędów.
Używanie wartości słownikowych
Aby użyć pojedynczych wartości słownikowych w kodzie, wpisz nazwę słownika, po której następuje klucz w nawiasach, tak samo jak w przypadku tablic - na przykład nazwa_słownika[klucz]. Lub, jeśli pracujesz z ciągami znaków, możesz również użyć notacji kropkowej:
local hero = {
Name = "Maria",
Level = 1000,
}
-- Remember that Name is a string and can be accessed with brackets
print ( "The hero's name is " .. hero["Name"] )
-- Or you can use dot notation
print ( "The hero's nam e is " .. hero.Name )
WSKAZÓWKA
Notacja kropkowa działa tylko z klawiszami łańcuchowymi.
Po raz kolejny notacja kropkowa działa tylko w przypadku łańcuchów, ale jest to coś, co zobaczysz całkiem sporo.
Użyj unikalnych kluczy
Lua nie powstrzyma Cię przed ponownym użyciem tego samego klucza. Pamiętaj o tym podczas kodowania. W poniższym przykładzie oryginalna wartość nazwy klucza zostanie nadpisana, a druga wartość podana dla nazwy klucza zostanie wydrukowana:
local hero = {
Name = "Maria",
Level = 1000,
Name = "Aya",
}
-- Will print Aya. The first value has been overwritten.
print ( "The her o's name is " .. hero.Name)
Dodawanie i usuwanie ze słowników
Aby dodać parę klucz-wartość do istniejącego słownika, użyj następującej formuły:
dictionaryName[key] = value
Lub jeśli pracujesz z łańcuchami:
dictionaryName.String = value
Dodawanie graczy do słownika, gdy dołączają do gry, a następnie rozpoczynanie ich z 0 punktami, może wyglądać następująco: playerPoints.Points = 0
Bądź ostrożny! Jak wspomniano wcześniej, jeśli klucz już istnieje, istniejąca wartość zostanie nadpisana.
Usuwanie par klucz-wartość
Aby usunąć parę klucz-wartość ze słownika, ustaw wartość klucza na zero. Spowoduje to usunięcie klucza:
local lightBulb = model.SpotLight
local flashLight = {
Brightness = 6,
[lightBulb] = "Enabled",
}
-- Remove string keys
flashLight.Brightness = nil
-- Remove other keys
flashLight[lightBulb] = nil
Oznacza to również, że jeśli kiedykolwiek próbujesz uzyskać wartość ze słownika i otrzymujesz tylko zero, oznacza to, że szukasz czegoś, co nie istnieje.
Dodaj nowych graczy do słownika
W tym Wypróbuj sam dodajesz nazwisko gracza do słownika, kiedy dołącza, a następnie przypisujesz go do drużyny. Jeśli używasz pary klucz-wartość, która nie została wcześniej dodana, zostanie ona dodana automatycznie.
1. W ServerScriptService utwórz nowy skrypt.
2. Uzyskaj usługę Players i utwórz pusty słownik:
Players = game:GetService("Players")
-- Empty dictionary
local teams= {
}
3. Dodaj nową funkcję przydzielania drużyn i dołącz parametr dla nowego gracza. Połącz funkcję ze zdarzeniem Players.PlayerAdded:
Players = game:GetService("Players")
local teams= {
}
-- Assign player to "Red" team
local function assignTeam(newPlayer)
end
Players.PlayerAdded:Connect(assignTeam)
4. W funkcji dodaj zmienną, aby uzyskać nazwę gracza:
-- Assign player to "Red" team
local function assignTeam(newPlayer)
local name = newPlayer.Name
end
Players.PlayerAdded:Connect(assignTeam)
5. Wstaw nazwę do słownika teamAssignments jako klucz i ustaw wartość na "Czerwony":
-- Assign player to "Red" team
local function assignTeam(newPlayer)
local name = newPlayer.Name
teams.name = "Red"
end
Players.PlayerAdded:Connect(assignTeam)
6. Użyj name, aby wydrukować nazwę gracza i teamAssignment[name], aby wydrukować wartość klucza:
Players = game:GetService("Players")
local teams = {
}
-- Assign player to "Red" team
local function assignTeam(newPlayer)
local name = newPlayer.Name
teams.name = "Red"
print( name .. " is on " .. teams.name .. " team. ")
end
Players .PlayerAdded:Connect(assignTeam
Praca ze słownikami i parami
pairs() może być używany do pracy z kluczem elementu słownika, wartością lub jednym i drugim. W poniższej pętli for pierwsza zmienna jest kluczem. Druga zmienna to wartość. Słownik, z którym chcesz pracować, jest przekazywany do pairs():
local inventory = {
["Gold Bricks"] = 43,
Carrots = 3,
Torches = 2,
}
print("You have:")
for itemName, itemValue in pairs (inventory) do
print(itemValue, itemName)
end
WSKAZÓWKA
Przecinek zamiast kropek
Jeśli drukujesz tylko dwie zmienne, możesz użyć przecinka zamiast dwóch kropek.
Zwracanie wartości z tabel
Możesz przeszukiwać tabelę za pomocą funkcji pairs() lub ipairs() w poszukiwaniu połowy dowolnego elementu tabeli, takiego jak klucz lub wartość, aby znaleźć i zwrócić drugą połowę. Poniższy fragment kodu przeszukuje słownik nazw, aby znaleźć wśród nich szpiega:
local friendOrSpy = {
Angel = "Friend",
Beth = "Spy",
Cai = "Friend",
Danny = "Friend",
}
-- Searches a given dictionary to find the spy
local function findTheSpy(dictionaryName)
for name, loyalty in pairs(dictionaryName) do
if loyalty == "Spy" then
return name
end
end
end
local spyName = findTheSpy(friendOrSpy)
print("The spy is " .. spyName)
Zagłosuj na nich z wyspy!
Tu będziesz udawać, że głosujesz na kogoś z wyspy. Końcowym celem tego ćwiczenia jest zebranie imienia każdego gracza biorącego udział w doświadczeniu, a następnie stworzenie sposobu, w jaki każdy może głosować na to, kto powinien zostać wyrzucony z wyimaginowanej wyspy. Aby rozpocząć, zacznij w myślach rozkładać problemy, które musisz rozwiązać, aby stworzyć ten skrypt. Kiedy zaczynasz pracować nad dłuższymi skryptami, pomocna może być lista rzeczy do zrobienia. Oto kilka problemów do rozwiązania:
* Przed głosowaniem musi być wystarczająco dużo czasu, aby wszyscy gracze dołączyli.
* Imię każdego gracza musi być przedstawione w taki sposób, aby gracze mogli z nim wchodzić w interakcje.
* Głosy oddane na każdą osobę muszą być śledzone.
* Wyniki muszą być podane na koniec głosowania.
Są inne rzeczy, które możesz rozwiązać, ale na razie wystarczy lista do pracy.
Konfiguracja
Pierwszy problem zostanie rozwiązany poprzez umożliwienie graczom kliknięcia przycisku, gdy będą gotowi do rozpoczęcia głosowania. W bardziej złożonym doświadczeniu głosowanie może nastąpić po serii mini-gier lub czegoś w tym stylu. Aby rozwiązać drugi problem, po rozpoczęciu głosowania pojawi się nowy zestaw przycisków reprezentujących każdego gracza.
W tym przykładzie ważne są nazwy ProximityPrompts. Monity o różnych nazwach będą używane do różnych rzeczy:
1. Skonfiguruj część, która będzie działać jako pierwszy przycisk rozpoczynający głosowanie:
A. Wstaw ProximityPrompt o nazwie StartVote.
B. Ustaw HoldDuration na 1.
2. Skonfiguruj drugą część, która będzie działać jako przycisk, który będzie zawierał imię gracza:
A. Wstaw ProximityPrompt o nazwie AddVote.
B. Ustaw HoldDuration na 0,5.
C. Po skonfigurowaniu przenieś przycisk do ServerStorage, gdzie można wykonać kopie.
Nie przesuwaj przycisku StartVote; to musi być tam, gdzie ludzie mogą to zobaczyć.
Kodowanie skryptu
Podczas wykonywania tego ćwiczenia używasz wielu tabel, zarówno tablic, jak i słowników. Nowi gracze zostaną dodani do tablicy o nazwie activePlayers. Po rozpoczęciu głosowania każdy, kto otrzyma głos, zostanie dodany do słownika wraz z liczbą posiadanych głosów.
Ustaw przyciski
Pamiętasz różne problemy wspomniane wcześniej, które należy rozwiązać? Rozpoczynając pracę nad większymi skryptami, lepiej podzielić skrypt na sekcje z poszczególnymi funkcjami przeznaczonymi do rozwiązywania unikalnych problemów. Zaczynasz od zebrania nazwisk wszystkich graczy i utworzenia przycisków dla każdego gracza:
1. W ServerScriptService dodaj nowy skrypt.
2. Utwórz zmienne dla następujących elementów:
A. Pamięć serwera
B. Usługa ProximityPrompt
C. Obsługa graczy
D. Czas, w którym gracze mają oddać swoje głosy
E. Tablica do przechowywania wszystkich aktywnych graczy
F. Słownik do przechowywania oddanych głosów
local ServerStorage = game:GetService("ServerStorage")
local ProximityPromptService = game:GetService("ProximityPromptService")
local PlayersService = game:GetService("Players")
local VOTING_DURATION = 30
local activePlayers = {}
local votes = {
}
3. W tym momencie zaczynasz dzielić swój kod na mniejsze rozwiązania. Utwórz nową funkcję, która dodaje graczy do tablicy activePlayers po dodaniu ich do gry. Użyj zdarzenia PlayerAdded, aby wywołać funkcję:
local function onPlayerAdded(player)
table.insert(activePlayers, player)
end
PlayersService.PlayerAdded:Connect(onPlayerAdded)
4. Utwórz nową funkcję, która tworzy przycisk dla każdej instancji gracza w tablicy active-Players. Ta funkcja jest wywoływana później w skrypcie z zachętą StartVote:
local function onPlayerAdded(player)
table.insert(activePlayers, player)
end
local function makeButtons()
for index, player in pairs(activePlayers) do
-- Use the name of your button in the next line
local newBooth = ServerStorage.Button:Clone()
newBooth.Parent = workspace
end
end
PlayersService.PlayerAdded:Connect(onPlayerAdded)
5. Znajdź ProximityPrompt w przycisku i ustaw ActionText tak, aby pasował do imienia gracza:
local function makeButtons()
for index, player in pairs(activePlayers) do
local newBooth = ServerStorage.VotingBooth:Clone()
local proximityPrompt =
newBooth:FindFirstChildWhichIsA("ProximityPrompt")
local playerName = player.Name
proximityPrompt.ActionText = playerName
newBooth.Parent = workspace
end
end
6. Dodaj podświetlone dodatki do kodu, aby nieco rozsunąć przyciski. Więcej informacji na temat pozycjonowania obiektów znajdziesz w Części 14, "Kodowanie w przestrzeni świata 3D":
local function makeButtons()
local position = Vector3.new(0,1,0)
local DISTANCE_APART = Vector3.new(0,0,5)
for index, player in pairs(activePlayers) do
local newBooth = ServerStorage.Button:Clone()
local proximityPrompt =
newBooth:FindFirstChildWhichIsA("ProximityPrompt")
local playerName = player.Name
proximityPrompt.ActionText = playerName
position = position + DISTANCE_APART
newBooth.Position = position
newBooth.Parent = workspace
end
end
7. Dodaj trzecią funkcję połączoną ze zdarzeniem PromptTriggered. Wewnątrz użyj zachęty zbliżeniowej StartVote, aby wywołać makeButtons():
local function makeButtons()
-- Earlier code
end
local function onPromptTriggered(prompt, player)
if prompt.Name == "StartVote" then
makeButtons()
end
end
PlayersService.PlayerAdded:Connect(onPlayerAdd ed)
ProximityPromptService.PromptTriggered:Connect(onPromptTriggered)
WSKAZÓWKA
Jeśli to możliwe, utrzymuj połączenia zdarzeń razem.
Zwróć uwagę, że wszystkie połączenia zdarzeń znajdują się na dole skryptu. Dzięki temu wszystko jest uporządkowane.
Testowanie dla wielu osób
Podczas testowania kodu, który ma być używany przez wielu graczy, musisz użyć Network Simulator zamiast po prostu grać lub grać tutaj. Network Simulator pozwala skonfigurować dowolną liczbę fałszywych osób, którymi możesz następnie sterować, aby przetestować swoją grę:
1. Na karcie Test znajdź sekcję zatytułowaną Klienci i serwery.
2. Ustaw dolne menu rozwijane na dwóch lub więcej graczy, jak pokazano na rysunku
3. Kliknięcie Start spowoduje wyświetlenie nowej instancji Studio reprezentującej serwer i dodatkowe okno dla każdego udawanego gracza. Okna odtwarzacza mają niebieską obwódkę
,
podczas gdy okno serwera ma zieloną obwódkę.
4. Kliknij dowolne z niebieskich okienek, aby sterować tym fałszywym bohaterem. Podczas testowania wszelkie błędy i drukowane komunikaty pojawiają się w oknie Dane wyjściowe serwera.
5. Wejdź w interakcję z StartVote i upewnij się, że dla każdego gracza testowego pojawił się przycisk.
WSKAZÓWKA
Pozycjonowanie obiektów
Przyciski powinny pojawić się nieco powyżej środka twojego doświadczenia, 0,0,0. W dalszej części dowiesz się, jak kontrolować położenie obiektów dodawanych do obszaru roboczego.
6. Aby zatrzymać test, kliknij Oczyść.
Dodawanie i liczenie głosów
Gdy przyciski głosowania są już uruchomione, głosy muszą być śledzone, a wyniki wyświetlane po zakończeniu głosowania. W tym przykładzie podajesz określoną ilość czasu, przez jaki gracze mogą głosować, a następnie pokazujesz wyniki:
1. W tym samym skrypcie, powyżej onPromptTriggered(), utwórz nową funkcję o nazwie showVotes, która wyświetla wszystkie wartości ze słownika głosów:
local function showVotes()
for playerName, value in pairs(votes) do
print(playerName .. " has " .. value .. " votes.")
end
end
2. W onPromptTriggered() rozpocznij odliczanie po rozpoczęciu głosowania i wywołaj showVotes po zakończeniu:
local function onPromptTriggered(prompt, player)
if prompt.Name == "StartVote" then
makeButtons()
for countdown = VOTING_DURATION, 0, -1 do
print(c ountdown .. " seconds left")
wait(1.0)
end
showVotes()
end
end
WSKAZÓWKA
Dalsza organizacja kodu
Jeśli chcesz, możesz również ustawić odliczanie jako własną funkcję. To pozwoliłoby na wywołanie go w inny sposób niż tylko monit.
3. Również w onPromptTriggered() dodaj drugi warunek, który nasłuchuje Proximity Prompts o nazwie AddVote:
local function onPromptTriggered(prompt, player)
if prompt.Name == "StartVote" then
makeButtons()
-- Countdown code
showVotes()
elseif prompt.Name == "AddVote" then
end
end
4. Uzyskaj nazwę gracza, na którego głosowano, z ActionText, gdzie została użyta do oznaczenia przycisku:
local function onPromptTriggered(prompt, player)
if prompt.Name == "StartVote" then
makeButtons()
-- Countdown code
showVotes()
elseif prompt.Name == "AddVote" then
local chosenPlayer = prompt.ActionText
end
end
5. Jeśli w słowniku głosów nie ma jeszcze wpisu o tej nazwie, dodaj nazwę gracza jako klucz i ustaw jego punkty na 1. Jeśli klucz istnieje, weź aktualną wartość i dodaj do niej:
local function onPromptTriggered(prompt, player)
if prompt.Name == "StartVote" then
makeButtons()
for countdown = VOTING_DURATION, 0, -1 do
print(countdown .. " seconds left")
wait(1.0)
end
showVotes()
elseif prompt.Name == "AddVote" then
local chosenPlayer = prompt.ActionText
print("A vote for " .. chosenPlayer)
if not votes[chosenPlayer] then
votes[chosenPlayer] = 1
else
votes[chosenPlayer] = votes[chosenPlayer] + 1
end
-- Optional check for debugging purposes
else
print("Prompt not found")
end
end
WSKAZÓWKA
Jeśli wszystko inne zawiedzie
W zestawie znajduje się kilka instrukcji print oraz instrukcja else, której można użyć do przetestowania kodu. Końcowe else, które jest uruchamiane, jeśli żaden inny warunek nie zostanie spełniony, może być bardzo pomocne w upewnieniu się, że funkcja została wywołana zgodnie z oczekiwaniami.
6. Użyj Network Simulator z co najmniej dwoma graczami, aby przetestować kod i poszukaj wyników w danych wyjściowych serwera.
Streszczenie
We wszystkich doświadczeniach Roblox tabele znajdują się za kulisami informacji o śledzeniu. Tablice służą do tworzenia list obiektów, a przechowywane informacje zawsze będą uporządkowane. Chociaż słowniki służą do śledzenia informacji o obiektach i właściwościach, w przeciwieństwie do tablic, wpisy w nich zawarte nie gwarantują zachowania określonej kolejności. Aby przeglądać słownik, chcesz użyć pairs() zamiast ipairs(). Te dwie funkcje są bardzo podobne, ale ipairs() działa tylko z tablicami.
Pytania i odpowiedzi
P. Widziałem, jak pairs() była używana z tablicami, więc dlaczego po prostu nie używać pairs() zarówno z tablicami, jak i ze słownikami?
O. Jedną z korzyści płynących z używania tablic jest to, że przechowują one elementy w odpowiedniej kolejności. pairs() nie gwarantuje zwrócenia każdego obiektu w kolejności, podczas gdy ipairs() tak.
P. Jeśli pairs() może technicznie działać z tablicami, dlaczego pairs() nie może działać ze słownikami?
O. ipairs() wymaga do działania uporządkowanego indeksu. Słowniki tego nie mają. Z drugiej strony, pary akceptują każdy prawidłowy typ danych jako klucz, w tym indeksy.
Warsztat
Skoro już skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Zamiast używać indeksów, słowniki używają ___.
2. Prawda czy fałsz: Słowniki przechowują informacje w określonej kolejności.
3. Aby przeglądać słownik, użyj funkcji ____.
4. Jeśli instancja jest używana jako klucz, czy musi zawierać nawiasy lub cudzysłowy?
5. Aby usunąć parę klucz-wartość ze słownika, ustaw wartość na ___.
6. Dlaczego showVotes() musi być powyżej onPromptTriggered()?
Odpowiedzi
1. Kluczy
2. Fałsz. Chociaż słowniki mogą czasami zwracać wartości w kolejności, w jakiej zostały zapisane, nie jest to w żaden sposób gwarantowane.
3. pairs()
4. Jeśli instancja jest używana jako klucz, potrzebuje tylko nawiasów.
5. Brak
6. Ponieważ kod jest czytany od góry do dołu, metoda showVotes() musi zostać utworzona przed wywołaniem metody onPromptTriggered().
Ćwiczenia
Wcześniej w tej godzinie osoba została przydzielona do zespołu "Czerwonych" po dołączeniu do doświadczenia. Czy w tym ćwiczeniu możesz wymyślić, jak naprzemiennie przydzielać zadania zespołowe między "Czerwonymi" a "Niebieskimi"? Wydrukuj członków każdego zespołu.
Porady
- Przetestuj za pomocą Network Simulator.
- Instancje nie mogą łączyć się z ciągami znaków, ale nazwa instancji tak.
Klient kontra serwer
Każde doświadczenie Roblox ma dwie strony. Jedna strona to miejsce, w którym ludzie wchodzą w interakcje z doświadczeniem, a druga strona to chmura, która kontroluje wszystko. Ta godzina dotyczy tego, jak te dwie strony współpracują ze sobą i jak przesyłane są między nimi wiadomości. Pod koniec godziny tworzysz sklep, w którym gracze mogą kliknąć przycisk, aby kupić drewno opałowe do gry z zasobami utworzonej w części 9. Zrozumienie klienta i serwera. Pierwsza strona, po której ludzie i gracze wchodzą w interakcje ze światem, to strona klienta. Klient to indywidualne urządzenie, którego ktoś używa do dołączenia do gry, niezależnie od tego, czy jest to Mac, PC, telefon, tablet, czy nawet konsola VR. Niektóre rzeczy dotyczące doświadczenia są obliczane na indywidualnym urządzeniu klienckim, podczas gdy innymi zajmuje się super potężny sprzęt Roblox zwany serwerem. Serwer i klient zawsze ze sobą rozmawiają. Serwer mówi klientowi, jaki jest ogólny świat, a klient mówi serwerowi, co dana osoba robi w świecie. Zazwyczaj chcesz, aby ważne informacje, takie jak wyniki, pieniądze w grze i poziomy postępów, były obsługiwane przez serwer. Serwer jest bezpieczniejszy niż klient i trudniej się do niego włamać. Tymczasem klient zajmuje się rzeczami, które dotyczą tylko konkretnej osoby korzystającej z urządzenia lub gdy ważne jest, aby mieć jak najmniejsze opóźnienie, na przykład pokazywanie jej własnego wyniku lub sterowanie kamerą.
Praca z GUI
Do tej pory pracowaliśmy tylko z kodem po stronie serwera wpisywanym w obiektach Script i wszyscy na świecie widzą to samo. Następnym krokiem jest rozpoczęcie tworzenia kodu, który pokazuje osobie na każdym kliencie informacje, które tylko ona widzi, takie jak aktualny wynik, postęp w zadaniu, poziom zdrowia i ilość pieniędzy. Takie informacje są wyświetlane w tak zwanym graficznym interfejsie użytkownika lub GUI, takim jak ten pokazany po lewej stronie na rysunku
Dzięki graficznym interfejsom użytkownika możesz także tworzyć przyciski ekranowe, które pozwalają budować rzeczy, takie jak sklepy. Większość elementów GUI, które mogą być widoczne tylko dla lokalnego klienta, powinna być umieszczona w Starter-GUI, a kod należy wpisać w obiekcie LocalScript zamiast w obiekcie Script. Wszystko w StarterGUI jest powielane dla każdego, kto dołączy do doświadczenia.
Utwórz GUI z nazwą gracza
Aby pokazać, jak to może wyglądać, gdy wszyscy na serwerze widzą informacje dostosowane specjalnie dla nich, w tym Wypróbuj sam tworzysz GUI z nazwą gracza.
Konfiguracja
1. W Eksploratorze wybierz StarterGUI.
2. Wstaw nowy ScreenGui
Będzie to pojemnik na wszystkie przyciski i etykiety, które chcesz utworzyć.
3. Wewnątrz ScreenGui dodaj TextLabel. Zmień nazwę TextLabel PlayerName, jak pokazano na rysunku
Scenariusz
Używasz LocalScript zamiast zwykłego obiektu Script. Obiekt Script jest przeznaczony dla kodu po stronie serwera:
1. Po wybraniu ScreenGui wstaw nowy obiekt LocalScript
WSKAZÓWKA
Umieszczanie skryptu GUI
Skrypty GUI muszą znajdować się wewnątrz StarterGUI. ServerScriptService może uzyskiwać dostęp tylko do obiektów skryptów serwera.
2. W LocalScript utwórz zmienne dla usługi Players i ScreenGui.
3. Utwórz nową zmienną dla TextLabel:
local Players = game:GetService("Players")
local screenGui = script.Parent
local textLabel = screenGui.PlayerName
4. Zdobądź lokalnego gracza. W LocalScripts można to łatwo zrobić za pomocą Players.LocalPlayer:
local Players = game:GetService("Players")
local screenGui = script.Parent
local textLabel = screenGui.PlayerName
local localPlayer = Players.LocalPlayer
5. Set TextLabel′s Text property to the name of the local player:
local Players = game:GetService("Players")
local screenGui = script.Parent
local textLabel = screenGui.PlayerName
local localPlayer = Players.LocalPlayer
textLabel.Text = localPlayer.Name
6. Użyj symulatora sieci, aby przetestować swój kod. Zobaczysz, że imię każdej osoby jest wyświetlane na ekranie.
Zrozumienie funkcji zdalnych
Należy pamiętać, że serwer i klient nie mają dostępu do tych samych informacji. Istnieją pewne foldery, do których klient nie ma dostępu i odwrotnie. Oto kilka przykładów:
Obiekt: Serwer: Klient
Przestrzeń robocza: tak: tak
ServerScriptService: tak: nie
SerwerStorage: tak: nie
ReplicatedStorage: tak: tak
Ponadto serwer i klient nie udostępniają informacji. Niektórzy nazywają to podziałem serwer/klient, ale można to sobie wyobrazić tak, jakby istniała ściana między tymi dwoma środowiskami, oddzielająca je od siebie. Aby uzyskać informacje z jednej strony na drugą, używa się specjalnych przedmiotów do rzucania informacji przez ścianę. Można to zrobić za pomocą obiektów RemoteEvent i RemoteFunction, których skrypty i skrypty lokalne mogą używać do wzajemnej komunikacji. W tej godzinie omówione są RemoteFunctions, a następna godzina dotyczy różnych typów RemoteEvents.
Korzystanie z funkcji zdalnych
Jak wspomniano wcześniej, RemoteFunctions są zaprojektowane do wysyłania żądania przez granicę serwer-klient. Tym, co wyróżnia RemoteFunctions, jest to, że mogą również czekać na odpowiedź z drugiej strony, działając jako komunikator między klientem a serwerem. Zwykle jest to żądanie od lokalnego klienta, aby serwer coś zrobił, a następnie serwer odsyła wyniki. Funkcje RemoteFunctions muszą być tworzone w miejscu, w którym zarówno klienci, jak i serwer mają do nich dostęp - na przykład ReplicatedStorage
Tymczasem masz normalny skrypt serwera w ServerScriptService i LocalScript w Starter-PlayerScripts, jak pokazano na rysunku
Pobierz wiadomość z serwera i wydrukuj ją lokalnie: po stronie serwera skonfiguruj funkcję, która zwraca prosty ciąg znaków do wydrukowania. Powiąż funkcję z obiektem RemoteFunction, jak zaznaczono tutaj:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteFunction = ReplicatedStorage:WaitForChild("RemoteFunction")
local function sayHello()
local serverMessage = "Hello from the server"
return serverMessage
end
remoteFunction.OnServerInvoke = sayHello
Z funkcjami RemoteFunctions można w danym momencie powiązać tylko jedną funkcję. Po stronie lokalnej kod do wywołania (pośredniego wywołania) serwera wyglądałby tak:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteFunction = ReplicatedStorage:WaitForChild("RemoteFunction")
local messageFromServer = remoteFunction:InvokeServer()
print(messageFromServer)
Serwer do Klienta
Możliwe jest pójście w drugą stronę - od serwera do klienta do serwera. Jest to jednak dość ryzykowne i nie zostanie omówione w tej książce z następujących powodów:
* Jeśli klient zgłosi błąd, serwer również zgłosi błąd.
* Jeśli klient rozłączy się podczas wywoływania, wywołanie InvokeClient() spowoduje błąd.
* Jeśli klient nigdy nie zwróci wartości, serwer zawiesi się na zawsze.
Zrób sklep
Dobrym przykładem sytuacji, w których możesz potrzebować podwójnego sprawdzenia z serwerem i oczekiwania na odpowiedź, jest sytuacja, w której ktoś chce coś kupić. Klient klika przycisk, aby coś kupić, a następnie serwer sprawdza, czy rzeczywiście ma wystarczającą ilość pieniędzy i potwierdza zakup. Na potrzeby tego Wypróbuj sam korzystasz z systemu tabeli liderów, z którym pracowałeś wcześniej, i modyfikujesz go, aby umożliwić graczom wydawanie złota na kupowanie większej ilości kłód do palenia w ogniu
Konfiguracja
Ze względu na szybkość użyj wcześniej skonfigurowanego systemu tabeli wyników dla paliwa i ognia. Jeśli go nie masz, możesz użyć kodu w sekcji Część 11 dodatku, aby szybko go skonfigurować.
1. W ServerScriptService, PlayerStats daj ludziom początkową ilość złota równą 10, aby ułatwić testowanie:
local gold = Instance.new("IntValue")
gold.Name = "Gold"
gold.Value = 10
gold.Parent = leaderstats
2. W ReplicatedStorage dodaj nową instancję RemoteFunction o nazwie CheckPurchase
3. W ServerStorage dodaj nowy folder o nazwie ShopItems
4. W ShopItems dodaj obiekt Folder o nazwie 3Logs i dodaj trzy atrybuty pokazane po prawej stronie na rysunku. Tych nazw i wartości użyjesz w scenariuszu
WSKAZÓWKA
Przygotowanie sklepu na przyszłość
W bardziej zaawansowanym sklepie folder może być również używany do przechowywania modeli siatek, ikon graficznych i innych.
5. W StarterGUI dodaj
* Nowy ScreenGui o nazwie ShopGui.
* W ShopGui dodaj nowy TextButton o nazwie Buy3Logs
<
WSKAZÓWKA
Przenoszenie GUI
Aby przenieść GUI, możesz wybrać obiekty GUI w Eksploratorze, a następnie przenieść je i skalować.
6. Wybierz Buy3Logs i dodaj atrybut, jak pokazano na rysunku:
* Name: Rodzaj zakupu
* Value: 3 dzienniki
* Type: ciąg
LocalScript
LocalScripts dla przycisków GUI muszą być bezpośrednim elementem potomnym przycisku, na który mają wpływ. W LocalScript konfigurujesz kod wywołujący serwer i informujący osobę, czy jej zakup się powiódł lub czy potrzebuje więcej złota:
1. W przycisku Buy3Logs dodaj skrypt lokalny.
2. Uzyskaj informacje potrzebne do RemoteFunction, CheckPurchase:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local checkPurchase= ReplicatedStorage:WaitForChild("CheckPurchase")
3. Dla przycisku musisz uzyskać atrybut PurchaseType:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local checkPurchase= ReplicatedStorage:WaitForChild("CheckPurchase")
local button = script.Parent
local purchaseType = button:GetAttribute("PurchaseType")
4. Użyj opcji PurchaseType, aby utworzyć i przypisać domyślny tekst przycisku, a następnie ustawić czas odnowienia, przez jaki przycisk będzie dezaktywowany między zakupami:
local defaultText = "Buy " .. purchaseType
button.Text = defaultText
local COOLDOWN = 2.0
WSKAZÓWKA
Upewnij się, że przypisano domyślne wartości właściwości
Będziesz zmieniać właściwość Tekst kilka razy, więc chcesz się upewnić, że na początku skryptu przypisano domyślną wiadomość.
5. Utwórz nową funkcję, która będzie wywoływana po aktywacji przycisku:
local function onButtonActivated()
end
button.Activated:Connect(onButtonActivated)
6. Utwórz zmienną, aby wywołać serwer w celu wysłania typu zakupu i zatrzymania zwróconego potwierdzenia zakupu:
local function onButtonActivated()
local confirmationText = checkPurchase:InvokeServer(purchaseType)
end
7. Wyświetl tekst potwierdzenia podczas wyłączania przycisku, a następnie przywróć przycisk do normalnego stanu:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local checkPurchase = ReplicatedStorage:WaitForChild("CheckPurchase")
local button = script.Parent
local purchaseType = button:GetAttribute("PurchaseType")
local defaultText = "Buy ".. purchaseType
button.Text = defaultText
local COOLDOWN = 2.0
local function onButtonActivated()
local confirmationText = checkPurchase:InvokeServer(purchaseType)
button.Text = confirmationText
button.Selectable = false
wait(COOLDOWN)
button.Text = defaultText
button.Selectable = true
end
button.Activated:Connect(onButtonActivated)
Skrypt serwera
Strona serwera to miejsce, w którym chcesz wykonać wszystkie ciężkie czynności związane ze sprawdzaniem i aktualizowaniem statystyk. Gdy klient prześle to, co użytkownik chce kupić, serwer sprawdza, czy użytkownik ma wystarczającą ilość złota. Jeśli to zrobią, zakup zostanie dokonany, a tekst przycisku powie Zakup udany! Jeśli nie mają wystarczającej ilości złota, serwer odeśle wiadomość z informacją
Niewystarczająco dużo złota.
1. W ServerScriptService dodaj nowy skrypt.
2. Pomyśl o tym, co Twój skrypt musi zrobić i przygotuj odniesienia, które Twoim zdaniem będą potrzebne. Porównaj swoją pracę z następującym fragmentem:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local checkPurchase = ReplicatedStorage:WaitForChild("CheckPurchase")
local shopItems = ServerStorage.ShopItems
3. Utwórz funkcję o nazwie ConfirmPurchase z parametrami do przekazania w odtwarzaczu i typie zakupu. Powiąż potwierdzenie zakupu z funkcją RemoteFunction:
local function confirmPurchase(player, purchaseType)
end
checkPurchase.OnServerInvoke = confirmPurchase
4. Wewnątrz potwierdzenia zakupu sprawdź, ile złota posiada dana osoba:
local function confirmPurchase(player, purchaseType)
local leaderstats = player.leaderstats
local currentGold = leaderstats:FindFirstChild("Gold")
end
5. Użyj przekazanego typu zakupu, aby znaleźć przedmiot, który chcą kupić. Uzyskaj statystyki zasobów, które zostaną zaktualizowane w tabeli liderów, cenę przedmiotu i liczbę zasobów, które zostaną otrzymane:
local function confirmPurchase(player, purchaseType)
local leaderstats = player.leaderstats
local currentGold = leaderstats:FindFirstChild("Gold")
local purchaseType = shopItems:FindFirstChild(purchaseType)
local resourceStat =
leaderstats:FindFirstChild(purchaseType:GetAttribute("StatName"))
local price = purchaseType:GetAttribute("Price")
local numberToGive = purchaseType:GetAttribute("NumberToGive")
end
WSKAZÓWKA
Sprawdź swoją pracę
Powinieneś mieć tutaj cztery zmienne. Zwróć uwagę, w jaki sposób buyType:GetAttribute("StatName") jest przekazywany do shopItems:FindFirstChild().
6. Skonfiguruj zmienną dla wiadomości serwera, która zostanie odesłana po sprawdzeniu wszystkiego:
local function confirmPurchase(player, purchaseType)
local leaderstats = player.leaderstats
local currentGold = leaderstats:FindFirstChild("Gold")
local purchaseType = shopItems:FindFirstChild(purchaseType)
local resourceStat =
leaderstats:FindFirstChild(purchaseType:GetAttribute("StatName"))
local price = purchaseType:GetAttribute("Price")
local numberToGive = purchaseType:GetAttribute("NumberToGive")
local serverMessage = nil
return serverMessage
end
WSKAZÓWKA
Ustaw Nieokreślone wartości na zero
W tym kodzie wartość serverMessage zostanie określona w następnym kroku. Zamiast na razie pozostawiać zmienną bez wartości, ustaw ją na zero, aby było jasne, że wartość nie została omyłkowo pominięta.
7. Skonfiguruj warunki, aby sprawdzić, ile złota ma dana osoba i czy może kupić przedmiot. W zależności od wyników odeślij klientowi odpowiednią wiadomość i zaktualizuj ranking. Oto gotowy skrypt:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local checkPurchase = ReplicatedStorage:WaitForChild("CheckPurchase")
local shopItems = ServerStorage.ShopItems
local function confirmPurchase(player, purchaseType)
local leaderstats = player.leaderstats
local currentGold = leaderstats:FindFirstChild("Gold")
local purchaseType = shopItems:FindFirstChild(purchaseType)
local resourceStat =
leaderstats:FindFirstChild(purchaseType:GetAttribute("StatName"))
local price = purchaseType:GetAttribute("Price")
local numberToGive = purchaseType:GetAttribute("NumberToGive")
local serverMessage = nil
if currentGold.Value >= price then
currentGold.Value = currentGold.Value - price
resourceStat.Value += numberToGive
serverMessage = ("Purchase Successful!")
elseif currentGold.Value < price then
serverMessage = ("Not enough Gold")
else
serverMessage = ("Didn't find necessary info")
end
return serverMessage
end
checkPurchase.OnServerInvoke = confirmPurchase
8. Przetestuj wszystko! Możesz upiększyć sklep, dodając obrazy do przedmiotów i stylizując wygląd i czcionkę tekstu i przycisków.
Streszczenie
Każde doświadczenie Roblox ma dwie strony, które łączą się, aby pokazać użytkownikom świat. Pierwsza strona to klient lokalny, czyli urządzenie takie jak komputer lub telefon, na którym ludzie wchodzą w interakcje z Robloxem. Druga strona to serwer, który dba o to, aby wszyscy przechodzili przez to doświadczenie w większości w ten sam sposób. Ogólnie rzecz biorąc, chcesz mieć pewność, że jak najwięcej utworzonego kodu pozostanie po stronie serwera, gdzie jest to bezpieczniejsze. Ostatnią rzeczą, jakiej chcesz, są ludzie wykorzystujący lokalnego klienta do dokonywania nieautoryzowanych zakupów lub aktualizacji swoich statystyk. Niektóre rzeczy są jednak specyficzne dla klienta. Jeśli jedna osoba otworzy witrynę sklepową lub dokona zakupu, nie chcesz, aby wszyscy uczestnicy musieli patrzeć na witrynę sklepową, dopóki nie skończą. Tak więc sklepy są przykładem czegoś, co chciałbyś zrobić lokalnie.
Pytania i odpowiedzi
P. Czy coś innego niż funkcja może być powiązana z funkcją RemoteFunction?
O. RemoteFunctions oczekują funkcji; próba powiązania czegoś takiego jak zmienna spowoduje błąd.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Urządzenie, którego ktoś używa do dołączenia do gry Roblox, to____.
2. Super wydajnym sprzętem obsługującym większość Robloxa jest ____.
3. Kod, który wprowadza tylko zmiany w tym, co widzi klient, jest wpisywany w obiekcie _____.
4. RemoteFunction jest
A. Rodzaj wydarzenia
B. Obiekt
C. Typ danych
5. Funkcji zdalnych można używać do ____-kierunkowej komunikacji między serwerem a klientem.
6. Co to znaczy przywoływać coś?
Odpowiedzi
1. Klient lokalny
2. Serwer
3. Lokalny skrypt
4. Obiekt
5. Dwu
6. Invoke oznacza wywołanie czegoś pośrednio.
Ćwiczenia
Jednym z problemów z obecnym sklepem jest to, że ceny przedmiotów nie są podane. Należy jednak pamiętać, że tylko jedna funkcja może być powiązana z RemoteFunction na raz. Utwórz drugą funkcję RemoteFunction i używaj jej do wyświetlania nie tylko nazw przedmiotów, które ludzie mogą kupować, ale także kosztów.
Pamiętaj, aby przetestować swój kod z wieloma przedmiotami na sprzedaż.
Zdarzenia zdalne: komunikacja jednokierunkowa
Część 11 obejmuje różnice między lokalnym klientem a serwerem działającym na sprzęcie Roblox. Obejmuje również jeden ze sposobów komunikowania się ponad podziałami. Ta część dotyczy drugiego sposobu wysyłania wiadomości.
Zdarzenia zdalne: ulica jednokierunkowa
Czasami wystarczy wysłać wiadomość od klienta do serwera lub odwrotnie, bez konieczności odpowiedzi. W takim przypadku zamiast używać RemoteFunction, powinieneś zamiast tego użyć RemoteEvent. Przypominamy, że obiekt RemoteEvent to obiekt, którego instancje można wstawić do obszaru roboczego, zazwyczaj w ramach ReplicatedStorage, gdzie zarówno klient, jak i serwer mają do niego dostęp .
Istnieją trzy główne sposoby wykorzystania RemoteEvents do wysłania sygnału:
- Od serwera do konkretnego klienta
- Z serwera do wszystkich dołączonych klientów
- Od klienta do serwera
Komunikacja z serwera do wszystkich klientów
Podstawowa formuła wysyłania wiadomości z serwera do wszystkich klientów jest następująca:
remoteEventName:FireAllClients(variableName)
Informacje, które należy wysłać do klienta, są przekazywane do FireAllClients(informationHere). Po stronie klienta konfigurujesz jedną lub więcej funkcji do wywołania, gdy zdarzenie zostanie uruchomione:
local function firstFunction(incomingInfo)
-- Do stuff
end
local function secondFunction(incomingInfo)
-- Do different stuff
end
-- Connect both functions to onClientEvent
remoteEventName.OnClientEvent:Connect(firstFunction)
remoteEventName.OnClientEvent:Connect(secondFunction)
Szybkie odliczanie
Zacznijmy od czegoś znajomego do zademonstrowania: odliczania. To dobry przykład, ponieważ serwer musi przekazywać informacje wszystkim na serwerze, ale nie potrzebuje żadnych informacji z powrotem. Do tej pory pokazaliśmy odliczanie na dwa sposoby. Pierwszy był po prostu w Output, gdzie klient go nie widzi. Drugi był wyświetlany na 3D GUI w przestrzeni gry. Problem polega na tym, że ludzie mogą od tego odejść i tego nie zobaczyć. Jeśli chcesz mieć pewność, że wszyscy uczestnicy doświadczenia zobaczą odliczanie, jak pokazano na rysunku , można to zrobić za pomocą zdarzenia RemoteEvent.
1. W ReplicatedStorage dodaj RemoteEvent o nazwie CountdownEvent.
2. W ServerScriptService dodaj skrypt. Utwórz referencje dla ReplicatedStorage i RemoteEvent:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
lokalne zdarzenie odliczania = ReplicatedStorage:WaitForChild("CountdownEvent")
3. Utwórz odliczanie za pomocą pętli for. W każdej iteracji uruchamiaj zdarzenie i cofaj bieżące odliczanie:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local countdownEvent = ReplicatedStorage:WaitForChild("CountdownEvent")
local secondsRemaining = 20
for count = secondsRemaining, 1, -1 do
countdownEvent:FireAllClients(count)
wait(1.0)
end
4. Uruchom stronę klienta; w StarterGui dodaj nowy ScreenGui i TextLabel. Tutaj wyświetlisz odliczanie.
5. W ScreenGui dodaj LocalScript. Tutaj umieścisz kod, który chcesz uruchamiać za każdym razem, gdy zdarzenie jest uruchamiane. W przykładzie pokazanym na rysunku nazwano go DisplayManager.
6. Skonfiguruj swoje referencje; następnie utwórz nową funkcję, która będzie wywoływana po uruchomieniu zdarzenia:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local countdownEvent = ReplicatedStorage:WaitForChild("CountdownEvent")
-- Get the ScreenGui and the TextLabel
local screenGui = script.Parent
local countDisplay = screenGui.ShowCountdown
local function onTimerUpdate(count)
-- Set the TextLabel to match the incoming count
countDisplay.Text = count
end
-- Call "onTimerUpdate()" when the server fires the remote event
countd ownEvent.OnClientEvent:Connect(onTimerUpdate)
WSKAZÓWKA
Sprawdzanie Twojego kodu
Pierwszą rzeczą, którą należy sprawdzić, są referencje. Upewnij się, że nazwy zdarzeń, instancji i elementów GUI są zgodne z odniesieniami w kodzie. Nie ma nic złego w tym, że różnią się one od przykładowego kodu, pod warunkiem, że zwracasz na to uwagę. Po drugie, zawsze dobrze jest sprawdzić cały swój kod za pomocą symulatora sieci i gry opublikowanej na żywo.
Komunikacja od Klienta do Serwera
Teraz nadszedł czas, aby dowiedzieć się, jak pójść w przeciwnym kierunku - przesłać informacje od klienta do serwera. Pamiętaj, w tym przypadku nie potrzebujesz odpowiedzi z serwera. Jeśli klient musi wprowadzić zmianę, która ma wpływ na cały serwer, może wysłać zdalne żądanie do serwera. W takim przypadku strona klienta uruchomiłaby RemoteEvent:
remoteEvent:FireServer(infoToPass)
A funkcje byłyby połączone po stronie serwera:
local function functionName(player, passedInfo)
print(player.Name)
-- Stuff to do
end
remoteEvent.OnServerEvent:Connect(functionName)
Spójrz na przykładową funkcję w powyższym kodzie. Gracz, który wywołał zdarzenie, został przekazany automatycznie. Pierwszy parametr zawsze będzie musiał to uwzględnić.
Wybierz mapę, dowolną mapę
Jednym ze sposobów na urozmaicenie rozgrywki jest umożliwienie wyboru różnych map. Świat na rysunku
Konfiguracja
Wszystkie budynki i rekwizyty na mapie można zgrupować w jeden model. W tej sekcji skonfigurujesz kilka prostych modeli do ćwiczeń i utworzysz przyciski GUI:
1. W ServerStorage dodaj nowy folder o nazwie Maps.
2. Umieść trzy różne modele w folderze Maps
.
Upewnij się, że każdy model ma unikalną nazwę. Aby zgrupować części w modelu, wybierz wszystkie żądane części; następnie kliknij prawym przyciskiem myszy i wybierz opcję Grupa (Cmd+G lub Ctrl+G). ma selektor mapy, w którym osoba może wybrać jedną z trzech różnych lokalizacji. W zależności od twojego doświadczenia, ludzie mogą zostać teleportowani do tej lokalizacji lub ta mapa może zostać załadowana. W tym przykładzie zamiast teleportować ludzi, utworzysz wybraną mapę, klonując ją z ServerStorage.
WSKAZÓWKA
Ćwicz z prostymi modelami
Na potrzeby tego Wypróbuj sam, nie ma problemu, jeśli modele składają się tylko z części lub dwóch.
3. W ReplicatedStorage dodaj RemoteEvent o nazwie MapPicked.
4. W StarterGui dodaj newScreenGui, aw nim ramkę o nazwie MapSelection.
Ramki umożliwiają grupowanie różnych elementów GUI.
5. Wybierz ramkę i wstaw trzy TextButtons.
Upewnij się, że nazwa każdego przycisku odpowiada mapom w ServerStorage. Użyjesz nazwy, aby sklonować poprawną mapę.
Strona klienta
Na kliencie pojawiają się przyciski umożliwiające wybór mapy i wysłanie wiadomości na serwer:
1. Wybierz jeden z przycisków i wstaw skrypt lokalny.
2. Skonfiguruj odniesienia dla zdarzenia RemoteEvent, przycisku i ramki:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local mapPicked = ReplicatedStorage:WaitForChild("MapPicked")
local button = script.Parent
local frame = button.Parent
3. Utwórz nową funkcję powiązaną ze zdarzeniem Aktywowany przycisku:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local mapPicked = ReplicatedStorage:WaitForChild("MapPicked")
local button = script.Parent
local frame = button.Parent
local function onButtonActivated()
end
button.Activated:Connect(onButtonActivated)
4. Wewnątrz użyj FireServer(), aby wysłać nazwę wybranego przycisku, a następnie uczynić ramkę niewidoczną:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local mapPicked = ReplicatedStorage:WaitForChild("MapPicked")
local button = script.Parent
local frame = button.Parent
local function onButtonActivated()
mapPicked:FireServe r(button.Name)
frame.Visible = false
end
button.Activated:Connect(onButtonActivated)
Po stronie serwera
Po stronie serwera wybrana mapa zostanie sklonowana z ServerStorage:
1. W ServerScriptService dodaj nowy skrypt.
2. Skonfiguruj odwołania do folderu RemoteEvent, ServerStorage i Maps:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local mapPicked = ReplicatedStorage:WaitForChild("MapPicked")
local ServerStorage = game:GetService("ServerStorage")
local mapsFolder = ServerStorage:WaitForChild("Maps")
3. Dodaj jeszcze jedno odniesienie, które będzie używane na bieżącej mapie. Na razie ustaw na zero. Użyjesz tej zmiennej, aby usunąć starą mapę po utworzeniu nowej, aby nie były ułożone jedna na drugiej:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local mapPicked = ReplicatedStorage:WaitForChild("MapPicked")
local ServerStorage = game:GetService("ServerStorage")
local mapsFolder = ServerStorage:WaitForChild("Maps")
local currentMap = nil
4. Utwórz nową funkcję i połącz ją ze zdarzeniem RemoteEvent o nazwie OnServerEvent:
local function onMapPicked(player, chosenMap)
end
mapPicked.OnServerEvent:Connect(onMapPicked)
5. Znajdź wybraną mapę w folderze Maps:
local function onMapPicked(player, chosenMap)
local mapChoice = mapsFolder:FindFirstChild(chosenMap)
end
6. Sprawdź, czy wybrana mapa została znaleziona; następnie zniszcz starą mapę i zrób kopię nowej:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local mapPicked = ReplicatedStorage:WaitForChild("MapPicked")
local ServerStorage = game:GetService("ServerStorage")
local mapsFolder = ServerStorage:WaitForChild("Maps")
local currentMap = nil
local function onMapPicked(player, chosenMap)
local mapChoice = mapsFolder:FindFirstChild(chosenMap)
if mapChoice then
-- Check for the old map and destroy it
if currentMap then
currentMap:Destroy()
end
-- Make a copy of the new map
currentMap = mapChoice:Clone()
currentMap.Parent = workspace
else
print("Map choice not found")
end
end
mapPicked.OnServerEvent:Connect(onMapPicked)
7. Przetestuj to! Jeśli to działa, umieść kopie LocalScript w pozostałych dwóch przyciskach.
WSKAZÓWKA
Porada dotycząca rozwiązywania problemów
Jeśli skrypty nie działają zgodnie z oczekiwaniami, upewnij się, że testujesz właściwy przycisk. Jeśli przyciski znajdują się jeden na drugim lub użyjesz niewłaściwego przycisku, nic się nie stanie
Komunikacja z serwera do jednego klienta
Jeśli musisz przekazać informacje konkretnemu graczowi, na przykład gdyby został losowo wybrany na łowcę lub "to" w grze, sytuacja wygląda trochę inaczej. Musisz upewnić się, że wysyłasz odtwarzacz, gdy serwer odpala RemoteEvent, zanim przekażesz jakiekolwiek dodatkowe informacje.
Po stronie serwera
remoteEvent:FireClient(player, additionalInfo)
Strona klienta:
local function onServerEvent(player, additionalInfo)
-- Whatever you want to happen
end
remoteEvent.OnClientEvent:Connect(onServerEvent)
Nawet jeśli nie planujesz używać argumentu gracza, to i tak trzeba go wysłać i rozliczyć po stronie lokalnej. To tylko dziwactwo RemoteEvent, które musi wiedzieć, do kogo wysłać wiadomość.
Komunikacja od Klienta do Klienta
Czwarty i ostatni sposób używania RemoteEvents to klient do klienta. Klienci nie mogą komunikować się bezpośrednio ze sobą; muszą przejść przez serwer, więc faktycznie używasz kombinacji trzech poprzednich metod. Klient użyłby FireServer(infoToPass), a stamtąd serwer przekazałby informacje z powrotem do jednego lub wszystkich klientów.
Streszczenie
RemoteEvents to naprawdę wszechstronny sposób przesyłania informacji między serwerem a klientem, ponieważ możesz połączyć kilka funkcji z tym samym zdarzeniem. Ponieważ informacje są przesyłane tylko w jedną stronę, funkcje RemoveEvents nie muszą czekać na odpowiedź i są generalnie szybsze i łatwiejsze w użyciu niż RemoteFunctions, gdy odpowiedź nie jest wymagana.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Prawda czy fałsz: RemoteEvents może wysyłać informacje do serwera i zwracać odpowiedź do klienta.
2. Prawda czy fałsz: Zdarzenia RemoteEvent mogą mieć w danej chwili powiązaną tylko jedną funkcję.
3. Aby wysłać wiadomość od klienta do serwera, należy użyć funkcji ____.
4. Prawda czy fałsz: serwer automatycznie otrzymuje nazwę klienta, który uruchomił zdarzenie RemoteEvent.
Odpowiedzi
1. Fałsz. RemoteEvents może wysyłać informacje tylko w jednym kierunku. Nie mogą się doczekać odpowiedzi.
2. Fałsz. Zamiast wiązać pojedynczą funkcję ze zdarzeniem RemoteEvent, można połączyć dowolną liczbę funkcji. Jest to jedna z ich zalet w porównaniu z RemoteFunctions.
3. Aby wysłać wiadomość od klienta do serwera, użyj FireServer() - na przykład mapPicked:FireServer(button.Nazwa).
4. True , dlatego podczas łączenia funkcji ze zdarzeniem RemoteEvent należy wziąć pod uwagę argument gracza przychodzącego.
Ćwiczenia
Gdy mapa zostanie wybrana, koniecznie poinformuj wszystkich na serwerze o wyborze mapy, jak pokazano na rysunku
Porady
- Pamiętaj, że klienci nie mogą rozmawiać bezpośrednio z innymi klientami.
- Nie potrzebujesz kolejnego RemoteEvent.
Korzystanie ze skryptów ModuleScript
Istnieje duża szansa, że Twoje doświadczenie będzie zawierało wiele rzeczy: wiele przycisków, wiele rzeczy do dotknięcia, wiele rzeczy do podniesienia. W miarę możliwości chcesz się upewnić, że nie skończysz z dużą ilością zduplikowanego kodu w swoim świecie do obsługi wszystkich tych obiektów. Posiadanie wielu kopii skryptów wszędzie jest trudne do zarządzania i trudne do aktualizacji. Wyobraźcie sobie wprowadzanie tej samej zmiany w dziesiątkach skryptów podnoszenia przedmiotów lub wchodzenie w pułapkę pojedynczą pułapką, aby zmienić zadawane przez nie obrażenia. W tej godzinie wyjaśnimy ModuleScripts, kolejne narzędzie w twoim pasie do utrzymywania scentralizowanego kodu i łatwego aktualizowania. Zawiera również nieco więcej informacji na temat ogólnej zasady kodowania Don't Repeat Yourself - znanej również jako DRY - oraz sposobu organizacji kodu, aby ułatwić jego poprawianie i aktualizowanie.
Kodowanie rzeczy tylko raz
Skrypty modułów to unikalny obiekt skryptu, który umożliwia przechowywanie funkcji i zmiennych, które mogą być następnie używane zarówno przez skrypty, jak i skrypty lokalne. W ten sposób możesz stworzyć główne źródło informacji za rzeczy takie jak zbieranie złota, statystyki potworów, a nawet zachowanie przycisków. Zamiast mieć tuzin, a nawet sto skryptów do aktualizacji, jeśli trzeba wprowadzić zmianę, wystarczy zaktualizować ModuleScript. Skrypty i skrypty lokalne będą nadal potrzebne do uzyskania dostępu do skryptu modułu, ale zawarty w nich kod można ograniczyć do niezbędnego minimum.
Umieszczanie skryptów ModuleScript
To, gdzie umieścisz ModuleScripts, zależy od tego, jak planujesz ich używać. Jeśli będą używane tylko przez skrypty serwera, powinieneś umieścić je w ServerStorage, gdzie będą lepiej chronione. Jeśli skrypty lokalne po stronie klienta muszą używać skryptów ModuleScript, można je umieścić w ReplicatedStorag
Zrozumienie, jak działają skrypty ModuleScript
Domyślnie każdy ModuleScript zaczyna się od tego kodu:
local module = {}
return module
Powinny to być zawsze pierwsza i ostatnia linia kodu dla ModuleScript. Zwróć uwagę na nawiasy klamrowe? Cały kod w ModuleScript jest umieszczany w tabeli, a następnie zwracany w ostatnim wierszu. W tabeli przechowywane są wszystkie wspólne funkcje i zmienne modułu.
Nazewnictwo ModuleScripts
Pierwszą rzeczą, którą musisz zrobić z tabelą, jest aktualizacja nazwy, aby odpowiadała nazwie skryptu, jak pokazano w poniższym kodzie i na rysunku
.
Nazwa powinna pasować do przeznaczenia wszystkich współdzielonych funkcji, takich jak ShopManager, TrapManager lub PetManager.
WSKAZÓWKA
Nazewnictwo skryptów
Słowo Menedżer jest dość powszechnie używane do oznaczania skryptu, który mówi coś innego, co należy zrobić. SoButtonManager można interpretować jako "Mówi przyciskowi, co ma robić".
local TrapManager = {}
function TrapManager.modifyHealth(player, amount)
-- Code
end
return TrapManager
Zauważ, że nazwy zarówno skryptu ModuleScript, jak i wewnętrznej tabeli pokazanej na powyższym rysunku są zapisane w PascalCase - pierwsza litera każdego słowa jest pisana wielką literą. Jeśli chcesz dowiedzieć się więcej o typowych konwencjach nazewnictwa w Roblox, niektóre zasady dotyczące stylu można znaleźć w załączniku.
Dodawanie funkcji i zmiennych
Aby dodać nową funkcję lub zmienną do tablicy modułu, użyj notacji kropkowej, podobnie jak wcześniej pracowałeś ze słownikami, jak pokazano tutaj:
local ModuleName = {}
-- Add a variable
ModuleName.variableName = 100
-- Adds a function
function ModuleName.functionName(parameter)
-- Code goes here
end
return ModuleName
Pamiętaj, że wszystko dodane do tabeli modułów musi być wpisane między local ModuleNa me = {} a zwróceniem ModuleName.
Zrozumienie zakresu w ModuleScripts
Jeśli spojrzysz wstecz na ostatni fragment kodu, zauważysz, że słowo kluczowe local nie jest używane dla dodanej funkcji i zmiennej:
-- The keyword 'local' isn't used
function ModuleName.functionName(parameter)
-- Code goes here
end
Wpisanie local przed zmiennymi i funkcjami oznacza, że są one dostępne tylko dla tego fragmentu kodu. Zwykle tego właśnie chcemy, ale skrypty ModuleScript są inne. Chodzi o to, aby kod można było udostępniać:
local ScoreManager= {}
-- The shareable function is not local
function ScoreManager.scoreCalculator(originalScore, newPoints)
local newScore = originalScore + newPoints
return newScore
end
return ScoreManager
Jednak zmienne używane tylko przez ModuleScript, takie jak zmienne w funkcji, nadal powinny być local:
local ScoreManager= {}
function ScoreManager.scoreCalculator(originalScore, newPoints)
-- This variable doesn't need to be shared outside the function
local newScore = originalScore + newPoints
return newScore
end
return ScoreManager
Używanie modułów w innych skryptach
ModuleScripts nie uruchamiają kodu samodzielnie. Zamiast tego dostęp do zmiennych i funkcji mają inne skrypty i są stamtąd uruchamiane. W skrypcie lub LocalScript użyj require() i podaj lokalizację ModuleScript:
Przykład skryptu:
local ServerStorage = game:GetService("ServerStorage")
local ModuleName = require(ServerStorage.ModuleName)
Skrypt zostanie załadowany do tabeli modułów, udostępniając zmienne i funkcje modułu. Aby użyć zmiennej lub funkcji z modułu, użyj nazwy ModuleScript, a następnie nazwę tego, czego potrzebujesz. Poniższy przykładowy kod zawiera zmienną ćwiczeniową o wartości 7 w ModuleScript. Poniższy fragment pokazuje, że dostęp do tej zmiennej uzyskuje się z normalnego obiektu skryptu. Rysunek
pokazuje wyniki w Output.
Kod ModuleScript w ServerStorage
local PracticeModuleScript = {}
PracticeModuleScript.practiceVariable = 7
function PracticeModuleScript.practiceFunction ()
print("This came from the practice ModuleScript")
end
return PracticeModuleScript
Kod skryptu w ServerScriptService
local Serverstorage = game:GetService("ServerStorage")
local PracticeModuleScript = require(Serverstorage.PracticeModuleScript)
-- This should print '7'
print(PracticeModuleScript.practiceVariable)
-- This should print the message within practiceFunction()
PracticeModuleScript.practiceFunction()
Wynikowy wynik wygląda jak na rysunku powyżej. Zauważ, że źródłem instrukcji drukowanej w pierwszym wierszu jest skrypt, natomiast źródłem drugiego wiersza jest skrypt ModuleScript. Upewnij się, że dokładnie pasujesz do nazw skryptów ModuleScript, funkcji i zmiennych; w przeciwnym razie nie będą działać. Nie ma nic złego w kopiowaniu i wklejaniu, aby upewnić się, że wszystko jest takie samo. Pamiętaj też, aby nie wprowadzać żadnych zmian w ModuleScript podczas działania środowiska. Nie można odświeżyć tabeli. Po załadowaniu tabeli modułu ponowne użycie metody require() zwróci tylko tę samą tabelę.
Stwórz Jump Pad
Podesty do skakania
zawsze przyciągają tłumy, ponieważ pozwalają ludziom w twoim doświadczeniu dotrzeć do obszarów, do których inaczej nie mogliby dotrzeć. W tym Wypróbuj sam masz zamiar stworzyć platformę do przeskakiwania, która będzie miała skrypt obsługujący tylko najbardziej podstawowy kod, który prawdopodobnie nie będzie wymagał aktualizacji.
Konfiguracja
Skonfiguruj obiekty skryptu i platformę skoku; w następnej sekcji będziesz pracować nad kodem.
1. Dodaj element lub siatkę. Podkładka do skakania na powyższym rysunku to po prostu neonowoniebieska część podstawowa.
2. Wstaw skrypt do panelu przeskoku.
3. W ServerStorage utwórz nowy skrypt ModuleScript o nazwie JumpPadManager.
Skrypt modułu
Zaczniesz od skonfigurowania ModuleScript. ModuleScript obsłuży, jak wysoko i jak długo gracz skoczy. Aby to zrobić, musisz zdobyć HumanoidRootPart postaci, która obsługuje podstawowy ruch osoby. Obiekt VectorForce zostanie dodany do HumanoidRootPart, co spowoduje, że osoba będzie strzelać tak długo, jak długo istnieje VectorForce. Być może nie jesteś jeszcze zaznajomiony z niektórymi używanymi koncepcjami, ale będziesz ich częściej używać w nadchodzących godzinach. Cała ciężka praca nad skryptem zostanie wykonana tutaj, pozostawiając tylko kilka wierszy kodu do utworzenia w skrypcie:
1. Zmień nazwę tabeli JumpPadManager:
local JumpPadManager = {}
return JumpPadManager
2. Utwórz stałą lokalną określającą, jak długo będzie trwał skok:
local JumpPadManager = {}
-- Local variable because they're not needed outside of this ModuleScript
local JUMP_DURATION = 1.0
return JumpPadManager
3. Utwórz drugą stałą lokalną dla kierunku skoku, jak pokazano poniżej. siła wektora wymaga współrzędnych X, Y, Z, aby wiedzieć, w którą stronę wysłać rzeczy. Środkowa liczba, Y, sprawia, że rzeczy idą w górę:
local JumpPadManager = {}
local JUMP_DURATION = 1.0
local JUMP_DIRECTION = Vector3.new(0, 6000, 0 )
return JumpPadManager
WSKAZÓWKA
Przenoszenie i animowanie obiektów
W ciągu następnej godziny dowiesz się więcej o współrzędnych Vector3, X, Y, Z oraz o tym, jak przenosić i animować obiekty. Jeśli czujesz się odważny, możesz poeksperymentować z wartościami X i Z dla siły działającej z przodu iz tyłu oraz z boku na bok.
4. Dodaj nową funkcję do tabeli:
local JumpPadManager = {}
local JUMP_DURATION = 1.0
local JUMP_DIRECTION = Vector3.new(0, 6000, 0)
-- Not local because the jump pads need these functions
function JumpPadManager.jump(part)
end
return JumpPadManager
5. Ta część jest podobna do robienia pułapki. Znajdź rodzica części i użyj go do wyszukania humanoida. Jeśli znajdzie Humanoida, wyszukaj HumanoidRootPart:
-- Top of ModuleScript
function JumpPadManager.jump(part)
local character = part.Parent
local humanoid = character:FindFirstChildWhichIsA("Humanoid")
if humanoid then
local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
end
end
return JumpPadManager
6. Wyszukaj instancję VectorForce, nawet jeśli nie będzie istnieć, dopóki jej nie dodasz. Będziesz tego potrzebować do odbicia w następnym kroku:
-- Top of ModuleScript
function JumpPadManager.jump(part)
local character = part.Parent
local humanoid = character:FindFirstChildWhichIsA("Humanoid")
if humanoid then
local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
local vectorForce = humanoidRootPart:FindFirstChild("VectorForce")
end
end
return JumpPadManager
7. Jeśli nie ma VectorForce, dodaj jeden. Daje to pewność, że zawsze zastosowano tylko jeden VectorForce:
local JumpPadManager = {}
-- Top of ModuleScript
function JumpPadManager.jump(part)
local character = part.Parent
local humanoid = character:FindFirstChildWhichIsA("Humanoid")
if humanoid then
local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
local vectorForce = humanoidRootPart:FindFirstChild("VectorForce")
if not vectorForce then
vectorForce = Instance.new("VectorForce")
end
end
end
return JumpPadManager
8. Ustaw właściwość Force na JUMP_DIRECTION, a następnie dołącz i nadrzędnie do HumanoidRootPart, jak pokazano tutaj:
-- Top of ModuleScript
function JumpPadManager.jump(part)
local character = part.Parent
local humanoid = character:FindFirstChildWhichIsA("Humanoid")
if humanoid then
local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
local vectorForce = humanoidRootPart:FindFirstChild("VectorForce")
if not vectorForce then
vectorForce = Instance.new("VectorForce")
vectorForce.Force = JUMP_DIRECTION
vectorForce.Attachment0 = humanoidRootPart.RootRigAttachment
vectorForce.Parent = humanoidRootPart
end
end
end
ret urn JumpPadManager
WSKAZÓWKA
Utrzymanie VectorForce i HumanoidRootPart razem
Załącznik zapewnia, że instancja VectorForce nie zostanie oddzielona od HumanoidRootPart.
9. Na koniec poczekaj JUMP_DURATION przed zniszczeniem BodyVelocity:
local JumpPadManager = {}
-- Local because they're not needed outside of this ModuleScript
local JUMP_DURATION = 1.0
local JUMP_DIRECTION = Vector3.new(0, 6000, 0)
-- Not local because the jump pads need these functions
function JumpPadManager.jump(part)
local character = part.Parent
local humanoid = character:FindFirstChildWhichIsA("Humanoid")
if humanoid then
local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
local vectorForce = humanoidRootPart:FindFirstChild("VectorForce")
if not vectorForce then
vectorForce = Instance.new("VectorForce")
vectorForce.Force = JUMP_DIRECTION
vectorForce.Attachment0 = humanoidRootPart.RootRigAttachment
vectorForce.Parent = humanoidRootPart
wait(JUMP_DURATION)
vectorForce:Destroy()
end
end
end
return JumpPadManager
Scenariusz
Po stronie skryptu potrzebujemy tylko absolutnego minimum kodu. Wszystko, co zrobi, to załaduje ModuleScript i wywoła JumpPadManager.jump(otherPart) za każdym razem, gdy coś dotknie części:
1. Załaduj JumpPadManager do skryptu:
local ServerStorage = game:GetService("ServerStorage")
local JumpPadManager = require(ServerStorage.JumpPadManager)
2. Połącz funkcję ze zdarzeniem Touched i wewnątrz przekaż dotykającą część do ModuleScript:
local ServerStorage = game:GetService("ServerStorage")
local JumpPadManager = require(ServerStorage.JumpPadManager)
local jumpPad = script.Parent
local function onTouch(otherPart)
JumpPadManager.jump(otherPart)
end
jumpPad.Touched:Connect(onTouch)
Przetestuj wszystko! Jeśli masz zero błędów, istnieje duża szansa, że jest to spowodowane gdzieś chybionym pisownią. Upewnij się, że wszystkie nazwy i wielkość liter pasują dokładnie.
Nie powtarzaj się
W miarę jak śledziłeś to przez wiele godzin, coraz częściej mówiliśmy o scentralizowaniu kodu lub napisaniu kodu w sposób, który można ponownie wykorzystać. Pomyśl o elementach zasobów, takich jak złoto i dzienniki w Godzinie 9. Zamiast mieć różne skrypty dla złota i dzienników, oba używają tego samego zestawu skryptów, ale kod umożliwia przetwarzanie różnych informacji. Jest to część ogólnej praktyki kodowania DRY. DRY oznacza Don't Repeat Yourself i jest to koncepcja, która dotyczy wszystkich kodowania i języków kodowania - nie tylko Lua i Roblox Studio. Przeciwieństwo, WET (Write Everything Twice), jest ogólnie uważane za coś złego i jest używane do oznaczania, że masz dużo zduplikowanego kodu w swoich skryptach.
Handel abstrakcjami
Kluczową częścią kodowania DRY są abstrakcje. Abstrakcja to proces wyciągania najważniejszych informacji i ukrywania wszystkiego, czym nie musisz się teraz zajmować. Wiele rzeczy w Roblox Studio jest dla ciebie abstrakcyjnych. Pomyśl o wszystkich funkcjach lub metodach, w których wystarczy wywołać funkcję i przekazać informacje. Funkcje są abstrakcjami wielokrotnego użytku. Po wywołaniu użytkownicy uzyskują korzyści z funkcji bez konieczności przepisywania lub nawet przeglądania reszty kodu. Typowym przykładem w językach kodowania jest print(). Większość jego kodu jest ukryta, więc programista może skupić się na tym, co należy wydrukować, a nie na tym, jak wyświetlić poszczególne piksele na ekran. Skrypty modułów pozwalają również skonfigurować abstrakcje niezbędne do dobrych praktyk kodowania DRY. Skrypty modułów mogą działać jako pojedyncze źródło prawdy (SSOT), co oznacza, że informacje i funkcje potrzebne wielu skryptom mogą być przechowywane w jednym skrypcie modułu. Dobrym sposobem na sprawdzenie, czy musisz utworzyć abstrakcję, jest rozważenie potrzeby użycia zmiennej lub funkcji w trzech lub więcej miejscach. Tak więc w przykładowej grze z zasobami był plik ,duża szansa, że chcesz, aby ludzie zbierali nie tylko kłody i złoto, ale potencjalnie także inne rodzaje zasobów - może w końcu także takie rzeczy, jak żelazo, jagody lub wełna.
Streszczenie
Abstrakcje zapewniają uproszczoną reprezentację czegoś większego, pomijając szczegóły. Podejmując decyzję o utworzeniu abstrakcji, szukaj kodu, który jest często używany ponownie, ale za każdym razem wprowadza niewielkie zmiany. Na przykład ogólny przedmiot, taki jak plecak, można przekształcić w funkcję wielokrotnego użytku, która wyszukuje cenę i pojemność. Poświęcenie czasu na zaplanowanie i ustrukturyzowanie kodu za pomocą abstrakcji pomaga programistom skupić się na tym, co ważne. Ta inwestycja w czas zapewnia lepszą organizację programów i ułatwia ich aktualizację.
Pytania i odpowiedzi
P. Dlaczego po prostu nie zawsze pomijać lokalne i udostępniać większość zmiennych i funkcji?
O-. Tworzenie zmiennych lokalnych jest zwykle najlepszą praktyką. Sprawia, że Twój kod działa nieco szybciej i eliminuje szanse na błędy i przypadkowe nadpisanie informacji. Zmienne nielokalne, takie jak i ModuleScripts, powinny zawsze stanowić wyjątek, a nie regułę.
P. Czy można przesadzić z abstrakcjami i kodowaniem DRY?
O. Zdecydowanie można przesadzić z tworzeniem abstrakcji. Generalnie jednak, jeśli potrafisz wyobrazić sobie przyszłość, w której będziesz chciał użyć tego samego fragmentu kodu więcej niż dwa lub trzy razy, abstrakcje są warte wysiłku.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Mój ModuleScript nazywa się RoundManager. Jak powinien wyglądać pierwszy i ostatni wiersz mojego ModuleScript?
2. Funkcje można dodawać do skryptu ModuleScript za pomocą ____.
3. Jeśli skrypt modułu musi być używany przez skrypt lokalny, gdzie należy go umieścić?
4. Jeśli moduł ModuleScript ma być używany tylko przez skrypty, gdzie należy go umieścić?
5. Jeśli skrypt modułu musi być używany zarówno przez skrypty lokalne, jak i skrypty, gdzie należy go umieścić?
6. Co oznacza DRY?
Odpowiedzi
1. Jeśli twój ModuleScript nazywa się RoundManager, pierwsza i ostatnia linia kodu powinna mieć taką nazwę
local RoundManager = {}
-- Code
return RoundManager
2. Notacja kropkowa, taka jak funkcja MyModule.myfunction()
3. W ReplicatedStorage
4. W ServerStorage
5. W ReplicatedStorage
6. Nie powtarzaj się
Ćwiczenia
Przećwicz tworzenie skryptu modułu przy użyciu znanego kodu. Zrób serię pułapek, których ludzie muszą unikać, jeśli nie chcą stracić całego zdrowia. Rysunek przedstawia przykład.
Kodowanie w przestrzeni świata 3D
W ciągu ostatniej Części użyłeś skryptu modułu do stworzenia skoczni, która wystrzeliwała ludzi prosto w powietrze. Ta godzina obejmuje umieszczanie obiektów w przestrzeni 3D i tworzenie części odradzających się w dowolnym miejscu na świecie.
Zrozumienie współrzędnych X, Y i Z
Zanim zaczniesz kodować, musisz zrozumieć, w jaki sposób obiekty są umieszczane i obracane w przestrzeni 3D. W świecie 3D każdy obiekt można ustawić na siatce kontrolowanej przez trzy osie, X, Y i Z. Góra i dół są kontrolowane przez oś Y, podczas gdy przód i tył oraz bok do boku są kontrolowane przez X i Z., odpowiednio. Na rysunku
zielona strzałka reprezentuje oś Y, czerwone strzałki to oś X, a niebieska strzałka to oś Z. Jeśli jeszcze go nie widzisz, włącz Selektor widoku
za pomocą opcji Widok > Akcje > Selektor widoku, aby zobaczyć, w którą stronę świata skierowana jest Twoja kamera. Po wybraniu obiektu za pomocą narzędzia Przesuń można zobaczyć trzy osie reprezentowane przez czerwoną (X), zieloną (Y) i niebieską (Z) strzałkę. Przeciągnięcie obiektu w przestrzeni świata spowoduje aktualizację wartości pozycji X, Y i Z w oknie Właściwości , Jeśli umieścisz obiekt w centrum świata, jego położenie X, Y i Z będzie wynosić 0, 0, 0.
Udoskonalanie rozmieszczenia za pomocą współrzędnych CFrame
Jeśli chcesz umieścić obiekt lub gracza w określonym miejscu, musisz zrozumieć CFrames. Dzięki Cframes możesz umieścić je dokładnie tam, gdzie chcesz, zamiast tylko odradzać obiekty w centrum świata. CFrame oznacza układ współrzędnych. Każdy obiekt w przestrzeni 3D ma jeden. Domyślna wartość CFrame to 0, 0, 0 - dlatego nowe obiekty pojawiają się w centrum świata. Aby zaktualizować pozycję obiektu, przypisz nową wartość CFrame za pomocą CFrame.new():
Przykład:
local part = script.Parent
part.CFrame = CFrame.new(1, 4, 1)
Możesz ustawić pozycje X, Y i Z indywidualnie, jak pokazano w powyższym kodzie, lub możesz przekazać dane Vector3, jak pokazano tutaj:
local vector3 = Vector3.new( 1, 4, 1)
part.CFrame = CFrame.new(vector3)
Umieść przedmiot tam, gdzie jest coś innego
Istnieje wiele sposobów na znalezienie współrzędnych do użycia dla nowej pozycji CFrame. Jednym ze sposobów jest znalezienie innej części, która jest już tam, gdzie chcesz, aby część się znalazła. Części podstawowe mają właściwość o nazwie Pozycja, która używa wartości Vector3. W tym szybkim Wypróbuj sam użyj właściwości Position jednej części, aby ustawić CFrame zupełnie nowej części.
1. Utwórz część gdzieś w swoim świecie. Nazwij go czymś odrębnym, na przykład Marker i utwórz dla niego odniesienie:
local marker = workspace.Marker
2. Utwórz nowe wystąpienie części. Domyślnie nowe części nie są zakotwiczone, więc pamiętaj o zakotwiczeniu ich w miejscu:
local marker = workspace.Marker
local newPart = Instance.new("Part")
newPart.Anchored = true
3. Pobierz CFrame nowej części i ustaw ją na CFrame.new():
local marker = workspace.Marker
local newPart = Instance.new("Part")
newPart.Anchored = true
newPart.CFrame = CFrame.new()
4. Podaj pozycję znacznika, a następnie nadrzędną nową część dla obszaru roboczego. Przetestuj swój kod; powinieneś skończyć z dwiema częściami w tym samym miejscu:
local marker = workspace.Marker
local newPart = Instance.new("Part")
newPart.Anchored = true
newPart.CFrame = CFrame.new(marker.Position)
newPart.Parent = workspace
Być może zadajesz sobie pytanie, dlaczego nie zawsze korzystasz z właściwości Position? Odpowiedź jest taka, że pozycja działa tylko z częściami, a nie z modelami. Omówimy to więcej za chwilę.
Przesuwanie CFramek
Dość często nie chcesz umieszczać czegoś dokładnie w tym samym miejscu; zamiast tego chcesz umieścić go powyżej lub trochę z boku. Aby to osiągnąć, możesz połączyć wartości CFrame i Vector3. W poniższym przykładzie Vector3 dodaje cztery kołki do wartości Y, która została przekazana do CFrame.new:
local marker = workspace.Marker
local newPart = Instance.new("Part")
newPart.Anchored = true
-- Will place the new part 4 studs above Marker
newPart.CFrame = CFrame.new(marker.Position) + Vect or3.new(0, 4, 0)
newPart.Parent = workspace
Dodawanie obrotów do ramek CF
Możesz dodać wartości rotacji do CFramek. Aby obrócić istniejący obiekt, weź bieżącą ramkę CFrame i pomnóż ją przez liczbę stopni, o jaką chcesz ją obrócić, używając funkcji CFrame.Angles():
local spinner = script.Parent
local ROTATION_AMOUNT = CFrame.Angles(0, math.rad(45), 0)
while wait(0.5) do
-- Take spinner's current CFrame and rotate it.
spinner.CFrame = spinner.CFrame * ROTATION_AMOUNT
end
CFrame.Angles przyjmuje również trzy wartości dla X, Y i Z. Poprzedni fragment kodu obraca pokrętło na osi Y. Należy zauważyć, że nie działa przy użyciu stopni. Wykorzystuje radiany. Radiany to koncepcja matematyczna używana podczas pracy z łukiem koła. Na szczęście nie musisz wiedzieć, jak używać radianów. Zamiast tego funkcja math.rad() może za Ciebie przekonwertować stopnie na radiany. Jeśli więc chcesz, aby część obracała się o 20 stopni na osi X, wyglądałaby tak:
local ROTATION_AMOUNT = CFrame.Angles(math.rad (20), 0, 0)
part.CFrame = part.CFrame * ROTATION_AMOUNT
Praca z modelami
Jak wspomniano wcześniej, poszczególne części bazowe mają właściwość o nazwie Pozycja. Jednak modele nie. Aby przesunąć pozycję modelu, musisz uzyskać element PrimaryPart modelu. Aby zademonstrować, wzięliśmy bardzo prosty model chmury złożony z kul, które zostały zgrupowane razem, jak pokazano na rysunku
WSKAZÓWKA
Grupowanie części w modelu
Aby pogrupować części w model, kliknij prawym przyciskiem myszy wybrane części i wybierz Grupuj.
Tego modelu chmury nie można przenieść przy użyciu wcześniejszej metody. Musisz użyć SetPrimaryPartCFrame(), a następnie przekazać nową CFrame:
local cloud = workspace.Cloud
cloud:SetPrimaryPartCFrame(CFrame.new(0, 20, 0))
WSKAZÓWKA
Ustawianie części głównej
Element PrimaryPart modelu można ustawić we właściwościach. Kliknij Element podstawowy; następnie w Eksploratorze kliknij część, którą chcesz oznaczyć jako główną część modelu.
Zrozumienie współrzędnych światowych i współrzędnych lokalnych obiektów
W doświadczeniu 3D są tak naprawdę dwa zestawy współrzędnych, o których musisz pomyśleć. Pierwsza to współrzędna światowa, o której mówiliśmy do tej pory - jak coś jest umieszczane i obracane zgodnie z osiami X, Y i Z całej przestrzeni 3D. Druga to lokalna oś obiektu - sposób, w jaki obiekt jest pozycjonowany i obracany względem siebie. X, Y i Z pojedynczego obiektu mogą nie pokrywać się ze światem. Na rysunku widać oś światową po lewej stronie i lokalną oś pudełka pokazaną po prawej stronie.
Pomyśl o tym w ten sposób. Świat, w którym się poruszasz, ma globalne kierunki kompasu (północ, południe, wschód, zachód), które nie zmieniają się bez względu na to, w którą stronę się zwrócisz. Ale twoja osobista lewa, prawa, do przodu i do tyłu porusza się i obraca tak jak ty. Możesz zmienić skalę i obracać narzędzia, aby zobaczyć współrzędne globalne lub lokalne, naciskając Cmd/Ctrl + L. Możesz powiedzieć, że jesteś w trybie lokalnym, jeśli zobaczysz małe L w rogu wzdłuż czerwonej osi X, jak pokazano na rysunku
Super Jump w stosunku do gracza
Bieżąca podkładka skoku używa współrzędnych globalnych. Ilekroć ktoś nadepnie na pad, jest zawsze strzelany w tym samym kierunku, bez względu na to, w którą stronę jest zwrócony (lub skocznia). W tym Wypróbuj sam modyfikujesz kod tak, aby używał kierunku, w którym gracz jest zwrócony za pomocą funkcji relativeto().
Użyj kodu z godziny 13, aby wprowadzić niewielkie zmiany:
1. W JumpPadManager znajdź stałą JUMP_DIRECTION. Zmień wartość Z na -6000:
local JumpPadManager = {}
-- Local because they're not needed outside of this ModuleScript
local JUMP_DURATION = 0.5
local JUMP_DIRECTION = Vector3.new(0, 6000, -6000)
2. Ustaw właściwość RelativeTo VectorForce na Enum.ActuatorRelativeTo.Attachment0:
local JumpPadManager = {}
-- Lokalne, ponieważ nie są potrzebne poza tym ModuleScript
local JUMP_DURATION = 0.5
local JUMP_DIRECTION = Vector3.new(0, 6000, -6000)
Nie local, ponieważ pady skokowe potrzebują tych funkcji
function JumpPadManager.jump(part)
local character = part.Parent
local humanoid = character:FindFirstChildWhichIsA("Humanoid")
if humanoid then
local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
local vectorForce = humanoidRootPart:FindFirstChild("
VectorForce")
if not vectorForce then
vectorForce = Instance.new("VectorForce")
vectorForce.Force = JUMP_DIRECTION
vectorForce.Attachment0 = humanoidRootPart.RootRigAttachment
vectorForce.RelativeTo = Enum.ActuatorRelativeTo.Attachment0
vectorForce.Parent = humanoidRootPart
wait(JUMP_DURATION)
vectorForce:Destroy()
end
end
end
return JumpPadManager
WSKAZÓWKA
VectorForce jest względny w stosunku do załącznika
Spowoduje to ustawienie Vecto rForce względem Attachment0, które w tym przypadku jest połączone z HumanoidRootPart.
3. Przetestuj to. Twoja postać powinna być wzmacniana w lokalnym kierunku, w którym jest zwrócona, zamiast wzmacniania wzdłuż osi świata.
WSKAZÓWKA
Różne awatary ważą różne ilości
Podobnie jak ludzie w prawdziwym życiu, waga awatara zależy od jego wielkości i rodzaju akcesoriów. Stopień wzmocnienia awatara będzie się różnić w zależności od wagi
Streszczenie
Gratulacje! Masz teraz moc umieszczania obiektów w dowolnym miejscu na świecie. I nie tylko części, możesz także teleportować ludzi. Wszystko w trójwymiarowej przestrzeni świata, w tym postacie ludzi, ma współrzędne, które można znaleźć wzdłuż X (czerwonego), Y (zielonego) i Z (niebieskiego) świata.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Jaka oś znajduje się w przestrzeni świata Roblox Studio?
2. Jeśli chcesz utworzyć nową Cframe z informacji Vector3, użyj funkcji ______.
3. Aby obrócić ramkę CF, użyj funkcji _____.
4. Aby zamienić stopnie na radiany, użyj ________.
5. Aby obrócić obiekt, ________ pozycję CFrame i CFrame.Angles().
6. Aby umieścić n obiekt z boku lub nad obiektem, należy ____ pozycję CFrame i nowy Vector3.
Odpowiedzi
1. Zielona oś Y jest skierowana w górę.
2. CFramka.Nowy()
3. CFramka.Kąty()
4. matematyka .rad()
5. Pomnóż
6. Dodaj żądaną pozycję CFrame i określ, o ile chcesz ją przesunąć.
Ćwiczenia
Ludzi można teleportować z miejsca na miejsce w świecie, aktualizując informacje CFrame ich postaci. Może to mieć na celu umożliwienie graczom przejścia przez kanion, taki jak ten pokazany na rysunku , lub teleportację uczestników z holu na arenę. W tym ćwiczeniu utwórz część, która teleportuje graczy do innej części.
Porady
* Ze względu na praktykę musisz się martwić tylko o teleportację gracza w jednym kierunku.
* Element PrimaryPart można znaleźć w cha danej osoby.
Płynne animowanie obiektów
CFrames umożliwiają nagłe przenoszenie rzeczy z jednego miejsca do drugiego w mgnieniu oka. Ale co, jeśli nie chcesz, żeby coś migało? Może zamiast tego chciałbyś, aby płynnie przechodziły między dwoma punktami lub zmieniały kolor z jednego na drugi. I tu właśnie wkraczają animacje. W tej części animacje są używane do płynnej zmiany położenia i koloru bloku, ale te same zasady dotyczą także pracy z graficznymi interfejsami użytkownika
Zrozumienie animacji
Animacje biorą punkt początkowy, taki jak pozycja na mapie lub określony kolor, i płynnie zmieniają się w czasie w punkt końcowy. Aby korzystać z weens, musisz uzyskać TweenService, jak pokazano tutaj:
local TweenService = game:GetService("TweenService")
Dostosuj kolor części
Animacje to jedna z tych rzeczy, które mają większy sens, jeśli je zademonstrujesz. Wykonaj poniższe czynności, aby skonfigurować prostą animację zmieniającą kolor części w czasie:
1. Utwórz nową część i dołącz skrypt.
2. W skrypcie pobierz usługę animacji i utwórz zmienną wskazującą na część docelową:
local TweenService = game:GetService("TweenService")
local part = script.Parent
3. TweenInfo kontroluje sposób obsługi przejścia. Utwórz nowy TweenInfo i przekaż 5.0, aby przejście do nowego koloru zajęło 5 sekund:
local TweenService = game:GetService("TweenService")
local part = script.Parent
local tweenInfo = TweenInfo.new(5.0)
4. Usługa TweenService potrzebuje tabeli do przechowywania wartości celu dla każdej właściwości, która ma zostać zmieniona - w tym przypadku ostateczny kolor części:
local TweenService = game:GetService("TweenService")
local part = script.Parent
local tweenInfo = TweenInfo.new(5.0)
local goal = {}
goal.Color = Color3.fromRGB(11, 141 , 255)
WSKAZÓWKA
Użyj dowolnej wartości RGB. Ten kolor to jasny niebieski.
5. Użyj TweenService:Create(), aby połączyć część docelową, TweenInfo i tabelę celów:
local goal = {}
goal.Color = Color3.fromRGB(11, 141, 255)
-- Put together the target part, TweenInfo, and goal values
local tween = TweenService:Create(part, tweenInfo, goal)
6. Daj trochę czasu, aby doświadczenie się załadowało, a następnie powiedz animacji, aby zagrała:
local part = script.Parent
local goal = {}
goal.Color = Color3.fromRGB(11, 141, 255)
local tweenInfo = TweenInfo.new(5.0)
local tween = TweenService:Create(part, tweenInfo, goal)
-- Delay to give things time to load properly
wait(2.0)
-- Tell the tween to play
tween:Play()
WSKAZÓWKA
Czas przejścia
Jeśli nie dodasz funkcji wait(), prawdopodobnie przegapisz początek przejścia. Jeśli jest to krótki czas przejścia lub długi czas ładowania, możesz go całkowicie przegapić. Gdyby ta klatka pośrednia miała być odtwarzana po uruchomieniu zdarzenia, funkcja wait() nie byłaby potrzebna.
Ustawianie parametrów TweenInfo
Możesz animować dowolną liczbę właściwości obiektu; wystarczy dodać je do tabeli. Ponadto można zrobić o wiele więcej, aby dostosować zachowanie animacji podczas interpolacji, czyli przejść, do wartości docelowych. Tabela zawiera listę wszystkich parametrów TweenInfo.
Parametr: Co robi
Czas [liczba, sekundy]: Określa, ile czasu potrzebuje animacja, aby osiągnąć swoje cele
EasingStyle [Enum] : Określa, w jaki sposób animacja zachowuje się w kierunku swojego celu
EasingDirection [Enum] : Określa kierunek funkcji EasingStyle
Liczba powtórzeń [liczba] : określa liczbę wykonań animacji po jej początkowym uruchomieniu
Odwraca [Bool] : Określa, czy animacja uruchamia odwrotną animację po pierwszym uruchomieniu
DelayTime [liczba, sekundy] : określa czas, jaki upłynął przed wykonaniem animacji
Kod wygląda następująco:
local tweenInfo = TweenInfo.new(
2.0, -- Time
Enum.EasingStyle.Linear, -- EasingStyle
Enum.EasingDirection.Out, -- EasingDirection
-1, -- RepeatCount (when less than zero the tween will loop indefinitely)
true, -- Reverses (tween will reverse once reaching its goal)
0.0 -- DelayTime
)
Jest to rzadki przypadek, w którym wszystkie argumenty są zwykle umieszczane w osobnej linii, aby ułatwić ich czytanie. Nie wszystko musi być wypełnione, ale argumentów nie można pominąć. Możesz zobaczyć pełną listę EasingStyles i EasingDirections w dodatku. Zauważ również, że ponieważ TweenInfo nie jest tabelą, nie stawiasz przecinka po ostatnim argumencie.
Utwórz drzwi windy
Poćwicz używanie większej liczby parametrów TweenInfo, ustawiając drzwi przesuwne, które mogą być używane przez coś w rodzaju windy lub eleganckiego budynku biurowego, takiego jak ten pokazany na rysunku
Po rozsunięciu drzwi animacja zatrzyma się, a następnie zmieni kierunek.
Konfiguracja
Potrzebujesz tylko części do tego Wypróbuj sam. Pamiętaj, że jeśli używasz modelu, musisz później uwzględnić to w kodzie.
1. Użyj gdzieś szklanej części jako drzwi przesuwnych.
2. Wstaw ProximityPrompt o nazwie SlidingDoorPrompt.
3. Ustaw HoldDurati na 0,5.
Scenariusz
Ten skrypt wykorzystuje pierwsze trzy parametry TweenInfo do utworzenia części, która porusza się płynnie na pewnej odległości.
1. W ServerScriptService utwórz nowy skrypt.
2. Uzyskaj niezbędną usługę dla ProximityPrompt i usługi Tween:
local ProximityPromptService = game:GetService("ProximityPromptService")
local TweenService = game:GetService("TweenService")
3. Skonfiguruj nową funkcję i połącz ją ze zdarzeniem PromptTriggered ProximityPrompt. Pamiętaj, aby dodać sprawdzenie, dla którego ProximityPrompt został uruchomiony:
local ProximityPromptService = game:GetService("ProximityPromptService")
local TweenService = game:GetService("TweenService")
local function onPromptTriggered(prompt, player)
if prompt.Name == "SlidingDoorPrompt" then
end
end
ProximityPromptService.PromptTriggered:Connect(onPromptTriggered)
4. Określ cel:
local ProximityPromptService = game:GetService("ProximityPromptService")
local TweenService = game:GetService("TweenService")
local function onPromptTriggered(prompt, player)
if prompt.Name == "SlidingDoorPrompt" then
local door = prompt.Parent
end
end
ProximityPromptService.PromptTriggered:Connect(onPromptTriggered)
5. Utwórz tabelę z docelowymi wartościami CFrame dla momentu otwarcia drzwi. Twoje wartości mogą się różnić:
if prompt.Name == "SlidingDoorPrompt" then
local door = prompt.Parent
local goal = {}
goal.CFrame = door.CFrame + Vector3.new(0, 0, 5)
end
WSKAZÓWKA
Twoje informacje Vector3 mogą być inne.
Dla uproszczenia ten kod po prostu przesuwa drzwi wzdłuż osi Z. Może być konieczne użycie innej osi. Jeśli miałbyś użyć tego kodu z kilkoma drzwiami obróconymi w różnych kierunkach, możesz nawet poeksperymentować ze współrzędnymi względnymi.
6. Utwórz nowy TweenInfo i ustaw czas trwania na 1 sekundę, styl wygładzania na Liniowy i kierunek wygładzania na In:
if prompt.Name == "SlidingDoorPrompt" then
local door = prompt.Parent
local goal = {}
goal.CFrame = door.CFrame + Vector3.new(0, 0, 5)
local tweenInfo = TweenInfo.new(
1.0,
Enum.EasingStyle.Linear,
Enum.EasingDirection.In
)
end
WSKAZÓWKA
Informacje o animacji nie są tabelą
Pamiętaj, że TweenInfo nie jest tabelą, więc nie potrzebuje przecinka po ostatnim argumencie.
WSKAZÓWKA
Autouzupełnienie
Podczas wpisywania wyliczeń zauważ, że autouzupełnianie
i wskazówki IDE
mogą ci bardzo pomóc.
7. Połącz to wszystko razem za pomocą TweenService:Create():
if prompt.Name == "SlidingDoorPrompt" then
local door = prompt.Parent
local goal = {}
goal.CFrame = door.CFrame + Vector3.new(0, 0, 5)
local tweenInfo = TweenInfo.new(
1.0,
Enum.EasingStyle.Linear,
Enum.EasingDirection.In
)
local openDoor = TweenService:Create(door, tweenInfo, goal)
end
ProximityPromptService.PromptTriggered:Connect(onPromptTriggered)
8. Odtwórz animację:
local ProximityPromptService = game:GetService("ProximityPromptService")
local TweenService = game:GetService("TweenService")
local function onPromptTriggered(prompt, player)
if prompt.Name == "SlidingDoorPrompt" then
local door = prompt.Parent
local goal = {}
goal.CFrame = door.CFrame + Vector3.new(0, 0, 5)
local tweenInfo = TweenInfo.new(
1.0,
Enum.EasingStyle.Linear,
Enum.EasingDirection.In
)
local openDoor = TweenService:Create(door, tweenInfo, goal)
openDoor:Play()
end
end
ProximityPromptService.PromptTriggered:Connect(on PromptTriggered)
Łączenie łańcuchów animacji razem
Po zakończeniu jednej animacji może być konieczne uruchomienie drugiej - na przykład, jeśli chcesz, aby drzwi pozostały otwarte przez chwilę, a następnie druga klatka pośrednia je zamknie. W takich przypadkach można użyć zdarzenia animacji, Completed. Najpierw poczekaj, aż uruchomi się zdarzenie Completed początkowej animacji, a następnie użyj drugiej funkcji wait(), aby określić, jak długo drzwi się zamkną, jak pokazano poniżej:
local ProximityPromptService = game:GetService("ProximityPromptService")
local TweenService = game:GetService("TweenService")
local DOOR_OPEN_DURATION = 2.0
local function onPromptTriggered(prompt, player)
if prompt.Name == "SlidingDoorPrompt" then
local door = prompt.Parent
local openGoal = {}
openGoal.CFrame = door.CFrame + Vector3.new(0, 0, 5)
local closeGoal = {}
closeGoal.CFrame = door.CFrame
local tweenInfo = TweenInfo.new(
1.0,
Enum.EasingStyle.Linear,
Enum.EasingDirection.In
)
local openDoor = TweenService:Create(door, tweenInfo, openGoal)
local closeDoor = TweenService:Create(door, tweenInfo, closeGoal)
-- Play the first tween
openDoor:Play()
-- Wait for the Completed event to fire
openDoor.Completed:Wait()
-- Pause, and then play the next tween
wait(DOOR_OPEN_DURATION)
closeDoor:Play()
end
end
ProximityPromptService.PromptTriggered:Connect(onPromptTriggered)
WSKAZÓWKA
Nie zapomnij odrzucić
Skrypt w obecnej postaci nie zawiera żadnego rodzaju debounce. Gdyby ktoś ciągle otwierał drzwi, drzwi mogłyby przesuwać się coraz bardziej w jedną stronę. Nie zapomnij dodać odbicia, jeśli faktycznie umieścisz to co de w swoim doświadczeniu.
Streszczenie
Animacje pozwalają na płynne przejście prawie każdej właściwości celu do nowej wartości w miarę upływu czasu. Chociaż ćwiczenia "Spróbuj sam" w tej godzinie pokazały, że zmieniana jest tylko jedna właściwość naraz, możesz dodać dowolną liczbę właściwości do tabeli celów. Wzorzec tworzenia animacji to
1. Uzyskaj TweenService.
2. Ustaw część docelową.
3. Skonfiguruj informacje TweenInfo.
4. Utwórz słownik celów.
5. Przekaż część docelową, TweenInfo i słownik celu w:
local tween = TweenService:Create(part, tweenInfo, goal)
6. Odtwórz animację.
Możesz polegać na IDE lub sprawdzać kolejność parametrów, jeśli nie możesz ich zapamiętać. Wszyscy inżynierowie tak robią. Aby przywrócić animację do pierwotnego stanu, można włączyć odwrócenie lub połączyć klatki pośrednie, nasłuchując zdarzenia animacji Ukończono.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na poniższe pytania.
Kartkówka
1. Jakiej usługi potrzebujesz, aby korzystać z animacji?
2. Czy można łączyć CFramki i animacje?
3. Prawda czy fałsz: możesz po prostu pominąć parametr, jeśli nie chcesz go używać.
4. To, czy animacja zostanie odwrócona na końcu, jest kontrolowane przez wartość ____.
5. Prawda czy fałsz: w danym momencie można animować tylko jedną właściwość.
6. Prawda czy fałsz: animacje są odtwarzane automatycznie po ich skonfigurowaniu.
Odpowiedzi
1. TweenService
2. Animacji można używać z wieloma właściwościami, w tym CFrame.
3. Fałsz: Chociaż nie zawsze musisz przekazywać argument dla każdego parametru, nie możesz pominąć parametrów.
4. Logiczna
5. Fałsz: Możesz zmienić wiele właściwości za pomocą tej samej animacji.
6. Fałsz: Po skonfigurowaniu animacji należy powiedzieć jej, kiedy ma być odtwarzana.
Ćwiczenia
W tym ćwiczeniu wyreguluj kolor światła SpotLight , aby zmieniał się w czasie i nigdy nie przestał się zapętlać.
Wskazówka: nie potrzebujesz wielu animacji.
Rozwiązywanie problemów za pomocą algorytmów
Ta część wprowadza do Twojego słownika nowy termin z dziedziny informatyki: algorytmy. Dowiesz się, jak już wprowadzasz tę koncepcję w życie, i pójdziesz o krok dalej dzięki wbudowanym algorytmom sortowania Roblox Studio, których możesz użyć do takich czynności, jak sortowanie przedmiotów w sklepie od najniższej do najwyższej wyceniać lub klasyfikować uczestników FPS na podstawie liczby ich zabójstw.
Definiowanie algorytmów
Algorytmy to precyzyjne instrukcje rozwiązania problemu. Właściwie stworzyłeś wiele algorytmów. Na przykład stworzyłeś algorytmy, które ustalają punkty zdrowia gracza po nadepnięciu na pułapkę, oraz algorytmy, które określają całkowitą liczbę punktów gracza za każdym razem, gdy ktoś dotknie jakiejś części. Aby funkcja była algorytmem, musi mieć trzy bardzo wyraźne etapy:
1. Informacje są pobierane, zwykle za pomocą parametrów.
2. Informacje te są przetwarzane za pomocą uporządkowanych kroków.
3. Podano rozwiązanie.
Weźmy bardzo prosty algorytm, który rozwiązuje problem dzielenia dwóch liczb.
Problem: Jaki jest wynik dzielenia z dwóch liczb?
local x = 20
local y = 9
-- Gives back the dividend of two numbers
local function divide(first, second)
return first / second
end
local result = divide(x,y)
print(result)
W tym małym przykładzie kodu możesz zobaczyć, co następuje:
1. Przez parametry przekazywane są dwie liczby.
2. Następuje etap dzielenia dwóch liczb.
3. Wynik dzielenia jest zwracany.
Algorytmy mogą być używane w kółko z różnymi danymi wejściowymi, podobnie jak funkcja może być używana z dowolnymi dwiema liczbami. Większość algorytmów będzie miała więcej kroków niż to, ale nigdy nie będą miały nieskończonych kroków. Aby być prawdziwym algorytmem, kod musi dać rozwiązanie twojego problemu.
Sortowanie tablicy
Klasycznym miejscem, w którym ludzie potrzebują algorytmów, jest sortowanie rzeczy - branie listy nazw, przedmiotów lub liczb i układanie ich w kolejności. W doświadczeniach Roblox informacje te będą najprawdopodobniej przechowywane w słowniku lub tablicy. Zacznijmy od tablic. table.sort(arrayName) używa algorytmu sortowania do porządkowania wartości w tablicach numerycznie lub alfabetycznie.
Sortuj listę nazwisk
W tym przykładzie posortujesz tablicę nazw w porządku alfabetycznym, a następnie ją wydrukujesz.
1. Utwórz listę trzech nazwisk:
local nameArray = {"Cat", "Mei", "Ana"}
2. Przekaż nazwę tablicy do table.sort():
local nameArray = {"Cat", "Mei", "Ana"}
table.sort(nameArray)
3. Wydrukuj zaktualizowaną tablicę:
local nameArray = {"Cat", "Mei", "Ana"}
table.sort(nameArray)
print(nameArray)
Tabele w danych wyjściowych są wyświetlane jako małe trójkąty, jak pokazano na rysunku
Kliknij trójkąt, aby go rozwinąć i zobaczyć całą tabelę, jak pokazano na rysunku
Tej samej metody można użyć do uporządkowania wartości liczbowych. Poniższy fragment kodu sortuje tablicę w porządku rosnącym, jak pokazano na rysunku
local testArray = {5, 2, 2, 10}
table.sort(testArray)
print(testArray)
OSTRZEŻENIE
Uważaj na liczby i łańcuchy podczas sortowania
Jeśli spróbujesz posortować tablicę mieszanych typów danych, takich jak liczby i łańcuchy, wszystko, co otrzymasz, to błąd:
-- Strings and numbers cannot be compared
local mixedArray = {5, "Frog", 2, 10}
Możesz użyć tostring() do konwersji z typów liczbowych na łańcuchowe, ale pamiętaj, że spowoduje to, że table.sort ułoży rzeczy w kolejności alfabetycznej, na przykład:
-- Numbers converted to strings will so rt alphabeticaly
local stringArray = {"10", "2", "5", "Frog"}
Sortowanie w porządku malejącym
Zarówno dane alfabetyczne, jak i numeryczne w poprzednich przykładach zostały posortowane w porządku rosnącym. Ale co, jeśli osoba, która ma najwięcej punktów, jest tak naprawdę ważnym czynnikiem? Istnieje drugi parametr, który można przekazać do funkcji table.sort(), a parametr ten umożliwia kontrolowanie sposobu sortowania tabeli. table.sort() działa na zasadzie przeglądania całej tablicy po dwie wartości naraz i porównywania wartości ze sobą. Domyślnie funkcja porównuje dwie wartości za pomocą operatora lessthan (<), dzięki czemu mniejsze liczby są na pierwszym miejscu. Aby dostosować algorytm sortowania, należy utworzyć nową funkcję do porównywania dwóch wartości, a następnie przekazać ją wraz z tablicą, jak pokazano w poniższym fragmencie kodu. Tutaj używany jest operator większy niż, więc większe wartości pojawią się najpierw:
-- First, set up the array
local testArray = {5, 2, 2, 10}
-- Second, create a function that shows how two values should be compared
local function DescendingSort(a, b)
return a > b
end
-- Third, pass the function into table.sort() along with the array.
table.sort(testArray, DescendingSort)
print(testArray)
Wyniki fragmentu kodu będą wyglądać jak na rysunku
Sortowanie słownika
Bardzo ważną rzeczą do zapamiętania w przypadku słowników w Lua jest to, że nie mają one gwarantowanej kolejności. Czasami mogą robić rzeczy w porządku, ale nie możesz na tym polegać. Innymi słowy, nie można tak naprawdę posortować słownika. Zamiast tego należy przekonwertować słownik na tablicę, której wyniki mogą wyglądać jak w poniższej tabeli, w której po lewej stronie znajduje się nieposortowany słownik, a po przekonwertowaniu na tablicę ten sam słownik. Za chwilę pokażemy Ci właściwą metodę konwersji słownika na tablicę.
Nieposortowany słownik
local IngredientDictionary = {
healthBerry = 10,
staminaOnion = 5,
speedPepper = 1,
}
Niesortowana tablica słowników
local sortingArray = {
{name = "healthBerry", amount = 10},
{name = "staminaOnion", amount = 5},
{name = "speedPepper", amount = 1},
}
Zauważ, że kolumna po prawej stronie jest tak naprawdę tablicą, ale każda wartość sama w sobie jest słownikiem. Tablice mogą zawierać dowolne prawidłowe typy danych, w tym słowniki, co pozwala na oznaczanie danych w celu ułatwienia ich sortowania. Po posortowaniu według nazwy tablica może wyglądać następująco:
Tablica posortowana według nazwy
local sortingArray = {
{name = "healthBerry", amount = 10},
{name = "speedPepper", amount = 1},
{name = "staminaOnion", amount= 5},
}
Znajdź, kto ma najwięcej punktów
Poświęć chwilę na stworzenie słownika z wyimaginowanymi wynikami graczy i przećwicz konwersję słownika do tablicy. Gdy słownik zostanie przekonwertowany na tablicę, nieco bardziej szczegółowa funkcja do porównywania wartości zostaną utworzone, a następnie przekazane.
1. Stwórz słownik czterech lub pięciu wyimaginowanych graczy i ich wyników, podobnie jak w przypadku następneg:
local playerScores = {
Ariel = 10,
Billie = 5,
Yichen = 4,
Kevin = 14,
}
2. Utwórz nową tablicę do przechowywania posortowanych wyników:
local playerScores = {
Ariel = 10,
Billie = 5,
Yichen = 4,
Kevin = 14,
}
local sortedArray = {}
3. Użyj funkcji pairs(), aby przejrzeć oryginalny słownik i wstaw każdą parę klucz/wartość do tablicy jako własny minisłownik:
-- Previous code
local sortedArray = {}
-- Go through dictionary, and assign each key/value pair to an index
for key, value in pairs(playerScores) do
table.insert(sortedArray, {playerName = key, points = value})
end
4. Skonfiguruj funkcję porównania. Tym razem chcesz porównać punkty i sprawić, by większe kwoty były pierwsze:
-- Previous code
local function sortByMostPoints(a, b)
return a.points > b.points
end
WSKAZÓWKA
Użyj notacji kropkowej, aby uzyskać dostęp do klawiszy słownika
Podczas sortowania algorytm patrzy na dwie wartości i ocenia je za pomocą funkcji porównania. W tym przypadku każda wartość jest tabelą, więc możesz użyć notacji kropkowej, aby przejść do właściwego klucza.
5. Przekaż tablicę i funkcję do table.sort() i wydrukuj wyniki:
local playerScores = {
Ariel = 10,
Billie = 5,
Yichen = 4,
Kevin = 14,
}
local sortedArray = {}
-- Go through dictionary, and assign each key/value pair to an index
for key, value in pairs(playerScores) do
table.insert(sortedArray, {playerName = key, points = value})
end
-- Set up comparison function
local function sortByMostPoints(a, b)
return a.points > b.points
end
-- Pass in array and function
table.sort(sortedArray, sortByMostPoints)
print(s ortedArray)
Sortowanie według wielu elementów informacji
Ostatnią rzeczą do omówienia przy użyciu algorytmu sortowania jest sortowanie według wielu informacji. Wyobraź sobie świat fantasy, w którym wchodzisz do sklepu i możesz kupić wiele rodzajów broni, takich jak te pokazane w tabeli
Nieposortowana broń
Nazwa broni: Rodzaj broni: Cena
Żelazny Miecz: Miecz: 250
Lekki łuk : Łuk : 150
Miecz treningowy: Miecz: 100
Krasnoludzki topór: Topór: 300
Galaktyczne Cięcie: Miecz: 500
Nieposortowane informacje utrudniają znalezienie tego, czego szukasz. Aby ułatwić zakupy, możesz wymienić broń według typu, a następnie według ceny, jak pokazano w tabeli
Broń posortowana według typu
Nazwa broni: Rodzaj broni: Cena
Krasnoludzki topór: Topór: 300
Lekki łuk : Łuk : 150
Miecz treningowy: Miecz: 100
Żelazny Miecz: Miecz: 250
Galaktyczne Cięcie: Miecz: 500
W formie kodu oryginalna tablica może wyglądać tak:
-- Original array
local inventory = {
{name = "Iron Sword", weaponType = "Sword", price = 250},
{name = "Light Bow", weaponType = "Bow", price = 150},
{name = "Training Sword", weaponType = "Sword", price = 100},
{name = "Dwarven Axe", weaponType = "Axe", price = 300},
{name = "The Galactic Slash", weaponType = "Sword", price = 500},
}
Ponieważ jest to już tablica, nie trzeba by jej konwertować, co oznacza, że następną rzeczą byłoby skonfigurowanie funkcji porównania. Najpierw możesz porównać typy lub, jeśli są tego samego typu, porównać rodzaj i cenę:
-- Sort first by most weapon type, then by price
local function sortByTypeAndPrice(a, b)
return (a.weaponType < b.weaponType)
or (a.weaponType == b.weaponType and a.price < b.price)
end
WSKAZÓWKA
Słowa kluczowe nie mogą być nazwami kluczy
Typ sam w sobie jest słowem kluczowym. Dlatego najlepiej jest użyć czegoś takiego jak weaponType zamiast typu.
Na koniec przekaż tablicę i funkcję porównania do table.sort() i wydrukuj wynik:
table.sort(inventory, sortByTypeAndPrice)
print(inven tory)
Streszczenie
Algorytmów używałeś już wcześniej, ale teraz wiesz, jak się nazywają. Istnieje wiele różnych algorytmów sortowania, z których każdy ma swoje mocne i słabe strony. Za kulisami Roblox Studio table.sort() używa tak zwanego szybkiego sortowania. Jeśli chcesz dowiedzieć się więcej o algorytmach sortowania lub stworzyć własne, w Internecie jest mnóstwo informacji na ich temat; po prostu wyszukaj "algorytm sortowania hms".
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na poniższe pytania.
Kartkówka
1. Co to jest algorytm?
2. Jakie są trzy składniki algorytmu?
3. Jaki jest pierwszy parametr metody table.sort()?
4. Jaki jest opcjonalny drugi parametr metody table.sort()?
5. Jeśli chcesz użyć table.sort() do wyświetlenia listy graczy z najszybszymi czasami, użyj ___operator.
6. Prawda czy fałsz: Słowniki można również sortować za pomocą table.sort().
Odpowiedzi
1. Algorytm to określony zestaw kroków, które można wykorzystać do rozwiązania problemu.
2. Przyjmuje dane wejściowe, przechodzi przez zestaw uporządkowanych kroków, wyprowadza rozwiązanie.
3. Nazwa tablicy do sortowania.
4. Funkcję niestandardowego komparatora można przekazać do drugiego parametru.
5. Operator mniej niż (<).
6. Fałsz. Słowniki muszą zostać przekonwertowane na tablice przed sortowaniem.
Ćwiczenia
Typowym zestawem statystyk związanych z rywalizacją jest liczba zabójstw, zgonów i asyst danej osoby (zabójstw innych osób, do których się przyczyniła). Weź przykładowy słownik, taki jak ten pokazany tutaj, i posortuj go według tego, kto ma najwięcej zabójstw. Jeśli liczba zabójstw jest równa, uszereguj, kto najbardziej pomagał innym członkom drużyny.
Porady
- Dołącz od trzech do pięciu różnych graczy i postaraj się, aby niektórzy z nich mieli remis w większości zabójstw. Jeśli chcesz, możesz użyć następującego przykładowego słownika:
local playerKDA = {
Ana = {kills = 0, deaths = 2, assists = 20},
Beth = {kills = 7, deaths = 5, assists = 0},
Cat = {kills = 7, deaths = 0, assists = 5},
Dani = {kills = 5, deaths = 20, assists = 8},
Ed = {kills = 1, deaths = 1, assists = 8},
}
- Będziesz musiał przekonwertować słownik na tablicę.
- Wydrukowanie tablicy zaraz po jej utworzeniu i przed jej posortowaniem może być dobrym krokiem do rozwiązania problemu, aby upewnić się, że tablica wygląda tak, jak tego oczekujesz.
Zapisywanie danych
Bez specjalnego mechanizmu zapisywania informacji wszystko, co ludzie w Twoich doświadczeniach zarabiają lub osiągają między sesjami gry, jest tracone. Gdy dana osoba opuści doświadczenie, punkty, złoto i zakupy przepadają w niepamięć. Ta godzina dotyczy sposobu zapisywania danych, aby nic nie zostało utracone między sesjami. Dane zapisywane z jednej sesji do drugiej są przechowywane w specjalnych tabelach, najczęściej w Data Stores. Magazyny danych działają jak słownik, w którym klucze i wartości mogą być przechowywane w chmurze za pomocą Roblox. Ta godzina zaczyna się od utworzenia pola, które śledzi, ile razy zostało kliknięte; następnie temat zmienia się na to, jak zminimalizować ryzyko utraty danych gracza.
Włączanie magazynów danych
Magazyny danych są dostępne tylko dla doświadczeń zapisanych w chmurze Roblox. Aby korzystać z magazynów danych, musisz zaktualizować niektóre ustawienia zabezpieczeń:
1. Upewnij się, że doświadczenie zostało opublikowane w Roblox, a nie tylko zapisane lokalnie na twoim komputerze.
2. Na karcie Strona główna kliknij Ustawienia gry.
3. Wybierz Zabezpieczenia i włącz opcję Włącz Studio Access to API Services. Następnie możesz zapisać i wyjść z Ustawień gry.
Tworzenie magazynu danych
Po włączeniu magazynów danych możesz uzyskać usługę DataStoreService w skrypcie. Poszczególne sklepy można tworzyć i uzyskiwać do nich dostęp za pomocą funkcji GetDataStore("DataStoreName"):
local DataStoreService = game:GetService("DataStoreService")
local dataStoreName= DataStoreService:GetDataStore("DataStoreName")
GetDataStore("DataStoreName") pobiera pasujący magazyn danych lub tworzy magazyn o tej nazwie, jeśli jeszcze nie istnieje.
Korzystanie z DataStore
Pamiętaj, że magazyny danych działają jak słowniki. Wszystkie dane w obrębie są przechowywane przy użyciu par klucz-wartość. Pary klucz-wartość można tworzyć i aktualizować za pomocą dataStoreName. SetAsync("Nazwa klucza", wartość). Lub możesz pobrać informacje za pomocą dataStoreName.GetAsync("KeyName"):
local DataStoreService = game:GetService("DataStoreService")
local dataStoreName = DataStoreService:GetDataStore("DataStoreName")
-- Update info in the Data Store, or create a new key/value pair
local updateStat = dataStoreName:SetAsync("StatName", value)
-- Retrieve information from the store using the key name
local storedStat = dataStoreName:GetAsync("StatName")
Pamiętaj, że SetAsync() zastąpi wartość klucza, jeśli już istnieje. Po nadpisaniu te informacje znikają. To jeden z powodów, aby upewnić się, że zawsze używasz unikalnych nazw kluczy.
Śledź liczbę kliknięć
Magazyny danych mogą przechowywać wszelkiego rodzaju informacje, które normalnie można przechowywać w słowniku. Magazyn danych, który utworzysz w tym Wypróbuj sam, śledzi liczbę kliknięć tej skrzynki
.
Nie chcesz zbyt często aktualizować magazynów danych, ponieważ częste aktualizacje mogą powodować opóźnienia w grze lub brak zapisywania danych. Będziesz więc używać pętli while do co jakiś czas aktualizowania magazynu danych.
Konfiguracja
Potrzebujesz części z etykietą TextLabel, więc wykonaj następujące czynności:
1. Wstaw część lub siatkę.
2. Włóż SurfaceGui.
3. Wstaw TextLabel do SurfaceGui o nazwie ClickDisplay.
4. Wybierz skrzynkę i wstaw ProximityPrompt o nazwie CratePrompt. Nie martw się o ustawienie HoldDuration. Twoja hierarchia powinna wyglądać mniej więcej tak jak Figure
Menedżer skrzynek
Użyjesz dwóch skryptów. Pierwszy będzie zarządzał ProximityPrompt i aktualizował Data Store. Drugi skrypt obsłuży domyślny ekran:
1. W ServerScriptService dodaj nowy skrypt o nazwie CrateManager.
2. Uzyskaj ProximityPromptService i DataStoreService.
3. Utwórz nowy magazyn danych o nazwie CrateData:
local ProximityPromptService = game:GetService("ProximityPromptService")
local DataStoreService = game:GetService("DataStoreService")
local crateData = DataStoreService:GetDataStore("CrateData")
4. Dodaj dwie stałe: jedną dla tego, jak często gracze będą mogli kliknąć monit, a drugą dla tego, jak często Data Store będzie aktualizowany:
local DISABLED_DURATION = 0.1
local SAVE_FREQUENCY = 10.0
5. Uzyskaj łączną liczbę dotychczasowych kliknięć z Data Store; jeśli nie ma jeszcze danych, zacznij od 0.
local DISABLED_DURATION = 0.1
local SAVE_FREQUENCY = 10.0
-- Get the current value of TotalClicks, or set to 0 if it doesn't exist
local totalClicks = crateData:GetAsync("TotalClicks") or 0
6. Skonfiguruj wszystko, czego potrzebujesz do nowej funkcji połączonej ze zdarzeniem PromptTriggered monitu:
local function onPromptTriggered(prompt, player)
if prompt.Name == "CratePrompt" then
prompt.Enabled = false
end
end
ProximityPromptService.PromptTriggered:Connect(onPromptTriggered)
7. Zaktualizuj totalClicks i wyświetlany tekst za każdym razem, gdy gracz kliknie:
-- Get the current value of TotalClicks, or set to 0 if it doesn't exist
local totalClicks = crateData:GetAsync("TotalClicks") or 0
local function onPromptTriggered(prompt, player)
if prompt.Name == "CratePrompt" then
prompt.Enabled = false
local crate = prompt.parent
local clickDisplay = crate:FindFirstChild("ClickDisplay", true)
totalClicks = totalClicks + 1
clickDisplay.Text = totalClicks
wait(DISABLED_DURATION)
prompt.Enabled = true
end
end
ProximityPromptService.Promp tTriggered:Connect(onPromptTriggered)
WSKAZÓWKA
Szukaj dzieci
Dodanie wartości true jako drugiego parametru funkcji FindFirstChild() jest jednym ze sposobów przejrzenia wszystkich elementów podrzędnych obiektu, a następnie przejrzenia elementów podrzędnych tych elementów w celu znalezienia tego, czego się szuka.
8. Użyj pętli while, aby co pewien czas aktualizować magazyn danych:
local ProximityPromptService = game:GetService("ProximityPromptService")
local DataStoreService = game:GetService("DataStoreService")
local crateData = DataStoreService:GetDataStore("CrateData")
local DISABLED_DURATION = 0.1
local SAVE_FREQUENCY = 10.0
-- Get the current value of TotalClicks, or set to 0 if it doesn′t exist
local totalClicks = crateData:GetAsync("TotalClicks") or 0
local function onPromptTriggered(prompt, player)
if prompt.Name == "CratePrompt" then
prompt.Enabled = false
local crate = prompt.parent
local clickDisplay = crate:FindFirstChild("ClickDisplay", true)
totalClicks = totalClicks + 1
clickDisplay.Text = totalClicks
wait(DISABLED_DURATION)
prompt.Enabled = true
end
end
ProximityPromptService.PromptTriggered:Connect(onPromptTriggered)
-- Update the Data Store every so often
while wait(SAVE_FREQUENCY) do
crateData:Set Async("TotalClicks", totalClicks)
end
Skrypt skrzyni
Ten drugi, krótszy skrypt będzie używany do aktualizowania wyświetlanego tekstu na początku każdej sesji, zanim ktokolwiek kliknie na skrzynkę.
1. Wybierz skrzynię i wstaw skrypt.
2. Pobierz DataStoreService i właśnie utworzony magazyn danych.
3. Utwórz referencje dla skrzyni i etykiety TextLabel:
local DataStoreService = game:GetService("DataStoreService")
local crateData = DataStoreService:GetDataStore("CrateData")
local crate = script.Parent
local clickDisplay = crate:FindFirstChild("ClickDisplay", true)
4. Dodaj wartość domyślną, która będzie używana w przypadku, gdy skrzynka nigdy nie została kliknięta:
local DEFAULT_VALUE = 0
5. Pobierz aktualną liczbę kliknięć z Data Store:
local DEFAULT_VALUE = 0
local totalClicks = crateData:GetAsync("TotalClicks")
6. Zaktualizuj TextLabel, aby wyświetlić aktualną liczbę lub wartość domyślną, jeśli TotalClicks nie zostanie znaleziony:
local DataStoreService = game:GetService("DataStoreService")
local crateData = DataStoreService:GetDataStore("CrateData")
local crate = script.Parent
local clickDisplay = crate:FindFirstChild("ClickDisplay", true)
local DEFAULT_VALUE = 0
local totalClicks = crateData:GetAsync("TotalClicks")
clickDisplay.Text = totalClicks or DEFAULT_VALUE
Przetestuj swoje miejsce. Powinieneś być w stanie zatrzymać test gry, a następnie uruchomić go ponownie ze zaktualizowaną liczbą kliknięć wyświetlaną na skrzynce. Czasami aktualizacja wartości może zająć sekundę.
WSKAZÓWKA
Upewnij się, że używasz unikalnych nazw kluczy
Pamiętaj, że klucze w magazynie danych muszą być unikalne. Jeśli zduplikujesz skrzynkę, każde kliknięcie na którąkolwiek ze skrzynek zwiększy sumę. Jeśli chcesz, aby ich sumy pozostały oddzielne, każda skrzynia potrzebuje własnego odrębnego klucza.
Ograniczanie liczby połączeń
SetAsync() i GetAsync() to wywołania sieciowe, a korzystanie z nich często może być ryzykowne, jeśli masz słabe połączenie z Internetem lub wysyłasz więcej połączeń, niż sieć może obsłużyć jednocześnie. Dlatego do aktualizacji magazynu danych użyto pętli while, zamiast aktualizacji za każdym razem, gdy gracz kliknął. Każde żądanie połączenia jest dodawane do kolejki, a w kolejce jest tylko określona liczba miejsc, zanim kolejka się zapełni i po prostu nie będzie przyjmować więcej żądań. Inne dobre momenty na aktualizację magazynu danych to moment dołączania, opuszczania gracza lub zamykania serwera.
Ochrona Twoich danych
Oprócz upewnienia się, że nie wysyłasz zbyt wielu połączeń naraz, innym sposobem upewnienia się, że połączenia sieciowe nie zostaną pominięte lub zrzucone, jest zawsze używanie chronionego połączenia - pcall(). Chronione połączenia śledzą, aby upewnić się, że połączenie sieciowe zostało zrealizowane. Jeśli połączenie nie powiodło się, zostanie wysłany komunikat o błędzie, który pomoże Ci dowiedzieć się, co poszło nie tak. pcall() przyjmuje funkcję i zwraca dwie wartości. Pierwsza wartość to wartość logiczna określająca, czy wywołanie zostało zrealizowane; druga wartość dotyczy wszelkich zwróconych komunikatów o błędach:
local setSuccess, errorMessage = pcall(funkcjaNazwa)
pcall() akceptuje tylko funkcje, więc jeśli nie chcesz wcześniej tworzyć funkcji, możesz przekazać wywołanie sieciowe za pomocą funkcji anonimowej:
local setSuccess, errorMessage = pcall(function()
dataStoreName:SetAsync(key, value)
end)
Następnie możesz przetestować zwróconą wartość, aby upewnić się, że przeszła. Tutaj, jeśli setSuccess ma wartość false, zostanie wydrukowany komunikat o błędzie:
if not setSuccess then
print(errorMessage)
end
Gdybyś miał zaktualizować pętlę while w ostatniej sekcji, mogłoby to wyglądać tak:
-- Update the Data Store every so often
while wait(SAVE_FREQUENCY) do
local setSuccess, errorMessage = pcall(function()
crateData:SetAsync("TotalClicks", totalClicks)
end)
if not setSuccess then
print(errorMessage)
else
print("Current Coun t:")
print(crateData:GetAsync("TotalClicks"))
end
end
Zapisywanie danych gracza
Jeśli zapisujesz dane gracza, pamiętaj, że czasami imię gracza może się zmienić. Bezpieczniejszą alternatywą jest użycie playerID do zapisania danych. Możesz uzyskać identyfikator gracza od samego gracza, używając następującego kodu:
local Players = game:GetService("Players")
local function onPlayerAdded(player)
local playerKey = "Player " .. player.UserId
end
Players.PlayerAdded:Connect(onPlayerAdded)
Używanie UpdateAsync do aktualizowania magazynu danych
UpdateAsync(), która jest podobna do SetAsync(), powinna być używana do aktualizowania magazynu danych, jeśli więcej niż jeden serwer ma szansę uzyskać dostęp do tego samego magazynu danych w tym samym czasie. Jeśli masz do czynienia z Robuxem lub masz doświadczenie, które przyciąga wiele osób, prawdopodobnie chcesz przejść do korzystania z UpdateAsync(). Po wywołaniu funkcja UpdateAsync() zwraca starą wartość klucza, a następnie ją aktualizuje.
local updateSuccess, errorMessage = pcall(function()
pointsDataStore:UpdateAsync(playerKey, function(oldValue)
local newValue = oldValue or 0
newValue = newValue + GOLD_ON_JOIN
return newValue
end)
end)
W ramach tego otrzymujesz magazyn danych w normalny sposób i używasz UpdateAsync() do przekazania klucza jako pierwszego parametru:
local updateSuccess, errorMessage = pcall(function()
pointsDataStore:UpdateAsync(playerKey, function(oldValue)
local newValue = oldValue or 0
newValue = newValue + GOLD_ON_JOIN
return newValue
Na koniec drugi parametr przyjmuje funkcję, która akceptuje starą wartość i zwraca wartość zaktualizowaną. Możesz wcześniej utworzyć funkcję lub użyć funkcji anonimowej, jak pokazano:
local updateSuccess, errorMessage = pcall(function()
pointsDataStore:UpdateAsync(playerKey, function(oldValue)
local newValue = oldValue or 0
new Value = newValue + GOLD_ON_JOIN
return newValue
end)
end)
Streszczenie
Możesz teraz zapisywać dane, co otwiera przed Tobą możliwości, takie jak zarabianie, które wcześniej nie były dostępne. Możesz zapisać prawie każdy rodzaj danych, jaki tylko przyjdzie Ci do głowy. W grze RPG możesz zapisywać poziom umiejętności ludzi, sprawność broni i ekwipunek. W grach rywalizacyjnych możesz zapisać rangę gracza lub średnie KDA. Możesz także rozpocząć śledzenie, czy ludzie kupili przedmioty w Twoim doświadczeniu, takie jak zwierzęta domowe, ulepszenia i broń. Magazyny danych są naprawdę potężne; musisz tylko upewnić się, że zawsze używasz unikalnych kluczy i sprawdzić, czy zarówno wysyłasz, jak i odbierasz poprawne dane za pomocą pcalls(). Ostatnią rzeczą, jakiej chcesz, jest społeczność ludzi, którzy są wściekli, że ich zakupy przepadły.
Pytania i odpowiedzi
P. W jaki inny sposób możesz zapisywać i aktualizować dane graczy?
O. Oprócz funkcji SetAsync() dostępne są dodatkowe funkcje, takie jak UpdateAsync() i IncrementAsync(). Gdy pracujesz nad większymi doświadczeniami, szczególnie jeśli masz do czynienia z Robuxem, zdecydowanie zaleca się korzystanie z funkcji UpdateAsync(). To trochę więcej pracy, ale dodaje warstwę ochrony danych. Dowiedz się więcej o tych dodatkowych metodach w Developer Hub.
P. Skąd mam wiedzieć, co oznacza zwrócony błąd?
O. Możesz przeszukać centrum Roblox Developer, aby znaleźć listę typowych błędów, a także ograniczenia dotyczące częstotliwości wysyłania próśb:
https://developer.roblox.com/articles/Datastore-Errors.
Warsztat
Teraz, gdy skończyłeś ed, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Jaki jest wzorzec pobierania magazynu danych (nie tylko klucza magazynu danych)?
2. Co oznacza p w pcall()?
3. Kiedy należy używać pcall()?
4. Magazyny danych pobierają i przechowują dwie informacje; czym oni są?
5. Jeśli istnieje prawdopodobieństwo, że więcej niż jeden serwer może jednocześnie aktualizować magazyn danych, czy należy użyć funkcji SetAsync() czy UpdateAsync()?
Odpowiedzi
1. local dataStoreName= DataStoreService:GetDataStore("DataStoreName")
2. Chroniony
3. Powinieneś używać pcall() za każdym razem, gdy czytasz lub aktualizujesz informacje z magazynu danych.
4. Klucz i wartość
5. UpdateAsync()
Ćwiczenia
Korzystając z informacji z tej godziny, powinieneś być w stanie nagrodzić ludzi pięcioma sztukami złota za każdym razem, gdy się zalogują. Możesz wyświetlać złoto ludzi na tablicy wyników, ale na potrzeby tego ćwiczenia wystarczy wydrukować ile złota ma osoba po aktualizacji.
Porady
- Upewnij się, że używasz identyfikatora gracza, a nie jego imienia i nazwiska.
- Nie zapomnij sprawdzić powodzenia podczas czytania i aktualizowania magazynu danych
Tworzenie pętli gry
Ta część wprowadza koncepcję pętli gry lub schematu działań, które mają miejsce w grze. Dowiesz się, jak skonfigurować prostą grę opartą na rundach, w której gracze są transportowani na arenę i z powrotem po określonym czasie. Aby to zrobić, potrzebujesz wszystkich umiejętności, których nauczyłeś się do tej pory, i dodaj jedną nową umiejętność: używanie BindableEvents. Prawdziwą lekcją tej godziny jest zastanowienie się, jak zorganizować swój świat i zawarte w nim skrypty. Główny projekt koncentruje się na najlepszych praktykach dotyczących organizacji obszaru roboczego, a także kodu.
Konfigurowanie pętli gier
Pętle gry to wzorce działań, które ludzie wykonują podczas korzystania z Roblox. Istnieje nieskończona różnorodność pętli, przez które mogą przechodzić ludzie w waszych światach. Oto kilka przykładów:
* W symulatorach żniwnych jednym ze schematów jest zbieranie przedmiotów, sprzedawanie przedmiotów, kupowanie większego plecaka lub szybszej łopaty, a następnie zbieranie jeszcze większej liczby przedmiotów.
* W trybie rywalizacji uczestnicy mogą zostać zabrani z lobby, a następnie teleportowani na arenę, aby walczyć w 15-minutowym meczu. Pod koniec meczu wszyscy wracają do lobby, aby przygotować się do następnego meczu.
* W świecie eksploracji jednostki mogą przechodzić przez cykle gotowania, wydobywania i polowania, aby ulepszyć swój sprzęt i umiejętności.
* Podczas doświadczenia edukacyjnego uczniowie mogą przeprowadzić wirtualną sekcję, zapisać swoje odkrycia, a następnie przejść do innego organizmu, aby porównać różnice.
Kod, który tworzysz, musi być w stanie ułatwiać pętle, przez które ludzie naturalnie chcą przechodzić, zapewniając im okresy zarówno podniecenia, jak i odpoczynku. Pętla, przez którą przejdziesz w tej godzinie, to prosta gra oparta na rundach. Uczestnicy zostaną teleportowani z lobby na arenę. Gdy znajdą się na arenie, mają określoną ilość czasu na ukończenie obby o tematyce księżycowej, zanim zostaną przetransportowani z powrotem do lobby. Ta pętla gry to taka, którą można rozszerzyć, jeśli później chcesz wprowadzić elementy, takie jak rywalizacja między graczami, rozwiązywanie zagadek lub zbieranie przedmiotów ms.
Praca z BindableEvents
Ten projekt wymaga wielu działań w różnych fazach pętli gry. Aby wiedzieć, kiedy te rzeczy mają się wydarzyć, używasz BindableEvents, które są podobne do RemoteEvents. To, co je wyróżnia, to możliwość komunikowania się z serwerem lub klientem z klientem, podczas gdy RemoteEvents wysyłają sygnały przez podział klient-serwer. BindableEvents do użytku na serwerze powinny być umieszczone w ServerStorage. W ramach ServerStorage najlepszą praktyką jest tworzenie folderów dla różnych typów obiektów w celu ich uporządkowania. Rysunek przedstawia dwa BindableEvents w folderze i drugi folder dla ModuleScripts.
BindableEvents są uruchamiane przy użyciu EventName:Fire(). Zdarzenie wywołane przez BindableEvents nosi nazwę Event. Funkcje można następnie połączyć ze zdarzeniem w normalny sposób:
EventName.Event:Connect(functionName)
Utwórz uproszczoną pętlę gry
Przez resztę tej godziny będziesz pracować nad wyzwaniem Moon Obby, skupiając się na uporządkowaniu skryptów i zasobów. Pierwszym krokiem, jaki musisz wykonać, jest utworzenie dwóch odrębnych obszarów: holu i areny. Możesz uczynić je tak szczegółowymi lub funkcjonalnymi, jak chcesz. Rysunek przedstawia rozbudowany hol i arenę; prostsze wersje są pokazane po prawej stronie.
Następnie skonfigurujesz wydarzenia oznaczające początek i koniec każdej rundy, a także skonfigurujesz kod uproszczonej pętli gry.
Konfiguracja
Głównym tematem tego rozdziału jest organizacja, która obejmuje organizowanie zasobów twojego świata. Zachowasz wszystkie zasoby lobby i areny w ich własnych folderach, a one będą miały własne miejsca odradzania.
1. Utwórz dwa obszary, które chcesz wykorzystać w swoim świecie, takie jak te pokazane na powyższym rysunku.
2. Rozdziel wszystkie elementy z dwóch obszarów na osobne foldery .
3. W folderze Lobby dodaj miejsce odradzania o nazwie StartSpawn. Następnie dodaj drugą lokalizację odradzania w folderze Arena
W razie potrzeby użyj dodatkowych folderów
Na powyższym rysunku widać dodatkowy folder o nazwie Environment; wszystkie części otoczenia zostały umieszczone w środku. Nie musisz tego robić, ale jest to pomocne.
5. Wewnątrz folderu Events dodaj dwa nowe zdarzenia BindableEvent. Nazwij jedno wydarzenie RoundStart, a drugie RoundEnd
WSKAZÓWKA
Motyw Twój świat
Jeśli chcesz stworzyć ustawienie przypominające księżyc, możesz zmienić powagę doświadczenia w Ustawieniach gry > Świat. W przeciwnym razie możesz skupić się na kodzie, jeśli nie chcesz tworzyć prawdziwego obiektu księżyca.
v
Skrypt modułu RoundSettings
Podstawowe ustawienia, które kontrolują, jak długo trwa każda runda i ile osób jest wymaganych przed rozpoczęciem, zostaną wyciągnięte do własnego ModuleScript. Dzięki temu aktualizacja często zmienianych ustawień jest łatwiejsza dla Ciebie i łatwiejsza dla każdego, kto może z Tobą współpracować w przyszłości.
1. W ServerStorage dodaj nowy folder o nazwie ModuleScripts.
2. W tym folderze utwórz nowy ModuleScript o nazwie RoundSettings
3. Wpisz wartości określające, jak długo powinna trwać każda runda, ile czasu gracze spędzają w obby oraz minimalną liczbę osób potrzebną do rozpoczęcia rundy. Nie zapomnij zmienić nazwy tabeli:
local RoundSettings = {}
-- Game Variables
RoundSettings.intermissionDuration = 5
RoundSettings.roundDuration = 15
RoundSettings.minimumPeople = 1
return RoundSettings
Menedżer rundy
Pętla będzie działać w skrypcie po stronie serwera. Podczas działania uruchomi zdarzenia w odpowiednim czasie. Oddzielny ModuleScript będzie wtedy nasłuchiwał tych zdarzeń. 1. W folderze ModuleScript dodaj nowy skrypt ModuleScript o nazwie PlayerManager
2. W ServerScriptService dodaj nowy zwykły skrypt o nazwie RoundManager
3. Skorzystaj z usług:
-- Services
local ServerStorage = game:GetService("ServerStorage")
local Players = game:GetService("Players")
4. Skonfiguruj odwołania do folderu ModuleScripts i dwóch skryptów ModuleScripts, których będziesz używać:
-- Module Scripts
local moduleScripts = ServerStorage.ModuleScripts
local playerManager = require(moduleScripts.PlayerManager)
local roundSettings = require(moduleScripts.RoundSettings)
5. Uzyskaj dwa zdarzenia BindableEvent: RoundStart i RoundEnd:
-- Events
local events = ServerStorage.Events
local roundStart = events.RoundStart
local roundEnd = events.RoundEnd
6. Utwórz pętlę while-true-do. W nim użyj ustawień z ModuleScript, aby czekać odpowiednią liczbę osób, aby dołączyć do doświadczenia, a następnie uruchomić RoundStart:
-- Runs the game loop
while true do
repeat
wait(roundSettings.intermissionDuration)
until Players.NumPlayers >= roundSettings.minimumPeople
roundStart:Fire()
wait (rou ndSettings.roundDuration)
roundEnd:Fire()
end
WSKAZÓWKA
Powtarzaj, aż warunek zostanie spełniony
Do tej pory często używaliśmy pętli while, która działa, dopóki warunek nie stanie się fałszywy. Tutaj użyliśmy powtarzania do, co działa odwrotnie; działa, dopóki warunek nie stanie się prawdziwy. W tym przypadku warunkiem jest dołączenie minimalnej liczby graczy.
7. Odczekaj czas określony w RoundSettings na czas trwania rundy, a następnie odpal RoundEnd. Oto gotowy skrypt:
-- Services
local ServerStorage = game:GetService("ServerStorage")
local Players = game:GetService("Players")
-- Module Scripts
local moduleScripts = ServerStorage.ModuleScripts
local playerManager = require(moduleScripts.PlayerManager)
local roundSettings = require(moduleScripts.RoundSettings)
-- Events
local events = ServerStorage.Events
local roundStart = events.RoundStart
local roundEnd = events.RoundEnd
while true do
repeat
wait(roundSettings.intermissionDuration)
until Players.NumPlayers >= roundSettings.minimumPeople
roundStart:Fire()
wait(roundSettings.roundDuration)
roundEnd:Fire()
end
To jest pętla, która będzie się powtarzać w kółko. Możesz teraz dodać funkcjonalność do rund, nasłuchując wystrzelonych zdarzeń.
Menedżer graczy
W tym miejscu będzie mięso twojego kodu. Tutaj trafia wszystko, co dzieje się z graczami wchodzącymi i wychodzącymi z rundy. Możesz dawać ludziom broń, przydzielać zespoły, rejestrować wyniki lub robić prawie wszystko, co tylko przyjdzie ci do głowy. Na razie będziemy je po prostu przenosić do i z obby.
1. Zacznij od swoich usług:
local PlayerManager = {}
-- Services
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
return PlayerManager
2. Skonfiguruj zmienne dla odradzania lobby, mapy areny i odradzania areny:
local PlayerManager = {}
-- Services
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
-- Variables
local lobbySpawn = workspace.Lobby.StartSpawn
local arenaMap = workspace.Arena
local arenaSpawn = arenaMap.SpawnLocation
return PlayerManager
3. Uzyskaj zdarzenia tak, jak w ostatnim skrypcie:
local PlayerManager = {}
-- Services
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
-- Variables
local lobbySpawn = workspace.Lobby.StartSpawn
local arenaMap = workspace.Arena
local arenaSpawn = arenaMap.SpawnLocation
local events = ServerStorage.Events
local roundEnd = events.RoundEnd
local roundStart = events.RoundStart
return PlayerManager
4. Zacznij od tego, co powinno się stać, gdy gracz po raz pierwszy dołączy do doświadczenia, zanim wejdzie na arenę. Tutaj po prostu odrodzimy je na arenie:
local PlayerManager = {}
Previous code not shown
local function onPlayerJoin(player)
player.RespawnLocation = lobbySpawn
end
return PlayerManager
WSKAZÓWKA
Uzyskaj dowolne zapisane dane
Jeśli zdecydujesz się dodać zapisane dane - takie jak poziom umiejętności, ulepszenia wyglądu lub łączna liczba punktów - możesz to sprawdzić i zaktualizować tabelę wyników.
5. Powiedz, co chcesz, aby się stało na początku rundy. Tutaj przeglądamy listę graczy i przeładowujemy ich postać w miejscu odrodzenia areny:
local PlayerManager = {}
-- Previous code not shown
local function onRoundStart()
for _, player in ipairs(Players:GetPlayers()) do
player.RespawnLocation = arenaSpawn
player:LoadCharacter()
end
end
return PlayerManager
6. Utwórz funkcję, która będzie uruchamiana na koniec rundy:
local PlayerManager = {}
-- Previous code not shown
local function onRoundEnd()
for _, player in ipairs(Players:GetPlayers()) do
player.RespawnLocation = lobbySpawn
player:LoadCharacter()
end
end
return PlayerManager
7. Połącz funkcje tak, aby działały we właściwym czasie:
local PlayerManager = {}
-- Services
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
-- Variables
local lobbySpawn = workspace.Lobby.StartSpawn
local arenaMap = workspace.Arena
local arenaSpawn = arenaMap.SpawnLocation
local events = ServerStorage.Events
local roundEnd = events.RoundEnd
local roundStart = events.RoundStart
local function onPlayerJoin(player)
player.RespawnLocation = lobbySpawn
end
local function onRoundStart()
for _, player in ipairs(Players:GetPlayers()) do
player.RespawnLocation = arenaSpawn
player:LoadCharacter()
end
end
local function onRoundEnd()
for _, player in ipairs(Players:GetPlayers()) do
player.RespawnLocation = lobbySpawn
player:LoadCharacter()
end
end
Players.PlayerAdded:Connect(onPlayerJoin)
round Start.Event:Connect(onRoundStart)
roundEnd.Event:Connect(onRoundEnd)
return PlayerManager
Streszczenie
Pętla gry lub pętla akcji to wzorzec działań podejmowanych przez ludzi w ramach Twojego doświadczenia. Napisany kod musi obsługiwać te działania. W ciągu tej godziny utworzyłeś pętlę przy użyciu literalnie zakodowanej pętli while. W innych doświadczeniach pętla może nadal być napędzana przez zdarzenia, takie jak zbiory, kupowanie i sprzedawanie, ale mogą one nie być tak dosłownie okrężne. Po zaprojektowaniu podstawowej pętli możesz kontynuować dodawanie funkcji, takich jak ogłoszenia na serwerze, przydziały drużyn, możliwość dodawania losowych map i aktualizowanie zapisanych danych. BindableEvents są dość często używane z pętlami gier, ponieważ umożliwiają wysyłanie sygnałów do serwera lub klienta do klienta. W tej godzinie dużo mówiono także o utrzymywaniu porządku w obiektach w projektach, co jest równie ważne jak pisanie czystego kodu, np. Używaj folderów do organizowania skryptów, modeli, wydarzeń i wszystkiego innego w swoim świecie.
Pytania i odpowiedzi
P. Dlaczego użyłeś funkcji LoadCharacter() zamiast zaktualizować ramkę CFrame HumanoidRootPart?
O. Jeśli wszystko, co chcesz zrobić, to po prostu przenieść gracza z jednego miejsca do drugiego, aktualizacja jego pozycji CFrame to szybki sposób na zrobienie tego. Istnieją jednak pewne korzyści, które można wykorzystać, zmuszając postać do przeładowania. SpawnLocations są w stanie zapewnić tymczasowe pola siłowe, a także mogą być wykorzystywane do zadań zespołowych i punktów kontrolnych.
Warsztaty
Skoro już skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Prawda czy fałsz: zdarzenia BindableEvent mogą wysyłać sygnały między serwerem a klientem.
2. Co robi pętla powtarzania do momentu?
3. Gdzie należy przechowywać BindableEvents?
4. Gdybyś miał dodać funkcjonalność, taką jak nadanie graczom supermocy, jak byś to zrobił?
Odpowiedzi
1. Fałsz. Jeśli między serwerem a klientem musi zostać wysłany sygnał, należy użyć RemoteEvents.
2. Pętla repeat-until powtarza się, dopóki warunek nie zostanie spełniony, co jest przeciwieństwem pętli while-do.
3. Jeśli BindableEvents będą używane przez serwer, powinny być przechowywane we własnym folderze w ServerStorage. Jeśli BindableEvents są używane przez klienta, są one przechowywane w ReplicatedStorage.
4. Możesz dodać funkcjonalność w roundStart() i usunąć ją w roundEnd(). Lub, jeśli jest to większy fragment kodu, możesz chcieć, aby w RoundManager był również wymagany osobny ModuleScript.
Ćwiczenia
Zawsze dobrze jest informować graczy o tym, co się z nimi stanie. Utwórz nowy ModuleScript zaprojektowany do ogłaszania początku i końca każdego meczu. Na razie dobrze jest po prostu poćwiczyć z BindableEvents i po prostu wypisać "Round Starting" i "Round Over" na początku i na końcu każdej rundy. W prawdziwym doświadczeniu chciałbyś skonfigurować interfejs użytkownika, aby ogłosić graczom.
Porady
- Utwórz osobny moduł o nazwie Ogłoszenia.
- W ramach modułu utwórz osobne funkcje dla zapowiedzi na początku i na końcu meczu.
- Dodaj moduł do tych wymaganych w RoundManager.
- Użyj instrukcji print, aby sprawdzić wszystko, a następnie, jeśli chcesz, przejdź do tworzenia bardziej sfinalizowanego kodu interfejsu użytkownika
Zarabianie: jednorazowe zakupy
Ta część dotyczy tego, jak zezwolić użytkownikom na kupowanie przedmiotów w twoich doświadczeniach za pomocą Robux. Możesz użyć zarobionych Robuxów w innych grach lub na zakup przedmiotów z katalogu, lub ostatecznie możesz wypłacić prawdziwe pieniądze za pomocą Programu wymiany programistów. Aby wypłacić pieniądze, musisz mieć aktywne członkostwo Roblox Premium, mieć co najmniej 13 lat i zdobyć co najmniej 100 000 zarobionych Robuxów. Aby zapoznać się z pełnym zestawem wytycznych, odwiedź często zadawane pytania dotyczące programu Developer Exchange. Podczas tej godziny możesz chcieć przetestować funkcjonalność sprzedawanych przedmiotów, do czego musisz mieć Robuxa. Jeśli w tej chwili nie masz żadnych Robuxów lub nie chcesz ich wydawać, możesz bezpiecznie przejść do następnej części, nie tracąc żadnych nowych koncepcji kodowania.
Dodawanie przepustek do Twojej atrakcji
Istnieje kilka sposobów zarabiania na swoim doświadczeniu w Roblox. Jednym z takich sposobów są przepustki. Przepustki pozwalają tworzyć specjalne przedmioty, które można kupić tylko raz na osobę za pomocą Robuxa. Oto kilka przykładów sytuacji, w których możesz chcieć użyć przepustki:
* Aby dać ludziom możliwość odwiedzenia nowych obszarów twojego doświadczenia.
* Aby odblokować elementy awatara, które ludzie mogą nosić.
* Aby oferować ludziom kosmetyki, takie jak iskierki, ślady i skórki broni.
WSKAZÓWKA
Zabawa jest najważniejsza
Najbardziej dochodowe doświadczenia to te, w których ludzie bawią się najlepiej. Jeśli ludzie dobrze się bawią w twoim środowisku przez jakiś czas, są bardziej skłonni do wydawania Robuxów, niż gdybyś kazał im płacić za rzeczy z góry.
Aby dokonać nowej przepustki, musisz najpierw ustawić pewne informacje na stronie Roblox o przepustce i potrzebujesz obrazu samej przepustki:
1. Upewnij się, że Twoje doświadczenie zostało opublikowane, a następnie przejdź do https://www.roblox.com/.
2. Kliknij Utwórz
3. Znajdź doświadczenie, na które chcesz wykupić przepustkę.
4. Wybierz opcję Utwórz przepustkę z menu rozwijanego Ustawienia po prawej stronie
5. Wszystkie przepustki wymagają zdjęcia, nazwy i opisu. Podaj wszystkie trzy, a następnie kliknij przycisk Podgląd
6. Na następnym ekranie kliknij Verify Upload, aby utworzyć przepustkę i przesłać ją do moderacji.
WSKAZÓWKA
Możesz zaktualizować przepustkę później
Zmiany można wprowadzić w przepustce, wybierając przepustkę, a następnie klikając prawym przyciskiem myszy i wybierając opcję Konfiguruj.
Konfigurowanie przepustki
Po utworzeniu przepustki do gry pojawi się ona nieco niżej na tej samej stronie, na której jesteś, pod sekcją tworzenia. Ostatnim krokiem jest skonfigurowanie przepustki, aby gracze mogli ją kupić:
1. Wybierz opcję Konfiguruj z menu rozwijanego Ustawienia po prawej stronie dla nowej przepustki
2. Na stronie konfiguracji kliknij kartę Sprzedaż
3. Kliknij przełącznik Pozycja na sprzedaż, aby udostępnić przepustkę graczom. Następnie wprowadź cenę (w Robuxach), jaką gracze zapłacą za przedmiot
WSKAZÓWKA
Członkowie Roblox Premium otrzymują więcej
Jeśli należysz do Roblox Premium, zarobki twórcy stanowią większy procent kosztu przedmiotu. Procent utrzymywany przez Roblox utrzymuje światła i serwery działają.
4. Kliknij przycisk Zapisz, aby potwierdzić ustawienia.
5. Poświęć chwilę na skopiowanie numeru identyfikacyjnego przepustki z adresu URL. Ten numer będzie potrzebny w Twoim kodzie
Zachęcanie do zakupów w grze
Gracze mogą kupować przepustki bezpośrednio na stronie głównej gry, klikając kartę Sklep i przeglądając dostępne przedmioty do kupienia. Alternatywnie możesz zadzwonić do MarketplaceService w sklepie w grze, używając następującego kodu:
MarketplaceService:PromptGamePassPurchase(player, gamePassID)
Gdy sprawdzasz, czy ktoś już posiada przepustkę, użyj tego kodu:
UserOwnsGamePassAsync(player.UserId, gamePassID)
Powinieneś zawsze zawijać czek w chronione połączenie w następujący sposób:
local success, message = pcall(function()
hasPass = MarketplaceService:UserOwnsGamePas sAsync(player.UserId, gamePassID)
end)
Sprzedawaj korony tłumowi
Utwórz przycisk i użyj lokalnego skryptu, aby utworzyć przepustkę, która pozwoli graczom z Twojego doświadczenia na noszenie fajnej neonowej korony imprezowej, takiej jak ta pokazana na rysunku
Skonfiguruj przepustkę
Najpierw musisz skonfigurować przepustkę, jak pokazano wcześniej; następnie usuwasz przepustkę ze swojego ekwipunku, aby móc przetestować jej zakup.
1. Wykonaj czynności opisane w pierwszej części godziny, aby utworzyć przepustkę. Pamiętaj, aby skonfigurować przepustkę, aby była dostępna do zakupu. Będziesz musiał przetestować zakup przepustki, więc możesz chcieć ustawić cenę na około 5 lub 10 Robuxów.
2. Na stronie przepustki usuń przepustkę ze swojego ekwipunku.
Jeśli zamknąłeś stronę, możesz wrócić do strony przepustki, wracając do menu rozwijanego Utwórz przepustkę, a następnie klikając przepustkę.
3. W StarterGui dodaj nowy ScreenGui i Button o nazwie BuyHat. Możesz użyć TextButton lub ImageButton.
Zachęcanie do zakupu
W tej sekcji konfigurujesz skrypt zakupu:
1. W ScreenGui dodaj nowy LocalScript.
2. Na górze skryptu pobierz MarketplaceService, Players i lokalnego gracza:
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local player = Players.LocalPlayer
3. Utwórz funkcję o nazwie promptPurchase połączoną z przyciskiem:
-- Previous Code
local screenGui = script.Parent
local button = screenGui:FindFirstChild("BuyHat")
local function promptPurchase()
end
button.Activated:Connect(promptPurchase)
4. Skonfiguruj zmienną z identyfikatorem przepustki skopiowanym z adresu URL. Wewnątrz funkcji skonfiguruj zmienną logiczną o nazwie hasPass. Ustaw hasPass na false:
-- Previous code
local screenGui = script.Parent
local button = screenGui:FindFirstChild("BuyHat")
local gamePassID = 0000000 -- Change this to your game pass ID
local function promptPurchase()
local hasPass = false
end
button.Activated:Connect(promptPurchase)
5. Wewnątrz podpowiedziZakup użyj chronionych połączeń, aby sprawdzić, czy gracz ma przepustkę:
local function promptPurchase()
local hasPass = false
local success, message = pcall(function()
hasPass =
MarketplaceService:UserOwnsGamePassAsync (player.UserId, gamePassID)
end)
if not success then
warn("Error while checking if player has pass: " .. tostring(message))
return
end
end
6. Jeśli gracz nie ma przepustki, uruchom monit o zakup. Oto gotowy skrypt:
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local screenGui = script.Parent
local button = screenGui:FindFirstChild("BuyHat")
local gamePassID = 0000000 -- Change this to your game pass ID
local function promptPurchase()
local hasPass = false
local success, message = pcall(function()
hasPass =
MarketplaceService:UserOwnsGamePassAsync (player.UserId, gamePassID)
end)
if not success then
warn("Error while checking if player has pass: " .. tostring(message))
return
end
if hasPass then
button.Text = "Already Owned"
else
-- Player does NOT own the game pass; prompt them to purchase
MarketplaceService:PromptGamePassPurch ase(player, gamePassID)
end
end
button.Activated:Connect(promptPurchase)
Testowanie
Prawidłowe testowanie wymaga serwera na żywo, ale najpierw możesz wykonać kilka szybkich kontroli w Studio, aby sprawdzić, czy jesteś na dobrej drodze:
1. Szybko przetestuj swój kod. Powinieneś zobaczyć potwierdzenie, że jesteś właścicielem przepustki lub błąd, że zakupy nie są dozwolone w tym środowisku . Błąd jest dobrym znakiem, ponieważ przepustki trzeba kupować na serwerze na żywo. Jeśli to widzisz, przejdź do następnego kroku. W przeciwnym razie sprawdź swój kod i upewnij się, że nie masz już przepustki
2. Opublikuj w Roblox. Będziesz potrzebować najbardziej aktualnej wersji swojego miejsca.
3. Przetestuj kod z serwera na żywo. Najprostszym sposobem jest powrót do zakładki Utwórz i kliknięcie miejsca rozpoczęcia doświadczenia, które chcesz przetestować. Spowoduje to przejście do strony gry.
4. W miejscu przetestuj swój przycisk. Powinieneś zobaczyć monit o zakup przepustki, taki jak ten pokazany na rysunku
WSKAZÓWKA
Często testuj serwer na żywo
Zawsze najlepiej jest przetestować swój kod na działającym serwerze, szczególnie jeśli masz elementy GUI. Różne rozmiary i typy ekranów mogą wpływać na to, jak ludzie postrzegają Twoje wrażenia. Jeśli możesz, przetestuj różne urządzenia, takie jak tablety, telefony, komputery PC i Mac.
Daj im kapelusz
Gdy ktoś kupi przepustkę, oczekuje, że będzie działać za każdym razem, gdy dołączy do gry. W tej sekcji dowiesz się, jak sprawdzić, czy ktoś ma przepustkę. Następnie zastąpisz obecny wygląd użytkownika, gdy jego postać zostanie dodana do obszaru roboczego.
1. W ServerScriptService dodaj nowy skrypt.
2. Skonfiguruj usługi i identyfikator przepustki. Następnie podłącz funkcję do PlayerAdded:
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local gamePassID = 0000000 -- Change this to your game pass ID
local function onPlayerAdded(player)
end
Players.PlayerAdded:Connect(onPlayerAdded)
3. Wewnątrz funkcji sprawdź przepustkę, jak pokazano
-- Previous code
local function onPlayerAdded(player)
local hasPass = false
local success, message = pcall(function()
hasPass =
MarketplaceService:UserOwnsGamePassAsync (player.UserId, gamePassID)
end)
if not success then
warn("Error while checking if player has pass: " .. tostring(message))
return
end
end
4. Jeśli gracz ma przepustkę, uruchamiasz wszystkie funkcje, które daje przepustka. Sprawdź przepustkę, jak pokazano tutaj:
-- Previous code
local function onPlayerAdded(player)
local hasPass = false
local success, message = pcall(function()
hasPass = MarketplaceService:UserOwnsGamePassAsync
(player.UserId, gamePassID)
end)
if not success then
warn("Error while checking if player has pass: " .. tostring(message))
return
end
if hasPass == true then
print(player.Name .. " owns the game pass with ID " .. gamePassID)
-- Code for pass functionality goes here.
end
end
5. W przypadku tej konkretnej przepustki chcemy nadpisać wygląd użytkownika. Utwórz nową funkcję o nazwie onCharacterAdded i wywołaj ją, jeśli użytkownik ma przepustkę:
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local gamePassID = 0000000 -- Change this to your game pass ID
local function onCharacterAdded(character)
end
local function onPlayerAdded(player)
local hasPass = false
local success, message = pcall(function()
hasPass =
MarketplaceService:UserOwnsGamePassAsync (player.UserId, gamePassID)
end)
if not success then
warn("Error while checking if player has pass: " .. tostring(message))
return
end
if hasPass == true then
print(player.Name .. " owns the game pass with ID " .. gamePassID)
player.CharacterAdded:Connect(onCharacterAdded)
end
end
Players.PlayerAdded:Connect(onPlayerAdded)
6. Skonfiguruj identyfikator kapelusza, którego chcesz użyć:
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local gamePassID = 0000000 -- Change this to your game pass ID
local HAT_ID = 156486131
7. Na koniec w onCharacterAdded pobierz aktualny opis humanoida i poczekaj, aż postać zostanie dodana do obszaru roboczego. Następnie użyj ApplyDescription(), aby dodać kapelusz, jak pokazano tutaj:
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local gamePassID = 0000000 -- Change this to your game pass ID
local HAT_ID = 156486131
local function onCharacterAdded(character)
local humanoid = character:WaitForChild("Humanoid")
local description = humanoid:GetAppliedDescription()
description.HatAccessory = HAT_ID
while not character.Parent do
character.AncestryChanged:Wait()
end
humanoid:ApplyDescription(description)
end
local function onPlayerAdded(player)
local hasPass = false
local success, message = pcall(function()
hasPass =
MarketplaceService:UserOwnsGamePassAsync (player.UserId, gamePassID)
end)
if not success then
warn("Error while checking if player has pass: " .. tostring(message))
return
end
if hasPass == true then
print(player.Name .. " owns the game pass with ID " .. gamePassID)
player .Cha racterAdded:Connec t(onCharacterAdded)
end
end
Players.PlayerAdded:Connect(onPlayerAdded)
WSKAZÓWKA
Korzystanie z opisów
Alternatywnym sposobem dodania czapki jest po prostu umieszczenie jej na głowie użytkownika. Jednak opisy są bardziej wiarygodne. Opisy mogą być również używane do aktualizowania innych akcesoriów, takich jak włosy. Pełną listę opisów zastosowań można znaleźć w Developer Hub.
8. Podczas testowania w Studio potwierdzenie drukowania powinno pojawić się w Wyjściu. Opublikuj w Roblox, a następnie sprawdź serwer, aby sprawdzić, czy wszystko faktycznie działa zgodnie z przeznaczeniem.
Streszczenie
Przede wszystkim zawsze koncentruj się na stworzeniu doświadczenia w miejscu, w którym ludzie chcą spędzić czas, zanim skoncentrujesz się na nakłonieniu społeczności do wydawania pieniędzy. Aby stworzyć jednorazowe przedmioty do kupienia, utwórz przepustkę do doświadczenia na www.roblox.com. Następnie skonfiguruj funkcjonalność przepustki w ramach doświadczenia. Zakupy i sprawdzanie przepustek są obsługiwane za pomocą MarketplaceService. Możesz użyć MarketplaceService, aby zachęcić użytkowników do zakupu przepustki za pomocą PromptGamePassPurchase() i sprawdzić, czy mają już przepustkę za pomocą UserOwnsGamePassAsync(player.UserId, game-PassID). Zawsze zawijaj czek w pcall(). Przepustka zademonstrowana w tej godzinie również wykorzystała humanoid:GetAppliedDescription() do sprawdzenia, jakie przedmioty dana osoba aktualnie nosi, a następnie zaktualizowała ich wygląd za pomocą humanoid:ApplyDescription(description).
Pytania i odpowiedzi
P. Co jeśli chcesz stworzyć coś, co można kupować wielokrotnie, na przykład waluty w grze lub ulepszenia?
O. Aby tworzyć przedmioty, które można kupować wielokrotnie, chcesz utworzyć Produkty deweloperskie. Działają one podobnie do przepustek. Jeśli szukasz produktów dla programistów na stronie developer.roblox.com, znajdziesz przykładowy kod. Z wiedzą, którą masz od tej godziny, powinieneś być w stanie dowiedzieć się, jak dostosować kod.
P. Jak mam wymyślać pomysły na produkty przepustkowe i deweloperskie?
O. Najważniejszą rzeczą, jaką możesz zrobić, to dowiedzieć się, co sprawi, że ludzie będą chcieli zostać dłużej w twoim doświadczeniu. Jeśli ludzie dobrze się bawią, wydadzą więcej. Jedną rzeczą, którą możesz zrobić, to oferować specjalne skórki, rzadkie przedmioty lub materiały eksploatacyjne, takie jak mikstury zdrowia. Możesz znaleźć świetne warsztaty na temat dobrych praktyk monetyzacji i tworzenia bardziej angażujących doświadczeń, śledząc Roblox Developer Relations i ich serię Level Up na YouTube.
P. Czy jest jakiś inny sposób zarabiania pieniędzy na Roblox?
O. Oprócz tworzenia produktów i przepustek dla programistów możesz także otrzymywać wypłaty za zaangażowanie. Jeśli użytkownicy, którzy są członkami Roblox Premium, spędzają dużo czasu w twoim doświadczeniu, możesz zarobić Robux. Im więcej czasu spędzasz, tym więcej zarabiasz. Istnieją również możliwości tworzenia i sprzedawania awatarów, koszulek i wtyczek.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Jaka usługa pozwala Ci sprzedawać przedmioty według Twojego doświadczenia?
2. Prawda czy fałsz: jeśli chcesz stworzyć przepustkę do sprzedaży, musisz dołączyć zdjęcie.
3. Zawsze zawijasz czek na przepustki w ________.
4. Czy potwierdzenie posiadania przepustki przez użytkownika powinno odbywać się po stronie serwera czy klienta?
5. Jeśli chcesz, aby twoje doświadczenie dobrze sobie radziło i tworzyło Robuxy, na czym powinieneś się skoncentrować przed czymkolwiek innym?
6. Ile musisz mieć lat, aby skorzystać z programu Developer Exchange?
Odpowiedzi
1. Usługa Marketplace
2. Prawda
3. Połączenia chronione, czyli pcall().
4. Po stronie serwera. Nigdy nie chcesz, aby coś tak ważnego wydarzyło się po stronie klienta.
5. Zabawa. Jeśli twoje doświadczenie nie jest zabawne, ludzie nie będą wydawać w nim Robuxów.
6. Co najmniej 13.
Ćwiczenia
Wymyśl pomysł na kosmetyczną zmianę, która może nastąpić, jeśli użytkownik ma poprawną przepustkę. W tym konkretnym przypadku kod będzie dla ciebie bardzo unikalny, ale powinieneś być w stanie sprawić, by działał, jeśli zastosujesz się do pokazanego już podstawowego wzorca. Kosmetyczną zmianą może być wszystko, od nadawania graczowi iskierek po trwałe zwiększenie prędkości. Niezależnie od tego, jaką przepustkę zdecydujesz się stworzyć, nigdy nie powinna ona dawać kupującym nieuczciwej przewagi w ramach Twojego doświadczenia. To nie jest zabawne dla nikogo. Twoja społeczność będzie narzekać, a na dłuższą metę najprawdopodobniej zarobisz mniej Robuxów, niż gdybyś utrzymywał równe szanse.
Programowanie obiektowe
W tej Części wyjaśniono, jak tworzyć niestandardowe instancje przy użyciu koncepcji zwanej programowaniem obiektowym, która jest częściej określana jako OOP. Ćwicząc programowanie obiektowe, zaczniesz zastanawiać się, jakie obiekty mają ze sobą wspólnego i jak zacząć je grupować i tworzyć nowe kategorie obiektów.
Co to jest OOP?
Rdzeniem OOP są koncepcje obiektów i klas. Obiekt reprezentuje pojedynczą rzecz w twoim świecie, taką jak dom, samochód, drzewo. Rzeczywiście, wszystko w Eksploratorze jest obiektem. Części, modele, emitery cząstek, podpowiedzi dotyczące bliskości - wszystko to są przedmioty. Klasa opisuje, czym jest obiekt i co robi. Na przykład Part i SurfaceLight to bardzo różne rzeczy, mimo że oba są obiektami. Ich klasy są tym, co ich wyróżnia. Część to jeden typ klasy, podczas gdy Światło to nie ona. Organizowanie kodu i projektów OOP pozwala rozbić problemy na mniejsze części. Planując swoje doświadczenie, możesz pomyśleć o różnych typach rzeczy, których potrzebuje Twój świat. Na przykład możesz mieć samochody, NPC, grywalne klasy i różne rodzaje broni. Gdy poznasz już wszystkie typy rzeczy, których potrzebujesz, możesz zacząć zastanawiać się, jak je zakodować w sposób, który umożliwi ponowne wykorzystanie kodu do tworzenia różnych wersji tych samych rzeczy. Na przykład jeden samochód może być czerwony z paskiem wyścigowym, a inny żółty. Nie chcesz tworzyć dwóch różnych scenariuszy tylko dlatego, że samochody muszą mieć dwa różne kolory. W przypadku OOP wystarczy stworzyć jedną klasę, samochód, a następnie ustawić kolor jako jedną z modyfikowalnych właściwości.
Tworzenie nowej klasy
Niektóre języki programowania używają specjalnych słów kluczowych do tworzenia klas. W Lua po prostu używasz tabeli z kilkoma modyfikacjami. Poniższe kroki przeprowadzą Cię przez ogólny proces tworzenia nowej klasy:
1. Aby utworzyć klasę, wpisz:
local NameOfClass = {}
NameOfClass.__index = NameOfClass
WSKAZÓWKA
Użyj dwóch podkreśleń przed indeksem
W rzeczywistości przed indeksem znajdują się dwa podkreślenia. Można go łatwo pomylić z jednym. __index powinien być zawsze ustawiony na nazwę klasy.
2. Sama klasa niewiele jednak zdziała. Musisz dodać do klasy funkcję opisującą sposób tworzenia nowego obiektu tej klasy. Funkcje te nazywane są konstruktorami, ponieważ tworzą rzeczy.
function NameOfClass.new()
end
3. Wewnątrz konstruktora utwórz nową tabelę i zwróć ją na końcu funkcji. Ta tabela jest obiektem tworzonym za każdym razem, gdy wymagana jest nowa instancja klasy:
function NameOfClass.new()
local self = {}
return self
end
WSKAZÓWKA
self jako konwencję nazewnictwa
self jest powszechnie uznanym sposobem odwoływania się do obiektu tworzonego wewnątrz klasy. Jest to tak powszechna konwencja, że Studio odważa się, aby ułatwić przeglądanie w klasie, mimo że zwykle nie jest to słowo kluczowe w Lua.
4. Użyj metody setmetatable(), aby wstawić self obiektu do oryginalnej tabeli:
function NameOfClass.new()
local self = {}
setmetatable(self, NameOfClass)
return self
end
5. Na koniec możesz wywołać funkcję, aby utworzyć nową instancję klasy:
local NameOfClass = {}
NameOfClass.__index = NameOfClass
function NameOfClass.new()
local self = {}
setmetatable(self, NameOfClass)
return self
end
local newO bject = NameOfClass.new()
Dodawanie właściwości klasy
Podobnie jak inne instancje Roblox, twoje niestandardowe klasy mogą również mieć właściwości, takie jak kolor, rozmiar i skala. Możesz dodać nową właściwość do funkcji konstruktora klasy. Jeśli chodzi o wartość właściwości, możesz dodać ją samodzielnie, bezpośrednio przypisując wartość, lub możesz przekazać wartość jako parametr konstruktora:
local NameOfClass = {}
NameOfClass.__index = NameOfClass
function NameOfClass.new(parameterProperty)
local self = {}
setmetatable(self, NameOfClass)
self.defaultProperty = "Default Value"
self.parameterProperty = parameterProperty
return self
end
local newObject = NameOfClass .new()
Utwórz klasę samochodów
Utwórz klasę samochodu, która ma jedną właściwość dla koloru i inną dla liczby kół w samochodzie. Kolor powinien zostać przekazany przez konstruktora, ale liczba kół powinna być zakodowana na stałe jako 4.
1. Utwórz klasę o nazwie Car i jej konstruktora:
local Car = {}
Car.__index = Car
function Car.new()
local self = {}
setmetatable(self, Car)
return self
end
2. Zakoduj na stałe liczbę kół:
local Car = {}
Car.__index = Car
function Car.new()
local self = {}
setmetatable(self, Car)
self.numberOfWheels = 4
return self
end
3. Dodaj parametr koloru samochodu. Pamiętaj, aby dodać parametr również do deklaracji konstruktora:
local Car = {}
Car.__index = Car
function Car.new(color)
local self = {}
setmetatable(self, Car)
self.numberOfWheels = 4
self.color = color
return self
end
4. Przetestuj swój kod, tworząc nową instancję samochodu i spróbuj wydrukować jego właściwości:
local Car = {}
Car.__index = Car
function Car.new(color)
local self = {}
setmetatable(self, Car)
self.numberOfWheels = 4
self.color = color
return self
end
local redCar = Car.new("red")
print(redCar.numberOfWheels) -- Prin ts "4"
print(r edCar.color) -- Prints "red"
Korzystanie z funkcji klasowych
Klasy niestandardowe mogą również mieć funkcje. W przeciwieństwie do właściwości, funkcje są deklarowane poza konstruktorem:
local NameOfClass= {}
NameOfClass.__index = NameOfClass
function NameOfClass.new()
local self = {}
setmetatable(self, NameOfClass)
return self
end
-- Class function is declared outside of constructor
function NameOfClass:nameOfFunction()
end
Często okaże się, że będziesz chciał odwoływać się do obiektu w funkcjach klasowych, szczególnie w celu uzyskania dostępu do właściwości tego obiektu. Możesz użyć self w funkcjach reprezentujących obiekt:
function NameOfClass:nameOfFunction()
local variab le = self.nameOfProperty
end
Stwórz własnego zwierzaka
Co jest lepsze od zwierzaka, który chodzi za tobą, kiedy głaszczesz go po głowie? W tym ćwiczeniu stwórz własną niestandardową klasę zwierzaka, która podąża za graczami przez krótki czas po interakcji z nią.
Konfiguracja
Po pierwsze, musisz mieć model, którego możesz użyć jako zwierzaka. Możesz użyć fantazyjnego modelu zwierzaka, jeśli go masz. W przeciwnym razie wykonaj następujące kroki, aby utworzyć kwadratowy zastępca:
1. Wstaw model do obszaru roboczego; zmień nazwę modelu na coś takiego jak Pies. Jeśli używasz tylko kwadratowej części, użyj kombinacji klawiszy Ctrl+G/Cmd+G, aby przekształcić ją w model, a następnie zmień jej nazwę.
2. Wewnątrz modelu wstaw nową część. Zmień nazwę części HumanoidRo otPart.
WSKAZÓWKA
MoveTo() potrzebuje HumanoidRootPart
W dalszej części tego skryptu funkcja MoveTo() zostanie użyta do poruszenia zwierzaka. Aby to zadziałało, główna część musi mieć dokładnie nazwę HumanoidRootPart. Jeśli ma inną nazwę lub jest błędnie pisana wielką literą, część nie będzie mogła prawidłowo animować.
3. Wybierz model i ustaw PrimaryPart na HumanoidRootPart.
4. Gdy model nadal jest wybrany, włóż humanoida.
5. Przenieś zwierzaka do ServerStorage.
Utwórz klasę Zwierzaka
Teraz skonfiguruj klasę, aby utworzyć zwierzaka, a następnie w następnej sekcji dodasz funkcję, dzięki której zwierzak będzie podążał za każdym, kto go pogłaska:
1. W ServerScriptService dodaj nowy skrypt.
2. Odwołaj się do ServerStorage i stwórz stałą określającą, jak długo zwierzak będzie podążał za graczem po tym, jak ktoś poklepie go po głowie:
local ServerStorage = game:GetService("ServerStorage")
local FOLLOW_DURATION = 5
3. Utwórz nową klasę zwierzaka i jego konstruktora. Dołącz parametr, który pozwoli Ci przekazać, jakiego modelu użyć dla zwierzaka:
local Pet = {}
Pet.__index = Pet
function Pet.new(model)
local self = {}
setmetatable(self, Pet)
return self
end
4. Przypisz przekazany parametr do modelu klasy i nadrzędny dla obszaru roboczego w następujący sposób:
local Pet = {}
Pet.__index = Pet
function Pet.new(model)
local self = {}
setmetatable(self, Pet)
self._model = model
self._model.Parent = workspace
return self
end
WSKAZÓWKA
Zwróć uwagę na konwencję nazewnictwa
Zauważ, że wewnątrz konstruktora nazwy poprzedzone są pojedynczym podkreśleniem.
5. Wewnątrz konstruktora utwórz nowy ProximityPrompt. Zaktualizuj ObjectText i ActionText zachęty, jak pokazano w poniższym kodzie, a następnie nadrzędnie zachętę dla zwierzaka:
local Pet = {}
Pet.__index = Pet
function Pet.new(model)
local self = {}
setmetatable(self, Pet)
self._model = model
self._model.Parent = workspace
self._petPrompt = Instance.new("ProximityPrompt")
self._petPrompt.ObjectText = "Pet"
self._petPrompt.ActionText = "Giv e pets!"
self._petPrompt.Parent = model.PrimaryPart
return self
end
Dodaj funkcję do zwierzaka
W tym miejscu utworzysz następującą funkcję. MoveTo() aktualizuje lokalizację zwierzaka co 0,25 sekundy do miejsca, w którym znajduje się gracz, który go poklepał:
1. Dodaj nową funkcję do klasy zwierzaka o nazwie getPets() z parametrem dla gracza:
Previous Code
function Pet:getPets(player)
end
Disable the prompt when it′s been triggered.
function Pet:getPets(player)
self._petPrompt.Enabled = false
end
2. Użyj pętli for, aby zaktualizować lokalizację zwierzaka co 0,25 sekundy, a następnie ponownie włącz monit:
function Pet:getPets(player)
self._petPrompt.Enabled = false
for i = 0, FOLLOW_DURATION, 0.25 do
local character = player.Character
if character and character.PrimaryPart then
self._model.Humanoid:MoveTo(character.PrimaryPart.Position)
end
wait(0.25)
end
self._petPrompt.Enabled = true
end
3. Zwróć klasę zwierzaka:
function Pet:getPets(player)
self._petPrompt.Enabled = false
for i = 0, FOLLOW_DURATION, 0.25 do
local character = player.Character
if character and character.PrimaryPart then
self._model.Humanoid:MoveTo(character.PrimaryPart.Position)
end
wait(0.25)
end
self._petPrompt.Enabled = true
return Pet
end
4. Wróć do konstruktora Pet i użyj anonimowej funkcji, aby wywołać getPets po wyzwoleniu monitu:
local Pet = {}
Pet.__index = Pet
function Pet.new(model)
local self = {}
setmetatable(self, Pet)
self._model = model
self._model.Parent = workspace
self._petPrompt = Instance.new("ProximityPrompt")
self._petPrompt.ObjectText = "Pet"
self._petPrompt.ActionText = "Give pets!"
self._petPrompt.Parent = model.PrimaryPart
self._petPrompt.Triggered:Connect(function (player)
self:getPets(player)
end)
return self
end
5. Teraz pozostaje tylko utworzyć nową instancję klasy Pet i przekazać model zwierzaka, którego ma używać. Oto gotowy skrypt:
local ServerStorage = game:GetService("ServerStorage")
local FOLLOW_DURATION = 5
local Pet = {}
Pet.__index = Pet
function Pet.new(model)
local self = {}
setmetatable(self, Pet)
self._model = model
self._model.Parent = workspace
self._petPrompt = Instance.new("ProximityPrompt")
self._petPrompt.ObjectText = "Pet"
self._petPrompt.ActionText = "Give pets!"
self._petPrompt.Parent = model.PrimaryPart
self._petPrompt.Triggered:Connect(function (player)
self:getPets(player)
end)
return self
end
function Pet:getPets(player)
self._petPrompt.Enabled = false
for i = 0, FOLLOW_DURATION, 0.25 do
local character = player.Character
if character and character.PrimaryPart then
self._model.Humanoid:MoveTo(character.PrimaryPart.Position)
end
wait(0.25)
end
self._petPrompt.Enabled = true
return Pet
end
-- Create a new Pet object and pass in the desired model
local rufus = Pet.new(ServerStorage.Dog:Clone())
local whiskers = Pet.new(ServerStorage.Ca t:Clone())
WSKAZÓWKA
Zmodyfikuj ProximityPrompt, jeśli go nie widzisz
Jeśli masz problem ze znalezieniem ProximityPrompt podczas testowania, spróbuj ustawić RequiresLineOfSight na False. Może być zakopany w modelu i dlatego niewidoczny. W razie potrzeby można również dostosować inne właściwości ProximityPrompt, takie jak UIOffset i Exclusivity.
Streszczenie
Ważną częścią kodowania DRY jest unikanie powtarzania się. Tworzenie klas oszczędza czas i pracę, tworząc kod wielokrotnego użytku. W ciągu następnej godziny dowiesz się, jak można jeszcze bardziej dostosować zajęcia. Więc może zamiast mieć tylko unikalne modele dla zwierzaków, możesz mieć różne zwierzaki, każdy z własnym modelem, teksturą i dźwiękiem.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Co oznacza OOP?
2. Co to jest klasa?
3. Jaka jest przynajmniej jedna korzyść z OOP?
4. Co to jest konstruktor?
5. Co jest nie tak z poniższym kodem?
local NameOfClass = {}
NameOfClass.__index = NameOfClass
function NameOfClass.new(parameterProperty)
local self = {}
self.defaultProperty = "Default Value"
sel f.parameterProperty = parameterProperty
return self
end
local newObject = NameOfClass.new()
Odpowiedzi
1. Programowanie obiektowe
2. Klasa opisuje, czym jest obiekt i co robi.
3. Korzyści z OOP to
A. Pozwala podzielić doświadczenie na mniejsze części
B. Utrzymuje porządek w kodzie
C. Zmniejsza ilość powtarzalnego kodu w projekcie
4. Konstruktor to funkcja opisująca sposób budowania nowego obiektu klasy. Wszystkie właściwości klasy znajdują się wewnątrz konstruktora.
5. Metatabela nie została ustawiona.
local NameOfClass = {}
NameOfClass.__index = NameOfClass
function NameOfClass.new(parameterProperty)
local self = {}
setmetatable(self, NameOfClass) -- Was missing
self.defaultProperty = "Default Value"
sel f.parameterProperty = parameterProperty
return self
end
local newObject = NameOfClass.new()
Ćwiczenia
Utwórz klasę osoby NPC, w której każda osoba NPC ma parametr do ustawienia swojej nazwy i ma funkcję drukowania jej imienia po wywołaniu.
Porady
- Najpierw utwórz klasę i konstruktora.
- Dołącz parametr, który umożliwia przekazanie nazwy.
- Nie musisz dołączać modelu ani innych informacji, chyba że chcesz.
- Utwórz funkcję poza konstruktorem i wywołuj ją osobno.
Dziedziczenie
W miarę jak Twoje projekty stają się coraz większe i bardziej złożone, możesz zauważyć, że niektóre zajęcia będą się nakładać i mieć wspólne cechy. W takich przypadkach można tak ustrukturyzować swój kod, aby klasy przekazywały swoje właściwości i funkcje innym klasom. Nazywamy tę praktykę dziedziczeniem. Klasy, które przekazują swoje zachowania, nazywane są klasami nadrzędnymi, a klasy, które dziedziczą te zachowania, nazywane są klasami podrzędnymi. Rozważ wbudowane klasy Roblox PointLight i SpotLight. Chociaż oświetlają scenę w nieco inny sposób, mają też ze sobą wiele wspólnego, co widać we właściwościach przedstawionych na rysunku . Możesz włączać i wyłączać dowolne z nich, a także dostosować kolor i jasność dla obu. Obie te klasy dziedziczą te zachowania po klasie nadrzędnej Light.
Konfigurowanie dziedziczenia
W Lua dziedziczenie opiera się na tej samej strukturze klas i metatablic, których używaliśmy dla klas . Poniższe kroki przeprowadzą Cię przez podstawowy wzorzec:
1. Utwórz klasę nadrzędną i jej konstruktora:
local ParentClass = {}
ParentClass.__index = ParentClass
function ParentClass.new()
local self = {}
setmetatable(self, ParentClass)
return self
end
2. Utwórz klasę potomną:
local ParentClass = {}
ParentClass.__index = ParentClass
function ParentClass.new()
local self = {}
setmetatable(self, ParentClass)
return self
end
local ChildClass = {}
ChildClass.__index = ChildClass
3. Aby utworzyć klasę potomną, użyj funkcji setmetatable. Tym razem przekaż w imieniu klasy potomneja, a następnie nazwa klasy nadrzędnej:
local ParentClass = {}
ParentClass.__index = ParentClass
function ParentClass.new()
local self = {}
setmetatable(self, ParentClass)
return self
end
local ChildClass = {}
ChildClass.__index = ChildClass
setmetatable(ChildClass, ParentClass)
4. Utwórz konstruktor dla nowej klasy:
local ParentClass = {}
ParentClass.__index = ParentClass
function ParentClass.new()
local self = {}
setmetatable(self, ParentClass)
return self
end
local ChildClass = {}
ChildClass.__index = ChildClass
setmetatable(ChildClass, ParentClass)
function ChildClass.new()
end
5. Wewnątrz konstruktora klasy potomnej utwórz zmienną o nazwie self. Jednak zamiast ustawiania wartości9 na {}, tak jak byliśmy w konstruktorach, ustaw ją na nową instancję klasy rodzica:
local ParentClass = {}
ParentClass.__index = ParentClass
function ParentClass.new()
local self = {}
setmetatable(self, ParentClass)
return self
end
local ChildClass = {}
ChildClass.__index = ChildClass
setmetatable(ChildClass, ParentClass)
function ChildClass.new()
local self = ParentClass.new()
end
6. Ustaw metatabelę dla zmiennej self na klasę potomną, a następnie zwróć self:
local ParentClass = {}
ParentClass.__index = ParentClass
function ParentClass.new()
local self = {}
setmetatable(self, ParentClass)
return self
end
local ChildClass = {}
ChildClass.__index = ChildClass
setmetatable(ChildClass, ParentClass)
function ChildClass.new()
local self = ParentClass.new()
setmetatable(self, Ch ildClass)
return self
end
Dziedziczenie właściwości
Wszelkie właściwości zdefiniowane przez klasę nadrzędną staną się również właściwościami klas podrzędnych. Jeśli pamiętasz, jak kilka godzin temu rozmawialiśmy o kodowaniu DRY, jest to idealne rozwiązanie, ponieważ oznacza to, że nie musisz się powtarzać, tworząc te same właściwości dla każdej klasy:
local ParentClass = {}
ParentClass.__index = ParentClass
function ParentClass.new()
local self = {}
setmetatable(self, ParentClass)
self.inheritedProperty = "Inherited property"
return self
end
Utwórz klasę pojazdu i klasę samochodu
Pokazany wzorzec zostanie użyty do utworzenia klasy nadrzędnej Vehicle, która ma właściwość określającą liczbę posiadanych silników. Następnie utwórz klasę potomną Car, która dziedziczy właściwość numeru silnika z klasy Vehicle, ale następnie używa własnej właściwości do określenia liczby kół samochodu.
1. Utwórz klasę Vehicle i jej konstruktora:
local Vehicle = {}
Vehicle.__index = Vehicle
function Vehicle.new()
local self = {}
setmetatable(self, Vehicle)
return self
end
2. Dodaj właściwość numberOfEngines do siebie:
local Vehicle = {}
Vehicle.__index = Vehicle
function Vehicle.new()
local self = {}
setmetatable(self, Vehicle)
self.numberOfEngines = 1
return self
end
3. Utwórz klasę Car i pozwól jej dziedziczyć po Vehicle:
local Vehicle = {}
Vehicle.__index = Vehicle
function Vehicle.new()
local self = {}
setmetatable(self, Vehicle)
self.numberOfEngines = 1
return self
end
local Car = {}
Car.__index = Car
setmetatable(Car, Vehicle)
4. Utwórz konstruktor klasy Car:
local Vehicle = {}
Vehicle.__index = Vehicle
function Vehicle.new()
local self = {}
setmetatable(self, Vehicle)
self.numberOfEngines = 1
return self
end
local Car = {}
Car.__index = Car
setmetatable(Car, Vehicle)
function Car.new()
local self = Vehicle.new()
setmetatable(self, Car)
return self
end
5. Dodaj właściwość numberOfWheels do klasy Car:
local Vehicle = {}
Vehicle.__index = Vehicle
function Vehicle.new()
local self = {}
setmetatable(self, Vehicle)
self.numberOfEngines = 1
return self
end
local Car = {}
Car.__index = Car
setmetatable(Car, Vehicle)
function Car.new()
local self = Vehicle.new()
setmetatable(self, Car)
self.numberOfWheels = 4
return self
end
6. Utwórz nową instancję samochodu i wydrukuj liczbę posiadanych silników oraz kół:
local Vehicle = {}
Vehicle.__index = Vehicle
function Vehicle.new()
local self = {}
setmetatable(self, Vehicle)
self.numberOfEngines = 1
return self
end
local Car = {}
Car.__index = Car
setmetatable(Car, Vehicle)
function Car.new()
local self = Vehicle.new()
setmetatable(self, Car)
self.numberOfWheels = 4
return self
end
local car = Car.new()
print("Engines:", car.numberOfEngines)
print("Wheels:", car.numberOfWheels)
Praca z wieloma klasami potomnymi
Możesz mieć tyle klas, ile chcesz dziedziczyć z klasy nadrzędnej. W poprzednim przykładzie utworzyłeś klasę Car, która dziedziczy z klasy Vehicle. Możesz także utworzyć klasę motocykla, która również dziedziczy po klasie Vehicle. Chociaż samochód i motocykl będą miały różne właściwości, w tym przypadku liczbę kół, będą miały wspólną liczbę silników:
local motorcycle = Motorcycle.new()
print("Engines:", motorc ycle.numberOfEngines)
print("Wheels:", motorcycle.numberOfWheels)
Funkcje dziedziczenia
Podobnie jak w przypadku właściwości, klasy potomne również odziedziczą wszystkie funkcje swojej klasy nadrzędnej:
local ParentClass = {}
ParentClass.__index = ParentClass
function ParentClass.new()
local self = {}
setmetatable(self, ParentCl ass)
return self
end
function ParentClass:inheritedFunction()
end
Zrozumienie polimorfizmu
Czasami klasy potomne muszą wykonywać podobne czynności, ale każda klasa wykonuje te funkcje w określony sposób. W takich przypadkach klasa nadrzędna może zdefiniować funkcję, która mówi, jakie będzie domyślne zachowanie klas podrzędnych korzystających z tej funkcji. Ale każda klasa potomna, która chce odejść od tego i zrobić coś specjalnego, może mieć funkcję o tej samej nazwie, która zastąpi funkcję odziedziczoną po rodzicu:
local ParentClass = {}
ParentClass.__index = ParentClass
function ParentClass:doSomething()
-- Default behavior if child classes do not define their own doSomething
end
local ChildClassOne = {}
setmetatable(ChildClassOne, ParentClass)
function ChildClassOne:doSomething()
-- Does something specific to ChildClassOne
end
local ChildClassTwo = {}
setmetatable(ChildClassTwo, ParentClass)
function ChildClassTwo:doSomething()
-- Does something specific to ChildClassTwo
end
local ChildClassThree = {}
setmetatable(ChildClassThree, ParentClass)
-- ChildClassThree did not define a doSomething function .
-- It will call the Parent class doSomething function instead.
Twórz różnie brzmiące zwierzęta
Załóżmy, że masz klasę nadrzędną Zwierzę i dwie klasy podrzędne, Pies i Kot. Chcesz, aby psy i koty potrafiły wydawać dźwięki odpowiednio "hau" i "miau". Możesz stworzyć dla nich oddzielnie nazwane funkcje w każdej klasie, takie jak Pies:woof() i Kot:miau(), ale bardziej powszechną praktyką byłoby utworzenie wspólnej nazwy funkcji. W takim przypadku utwórz funkcję Animal:speak():
1. Utwórz klasę nadrzędną Animal i jej konstruktora:
local Animal = {}
Animal.__index = Animal
function Animal.new()
local self = {}
setmetatable(self, Animal)
return self
end
2. Utwórz klasy potomne Dog i Cat oraz ich konstruktory:
local Animal = {}
Animal.__index = Animal
function Animal.new()
local self = {}
setmetatable(self, Animal)
return self
end
local Dog = {}
Dog.__index = Dog
setmetatable(Dog, Animal)
function Dog.new()
local self = Animal.new()
setmetatable(self, Dog)
return self
end
local Cat = {}
Cat.__index = Cat
setmetatable(Cat, Animal)
function Cat.new()
local self = Animal.new()
setmetatable(self, Cat)
return self
end
3. Dodaj funkcję speak do klasy Animal:
local Animal = {}
Animal.__index = Animal
function Animal.new()
local self = {}
setmetatable(self, Animal)
return self
end
function Animal:speak()
print("The animal makes a noise")
end
local Dog = {}
Dog.__index = Dog
setmetatable(Dog, Animal)
function Dog.new()
local self = Animal.new()
setmetatable(self, Dog)
return self
end
local Cat = {}
Cat.__index = Cat
setmetatable(Cat, Animal)
function Cat.new()
local self = Animal.new()
setmetatable(self, Cat)
return self
end
WSKAZÓWKA
Użyj dwukropków dla funkcji
Zauważ, że używamy dwukropka zamiast kropki, tak jak w przypadku właściwości.
4. Dodaj funkcje mowy do klas Dog i Cat:
local Animal = {}
Animal.__index = Animal
function Animal.new()
local self = {}
setmetatable(self, Animal)
return self
end
function Animal:speak()
print("The animal makes a noise")
end
local Dog = {}
Dog.__index = Dog
setmetatable(Dog, Animal)
function Dog.new()
local self = Animal.new()
setmetatable(self, Dog)
return self
end
function Dog:speak()
print("Woof")
end
local Cat = {}
Cat.__index = Cat
setmetatable(Cat, Animal)
function Cat.new()
local self = Animal.new()
setmetatable(self, Cat)
return self
end
function Cat:speak()
print("Meow")
end
5. Na dole skryptu wywołaj funkcję speak() dla obu klas: Kot i Pies:
Cat:speak()
Dog:speak()
Wywoływanie funkcji nadrzędnych
W przypadku polimorfizmu czasami możesz chcieć wywołać funkcję domyślną zdefiniowaną przez klasę nadrzędną, a także niestandardowe zachowanie klasy podrzędnej. Jeśli klasa nadrzędna i klasa podrzędna mają funkcję o tej samej nazwie, możesz wywołać funkcję nadrzędną z klasy podrzędnej według następującego wzorca:
function ChildClass:sameFunctionName()
ParentClass.sameFunctionName(self)
end
Zauważ, że z tym wywołaniem funkcji robisz coś nieco innego niż zwykle. Nie wywołujesz funkcji z określonego obiektu; nazywasz to samą klasą. Używasz również operatora kropki (.) zamiast operatora dwukropka (:) do wywołania funkcji. Na koniec przekazujesz zmienną self. Ta zmienna odnosi się do rzeczywistego obiektu, z którym chcesz wywołać funkcję. Poniższy przykładowy kod pokazuje dwie różne prace, które ludzie mogą wykonywać w ramach doświadczenia, wojownika i maga. W obu przypadkach ludzie muszą używać energii do ataku. Właściwość energii i funkcja ataku są ustawione w nadrzędnej klasie zadań:
local Job = {}
Job.__index = Job
function Job.new()
local self = {}
setmetatable(self, Job)
self.energy = 1
return self
end
function Job:attack()
if self.energy > 0 then
self.energy -= 1
return true
end
return false
end
local Warrior = {}
Warrior.__index = Warrior
setmetatable(Warrior, Job)
function Warrior.new()
local self = Job.new()
setmetatable(self, Warrior)
return self
end
function Warrior:attack()
local couldAttack = Job.attack(self)
if couldAttack then
print("I swing my weapon!")
else
print("I'm too tired to attack!")
end
end
local Mage = {}
Mage.__index = Mage
setmetatable(Mage, Job)
function Mage.new()
local self = Job.new()
setmetatable(self, Mage)
return self
end
function Mage:attack()
local couldAttack = Job.attack(self)
if couldAttack then
print("I cast a spell!")
else
print("I'm out of mana!")
end
end
local warrior = Warrior.new()
local mage = Mage.new()
-- The first attack() i s called, they'll attack.
-- The second time they'll be out of mana.
warrior:attack()
warrior:attack()
mage:attack()
mage:attack()
Streszczenie
Dziedziczenie i polimorfizm to więcej narzędzi w twoim pasie, jeśli chodzi o dobre praktyki programowania obiektowego. Po utworzeniu klasy nadrzędnej, takiej jak klasa zwierząt w ogrodach zoologicznych, możesz utworzyć dowolną liczbę klas podrzędnych. Domyślne właściwości, takie jak liczba głów, ogonów i nóg zwierzęcia, można ustawić w klasie nadrzędnej, a następnie przekazać klasie podrzędnej. Dzięki polimorfizmowi funkcje i właściwości klasy nadrzędnej można modyfikować, tak aby każde zwierzę w ogrodach zoologicznych było niepowtarzalne. Zebra i szympans mogą mieć te same zachowania wędrujące, ale różne animacje i dźwięki.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Prawda czy fałsz: Klasa potomna musi zawierać wszystkie właściwości klasy nadrzędnej.
2. Jaka jest definicja dziedziczenia?
3. Jaka jest maksymalna liczba klas potomnych, które można utworzyć?
4. Jaka jest definicja polimorfizmu?
5. Prawda czy fałsz: Klasy potomne dziedziczą właściwości, ale nie funkcje.
Odpowiedzi
1. Fałsz. Jeśli właściwość istnieje w klasie nadrzędnej i nie jest zdefiniowana w klasie podrzędnej, dziecko korzysta z tego, co jest określone w klasie nadrzędnej.
2. Dziedziczenie ma miejsce, gdy funkcje i właściwości jednej klasy są przekazywane innej.
3. Nie ma maksymalnej liczby klas potomnych. Możesz mieć tyle, ile chcesz.
4. Czasami klasy potomne muszą wykonać podobne czynności, ale wykonać je na różne sposoby. Przykładem w tej godzinie były zwierzęta, z których każde miało niepowtarzalny dźwięk.
5. Fałsz. Klasy potomne mogą dziedziczyć zarówno funkcje, jak i właściwości.
Ćwiczenia
Spróbuj wykonać dwie prace do gry fabularnej. Zrób klasę dla każdego zadania. Każda praca powinna śledzić, ile doświadczenia zdobył gracz i mieć funkcję dodawania do tego doświadczenia. Każda praca powinna również mieć specjalny zasób, który śledzi, taki jak energia, wytrzymałość, mana itp. Zadania powinny mieć funkcję ataku, która wyczerpuje ten zasób. Gdy skończysz, utwórz obiekty tych klas.
Porady
- Jeśli okaże się, że dwie klasy mają dokładnie takie same zmienne lub kod, sprawdź, czy możesz użyć klasy nadrzędnej na swoją korzyść.
- Drukowanie wartości właściwości obiektu przed i po wywołaniu funkcji może być dobrym sposobem na upewnienie się, że funkcja działa poprawnie.
Rzutowanie promieniami
Obiekty w doświadczeniu Roblox mogą się dużo poruszać. Czasami będziesz potrzebować swojego kodu do zbadania środowiska, aby zrozumieć, gdzie się znajdują. Jednym ze sposobów na to jest technika zwana raycastingiem. Kiedy rzucasz promieniowanie, mówisz silnikowi, aby uruchomił się w danym punkcie i narysował linię od tego punktu w określonym kierunku na określoną odległość. Jeśli ta linia trafi w coś podczas rysowania, funkcja raycast zwraca to, co zostało trafione. Ta technika jest używana do wszystkiego, od tworzenia szczegółowych odbić na wirtualnych szklanych powierzchniach po śledzenie torów pocisków w grach rywalizacyjnych.
Konfigurowanie funkcji do Raycast
Aby raycastować w Roblox, używasz funkcji workspace:Raycast(). Ta funkcja przyjmuje trzy parametry: początek promienia, kierunek, w którym powinien iść promień, oraz trzeci opcjonalny parametr, który pozwala określić pewne zachowanie promienia. Do trzeciego parametru przejdziemy w dalszej części. Na razie skupmy się na dwóch pierwszych:
1. Zdefiniuj parametry początku i kierunku promienia za pomocą współrzędnych Vector3:
local origin = Vector3.new(0, 5, 0)
local direction = Vector3.new(0, -10, 0)
2. Wywołaj workspace:Raycast() i zapisz wynik w zmiennej:
local origin = Vector3.new(0, 5, 0)
local direction = Vector3.new(0, -10, 0)
local result = game.Workspace:Raycast(origin, direction)
3. Sprawdź, czy wynik istnieje:
local origin = Vector3.new(0, 5, 0)
local direction = Vector3.new(0, -10, 0)
local result = game.Workspace:Raycast(origin, direction)
if result then
-- Do something
end
4. Jeśli wynik istnieje, wydrukuj instancję, do której trafił:
local origin = Vector3.new(0, 5, 0)
local direction = Vector3.new(0, -10, 0)
local result = game.Workspace:Raycast(origin, direction)
if result then
print("Found object:", result.Instance)
end
Materiał kameleona
Wykonasz część, która kamufluje się, kopiując materiał z dowolnej części, jak pokazano na rysunku
1. Utwórz kilka kafelków z różnych materiałów, jak pokazano na rysunku, i jedną część, która będzie działać jak kameleon.
2. Wstaw skrypt do części; następnie odwołaj się do części:
local camouflagePart = script.Parent
3. Użyj części kamuflażu, aby określić początek promienia; następnie zdefiniuj kierunek:
local camouflagePart = script.Parent
local origin = camouflagePart.Position
local direction = Vector3.new(0, -5, 0)
4. Utwórz promień i zapisz wynik:
local camouflagePart = script.Parent
local origin = camouflagePart.Position
local direction = Vector3.new(0, -5, 0)
local result = game.Workspace:Raycast(origin, direction)
5. Jeśli część została znaleziona wzdłuż promienia, zaktualizuj materiał części kamuflażu, aby pasował:
local camouflagePart = script.Parent
local origin = camouflagePart.Position
local direction = Vector3.new(0, -5, 0)
local result = game.Workspace:Raycast(origin, direction)
if result then
camouflagePart.Material = result.Material
end
WSKAZÓWKA
Długość promienia
Jeśli część nie zmienia materiałów, możliwe, że promień nie jest wystarczająco długi. Zaktualizuj kierunek lub przesuń część kamuflażu.
Sztuczka matematyczna 3D: wyznaczanie kierunku
Drugi parametr funkcji Raycast, kierunek, to Vector3. W prostych przypadkach możesz znać kierunek, w którym ma iść promień, i po prostu zakodować te wartości. Na przykład, jeśli chcesz sprawdzić poniżej punktu, po prostu użyj wartości ujemnych dla osi y. Ale co, jeśli chcesz zobaczyć, czy coś jest między dwoma punktami i czy nie są one po prostu nad i pod sobą, jak na rysunku
br>
Na szczęście istnieje wygodny sposób wyznaczania kierunku między dwoma punktami za pomocą matematyki wektorowej. Po prostu odejmij pozycję początku od pozycji celu:
local pointA = Vector3.new(0, -4, 0)
local pointB = Vector3.new(24, -10, 0)
lo cal fromAToB = pointB - pointa
Ustawianie parametrów Raycast
Chociaż wykrywanie części na promieniu jest przydatne, istnieje kilka konfiguracji, których możemy użyć w bardziej skomplikowanych sytuacjach. W szczególności są chwile, kiedy zależy nam tylko na wykrywaniu pewnych części w środowisku, a innych nie. Poniższe kroki pokazują, jak sprawdzić obiekt, ignorując listę innych obiektów:
1. Utwórz zmienne określające początek i kierunek promienia:
local origin = Vector3.new(0, 5, 0)
local direction = Vector3.new(0, -10, 0)
2. Utwórz nowy obiekt RaycastParams i zapisz go w zmiennej:
local origin = Vector3.new(0, 5, 0)
local direction = Vector3.new(0, -10, 0)
local parameters = RaycastParams.new()
3. Ustaw wartość parametrów.FilterDescendantsInstances na nową tabelę:
local origin = Vector3.new(0, 5, 0)
local direction = Vector3.new(0, -10, 0)
local parameters = RaycastParams.new()
parameters.FilterDescendantsInstances = {
-- Filter information goes here
}
4. Umieść obiekty, które mają być ignorowane przez raycast w nowej tabeli:
local origin = Vector3.new(0, 5, 0)
local direction = Vector3.new(0, -10, 0)
local parameters = RaycastParams.new()
parameters.FilterDescendantsInstances = {
game.Workspace.IgnorePart1,
game.Workspace.IgnorePart2,
}
5. Ustaw parametr FilterType obiektu Parameters na
local origin = Vector3.new(0, 5, 0)
local direction = Vector3.new(0, -10, 0)
local parameters = RaycastParams.new()
parameters.FilterDescendantsInstances = {
game.Workspace.IgnorePart1,
game.Workspace.IgnorePart2,
}
parameters.FilterType = Enum.RaycastFilterType.Blacklist
6. Wywołaj funkcję Raycast:
local origin = Vector3.new(0, 5, 0)
local direction = Vector3.new(0, -10, 0)
local parameters = RaycastParams.new()
parameters.FilterDescendantsInstances = {
game.Workspace.IgnorePart1,
game.Workspace.IgnorePart2,
}
parameters.FilterType = Enum.RaycastFilterType.Blacklist
local result = game.Workspace:Raycast(origin, directio n, parameters)
Raycast przez okno
Utwórz półprzezroczyste okno z obiektem po obu jego stronach. Rzuć promień pomiędzy obiekty, ignorując okno.
1. Utwórz kulę i sześcian po obu stronach szklanego okna, jak pokazano na rysunku
2. W ServerScriptService dodaj skrypt i utwórz odniesienia dla obu części i dla okna.
3. Użyj położenia kuli jako początku:
local sphere = game.Workspace.Sphere
local cube = game.Workspace.Cube
local window = game.Workspace.Window
local origin = sphere.Position
4. Aby znaleźć kierunek, odejmij położenie kuli od sześcianu:
local origin = sphere.Position
local direction = cube.Position - sphere.Position
5. Utwórz nowy obiekt RaycastParams:
local origin = sphere.Position
local direction = cube.Position - sphere.Position
local parameters = RaycastParams.new()
6. Skonfiguruj listę filtrów i dołącz okno:
local origin = sphere.Position
local direction = cube.Position - sphere.Position
local parameters = RaycastParams.new()
parameters.FilterDescendantsInstances = {
window,
}
parameters.FilterType = Enum.RaycastFilterType.Blacklist
7. Rzuć promień i potwierdź, że okno nie jest zwracane w wynikach:
local sphere = game.Workspace.Sphere
local cube = game.Workspace.Cube
local window = game.Workspace.Window
local origin = sphere.Position
local direction = cube.Position - sphere.Position
local parameters = RaycastParams.new()
parameters.FilterDescendantsInstances = {
window,
}
parameters.FilterType = Enum.RaycastFilterType.Blacklist
local result = game.Workspace:Raycast(origin, direction, parameters)
if result then
print("Found object:", result.Instance)
end
WSKAZÓWKA
Brak dowodu nie jest dowodem braku
Do tej pory przetestowałeś tylko, że promień nie wykrywa okna. Podczas kodowania staraj się zawsze myśleć o wielu sposobach testowania skryptów. W takim przypadku należy również potwierdzić, że zostaną wykryte części inne niż okno.
Sztuczka matematyczna 3D: ograniczenie kierunku
Drugi parametr funkcji Raycast nie tylko określa kierunek promienia, ale także jego długość. W niektórych przypadkach możesz chcieć, aby promień wydłużył się na określoną odległość. Na przykład, jeśli używasz raycastingu, aby pomóc wrogom wykryć gracza, możesz nie chcieć, aby widzieli całą mapę. Chciałbyś ograniczyć ich zasięg widzenia. W takich przypadkach można użyć wektora jednostkowego kierunku. Wektor jednostkowy Vector3 to inny wektor, który jest w tym samym kierunku, ale ma tylko 1 pin:
local maximumDistance = 10
local pointA = Vector3.new(2.5, 10, 0)
local pointB = Vector3.new(16, 5, -9)
local fromAToB = pointB - pointA
local unit = fromAToB.Unit
local fromAToBCapped = unit * maximumDistance
Streszczenie
Raycasting rysuje linię i zwraca wyniki tego, co znajduje się na tej linii. Możesz użyć tej techniki, aby sprawdzić przeszkody, narysować odbicia, stworzyć ogień z broni lub zaktualizować obiekt na podstawie jego otoczenia. Promień wymaga punktu początkowego X, Y, Z oraz kierunku X, Y, Z. Jeśli nie znasz kierunku, ale znasz punkt końcowy promienia, możesz znaleźć kierunek, odejmując początek od miejsca docelowego. Tak więc, jeśli masz początek (2, 2, 2) i współrzędne miejsca docelowego (7, 7, 7), twoim kierunkiem będzie (5, 5, 5). Opcjonalnie dla promienia można podać parametry, tak aby wykluczał on pewne obiekty na swojej ścieżce.
Pytania i odpowiedzi
P. Co jeśli zamiast ignorować obiekty wzdłuż promienia, chcę zwrócić tylko niektóre obiekty?
O. Jeśli chcesz wyszukiwać tylko określone typy obiektów zamiast obiektów na czarnej liście, możesz filtrować za pomocą Enum.RaycastFilterType.Whitelist.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Jakie są dwa parametry wymagane do rzucania promienia?
2. Jak możesz znaleźć kierunek promienia, jeśli znasz jego początek i cel?
3. Prawda czy fałsz: Promień będzie szukał rzeczy na wytyczonej ścieżce.
4. Prawda czy fałsz: Możesz ustawić maksymalną długość rzucanego promienia.
Odpowiedzi
1. Pochodzenie i kierunek.
2. Odejmij początek od celu, aby znaleźć kierunek promienia.
3. Fałsz. Promień zwraca wyniki tylko raz, chyba że powiesz mu inaczej.
4. Prawda. Możesz użyć wektora jednostkowego kierunku, aby ustawić maksymalną długość, jaką może narysować promień.
Ćwiczenia
Zbuduj w swojej grze detektor, który wykrywa, kiedy gracz jest w pobliżu. Detektor powinien zmieniać kolory, jeśli gracz znajdzie się w określonej odległości od detektora. Pamiętaj, że możesz zebrać wszystkich graczy w grze za pomocą Players:GetPlayers(), a postać gracza możesz uzyskać za pomocą player.Character.
Wskazówka: do tej pory raycasting wykonywałeś tylko raz na skrypt. Należy pamiętać, że funkcja Raycast rysuje promień tylko dokładnie w momencie jej wywołania. Aby zbudować odpowiedni detektor, musisz raycastować co kilka milisekund.
Umieszczanie obiektów w doświadczeniu: część 1
Umożliwienie użytkownikom kontrolowania wyglądu ich otoczenia daje im szansę wyrażenia siebie w stworzonym przez Ciebie świecie, a to sprawia, że są bardziej zaangażowani i bardziej skłonni do powrotu. Przez następne dwie godziny będziesz pracować nad swoim kluczowym projektem: tworzeniem przycisku, który umożliwi osobom z Twojego doświadczenia umieszczenie lub "wrzucenie" elementu w dowolne miejsce. Mogą go użyć do udekorowania domu, jak na rysunku, lub posadzenia kwiatów w ogrodzie.
Podczas pracy nad projektem zostaną omówione dwie nowe koncepcje: możliwość aktualizacji obiektu lub kodu przy każdym odświeżeniu gry oraz wykrywanie kliknięć graczy w środowisku 3D. Pod koniec nauczysz się śledzić ruch myszy użytkownika, aby umożliwić mu przeciągnięcie widmowego obrazu, który tylko on widzi. Aby to zrobić, poznasz krok renderowania. W ciągu następnej godziny nauczysz się, jak nasłuchiwać informacji wprowadzanych przez użytkownika, aby sfinalizować umieszczenie obiektu na serwerze.
Konfigurowanie obiektu
Do tego projektu potrzebny jest obiekt, który gracze mogą umieścić, oraz część, na której można umieścić obiekt. W tej sekcji możesz także skonfigurować zdarzenia, przyciski i foldery potrzebne do uporządkowania wszystkiego:
1. W Workspace dodaj nowy folder o nazwie Surfaces, aby przechowywać wszystkie części, w których ludzie mogą umieszczać elementy.
2. W folderze utwórz nową część, która posłuży jako podłoga do umieszczenia elementu, jak pokazano na rysunku
3. W ReplicatedStorage dodaj nowy folder o nazwie Events i nowy RemoteEvent o nazwie PlopEvent
4. Również w ReplicatedStorage dodaj folder o nazwie GhostObjects.
5. Wybierz obiekt, który chcesz umieścić i stwórz nieco przezroczystą wersję ducha, którą użytkownik będzie przeciągał przed zakończeniem umieszczania. Model powinien mieć podstawę w dolnej części modelu, która posłuży do wyrównania obiektu z podłogą. Rysunek przedstawia widmowe krzesło.
6. Upewnij się, że podstawą jest PrimaryPart i wstaw cały model do folderu GhostObjects
7. W ServerStorage dodaj kolejny folder o nazwie Ploppables i wstaw kopię tego samego modelu z normalną przezroczystością.
Powinien również mieć element bazowy jako Element podstawowy.
8. W StarterGui dodaj ScreenGui i wstaw TextButton o nazwie PlopButton.
Tworzenie przycisku Plop
Zaczynasz po stronie klienta i tworzysz skrypt lokalny, aby stworzyć duchowy obiekt, który ludzie mogą przemieszczać, gdy decydują, dokąd chcą się udać. Ponieważ jest to strona klienta, tylko użytkownik będzie mógł zobaczyć ducha. Pierwszym krokiem jest podłączenie danych wejściowych do przycisku plop, ponieważ jest to pierwsza część sekwencji działań, które gracz wykona w logice plop:
1. W StarterPlayer > StarterPlayerScripts dodaj nowy LocalScript.
2. W LocalScript utwórz zmienne dla usługi Player i lokalnego playera:
local Players = game:GetService("Players")
local player = Players.LocalPlayer
3. Utwórz zmienne dla komponentów GUI:
-- Previous code
local playerGui = player:WaitForChild("PlayerGui")
local plopScreen = playerGui:WaitForChild("ScreenGui")
local plopButton = plopScreen:WaitForChild("PlopButton")
4. Utwórz funkcję o nazwie OnPlopButtonActivated:
-- Previous code
local function onPlopButtonActivated()
end
5. Wewnątrz funkcji zmień właściwość Visible przycisku na false. W ten sposób przycisk jest ukryty, gdy gracz wykonuje plop:
-- Previous code
local function onPlopButtonActivated()
plopButton.Visible = false
end
6. Połącz funkcję ze zdarzeniem Activated plopButton:
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")
local plopScreen = playerGui:WaitForChild("ScreenGui")
local plopButton = plopScreen:WaitForChild("PlopButton")
local function onPlopButtonActivated()
plopButton.Visible = false
end
plopButton.Activated:Connect(onPlopButtonActivated)
plopButton.Activated:Connect(onPlopButtonActivated)
7. Przetestuj kod i upewnij się, że przycisk zniknie po kliknięciu.
Śledzenie ruchów myszy
Teraz, gdy masz przycisk, aby rozpocząć plopping, następnym krokiem jest śledzenie ruchów myszy gracza, ponieważ będziesz potrzebować tych informacji później, aby określić, gdzie umieścić obiekt.
Krok BindToRender
Za każdym razem, gdy ekran jest odświeżany, za sceną dzieje się mnóstwo kodu, który oblicza, co powinno pojawić się na ekranie. Nazywa się to krokiem renderowania. Jeśli coś musi być płynnie animowane, na przykład kamera, możesz dodać funkcje do kroku renderowania za pomocą BindToRenderStep(). Jednak chcesz być ostrożny przy dodawaniu zbyt wielu. Nadanie kroku renderowania zbyt dużej liczby czynności spowoduje spowolnienie częstotliwości odświeżania ekranu, co sprawi, że Twoje wrażenia będą wyglądać na opóźnione, a ruch będzie nierówny. BindToRenderStep jest częścią RunService i ma trzy parametry. Wygląda to tak: RunService:BindToRenderStep(nazwa powiązania, priorytet, nazwa funkcji)
Trzy części są następujące:
* bindingName: to nie to samo co nazwa funkcji; to nazwa wiązania.
* Priorytet: Wartość liczbowa określająca, jak szybko w kroku renderowania powinna zostać obliczona funkcja związana.
* functionName: Nazwa funkcji do powiązania.
W projekcie plopping do kroku renderingu dodasz kod, aby można było płynnie przesuwać widmowy obiekt przed sfinalizowaniem miejsca umieszczenia obiektu stałego:
1. W tym samym skrypcie lokalnym utwórz zmienną dla usługi RunService, która jest właścicielem kroku renderowania:
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
-- Rest of code
2. Utwórz zmienną dla nazwy powiązania:
-- Previous variables
local plopButton = plopScreen:WaitForChild("PlopButton")
local PLOP_MODE = "PLOP_MODE"
local function onPlopButtonActivated()
plopButton.Visible = false
local
plopButton.Activated:Connect(onPlopButtonActivated)
3. Powyżej onPlopButtonActivated() utwórz nową funkcję o nazwie onRenderStepped:
-- Previous variables
local plopButton = plopScreen:WaitForChild("PlopButton")
local PLOP_MODE = "PLOP_MODE"
local function onRenderStepped()
end
local function onPlopButtonActivated()
plopButton.Visible = false
end
plopButton.Activated:Connect(onPlopButtonActivated)
plopButton.Activated:Connect(onPlopButtonActivated)
4. Wewnątrz funkcji onPlopButtonActivated powiąż funkcję onRenderStepped:
-- Previous variables
local plopButton = plopScreen:WaitForChild("PlopButton")
local PLOP_MODE = "PLOP_MODE"
local function onRenderStepped()
end
local function onPlopButtonActivated()
plopButton.Visible = false
RunService:BindToRenderStep(PLOP_MODE,
Enum.RenderPriority.Camera.Value + 1, onRenderStepped)
end
plopButton.Ac tivated:Connect(onPlopButtonActivated)
WSKAZÓWKA
Określanie wartości priorytetu do wykorzystania
Zamiast próbować podać konkretną wartość, na przykład 20, kod ten wyszukuje wartość kamery gracza i dodaje jedną, dzięki czemu powiązany kod jest wykonywany zaraz po.
Raycasting z myszy
Następnie użyjesz rzutowania promieniowego, aby narysować linię od kursora użytkownika do dowolnych części w folderze Powierzchnie.
1. Nadal w tym samym skrypcie, pod zmienną player, dodaj zmienne dla kamery i myszy:
-- Previous variables
local player = Players.LocalPlayer
local camera = game.Workspace.Camera
local mouse = player:GetMouse()
local player Gui = player:WaitForChild("Player Gui")
-- Rest of code
WSKAZÓWKA
Zachowaj podobne zmienne razem
Przeskakujemy dość sporo, ale ważne jest, aby podobne zmienne były razem. Usługi powinny być z usługami, obiekty z obiektami, a stałe ze stałymi.
2. Utwórz parametry Raycast i zapisz je w zmiennej o nazwie RaycastParameters:
-- Previous variables
local plopButton = plopScreen:WaitForChild("PlopButton")
local raycastParameters = RaycastParams.new()
local PLOP_MODE = "PLOP_MODE"
-- Rest of code
3. Ustaw typ filtra na Biała lista i dodaj utworzony wcześniej folder powierzchni do listy instancji:
-- Previous variables
local plopButton = plopScreen:WaitForChild("PlopButton")
local raycastParameters = RaycastParams.new()
raycastParameters.FilterType = Enum.RaycastFilterType.Whitelist
raycastParameters.FilterDescendantsInstances = { game.Workspace.Surfaces }
local PLOP_MODE = "PLOP_MODE"
-- Rest of code
4. Utwórz zmienną o nazwie RAYCAST_DISTANCE. To będzie kontrolować, jak długi promień będzie później używany przez funkcję Raycast:
-- Previous variables
local PLOP_MODE = "PLOP_MODE"
local RAYCAST_DISTANCE = 200
local function onRenderStepped()
-- Rest of code
5. W funkcji onRenderStepped użyj funkcji ScreenPointToRay, aby promień zaczynał się od kamery i szedł w kierunku myszy gracza:
-- Other variables
local function onRenderStepped()
local mouseRay = camera:ScreenPointToRay(mouse.X, mouse.Y, 0)
end
-- Rest of code
6. Nadal w onRenderStepped(), użyj funkcji Raycast z wartościami otrzymanymi z ScreenPointToRay() w ostatnim kroku. Pamiętaj, że musisz pomnożyć kierunek przez wartość RAYCAST_DISTANCE, ponieważ ta wartość jest domyślnie wektorem jednostkowym:
local function onRenderStepped()
local mouseRay = camera:ScreenPointToRay(mouse.X, mouse.Y, 0)
-- Casts the ray using the origin and direction from mouseRay
local raycastResults = game.Workspace:Raycast(mouseRay.Origin,
mouseRay.Direction * RAYCAST_DISTANCE, raycastParameters)
end
7. Ponownie przetestuj swój kod. Gra nie zrobi nic innego niż ostatni test, ale upewnij się, że nie ma błędów.
Oto kod do tego punktu:
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local camera = game.Workspace.Camera
local mouse = player:GetMouse()
local playerGui = player:WaitForChild("PlayerGui")
local plopScreen = playerGui:WaitForChild("ScreenGui")
local plopButton = plopScreen:WaitForChild("PlopButton")
local raycastParameters = RaycastParams.new()
raycastParameters.FilterType = Enum.RaycastFilterType.Whitelist
raycastParameters.FilterDescendantsInstances = { game.Workspace.Surfaces }
local PLOP_MODE = "PLOP_MODE"
local RAYCAST_DISTANCE = 200
local function onRenderStepped()
local mouseRay = camera:ScreenPointToRay(mouse.X, mouse.Y, 0)
local raycastResults = game.Workspace:Raycast(mouseRay.Origin,
mouseRay.Direction * RAYCAST_DISTANCE, raycastParameters)
end
local function onPlopButtonActivated()
plopButton.Visible = false
RunService:BindToRenderStep(PLOP_MODE, Enum.RenderPriority.Camer a.Value + 1,
onRenderStepped)
end
plopButton.Activated:Connect(onPlopButtonActivated)
Podgląd obiektu
Następnym krokiem jest pokazanie fantomowej wersji obiektu plop, na który wskazuje mysz gracza. Daje to graczowi podgląd ich wyboru. Ten przykład obejmuje umieszczenie obiektu o nazwie GhostChair. Jeśli Twój obiekt ma inną nazwę, zaktualizuj ją w kodzie:
1. Nadal w tym samym skrypcie utwórz zmienne dla ReplicatedStorage i przechowywanego w nich obiektu ducha:
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
...
...
raycastParameters.FilterDescendantsInstances = {game.Workspace.Surfaces}
local ghostObjects = ReplicatedStorage:WaitForChild("GhostObjects")
local ghostChair = ghostObjects:Wa itForChild("GhostChair")
local PLOP_MODE = "PLOP_MODE"
WSKAZÓWKA
Brak kodu
Jeśli widzisz
w próbce kodu, załóż, że kod nie jest w tej chwili wyświetlany.
2. Utwórz zmienną o nazwie plopCFrame. Spowoduje to zapisanie pozycji, na którą wskazuje mysz gracza:
...
local ghostChair = ReplicatedStorage:WaitForChild("GhostChair")
local plopCFrame = nil
local PLOP_MODE = "PLOP_MODE"
...
3. Wewnątrz funkcji onRenderStepped i po Raycast sprawdź, czy Raycast zwrócił jakieś wyniki:
...
local function onRenderStepped()
local mouseRay = camera:ScreenPointToRay(mouse.X, mouse.Y, 0)
local raycastResults = game.Workspace:Raycast(mouseRay.Origin,
mouseRay.Direction * RAYCAST_DISTANCE, raycastParameters)
if raycastResults then
end
end
...
4. Jeśli Raycast zwrócił wynik, oznacza to, że mysz znajduje się w prawidłowym obszarze i powinieneś pokazać tam obiekt. Ustaw wartość plopCFrame na pozycję wyniku Raycast:
...
local function onRenderStepped()
local mouseRay = camera:ScreenPointToRay(mouse.X, mouse.Y, 0)
local raycastResults = game.Workspace:Raycast(mouseRay.Origin,
mouseRay.Direction * RAYCAST_DISTANCE, raycastParameters)
if raycastResults then
plopCFrame = CFrame.new(raycastResults.Position)
end
end
...
5. Użyj funkcji SetPrimaryPartCFrame obiektu ducha, aby przenieść obiekt do plopCFrame:
...
local function onRenderStepped()
local mouseRay = camera:ScreenPointToRay(mouse.X, mouse.Y, 0)
local raycastResults = game.Workspace:Raycast(mouseRay.Origin,
mouseRay.Direction * RAYCAST_DISTANCE, raycastParameters)
if raycastResults then
plopCFrame = CFrame.new(raycastResults.Position)
ghostChair:SetPrimaryPartCFrame(plopCFrame)
end
end
...
6. Przenieś obiekt do obszaru roboczego. W instrukcji else przenieś obiekt z powrotem do ReplicatedStorage:
...
local function onRenderStepped()
local mouseRay = camera:ScreenPointToRay(mouse.X, mouse.Y, 0)
local raycastResults = game.Workspace:Raycast(mouseRay.Origin, mouseRay.
Direction * RAYCAST_DISTANCE, raycastParameters)
if raycastResults then
plopCFrame = CFrame.new(raycastResults.Position)
ghostChair:SetPrimaryPartCFrame(plopCFrame)
ghostChair.Parent = game.Workspace
else
plopCFrame = nil
ghostChair.Parent = ReplicatedStorage
end
end
...
7. Przetestuj swoją grę. Upewnij się, że po kliknięciu przycisku Plop Chair po najechaniu myszką na obszar budowy zobaczysz widmowe krzesło, jak pokazano na rysunku
Streszczenie
W ciągu tej godziny nauczyłeś się, jak tworzyć duchy, które widzi tylko użytkownik. Dowiedziałeś się również o kroku renderowania, krótkim czasie, w którym obliczana jest cała grafika. Aby powiązać obiekty z krokiem renderowania, użyj
RunService:BindToRenderStep(bindingName,priorytet,functionName). Parametry działają w następujący sposób:
- bindingName: To nie to samo co nazwa funkcji; to nazwa wiązania. Pozwala łączyć i rozłączać rzeczy z krokiem renderowania.
-priorytet: wartość liczbowa określająca, jak szybko w kroku renderowania ma zostać obliczona funkcja związana.
- functionName: Nazwa funkcji do powiązania.
W ciągu następnej godziny dowiesz się, jak zakończyć umieszczanie obiektu, nasłuchując kliknięć użytkownika.
Pytania i odpowiedzi
P. Dlaczego używamy białej listy do raycastu?
O. Biała lista umożliwia wyszukiwanie tylko określonych obiektów. Teoretycznie możesz umieścić na czarnej liście każdy inny obiekt w doświadczeniu, ale byłaby to znacznie dłuższa lista i dużo więcej pracy. Będziesz także musiał aktualizować czarną listę za każdym razem, gdy dodasz nowy obiekt do doświadczenia.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Jaki jest krok renderowania?
2. Co robi BindToRenderStep?
3. Jakiej usługi potrzebujesz, aby dodać akcje do kroku renderowania?
4. Jaki jest pierwszy parametr BindToRenderStep i do czego służy?
Odpowiedzi
1. Etap renderowania polega na wykonaniu wszystkich obliczeń potrzebnych do wyświetlenia obrazu na ekranie.
2. BindToRenderStep łączy funkcję z krokiem renderowania, sprawiając, że dzieje się to podczas tego procesu.
3. Aby dodać akcje do kroku renderowania, potrzebujesz RunService.
4. Pierwszy parametr dotyczy nazwy powiązania, co pozwala nie tylko łączyć funkcje z krokiem renderowania, ale także rozłączać elementy.
Ćwiczenia
Użyj tego samego wzorca pokazanego do tej pory, aby dodać drugi obiekt dla użytkowników, jak pokazano na rysunku
.
Wskazówka: Pamiętaj, aby upewnić się, że model ma zestaw bazowy jako część podstawową. Rozwiązanie kodowe znajdziesz w załączniku
Umieszczanie obiektów w doświadczeniu: część 2
To druga godzina twojego dwuczęściowego projektu zwieńczenia. W ciągu ostatniej godziny nauczyłeś się, jak śledzić ruch myszy gracza i aktualizować to, co widzi, za pomocą kroku renderowania. W tej godzinie używasz usługi ContextActionService, aby nasłuchiwać, czy gracz kliknie i zakończy umieszczanie obiektu, na przykład gdy gracz dekoruje dom w Figure
Wykrywanie danych wejściowych myszy
Teraz, gdy wiesz, gdzie gracz zamierza umieścić obiekt, nadszedł czas, aby nasłuchiwać, kiedy gracz kliknie, aby zakończyć umieszczanie obiektu. W tym celu używasz usługi ContextActionService. Usługa ContextActionService umożliwia wykonywanie działań tylko pod pewnymi warunkami. Typowym zastosowaniem jest szybkie wiązanie i odłączanie akcji z danymi wprowadzanymi przez gracza, takimi jak kliknięcia myszą lub naciśnięcia klawiatury za pomocą funkcji BindAction(), w następujący sposób:
ContextActionService:BindAction(actionName, functionName, addMobileButton, inputTypes)
-actionName: Nazwa powiązania.
- nazwa_funkcji: Funkcja do wywołania po wyzwoleniu wejścia.
- addMobileButton: Dodaje przycisk na ekranie dla tej akcji na urządzeniach mobilnych.
- inputTypes: Lista danych wejściowych do uruchomienia tego powiązania.
Funkcja, którą powiążesz z BindAction, powinna mieć następujące parametry:
onInput(actionName, inputState)
-actionName: Nazwa powiązania.
- inputState: W jakim stanie znajdowało się wejście w momencie wywołania tej funkcji.
Jeśli nie musisz już nasłuchiwać określonych danych wejściowych, możesz usunąć powiązanie za pomocą funkcji UnbindAction:
ContextActionService:UnbindAction(actionName)
1. W tym samym skrypcie co poprzednio utwórz zmienną dla ContextActionService i jedną dla wiążącej nazwy:
local ContextActionService = game:GetService("ContextActionService")
local Players = game:GetService("Players")
...
...
local plopCFrame = nil
local PLOP_CLICK = "PLOP_CLICK"
local PLOP_MODE = "PLOP_MODE"
...
2. Powyżej funkcji onPlopButtonActivated() dodaj funkcję o nazwie onMouseInput:
...
local function onMouseInput(actionName, inputState)
end
local function onPlopButtonActivated()
...
3. Przełącz się na funkcję onPlopButtonActivated i powiąż funkcję onMouseInput za pomocą funkcji BindAction:
...
local function onPlopButtonActivated()
plopButton.Visible = false
RunService:BindToRenderStep(PLOP_MODE,
Enum.RenderPriority.Camera.Value + 1, onRenderStepped)
ContextActionService:BindAction(PLOP_CLICK, onMouseInput, false,
Enum.UserInputType.MouseButton1)
end
...
4. W funkcji onMouseInput sprawdź, czy stan wejścia to Koniec, ponieważ w tym momencie gracz kliknął myszką:
...
local function onMouseInput(actionName, inputState)
if inputState == Enum.UserInputState.End then
end
end
...
...
5. W instrukcji if upewnij się, że obiekt widmo znajduje się z powrotem w ReplicatedStorage:
...
local function onMouseInput(actionName, inputState)
if inputState == Enum.UserInputState.End then
ghostChair.Parent = ReplicatedStorage
end
end
...
6. Również w instrukcji if rozdziel akcję click i akcje renderowania, używając UnbindAction i UnbindFromRenderStep:
...
local function onMouseInput(actionName, inputState)
if inputState == Enum.UserInputState.End then
ghostChair.Parent = ReplicatedStorage
RunService:UnbindFromRenderStep(PLOP_MODE)
ContextActionService:UnbindAction(PLOP_CLICK)
end
end
...
7. Spraw, aby przycisk plop był ponownie widoczny:
...
local function onMouseInput(actionName, inputState)
if inputState == Enum.UserInputState.End then
ghostChair.Parent = ReplicatedStorage
RunService:UnbindFromRenderStep(PLOP_MODE)
ContextActionService:UnbindAction(PLOP_CLICK)
plopButton.Visible = true
end
end
...
8. Przetestuj swoją grę. Potwierdź, że po kliknięciu obiekt znika, a przycisk plop pojawia się ponownie.
Wysyłanie wiadomości do serwera
Teraz musisz powiadomić serwer, że gracz chce umieścić obiekt i gdzie chce go umieścić. W tym celu używasz zdarzenia RemoteEvent, które utworzyliśmy w konfiguracji:
1. W tym samym skrypcie utwórz zmienną dla zdarzenia zdalnego:
...
local ghostChair = ReplicatedStorage:WaitForChild("GhostChair")<
br>
local events = ReplicatedStorage:WaitForChild("Events")
local plopEvent = events:WaitForChild("PlopEvent")
local raycastParameters = RaycastParams.new()
...
2. Na końcu polecenia onMouseInput iw instrukcji if uruchom zdarzenie zdalne, przekazując zmienną plopCFrame jako argument, jeśli plopCFrame istnieje:
...
local function onMouseInput(actionName, inputState)
if inputState == Enum.UserInputState.End then
ghostChair.Parent = ReplicatedStorage
RunService:UnbindFromRenderStep(PLOP_MODE)
ContextActionService:UnbindAction(PLOP_CLICK)
plopButton.Visible = true
if plopCFrame then
plopEvent:FireServe r(plopCFrame)
end
end
end
...
WSKAZÓWKA
Radzenie sobie z wieloma przedmiotami
Jeśli wykonałeś ćwiczenie w ciągu ostatniej godziny i masz wiele obiektów, musisz również przesłać nazwę obiektu ducha.
Odbieranie wiadomości
Teraz czas na zmianę skryptów! Nowy skrypt w ServerScriptService nasłuchuje zdarzenia PlopEvent i umieszcza obiekt w miejscu, w którym każdy może go wreszcie zobaczyć na serwerze:
1. W ServerScriptService dodaj nowy skrypt.
2. Utwórz zmienne dla ReplicatedStorage i ServerStorage:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
3. Utwórz zmienne dla obiektu RemoteEvent i krzesła:
local events = ReplicatedStorage.Events
local plopEvent = events.PlopEvent
local ploppables = ServerStorage.Ploppables
local chair = ploppables.Chair
4. Utwórz funkcję o nazwie onPlop, która jako argument przyjmuje gracza i CFrame:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local events = ReplicatedStorage.Events
local plopEvent = events.PlopEvent
local ploppables = ServerStorage.Ploppables
local chair = ploppables.Cha ir
local functio n onPlop(player, cframe)
end
WSKAZÓWKA
Radzenie sobie z wieloma przedmiotami
Pamiętaj, aby dodać tutaj kolejny parametr, aby wziąć e w nazwie obiektu, jeśli masz więcej niż jeden.
5. Połącz funkcję onPlop ze zdarzeniem OnServerEvent obiektu plopEvent:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local events = ReplicatedStorage.Events
local plopEvent = events.PlopEvent
local ploppables = ServerStorage.Ploppables
local chair = ploppables.Chair
local function onPlop(player, cframe)
end
plopEvent.OnServerEvent:Connect(onPlop)
6. Wewnątrz funkcji onPlop utwórz kopię krzesła za pomocą funkcji Clone:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local events = ReplicatedStorage.Events
local plopEvent = events.PlopEvent
local ploppables = ServerStorage.Ploppables
local chair = ploppables.Chair
local function onPlop(player, cframe)
local chairCopy = chair:Clone()
end
plopEvent.OnServerEvent:Connect(onPlop)
7. Przenieś skopiowane krzesło do CFrame i zmień jego rodzica na przestrzeń roboczą:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local events = ReplicatedStorage.Events
local plopEvent = events.PlopEvent
local ploppables = ServerStorage.Ploppables
local chair = ploppables.Chair
local function onPlop(player, cframe)
local chairCopy = chair:Clone()
chairCopy:SetPrimaryPartCFrame(cframe)
chairCopy.Parent = game.Workspace
end
8. Przetestuj swoją grę. Potwierdź, że po kliknięciu miejsca, w którym chcesz umieścić obiekt, zostanie on dodany do obszaru roboczego. Spróbuj także ustawić kilka krzeseł.
Streszczenie
Gratulacje! Ukończyłeś duży projekt, w którym wykorzystałeś całą wiedzę zdobytą w tej książce. Oto krótkie podsumowanie umiejętności, których użyłeś do stworzenia systemu ploppingu, który umożliwia użytkownikom dekorowanie i większą kontrolę nad ich otoczeniem:
1. Użyłeś raycastingu, aby znaleźć lokalizację myszy i białej listy, aby upewnić się, że zwróciła tylko nazwy niektórych obiektów.
2. Użytkownicy mogli przeciągać widmowy obiekt za pomocą myszy, a lokalizacja była aktualizowana przez powiązanie funkcji z krokiem renderowania.
3. Użytkownicy sfinalizowali umieszczenie obiektu, klikając. Użyłeś usługi ContextActionService do powiązania funkcji z kliknięciem i umieszczenia obiektu.
To ostatnia część , ale jest jeszcze wiele do nauczenia się. Cały kod, nad którym pracowałeś w tej książce, powinien być traktowany jako punkt wyjścia. W przypadku każdego projektu możesz rozszerzyć kod i wykorzystać to, co znasz, w nowy sposób. Istnieje wspaniała społeczność programistów Roblox, którzy mogą ci pomóc na forach programistów Roblox i mnóstwo próbek kodu do wykorzystania na developer.roblox.com. Rozwijając swoje umiejętności, pamiętaj o tym, jak ważna jest organizacja i testowanie kodu w wielu scenariuszach. Zawsze powinieneś myśleć o tym, jak różne sytuacje, takie jak różne rozmiary ekranu i posiadanie wielu osób na serwerze, wpływają na zachowanie twojego kodu. Najlepiej byłoby, gdybyś nawet poprosił inne osoby o wypróbowanie Twojego doświadczenia i upewnienie się, że wszystko działa zgodnie z oczekiwaniami.
Pytania i odpowiedzi
P: Jak sprawić, by przyciski plopu znikały, kiedy ich nie potrzebujesz?
O. Możesz umieścić wszystkie GUI potrzebne do przycisków plopu w większej ramce, nazwanej na przykład Dekoracje lub Sklep. Domyślnie wyłącz ramkę. Wykorzystaj swoją wiedzę, aby utworzyć kolejny przycisk, który ludzie będą mogli klikać, aby włączać i wyłączać ramkę oraz wyświetlać lub ukrywać wszystkie wybrane dekoracje.
P. Jakich innych typów działań można użyć w usłudze ContextActionService?
O. Możesz skonfigurować scenariusze, które włączają nowe sterowanie w zależności od tego, co robi gracz. Na przykład, jeśli są w samochodzie, możesz włączyć przyciski działające jako hamulce, gaz i klakson. Następnie możesz wyłączyć te przyciski, gdy odtwarzacza nie ma w samochodzie.
Warsztat
Teraz, gdy skończyłeś, przejrzyjmy to, czego się nauczyłeś. Poświęć chwilę, aby odpowiedzieć na następujące pytania.
Kartkówka
1. Co umożliwia usługa ContextActionService?
2. Jaka jest funkcja, która umożliwia włączenie niektórych klawiszy w określonych okolicznościach?
3. Jeśli masz wiele obiektów, które można umieścić, jaką właściwość musisz przekazać oprócz kodu podstawowego?
Odpowiedzi
1. ContextActionService umożliwia takie ustawienie, aby akcja mogła mieć miejsce tylko w określonych warunkach.
2. Użyj funkcji BindAction(), jeśli chcesz włączyć określone klawisze w określonych okolicznościach.
3. Musisz również przesłać nazwę obiektu, jeśli masz wiele obiektów, które można umieścić.
Ćwiczenia
Spróbuj dodać polecenie obracania obiektów podczas ich umieszczania. Za każdym razem, gdy gracz naciśnie klawisz, obiekt powinien obrócić się o określoną liczbę stopni.
Porady
- Ustaw stałą dla tego, o ile stopni obiekt powinien się obracać.
- Użyj usługi ContextActionService, aby włączyć obracanie obiektu za pomocą klawisza, takiego jak R.