Python Forum
How to move an object over time rather than instantly?
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to move an object over time rather than instantly?
#1
I'll post more code if needed, but I'm a bit confused. I'm currently using pygame for this.

I use randint() to spawn objects at random intervals, and then after that, move them in a random direction (up, down, left or right) at a different random interval. My problem is that with how I've coded it, the objects snap/teleport whatever the distance is, rather than move there at a fast speed that I set. I can't seem to figure out how to make them move gradually to a random destination.

# directions for the enemy object to move in
directions = [(1, 0), (-1, 0), (0, 1), (0, -1)]

# spawn an enemy in a random spot
def spawn_enemy_object():
    x = randint(0, window.get_width() - object_size)
    y = randint(0, window.get_height() - object_size)
    enemy_direction = choice(directions) 
    return {
        'rect': pygame.Rect(x, y, object_size, object_size),
        'enemy_direction': enemy_direction, 
        'enemy_move_time': pygame.time.get_ticks() + randint(5000, 8000),
        'enemy_moves_left': 2, 
        'is_enemy_moving': True,  # start moving
        'speed': 50  # speed of the enemy rectangle object
    }

        elif event.type == move_event:
            current_time = pygame.time.get_ticks()
            for obj in objects[:]:  
                if current_time >= obj['enemy_move_time']:
                    if obj['is_enemy_moving']:
                        if obj['enemy_moves_left'] > 0:
                            
                            obj['rect'].x += obj['enemy_direction'][0] * obj['speed']
                            obj['rect'].y += obj['enemy_direction'][1] * obj['speed']
                            obj['enemy_move_time'] = current_time + randint(5000, 9000)
                            obj['enemy_moves_left'] -= 1
                        else:
This was my final attempt. My previous attempt was much more basic, but there was no difference unfortunately.
Reply
#2
I don't know what the rest of your code looks like but, here is a basic example of pygame animation.
A red ball bouncing off of the walls.

Added comments and added ability to increase or decrease speed using keypad + or minus key.
Speed will increase or decrease after bounce. The r key will reset default speed.

import pygame
from random import choice

pygame.init()
pygame.font.init()

font = pygame.font.SysFont(None, 28)

screen_width, screen_height = (1280, 740)
screen = pygame.display.set_mode((1280,740))

clock = pygame.time.Clock()

running = True

# Create a sprite group to update sprites on the screen
allsprites = pygame.sprite.Group()
text_sprites = pygame.sprite.Group()
instruct_sprites = pygame.sprite.Group()
ball_sprites = pygame.sprite.Group()

class Ball(pygame.sprite.Sprite):
    ''' Mob class creates a red ball '''
    def __init__(self, screen_width, screen_height):
        pygame.sprite.Sprite.__init__(self)

        # Create a surface
        self.image = pygame.Surface((50,50))
        self.image.fill('black')
        self.image.set_colorkey('black')

        # Get surface rect
        self.rect = self.image.get_rect()

        # Starting position - center of screen
        self.rect.centerx = screen_width/2
        self.rect.centery = screen_height/2

        # Set instance variables
        self.screen_width = screen_width
        self.screen_height = screen_height

        # Size of the ball
        size = (25, 25)

        # Radius of ball
        radius = 25

        # Draw the ball on the surface
        pygame.draw.circle(self.image, 'red', size, radius)

        # Set a speed
        self.speed = 8

        # Set x and y speed
        self.speed_x = self.speed
        self.speed_y = self.speed


        # Blit image to screen
        screen.blit(self.image, self.rect)

        # Add ball to sprite group
        allsprites.add(self)
        ball_sprites.add(self)

    def update(self):
        ''' Method for updating ball position '''

        # If ball reaches screen with - minus speed to go in other direction towards 0
        if self.rect.right >= self.screen_width:
            self.speed_x = -self.speed

        # If ball reaches 0 position add positive speed for ball go in other direction towards screen width
        if self.rect.left <= 0:
            self.speed_x = self.speed

        # If ball reaches top of screen (0 position) add positive speed to go down
        if self.rect.top <= 0:
            self.speed_y = self.speed

        # If ball reaches screen height, add negative speed to go up toward 0 position
        if self.rect.bottom >= self.screen_height:
            self.speed_y = -self.speed

        # Update ball position
        self.rect.centerx += self.speed_x
        self.rect.centery += self.speed_y


class MyText(pygame.sprite.Sprite):
    def __init__(self, text, x, y, width, height):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((width, height))
        self.image.fill('#555555')
        self.rect = self.image.get_rect()
        self.rect.left = x
        self.rect.top = y

        self.msg = text
        self.text = font.render(self.msg, True, 'white')

        self.image.blit(self.text, (10,5))

        
# Create a ball object
ball = Ball(screen_width, screen_height)

# Create the speed text object
speedtext = MyText(f'Speed: {ball.speed}', 0, 0, 200, 30)

