GameHosting.pl

Notatki operatora

Datapack: funkcje .mcfunction i scoreboardy w Minecraft

Część trzecia serii o datapackach. Tu robi się ciekawie: piszemy własne funkcje w plikach .mcfunction, podpinamy je pod tagi minecraft:tick i minecraft:load, a scoreboardu używamy jako licznika i timera. Na końcu składamy z tego prosty, działający przykład mechaniki. Składnia opisana dla Minecraft 1.21 i nowszych, bo właśnie tam zmieniły się nazwy folderów.

Opublikowano · ~9 min czytania

Seria o datapackach (4 części):

  1. Część 1: Datapack w Minecraft, czyli od czego zacząć (hub)
  2. Część 2: Własne przepisy (recipe) w datapacku
  3. Część 3: Funkcje i scoreboardy (czytasz tę część)
  4. Część 4: Własne przedmioty w datapacku

W skrócie: funkcja to plik .mcfunction z listą komend, jedna na linię, bez ukośnika na początku. Komentarze zaczynasz od #. Funkcję z tagu minecraft:load gra odpala raz przy starcie i przy /reload (tam tworzysz cele scoreboardu), a funkcję z tagu minecraft:tick co ticka, czyli 20 razy na sekundę (tam żyje pętla mechaniki). Scoreboard typu dummy to licznik, którym sterujesz komendami scoreboard players set/add/operation. Warunki dla graczy sprawdzasz przez execute as @a at @s if score @s ... run .... Jeśli nie chcesz stawiać serwera od zera, weź gotowy hosting z dostępem do plików.

Pliki .mcfunction: reguły, których łatwo nie złamać

W części pierwszej powstał szkielet datapacku. Teraz dokładamy do niego logikę. Funkcja to zwykły plik tekstowy z rozszerzeniem .mcfunction, w którym każda linia to jedna komenda. Trzy reguły, które trzeba mieć w głowie od początku:

Funkcje leżą w folderze data/<przestrzen>/function/. To ważny szczegół wersyjny: od Minecraft 1.21 ten folder nazywa się function w liczbie pojedynczej (wcześniej był functions). Jeśli używasz starszego poradnika, najczęstszą przyczyną tego, że funkcja nie działa na 1.21, jest właśnie stara nazwa folderu z literą s na końcu.

Funkcję uruchamiasz z czatu komendą /function <przestrzen>:<nazwa>, na przykład /function moj_pack:powitanie dla pliku data/moj_pack/function/powitanie.mcfunction. Po każdej zmianie w plikach wywołaj /reload, żeby gra wczytała nową wersję.

# data/moj_pack/function/powitanie.mcfunction
# Prosta funkcja: napis na czacie i dzwiek dla wszystkich graczy

say Datapack zaladowany
playsound minecraft:entity.player.levelup master @a

Tagi funkcji: tick i load

Samo napisanie funkcji nie sprawia, że gra ją odpala. Albo wywołujesz ją ręcznie komendą /function, albo podpinasz pod tag funkcji, czyli plik JSON, który mówi grze: te funkcje uruchamiaj automatycznie. Tagi leżą w data/minecraft/tags/function/, i tu znów liczba pojedyncza function od wersji 1.21. Dwa wbudowane tagi załatwiają większość potrzeb:

Plik tagu to JSON z tablicą values, w której wpisujesz pełne nazwy funkcji (przestrzeń i nazwa). Tak wygląda podpięcie funkcji startowej pod load:

// data/minecraft/tags/function/load.json
{
  "values": [
    "moj_pack:start"
  ]
}

I analogicznie pętla co ticka:

// data/minecraft/tags/function/tick.json
{
  "values": [
    "moj_pack:petla"
  ]
}

Uwaga: tagi tick i load należą do przestrzeni minecraft, dlatego leżą w data/minecraft/tags/function/, a nie w folderze Twojego packa. Same funkcje, na które wskazują, są już w Twojej przestrzeni (tu moj_pack). To częsty błąd początkujących, że wrzucają tick.json do własnej przestrzeni i potem dziwią się, że nic się nie dzieje.

