python - Dealing with Sprites and Collisions Using Pygame -


i learning python using pygame , working on involves sprites , collisions. i've looked @ examples still don't quite understand it. attempting able add sprites(a ball) when user presses "=" key , able remove last sprite added when pressing "-". not able remove last one, have been able remove of them.

so far have been able add balls window , have them bounce off walls , 1 another(sort of). when 2 balls collide, don't touch yet bounce off. balls stuck , won't move , balls bounce off frame aren't suppose to.

its first time working sprite groups , appreciate help/guidance making work smoothly.thanks.

the code:

ball.py

import pygame pygame.locals import *   class ball(pygame.sprite.sprite):     def __init__(self, x, y, vx, vy):          super().__init__();         self.image = pygame.image.load("ball.png").convert()           self.image.set_colorkey(pygame.color(0, 0, 0))          self.rect = self.image.get_rect()          self.rect.x = x         self.rect.y = y         self.vx = vx         self.vy = vy      def draw(self, screen):         screen.blit(self.image, (self.rect.x, self.rect.y))      def move(self, screen, balls):         l_collide = self.rect.x + self.image.get_width() + self.vx > screen.get_width()         r_collide = self.rect.x + self.vx < 0         t_collide = self.rect.y + self.vy < 0         b_collide = self.rect.y + self.image.get_height() + self.vy > screen.get_height()          = pygame.sprite.spritecollide(self, balls, false, false)          if len(a) > 1:             self.vx *= -1             self.vy *= -1            if l_collide or r_collide:             self.vx *= -1           if t_collide or b_collide:             self.vy *= -1          self.rect.x += self.vx         self.rect.y += self.vy 

ball_animation.py

import pygame import sys import random import math pygame.locals import * ball.ball import ball random import randint  def ball_list(num):     ball_list = pygame.sprite.group()      x in range(num):         rand_x = random.randint(0,400)         rand_y = random.randint(0,400)         vx = 4         vy = 5          ball_list.add(ball(rand_x, rand_y, vx, vy))      return ball_list  def main():     pygame.init()      fps = 30     fps_clock = pygame.time.clock()      # color list     black = pygame.color(0, 0, 0)      # code create initial window     window_size = (500, 500)     screen = pygame.display.set_mode(window_size)      # set title of window     pygame.display.set_caption("bouncing ball animation")      # change initial background color white     screen.fill(black)      balls = ball_list(0)      while true:  # <--- main game loop         event in pygame.event.get():             if event.type == quit:  # quit event exit game                 pygame.quit()                 sys.exit()             if event.type == keydown:                 if event.key == k_equals:                     balls.add(ball(randint(0,400),randint(0,400), 4,5))                 if event.key == k_minus:                     try:                         balls.remove()                     except indexerror:                         print('there no balls take!')             screen.fill(black)         x in balls:             x.move(screen,balls)             x.draw(screen)          pygame.display.update()  # update display when events have been processed         fps_clock.tick(fps)  if __name__ == "__main__":     main() 

removing sprites on press

the problem sprite.group.remove(sprites) wants specify sprites should remove. sprites here should sprite/list of sprites want remove group. means remove last ball added on key press need keep list of ball sprites , pop() added item it, , use result of pop() sprite remove group. sprite.group has .sprites() method returns list of sprites in group, in order added. list generated group , not interface it, doing things list won't affect group. can still use last added sprite. here looks like:

elif event.key == k_0:     try:         sprite_list = balls.sprites()         to_remove = sprite_list[-1] # last element of list         balls.remove(to_remove)     except indexerror:         print('there no balls take!') 

collisions

so bit more involved , not simple fix in code. understand problem is, @ collision velocity adjustments doing screen border case.

l_collide = self.rect.x + self.image.get_width() + self.vx > screen.get_width() r_collide = self.rect.x + self.vx < 0 t_collide = self.rect.y + self.vy < 0 b_collide = self.rect.y + self.image.get_height() + self.vy > screen.get_height()  #################  if l_collide or r_collide:     self.vx *= -1  if t_collide or b_collide:     self.vy *= -1 

consider single time-step in code. check see if sprite sitting on edge of boundaries any amount. if hanging over, reverse velocity. there case edge checking trouble. if self.vx less difference between current position x , boundary of x dimension, reverse speed, travel self.vx back towards boundary, not make past. in next time-step, see still on boundary, , program again reverse self.vx, sending away boundary. in case bound , forth each time-step self.vx. wouldn't happen in code, except when spawn new ball sprite on boundary further self.vx or self.vy ball. can remedied making sure don't spawn balls off edges, or better yet, reversing velocity if need to.

if (l_collide , self.vx>0) or (r_collide , self.vx<0):     self.vx *= -1   if (t_collide , self.vy<0) or (b_collide , self.vy>0):     self.vy *= -1 

notice here reverse velocity if on edge and velocity headed deeper in direction. sprites have 2 options, boundaries:

  • only initiate new ball in empty space cannot collide.
  • implement way calculate correct velocity adjustment , apply if velocity headed in opposite direction.

from read in documentation, sprite.group looks meant checking if sprites overlapping, , not physics simulation. recommend doing research on 2d physics simulation nice conceptualization of information should want communicate between objects. i'm sure there nice tutorials out there.

finally, address other question why colliding when don't appear touching. sprite.spritecollide returning sprites have rectangles intersect. if ball.png color keyed transparency, not affect rect of sprite. pygame appears have functionality implemented designed handle problem in collided keyword of sprite.spritecollide:

pygame.sprite.spritecollide()

  • find sprites in group intersect sprite.

spritecollide(sprite, group, dokill, collided = none) -> sprite_list

the collided argument callback function used calculate if 2 sprites >are colliding. should take 2 sprites values, , return bool value >indicating if colliding. if collided not passed, sprites must >have “rect” value, rectangle of sprite area, >used calculate collision. collided callables:

  • collide_rect
  • collide_rect_ratio
  • collide_circle
  • collide_circle_ratio
  • collide_mask

that's pygame documentation. documentation collide_circle function states sprite should have radius attribute, or else 1 calculated fit entire rectangle inside circle. such, in ball.__init__ function recommend adding:

self.radius = self.rect.width/2  

this make collide_circle use radius approximates ball image, assuming centered , circular , occupies entire image. next, must add collision specification collision check changing:

a = pygame.sprite.spritecollide(self, balls, false, false) 

to

a = pygame.sprite.spritecollide(self, balls, false, pygame.sprite.collide_circle) 

if solve problem of not spawning new ball objects inside each other, should work nicely. if can't them spawn inside each other, think different data-structure or different way of collision checking results want. best of luck!


Comments

Popular posts from this blog

shopping cart - Page redirect not working PHP -

php - How to modify a menu to show sub-menus -

python - Installing PyDev in eclipse is failed -