python – Simple enemy movement simulation on Tkinter

Kind of a noob at Python here, and this is one of my first “big”(big for a beginner like me) project that I undertook with Tkinter and Pynput. Basically, this code will simulate an enemy’s movement pattern based on some conditions that I made(you will be able to see the different “phases” being printed out on the console). You can then control the player using the arrow keys.

I would like some advice on what I should improve on for future projects. Should I add more comments? Is the code structured well? etc.

import math
import tkinter as tk
from pynput import keyboard
class Application:
    def __init__(self, master, height = 800, width = 800, updatesPerSecond = 10, safeCircle = True):
        self.height = height
        self.width = width
        self.root = master
        self.updatesPerSecond = updatesPerSecond
        self.player = Player()
        self.enemy = Enemy()
        self.safeCircle = safeCircle
        self.canvas = tk.Canvas(self.root, height = self.height, width = self.width)
        self.canvas.pack()
        self.player_rectangle = self.canvas.create_rectangle(self.player.x-self.player.hLength, self.player.y-self.player.hLength, self.player.x+self.player.hLength, self.player.y+self.player.hLength)
        self.enemy_rectangle = self.canvas.create_rectangle(self.enemy.x-self.player.hLength, self.enemy.y-self.player.hLength, self.enemy.x+self.player.hLength, self.enemy.y+self.player.hLength)
        if self.safeCircle:
            self.safe_circle = self.canvas.create_oval(self.player.x-self.enemy.safe_distance, self.player.y-self.enemy.safe_distance, self.player.x+self.enemy.safe_distance, self.player.y+self.enemy.safe_distance)
        self.keypress_list = ()
        self.listener = keyboard.Listener(on_press = self.on_press, on_release = self.on_release)
        self.listener.start()
        self.player_movement()
        self.enemy_movement()
    def player_movement(self):
        if "down" in self.keypress_list:
            self.player.update_y(self.player.speed)
        if "up" in self.keypress_list:
            self.player.update_y(-self.player.speed)
        if "left" in self.keypress_list:
            self.player.update_x(-self.player.speed)
        if "right" in self.keypress_list:
            self.player.update_x(self.player.speed)
        self.player.boundary_check(self.height, self.width)
        self.canvas.coords(self.player_rectangle, self.player.x-self.player.hLength, self.player.y-self.player.hLength, self.player.x+self.player.hLength, self.player.y+self.player.hLength)
        if self.safeCircle:
            self.canvas.coords(self.safe_circle, self.player.x-self.enemy.safe_distance, self.player.y-self.enemy.safe_distance, self.player.x+self.enemy.safe_distance, self.player.y+self.enemy.safe_distance)
        self.root.after(1000//self.updatesPerSecond, self.player_movement)
    def enemy_movement(self):
        self.enemy.update_pos(self.player)
        self.enemy.boundary_check(self.height, self.width)
        self.canvas.coords(self.enemy_rectangle, self.enemy.x-self.enemy.length/2, self.enemy.y-self.enemy.length/2, self.enemy.x+self.enemy.length/2, self.enemy.y+self.enemy.length/2)
        self.root.after(1000//self.updatesPerSecond, self.enemy_movement)
    def key_test(self, key):
        try:
            return key.name
        except:
            return
    def on_press(self, key):
        key = self.key_test(key)
        if not key in self.keypress_list:
            self.keypress_list.append(key)
    def on_release(self, key):
        key = self.key_test(key)
        self.keypress_list.remove(key)

class SimObject:
    def __init__(self, x, y, speed, length):
        self.x = x
        self.y = y
        self.speed = speed
        self.length = length
        self.hLength = self.length/2
    def boundary_check(self, height, width):
        if self.x - self.hLength < 0:
            self.x = self.hLength
        if self.y - self.hLength < 0:
            self.y = self.hLength
        if self.x + self.hLength > width:
            self.x = width - self.hLength
        if self.y + self.hLength > height:
            self.y = height - self.hLength
    def update_x(self, offset):
        self.x+=offset
    def update_y(self, offset):
        self.y+=offset

class Player(SimObject):
    def __init__(self, x = 400, y = 400, speed = 10, length = 20):
        super().__init__(x, y, speed, length)

class Enemy(SimObject):
    def __init__(self, x = 10, y = 10, speed = 5, length = 20, safe_distance = 100):
        super().__init__(x, y, speed, length)
        self.safe_distance = safe_distance
        self.last_phase = -1
    def update_phase(self, n):
        phase_list=(f"{i} Phase" for i in ("Orbit", "Rush", "Run"))
        if self.last_phase!=n:
            print(phase_list(n))
            self.last_phase = n
    def update_pos(self, player):
        PI=math.pi
        dx=player.x-self.x
        dy=player.y-self.y
        g_to_p_ang=math.atan2(dy,dx)
        p_to_g_ang=PI+g_to_p_ang
        dist=math.sqrt(dx*dx+dy*dy)
        ang_increase=self.speed/self.safe_distance
        t=p_to_g_ang
        if abs(dist-self.safe_distance)<=self.speed:#near the orbit
            self.update_phase(0)
            t+=ang_increase
            self.x=self.safe_distance*math.cos(t)+player.x
            self.y=self.safe_distance*math.sin(t)+player.y
        elif dist>self.safe_distance:#far from orbit
            self.update_phase(1)
            self.update_x(self.speed*math.cos(g_to_p_ang))
            self.update_y(self.speed*math.sin(g_to_p_ang))
        elif dist<self.safe_distance:#far inside of orbit
            self.update_phase(2)
            self.update_x(self.speed*math.cos(p_to_g_ang))
            self.update_y(self.speed*math.sin(p_to_g_ang))


root = tk.Tk()
root.resizable(0,0)
root.title("Enemy Movement Test")
application = Application(root)
tk.mainloop()