Ekwpiunek i statystyki

Wtorek, 11 Czerwca 2024, 22:52
Czas czytania 9 minut, 32 sekundy
Zgodne z GM: gms2
Opis tego jak stworzyć ekwipunek i statystyki postaci.
Mamy już na GMCLANie artykuł o tym jak stworzyć inwentory, a teraz przyszedł czas na ekwipunek. Oba systemy można ze sobą połączyć, lub używać rozdzielnie. Dla uproszenia, w tym artykule założymy, że nie będzie istniał plecak, a zdobyty przedmiot będziemy mogli wymieniać.

Baza przedmiotów
Po pierwsze, musimy stworzyć bazę przedmiotów. W przeciwieństwie do przykładu z inventory, nie wystarczy nam lista spritów czy obiektów - itemy muszą bowiem mieć swoje statystyki.

Itemy muszą gdzieś trafić - potrzebujemy dwóch rzeczy - tablicy z ID itemów jakie obecnie są wyekwipowane (-1 gdy żaden), oraz typów slotów, żeby przedmioty automatycznie trafiały w dobre miejsca.

Ja uznałem, że przygotujemy sloty na: hełm, miecz, zbroję, tarczę, spodnie, buty.

Skrypt scr_items

Tworzymy nowy skrypt i definiujemy wspominany enum.
kod// defnicja miejsc, w ktore moga trafic itemy
enum slot_type {
helmet,
sword,
armor,
shield,
pants,
boots,

__size, // bedziemy uzywac tego enuma gdy trzeba policzyć ilość elementów; tutaj __size = 6
}
Wskazówka:
Ponieważ enum działa podobnie do #macro - w momencie kompilacji enumy zamieniane są na kolejne liczby rzeczywiste - w grze nie istnieje możliwość policzenia ile elementów miał enum. Wystarczy jednak dodać na końcu jakiś dodatkowy element, który po konwersji będzie podobnie jak przy tablicach oznaczał, ile jest elementów i poniżej jakiego elementu wykonywać pętle for.

Teraz stworzymy konstruktor, który będzie tworzył nam structy z definicją itemów.
kod/// @desc konstruktor dla itemów
/// @param {String} _name Nazwa
/// @param {Asset.GMSprite} _sprite Sprite
/// @param {Enum.slot_type} _slot Slot (slot_type. )
/// @param {Real} _atk Atak
/// @param {Real} _def Defensywa
function item(_name, _sprite, _slot = slot_type.sword, _atk = 0, _def = 0) constructor {
name = _name;
atk = _atk;
def = _def;
sprite = _sprite;
slot = _slot;
}
W tym przykładzie itemy będą mieć tylko dwie statystyki - atk i def, czyli atak i defensywę.

Następnie stworzymy definicję zmiennych gracza - jego ataku, defensywy i ewkipunku:
kod// statsy gracza
global.atk = 0;
global.def = 0;
// definicja itemów i ekwipunku
global.equipment = array_create(slot_type.__size, -1); // tablica o rozmiarze 6, indeksy od 0 - 5
A na koniec stworzymy tablicę, w której znajdą się structy z itemami. ID itemów będą indeksy tej tablicy.
kodglobal.items = [
new item("Hełm (srebro)", spr_helmet, slot_type.helmet, 0, 2),

new item("Miecz (brąz)", spr_sword_brown, slot_type.sword, 2, 0),
new item("Miecz (srebro)", spr_sword_silver, slot_type.sword, 5, 0),

new item("Zbroja (brąz)", spr_armor_brown, slot_type.armor, 0, 5),
new item("Zbroja (srebro)", spr_armor_silver, slot_type.armor, 0, 10),

new item("Tarcza (srebro)", spr_shield_silver, slot_type.shield, 0, 2),
new item("Tarcza (złoto)", spr_shield_gold, slot_type.shield, 0, 5),

new item("Nogawki (brąz)", spr_pants_brown, slot_type.pants, 0, 1),
new item("Nogawki (srebro)", spr_pants_silver, slot_type.pants, 0, 2),

new item("Buty (brąz)", spr_boots_silver, slot_type.boots, 0, 2),
new item("Buty (srebro)", spr_boots_silver, slot_type.boots, 0, 2),
];
Zatem Hełm ze srebra to item 0, a Buty ze srebra to 10 (bo to 11 element).

