Troman - tworzenie gier
Logo serwisu Troman.pl
Panel Logowania

Godot podstawy GDscripta vol.2

napisał : tyser
09
września
2018
Druga część tutoriala o gdscripcie:programowanie obiektowe, dodawanie/usuwanie scen w trakcie działania gry, przemieszczanie obiektów, zapis zmiennych do pliku
tagi :

Programowanie obiektowe - polega na tworzeniu klas, które zawierają zmienne i funkcje. Jets to bardzo przydatne gdy trzeba użyć wielu obiektów o tych samych właściwościach np. gdybyśmy tworzyli grę w szachy to jako klasę można by zdefiniować pionki z których każdy będzie posiadał swoją pozycję na planszy i informację o tym jakim jest rodzajem pionka. Poniżej przykład zdefiniowania klasy z jedną zmienną i funkcją:

extends Node

var new_class #zmienna która zostanie klasą

class new_class1: #zdefiniowanie klasy
	var a = 10

	func add(a,b):
		return a + b

func _ready():
	print(new_class.a) #wywołanie wartości z klasy(klasa po zainicjowaniu może być użyta w dowolnej części kodu)
	new_class.a = "abc" #zmiana wartości zmiennej w klasie
	print(new_class.a)

func _init(): #funkcja wbudowana służąca do inicjowania klas
	new_class = new_class.new() #zainicjowanie klasy(klasa musi zostać przypisana do jakiejś zmiennej, nie ma ograniczenia do ilu zmiennych można przypisać klasę)

func _process(delta):
	print(new_class.add(new_class.a,15)) #wywołanie funkcji z klasy z jedna wartością z klasy


funkcje wbudowane - to funkcje wbudowane w silnik np. _ready(),_process(delta) i _init(). Tutaj opiszę tylko _ready() i _process() resztę w tutorialach tematycznych(tak jak _init() przy programowaniu obiektowym)

_ready():

wszystko co w tej funkcji wykonuje się tylko raz kiedy nod jest dodawany do sceny co najczęściej dzieje się albo przy odpalaniu gry albo przy zmienianie czegoś ręcznie w czasie działania gry.

_process(delta)

wszystko co w tej funkcji wykonuje się co klatę obrazu(automatycznie jest chyba ustawione na 60 fps). Tutaj wstawia się rzeczy typu poruszanie się,różne sensory itp. ogólnie wszystko co musi być sprawdzane i wykonywane cały czas. Ps. nie można zapomnieć o "delta" w nawiasie bo bez tego uruchamia się ale nie działa(nie wykonuje się nic w funkcji _process())

teraz trochę zabawy ze scenami(usuwanie,dodawanie,zamiana):

get_tree().change_scene("res://folder/scena.tscn") 

zmienia scenę do której jest podłączony skrypt na scenę której ścieżka jest w nawiasie

podawanie ścieżki do pliku jest bardzo proste: "res://" to główny folder projektu jeśli chcemy dostać się do któregoś podkatalogu wpisujemy jego nazwę np. "res://sceny" następnie jeśli chcemy dostać się do konkretnego pliku lub podfolderu dodajemy jego nazwę po nawiasie np. "res://sceny/budynki/karton_zula.tscn"

usuwanie noda:

level = get_node("nazwa_noda") #pobranie noda do zmiennej, pobrać można tylko noda pod nodem do którego podłączony jest skrypt w przeciwnym razie trzeba podać ścieżkę nodów od głównego do tego który chcemy pobrać
remove_child(level) #usunięcie noda
level.call_deferred("free") #nie jestem pewny co to robi podejrzewam że remove_child() usuwa tylko zawartość a w drzewku nadal zostaje nazwa noda a call_deferred("free") ją usuwa

 

dodawanie noda:

level = load("res://scenes/Sprite2.tscn") #załadowanie sceny z dysku
level = level.instance() #przygotowanie sceny do dodania do drzewka
add_child(level) #dodanie sceny do drzewka jako nod, pod nodem z podłączonym skryptem


zpisywanie i odczytywanie z pliku(najczęściej używa się tego do zapisywania stanu gry lub ustawień):

zapis:

a = { #zapisać można dowolną zmienną ale najłatwiej jest ze słownikami ponieważ można je odczytać od razu w formie słownika a nie jednej długiej lini tekstu
    "a" : 1,
    "b" : 2,
    "c" : 3
}

