Kürzesten Abstand messen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
DerKian
User
Beiträge: 7
Registriert: Samstag 28. Mai 2022, 21:13

Hallo,

ich bin derzeit dabei ein kleines Spiel zu schreiben, indem es darum geht vor einem npc zu flüchten.

Leider habe ich es bisher nur soweit geschafft, dass der NPC sich nur zufällig bewegt, aber nicht in Spieler Richtung.

Ich würde dass ganze so umsetzen, dass geschaut wird, von welcher Seite der Spieler am kürzesten entfernt ist und dann in die Richtung gegangen wird.

Hat jemand von euch eine bessere Idee, wie man das ganze umsetzen könnte oder kann mir erklären, wie dies geht ?

Erster Ansatz war:

if kianrect.top - honeyrect.bottom > kianrect.left - honeyrect.right or kianrect.top - honeyrect.bottom > kianrect.bottom - honeyrect.top or ...

Allerdings kann dass für mich nicht der richtige Lösungsansatz sein, da er mir ziemlich umständlich erscheint.

Mein Code:

Code: Alles auswählen

import pygame, sys, random  #Funktionssammlungen werden geladen
pygame.init()               #Die Funktionssammlung pygame wird initialisiert



#Programmtaktung
clock = pygame.time.Clock() # Zeitgeberobjekt wird erstellt --> Für die Begrenzung der Framerate

#Displayeinstellungen
displaysize = width, height = 1200, 900  # Größe des anzuzeigenden Fensters
BLACK = 0, 0, 0                         # Erstellung einer Konstanten mit dem RGB-Wert für Schwarz
WHITE = 255, 255, 255                   # Erstellung einer Konstanten mit dem RGB-Wert für Weiß
screen = pygame.display.set_mode(displaysize)   #Erstellen eines Bildschirmojektes bzw. einer Leinwand zum "Bemalen"
pygame.display.set_caption("EscapefromHoney / ETAGE 0")   # Setzen des Spieltitels

#Spielentities
map1 = pygame.image.load("maps\map1.png")  # Eine Bilddatei wird geladen und in einer Variablen gespeichert
map1rect = map1.get_rect()  # Ein unsichtbarer Kasten wird gemäß der Bildgröße gespeichert
map1rect.top = 0   # Die Oberkannte des Kastens wird in der Mitte des Bildschirms positioniert
map1rect.left = 0   # Die Oberkannte des Kastens wird in der Mitte des Bildschirms positioniert
#map1speed = [3, 3] 

#Spielentities
kian = pygame.image.load("pictures\kian60x60.png")  # Eine Bilddatei wird geladen und in einer Variablen gespeichert
kianrect = kian.get_rect()  # Ein unsichtbarer Kasten wird gemäß der Bildgröße gespeichert
kianrect.top = 120   # Die Oberkannte des Kastens wird in der Mitte des Bildschirms positioniert
kianrect.left = 120   # Die Oberkannte des Kastens wird in der Mitte des Bildschirms positioniert
#kianspeed = [3, 3] 

#Spielentities
honey = pygame.image.load("pictures\honey60x60.png")  # Eine Bilddatei wird geladen und in einer Variablen gespeichert
honeyrect = honey.get_rect()  # Ein unsichtbarer Kasten wird gemäß der Bildgröße gespeichert
honeyrect.top = 480   # Die Oberkannte des Kastens wird in der Mitte des Bildschirms positioniert
honeyrect.left = 600   # Die Oberkannte des Kastens wird in der Mitte des Bildschirms positioniert
#honeyspeed = [3, 3] 

door = pygame.image.load("pictures\door.png")
doorrect = door.get_rect()
doorrect.top = 300
doorrect.left = 0

#Spiellogikvariablen
score = 0
gameover = False
key_picked = False

#Gameover Szenario
# def Start_Gameover():
#     while 1:
#         global gameoverscreen
#         global gameover
#         global gameoverrect
#         gameover = True
#         gameoverscreen = pygame.image.load("pictures\gameover.jpg")
#         gameoverrect = gameoverscreen.get_rect()
#         gameoverrect.left = 0
#         gameoverrect.top = 0
#         screen.blit(gameoverscreen,gameoverrect)
#         pygame.display.flip()

