Logika potworów w grze RPG

Logika potworów w grze RPG
tBane
Tester Beta
  • Rejestracja:ponad rok
  • Ostatnio:około 10 godzin
  • Lokalizacja:Poznań
  • Postów:288
0

Witam. Napisałem prostą logikę potworów w grze. Wydaje mi się, że przewidziałem każde zachowanie. Co o tym sądzicie ?

Logika ma działać następująco:
-gdy gracz jest w zasiegu ataku - potwór atakuje gracza
-gdy gracz jest w polu widzenia - potwór idzie na pozycje gracza
-gdy gracz znika z pola widzenia - potwor idzie do miejsca, w którym ostatnio widział gracza
-gdy potwór dojdzie do celu i nie ma w zasięgu ataku ani w polu widzenia gracza to nic nie robi
-gdy potwór nic nie robi to co jakiś czas szuka sobie celu do którego może się przejść

Bazowe funkcje to:
-playerInAttackRange(); // bool gracz w zasięgu ataku
-playerInViewRange(); // bool gracz w polu widzenia
-player->takeDamage(2); // zadaj "2" obrazenia graczowi
-searchPath(); // szuka drogi do celu opisanej punktami

Stany:
-state::fight; // stan atakowania gracza
-state::run; // stan dazenia do jakiegoś celu
-state::idle; // potwor stoi bezczynnie w miejscu

Dodatkowo dodałem:
-sf::vector2f pointBase; // punkt spawnu potwora
-sf::vector2f target; // cel do ktorego potwor zmierza
-std::vector <Point> path; // droga do celu opisana punktami ( powinna być opisywana przez punkty sf::Vector2f ale jeszcze tego nie zrobiłem :-/ )

Kopiuj
if (state == states::fight) {

	if (playerInAttackRange()) {
		
		if (cooldown <= 0) {
			player->takeDamage(2);
			cooldown = attackTime;
			frame = 0;
		}

		frame = cooldown / attackTime * 4.0f - 1.0f;
		sprite->setTexture(*fightTextures[direction * 4 + frame]);
		
	}
	else if (playerInViewRange()) {
		target = player->position;
		state = states::run;
	}
	else
	{
		state = states::idle;
		frame = 0;
		sprite->setTexture(*idleTextures[direction * 4 + frame]);
	}
}
else if (state == states::run) {
	
	if (playerInAttackRange()) {
		state = states::fight;
	}
	
	if (playerInViewRange()) {
		target = player->position;
	}

	calculateCurrentFrame(dt);
	float distance = 15.0f * stepSize * dt;
	move(distance);
	sprite->setTexture(*runTextures[direction * 4 + frame]);
}

else if (playerInAttackRange()) {
	state = states::fight;
}
else if (playerInViewRange()) {
	target = player->position;
	state = states::run;
}

if (state == states::idle) {
	if (rand() % 300 == 0) {

		// rangdom target
		target.x = pointBase.x + (rand() % 3 - 1) * 100;
		target.y = pointBase.y + (rand() % 3 - 1) * 100;
		state = states::run;
	}
	else {
		calculateCurrentFrame(dt);
		sprite->setTexture(*idleTextures[direction * 4 + frame]);
	}
}

W wolnych chwilach od codzienności programuję hobbystycznie Edytor gier RPG 2D.
Technologie, z których korzystam to C++ oraz SFML 2.X.
edytowany 2x, ostatnio: tBane
bagietMajster
  • Rejestracja:ponad rok
  • Ostatnio:około 15 godzin
  • Postów:434
0

Zamiast wrzucać kod opisz co ona robi.


Praktyczna implementacja TDD zaczyna się od ciebie.
tBane
myślę, że kod jest wystarczająco czytelny i nie ma sensu go jeszcze komentować
KamilAdam
  • Rejestracja:ponad 6 lat
  • Ostatnio:9 dni
  • Lokalizacja:Silesia/Marki
  • Postów:5505
4

Super. 10/10. najbardziej podoba mi się fragment if (rand() % 300 == 0). Kiedy można spodziewać się całej gry?


