0
votes

I am doing some basic physics stuff with a sprite, this one being gravity with some extra drag so that the sprite in theory should come to rest on the top edge of the screen. The issue is however that as it gets closer to the ground the sprite vibrates faster and faster and ends up 'squeezing' over the boundary.

Whats a good way to stop this? I could pst code if required but there is nothing unusual about it, I just think it needs a workaround to reduce the velocity as it gets closer to be stationary.

import pygame, math, random

pygame.init() 

class Circle(pygame.sprite.Sprite):
    def __init__(self, screen):
        pygame.sprite.Sprite.__init__(self)
        self.screen = screen
        self.dx = 0
        self.dy = 0
        self.ddx = 0
        self.ddx = 0
        self.forceX = 0
        self.forceY = 0
        self.x = random.randrange(20,self.screen.get_width())
        self.y = self.screen.get_height()/2
        self.radius = random.randrange(5,30)
        self.image = pygame.Surface((self.radius*2,self.radius*2))
        self.image.set_colorkey((0,0,0))
        self.image.set_alpha(120)
        self.mass = self.radius/15.0
        pygame.draw.circle(self.image, (175,255,0), (self.radius,self.radius), self.radius)
        self.image = self.image.convert_alpha()
        self.rect = self.image.get_rect()
        self.rect.center = (self.x, self.y)

    def update(self):
        self.calcPos()
        self.checkBounds()
        self.rect.centerx = self.x
        self.rect.centery = self.y
        self.forceX = 0
        self.forceY = 0


    def calcPos(self): 
        self.ddx = self.forceX
        self.ddy = self.forceY
        self.dy += self.ddy
        self.dx += self.ddx
        self.x += self.dx
        self.y += self.dy 

    def applyForce(self, force):
        self.forceX += force[0]/self.mass
        self.forceY += force[1]/self.mass

    def checkBounds(self):
        if self.y > self.screen.get_height():
            self.dy *= -1.05
        if self.x > self.screen.get_width(): 
            self.dx *= -1.05
        if self.y < 0: 
            self.dy *= -1.05
        if self.x < 0: 
           self.dx *= -1.05

def main():
    screen = pygame.display.set_mode((600,400))
    background = pygame.Surface((screen.get_size()))
    background.fill((150,150,150)) 
    background = background.convert()

    circleGRP = pygame.sprite.Group() #Add balls
    for x in range(10):
        circleGRP.add(Circle(screen))

    wind = (1, 0)
    gravity = (0, 1.0)

    clock = pygame.time.Clock()
    mainLoop = True

    while mainLoop: 
        clock.tick(30) #Clock
        for event in pygame.event.get(): #Key events
            if event.type == pygame.QUIT:
                mainLoop = False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    mainLoop = False
            elif event.type == pygame.MOUSEBUTTONDOWN: #Add wind
                if pygame.mouse.get_pressed()[0]:
                    for circle in circleGRP:
                        circle.applyForce(wind)

#----------------------------------------------------------------------------                   
        for circle in circleGRP: #Add gravity
            gravity = (0, 1.0)
            gravity = tuple([each * circle.mass for each in gravity])
            circle.applyForce(gravity)

            circleX = circle.dx * -1 #Add friction
            circleY = circle.dy * -1
            friction = (circleX/80* circle.mass* (circle.radius/5), circleY/80* circle.mass* (circle.radius/5))
            circle.applyForce(friction)

 #----------------------------------------------------------------------------    
        circleGRP.update()  
        screen.blit(background, (0,0))
        circleGRP.draw(screen)
        pygame.display.flip()

    pygame.quit()

if __name__ == "__main__":
    main()    
1

1 Answers

0
votes

I really would need to see the code, but if what i understand what you want properly you could have the velovity based on the distance to the top, for example if y is the location of your sprite:

ydir=y/50
y+=int(ydir)

edit:

def checkBounds(self):
    if self.y > self.screen.get_height():
        self.dy *= -1.05
        self.y=self.screen.get_height()

the last line is the only necessary change, every loop would push the ball a little bit past it's bounds, which caused problems when the ball had no upward force, this makes it so the ball cannot pass its lower bound, the same thing could be done for the others.