# Create the instructions text object
text = 'keypad + = increase speed, keypad - = decrease speed, key r = reset speed'
instructions = MyText(text, screen_width-720, screen_height-30, 720, 30)

# Add to sprite groups
allsprites.add(speedtext)
text_sprites.add(speedtext)

allsprites.add(instructions)
instruct_sprites.add(instructions)




while running:
    screen.fill('black')
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

        # If event type is key press
        if event.type == pygame.KEYDOWN:

            # Get the key pressed. + plus key increase speed by 1
            # Minus key decrease speed by 1
            # r key reset default speed
            if event.key == pygame.K_KP_PLUS:
                ball.speed += 1
                if ball.speed >= 25:
                    ball.speed = 25

            if event.key == pygame.K_KP_MINUS:
                ball.speed -= 1
                if ball.speed <= 2:
                    ball.speed = 2

            if event.key == pygame.K_r:
                ball.speed = 8

            # This is to empty the sprite groups of the object speed text
            # so we are not piling on sprite after sprite after sprite
            text_sprites.empty()
            allsprites.remove(speedtext)

            # Create new text with updated speed value and add to sprite groups
            speedtext = MyText(f'Speed: {ball.speed}', 0, 0, 200, 30)
            text_sprites.add(speedtext)
            allsprites.add(speedtext)
            
    # Detect collissions with the text boxes
    if pygame.sprite.groupcollide(ball_sprites, text_sprites, False, False):
        ball.speed_y = ball.speed

    if pygame.sprite.groupcollide(ball_sprites, instruct_sprites, False, False):
        ball.speed_y = -ball.speed

    # Update and draw all sprites
    allsprites.update()
    allsprites.draw(screen)
    
    pygame.display.update()

    # Set a frame rate. If you don't have this the speed is very fast
    clock.tick(60)

pygame.quit()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags
Download my project scripts


Reply
#3
Just wanted to add one more way that doesn't use sprites. I prefer sprites as it makes collision detection easier.

import pygame
from pygame import gfxdraw


running = True
window_size = (1280,740)
screen = pygame.display.set_mode(window_size)
clock = pygame.time.Clock()
fps = 60

screen_width, screen_height = screen.get_size()

# Colors
BLACK = pygame.Color(0, 0, 0)
WHITE = pygame.Color(255, 255, 255)
RED = pygame.Color(255, 0, 0)
GREEN = pygame.Color(0, 255, 0)
BLUE = pygame.Color(0, 0, 255)
ORANGE = pygame.Color(255, 127, 0)
PURPLE = pygame.Color(255, 0, 255)
CYAN = pygame.Color(0, 255, 255)

pygame.init()


class Ball:
    ''' Ball class will draw a ball object '''
    def __init__(self, screen):
        # Set starting instance variables
        self.x = screen_width/2
        self.y = screen_height/2
        self.radius = 25
        self.speed = 8
        self.speed_x = self.speed
        self.speed_y = self.speed
        self.color = GREEN
        

    def update(self):
        ''' Method for updating ball position. Also chamges ball colors on wall impact '''
        if self.x <= 0 + (self.radius/2+self.radius*0.5):
            self.speed_x = self.speed
            self.color = RED

        if self.x >= screen_width-(self.radius/2+self.radius*0.5):
            self.speed_x = -self.speed
            self.color = CYAN

        if self.y <= 0 + (self.radius/2+self.radius*0.5):
            self.speed_y = self.speed
            self.color = WHITE

        if self.y >= screen_height-(self.radius/2+self.radius*0.5):
            self.speed_y = -self.speed
            self.color = GREEN

        

        self.x += self.speed_x
        self.y += self.speed_y
        
        

    def draw(self, screen):
        ''' Method for drawing ball on the screen '''
        self.ball = pygame.draw.circle(screen, self.color, (self.x, self.y), self.radius)



ball = Ball(screen) 


while running:
    screen.fill(BLACK)


    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # Update position and draw ball
    ball.update()
    ball.draw(screen)


    pygame.display.update()
    clock.tick(fps)

pygame.quit()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags
Download my project scripts


Reply
#4
(Oct-21-2024, 07:38 AM)menator01 Wrote: I don't know what the rest of your code looks like but, here is a basic example of pygame animation.
A red ball bouncing off of the walls.

Added comments and added ability to increase or decrease speed using keypad + or minus key.
Speed will increase or decrease after bounce. The r key will reset default speed.

import pygame
from random import choice

pygame.init()
pygame.font.init()

font = pygame.font.SysFont(None, 28)

screen_width, screen_height = (1280, 740)
screen = pygame.display.set_mode((1280,740))

clock = pygame.time.Clock()

running = True

# Create a sprite group to update sprites on the screen
allsprites = pygame.sprite.Group()
text_sprites = pygame.sprite.Group()
instruct_sprites = pygame.sprite.Group()
ball_sprites = pygame.sprite.Group()