Mama called me disappointment, Papa called me fat
Każdego eksperta można zastąpić backendowcem który ma się douczyć po godzinach. Tak zostałem ekspertem AI, Neo4j i Nest.js . Przez mianowanie
tBane
:D Nie wiem czy uda mi się w ogóle stworzyć tę grę. Mam poważne braki w programowaniu a i można powiedzieć, że piszę ją hobbystycznie, więc nie mam pojęcia czy wyjdzie, bo parcia na to jakiegoś nie mam.
loza_prowizoryczna
Nawet jak nie stworzysz gry to zawsze można wykorzystać to co jest do testowania exploitów na dowolnym systemie operacyjnym na którym się skompiluje.
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 9 godzin
  • Lokalizacja:Tuchów
  • Postów:12165
1

Jeśli o mnie chodzi, to jest dobrze — ot prosta maszyna stanów, robi to co do niej należy. Przy większej liczbie możliwych stanów, zamiast drabinki ifów, zastanów się nad zastosowaniem instrukcji wyboru. Ale póki co jest w porządku, choć fajnie by było zobaczyć ten kod w akcji, czyli gdybyś nagrał krótkie wideo ilustrujące jego działanie.

KamilAdam napisał(a):

najbardziej podoba mi się fragment if (rand() % 300 == 0).

Mi też — wygląda jak easter egg, mocno nieprawdopodobny, odpalany w losowych momencie. 😉

Choć zmieniłbym ten warunek, dlatego że jego działanie jest zbyt nieprzewidywalne. Zamiast w każdej klatce robić rand, zadeklarowałbym osobny licznik dla stanu idle i w trakcie aktywacji tego stanu, wylosowałbym wartość licznika raz, a następnie w każdej klatce go inkrementował, a po doliczeniu do końca, aktywował nowy stan (tutaj jest to run w losowym kierunku).

Chodzi o to, aby po pierwsze zwiększyć prawdopodobieństwo wykonania tej automatycznie odpalanej akcji, a po drugie, aby móc ustalić konkretny przedział czasu jej zaistnienia. Żeby się nie kazało, że ustawia się stan idle, a już w następnej klatce ta dodatkowa akcja zostanie wykonana (bo rand akurat zwrócił pasujące ziarno). Czyli coś w tym stylu:

Kopiuj
counter = 300 + rand() % 300;

Akcja zostanie wykonana w losowym momencie, ale nie wcześniej niż po pięciu sekundach i jednocześnie nie później niż po dziesięciu sekundach. Oczywiście zakładając, że logika aktualizowana jest 60 razy na sekundę, a literały 300 dotyczą liczby klatek, a nie delty.

W ten sposób, czyli losując przedziały czasu, od zarania dziejów implementowało się specjalne akcje idle. Wystarczyło pozostawić główną postać, nie ruszać nią, a po X sekundach ta zaczynała robić jakąś głupotę — Earthworm Jim robił śmieszne rzeczy.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 2x, ostatnio: flowCRANE
Zobacz pozostałe 27 komentarzy
obscurity
Jednak większość gier tak działa, na przykład potworki mają ustalone wartości procentowe jaka szansa jest że wydropią jakiś item po śmierci, jest to całkiem losowe i trzeba mieć szczęście przy speedrunach ale jednak jakoś to działa i nie zdarzyło się żeby jakiś speedrunner nie mógł przejść gry bo item z szansą dropu 10% nie wypadł 1000 razy pod rząd. Gry przede wszystkim mają służyć zwykłym graczom którzy jednak wolą urozmaiconą rozgrywkę i lepiej gdy gra za każdym razem lekko sie zmienia i jest nieprzewidywalna, inaczej staje się nudna i gracz do niej nie wraca
obscurity
Jedyne na co trzeba uważać to żeby na przykład npc losowo nie uniemożliwił dalszej gry np wchodząc i blokując jakiś korytarz ale i to się zdarza w wielu grach i gracz musi np zresetować level bo miał pecha. Nikt za bardzo o to nie płacze. O balans trzeba dbać w grach multiplayer a nie przejmować się jak często potworek w single player ziewa
flowCRANE
bo item z szansą dropu 10% nie wypadł 1000 razy pod rząd — i właśnie dlatego tego typu mechaniki nie są „całkiem losowe”, a oparte o konkretne obliczenia, aby uzyskać założony procentaż prawdopodobieństwa i sensowny rozkład w czasie.
flowCRANE
Jedyne na co trzeba uważać to żeby na przykład npc losowo nie uniemożliwił dalszej gry np wchodząc i blokując jakiś korytarz — to nie ma nic wspólnego z balansem. To jest wyłącznie kwestia programowania AI enpeców.
flowCRANE
O balans trzeba dbać w grach multiplayer a nie przejmować się jak często potworek w single player ziewa — akurat nic bardziej mylnego. O balans trzeba dbać zawsze i w przypadku każdej mechaniki każdej gry, bo one ostatecznie wpływają na doświadczenia gracza, które jest niezwykle istotne. Pseudolosowość nie jest mechaniką, a jedynie narzędziem do jego implementacji.
Boski
  • Rejestracja:prawie 6 lat
  • Ostatnio:29 minut
  • Postów:132