Potrzebujemy jeszcze 3 funkcji - tworzącej przedmioty na ziemi (wykorzystamy ją na starcie gry, oraz wyrzucając ekwipunek), funkcji dodawania i usuwania ekwipunku.

Tworzenie przedmiotu:
kod/// @desc Tworzy item na ziemi
/// @param {Real} _item_id id itemu w global.inventory
function create_item(_item_id) {
return instance_create_layer(irandom_range(300, 900), irandom_range(200, 500), layer, obj_item, {item_id: _item_id});
}

Dodanie itemu do ekwipunku (po kliknięciu, ale można też wymusić jakiś item w dowolnym momencie gry). Ponieważ za chwilę dodamy funkcję do usuwania przedmiotu z ekwipunku, skorzystamy z niej i jeśli coś już aktualnie jest w ekwipunku, wyrzucimy to - to raptem 2 dodatkowe linijki kodu.
Wskazówka:
Jeśli nie chcesz takiej funkcji i gracz ma ręcznie wyrzucić item zanim doda nowy, wystarczy zakomentować pierwszego ifa.
kod/// @desc Ekwpuje item
/// @param {Real} _item_id id itemu w global.inventory
/// @returns {Bool}
function item_equip(_item_id) {
var _slot = global.items[_item_id].slot; // w jaki slot władować

// usuń jeśli coś już jest
if (global.equipment[_slot] != -1) {
item_remove(_slot);
}

global.equipment[_slot] = _item_id; // ustal jaki item jest teraz w tym slocie
// zwieksz statsy
global.atk += global.items[_item_id].atk;
global.def += global.items[_item_id].def;

return true;
}
Następnie, usuwanie itemu z danego slotu ekwipunku - oczywiście o ile coś tam jest (-1 oznacza, że nic).
kod/// @desc Usuwa item z ekwipunku
/// @param {Real} _slot numer slotu w global.equipment
/// @returns {Bool} Zwraca czy usunieto item
function item_remove(_slot) {
if (global.equipment[_slot] == -1) {
return false; // juz jest pusty
}

var _item_id = global.equipment[_slot]; // sprawdź jaki item jest w tym slocie

// ZMNIEJSZ statsy
global.atk -= global.items[_item_id].atk;
global.def -= global.items[_item_id].def;

create_item(_item_id); // wyrzuc na ziemię

global.equipment[_slot] = -1; // ustaw, ze nic tu nie ma

return true;
}

Na koniec dodamy jeszcze jedną funkcję pomocniczą, która wyświetli nam opis przedmiotu obok kursora myszy:
kod/// @desc Rysuje opis itemu
/// @param {Real} _item_id id itemu w global.inventory
function item_desc_draw(_item_id) {
/// ustaw kolor i font
draw_set_color(c_white);
draw_set_font(fnt_main);
/// ustaw opis
var _desc = $"{global.items[_item_id].name}\nAtk: {global.items[_item_id].atk}, Def: {global.items[_item_id].def}";

/// rysuj, podążaj za kursorem
draw_text(mouse_x + 20, mouse_y + 10, _desc);
}

To już wszystko co dodawaliśmy w skrypcie scr_items, można go zamknąć. Razem powinno tam być 5 funkcji, 4 zmienne globalne (poza funkcjami) i enum.

Przy okazji, w funkcji rysowania zauważyliście może, że wymuszam czcionkę fnt_main. To dlatego, że domyślny GMowy font nie obsługuje polskich znaków diakrytycznych, więc aby je wyświetlić, należy stworzyć nową czcionkę fnt_main, oraz wybrać w niej:
- Add (Add new range)
- From Code, Add Range
Można zamknąć okno czcionek.

Teraz przejdziemy do programowania obiektów.

Obiekt - item
Pierwszy z obiektów jest bardzo prosty - to obiekt item. Będzie posiadał zmienną item_id, ale skorzystamy z funkcjonalności GMa która pozwala nam przekazać zmienną w trakcie tworzenia obiektu, przed eventem Create i w tym evencie sprawdzić, czy została ona ustawiona.

Tworzymy więc obj_item i otwieramy okno Variable Definitions. Tam dodajemy zmienną item_id, typu Real, o domyślnej wartości -1.

Grafika: /upload/ajax/20240612_32c38909fecf5a28ea04481e86c91faa.png

