from github
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
import sys
|
||||
import time
|
||||
from MoveAble import MoveAble
|
||||
from Node import Node
|
||||
from Vec import Vec
|
||||
|
||||
|
||||
# Funktion für den Pathfinding Algorithmus, welche überprüft, ob der Parent eines Nodes neu gesetzt werden muss.
|
||||
def handle_node_cost_check(temp_node, open_nodes, current_node, end_node):
|
||||
if temp_node not in open_nodes:
|
||||
temp_node.parent = current_node
|
||||
temp_node.calculate_cost(end_node)
|
||||
open_nodes.append(temp_node)
|
||||
elif current_node.p_cost + 1 + temp_node.h_cost < temp_node.g_cost:
|
||||
temp_node.parent = current_node
|
||||
temp_node.calculate_cost(end_node)
|
||||
|
||||
|
||||
class Ghost(MoveAble):
|
||||
|
||||
def __init__(self, start_position: Vec, start_size: Vec, cell_count, grid_cell_size, player, maze_grid,
|
||||
start_delay):
|
||||
super().__init__(start_position, start_size, 2)
|
||||
self.cell_count = cell_count
|
||||
self.start_delay = start_delay
|
||||
self.chase = True
|
||||
self.stateChange = time.time()
|
||||
self.grid = []
|
||||
self.grid_cell_size = grid_cell_size
|
||||
self.player = player
|
||||
self.maze_grid = maze_grid
|
||||
self.res = []
|
||||
self.generate_nodes()
|
||||
self.distance = 0
|
||||
self.direction = None
|
||||
self.default_pos = start_position.get_instance()
|
||||
self.eaten = False
|
||||
|
||||
def reset_position(self):
|
||||
self.distance = 0
|
||||
self.position = self.default_pos.get_instance()
|
||||
|
||||
def idle(self):
|
||||
pass
|
||||
|
||||
def hunting(self):
|
||||
pass
|
||||
|
||||
# Diese Funktion wird in jedem Update des Spiels (GameLoop) aufgerufen
|
||||
def run(self, afraid, relative_start_time):
|
||||
if time.time() - relative_start_time > self.start_delay:
|
||||
# Sollte der Geist von PacMan gefressen worden sein, bewegt sich dieser ins Geisterhaus zurück.
|
||||
|
||||
if self.eaten:
|
||||
self.move_to_point(Vec(15, 14))
|
||||
if int((self.position.x + self.grid_cell_size / 2) / self.grid_cell_size) == 15 and \
|
||||
int((self.position.y + self.grid_cell_size / 2) / self.grid_cell_size) == 14:
|
||||
self.eaten = False
|
||||
else:
|
||||
# Sollte der Geist verängstigt / Blau sein, führt dieser seine Idle routine aus.
|
||||
if afraid:
|
||||
self.idle()
|
||||
else:
|
||||
# Hier wird anhand des aktuellen States (siehe Timer) zwischen Jagd und Idle gewechselt
|
||||
self.timer()
|
||||
if self.chase:
|
||||
self.hunting()
|
||||
else:
|
||||
self.idle()
|
||||
|
||||
# Diese Funktion wechselt in Zeitabständen von 16 und 4,5 Sekunden zwischen dem Jagd- und dem Idle-Modus
|
||||
def timer(self):
|
||||
if time.time() - self.stateChange >= (16 if self.chase else 4.5):
|
||||
self.chase = not self.chase
|
||||
self.stateChange = time.time()
|
||||
|
||||
# Diese Funktion gibt an, ob das Bild des Geistes gespiegelt werden muss.
|
||||
def is_mirrored(self):
|
||||
return self.velocity.x < 0
|
||||
|
||||
# Diese Funktion setzt die Kosten aller Nodes zurück (dadurch müssen keine neuen Objekte generiert werden)
|
||||
def clear_nodes(self):
|
||||
for x in range(0, self.cell_count):
|
||||
for y in range(0, self.cell_count):
|
||||
self.grid[x][y].g_cost = sys.float_info.max
|
||||
|
||||
# Generiere für jedes Feld (Wand oder Frei oder Geist) einen Knoten, welcher Informationen wie Parent und Kosten
|
||||
# beinhaltet die Nodes müssen nur zu Beginn des Spiels einmal generiert werden.
|
||||
def generate_nodes(self):
|
||||
for x in range(0, self.cell_count):
|
||||
self.grid.append([])
|
||||
for y in range(0, self.cell_count):
|
||||
self.grid[x].append(Node(x, y))
|
||||
|
||||
# Diese Funktion bewegt den Geist in Richtung eines Gegeben End Punktes v
|
||||
def move_to_point(self, v):
|
||||
if self.distance <= self.speed:
|
||||
# Rufe den Pathfinding Algorithmus
|
||||
self.res = self.find_path(v)
|
||||
if len(self.res) > 0:
|
||||
x_pos = int((self.position.x + self.grid_cell_size / 2) / self.grid_cell_size)
|
||||
y_pos = int((self.position.y + self.grid_cell_size / 2) / self.grid_cell_size)
|
||||
|
||||
# Bilden den Velocity Vektor (Richtung und Geschwindigkeit), wohin der Geist sich bewegen soll
|
||||
self.direction = Vec(self.res[0].x - x_pos, self.res[0].y - y_pos)
|
||||
self.direction.mul(self.speed)
|
||||
self.velocity = self.direction
|
||||
|
||||
if len(self.res) > 0:
|
||||
# Bestimme die Distanz zum nächsten Node (resultiert aus dem Pathfinding) um oben in der Funktion zu
|
||||
# bestimmen, ob das Pathfinding erneut ausgeführt werden soll
|
||||
self.distance = abs(
|
||||
self.position.distance(Vec(self.res[0].x * self.grid_cell_size, self.res[0].y * self.grid_cell_size)))
|
||||
# Bewege den Geist
|
||||
self.move()
|
||||
|
||||
# In dieser Funktion werden alle Parents eines Nodes in eine Liste abgespeichert
|
||||
@staticmethod
|
||||
def path_node_to_list(node, start_node):
|
||||
result = []
|
||||
# Iteratives aufbauen der Liste
|
||||
while node is not start_node:
|
||||
result.append(node)
|
||||
node = node.parent
|
||||
# Liste Invertieren, da diese noch in verkehrter Reihenfolge ist. (End -> Start, muss ja Start -> End sein)
|
||||
result.reverse()
|
||||
|
||||
return result
|
||||
|
||||
# Eine Methode, welche einen Pfad von der aktuellen Position des Geistes zu einem EndPunkt berechnet
|
||||
def find_path(self, end_point):
|
||||
# Alle Nodes werde zu Beginn zurückgesetzt bzw. ihre Kosten werden auf "unendlich" gesetzt.
|
||||
self.clear_nodes()
|
||||
|
||||
# Zwei listen, in welchen die zu überprüfenden Nodes und die bereits überprüften Nodes abgespeichert werden.
|
||||
checked_nodes = []
|
||||
open_nodes = []
|
||||
|
||||
# umrechnen der Positionen von 0 - bildschirmbreite, 0 - bildschirmhöhe in 0 - 30, 0 - 30
|
||||
x_pos = int((self.position.x + self.grid_cell_size / 2) / self.grid_cell_size)
|
||||
y_pos = int((self.position.y + self.grid_cell_size / 2) / self.grid_cell_size)
|
||||
x_pos_end_point = end_point.x
|
||||
y_pos_end_point = end_point.y
|
||||
|
||||
# Positionen gegebenenfalls (falls über die Array-Grenzen hinaus) "abschneiden"
|
||||
if x_pos_end_point >= 30:
|
||||
x_pos_end_point = 29
|
||||
if x_pos_end_point < 0:
|
||||
x_pos_end_point = 0
|
||||
|
||||
# Das Start Node bestimmen und dessen Kosten auf 0 setzen (dort wo man schon ist, muss man nicht hinlaufen)
|
||||
start_node = self.grid[x_pos][y_pos]
|
||||
start_node.g_cost = 0
|
||||
start_node.h_cost = 0
|
||||
start_node.p_cost = 0
|
||||
|
||||
# Das End Node merken und das Start Node in die Liste der zu überprüfenden Nodes einfügen.
|
||||
# (Es wird als Erstes zum current Node)
|
||||
end_node = self.grid[x_pos_end_point][y_pos_end_point]
|
||||
current_node: Node = None
|
||||
open_nodes.append(start_node)
|
||||
|
||||
# Den Algorithmus so lange ausführen, bis das aktuelle Node dem End Node entspricht (=> Weg wurde gefunden)
|
||||
while current_node is not end_node:
|
||||
# Sollte die Liste der zu überprüfenden Nodes Elemente enthalten,
|
||||
# wird das Node mit den aktuell geringsten Kosten zum Current Node
|
||||
if len(open_nodes) > 0:
|
||||
open_nodes.sort(key=lambda x: x.g_cost)
|
||||
current_node = open_nodes.pop(0)
|
||||
else:
|
||||
break
|
||||
|
||||
# Das Aktuelle Node wurde mit diesem Schleifendurchlauf überprüft, und ist hiermit fest
|
||||
# (es hatten in der Liste open_nodes die geringsten Kosten)
|
||||
checked_nodes.append(current_node)
|
||||
|
||||
# Hier werden alle Nachbarknoten des aktuellen Knotens überprüft und eine Funktion aufgerufen,
|
||||
# die gegebenenfalls den Parent dieses Nachbar Nodes umsetzt.
|
||||
if current_node.x + 1 < 30 and (
|
||||
self.maze_grid[current_node.x + 1][current_node.y] == 0 or self.maze_grid[current_node.x + 1][
|
||||
current_node.y] == 2) and \
|
||||
(not self.grid[current_node.x + 1][current_node.y] in checked_nodes):
|
||||
# Nachbar Node vom aktuellen Node (eins nach rechts) zwischen speichern
|
||||
temp_node = self.grid[current_node.x + 1][current_node.y]
|
||||
# Hier wird der Parent des Nachbar Node gegebenenfalls umgesetzt
|
||||
handle_node_cost_check(temp_node, open_nodes, current_node, end_node)
|
||||
|
||||
if current_node.x - 1 >= 0 and (
|
||||
self.maze_grid[current_node.x - 1][current_node.y] == 0 or self.maze_grid[current_node.x - 1][
|
||||
current_node.y] == 2) and \
|
||||
(not self.grid[current_node.x - 1][current_node.y] in checked_nodes):
|
||||
temp_node = self.grid[current_node.x - 1][current_node.y]
|
||||
handle_node_cost_check(temp_node, open_nodes, current_node, end_node)
|
||||
|
||||
if current_node.y + 1 < 30 and (
|
||||
self.maze_grid[current_node.x][current_node.y + 1] == 0 or self.maze_grid[current_node.x][
|
||||
current_node.y + 1] == 2) and \
|
||||
(not self.grid[current_node.x][current_node.y + 1] in checked_nodes):
|
||||
temp_node = self.grid[current_node.x][current_node.y + 1]
|
||||
handle_node_cost_check(temp_node, open_nodes, current_node, end_node)
|
||||
|
||||
if current_node.y - 1 >= 0 and (
|
||||
self.maze_grid[current_node.x][current_node.y - 1] == 0 or self.maze_grid[current_node.x][
|
||||
current_node.y - 1] == 2) and \
|
||||
(not self.grid[current_node.x][current_node.y - 1] in checked_nodes):
|
||||
temp_node = self.grid[current_node.x][current_node.y - 1]
|
||||
handle_node_cost_check(temp_node, open_nodes, current_node, end_node)
|
||||
|
||||
# Hier wird eine Liste in der richtigen Reihenfolge basierend auf allen Parents des End Nodes zurückgegeben
|
||||
return self.path_node_to_list(current_node, start_node)
|
||||
Reference in New Issue
Block a user