1

Ewentualnie jakbyś chciał to jeszcze możesz zrobić tak, że jest klasa bazowa State, i każdy stan to osobna implementacja. Masz kilka klas, każda ładnie sobie implementuje co tam w środku robi. Wtedy też już nie trzeba if/switch, tylko wołasz, dajmy ".Update(dt)" na aktualnym stanie.

flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 9 godzin
  • Lokalizacja:Tuchów
  • Postów:12165
1

W przypadku, gdy dany obiekt może posiadać jeden z kilku stanów, implementacja maszyny stanów nie ma zbyt dużego znaczenia. Kodu jest tak mało, że można go zamknąć w jednej funkcji (jak to widać w pierwszym poście).

Przy większej liczbie stanów, switch jest lepszy, bo lepiej w nim widać poszczególne fragmenty obsługi danych stanów i łatwiej pomiędzy nimi skakać. Niektórzy preferują nawet i gigantyczne switche, na setki linijek kodu, dlatego że cała maszyna jest w jednej funkcji, łatwo się to analizuje i debuguje, ale też łatwo jest przeskakiwać pomiędzy stanami w ramach jednego wywołania funkcji aktualizującej.

Przy setkach możliwych stanów danego obiektu, taki switch miałby pewnie tysiące linijek kodu i zrozumienie jak działa, dla wielu było by po prostu zbyt trudne. Podział na osobne implementacje stanów mógłby wszystko ułatwić. Pod warunkiem oczywiście, że nie stałaby się jakąś gigantyczną, wielopoziomową abstrakcją, której zrozumienie i debugowanie byłoby koszmarem.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 4x, ostatnio: flowCRANE
Zobacz pozostałe 2 komentarze
flowCRANE
Można by takie podejście — czyli osobne implementacje stanów — zastosować nawet i w czystym kodzie proceduralnym. Ot obsługa każdego stanu to osobna funkcja, a wszystkie funkcje umieszczone w tablicy, każdy stan to osobna funkcja go aktualizująca (czyli typowy dispatch table). Wtedy też zamiast switcha będzie proste wywołanie funkcji, tyle że zamiast z poziomu obiektu (metody stanu), to bezpośrednio z tablicy.
Boski
polecam, łatwo się rozszerza o kolejne stany (kosztem kolejnych klas a nie rozrastaniem jednego pliku), można zdefiniować w bazie np. enter/exit/update, i automatycznie wołać z poziomu kodu obsługującego stany; w stanach gdzie jest potrzeba to nadpisywać te metody, a inne zostawić. W bazie też można przechować obiekt który zarządza stanami, żeby móc ze stanu przejść w stan. Dużo na internatach jest informacji bo to dosyć powszechne podejście
flowCRANE
Masz tutaj na myśli stricte gamedev i maszyny stanów dla animowanych obiektów, czy jakieś ogólne wzorce?
Boski
ogólne wzorce. korzystam z tego przykładowo i do stanu gry (stan-ładowanie-gry / stan-menu / stan-ładowanie-save / stan-rozgrywka / stan-przerwania-rozgrywki) i często jak tutaj, do jednostek/wrogów/postaci, żeby definiować ich zachowanie (idle/idzie-gdzies/bije-coś)
FA
@furious programming: pracowałem w projekcie z podejściem że stany to klasy, w moich oczach ma to 3 główne zalety. Klasa bazowa zapewnia dostęp do startowych akcji lub ew, łatwą edycje wszystkich przypadków, maszyne stanów pisze sie raz i o niej zapomina, przez to że nie trzeba odkrywać koła na nowo przy każdym przypadku, zadania łatwo dzielić i trywializują się do kategorii min/mocny junior. Ale to był specyficzny projekt gdzie maszyn stanów potrzebne były dziesiatki w POC, i setki czy tysiace w gotowym produkcie.
LukeJL
  • Rejestracja:około 11 lat
  • Ostatnio:minuta
  • Postów:8403
