Reputation: 79
Right now, I have a ball that moves around the screen in a random diagonal direction and bounces off the wall when it collides with it.
What I want to do: If the ball goes over a cell/box in the grid, have the cell/box turn red and then keep changing colors until the amount of times that specific spot is 10. If the value is equal to 10, the program ends.
Let me give you a scenario: let's say I have a 3x3 grid and each cell in that grid has a value of 0. Every time the ball goes over a cell, that value goes up by one until it hits 3. Every time the value goes up, the color for that cell changes. 0=white, 1=red, 2=green, and 3=blue. That's what I wanted the output to be.
What I have so far: The entire screen turns red instead of just the individual cell. I am just testing it for one color at the moment, but then want to add a multitude of colors.
for row in range(GRIDY):
for column in range(GRIDX):
rect = [(MARGIN + WIDTH) * column + MARGIN, (MARGIN + HEIGHT) * row + MARGIN, WIDTH, HEIGHT]
color = WHITE
if GRIDY or GRIDX == 1:
color = RED
pg.draw.rect(screen, color, rect)
Here's the rest of the code for reference:
import sys
import math
from random import randrange
import pygame as pg
# define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
# define measurements
WIDTH, HEIGHT, MARGIN = 10, 10, 1
GRIDX, GRIDY = 91, 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):
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)
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((495, 193), ballGrid, sprite_group)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
# Used to track the grid coordinates
if event.type == pg.MOUSEBUTTONDOWN:
# Get the position is screen is clicked
pos = pg.mouse.get_pos()
# Change the x/y screen coordinates to grid coordinates
column = pos[0] // (WIDTH + MARGIN)
row = pos[1] // (HEIGHT + MARGIN)
# Set that location to one
grid[row][column] = 1
print("Click ", pos, "Grid coordinates: ", row, column)
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]
color = WHITE
if GRIDY or GRIDX == 1:
color = RED
pg.draw.rect(screen, color, rect)
sprite_group.update(screen.get_rect())
sprite_group.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()
btw if this looks similar, my other account posted this question: Using a matrix as a sprite and testing if two sprites overlap
Link to the images: https://i.sstatic.net/zYZdA.jpg
Upvotes: 2
Views: 93
Reputation: 211176
let's say I have a 3x3 grid and each cell in that grid has a value of 0. Every time the ball goes over a cell, that value goes up by one until it hits 3. Every time the value goes up, the color for that cell changes. 0=white, 1=red, 2=green, and 3=blue. That's what I wanted the output to be.
You have how often the ball is on a cell, for each cell. Since the ball covers more than 1 cell (the size of the ball is greater than 1 cell), the cell counter would be incremented multiple times in consecutive frames, when the ball rolls over the cell.
So you have to ensure that a ball has left a cell, before its counter is allowed to be incremented again.
Create a 2 dimensional array (nested list) of integral values, initialized by 0 with the size of the field. The filed sores the hit counters for the cells. Furthermore, create a list (hitList
) which stores the indices of the cells which have been hit in a frame.
hitGrid = [[0 for i in range(GRIDX)] for j in range(GRIDY)]
hitList = []
The grid (hitGrid
) and the list (hitList
) have to be passed to the update
method of the class GridObject
. If the ball touches a field and the field was not touched in the previous frame, then the corresponding entry in the gird is has to be incremented. Further more the function can set a global variable max_hit
, wiht the highest fit count in the current frame:
class GridObject(pg.sprite.Sprite):
# [...]
def update(self, boundrect, gridHit):
# [...]
# increment touched filed
global max_hit
max_hit = 0
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
max_hit = max(max_hit, hitGrid[p[0]][p[1]])
Evaluate max_hit
after invoke update
to the GridObject
obejcts:
sprite_group.update(screen.get_rect(), hitGrid, hitList)
if max_hit >= 4:
print("game over")
done = True
Tint the color of a field dependent on the value in gridHit
:
for row in range(GRIDY):
for column in range(GRIDX):
rect = [(MARGIN + WIDTH) * column + MARGIN, (MARGIN + HEIGHT) * row + MARGIN, WIDTH, HEIGHT]
colorlist = [WHITE, RED, GREEN, BLUE]
color = colorlist[min(len(colorlist)-1, hitGrid[row][column])]
pg.draw.rect(screen, color, rect)
See the example:
from random import randrange
import pygame as pg
# define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 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
global max_hit
max_hit = 0
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
max_hit = max(max_hit, hitGrid[p[0]][p[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)
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, GREEN, BLUE]
color = colorlist[min(len(colorlist)-1, hitGrid[row][column])]
pg.draw.rect(screen, color, rect)
sprite_group.update(screen.get_rect(), hitGrid, hitList)
if max_hit >= 4:
print("game over")
done = True
sprite_group.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()
Upvotes: 2