Event Create
Sprawdzamy, czy przy instance_create_layer() przekazano id_itemu i jeśli tak - ustawiamy sprite tego obiektu, jeśli nie - niszczymy go:
kodif (item_id == -1) {
instance_destroy();
}

sprite_index = global.items[item_id].sprite;

Draw
Rysujemy item, a jeśli akurat jest nad nim kursor myszy, pokazujemy opis:
koddraw_self();

if position_meeting(mouse_x, mouse_y, self) {
item_desc_draw(item_id);
}

Left Pressed
Jeśli wciśnięto lewy przycisk myszy na danym itemie - dodajmy go do ekwipunku i niszczymy item w grze:
kod/// @desc ekwipuj
item_equip(item_id);
instance_destroy();

Wyświetlanie ekwipunku
Teraz sprawa najtrudniejsza, ale i najważniejsza - wyświetlanie ekwipunku na ekranie. Część kodu będzie przypominać ten dotyczący wyświetlania inventory z przykładu który wcześniej wspomniałem. Zaczniemy jednak od tego, że nasz obiekt na start stworzy nam po 1 sztuce każdego możliwego itemu na obszarze gry.

Tworzymy obiekt obj_equipment.

Event Create
kod/// stwórz po 1 sztuce każdego itemu
var n = array_length(global.items);

for(var i = 0; i < n; i++) {
create_item(i);
}

Draw GUI
Teraz czas na rysowanie.
Ponieważ itemy mogą być wyświetlane na tle postaci, odpowiednie sloty muszą wylądować w odpowiednich miejscach.
Wykonamy pewną sztuczkę - zamiast rysować po kolei każdy z 6 slotów, narysujemy ich nieco więcej - aż 12.
Stworzymy jednak tymczasową tablicę, która będzie trzymała informację w jakim kwadracie jest jaki slot, a jeśli w danym kwadracie nie ma być nic wyświetlane, wpiszemy tam -1.
Zamiast więc wyświetlać ekwipunek na pozycjach 0 - 6, wyświetlimy go na pozycjach 1, 3, 4, 5, 7, 10.
Następnie, w pętli, odczytamy jaki "faktycznie" slot ma być w danym miejscu i na tej podstawie wyświetlimy już prawidłowy element z ekwipunku.
kod// ustal w którym kwadracie będzie widoczny item z jakiego slotu
var slot_id = [
-1, slot_type.helmet, -1,
slot_type.sword, slot_type.armor, slot_type.shield,
-1, slot_type.pants, -1,
-1, slot_type.boots, -1,
];
Teraz przygotujemy tymczasowe zmienne:
kod// przygotuj zmiene tymczasowe
var row = 0, col = 0; // rząd, kolumna komórki
var n = array_length(slot_id); // ilosc tablicy ze slotami
var xx = 0, yy = 0; // x i y aktualnej komórki
var hover = false; // czy kursor najechał

A następnie narysujemy cały ekwipunek. Jak już wspomniałem, jeśli dany slot_id = -1, pomijamy rysowanie korzystając z instrukcji continue;, a w przeciwnym wypadku wiemy, że jest to slot z global.inventory. Wciąż jednak może tam nie być żadnego itemu, dlatego trzeba będzie sprawdzić czy nie jest on równy -1 (ostatni "if").

Standardowo sprawdzimy też czy najeżdżamy kursorem - jeśli tak, to zmieniamy obwódkę prostokątu na czerwono. Jeśli jest tam jakiś item, dodatkowo klikając PRAWYM przyciskiem myszy, możemy go usunąć z tego slotu.
kod/// pętla rysująca przedmioty
for(var i = 0; i < n; i++) {

if (slot_id[i] == -1) {
// w tym slocie nie ma zadnego typu itemu, pomiń
continue;
}

// rysuj w prostokacie, max 3 sloty na linię
col = i mod 3;
row = i div 3;

// pozycja slotu
xx = 10 + col * 40; // 40, bo 32 + 8 pikseli marginesu
yy = 10 + row * 40;

// sprawdź kursor
hover = false;
draw_set_color(c_gray);

// jeśli kursor najechał
if (mouse_x >= xx and mouse_x < xx + 32) {
if (mouse_y >= yy and mouse_y < yy + 32) {
hover = true;
draw_set_color(c_red);
}
}

// rysuj prostokąt
draw_rectangle(xx, yy, xx + 32, yy + 32, true);

// sprawdź czy w tym slocie jest jakiś item
var _item_id = global.equipment[ slot_id[i] ]; // mapuj slot w tym kwadracie na ekwipunek

// jeśli jest, narysuj go
if (_item_id != -1) {
draw_sprite(global.items[ _item_id ].sprite, 0, xx, yy);

// jeśli kursor najechał, dodatkowo pokaż opis
if (hover) {
item_desc_draw(_item_id);

// jeśli kursor najechał i wciśnięto PPM, usuń item
if (mouse_check_button_pressed(mb_right)) {
item_remove(slot_id[i]);
}
}
}
}