2
Kopiuj
player->takeDamage(2);

To jest problematyczne, że to potwór ustala, z jaką siłą gracz dostanie zniszczenie.
A co jeśli gracz będzie miał jakiś bonus, który sprawi, że będzie bardziej odporny na atak potwora? (np. będzie miał zbroję).

Myślę, że tu potrzebna jest jakaś abstrakcja.

  • Potwór atakuje z jakąś siłą
  • Gracz przyjmuje atak i może być mniej lub bardziej odporny na niego

To jakby dwa etapy. Mogłoby to być rozwiązane jeszcze w innym miejscu, zewnątrz, w jakimś systemie walki.

Kopiuj
sprite->setTexture(*fightTextures[direction * 4 + frame]);

To w ogóle wywaliłbym z tych ifów, żeby oddzielić logikę (co potwór robi, jak się zachowuje) ze szczegółami implementacji grafiki (jak to będzie wyglądać).

W szczególności tekstura wydaje się zależeć wprost od stanu, więc może napisać drugi zestaw ifów, które będą przeliczać stan na teksturę.
Albo zrobić metodę do zmiany stanu (zamiast zmieniać go wprost) i w tej metodzie mogła być ustalana tekstura.


edytowany 1x, ostatnio: LukeJL
tBane
potwór zadaje obrażenia graczowi, a gracz je przetwarza np. z odjęciem wartości pancerza. Zaś teksturę może faktycznie lepiej by było osobno przetwarzać. Zobaczę co da się zrobić z tym. Dzięki za rady :-)
RD
  • Rejestracja:około rok
  • Ostatnio:około 16 godzin
  • Postów:56
0

Nawet jeśli to działa to jest to tragedia. Ifologia stosowana. Switch to tylko pudrowanie syfu. Zachęcam do zainteresowania się skończoną maszyną stanów i jak najlepiej zaimplementować stan i tranzycie między nimi (tablica/lista stanów i przejść) oraz towarzyszące akcje. Warto zastosować wzorzec stretegii. Warto zaimplementować coś takiego w celu treningowym ale docelowo doradzał bym użyć gotowej biblioteki.

tBane
Tester Beta
  • Rejestracja:ponad rok
  • Ostatnio:około 10 godzin
  • Lokalizacja:Poznań
  • Postów:288
0

w takim razie, jak ty byś napisał całą logikę potworów ? bo nie rozumiem...


W wolnych chwilach od codzienności programuję hobbystycznie Edytor gier RPG 2D.
Technologie, z których korzystam to C++ oraz SFML 2.X.
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 9 godzin
  • Lokalizacja:Tuchów
  • Postów:12165
3

@tBane jeśli chcesz wiedzieć czym jest maszyna stanów i jak wygląda typowa jej implementacja, to musisz co nieco poczytać. Na YouTube też jest mnóstwo informacji na ten temat, łącznie z animacjami ilustrującymi grafy przejść pomiędzy stanami, więc cóż… trzeba zacząć od przyswojenia teorii. 😉

Trochę to też bez sensu jest, bo samo znaczenie maszyny stanów jest ogólne i określa jedynie tyle, że operuje na skończonej liczbie stanów i w nieskończoność przełącza się pomiędzy nimi. A to czy taki automat zostanie zaimplementowany w formie ifów, switcha, dispatch table czy w pełni obiektowo, to już kwestia drugorzędna. Nawet twój obecny kod implementuje skończoną maszynę stanów, bo spełnia wszystkie jego założenia — obsługuje skończoną liczbę stanów i bez końca przełącza się pomiędzy nimi, w ściśle określony sposób.