def drawGrid():
    blockSize = 60 #Set the size of the grid block
    for x in range(0, width, blockSize):
        for y in range(0, height, blockSize):
            rect = pygame.Rect(x, y, blockSize, blockSize)
            pygame.draw.rect(screen, WHITE, rect, 1)

def Honeyup():
    if honeyrect.top - 60 < 0:  
        honeyrect.top = 55
    if gameover == False:
        honeyrect.top = honeyrect.top - 60
        
def Honeydown():
    if honeyrect.bottom + 30 < 880 and gameover == False:
        honeyrect.top = honeyrect.top + 60
        
def Honeyright():
    if honeyrect.left + 30 < 1140 and gameover == False:
        honeyrect.left = honeyrect.left + 60
        
def Honeyleft():
    if honeyrect.left - 60 < 0:
        honeyrect.left = 60
    if gameover == False:
        honeyrect.left = honeyrect.left - 60

#Spieldauerschleife, was hier geschrieben steht wird solange wiederholt bis es vom Benutzer oder durch Programmcode aktiv abgebrochen wird
while 1:
    if key_picked == False:
        key = pygame.image.load("pictures\key.png")
        keyrect = key.get_rect()
        keyrect.top = 600
        keyrect.left = 480
        screen.blit(key, keyrect)
        
    for event in pygame.event.get():    #Schleife die für jedes Event/Ereignis, welches in der Zwischenzeit stattgefunden hat durchgeführt wird
        
        #drawGrid()
      

        if event.type == pygame.QUIT: sys.exit()    #Der Benutzer beendet das Programm --> Programm/Spiel wird beendet
        if event.type == pygame.KEYDOWN:            #Der Benutzer hat eine Tastaturtaste gedrückt                      
            if event.key == pygame.K_ESCAPE:        #Die dedrückte Taste ist Escape
                pygame.quit()
                sys.exit()                          #Programm/Spiel wird beendet
            if event.key == pygame.K_DOWN:
                if kianrect.bottom + 30 < 880 and gameover == False:
                    kianrect.top = kianrect.top + 60
            if event.key == pygame.K_UP:
                if kianrect.top - 60 < 0:
                    kianrect.top = 45
                if gameover == False: 
                    kianrect.top = kianrect.top - 60
            if event.key == pygame.K_RIGHT:
                if kianrect.left + 30 < 1140 and gameover == False:
                    kianrect.left = kianrect.left + 60
            if event.key == pygame.K_LEFT:
                if kianrect.left - 60 < 0:
                    print(kianrect.left)
                    kianrect.left = 60
                if gameover == False:
                    kianrect.left = kianrect.left - 60
            

            Spielzuege = ["Honeyup", "Honeydown", "Honeyright", "Honeyleft"]
            if event.type == pygame.KEYDOWN and gameover == False:
                if random.choice(Spielzuege) == "Honeyup":
                    print("Up")
                    Honeyup()
                elif random.choice(Spielzuege) == "Honeydown":
                    Honeydown()
                    print("DOWN")
                elif random.choice(Spielzuege) == "Honeyright":
                    Honeyright()
                    print("RIGHT")
                elif random.choice(Spielzuege) == "Honeyleft":
                    Honeyleft()
                    print("LEFT")
            

            #Gameover Bedingung, wenn Honey und Spieler auf einem Kästchen
        if kianrect.bottom == honeyrect.bottom and kianrect.left == honeyrect.left:
            gameover = True
            print("GAMEOVER")
            import gameover

        if kianrect.bottom == keyrect.bottom and kianrect.left == keyrect.left:
            key_picked = True
            kianrect.bottom = keyrect.bottom
            kianrect.left = keyrect.left

        if kianrect.bottom == doorrect.bottom and kianrect.left == doorrect.left:
            if key_picked == True:
                import etage1
                print("JO")
            else:
                pass
            #import etage1
        
    pygame.display.update()
    clock.tick(60) #Begrenzung der Framerate auf max. 60 fps
    
    screen.blit(map1, map1rect)
    screen.blit(door, doorrect)
    screen.blit(kian, kianrect)
    screen.blit(honey,honeyrect)
    pygame.display.flip()                   #Schattenbild/Hintergrund/Backbuffer Bild wird mit dem Lichtbild/Vordergrund/Frontbuffer Bild getauscht