file = File.new() #stworzenie nowej klasy pliku
if file.open("res://txt/test.sav", File.WRITE) != 0: #stworzenie pliku i otwarcie w trybie do zapisy if sprawdza czy zadziałało jeśli nie to wypisuje "error" można to też zrobić bez sprawdzenia ale wtedy jeśli coś jest nie tak to nic o tym nie wiemy i jest smutno,apokalipsa i w ogóle(na polski: będziesz dwie godziny szukać błędu w kodzie zanim się skapniesz że folder z zapisem wymaga uprawnień admina czy coś takiego)
    print("error")
file.store_line(to_json(a)) #zapis do pliku, u życie na słowniku funkcji to_json() pozwala na późniejszy odczyt jako słownik
file.close() #po skończeniu pracy z plikiem należy go zamknąć


odczyt:

file = File.new() #przy odczytywaniu także trzeba stworzyć nową klasę(ale nie musi być w nowej zmiennej)
if file.open("res://txt/test.sav", File.READ) != 0: #otwarcie w trybie do odczytu ze sprawdzeniem błędów
    print("error")
a = parse_json(file.get_line()) #odczytanie z powrotem do słownika


wejście:

Część funkcji zwraca tylko wartości true lub false  i można trzeba ich używać jako warunków w if/elif/while te które działają inaczej są osobno poniżej. Sprawdzić już utworzone akcje(obsługiwane przyciski) lub dodać nowe możemy w ustawieniach projektu w zakładce "input map"

Input.is_action_pressed("nazwa_akcji")

kiedy przycisk akcji jest wciśnięty zwraca wartość true

Input.is_action_just_pressed("nazwa_akcji")

zwraca true tylko w klatce w której przycisk został wciśnięty

Input.is_action_just_released("nazwa_akcji")

zwraca true tylko w klatce w której przycisk został puszczony

reszta:

Input.action_press("nazwa_akcji")

symuluje wciśnięcie danego przycisku

Input.action_release("nazwa_akcji")

jeśli podany przycisk jest wciśnięty zostaje zwolniony

zmienna = get_viewport().get_mouse_position()

pobiera pozycję myszki po dodaniu na końcu .x lub .y zwraca pozycję w danej osi

get_viewport().warp_mouse(Vector2(współrzędne_x,współrzędne_y))

ściąga kursor w dany punkt na ekranie

przemieszczanie obiektów w godocie:

2D:
osie globalne:

move_and_slide(Vector2(x,y))

przemieszcza obiekt w danym kierunku z podaną prędkością np.move_and_slide(Vector2(20,0)) będzie przemieszczać obiekt w prawo(bo zwiększamy wartość x) z prędkością 20/1fps(20 jednostek odległości na jedną klatkę obrazu). Ta funkcja działa w KinematicBody2D nie działała w żadnym innym testowanym nodzie

translate(Vector2(x,y))/global_translate(Vector2(x,y))

od move_and_slide() różni się tym że działa chyba we wszystkich nodach(działało z każdym na jakim to testowałem) i poruszają obiektami szybciej przy tych samych parametrach. Oprócz tego translate() powinno(z tego co wyczytałem) poruszać obiektem w osiach lokalnych ale jakoś to nie działa może to być jakiś błąd lub niedopatrzenie twórców

obracanie obiektu:

rotation += kierunek * szybkość * ogranicznik

rotation to zmienna dostępna dla każdego obiektu w godocie(była w każdym testowanym) od liczby która w niej jest zależy o ile jest obrócony obiekt liczby w działaniu: kierunek odpowiada za to w którą stronę będzie się obracać obiekt jeśli jest tam 1 będzie się obracać w prawo jeśli -1 to w lewo, szybkość tutaj podaje się szybkość z jaką obiekt ma się obracać, ogranicznik tego elementu może nie być ale wygodniej jest żeby był służy do spowolnienia obrotów żeby prędkość można było podawać w normalnych liczbach a nie w ułamkach ja zazwyczaj używam tu 0.07 ale to już jak komu wygodniej jeśli ktoś chce to może użyć tylko dwóch liczb i prędkość podawać w ułamkach

osie lokalne:

Vector2(x,y).rotated(rotation)