To w jaki sposób zaimplementować taki automat, zależy od języka, paradygmatu oraz własnych wymagań/preferencji — nie ma jednej, właściwej implementacji. Ty piszesz w C++, więc możesz do tego celu wykorzystać klasy. Tutaj masz taki w miarę przystępny artykuł, tak na początek — Implementing a Finite State Machine in C++. Zapewne jeden artykuł nie wystarczy, bo maszyny stanów nie są najprostsze w implementacji, ale zawsze można poszukać innych, jako dopełnienie.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 2x, ostatnio: flowCRANE
tBane
ok. Teraz już wiem jak złożone to zagadnienie jest, a myślałem, że to jest proste. Dziękuję za radę, postaram się zrozumieć zasadę działania maszyny stanów.
flowCRANE
Takie rzeczy są proste, tylko kiedy ktoś na forum pisze, że są proste. A kiedy przychodzi czas na implementację, to okazuje się, że to wszystko jest zawiłe i wymaga kupy kodu, żeby taka obiektowa i uniwersalna maszyna w ogóle miała prawo działać. Dlatego wcale nie jest powiedziane, że koniecznie musisz z takiej maszyny korzystać, szczególnie, jeśli stanów jest mało.
tBane
Tester Beta
  • Rejestracja:ponad rok
  • Ostatnio:około 10 godzin
  • Lokalizacja:Poznań
  • Postów:288
0

Działająca metoda bez implementacji maszyny stanu:

Kopiuj
void update(float dt) {

	switch (state) {
	case states::fight:

		if (playerInAttackRange()) {
			if (cooldown <= 0.0f) {
				player->takeDamage(2);
				cooldown = attackTime;
				frame = 0;
			}

			frame = cooldown / attackTime * 4.0f - 1.0f;
			sprite->setTexture(*fightTextures[direction * 4 + frame]);
		}
		else if (playerInViewRange()) {
			state = states::run;
			target = player->position;
		}
		else
		{
			state = states::run;
		}
		break;

	case states::run:
		if (playerInAttackRange())
			state = states::fight;
		else if (playerInViewRange()) {
			target = player->position;
			state = states::run;
			float distance = 15.0f * stepSize * dt;
			goToTarget(distance);
			calculateCurrentFrame(dt);
			sprite->setTexture(*runTextures[direction * 4 + frame]);
		}
		else if (position == target) {
			state = states::idle;
		}
		else
		{
			float distance = 15.0f * stepSize * dt;
			goToTarget(distance);
			calculateCurrentFrame(dt);
			sprite->setTexture(*runTextures[direction * 4 + frame]);
		}
		break;

	case states::idle:
		if (playerInAttackRange()) {
			state = states::fight;
		}
		else if (playerInViewRange()) {
			target = player->position;
			state = states::run;
		}
		else {

			if ( rand()%600 == 0) {	// HERE HELP ME
				target.x = pointBase.x + rand() % 300 - 150;
				target.y = pointBase.y + rand() % 300 - 150;
				state = states::run;
			}
			
		}
		calculateCurrentFrame(dt);
		sprite->setTexture(*idleTextures[direction * 4 + frame]);
		break;
	}
	

	mouseOvering();

	if (cooldown >= 0.0f)
		cooldown -= dt;

	sprite->setPosition(position);
	collider->setPosition(position);
	viewRangeArea->setPosition(position);
	attackRangeArea->setPosition(position);

	textname->setPosition(position.x, position.y - height - 30);
	panelHP->setPosition(position.x, position.y - height - 10);
	panelHPcurrent->setPosition(position.x, position.y - height - 10);
	
}

W wolnych chwilach od codzienności programuję hobbystycznie Edytor gier RPG 2D.
Technologie, z których korzystam to C++ oraz SFML 2.X.
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Laska, z Polski
  • Postów:10056
2

Po prostu uruchom grę i graj w nią , i zobacz czy się fajnie gra.

tBane
Tester Beta
  • Rejestracja:ponad rok
  • Ostatnio:około 10 godzin
  • Lokalizacja:Poznań
  • Postów:288
0

tak właśnie robię :-) ale czasem nie mam pomysłu co dodać lub jak coś zmienić i wtedy wrzucam post. W tym przypadku nie jestem pewien czy akurat logika potworków jest dobrze napisana a nie jestem w tym ekspertem więc zbieram rady.


W wolnych chwilach od codzienności programuję hobbystycznie Edytor gier RPG 2D.
Technologie, z których korzystam to C++ oraz SFML 2.X.
KS
  • Rejestracja:prawie 4 lata
  • Ostatnio:około 6 godzin
  • Postów:581
0

Potwór powinien się szwendać jak mu gracz zniknął

Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:2 minuty
  • Lokalizacja:Laska, z Polski
  • Postów:10056
1
tBane napisał(a):