Scoreboard: licznik, którym sterujesz komendami

Scoreboard to wbudowany system liczb przypisanych do graczy i do tak zwanych fałszywych graczy. Dla mechaniki datapacku to po prostu pamięć na wartości całkowite. Pracujesz na celach (objectives), które najpierw trzeba utworzyć.

Tworzenie celu

Cel zakładasz raz, najlepiej w funkcji z tagu load:

scoreboard objectives add timer dummy
scoreboard objectives add kliki dummy

Kryterium dummy oznacza, że gra nie zmienia tej wartości sama. To Ty nią sterujesz komendami, więc nadaje się idealnie na własne liczniki i timery. (Istnieją kryteria, które gra aktualizuje automatycznie, na przykład liczące zgony czy skoki, ale do własnej mechaniki najczęściej chcesz właśnie dummy.)

Ustawianie i zmiana wartości

Trzy komendy, które robią 90 procent roboty:

KomendaCo robi
scoreboard players set <cel> <obiekt> <liczba>Ustawia wynik na konkretną liczbę (na przykład zeruje licznik).
scoreboard players add <cel> <obiekt> <liczba>Dodaje liczbę do wyniku (zwiększa licznik o krok).
scoreboard players operation <cel> <obiekt> <op> <zrodlo> <obiekt2>Działanie między dwoma wynikami (kopiowanie, dodawanie, minimum, maksimum i tak dalej).

W miejscu <cel> wpisujesz nick gracza, selektor (na przykład @s) albo fałszywego gracza. Fałszywy gracz to dowolna nazwa, której nie ma w świecie, używana jako globalny schowek na jedną liczbę. Z przyzwyczajenia zapisuje się ją z hashem na początku, na przykład #t, żeby było widać, że to nie prawdziwy gracz. Hash nie ma magicznego znaczenia w składni, to tylko konwencja, ale dodatkowo sprawia, że taki wpis nie pojawia się przypadkiem przy selektorach graczy.

Operacje w scoreboard players operation mają stałe symbole:

Timer w tagu tick

Najprostsza pętla czasu: w funkcji odpalanej co ticka zwiększasz licznik o jeden, a gdy dobije do progu, wykonujesz akcję i zerujesz go. Dwadzieścia ticków to jedna sekunda, więc próg 20 daje akcję raz na sekundę.

# data/moj_pack/function/petla.mcfunction
# Uruchamiane co tick z tagu minecraft:tick

# zwieksz licznik o 1
scoreboard players add #t timer 1

# gdy licznik dojdzie do 20 (jedna sekunda):
#  - wykonaj akcje (tu: dzwiek dla wszystkich)
#  - wyzeruj licznik
execute if score #t timer matches 20.. run playsound minecraft:block.note_block.pling master @a
execute if score #t timer matches 20.. run scoreboard players set #t timer 0

Zapis matches 20.. to przedział: dwadzieścia i więcej. Używamy go zamiast samego 20, bo gdyby z jakiegoś powodu licznik przeskoczył próg (na przykład po lagu serwera), warunek i tak się złapie. Sprawdzanie konkretnej liczby przedziałem to typowa ostrożność w mechanikach na tick.

execute as @a at @s if score: warunki na graczach

Komenda execute to sposób na warunki i na zmianę tego, kto i gdzie wykonuje akcję. Łańcuch buduje się z podkomend, a zamyka go zawsze run z właściwą komendą na końcu. Cztery podkomendy, które tu używamy:

Złożone razem dają wzorzec, który czyta się jak zdanie: dla każdego gracza, w jego pozycji, jeśli jego wynik spełnia warunek, wykonaj komendę.

