0

I am writing a program where a ball moves randomly across a screen and has a trail which changes color as the trail gets overwritten. This ball is a matrix and not a sprite and uses rect instead of circle.

I have found a question similar to mine but it does not apply in the same way, at least from my understanding. (Link: Changing direction of rotation Pygame)

Basically, instead of the sprite rotating and changing direction, I need the velocity to change by a specific degree every 10 seconds. I found out about the transform.rotate function in pygame, but couldn't figure out how to implement it within my code without having an error pop up.

Here is my use of transform.rotate, which doesn't work:

#change direction every ten seconds
while True:
   newsurf = pg.transform.rotate(ballGrid, -45)
   screen.blit(newball, [[0, 1, 1, 1, 0],
                         [1, 1, 1, 1, 1],
                         [1, 1, 1, 1, 1],
                         [1, 1, 1, 1, 1],
                         [0, 1, 1, 1, 0]])
   pg.display.flip()
   time.sleep(10)

The rest of my code for reference:

import sys
from random import randrange
import pygame as pg

# define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
RED2 = (231, 0, 0)
RED3 = (207,0,0)
RED4 = (183,0,0)
RED5 = (159,0,0)
RED6 = (135,0,0)
RED7 = (111,0,0)
RED8 = (87,0,0)
RED9 = (63,0,0)
RED10 = (39,0,0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)

# define measurements
WIDTH, HEIGHT, MARGIN = 10, 10, 1
GRIDX, GRIDY = 36, 36

class GridObject(pg.sprite.Sprite):
    def __init__(self, pos, grid, *groups):
        super().__init__(groups)

        # create image from grid
        self.grid = grid
        self.gridsize = (len(grid[0]), len(grid))
        imgsize = self.gridsize[0]*(WIDTH+MARGIN), self.gridsize[1]*(HEIGHT+MARGIN)
        self.image = pg.Surface(imgsize, flags=pg.SRCALPHA)
        self.image.fill((0, 0, 0, 0))
        col = (235, 175, 76)
        for c in range(self.gridsize[0]):
            for r in range(self.gridsize[1]):
                if self.grid[r][c] == 1:
                    rect = [(MARGIN + WIDTH) * c + MARGIN, (MARGIN + HEIGHT) * r + MARGIN, WIDTH, HEIGHT]
                    pg.draw.rect(self.image, col, rect)

        self.rect = self.image.get_rect(center=pos)
        self.vel = pg.math.Vector2(8, 0).rotate(randrange(360))
        self.pos = pg.math.Vector2(pos)


    def update(self, boundrect, hitGrid, hitList):
        self.pos += self.vel
        self.rect.center = self.pos
        if self.rect.left <= boundrect.left or self.rect.right >= boundrect.right:
            self.vel.x *= -1                            
        if self.rect.top <= boundrect.top or self.rect.bottom >= boundrect.bottom:
            self.vel.y *= -1     
        # align rect to grid
        gridpos = round(self.rect.x / (WIDTH+MARGIN)), round(self.rect.y / (HEIGHT+MARGIN))
        self.rect.topleft = gridpos[0] * (WIDTH+MARGIN), gridpos[1] * (HEIGHT+MARGIN)

        # increment touched filed
        oldHitList = hitList[:]
        hitList.clear()
        for c in range(self.gridsize[0]):
            for r in range(self.gridsize[1]):
                p = gridpos[1] + r, gridpos[0] + c
                if p in oldHitList:
                    hitList.append(p)
                elif self.grid[r][c] == 1:
                    if p[0] < len(hitGrid) and p[1] < len(hitGrid[p[0]]):
                        hitList.append(p)
                        if p not in oldHitList:
                            hitGrid[p[0]][p[1]] +=1

ballGrid = [[0, 1, 1, 1, 0],
            [1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1],
            [0, 1, 1, 1, 0]]

def main():
    #overlap = False
    screen = pg.display.set_mode((GRIDX * (WIDTH+MARGIN) + MARGIN, GRIDY * (HEIGHT+MARGIN)))
    # Set title of screen
    pg.display.set_caption("Ball With Grid")
    clock = pg.time.Clock()
    sprite_group = pg.sprite.Group()
    ball = GridObject((screen.get_width()//2, screen.get_height()//2), ballGrid, sprite_group)
    #change direction every ten seconds
    while True:
       newsurf = pg.transform.rotate(ballGrid, -90)
       screen.blit(newball, (100,100))
       pg.display.flip()
       time.sleep(10)

    hitGrid = [[0 for i in range(GRIDX)] for j in range(GRIDY)]
    hitList = []
    done = False
    while not done:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                done = True
            if event.type == pg.KEYDOWN and event.key == pg.K_SPACE:
                hitGrid = [[0 for i in range(GRIDX)] for j in range(GRIDY)]

        screen.fill((0, 0, 0))

        # Draw the grid and add values to the cells
        for row in range(GRIDY):
            for column in range(GRIDX):
                rect = [(MARGIN + WIDTH) * column + MARGIN, (MARGIN + HEIGHT) * row + MARGIN, WIDTH, HEIGHT]
                colorlist = [WHITE, RED, RED2, RED3, RED4, RED5, RED6, RED7, RED8, RED9, RED10, GREEN]
                color = colorlist[min(len(colorlist)-1, hitGrid[row][column])]
                pg.draw.rect(screen, color, rect)
                if color == GREEN:
                    pg.quit()

        sprite_group.update(screen.get_rect(), hitGrid, hitList)
        sprite_group.draw(screen)

        pg.display.flip()
        clock.tick(30)

if __name__ == '__main__':
    pg.init()
    main()
    pg.quit()
    sys.exit()

  • `newball` is not defined (used in the full code in the loop that's supposed to rotate the ball every 10 seconds) – chuck Mar 02 '20 at 20:15

2 Answers2

1

I recommend to use a timer event. Use pygame.time.set_timer() to repeatedly create an USEREVENT and change the angel of the direction vector (ball.vel) by a certain amount of degree (e.g. 30°):

# create timer event
change_delay = 10000 # 10 seconds
change_event = pg.USEREVENT + 1
pg.time.set_timer(change_event, change_delay)

# [...]

while not done:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            done = True

        # receive timer event
        if event.type == change_event:
            # change angle by 30°
            ball.vel = ball.vel.rotate(30)

Note, in pygame customer events can be defined. Each event needs a unique id. The ids for the user events have to be between pygame.USEREVENT (24) and pygame.NUMEVENTS (32). In this case pygame.USEREVENT+1 is the event id for the timer event.
The timer event can be stopped by passing 0 to the time parameter.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Can you explain what a userevent and a numevent is in more detail? – WebDevStudent Mar 02 '20 at 20:02
  • 1
    @DougJames I've explained all in detail. For further information pleas follow the links in the answer. It makes no sense to copy the pygame documentation to the answer. – Rabbid76 Mar 02 '20 at 20:03
0

The first argument for pygame.transfrom.rotate is a pygame.Surface object. You're passing a list. Instead try passing ball.image.

chuck
  • 1,420
  • 4
  • 19