Danke schonmal für eure Hilfe!

Viele Grüße
Kian

PS: Ich möchte nochmal anmerken, dass der Code noch nicht aufgeräumt oder optimiert ist, ich bitte um Verständnis:)
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du brauchst wirklich dringend objekt-orienterte Programmierung fuer ein Spiel. Das du deine ganzen Funktionen und Variablen (immerhin konsistent, das ist schonmal gut!) mit einem Namen gepraefixt hast, ist ein klares Zeichen, dass du das eigentlich in einem Objekt halten willst.

Die importe bei bestimmten Situationen sind eine Katastrophe. Das funktioniert so nur einmal, und ist ein anti-Muster. Importiere die zu Beginn deines Codes, und steck was auch immer darin passiert in eine ordentliche Funktion (oder wieder Klasse, ggf). Gleiches gilt generell fuer den Code.

Was deine eigentliche Frage angeht - so kann eine Funktion aussehen, die immer im rechten Winkel nach dem Spieler jagd:

Code: Alles auswählen

def enemy_chase(enemy_position, player_position):
     ex, ey = enemy_position  # Position needs to be a tuple, (x, y)
     px, py = player_position
     dx, dy = px - ex, py - ey
     if abs(dx) >= abs(dy): # horizontal movement if that distance is larger
         return "left" if dx < 0 else "right"
     else:
         return "up" if dy < 0 else "down"
DerKian
User
Beiträge: 7
Registriert: Samstag 28. Mai 2022, 21:13

Moin __deets_,
danke für deine Antwort.

Das Problem mit dem import habe ich genau wie du gesagt hast behoben :)

Auch das "enemy-chase" klappt nun.
Danke für deine Hilfe.
greetings1
User
Beiträge: 51
Registriert: Donnerstag 22. Oktober 2020, 18:19

@ deets : Das ist schon gut, aber es geht noch eleganter. ;) :

Code: Alles auswählen

def choose_enemy(enemy_position, player_position):
     ex, ey = enemy_position  # Position needs to be a tuple, (x, y)
     px, py = player_position
     dx, dy = px - ex, py - ey
     # return:
     # 1 2 3
     # 8   4
     # 7 6 5
     return 3 * (1 if dx < 0 else 2 if dx > 0 else 0) + (2 if dy < 0 else 1 if dy > 0 else 0)

print (choose_enemy((3,3), (3,0))) #2
print (choose_enemy((3,3), (0,3))) #3
print (choose_enemy((3,0), (0,3))) #4
print (choose_enemy((3,3), (0,0))) #5
print (choose_enemy((0,3), (3,3))) #6
print (choose_enemy((0,0), (3,3))) #7
print (choose_enemy((0,3), (3,0))) #8
print (choose_enemy((3,0), (3,3))) #1
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Eleganz ist relativ. Wenn ich in zwei Monaten verstehen muss, was da passiert, ist das ehre mittelgut. Es ist doppelt so schwer Code zu debuggen, als es ist ihn zu schreiben. Wenn man dabei also alles gibt, wird es spannend.
greetings1
User
Beiträge: 51
Registriert: Donnerstag 22. Oktober 2020, 18:19

Na ja, die Funktion/Methode gibt einfach nur zurück, in welcher Richtung der Feind gesehen vom Spieler aus gerade ist... (oder umgekehrt, ich bin mir da gerade nicht sicher)

Jedenfalls hab ich mir das nicht selber ausgedacht, das ist eine "Formel" aus der Spieleprogrammierung. :) ... Denn dort muss ja oft geschaut werden, in welcher Richtung andere Objekte sind...
Benutzeravatar
sparrow
User
Beiträge: 4183
Registriert: Freitag 17. April 2009, 10:28

@greetings1 und weil du dir selbst nicht sicher bist, und das nicht ableiten kannst, ist es unelegant.
DerKian
User
Beiträge: 7
Registriert: Samstag 28. Mai 2022, 21:13

Hallo nochmal,

ich hatte mir den Vorschlag mit der objektorientierten Schreibweise zu Herzen genommen und habe meinen Code umgeschrieben.

