pygame - Enemy flies off screen when collision with ground? - python

first time poster here.
Ill try to keep this as simple as possible. I am trying to make a game in pygame but it seems my collision detection is acting up. I have a player class that detects whether or not the player is colliding with the ground or any other objects in the environment list, and I have an enemy class that collides with anything in the same list. The enemies work out what direction they need to travel in in order to try and hit the player. There is gravity which is very likely to play a major part in the problem.
The problem is that when the 'flies' are placed in and fall to the ground, they immediately jump off screen, even though their X and Y values seem (according to logs of items on the screen) to not move at all?
As clarification, the two 'flies' are placed into a secondary list to allow for collision detection. Also, as a side note, the 'glitch' doesn't occur if there is no left and right collide detection... Thanks to anyone who can provide help :)
def collisions():
#Detection Working as intended
for fly in Fly.List:
fly_proj = pygame.sprite.spritecollide(fly, Projectile.List, True)
if len(fly_proj) > 0:
for hit in fly_proj:
fly.health -= 100
X = pygame.sprite.spritecollide(fly, current_level.object_list, False)
if len(X) == 0: #Gravity here:
if (fly.vspeed == 0):
fly.vspeed = 1
print("Gravity")
else:
fly.vspeed += 0.35
if len(X) > 1:
for hit in X:
if fly.vspeed > 0:
fly.rect.bottom = hit.rect.top +1
fly.vspeed = 0
elif fly.vspeed < 0:
fly.rect.top = hit.rect.bottom -1
elif fly.hspeed > 0:
fly.rect.right = hit.rect.left
fly.hspeed = 0
elif fly.hspeed < 0:
fly.rect.left = hit.rect.right
fly.hspeed = 0
print(len(X),framecounter, fly.vspeed, fly.hspeed)
#Kill fly if health reaches 0
if fly.health <= 0:
fly.destroy(Fly)
#Detect where the player is
if window_rect.contains(fly.rect):
fly.playersnapshot = player.rect.x
if fly.rect.x - player.rect.x >= -10:
#print("LEFTING")
fly.hspeed = -2
if fly.rect.x - player.rect.x <= 10:
fly.hspeed = 2
fly.truefalse = True
event = None
fly.rect.y += fly.vspeed
fly.rect.x += fly.hspeed

I think your if/else is incorrect.
Probably when fly touch ground you set vspeed to zero and then if/else checks hspeed and it use ground left/right to change fly left/right.
I know one method.
move fly vertically
check collision and use if/else with vspeed
move fly horizontally
check collision and use if/else with hspeed
--
EDIT:
# move horizontally only
fly.rect.x += fly.hspeed
X = pygame.sprite.spritecollide( ... )
for hit in X:
if fly.hspeed > 0:
fly.rect.right = hit.rect.left
else:
fly.rect.left = hit.rect.right
# move vertically only
fly.rect.y += fly.vspeed
X = pygame.sprite.spritecollide( ... )
for hit in X:
if fly.vspeed > 0:
fly.rect.bottom = hit.rect.top
else:
fly.rect.top = hit.rect.bottom
# on_ground = True
I found this method in source code of "Platform Jumper" on ProgramArcadeGames.com
see page with: platform_jumper.py

Related

Multiprocessing with python objects

I've been doing a Genetic Algorithm to solve simple 2d platformer game levels. Now I want to add multiprocessing to execute the "update" of my population in parallel, yet I haven't been able to make it work as all working examples I've found are with simple arrays, even those with shared memory, and I need to work with nested objects and shared memory.
Right now I have been trying the following code:
# divide players for processes
splits = np.array_split(groups.players_group, settings.PROCESSES)
# create processes
processes = []
for i in range(settings.PROCESSES):
p = mp.Process(target=update_players, args=(splits[i],))
processes.append(p)
processes[i].start()
# join processes (wait for 'em to finish)
for p in processes:
p.join()
Now, errors have not been clear, as now they just seem to have issues with finding objects (logs say that there are empty objects which don't have some required property for the update process, when they in fact should have it. So I am starting to believe the issue is with shared memory, which I don't know how to apply to python objects).
[EDIT]
I've tried wrapping my object lists with the multiprocessing.Array(), yet it gives the following error:
TypeError: unsupported operand type(s) for *: 'type' and 'int'
Also... here is the "update players" function:
def update_players(players, ret_queue):
for p in players:
p.update()
ret_queue.put(players)
and as you can see, it calls Player.Update(), which is insanely long but here it is in case you want to check it out:
def update(self):
if not self.is_dead and not self.reached_goal and not self.finished:
# act according to brain if necessary
if self.is_ai:
# reset movements
self.releaseLeft()
self.releaseRight()
if self.brain_step >= len(self.brain.instructions):
if not self.reached_goal and not self.isMoving():
self.finished = True
else:
self.executeNextBrainStep()
self.rect.y += self.y_spd
self.y_spd += self.gravity
# check if player is colliding with floor
for tile in groups.floor_tiles:
if helpers.rectsColliding(self.rect, tile.rect):
# check vertical collision
if self.rect.getCenterY() < tile.rect.y: # from top
self.y_spd = 0
self.rect.y = tile.rect.y - self.rect.height + 1
self.is_jumping = False
# from bottom
elif self.rect.getCenterY() > (tile.rect.y + tile.rect.height):
self.y_spd = 0
self.rect.y = tile.rect.y + tile.rect.height
# check horizontal collision
# should be within same vertical space
if self.rect.getCenterY() >= (tile.rect.getCenterY() - tile.rect.height/3):
if self.rect.getCenterY() <= (tile.rect.getCenterY() + tile.rect.height/3):
# now we can check the horizontal collision
if self.rect.getCenterX() < tile.rect.x: # from left
self.right = 0
self.rect.x = tile.rect.x - self.rect.width
# from right
elif self.rect.getCenterX() > (tile.rect.x + tile.rect.width):
self.left = 0
self.rect.x = tile.rect.x + tile.rect.width
self.dir = (self.right - self.left)
self.rect.x += self.dir * self.walk_spd
# check if dead
if self.rect.x > settings.SCR_W or (self.rect.x + self.rect.width) < 0:
self.is_dead = True
self.finished = True
if self.rect.y > settings.SCR_H or (self.rect.y + self.rect.height) < 0:
self.is_dead = True
self.finished = True
# if optimizing, check if should stop
if settings.OPTIMIZATION_FITNESS:
self_dist = helpers.dist_modular(self.rect.x, settings.goal.rect.x, self.rect.y, settings.goal.rect.y)
best_dist = helpers.dist_modular(settings.BEST_X, settings.goal.rect.x, settings.BEST_Y, settings.goal.rect.y)
if self_dist < best_dist:
self.finished = True
self.reached_goal = True
# check if reached goal
if helpers.objectsColliding(self, settings.goal):
self.reached_goal = True
self.finished = True
And in case any of you wants to check out the repo, here it is: https://github.com/santyarellano/GeneticLevelTester

