Reputation: 43
In the game i am currently making i need to make an ennemy move towards a player (here the enemy is a blob) to make this, i already created a function : def calculate_enemy_movement(enemy):
and inside of it i put what the function needs to do :
calculate what x value needs to be added each frame(x velocity), and what y value needs to be added each frame(y velocity) to the
enemy.x
andenemy.y
to walk towards theplayer.x
andplayer.y
and then return x velocity and return y velocity.
This function gets called here : add x velocity and y velocity to x and y pos of enemy each frameenemy_blob.x,enemy_blob.y += calculate_enemy_movement(enemy_blob)
now i just need someone to help me create this function. I've already looked into it and i know it has something to do with the vector mathematics but i'm not sure how exactly it works, so if someone could write the function and explain me how it works i would be really pleased.
if you need the rest of the code :
import pygame
import os
import sys
from math import *
import math
from typing import Tuple
from pygame.locals import *
running = False
class Game():
def __init__(self):
main()
pygame.init()
FPS = 60
ani = 4
def diagonal_x_vel(velocity):
diagonal_x = int((math.pi / 4) * velocity)
return diagonal_x
def diagonal_y_vel(velocity):
diagonal_y = int(sin(math.pi / 4) * velocity)
return diagonal_y
def move(rect, movement):
global player_rect
rect.x += movement[0]
rect.y += movement[1]
return rect
WIDTH, HEIGHT = 1920, 1080
player_action = "idle"
player_frame = 0
class Player():
def __init__(self):
self.main_char_height, self.main_char_width = 35 * 4, 35 * 4
self.main_char_x = WIDTH / 2 - self.main_char_width / 2
self.main_char_y = HEIGHT / 2 - self.main_char_height / 2
self.current_health = 80
self.maximum_health = 100
self.health_bar_length = 500
self.health_ratio = self.maximum_health / self.health_bar_length
self.moving_right, self.moving_left, self.moving_down, self.moving_up, self.idle = False, False, False, False, True
self.player_rect = pygame.Rect(self.main_char_x, self.main_char_y, self.main_char_width, self.main_char_height)
self.player_flip = False
self.VEL = 8
def update():
pass
def take_dmg(self, amount):
if self.current_health > 0:
self.current_health -= amount
if self.current_health <= 0:
self.current_health = 0
def get_health(self, amount):
if self.current_health < self.maximum_health:
self.current_health += amount
if self.current_health >= self.maximum_health:
self.current_health = self.maximum_health
class Enemy():
def __init__(self, health, attack_damage, raw_speed, x, y, height, width, action,diagonal_vel_x,diangonal_vel_y):
self.x = x
self.y = y
self.health = health
self.attack_damage = attack_damage
self.speed = raw_speed
self.enemy_rect = pygame.Rect(x,y,width,height)
self.action = action
WIN = pygame.display.set_mode([WIDTH, HEIGHT], pygame.FULLSCREEN)
pygame.display.set_caption("first game")
player = Player()
animation_frames = {}
def load_animation(path, image_name, frame_duration, scale_width, scale_height):
global animation_frames
animation_frame_data = []
n = 0
for frame in frame_duration:
animation_frame_id = image_name + "_" + str(n)
img_loc = path + "/" + animation_frame_id + ".png"
animation_image = pygame.transform.scale(pygame.image.load(img_loc),
(scale_width, scale_height)).convert_alpha()
animation_image.convert()
animation_frames[animation_frame_id] = animation_image.copy()
for i in range(frame):
animation_frame_data.append(animation_frame_id)
n += 1
return animation_frame_data
def change_action(action_var, frame, new_value):
if action_var != new_value:
action_var = new_value
return action_var, frame
animation_database = {}
animation_database["idle"] = load_animation("assets", "darkpurpleknight_idle", [8, 8, 8, 8], player.main_char_width,
player.main_char_height)
animation_database["run_top"] = load_animation("assets", "darkpurpleknight_run_up", [8, 8, 8, 8, 8, 8],
player.main_char_width,
player.main_char_height)
animation_database["run_bot"] = load_animation("assets", "darkpurpleknight_run_down", [8, 8, 8, 8, 8, 8],
player.main_char_width, player.main_char_height)
animation_database["run"] = load_animation("assets", "darkpurpleknight_run", [8, 8, 8, 8, 8, 8], player.main_char_width,
player.main_char_height)
scroll = [0, 0]
blob_height = 17
blob_width = 25
blob_frame = 0
enemy_blob = Enemy(50, 10, 5, 1000 , 1000, 17, 25, "blob_idle",0,0)
animation_database["blob_idle"] = load_animation("assets", "blob_idle", [30, 30], blob_width * 4, blob_height * 4)
clock = pygame.time.Clock()
def collision(rectA, rectB):
if rectB.right < rectA.left:
# rectB est à gauche
return False
if rectB.bottom < rectA.top:
# rectB est au-dessus
return False
if rectB.left > rectA.right:
# rectB est à droite
return False
if rectB.top > rectA.bottom:
# rectB est en-dessous
return False
elif rectB.right > rectA.left:
# rectB est en collision avec la gauche
return True
elif rectB.bottom < rectA.top:
# rectB est en collision avec le haut
return True
elif rectB.left > rectA.right:
# rectB est en collision avec la droite
return True
elif rectB.top > rectA.bottom:
# rectB est en collision avec le bas
return True
bg_img = pygame.transform.scale(pygame.image.load("assets/DUNGEON_PISKEL_REMAKE_WITH_WALL_PROPS-1.png"),
(WIDTH * 2, HEIGHT * 2)).convert()
def animate_sprite(frame_counter, action):
global animation_database
frame_counter += 1
if frame_counter >= len(animation_database[action]):
frame_counter = 0
img_id = animation_database[action][frame_counter]
animated_image = animation_frames[img_id]
return animated_image, frame_counter
#def calculate_enemy_movement(enemy):
#calculate what x value needs to be added each frame(x velocity), and what y value needs to be added each frame(y velocity) to the enemy.x and enemy.y to walk
#towards the player.x and player.y.
#return x velocity and return y velocity
def draw_window(window):
global enemy_blob
window = window
global main_char_x, main_char_y
global player_frame, bg_img, blob_frame
global scroll
global slide
#add x velocity and y velocity to x and y pos of enemy each frame
#enemy_blob.x,enemy_blob.y += calculate_enemy_movement(enemy_blob)
window.blit(bg_img, (-WIDTH / 2 - scroll[0], -HEIGHT / 2 - scroll[1]))
blob_img, blob_frame = animate_sprite(blob_frame, enemy_blob.action)
window.blit(blob_img, (enemy_blob.x - scroll[0], enemy_blob.y - scroll[1]))
player_img, player_frame = animate_sprite(player_frame, player_action)
window.blit(pygame.transform.flip(player_img, player.player_flip, False), (player.player_rect.x, player.player_rect.y))
pygame.display.update()
borders = []
coliding = False
def set_borders(x, y, width, height):
border = pygame.Rect((x, y), (width, height))
return border
def check_colision(target, list):
is_coliding = []
for i in list:
is_coliding.append(collision(i, target))
return is_coliding
def main():
global scroll
global player_frame
global player_action
global player_flip
global player_rect, rect1, coliding
global VEL
global top_wall
global borders
global main_char_x, main_char_y
global moving_left, moving_right, moving_up, moving_down, idle
game_running = True
while game_running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == K_ESCAPE:
game_running = False
from start_menu import Open_launcher
Open_launcher()
sys.exit()
top_map_border = set_borders(-WIDTH / 2 - scroll[0], -HEIGHT / 2 + 250 - scroll[1], 5000, 1)
bot_map_border = set_borders(-WIDTH / 2 - scroll[0], HEIGHT + 355 - scroll[1], 5000, 1)
left_map_border = set_borders(-WIDTH / 2 + 160 - scroll[0], -HEIGHT / 2 - scroll[1], 1, 5000)
right_map_border = set_borders(WIDTH + 785 - scroll[0], -HEIGHT / 2 + 150 - scroll[1], 1, 5000)
borders = [top_map_border, bot_map_border, left_map_border, right_map_border]
enemies = []
is_coliding_with_borders = check_colision(player.player_rect, borders)
is_coliding_with_enemy = check_colision(player.player_rect, enemies)
top_coliding = is_coliding_with_borders[0]
bot_coliding = is_coliding_with_borders[1]
left_coliding = is_coliding_with_borders[2]
right_coliding = is_coliding_with_borders[3]
keys = pygame.key.get_pressed()
# EVERY MOVEMENT THAT USES "A"
if keys[pygame.K_a]:
# DIAGONAL TOP LEFT
if keys[pygame.K_w] and not keys[pygame.K_d]:
if not left_coliding and not top_coliding:
player.player_flip = True
player_action, player_frame = change_action(player_action, player_frame, "run")
scroll[0] -= diagonal_x_vel(player.VEL)
scroll[1] -= diagonal_y_vel(player.VEL)
elif left_coliding and not top_coliding:
player.player_flip = True
scroll[1] -= player.VEL
player_action, player_frame = change_action(player_action, player_frame, "run_top")
elif top_coliding and not left_coliding:
player.player_flip = True
scroll[0] -= player.VEL
player_action, player_frame = change_action(player_action, player_frame, "run")
elif left_coliding and top_coliding:
player.player_flip = False
player_action, player_frame = change_action(player_action, player_frame, "idle")
# DIAGONAL BOT LEFT
elif keys[pygame.K_s] and not keys[pygame.K_d]:
if not left_coliding and not bot_coliding:
player.player_flip = True
player_action, player_frame = change_action(player_action, player_frame, "run")
scroll[0] -= diagonal_x_vel(player.VEL)
scroll[1] += diagonal_y_vel(player.VEL)
elif left_coliding and not bot_coliding:
player.player_flip = True
scroll[1] += player.VEL
player_action, player_frame = change_action(player_action, player_frame, "run_bot")
elif not left_coliding and bot_coliding:
player.player_flip = True
scroll[0] -= player.VEL
player_action, player_frame = change_action(player_action, player_frame, "run")
elif left_coliding and bot_coliding:
player.player_flip = False
player_action, player_frame = change_action(player_action, player_frame, "idle")
# LEFT MOVEMENT
elif not keys[pygame.K_s] and not keys[pygame.K_d] and not keys[pygame.K_w]:
if not left_coliding:
player.player_flip = True
scroll[0] -= player.VEL
player_action, player_frame = change_action(player_action, player_frame, "run")
else:
player.player_flip = True
player_action, player_frame = change_action(player_action, player_frame, "run")
else:
player_action, player_frame = change_action(player_action, player_frame, "idle")
# EVERY MOVEMENT THAT USES "D"
if keys[pygame.K_d]:
# DIAGONAL TOP RIGHT
if keys[pygame.K_w] and not keys[pygame.K_a] and not keys[pygame.K_s]:
if not right_coliding and not top_coliding:
player.player_flip = False
player_action, player_frame = change_action(player_action, player_frame, "run")
scroll[0] += diagonal_x_vel(player.VEL)
scroll[1] -= diagonal_y_vel(player.VEL)
elif right_coliding and not top_coliding:
player.player_flip = False
scroll[1] -= player.VEL
player_action, player_frame = change_action(player_action, player_frame, "run_top")
elif top_coliding and not right_coliding:
player.player_flip = False
scroll[0] += player.VEL
player_action, player_frame = change_action(player_action, player_frame, "run")
elif right_coliding and top_coliding:
player.player_flip = False
player_action, player_frame = change_action(player_action, player_frame, "idle")
# DIAGONAL BOT RIGHT
elif keys[pygame.K_s] and not keys[pygame.K_a] and not keys[pygame.K_w]:
if not right_coliding and not bot_coliding:
player.player_flip = False
player_action, player_frame = change_action(player_action, player_frame, "run")
scroll[0] += diagonal_x_vel(player.VEL)
scroll[1] += diagonal_y_vel(player.VEL)
elif right_coliding and not bot_coliding:
player.player_flip = False
scroll[1] += player.VEL
player_action, player_frame = change_action(player_action, player_frame, "run_bot")
elif not right_coliding and bot_coliding:
player.player_flip = False
scroll[0] += player.VEL
player_action, player_frame = change_action(player_action, player_frame, "run")
elif right_coliding and bot_coliding:
player.player_flip = False
player_action, player_frame = change_action(player_action, player_frame, "idle")
# RIGHT MOVEMENT
elif not keys[pygame.K_s] and not keys[pygame.K_a] and not keys[pygame.K_w]:
if not right_coliding:
player.player_flip = False
scroll[0] += player.VEL
player_action, player_frame = change_action(player_action, player_frame, "run")
else:
player.player_flip = False
player_action, player_frame = change_action(player_action, player_frame, "run")
else:
player_action, player_frame = change_action(player_action, player_frame, "idle")
# EVERY MOVEMENT THAT USES "W"
if keys[pygame.K_w]:
# UP MOVEMENT
if not keys[pygame.K_d] and not keys[pygame.K_a]:
if not top_coliding:
player_action, player_frame = change_action(player_action, player_frame, "run_top")
scroll[1] -= player.VEL
else:
player_action, player_frame = change_action(player_action, player_frame, "run_top")
# EVERY MOVEMENT THAT USES "S"
if keys[pygame.K_s]:
# DOWN MOVEMENT
if not keys[pygame.K_d] and not keys[pygame.K_a]:
if not bot_coliding:
player_action, player_frame = change_action(player_action, player_frame, "run_bot")
scroll[1] += player.VEL
else:
player_action, player_frame = change_action(player_action, player_frame, "run_bot")
if not keys[pygame.K_a] and not keys[pygame.K_s] and not keys[pygame.K_d] and not keys[pygame.K_w]:
player_action, player_frame = change_action(player_action, player_frame, "idle")
draw_window(WIN)
Upvotes: 2
Views: 1055
Reputation: 210968
Compute the vector from the the enemy position to the the player position:
dx = player_x - enemy_x
dy = player_y - enemy_y
Compute the length of the vector (Euclidean distance):
dist = math.sqrt(dx*dx + dy*dy)
or
dist = math.hypot(dx, dy)
Normalize the vector (Unit vector). A normalized vector has a length of 1:
if dist > 0:
dx /= dist
dy /= dist
Move the enemy a certain distance in the direction of the vector. Make sure the moving distance is no greater than the remaining distance of the enemy to the player:
move_dist = min(enemy_vel, dist)
enemy_x += move_dist * dx
enemy_y += move_dist * dy
For a more sophisticated solution see How to make smooth movement in pygame
See also Follow target or mouse
Minimal example:
import pygame, math
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
player_x, player_y, player_vel = 100, 100, 5
enemy_x, enemy_y, enemy_vel = 300, 300, 3
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
player_x = max(10, min(390, player_x + player_vel * (keys[pygame.K_d] - keys[pygame.K_a])))
player_y = max(10, min(390, player_y + player_vel * (keys[pygame.K_s] - keys[pygame.K_w])))
dx = player_x - enemy_x
dy = player_y - enemy_y
dist = math.hypot(dx, dy)
if dist > 0:
enemy_x += min(enemy_vel, dist) * dx / dist
enemy_y += min(enemy_vel, dist) * dy / dist
window.fill(0)
pygame.draw.circle(window, (0, 128, 255), (player_x, player_y), 10)
pygame.draw.circle(window, (255, 32, 32), (enemy_x, enemy_y), 10)
pygame.display.flip()
pygame.quit()
exit()
For your particular code, the calculate_enemy_movement
function might look like this:
def calculate_enemy_movement(enemy_blob):
dx = player.player_rect.x - enemy_blob.x
dy = player.player_rect.y - enemy_blob.y
dist = math.hypot(dx, dy)
if dist > 0:
move_x = min(enemy_blob.speed, dist) * dx / dist
move_y = min(enemy_blob.speed, dist) * dy / dist
return move_x, move_y
return 0, 0
move_x, move_y = calculate_enemy_movement(enemy_blob)
enemy_blob.x += move_x
enemy_blob.y += move_y
Upvotes: 2