Leider klappt nun das Verfolgen nicht mehr. Ich bin mal wieder ratlos...

Vielleicht kann mir einer von euch nochmal helfen :)

Code: Alles auswählen

import sys
import pygame, random, time
from time import sleep
 
# -- Global constants
 
# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
BLUE = (50, 50, 255)
GREY = (238,223,204)
 
# Screen dimensions
SCREEN_WIDTH = 1200
SCREEN_HEIGHT = 900
 
gameover = False
player_in_radius = False

class Player(pygame.sprite.Sprite):
    """ This class represents the bar at the bottom that the player
    controls. """
 
    # Constructor function
    def __init__(self, x, y, image):
        # Call the parent's constructor
        super().__init__()
 
        # Set height, width
        self.image = pygame.image.load(image)
 
        # Make our top-left corner the passed-in location.
        self.rect = self.image.get_rect()
        self.rect.y = y
        self.rect.x = x
 
        # Set speed vector
        self.change_x = 0
        self.change_y = 0
        self.walls = None
 
    def changespeed(self, x, y):
        """ Change the speed of the player. """
        self.change_x += x
        self.change_y += y
 
    def update(self):
        """ Update the player position. """
        # Move left/right
        self.rect.x += self.change_x
 
        # Did this update cause us to hit a wall?
        block_hit_list = pygame.sprite.spritecollide(self, self.walls, False)
        for block in block_hit_list:
            # If we are moving right, set our right side to the left side of
            # the item we hit
            if self.change_x > 0:
                self.rect.right = block.rect.left
            else:
                # Otherwise if we are moving left, do the opposite.
                self.rect.left = block.rect.right
 
        # Move up/down
        self.rect.y += self.change_y
 
        # Check and see if we hit anything
        block_hit_list = pygame.sprite.spritecollide(self, self.walls, False)
        for block in block_hit_list:
 
            # Reset our position based on the top/bottom of the object.
            if self.change_y > 0:
                self.rect.bottom = block.rect.top
            else:
                self.rect.top = block.rect.bottom
 
class NPC(pygame.sprite.Sprite):
    def __init__(self, x, y, image):
        # Call the parent's constructor
        super().__init__()
        self.image = pygame.image.load(image)
 
        # Make our top-left corner the passed-in location.
        self.rect = self.image.get_rect()
        self.rect.y = y
        self.rect.x = x

        # Set speed vector
        self.change_x = 0
        self.change_y = 0
        self.walls = None

    def changespeed(self, x, y):
        """ Change the speed of the NPC. """
        self.change_x += x
        self.change_y += y

    def update(self):
        """ Update the NPC position. """
        # Move left/right
        self.rect.x += self.change_x
 
        # Did this update cause us to hit a wall?
        block_hit_list = pygame.sprite.spritecollide(self, self.walls, False)
        for block in block_hit_list:
            # If we are moving right, set our right side to the left side of
            # the item we hit
            if self.change_x > 0:
                self.rect.right = block.rect.left
            else:
                # Otherwise if we are moving left, do the opposite.
                self.rect.left = block.rect.right
 
        # Move up/down
        self.rect.y += self.change_y
 
        # Check and see if we hit anything
        block_hit_list = pygame.sprite.spritecollide(self, self.walls, False)
        for block in block_hit_list:
 
            # Reset our position based on the top/bottom of the object.
            if self.change_y > 0:
                self.rect.bottom = block.rect.top
            else:
                self.rect.top = block.rect.bottom

    def rand_walking(self):
        if player_in_radius == False:
            Spielzuege = ["Honeyup", "Honeydown", "Honeyright", "Honeyleft"]
            if random.choice(Spielzuege) == "Honeyup":
                self.changespeed(0, 3)
                print("UP")
            elif random.choice(Spielzuege) == "Honeydown":
                self.changespeed(0, -3)
                print("DOWN")
            elif random.choice(Spielzuege) == "Honeyright":
                self.changespeed(3, 0)
                print("RIGHT")
            elif random.choice(Spielzuege) == "Honeyleft":
                
                print("LEFT")

    def walking(self):
        while True:
            enemy_position = self.rect.x, self.rect.y  # Position needs to be a tuple, (x, y)
            player_position = player.rect.x, player.rect.y
            dx, dy = player.rect.x - self.rect.x, player.rect.y - self.rect.y
            if abs(dx) >= abs(dy): # horizontal movement if that distance is larger
                return self.changespeed(-2.5, 0) if dx < 0 else self.changespeed(2.5, 0)
            else:
                return self.changespeed(0, 2.5) if dy < 0 else self.changespeed(0, -2.5)