Na koniec wyświetlimy jeszcze globalne statystyki gracza, żeby sprawdzić, czy dodawanie i usuwanie itemów zwiększa/zmniejsza atak i defensywę.
kod// statystyki gracza
draw_set_color(c_white);
draw_set_font(fnt_main);
var _stats = $"Atak: {global.atk}\nObrona: {global.def}";
draw_text(10, 180, _stats);

Uff, prawie 70 linijek za nami i... niemal wszystko gotowe.

Wystarczy teraz dodać obj_equipment do Roomu i odpalić grę.

Grafika: /upload/ajax/20240612_228f9c20c0006fc95b3cb04eb3703988.png

Gotowy projekt (GM 2024.4) pobierzesz tutaj: gmclan.org/upload/gm/gmclan_equipment.yyz .
Komentarze (łącznie 0):
Nie ma jeszcze żadnego komentarza. Czas to zmienić

Najnowsze wersje GameMakera:

Stabilna
2024.11.0.179 • 2024.11.0.227
wydana 43 dni temu
LTS
2022.0.3.85 • 2022.0.3.99
wydana 59 dni temu
Beta
2024.1300.0.732 •
2024.1300.0.743
 0.15.4

wydana  2 dni temu
= IDE, = Runtime, = GMRT
Użytkownicy online
1 użytkownik aktywny:
gości: 1,
(~ostatnie 15 minut)
Discord
47 użytkowników online na discordzie:
Kysiu, 🧁Cupcake🧁, Nikas, Alice, LeD, Carl-bot, p..., lethian, Wielki Druid, Alkapivo, Kowu, Kuzyn, GMRussell, Gameduro, OdrzuconyKrakers, fervi, 𝕳𝖚𝖌𝖔 𝕲𝖔𝖓𝖝𝖆𝖑𝖊𝖝, Kalor, r..., HappyOrange, Arrekin, Domeen0, LadyLush, Dyno, 🆅🅸🆃🅾74🅼, szmalu, Korodzik, m..., Miłosz, Marco, bagno, Tidżi, Danieo, Mtax, Kandif, g..., Huder, l..., moeglich, s..., d..., Add92, Krzysiek1250, h..., Shockah, PeekoHiko, xVANiLL
Shoutbox
Wojo (10:25, 27.12.24)
Jak tworzyłeś* ah ta niecną autokorekta (kiedyś też stworzyłem apki na androida w sumie)
Wojo (10:23, 27.12.24)
O siemka baca, czasami myślę o tobie w kontekście tego jak tworzyłem apki na androida. Swoją drogą czasami zapominam, że forum istnieje bo cały ruch teraz utrzymuje się na discordzie, ale pora to zmienić!
Uzjel (20:17, 10.12.24)
Cały ruch przeniósł się na Discorda.
MagnusArias (17:43, 01.12.24)
O matko... a ja tutaj jestem od ponad 15 lat i czasami zaglądam... biernie bo biernie, ale czasem wpadnę
gnysek (11:46, 17.11.24)
Witamy, witamy!
baca (12:22, 16.11.24)
To już 25 lat.. Witam po paru latach nieobecności.
gnysek (11:05, 15.11.24)
Natomiast obecne forum istnieje od 2004, jak z iglu.cz na gmclan.org przeszliśmy i od tego czasu nie było resetów danych.
gnysek (12:35, 13.11.24)
Ogólnie GMCLAN istnieje 22 lata, ale na to trofeum nie zrobiłem (jeszcze xD)
Chell (20:41, 08.11.24)
wow, ta emotka w ogóle nie wygląda jak : O xD
Chell (20:40, 08.11.24)
tylko? :O 4tk ma 15
Starsze wpisy znajdziesz w Archiwum.
Ankieta
Ile zarobiłeś do tej pory na grach stworzonych w GM?