# dla kazdego gracza, ktory ma kliki >= 10,
# zagraj dzwiek przy nim i wypisz mu komunikat
execute as @a at @s if score @s kliki matches 10.. run playsound minecraft:entity.player.levelup master @s
execute as @a if score @s kliki matches 10.. run tellraw @s {"text":"Gratulacje, 10 klikniec!","color":"green"}

Można też porównywać dwa wyniki zamiast sprawdzać przedział, na przykład if score @s kliki >= #cel kliki z operatorami <, <=, =, >=, >. Przedział (matches) jest jednak czytelniejszy do prostych progów.

schedule function: opóźnienie i własna pętla

Nie wszystko musi chodzić co ticka. Gdy chcesz coś zrobić za chwilę, użyj schedule function:

schedule function moj_pack:krok 20t

To odpali funkcję moj_pack:krok za 20 ticków, czyli za sekundę. Czas podajesz z sufiksem: t to ticki (domyślne, można pominąć), s to sekundy (20 ticków), d to dzień gry (24000 ticków). Pełna składnia to schedule function <funkcja> <czas> [append|replace], a zaplanowane wywołanie skasujesz przez schedule clear <funkcja>.

Sztuczka: funkcja może zaplanować samą siebie, tworząc własną, rzadszą pętlę bez obciążania tagu tick. To wygodne dla mechanik, które mają działać na przykład co 5 sekund, a nie co tick:

# data/moj_pack/function/krok.mcfunction
say tik co 5 sekund
schedule function moj_pack:krok 5s

Taką pętlę rozkręcasz raz, na przykład w funkcji z tagu load, jednym wywołaniem schedule function moj_pack:krok 5s, a dalej podtrzymuje się sama.

Tagi encji: oznaczanie, żeby nie powtórzyć akcji

Tag encji to etykieta przypięta do gracza lub innej encji. Nie myl go z tagiem funkcji, to zupełnie co innego. Tag encji nadajesz i zdejmujesz komendą tag:

Po nadaniu tagu możesz wybierać tylko oznaczone encje selektorem z filtrem tag, na przykład @a[tag=nagrodzony] (gracze z tagiem) albo @a[tag=!nagrodzony] (gracze bez niego). To rozwiązuje klasyczny problem: jak nadać nagrodę dokładnie raz, a nie co tick? Oznaczasz gracza po nagrodzeniu i pomijasz oznaczonych przy kolejnych sprawdzeniach.

Działający przykład: nagroda za warunek

Składamy wszystko w jedną kompletną mechanikę. Cel: gdy gracz uzbiera 64 sztuki diamentów w ekwipunku, dostaje jednorazowo efekt i komunikat. Liczbę diamentów wykryjemy przez sprawdzenie ekwipunku, a tag encji dopilnuje, żeby nagroda padła tylko raz.

1. Konfiguracja w tagu load

// data/minecraft/tags/function/load.json
{
  "values": [
    "moj_pack:start"
  ]
}
# data/moj_pack/function/start.mcfunction
# uruchamiane raz przy starcie i przy /reload

scoreboard objectives add timer dummy
say Mechanika nagrody zaladowana

2. Pętla w tagu tick

// data/minecraft/tags/function/tick.json
{
  "values": [
    "moj_pack:petla"
  ]
}
# data/moj_pack/function/petla.mcfunction
# uruchamiane co tick

# licznik 0..19 -> sprawdzamy warunek raz na sekunde, nie co tick
scoreboard players add #t timer 1
execute if score #t timer matches 20.. run function moj_pack:sprawdz
execute if score #t timer matches 20.. run scoreboard players set #t timer 0

3. Sprawdzenie warunku i nagroda

# data/moj_pack/function/sprawdz.mcfunction
# dla kazdego gracza, ktory NIE byl jeszcze nagrodzony
# i ma w ekwipunku co najmniej 64 diamenty:
#  - nadaj nagrode w jego pozycji
#  - oznacz go tagiem, zeby nie nagrodzic ponownie