(Pygame) What is wrong with this function?

So, i have been working on a snake game made in Pygame. So far, everything is fine, except for one problem: When the snake eats the fruit, the fruit (that randomly spawns) sometimes appears inside the snake's body. So, to avoid this, i made this function:
def random_fruit(body_pos):
global general_fruit_x, general_fruit_y # Fruit rectangle coordinates
while True:
general_fruit_x = randrange(window[0] // snake.w) * snake.w # (Snake is a pygame.Rect)
general_fruit_y = randrange(window[1] // snake.h) * snake.h
if len(list(filter(lambda z: body_pos == (general_fruit_x, general_fruit_y), body_pos))) > 0:
continue # If the spawning position of the fruit is the same as the snake's body, we continue the loop
else:
break # If not, we are done
set_obj_coordinates(general_fruit, general_fruit_x, general_fruit_y) # set fruit random position
And implemented it in the main game loop:
if fruit_eated:
random_ind1 = random_ind2
snake_len += 1
apple_sound.play()
random_fruit(snake_pos) # snake_pos is a list of tuples with all snake's body coordinates
for m in range(3):
snake_imgs[random_ind1][m] = img("snake_" + snake_colors[random_ind1] + str(m + 1)) # Reset snake image
random_ind2 = randint(0, 3)
if x_move < 0:
rotate_imgs(90, random_ind1)
if x_move > 0:
rotate_imgs(-90, random_ind1)
if y_move > 0:
rotate_imgs(180, random_ind1)
if y_move < 0:
pass
But it seems that the random_fruit function ignores the condition of the snake's body.
Here is the complete code: https://github.com/n4tm/PySnake/tree/main/snake
You have to check if any position of the body is equal to the new random position of the fruit:
if len(list(filter(lambda z: body_pos == (general_fruit_x, general_fruit_y), body_pos))) > 0:`
if any(pos == (general_fruit_x, general_fruit_y) for pos in body_pos):
random_fruit function:
def random_fruit(body_pos):
global general_fruit_x, general_fruit_y
while True:
general_fruit_x = randrange(window[0] // snake.w) * snake.w
general_fruit_y = randrange(window[1] // snake.h) * snake.h
if not any(pos == (general_fruit_x, general_fruit_y) for pos in body_pos):
break
set_obj_coordinates(general_fruit, general_fruit_x, general_fruit_y)

Pygame: Can't draw anythin on screen after "pygame.Surface.fill()"

I'm trying to make a chess game, and I've encountered a problem:
I'm trying to update the display for every 'tick' by using pygame.Surface.fill(black_sc).
But as a result, it seems I'm not able to draw anything on top of the now black screen:
#GAME LOOP
while True:
screen.fill(black_sc)
#SQUARE DRAWING LOOP
s_draw_loop()
#DRAW THE PIECES
draw_pieces()
m_pos = pygame.mouse.get_pos()
for x in pygame.event.get():
if x.type == pygame.QUIT or pygame.key.get_pressed()[pygame.K_ESCAPE] == True:
exit()
if x.type == pygame.MOUSEBUTTONDOWN:
for space in range(len(rect_database)):
if rect_database[space].collidepoint(m_pos) == True:
print(space)
pygame.display.flip()
Here's the s_draw_loop():
class s_draw_loop():
s_posx = 0
s_posy = 0
s_w = size[0] / 8
s_h = size[1] / 8
row = 0
i = 0
while i < 64:
if i % 2 == 0:
if row % 2 == 0:
current_rect = pygame.Rect(s_posx, s_posy, s_w, s_h)
screen.fill(white, current_rect)
else:
current_rect = pygame.Rect(s_posx, s_posy, s_w, s_h)
screen.fill(black,current_rect)
s_posx += s_w
i += 1
else:
if row % 2 == 0:
current_rect = pygame.Rect(s_posx, s_posy, s_w, s_h)
screen.fill(black,current_rect)
else:
current_rect = pygame.Rect(s_posx, s_posy, s_w, s_h)
screen.fill(white,current_rect)
s_posx += s_w
i += 1
if i % 8 == 0:
s_posx = 0
s_posy += s_h
row += 1
I'll spare you from the entire function drawing the pieces, unless you need it, but it's basically just using pygame.Surface.blit() to draw the pictures onto the display.
I've tried including the 'drawing functions' into the game-loop itself, but it seems to make no difference.
Here's the output when including 'screen.fill(black_sc)'
And here it is when commenting 'screen.fill(black_sc)' out
It's because s_draw_loop is a class, not a function.
The drawing code inside s_draw_loop is only executed once, before entering the game loop, when the python runtime reads the code of the class.
Calling s_draw_loop() inside your game loop does actually nothing (except creating an useless instace of that class that does nothing).
Simply change
class s_draw_loop():
...
to
def s_draw_loop():
...
so the code inside s_draw_loop gets executed every frame.

Cant figure out how to kill a checker when it is jumped over

I am trying to make a game of checkers in pygame, and can't think of a way to do the logic for killing a checker once I jump over it. If you need any more information let me know. Here is the function in the checker sprite class:
# Determine if move is valid, and if so snap to the grid
# and tell the game logic that there is a checker there
def snap_to_grid(self):
global turn
x, y = self.rect.topleft
coord_x = round(x / square_size)
coord_y = round(y / square_size)
new_x = coord_x * square_size + 5
new_y = coord_y * square_size + 5
# get all valid moves from the starting position
valid_moves = self.valid_moves()
# check if it is valid, or if it is going back to the same spot. If either is true,
# then reset. If not, move to that spot.
if [new_x, new_y] == self.location or not [coord_x, coord_y] in valid_moves:
self.set_pos(self.location, 'topleft')
else:
# move to new position
self.set_pos((new_x, new_y), 'topleft')
# tell game data that the checker moved from the old square
board[self.coords[1]][self.coords[0]] = None
# the next few lines are to determine if the checker jumped or not
old_x, old_y = self.coords
self.location = [new_x, new_y]
self.coords = [int(self.location[0]//square_size), int(self.location[1]//square_size)]
distance = abs((old_x - self.coords[0])^2 + (old_y - self.coords[1])*2)
# check if checker jumped
if not distance == 1 and not distance == 5:
print("jumped")
"""
this code here should trigger when a jump happens.
I need to kill the checker it jumped over
"""
# tell game data the checker moved to the new square
board[self.coords[1]][self.coords[0]] = self
# set to be the next turn
if self.color == white:
turn = dark_gray
else:
turn = white
ok I think I figured it out, if there is a better way let me know (the distances in the if statements are the output if it is a jump in a specific direction):
old_x, old_y = self.coords
self.location = [new_x, new_y]
self.coords = [int(self.location[0]//square_size), int(self.location[1]//square_size)]
distance = (old_x - self.coords[0])^2 + (old_y - self.coords[1])*2
# check if checker jumped top right
if distance == -8:
board[self.coords[1]+1][self.coords[0]-1].kill()
# check if checker jumped top left
elif distance == 4:
board[self.coords[1]+1][self.coords[0]+1].kill()
# check if checker jumped bottom right
elif distance == 0:
board[self.coords[1]-1][self.coords[0]-1].kill()
# check if checker jumped bottom left
elif distance == -4:
board[self.coords[1]-1][self.coords[0]+1].kill()

How do I make two characters not run through each other when colliding in PYGAME?

I'm creating my first game and I am having some trouble handling collisions. What I have is a 2 player game played on the same keyboard, awsd and updownleftright controls. When the two players collide, I want them to not be able to move through each other. I'm having trouble figuring that out.
player_one_pos = [300,310]
player_two_pos = [600,310]
def detect_collision(player_one_pos, player_two_pos):
p1_x = player_one_pos[0]
p1_y = player_one_pos[1]
p2_x = player_two_pos[0]
p2_y = player_two_pos[1]
if (p1_x + player_width/2) == (p2_x - player_width/2):
return True
return False
if detect_collision(player_one_pos, player_two_pos):
## players collide, can't go through each other
I was able to get it done by adding an and statement. This fix should suffice for now.
if key_pressed[pygame.K_RIGHT] and x < (goal_right[0] - player_width) and (x != a - player_width):
x += speed_of_travel
if key_pressed[pygame.K_a] and a > (goal_left[0] + goal_width) and (a != x + player_width):
a -= speed_of_travel

Categories