aqua959
aqua959

Reputation: 99

Object acting weird in vector movement

I currently have a game where I want the user to control an orb by dragging their mouse from the orb to an arbitrary position, which the program will respond by moving the orb in the opposite direction. The direction is fine, however, I noticed when using the program the orb moves less in the up-left direction than the down-right direction. I believe this might be due to the gravity slow I've implemented to the orb, however, I can't find exactly where it is causing the problem.

Main.py:

import pygame, sys, orb ...

def run_game():
    #Screen settings
    -snip-

    main_orb = Orb(screen)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.MOUSEBUTTONDOWN:
            main_orb.reset()
        elif event.type == pygame.MOUSEBUTTONUP:
            main_orb.release = True
            main_orb.calc_vector(mouse_pos)
        
    main_orb.update()
    main_orb.release_func(mouse_pos)
    main_orb.blitme()
    pygame.display.flip()

run_game()

orb.py

import pygame, math ...

class Orb():
    def __init__(self,screen):
        self.screen = screen

        # Get self image&rect and center position
        -snip-

        # Physics properties
        self.velocity = 0
        self.acceleration = 0
        self.vector = pygame.Vector2(0,-1)
        self.g = -30
        self.release = False

        # Update flag
        self.dont_update = False

    def reset(self):
        self.acceleration = 0
        self.velocity = 0
    def update(self):
        if self.velocity < 0 or self.dont_update == True:
            self.acceleration = 0
            self.velocity = 0
            self.dont_update = True
        else:
            self.acceleration = self.acceleration + self.g/60/60
            self.velocity = self.velocity + self.acceleration
            self.rect.x = self.rect.x + self.vector[0] * self.velocity
            self.rect.y = self.rect.y + self.vector[1] * self.velocity

    def calc_vector(self, mouse_pos):
        self.vector = pygame.Vector2(mouse_pos[0] - self.rect.centerx, 
                                         mouse_pos[1] - self.rect.centery)
        self.vector.rotate_ip(180)
        self.vector.normalize_ip()


    def release_func(self,mouse_pos):
        if self.release == True:
            self.acceleration = 2/1000 * math.hypot((self.rect.x-mouse_pos[0]),(self.rect.y-mouse_pos[1]))
            self.dont_update = False
            self.release = False

    def blitme(self):
        self.screen.blit(self.image,self.rect)

(P.S.) I know I use a lot of flags, so if there any suggestions of how to refactor the code it will be much appreciated.

Upvotes: 1

Views: 107

Answers (1)

Rabbid76
Rabbid76

Reputation: 210889

The issue is caused, because pygame.Rect stores integral coordinates:

The coordinates for Rect objects are all integers. [...]

The fraction component of self.vector[0] * self.velocity and self.vector[1] * self.velocity is lost when you do:

self.rect.x = self.rect.x + self.vector[0] * self.velocity
self.rect.y = self.rect.y + self.vector[1] * self.velocity

See also Pygame doesn't let me use float for rect.move, but I need it.


You have to do the calculations with floating point accuracy. Add an attribute self.pos of type pygame.Vector2 to the class:

class Orb():
    def __init__(self,screen):
        # [...]
        self.rect.center = self.screen_rect.center
        self.pos = pygame.Vector2(self.rect.center)

Increment the attribute in update and synchronize the rect attribute:

class Orb():
    # [...]

     def update(self):
        if self.velocity < 0 or self.dont_update == True:
            # [...]
        else:
            # [...]

            self.pos = self.pos + self.vector * self.velocity
            self.rect.center = round(self.pos.x), round(self.pos.y)

Complete class Orb:

class Orb():
    def __init__(self,screen):
        self.screen = screen

        # Get self image&rect and center position
        self.image = pygame.image.load('orb.bmp')
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()
        self.rect.center = self.screen_rect.center
        self.pos = pygame.Vector2(self.rect.center)

        # physics properties
        self.velocity = 0
        self.acceleration = 0
        self.vector = pygame.Vector2(0,-1)
        self.collided = False
        self.g = -20
        self.release = False

        # Update flag
        self.dont_update = False

    def reset(self):
        self.acceleration = 0
        self.velocity = 0
    def update(self):
        if self.velocity < 0 or self.dont_update == True:
            self.acceleration = 0
            self.velocity = 0
            self.dont_update = True
        else:
            self.acceleration = self.acceleration + self.g/60/60
            self.velocity = self.velocity + self.acceleration
            print(self.vector)
            print(self.velocity)
            self.pos = self.pos + self.vector * self.velocity
            self.rect.center = round(self.pos.x), round(self.pos.y)

    def calc_vector(self, mouse_pos):
        self.vector = pygame.Vector2(mouse_pos[0] - self.pos.x, mouse_pos[1] - self.pos.y)
        self.vector.rotate_ip(180)
        self.vector.normalize_ip()


    def blitme(self):
        self.screen.blit(self.image,self.rect)

    def release_func(self,mouse_pos):
        if self.release == True:
            self.acceleration = 2/1000 * math.hypot((self.pos.x-mouse_pos[0]),(self.pos.y-mouse_pos[1]))
            self.dont_update = False
            self.release = False

    def collisioncheck(self):
        if self.rect.bottom >= self.screen_rect.bottom:
            self.collided = True
        elif self.rect.top <= self.screen_rect.top:
            self.collided = False
        elif self.rect.left <= self.screen_rect.left:
            self.collided = True
        elif self.rect.right >= self.screen_rect.right:
            self.collided = True
        else:
            self.collided = False

Upvotes: 1

Related Questions