class Wall(pygame.sprite.Sprite):
    """ Wall the player can run into. """
    def __init__(self, x, y, width, height):
        """ Constructor for the wall that the player can run into. """
        # Call the parent's constructor
        super().__init__()
 
        # Make a blue wall, of the size specified in the parameters
        self.image = pygame.Surface([width, height])
        self.image.fill(BLACK)
 
        # Make our top-left corner the passed-in location.
        self.rect = self.image.get_rect()
        self.rect.y = y
        self.rect.x = x
 
 
# Call this function so the Pygame library can initialize itself
pygame.init()
 
# Create an 800x600 sized screen
screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
 
# Set the title of the window
pygame.display.set_caption("EscapefromHoney / ETAGE 0")
 
# List to hold all the sprites
all_sprite_list = pygame.sprite.Group()
 
# Make the walls. (x_pos, y_pos, width, height)
wall_list = pygame.sprite.Group()
 
wall = Wall(0, 0, 10, 900)
wall_list.add(wall)
all_sprite_list.add(wall)
 
wall = Wall(1190, 0, 10, 900)
wall_list.add(wall)
all_sprite_list.add(wall)

wall = Wall(0, 890, 1200, 10)
wall_list.add(wall)
all_sprite_list.add(wall)

wall = Wall(10, 0, 1200, 10)
wall_list.add(wall)
all_sprite_list.add(wall)
 
wall = Wall(10, 200, 120, 10)
wall_list.add(wall)
all_sprite_list.add(wall)

# Create the player paddle object
player = Player(50, 50, "pictures\kian60x60.png")
player.walls = wall_list

enemy_h = NPC(300,80, "pictures\honey60x60.png")
enemy_h.walls = wall_list

all_sprite_list.add(player)
all_sprite_list.add(enemy_h)

clock = pygame.time.Clock()
 
done = False

while not done:
 
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        
        
 
        elif event.type == pygame.KEYDOWN:
            enemy_h.walking()
            if event.key == pygame.K_LEFT:
                player.changespeed(-3, 0)
            elif event.key == pygame.K_RIGHT:
                player.changespeed(3, 0)
            elif event.key == pygame.K_UP:
                player.changespeed(0, -3)
            elif event.key == pygame.K_DOWN:
                player.changespeed(0, 3)
            elif event.key == pygame.K_ESCAPE:
                pygame.QUIT
                sys.exit()
            
        
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT:
                player.changespeed(3, 0)
            elif event.key == pygame.K_RIGHT:
                player.changespeed(-3, 0)
            elif event.key == pygame.K_UP:
                player.changespeed(0, 3)
            elif event.key == pygame.K_DOWN:
                player.changespeed(0, -3)
            
 
    all_sprite_list.update()
 
    screen.fill(GREY)
 
    all_sprite_list.draw(screen)
 
    pygame.display.flip()
 
    clock.tick(60)
 
pygame.quit()
Viele Grüße
Kian
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Da sind diverse No-Nos: du benutzt globale Variablen wie player_in_radius, die so ja nie funktionieren koennen - der Radius haengt ja konkret vom NPC und seiner Position zu Spieler ab. Und der NPC kennt den Spieler doch sogar, warum also nicht einfach da berechnen? Die while-Schleife in walking is auch Unfug.

Was das eigentliche Problem angeht: du arbeitest jetzt nicht mehr mit absoluten Schritten/Geschwindigkeiten, sondern beschleunigst. Das ist komplett anders: wenn du erstmal 10 mal nach rechts beschleunigt hast, und dann bewegt der Spieler sich nach oben, dann ueberschiesst der ja krass, weil er zwar ein bisschen nach oben korrigiert, aber erstmal seinen Geschwindigkeitsueberschuss loswerden muss. Da muss also ein oberes Limit rein.
Antworten