najprościej mówiąc zmienia poruszanie w osiach globalnych na poruszanie w osiach lokalnych np. translate(Vector2(20,0).rotated(rotation)) przy tych parametrach obiekt powinien lecieć w prawo i tak też się będzie zachowywał różnica jest taka że jeśli obrócimy teraz ten obiekt to nie będzie nadal lecieć w prawo tylko w kierunku który teraz wyznacza jego prawa ściana

przykładowy system poruszania obiektem po osiach lokalnych(niezbyt rozbudowany ale jako prezentacja wystarczy):

extends KinematicBody2D #robiłem to w kinematicbody ale powinno działać przy każdym obiekcie

export var speed = 5 #prędkość poruszanie i prędkość obrotu
export var rotation_speed = 1.5 #export przed zmienną powoduje że jej wartość można zmieniać w opcjach obiektu

var velocity = Vector2() #wartości startowe dla kierunku poruszania i obrotu
var rotation_dir = 0

func _process(delta):
    rotation_dir = 0 #czyszczenie wartości obrotu i poruszania(bez tego nie zatrzymywały by się po puszczeniu przycisku)
    velocity = Vector2()
    if Input.is_action_pressed("ui_right"): #ustawia kierunek obrotu na prawo
        rotation_dir += 1
    if Input.is_action_pressed("ui_left"): #ustawia kierunek obrotu na lewo
        rotation_dir -= 1
    if Input.is_action_pressed("ui_down"): #ustawia kierunek poruszania na w tył
        velocity = Vector2(-speed, 0).rotated(rotation)
    if Input.is_action_pressed("ui_up"): #ustawia kierunek poruszania na w przód
        velocity = Vector2(speed, 0).rotated(rotation)

    rotation += rotation_dir * rotation_speed * 0.07 #obraca obiekt(jeśli wciśnie się odpowiedni przycisk)
    translate(velocity) #zmienia pozycję obiektu

3D:
osie globalne:

global_translate(Vector3(x,y,z))

to dokładnie ta sama funkcja co przy 2D jedyna różnica to parametry w środku

move_and_slide(Vector3(x,y,z),Vector3(x,y,z))

działa tak samo jak w 2D parametry poruszania wpisuje się tylko w pierwszy Vector3() w drugim czego by się nie wpisywało i tak nic to nie daje

osie lokalne:

translate(Vector3(x,y,z))

w 3D ta funkcja rzeczywiście przemieszcza obiekt w osiach lokalnych a nie globalnych

obracanie obiektu:

rotate(Vector3(0,-1,0),1.5 * 0.07)

działa to podobnie do obracania w 2D w Vector3() ustalamy oś i kierunek obrotu(liczba dodatnia=w prawo, liczba ujemna=w lewo) drugi argument to prędkość obrotu mnożenie tego przez 0.07 nie jest obowiązkowe to tak jak w 2D tylko spowalniacz

rotation += kierunek * szybkość * ogranicznik

działa to tak samo jak w 2D tylko w kierunek trzeba zmienić wspułrzędne na Vector3()

przykładowe sterowanie w 3D(tak jak w 2D jest bardzo proste i powinno działać ze wszystkimi nodami):

extends RigidBody


export var speed = 0.1 #prędkość obiektu i prędkość obrotu
export var rot_speed = 1.5

var velocity = Vector3() #wartości startowe dla poruszania i kierunku obrotu
var rot_dir = Vector3()

func _process(delta):
    velocity = Vector3() #czyszczenie wartości ruchu i kierunku obrotu
    rot_dir = Vector3()
    if Input.is_action_pressed("ui_up"):
        velocity = Vector3(0,0,-speed)
    if Input.is_action_pressed("ui_down"):
        velocity = Vector3(0,0,speed)
    if Input.is_action_pressed("ui_right"):
        rot_dir = Vector3(0,-1,0)
    if Input.is_action_pressed("ui_left"):
        rot_dir = Vector3(0,1,0)
    
    rotation += rot_dir * rot_speed * 0.07
    #rotate(rot_dir, rot_speed * 0.07) to nie chciało działać, czego bym nie robił zawsze wywalało obiekt w kosmos, gdyby ktoś wiedział czemu będę wdzięczny za informację
    translate(velocity)


to na tyle w tym tutorialu następny nie wiem o czym będzie może zajmę się w nim animacjami? a może dokończę artykuł o visualscripcie? nie wiem.coś wymyślę.

Brak komentarzy
Dodaj komentarz
Aby dodać komentarz do newsa, musisz być zalogowany w Serwisie.. Zaloguj