Attributes fall into two categories: class attributes and instance attributes.
Class attributes are things associated with the class. You don't need to create an instance of the class to access these attributes. So far, the only class attribute you have created are Man.__init__() and Man.move(). Class variables are the same for all instances of the class.
Instance attributes are associated with an instance of a class. Each instance may have the same instance attribute names, but the values can differ from one instance to another.
Instance attributes are usually created in the __init__() method. In your Man class you only inherited the instance variables created by Rect.__init__(). If you want to "add" an instance attribute, you just need to assign a value to the instance. To give your Enemy instances a "speed" attribute, assign a value to self.speed in the __init__(). May as well give Enemy damage and hp attributes too.
class Enemy(Rect):
def __init__(self, x, y, speed=0.6, angle=0, damage=1, hp=3):
super().__init__(x, y, 20, 20, fill='red', border='darkRed')
self.damage = damage
self.hp = hp
self.speed = speed
All instances of Enemy will have damage, hp and speed attributes, but different Enemy objects can have different speed, hp or damage values.
If I understand what you are trying to do with ai (crash an Enemy into peter), the code can be simplified and there is no reason to compute angles. There's also no need to compute speedX and speedY.
def ai(self, target):
if not self.visible:
return
x = target.centerX - self.centerX
y = target.centerY - self.centerY
if x != 0 or y != 0:
# Convert x, y to a unit vector.
d = (dx**2 + dy**2)**0.5 # Compute distance from self to target.
# Move toward target at self.speed
self.centerX += self.speed * x / d
self.centerY += self.speed * y / d
If Rect.centerX is an int, this code may not work. The code might complain that you cannot set centerX to a float, or it might round or truncate the value to the nearest int. round(1 + 0.1) == 1, not 1.1. int(1 + 0.9) == 1, not 1.9 or 2.
The reason I use target instead of peter is because classes should not reference variables that are not part of the class. To use your Enemy class I need to create a variable named peter. If I wanted to change the game I was writing to two players, I could not use your enemy class because it is limited to one player, and the player instance needs to be peter. By passing the target as an argument, I can have multiple players, and target different enemies at different players.
Depending on how you use the Enemy objects it might make sense for the Enemy to have a target attribute. This code (I think) creates 3 enemies. Enemy "a" wanders about directionless, doggedly pursued by enemy "b" which in turn is pursued by "c". I cannot test the code as I don't have access to Rect.
class Enemy(Rect):
def __init__(self, x, y, speed=0.6, damage=1, hp=3, color="red", fill="darkRed"):
super().__init__(x, y, 20, 20, fill=fill, border=color)
self.speed = speed
self.damage = damage
self.hp = hp
self.target = None
def set_target(self, target):
self.target = target
def ai(self):
if not self.visible:
return
if self.target is not None:
# Move toward target
x = self.target.centerX - self.centerX
y = self.target.centerY - self.centerY
else:
# Randomly wander about
x = random.random() - 0.5
y = random.random() - 0.5
if x != 0 or y != 0:
d = (x*x + y*y)**0.5
self.centerX += self.speed * x / d
self.centerY += self.speed * y / d
a = Enemy(50, 100, speed=5, color="blue", fill="yellow")
b = Enemy(100, 50, speed=2)
b.set_target(a)
c = Enemy(75, 75, speed=1)
c.set_target(a)