MicroStudio und Python: Kollisionserkennung (1)

microStudio
Python
Spieleprogrammierung
Autor:in

Jörg Kantel

Veröffentlichungsdatum

2. April 2024

Meine Expedition durch das Land der Spieleprogrammierung mit microStudio und Python/Brython geht in die nächste Runde. Nachdem ich im letzten Ausflug ein paar Kreise über den Bildschirm hüpfen ließ, steht nun die nächste Frage im Raum: Wie erkennt man, wenn die Kreise miteinander kollidieren?

Kollisionserkennung ist ein großes Thema, denn kaum eine Computersimulation oder ein -spiel kommt ohne aus. Für den Anfang möchte ich erst einmal den einfachsten Fall behandeln, die Kollision zweier Kreise:

Ich hatte diese Kollisionserkennung schon einmal (in Python (Processing.py) und JavaScript (P5.js)) im Schockwellenreiter behandelt. Es ist wirklich die einfachste Form, denn zwei Kreise kollidieren dann und nur dann, wenn die Distanz der beiden Mittelpunkte der Kreise gleich oder kleiner der Länge der beiden Radii ist. In Python kann man das so abfragen:

def is_circle_collision(obj1, obj2):
  distance = math.dist([obj1.x, obj1.y], [obj2.x, obj2.y])
  if distance < obj1.r + obj2.r:
    return True
  return False

Der Abstand ist der euklidische Abstand, der (auch in obiger math.dist()-Funktion) nach dem guten alten Satz des Pythagoras berechnet wird.

In meinem Beispielprogramm lasse ich ein kreisrundes Kaninchen vom Spieler mit der Maus steuern. Berührt das Kaninchen die (ebenfalls kreisrunde) Schlange, färbt sich der Hintergrund blutrot. Das ist doch alles recht einleuchtend und auch schnell implementiert. Zuerst die Schlange:

class Snake:
  
  def __init__(self):
    self.x = 0
    self.y = 0
    self.w = 70
    self.h = 70
    self.r = 35
    self.im = "snake"
    
  def display(self):
    screen.drawSprite(self.im, self.x, self.y, self.w, self.h)

Die Klasse ist fast schon ein wenig overkill. Da die Schlange im Spiel unbeweglich ist, besitzt sie nur ein paar Eigenschaften und die Methode display(), die sie auf den Bildschirm zeichnet.

class Rabbit:
  
  def __init__(self):
    self.x = 120
    self.y = 60
    self.w = 40
    self.h = 40
    self.r = 20
    self.im = "rabbit"
    
  def move(self):
    self.x = mouse.x
    self.y = mouse.y
  
  def display(self):
    screen.drawSprite(self.im, self.x, self.y, self.w, self.h)

Viel komplexer ist aber auch die Klasse des Kaninchens nicht, außer das sie zusätzlich noch die Methode move() besitzt, die dafür verantwortlich ist, daß das Kaninchen dem Mauszeiger folgt.

Um die Objektorientierung auf die Spitze zu treiben (aber in der Hauptsache, um die Verwendung globaler Variablen zu minimieren), habe ich dem Bildschirmhintergrund auch eine eigene Klasse spendiert:

class Back:
  
  def __init__(self):
    self.bg_col = "rgb(234, 218, 184)"
    
  def display(self):
    screen.fillRect(0, 0, screen.width, screen.height, self.bg_col)

Bei soviel OOP kann die Hauptroutine ja nur noch übersichtlich kurz geraten sein:

import math

def init():
  global rabbit, snake, back
  back = Back()
  rabbit = Rabbit()
  snake = Snake()

def update():
  rabbit.move()
  if is_circle_collision(rabbit, snake):
    back.bg_col = "rgb(250, 25, 25)"
  else:
    back.bg_col = "rgb(234, 218, 184)"   
    
def draw():
  back.display()
  snake.display()
  rabbit.display()
  
def is_circle_collision(obj1, obj2):
  distance = math.dist([obj1.x, obj1.y], [obj2.x, obj2.y])
  if distance < obj1.r + obj2.r:
    return True
  return False

Hier findet dann endlich die oben erwähnte Kollisionserkennung der beiden Kreise statt.

Den kreisrunden Schlangen- wie auch den (ebenfalls kreisrunden) Kaninchenkopf (in meiner Spielewelt habe Tiere nun einmal die Form einer Kugel) habe ich dem freien (CC0) Animal Pack von Kenney entnommen, denn es gibt nahezu nichts, was es bei Kenney nicht gibt. Und auch dieses kleine Skript habe ich wieder auf meinem microStudio-Account zu Eurer Erbauung hochgeladen.

Das ist dann das siebte Tutorial dieser kleinen Reihe. Macht was daraus …