Skocz do zawartości

Problem ze ścieżkami


Kuzyn
Przejdź do rozwiązania Rozwiązane przez gnysek,

Rekomendowane odpowiedzi

Cześć, przychodzę z nieco bardziej skomplikowanym problemem, ale już nie mam w głowie żadnych innych pomysłów jak go rozwiązać. Postaram się jak najdokładniej opisać co jest nie tak.

 

Co jest nie tak?

Moje obiekty objCitizen nie przechodzą ścieżki od punktu A do punktu B w drugiej fazie swojej pracy, co powoduje, że kod z warunkiem (path_position == 1) wykonuje się natychmiastowo.

 

A jak powinno być?

Obiekt objBuilding na początku szuka wolnego objCitizen, który mógłby dla niego pracować. Jeśli go znajdzie, przydziela go do siebie.

Następnie tenże objBuilding sprawdza czy ma wolne miejsce na składzie, jeśli tak to z puli przypisanych pracowników (na ten moment maks. 1) wybiera sobie pierwszego wolnego, wynajduje mu pracę i wytycza mu ścieżkę do tej pracy.

objCitizen w tym momencie rusza do miejsca pracy i wykonuje swoją pracę, po czym zgłasza, że ją wykonał.

Wtedy objBuilding woła go z powrotem do siebie, żeby wrócił z tym wynikiem pracy. (to nie działa)

objCitizen wraca, oddaje na skład wynik swojej pracy i jest gotowy na podjęcie nowej, wyznaczonej przez objBuilding.

 

Czy wcześniej to działało?

Tak i nawet dzieliłem się tym na discordzie GMClanu https://streamable.com/qx78pz

Natomiast w momencie gdy dodałem drugi rodzaj budynku i nieco pozmieniałem kod, całość się wysypała. Opierając się na przykładzie z wideo, objCitizen zamiast odnosić drewno do składu, zbiera po prostu jedno po drugim, oddając je na odległość do składu i staje w miejscu.

 

Co do tej pory sprawdziłem i co mogę jeszcze zrobić?

Opcją ostateczną zostaje dla mnie cofnięcie się do poprzedniej wersji projektu z githuba, chciałbym rozwiązać ten problem, a nie cofać wszystko.

Sprawdzałem czy objCitizen ma poprawnie wyznaczony cel, próbowałem podmieniać różne warunki, sprawdzałem czy parametry ścieżki w postaci celu są poprawne i tak, wszystko się zgadzało.

 

 

Zanim wkleję kod, pokrótce rozpiszę zmienne, które mają kontrolować zachowanie objCitizen objBuilding i jak po kolei powinny się zmieniać.

SpN6CuG.png

 

 

objBuilding w Step Evencie ma switcha, który zmienia mu stany. Stan odpowiedzialny za wyznaczanie pracy ma taki kod:

function buildingStateWork(){

if (building.buildingTaskAssigned == false) {
	switch (building.buildingType) {
		
		case "Utility":
		#region UTILITY
		switch (building.buildingUtilityType) {
			#region WAREHOUSE
			// Check if building is Warehouse.
			case "Warehouse":
				//Check if the building got any space left.
				if (building.buildingStorageFull == false) {
					//If building is full, do not perform this code for performance.
					for (var i = 0; i + 1 < array_length(building.buildingStorage); i += 2) {
						if (building.buildingStorage[@ i][1] < building.buildingStorage[@ i + 1][1]) {
							//There is space for something. For this example, simplify it to wood only.
							//Look for somebody to bring items.
							for (var i = 0; i < array_length(building.buildingWorkerList); i++) {
								//Check if the workers are already occupied doing something or not.
								if (building.buildingWorkerList[i].citizen.citizenOccupied == false and building.buildingWorkerList[i].citizen.citizenBackFromTask == false) {
									//Check for items to be picked up in range.
									var AvailableItems	= ds_list_create();					//Store there items that will be found in range.
									var CurrentWorker = building.buildingWorkerList[i];		//Store id of the worker.
									collision_circle_list(x, y, building.buildingWorkRange, objectItem, false, true, AvailableItems, true); //Find items.
								
									for (var i = 0; i < ds_list_size(AvailableItems); i++) {
										if (AvailableItems[| i].item.itemName == "Wood log") {
											CurrentWorker.citizen.citizenOccupied	= true					;
											CurrentWorker.citizen.citizenGoToTask	= true					;
											CurrentWorker.citizen.citizenTarget		= AvailableItems[| i].id;
											CurrentWorker.citizen.citizenTargetX	= AvailableItems[| i].x	;
											CurrentWorker.citizen.citizenTargetY	= AvailableItems[| i].y	;
											CurrentWorker.pathStarted				= false					;
											with (CurrentWorker) {
												citizenPath = path_add();	
											}
											CurrentWorker.citizen.citizenState		= CITIZEN_STATES.MOVE	;
											building.buildingTaskAssigned			= true					;
											break;
										} else {
											break;
										}
									}
								}
							}
						} else {
							//There is no space, so there is no need to look out for resources to be picked up/send worker.
							building.buildingStorageFull = true;	//This needs to be set to false once somebody picks up something from there.
							break;
						}
					}
				}
			break; //WAREHOUSE
			#endregion
		}
		#endregion
		break;
		
		case "Production":
		#region PRODUCTION
		//Check if the building got any space left.
		if (building.buildingOutputAmount < building.buildingOutputAmountMax) {
			//Look for somebody to cut the tree.
			for (var i = 0; i < array_length(building.buildingWorkerList); i++) {
				//Check if the workers are already occupied doing something or not.
				if (building.buildingWorkerList[i].citizen.citizenOccupied == false and building.buildingWorkerList[i].citizen.citizenBackFromTask == false) {
					//Check for trees to be cut down.
					var AvailableResources	= ds_list_create();					//Store there trees that will be found in range.
					var CurrentWorker		= building.buildingWorkerList[i];	//Store id of the worker.
					collision_circle_list(x, y, building.buildingWorkRange, building.buildingProductionTarget, false, true, AvailableResources, true); //Find trees.
								
					for (var i = 0; i < ds_list_size(AvailableResources); i++) {
						if (AvailableResources[| i].tree.treeGrowthStage == 2) {
							CurrentWorker.citizen.citizenVocation	= "Woodcutter"															;
							CurrentWorker.citizen.citizenOccupied	= true																	;
							CurrentWorker.citizen.citizenGoToTask	= true																	;
							CurrentWorker.citizen.citizenTarget		= AvailableResources[| i].id											;
							CurrentWorker.citizen.citizenTargetX	= AvailableResources[| i].x												;
							CurrentWorker.citizen.citizenTargetY	= AvailableResources[| i].y	+ AvailableResources[| i].tree.treeYOffset	;
							CurrentWorker.pathStarted				= false																	;
							with (CurrentWorker) {
								citizenPath = path_add();	
							}
							CurrentWorker.citizen.citizenState		= CITIZEN_STATES.MOVE	;
							building.buildingTaskAssigned			= true					;
							break;
						}
					}
				}
			}
		} else {
			//There is no space, so there is no need to look out for resources to be picked up/send worker.
			building.buildingState = BUILDING_STATES.HOLD;	//This needs to be set to false once somebody picks up something from there.
		}
		#endregion
		break;
	}

} else {
	switch (building.buildingType) {
		case "Utility":
		#region UTILITY
		switch (building.buildingUtilityType) {
			#region WAREHOUSE
			// Check if building is Warehouse.
			case "Warehouse":
				for (var i = 0; i < array_length(building.buildingWorkerList); i++) {
					var CurrentWorker = building.buildingWorkerList[i];
					if (CurrentWorker.citizen.citizenOccupied == false and CurrentWorker.citizen.citizenBackFromTask == true) {
						CurrentWorker.citizen.citizenOccupied		= true							;
						CurrentWorker.citizen.citizenTarget			= id							;
						CurrentWorker.citizen.citizenTargetX		= x								;
						CurrentWorker.citizen.citizenTargetY		= y+building.buildingYOffset	;
						CurrentWorker.pathStarted					= false							;
						with (CurrentWorker) {
							citizenPath = path_add();	
						}
						CurrentWorker.citizen.citizenState		= CITIZEN_STATES.MOVE	;
						break;
					}
				}
			break;
			#endregion
		}
		#endregion
		break;
		
		case "Production":
		#region PRODUCTION
			for (var i = 0; i < array_length(building.buildingWorkerList); i++) {
				var CurrentWorker = building.buildingWorkerList[i];
				if (CurrentWorker.citizen.citizenOccupied == false and CurrentWorker.citizen.citizenBackFromTask == true) {
					CurrentWorker.citizen.citizenOccupied		= true							;
					CurrentWorker.citizen.citizenTarget			= id							;
					CurrentWorker.citizen.citizenTargetX		= x								;
					CurrentWorker.citizen.citizenTargetY		= y+building.buildingYOffset	;
					CurrentWorker.pathStarted					= false							;
					with (CurrentWorker) {
						citizenPath = path_add();	
					}
					CurrentWorker.citizen.citizenState		= CITIZEN_STATES.MOVE	;
					break;
				}
			}
		#endregion
		break;
	} //Switch
} //Else
} //Function

 