class Ball(pygame.sprite.Sprite):
    ''' Mob class creates a red ball '''
    def __init__(self, screen_width, screen_height):
        pygame.sprite.Sprite.__init__(self)

        # Create a surface
        self.image = pygame.Surface((50,50))
        self.image.fill('black')
        self.image.set_colorkey('black')

        # Get surface rect
        self.rect = self.image.get_rect()

        # Starting position - center of screen
        self.rect.centerx = screen_width/2
        self.rect.centery = screen_height/2

        # Set instance variables
        self.screen_width = screen_width
        self.screen_height = screen_height

        # Size of the ball
        size = (25, 25)

        # Radius of ball
        radius = 25

        # Draw the ball on the surface
        pygame.draw.circle(self.image, 'red', size, radius)

        # Set a speed
        self.speed = 8

        # Set x and y speed
        self.speed_x = self.speed
        self.speed_y = self.speed


        # Blit image to screen
        screen.blit(self.image, self.rect)

        # Add ball to sprite group
        allsprites.add(self)
        ball_sprites.add(self)

    def update(self):
        ''' Method for updating ball position '''

        # If ball reaches screen with - minus speed to go in other direction towards 0
        if self.rect.right >= self.screen_width:
            self.speed_x = -self.speed

        # If ball reaches 0 position add positive speed for ball go in other direction towards screen width
        if self.rect.left <= 0:
            self.speed_x = self.speed

        # If ball reaches top of screen (0 position) add positive speed to go down
        if self.rect.top <= 0:
            self.speed_y = self.speed

        # If ball reaches screen height, add negative speed to go up toward 0 position
        if self.rect.bottom >= self.screen_height:
            self.speed_y = -self.speed

        # Update ball position
        self.rect.centerx += self.speed_x
        self.rect.centery += self.speed_y


class MyText(pygame.sprite.Sprite):
    def __init__(self, text, x, y, width, height):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((width, height))
        self.image.fill('#555555')
        self.rect = self.image.get_rect()
        self.rect.left = x
        self.rect.top = y

        self.msg = text
        self.text = font.render(self.msg, True, 'white')

        self.image.blit(self.text, (10,5))

        
# Create a ball object
ball = Ball(screen_width, screen_height)

# Create the speed text object
speedtext = MyText(f'Speed: {ball.speed}', 0, 0, 200, 30)

# Create the instructions text object
text = 'keypad + = increase speed, keypad - = decrease speed, key r = reset speed'
instructions = MyText(text, screen_width-720, screen_height-30, 720, 30)

# Add to sprite groups
allsprites.add(speedtext)
text_sprites.add(speedtext)

allsprites.add(instructions)
instruct_sprites.add(instructions)




while running:
    screen.fill('black')
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

        # If event type is key press
        if event.type == pygame.KEYDOWN:

            # Get the key pressed. + plus key increase speed by 1
            # Minus key decrease speed by 1
            # r key reset default speed
            if event.key == pygame.K_KP_PLUS:
                ball.speed += 1
                if ball.speed >= 25:
                    ball.speed = 25

            if event.key == pygame.K_KP_MINUS:
                ball.speed -= 1
                if ball.speed <= 2:
                    ball.speed = 2

            if event.key == pygame.K_r:
                ball.speed = 8

            # This is to empty the sprite groups of the object speed text
            # so we are not piling on sprite after sprite after sprite
            text_sprites.empty()
            allsprites.remove(speedtext)

            # Create new text with updated speed value and add to sprite groups
            speedtext = MyText(f'Speed: {ball.speed}', 0, 0, 200, 30)
            text_sprites.add(speedtext)
            allsprites.add(speedtext)
            
    # Detect collissions with the text boxes
    if pygame.sprite.groupcollide(ball_sprites, text_sprites, False, False):
        ball.speed_y = ball.speed

    if pygame.sprite.groupcollide(ball_sprites, instruct_sprites, False, False):
        ball.speed_y = -ball.speed

    # Update and draw all sprites
    allsprites.update()
    allsprites.draw(screen)
    
    pygame.display.update()

    # Set a frame rate. If you don't have this the speed is very fast
    clock.tick(60)

pygame.quit()

Thanks for the reply. I actually managed to solve this on my own by just redoing the entire code. I had to completely change every part of the code in my post that handles AI movement and now I get that smooth, gradual movement that I was trying to get. But I also tested your code and it was insightful so I appreciate it. I'm going to look into both replies of yours once I've progressed a bit more with pygame, especially when it comes to sprites.
samhatter likes this post
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  How to instantly add quotation marks and comma for parameters? cheers100 4 12,169 Oct-22-2020, 12:51 PM
Last Post: cheers100
  Python instantly closes (Twitch Plays, Python 2) JustHelpMePlease 1 2,383 Jun-16-2019, 06:29 AM
Last Post: stranac
  Convert from datetime to time.struct_time object chris0147 0 5,346 Mar-11-2018, 12:01 AM
Last Post: chris0147
  add next day date in time.struct_time object chris0147 2 5,537 Feb-22-2018, 11:34 PM
Last Post: chris0147

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020