To get a "smooth" you've to do the computation of the enemy position and movement with floating point values rather than integral values.
Note, the position and size of a pygame.Rect
object is stored integral. Every time when a movement is added to the position, then the fractional part is lost. This causes that small movements in a direction are not performed at all. If a value less than 0.5 (round
) is added to a integral coordinate then the position will never change, even beyond frames. If the coordinate is a floating point value, then the integral part of the position will change after some frames.
Add a position attribute (.pos) the the class Enemy
:
class Enemy(pygame.sprite.Sprite):
def __init__(self, x, y, speed, walls):
# [...]
self.pos = [x, y]
self.rect.y = y
self.rect.x = x
Do the change of the position on the attribute .pos
and update .rect
by .pos
. e.g.:
self.pos[0] = self.pos[0] + self.move_x
self.rect.x = round(self.pos[0])
If a collision has been detected, then the attribute .pos
has to be corrected by the integral position in of the attribute .rect
. e.g.:
if block_collide:
self.pos[0] = self.rect.x
Further you've to create the object attributes attribute self.move_x
and self.move_y
rather than the class attributes Enemy.move_x
and Enemy.move_y
.
Note, a class exists attribute once for each type (calss), but an object attribute exists ist once for each object ("enemy"). Each enemy has to have its own movement vector, else the movement what was computed by the last enemy would be applied to all enemies. This causes that enemies do unexpected movement. See Class Definition Syntax.
Of course you've to move the enemies in a loop
for e in enemy_list:
e.move(player)
Complete code of the class Enemy
:
class Enemy(pygame.sprite.Sprite):
def __init__(self, x, y, speed, walls):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([20, 20])
self.image.fill(red)
self.rect = self.image.get_rect()
self.pos = [x, y]
self.rect.y = y
self.rect.x = x
self.move_x = 0
self.move_y = 0
self.speed = speed # speed of the enemy
self.walls = walls # walls for the collision test
def move(self, player):
dx, dy = player.rect.x - self.pos[0], player.rect.y - self.pos[1]
dist = math.hypot(dx, dy)
dx, dy = dx / dist, dy / dist
self.move_x = dx * min(dist, self.speed)
self.move_y = dy * min(dist, self.speed)
def update(self):
self.pos[0] = self.pos[0] + self.move_x
self.rect.x = round(self.pos[0])
block_collide = pygame.sprite.spritecollide(self, self.walls, False)
for block in block_collide:
if self.move_x > 0:
self.rect.right = block.rect.left
else:
self.rect.left = block.rect.right
if block_collide:
self.pos[0] = self.rect.x
self.pos[1] = self.pos[1] + self.move_y
self.rect.y = round(self.pos[1])
block_collide = pygame.sprite.spritecollide(self, self.walls, False)
for block in block_collide:
if self.move_y > 0:
self.rect.bottom = block.rect.top
else:
self.rect.top = block.rect.bottom
if block_collide:
self.pos[1] = self.rect.y