tak właśnie robię :-) ale czasem nie mam pomysłu co dodać lub jak coś zmienić i wtedy wrzucam post. W tym przypadku nie jestem pewien czy akurat logika potworków jest dobrze napisana a nie jestem w tym ekspertem więc zbieram rady.

Nie myśl o tym czy jest "dobrze napisana", tylko czy się dobrze gra w grę - przecież o to chodzi w grach, nie?

flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 9 godzin
  • Lokalizacja:Tuchów
  • Postów:12165
1

Jeśli nie masz pomysłu, a bardzo chcesz (ot w ramach ćwiczenia) rozbudować AI, to zawsze możesz kuknąć na inne gry i sprawdzić jak zachowują się różne postacie, w tym potworki. Póki robisz ten projekt głównie dla siebie, to możesz nawet i klona innej gry stworzyć.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
YA
  • Rejestracja:prawie 4 lata
  • Ostatnio:2 dni
  • Postów:252
2
tBane napisał(a):

-gdy potwór nic nie robi to co jakiś czas szuka sobie celu do którego może się przejść

Mam pytanie odnośnie założeń, może ja jestem odskulowy trochę, ale czy w grach RPG nie jest zazwyczaj tak, że potwory stoją mniej więcej w z góry wyznaczonych miejscach? Jak ma wyglądać design gry, gdzie potworki mogą sobie losowo chodzić po całej mapie? Co, jeżeli mamy pecha i na całej mapie jest pusto, a wszystkie stworki znalazły się w mniej więcej jednym miejscu, więc jak gracz tam pójdzie, to się wszystkie razem na niego rzucą i go zniszczą?

roark.dev napisał(a):

Nawet jeśli to działa to jest to tragedia. Ifologia stosowana. Switch to tylko pudrowanie syfu. Zachęcam do zainteresowania się skończoną maszyną stanów i jak najlepiej zaimplementować stan i tranzycie między nimi (tablica/lista stanów i przejść) oraz towarzyszące akcje. Warto zastosować wzorzec stretegii. Warto zaimplementować coś takiego w celu treningowym ale docelowo doradzał bym użyć gotowej biblioteki.

iks de

tBane
Tester Beta
  • Rejestracja:ponad rok
  • Ostatnio:około 10 godzin
  • Lokalizacja:Poznań
  • Postów:288
1
Kopiuj
target.x = pointBase.x + (rand() % 3 - 1) * 100;
target.y = pointBase.y + (rand() % 3 - 1) * 100;

Potwory poruszają się w ograniczonym polu tzn. wokół punktu pointBase


W wolnych chwilach od codzienności programuję hobbystycznie Edytor gier RPG 2D.
Technologie, z których korzystam to C++ oraz SFML 2.X.
YA
  • Rejestracja:prawie 4 lata
  • Ostatnio:2 dni
  • Postów:252
0

Racja, zagapiłem się, przepraszam za zawracanie głowy.

tBane
Tester Beta
  • Rejestracja:ponad rok
  • Ostatnio:około 10 godzin
  • Lokalizacja:Poznań
  • Postów:288
0

nie ma problemu :-)


W wolnych chwilach od codzienności programuję hobbystycznie Edytor gier RPG 2D.
Technologie, z których korzystam to C++ oraz SFML 2.X.
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 9 godzin
  • Lokalizacja:Tuchów
  • Postów:12165
1

@tBane: teraz zauważyłem, że w tym snippecie masz linijkę z komentarzem:

Kopiuj
if ( rand()%600 == 0) {	// HERE HELP ME

Potrzebujesz jakiejś pomocy? Chodzi o ten licznik, który zaproponowałem wcześniej?


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 1x, ostatnio: flowCRANE
tBane
Tester Beta
  • Rejestracja:ponad rok
  • Ostatnio:około 10 godzin
  • Lokalizacja:Poznań
  • Postów:288
1

Nie, pomocy już nie potrzebuję. Wymyśliłem coś takiego i działa :-)

Kopiuj
if (cooldown <= 0) {
	cooldown = 3 + rand()%5;
	target.x = pointBase.x + rand() % 300 - 150;
	target.y = pointBase.y + rand() % 300 - 150;
	state = states::run;
}

W wolnych chwilach od codzienności programuję hobbystycznie Edytor gier RPG 2D.
Technologie, z których korzystam to C++ oraz SFML 2.X.
edytowany 3x, ostatnio: tBane
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)