execute as @a[tag=!nagroda_diamenty,nbt={Inventory:[{id:"minecraft:diamond",Count:64b}]}] at @s run function moj_pack:nagroda
# data/moj_pack/function/nagroda.mcfunction
# wykonawca to gracz, ktory spelnil warunek; miejsce = jego pozycja

effect give @s minecraft:hero_of_the_village 200 0
playsound minecraft:ui.toast.challenge_complete master @s
tellraw @s {"text":"Nagroda za 64 diamenty odebrana!","color":"gold"}
tag @s add nagroda_diamenty

Jak to działa razem: tag load przy starcie zakłada cel timer. Tag tick co ticka podbija licznik, a raz na sekundę odpala sprawdz i zeruje licznik. Funkcja sprawdz wybiera tylko graczy, którzy jeszcze nie mają etykiety nagroda_diamenty i mają w ekwipunku pełny stos diamentów, i dla każdego z nich odpala nagroda w jego pozycji. Ta z kolei daje efekt, gra dźwięk, pisze komunikat i nadaje etykietę, więc przy kolejnym sprawdzeniu gracz jest już pomijany przez filtr tag=!nagroda_diamenty.

Z doświadczenia: warunek po ekwipunku (sprawdzanie Count:64b) złapie dokładnie stos po 64. Realne mechaniki często luzują to do prostszego progu albo używają osobnego celu scoreboardu z kryterium liczącym posiadane przedmioty. Dla pierwszego packa ważniejsze jest, żebyś zrozumiał szkielet: load konfiguruje, tick co chwilę sprawdza, tag encji pilnuje jednorazowości. Tę samą konstrukcję zastosujesz do dowolnej nagrody za dowolny warunek.

Najczęstsze problemy

Najczęstsze pytania

Czy komendy w pliku .mcfunction zaczynają się od ukośnika?

Nie. Każda komenda zajmuje jedną linię i jest bez ukośnika na początku (say czesc, a nie /say czesc). Komentarze zaczynasz od #, a puste linie są ignorowane.

Jaka jest różnica między minecraft:tick a minecraft:load?

Funkcje z minecraft:load odpalają się raz przy starcie i przy /reload (tam zakładasz cele scoreboardu). Funkcje z minecraft:tick odpalają się co tick, 20 razy na sekundę (tam żyje pętla mechaniki). Oba tagi to pliki JSON z tablicą values, w data/minecraft/tags/function/.

Jak zrobić timer albo licznik na scoreboardzie?

Załóż cel dummy (scoreboard objectives add timer dummy), w tagu tick zwiększaj go o jeden (scoreboard players add #t timer 1), a po dojściu do progu wykonaj akcję i wyzeruj (scoreboard players set #t timer 0). Zapis z hashem, jak #t, to fałszywy gracz służący jako globalny schowek na liczbę.

Co dokładnie robi execute as @a at @s if score @s ... run ...?

as @a ustawia każdego gracza po kolei jako wykonawcę (od tej chwili @s to ten gracz), at @s przenosi miejsce wykonania na jego pozycję, if score sprawdza jego wynik, a run wykonuje komendę dla tych, którzy spełnili warunek. run zawsze kończy łańcuch i może wystąpić raz.

Do czego służy schedule function i tagi encji?

schedule function nazwa czas odpala funkcję z opóźnieniem (np. 20t to sekunda), a funkcja może planować samą siebie, tworząc własną pętlę. Tagi encji (tag @s add nazwa) oznaczają encje, dzięki czemu selektorem @a[tag=nazwa] wybierasz tylko oznaczone, na przykład żeby nie nadać nagrody dwa razy.

Co dalej w serii

Czytasz część 3. Przejdź dalej albo wróć:

  1. Część 1: Datapack w Minecraft, czyli od czego zacząć (hub, zacznij tu, jeśli nie masz jeszcze szkieletu packa)
  2. Część 2: Własne przepisy w datapacku (poprzednia)
  3. Część 3: Funkcje i scoreboardy (ta część)
  4. Część 4: Własne przedmioty w datapacku (dalej)

Powiązane