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()
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.