objCitizen działa na podobnej zasadzie, w Step Evencie jest switch. Kod odpowiedzialny za pracę poniżej:

function citizenStateWork(){
	switch (citizen.citizenVocation) {
		case "Mover":
		
		//Citizen is moving to the resource.
		if (citizen.citizenGoToTask == true) {
			if collision_circle(x, y, citizen.citizenPickupRange, citizen.citizenTarget, false, true) {
					citizen.citizenGoToTask		= false						;
					citizen.citizenBackFromTask = true						;
					citizen.citizenOccupied		= false						;
					citizen.citizenCarrying		= true						;
					citizen.citizenCargo		= citizen.citizenTarget.item;
					instance_destroy(citizen.citizenTarget);
			} //Add else if someone picks resource before Citizen, reset back to go to the building.
		}
		
		if (citizen.citizenCarrying == true and citizen.citizenBackFromTask == false) {
			citizen.citizenOccupied	= false;
			citizen.citizenCarrying	= false;
			var CarriedItem = citizen.citizenCargo.itemName;
			citizen.citizenCargo	= 0;
			
			//Find carried item in a storage and add to the pool.
			for (var i = 0; i < array_length(citizen.citizenAssigned.building.buildingStorage); i++) {
				if (CarriedItem == citizen.citizenAssigned.building.buildingStorage[i][0]) {
					citizen.citizenAssigned.building.buildingStorage[i][1] += 1;
				}
			}
			//Let building find another task.
			citizen.citizenAssigned.building.buildingTaskAssigned = false; 
		}
		break;
		
		case "Woodcutter":
			
		//Citizen is moving to the building back with the resource.
		if (citizen.citizenGoToTask == true) {
			if collision_circle(x, y, citizen.citizenPickupRange, citizen.citizenTarget, false, true) {
				//Cut the tree here.f
				if (workTimerOn == false and citizen.citizenTarget.tree.treeHealth > 0) {
					workTimerOn = true;
					citizen.citizenTarget.tree.treeHealth = citizen.citizenTarget.tree.treeHealth - 1;
					time_source_start(workTimer);
				}
				
				//Remove the tree and add resource to worker.
				if (citizen.citizenTarget.tree.treeHealth <= 0) {
					citizen.citizenOccupied		= false						;	
					citizen.citizenGoToTask		= false						;
					citizen.citizenBackFromTask = true						;
					citizen.citizenCarrying		= true						;
					var outputToPick = instance_create_layer(citizen.citizenTargetX, citizen.citizenTargetY, "instanceSystem", objectItem);
					with (outputToPick) {
						item 			= new WoodLog()		;
						image_speed		= 0					;
						sprite_index	= item.itemSprite	;
						image_index		= item.itemSpriteI	;
					}
					instance_destroy(citizen.citizenTarget);
					citizen.citizenCargo		= outputToPick.item;
					instance_destroy(outputToPick);
				}
			} //Add else if someone picks tree before Citizen, reset back to go to the building.
		}
		
		if (citizen.citizenCarrying == true and citizen.citizenBackFromTask == false) {
			citizen.citizenOccupied	= false;
			citizen.citizenCarrying	= false;
			var CarriedItem = citizen.citizenCargo.itemName;
			citizen.citizenCargo	= 0;
			
			//Add carried item to the pool, check before if right item is coming in.
			if (CarriedItem == citizen.citizenAssigned.building.buildingOutput) {
					citizen.citizenAssigned.building.buildingOutputAmount += 1;
			}
			//Let building find another task.
			citizen.citizenAssigned.building.buildingTaskAssigned = false; 
		}
		break;
	}
}

 

A na końcu kod odpowiedzialny za przemieszczenia objCitizen:

function citizenStateMove(){
	if (pathStarted == false) {
		if mp_grid_path(
		systemPathfinding.pathfindingCitizenGrid,
		citizenPath, x, y,
		citizen.citizenTargetX, citizen.citizenTargetY,	1) {
			path_start(citizenPath, citizen.citizenSpeed, path_action_stop, true);
			pathStarted = true;
		}
		
	}
	
	if (path_position == 1) {
		if (citizen.citizenOccupied == true and citizen.citizenBackFromTask == true) {
			citizen.citizenBackFromTask = false;
		}
		path_delete(citizenPath);
		pathStarted					= false					;
		citizen.citizenState		= CITIZEN_STATES.WORK	;
	}
}

 

Odnośnik do komentarza
Udostępnij na innych stronach

  • Administratorzy
  • Rozwiązanie

Po sprawdzeniu tego prywatnie, dopiszę w razie czego, dla potomności:

 

Ze względu na to co znajduje się w mp_grid, ścieżka może się czasem nie wygenerować, w związku z tym, w poniższym kodzie

 

function citizenStateMove(){
	if (pathStarted == false) {
		if mp_grid_path(...

 

może zostać zwrócony false w ostatnim ifie, a wtedy path_position nadal będzie wynosić 1, bo obiekt nadal widzi ostatni path, na końcu którego stoi, dlatego warunek był od razu spełnianiany i gra zachowywała się tak, jakbyśmy wrócili do budynku gdzie zostawia się zasoby (tak naprawdę robiąc 0 pikseli), następnie szukając ścieżki do następnego zasobu.

 

Nawet jeśli wydaje się to mało prawdopodobne, czasem warto dodać else i obsłużyć teoretycznie nieistniejący wyjątek. Potem łatwiej go znaleźć :)

Odnośnik do komentarza
Udostępnij na innych stronach

28 minut temu, gnysek napisał(a):

Po sprawdzeniu tego prywatnie, dopiszę w razie czego, dla potomności:

 

Ze względu na to co znajduje się w mp_grid, ścieżka może się czasem nie wygenerować, w związku z tym, w poniższym kodzie

 

function citizenStateMove(){
	if (pathStarted == false) {
		if mp_grid_path(...

 

może zostać zwrócony false w ostatnim ifie, a wtedy path_position nadal będzie wynosić 1, bo obiekt nadal widzi ostatni path, na końcu którego stoi, dlatego warunek był od razu spełnianiany i gra zachowywała się tak, jakbyśmy wrócili do budynku gdzie zostawia się zasoby (tak naprawdę robiąc 0 pikseli), następnie szukając ścieżki do następnego zasobu.

 

Nawet jeśli wydaje się to mało prawdopodobne, czasem warto dodać else i obsłużyć teoretycznie nieistniejący wyjątek. Potem łatwiej go znaleźć :)

Uprzedziłeś mnie z odpowiedzią :D 

Dzięki za pomoc i dodam jeszcze, czemu objCitizen nie mógł znaleźć ścieżki. Ano dlatego, że źle ustawione maski budynków zajmowały zbyt dużo komórek w gridzie i cel wskazany dla objCitizen był w niedostępnej komórce. Także uczulam na sprawdzanie tego również!

Odnośnik do komentarza
Udostępnij na innych stronach

Dodatkowo polecam rysować ścieżki chociażby dla debugowania + rysować mp_grid_draw, żeby zobaczyć czy siatka odpowiednio się wygenerowała.

I na koniec jeszcze taki tip, jak nie chcemy żeby ścieżka była taka "kwadratowa" można użyć np tego: https://marketplace.yoyogames.com/assets/1687/optimized-path

Odnośnik do komentarza
Udostępnij na innych stronach

  • Administratorzy
12 godzin temu, Uzjel napisał(a):

polecam rysować ścieżki chociażby dla debugowania

 

to akurat robiliśmy - ale o mp_grid_draw zapomniałem, dzięki za tip!

 

Jakby ktoś chciał zobaczyć moją wersję powyższego, zrobioną w 40-60 minut, to jest tutaj: https://gmclan.org/uploader/plik/21277,worker_placement

Odnośnik do komentarza
Udostępnij na innych stronach

Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto

Jedynie zarejestrowani użytkownicy mogą komentować zawartość tej strony.

Zarejestruj nowe konto

Załóż nowe konto. To bardzo proste!

Zarejestruj się

Zaloguj się

Posiadasz już konto? Zaloguj się poniżej.

Zaloguj się
  • Ostatnio przeglądający   0 użytkowników

    • Brak zarejestrowanych użytkowników przeglądających tę stronę.
×
×
  • Dodaj nową pozycję...