I am trying to draw squares in random positions and random rgb values and I want 1000 of them to be created. The problem I'm facing is that everytime the loop for drawing occurs, it randomizes it all again, is there any way to make this not happen
import pygame
import sys
import random
pygame.init()
win = pygame.display.set_mode((800,600))
pygame.display.set_caption("Simulation")
def safeZone():
#Draws a top rectangle
pygame.draw.rect(win, (50,205,50), (0, 0, 800, 100))
def dot():
width = 10
height = 10
spawnX = random.randrange(1, 801)
spawnY = random.randrange(1, 601)
r = random.randrange(1, 256)
g = random.randrange(1, 256)
b = random.randrange(1, 256)
pygame.draw.rect(win, (r, g, b), (spawnX, spawnY, width, height))
def population(size):
for x in range(size):
dot()
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
win.fill((255, 255, 255))
safeZone() # Always draw dots after safe zone
population(1000)
pygame.display.update()
pygame.quit()
Create a dot collection, then just draw that dot collection. Now you can update the dot positions separately, and they will redraw in the new positions. Here, I'm having each dot move a random amount in every loop.
import pygame
import sys
import random
pygame.init()
win = pygame.display.set_mode((800,600))
pygame.display.set_caption("Simulation")
class Dot:
def __init__(self):
self.spawnX = random.randrange(0, 800)
self.spawnY = random.randrange(0, 600)
self.r = random.randrange(0, 256)
self.g = random.randrange(0, 256)
self.b = random.randrange(0, 256)
def safeZone():
#Draws a top rectangle
pygame.draw.rect(win, (50,205,50), (0, 0, 800, 100))
def drawdot(dot):
width = 10
height = 10
pygame.draw.rect(win, (dot.r, dot.g, dot.b), (dot.spawnX, dot.spawnY, width, height))
def population(dots):
for dot in dots:
dot.spawnX += random.randrange(-3,4)
dot.spawnY += random.randrange(-3,4)
drawdot(dot)
alldots = [Dot() for _ in range(1000)]
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
win.fill((255, 255, 255))
safeZone() # Always draw dots after safe zone
population(alldots)
pygame.display.update()
A worthwhile modification is to store the whole rectangle in the object:
...
class Dot:
def __init__(self):
self.location = [
random.randrange(0, 800),
random.randrange(0, 600),
10, 10
]
self.color = (
random.randrange(0, 256),
random.randrange(0, 256),
random.randrange(0, 256)
)
def move(self, dx, dy ):
self.location[0] += dx
self.location[1] += dy
def drawdot(dot):
pygame.draw.rect(win, dot.color, dot.location)
def population(dots):
for dot in dots:
dot.move( random.randrange(-3,4), random.randrange(-3,4) )
drawdot(dot)
...
You call a function dot() in which you have assigned randomization. You should introduce a piece of code that randomizes the values outside of the dot() function, and store them in a separate array, and then call the function.
Your description sounds like you aren't necessarily trying to store the result so much as you want the process to be the same every time, but still sort of random? You could just use a hard-coded seed?
import random
random.seed(10)
print(random.random())
See this link for more detail: Random Seed
Related
Right now, my game blits all the images in random positions correctly and also gets the rect of the images correctly, but I can´t figure out how to use colliderect to make sure the images don´t overlap. How could it work for my code?
Also I´m trying to make the first text fade out and I don´t know why it doesn´t work for me.
Here is the code:
class GAME1:
def __init__(self, next_scene):
self.background = pygame.Surface(size)
# Create an array of images with their rect
self.images = []
self.rects = []
self.imagenes1_array = ['autobus.png','coche.png','barco.png','autobus2.png','grua.png','bici.png']
for i in self.imagenes1_array:
# We divide in variables so we can then get the rect of the whole Img (i2)
i2 = pygame.image.load(i)
self.images.append(i2)
s = pygame.Surface(i2.get_size())
r = s.get_rect()
# Trying to use colliderect so it doesnt overlap
if pygame.Rect.colliderect(r,r) == True:
x = random.randint(300,1000)
y = random.randint(200,700)
self.rects.append(r)
def start(self, gamestate):
self.gamestate = gamestate
for rect in self.rects:
# Give random coordinates (we limit the dimensions (x,y))
x = random.randint(300,1000)
y = random.randint(200,700)
rect.x = x
rect.y = y
def draw(self,screen):
self.background = pygame.Surface(size)
font = pygame.font.SysFont("comicsansms",70)
# First half (Show image to remember)
text1 = font.render('¡A recordar!',True, PURPLE)
text1_1 = text1.copy()
# This surface is used to adjust the alpha of the txt_surf.
alpha_surf = pygame.Surface(text1_1.get_size(), pygame.SRCALPHA)
alpha = 255 # The current alpha value of the surface.
if alpha > 0:
alpha = max(alpha-4, 0)
text1_1 = text1.copy()
alpha_surf.fill((255, 255, 255, alpha))
text1_1.blit(alpha_surf, (0,0), special_flags = pygame.BLEND_RGBA_MULT)
screen.blit(text1_1, (600,50))
# Second half (Show all similar images)
text2 = font.render('¿Cuál era el dibujo?',True, PURPLE)
#screen.blit(text2, (500,50))
for i in range(len(self.images)):
#colliding = pygame.Rect.collidelistall(self.rects)
screen.blit(self.images[i], (self.rects[i].x, self.rects[i].y))
def update(self, events, dt):
for event in events:
if event.type == pygame.MOUSEBUTTONDOWN:
for rect in self.rects:
if rect.collidepoint(event.pos):
print('works!')
Use collidelist() to test test if one rectangle in a list intersects:
for i in self.imagenes1_array:
s = pygame.image.load(i)
self.images.append(s)
r = s.get_rect()
position_set = False
while not position_set:
r.x = random.randint(300,1000)
r.y = random.randint(200,700)
margin = 10
rl = [rect.inflate(margin*2, margin*2) for rect in self.rects]
if len(self.rects) == 0 or r.collidelist(rl) < 0:
self.rects.append(r)
position_set = True
See the minimal example, that uses the algorithm to generate random not overlapping rectangles:
import pygame
import random
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
def new_recs(rects):
rects.clear()
for _ in range(10):
r = pygame.Rect(0, 0, random.randint(30, 40), random.randint(30, 50))
position_set = False
while not position_set:
r.x = random.randint(10, 340)
r.y = random.randint(10, 340)
margin = 10
rl = [rect.inflate(margin*2, margin*2) for rect in rects]
if len(rects) == 0 or r.collidelist(rl) < 0:
rects.append(r)
position_set = True
rects = []
new_recs(rects)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.KEYDOWN:
new_recs(rects)
window.fill(0)
for r in rects:
pygame.draw.rect(window, (255, 0, 0), r)
pygame.display.flip()
pygame.quit()
exit()
In this program when the user types more text I want the rectangle to automatically get longer when the user types to keep the letters inside of the rectangle. However, it doesn't update the rectangle when the text gets longer. How do I fix this?
from pygame import *
init()
screen = display.set_mode((800, 600))
name_font = font.Font(None, 32)
name_text = ''
class Rectangle:
def __init__(self, x, y):
self.x = x
self.y = y
self.input_rect = Rect(x, y, 140, 32)
self.text_surface = name_font.render(name_text, True, (255, 255, 255))
color = Color('lightskyblue3')
draw.rect(screen, color, self.input_rect, 2)
self.input_rect.w = self.text_surface.get_width() + 10
screen.blit(self.text_surface, (self.input_rect.x + 5, self.input_rect.y + 5))
def naming():
global name_text
if events.type == KEYDOWN:
if keys[K_BACKSPACE]:
name_text = name_text[:-1]
screen.fill((0, 0, 0))
rect_1 = Rectangle(200, 200)
else:
name_text += events.unicode
while True:
rect_1 = Rectangle(200, 200)
for events in event.get():
keys = key.get_pressed()
naming()
if events.type == QUIT:
quit()
display.update()
time.delay(1)
The Rectangle.text_surface is a PyGame Surface. So you can easily get the precise width of the bounding box by simply calling self.text_surface.get_width().
But you start the size of the border-rect at 140, so this size has to be the maximum of 140 or whatever the new (longer) width is. Another problem is that when the rectangle re-sizes, the old rectangle is left behind. So whenever we now re-draw the rectangle, it erases the background to black.
This is all pretty easily encapsulated into the exiting __init__():
def __init__(self, x, y):
self.x = x
self.y = y
self.text_surface = name_font.render(name_text, True, (255, 255, 255))
rect_width = max( 140, 10 + self.text_surface.get_width() ) # Adjust the width
color = Color('lightskyblue3')
self.input_rect = Rect(x, y, rect_width, 32) # Use new width (if any)
draw.rect(screen, (0,0,0) , self.input_rect, 0) # Erase any existing rect
draw.rect(screen, color, self.input_rect, 2)
self.input_rect.w = self.text_surface.get_width() + 10
screen.blit(self.text_surface, (self.input_rect.x + 5, self.input_rect.y + 5))
I am making an agario clone for a project and I was wondering what the quickest way to draw many dots in pygame.
from pygame import *
import random as rd
x = rd.randint(100, 700)
y = rd.randint(100, 500)
# I would like to draw about 50 dots of this type.
dot = draw.circle(screen, (0, 0, 0), (x, y), 5)
Here's some basic code to draw circles using pygame:
import pygame as pg
import random as rd
pg.init() # initialize pygame
screen = pg.display.set_mode((500,500)) # create main screen
for ctr in range(25): # 25 circles
x = rd.randint(50, 450)
y = rd.randint(50, 450)
# I would like to draw about 50 dots of this type.
dot = pg.draw.circle(screen, (100, 200, 100), (x, y), 15)
pg.display.update() # update screen
while True: # main pygame loop, always include this
for event in pg.event.get(): # required for OS events
if event.type == pg.QUIT: # user closed window
pg.quit()
Output
Any easy way to do this to use a simple python "for" loop with a "range":
As the loop iterates, it executes the content of the loop body, with the variable i incrementing from 0 to N-1 (i.e. 49). The variable i could be any valid variable name, but for simple numbered loops, i, j & k are commonly used.
from pygame import *
import random as rd
# Draw 50 dots
for i in range( 0, 50 ):
x = rd.randint(100, 700)
y = rd.randint(100, 500)
dot = draw.circle(screen, (0, 0, 0), (x, y), 5)
I made a class for dots:
class Dot():
SIZE = 5
def __init__(self, x, y):
self.x = x
self.y = y
def draw(self):
draw.circle(screen, self.color, (self.x, self.y), Dot.SIZE)
Then I made an array and generate NUMBER_OF_DOTS like this:
dots = []
for i in range(NUMBER_OF_DOTS):
x = rd.randint(100, 700)
y = rd.randint(100, 500)
dots.append(Dot(x,y))
and in the while loop and redraw after filling the whole scene by white color:
while True:
screen.fill((255, 255, 255))
...
for dot in dots:
dot.draw()
The whole source:
from pygame import *
import random as rd
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
NUMBER_OF_DOTS = 300
class Dot():
SIZE = 5
def __init__(self, x, y):
self.x = x
self.y = y
self.color = random_color()
def draw(self):
draw.circle(screen, self.color, (self.x, self.y), Dot.SIZE)
def random_color():
r = rd.randint(0, 255)
g = rd.randint(0, 255)
b = rd.randint(0, 255)
return (r, g, b)
init()
screen = display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
dots = []
# generate random dots all over the screen
for i in range(NUMBER_OF_DOTS):
x = rd.randint(100, 700)
y = rd.randint(100, 500)
dots.append(Dot(x,y))
# main while loop
while True:
screen.fill((255, 255, 255))
for dot in dots:
dot.draw()
display.update()
time.delay(1) # Speed down
If you want to draw the same circles continuously in the main application loop, then you've to generate a list of random positions:
import pygame as pg
import random as rd
pg.init()
screen = pg.display.set_mode((800,600))
cpts = []
for i in range(25):
x = rd.randint(100, 700)
y = rd.randint(100, 500)
cpts.append((x, y))
run = True
while run:
for event in pg.event.get():
if event.type == pg.QUIT:
run = False
for cpt in cpts:
pg.draw.circle(screen, (255, 255, 255), cpt, 15)
pg.display.update()
See the answer to Pygame unresponsive display for a minimum pygame application.
Here's the code, i'm working on Atom. I've been using pygame for a lot of time, and that rarely happens. I never knew what the problem was, but i never needed to, until now.
import pygame
from random import randint as rnd
from colorama import Cursor
import math
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (600,90)
pygame.init()
xsize, ysize = 700, 700
screen = pygame.display.set_mode((xsize, ysize))
screen.fill((0, 0, 0))
def dis(p1, a1):
return math.sqrt((p1[0] - a1[0])**2 + (p1[1] - a1[1])**2)
inner = 0
tot = 0
pygame.draw.circle(screen, (255, 255, 255), (350, 350), 350, 2)
while True:
pos = (rnd(0, xsize), rnd(0, ysize))
if dis((350, 350), pos) < 350:
color = (50, 255, 50)
inner += 1
else:
color = (50, 50, 250)
tot += 1
pygame.draw.circle(screen, color, pos, 2)
print(" pi = " + str(4 * inner / tot) + "\nnº dots: " + str(tot), Cursor.UP(2))
pygame.display.flip()
The window freeze, because you do not handle the events. You have to handle the events by either pygame.event.pump() or pygame.event.get(), to keep the window responding.
Add an event loop, for instance:
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# [...]
I have been working on an "Asteroids" remake. However I can not get the objects to move in a random motion. I'm sure this evolves using vectors, however I do not know how to randomly generate a vector for each individual asteroid.
I am looking for a motion similar to this motion shown in this asteroid game, however I have no idea how to even start. This is my code so far:
import pygame as game
import random as r
import math
game.init()
game.display.set_caption("Asteroids")
screen = game.display.set_mode([800,600])
time=0
gameon=True
bgcolor = game.color.Color("#f6cb39")
black=game.color.Color("black")
badguy = game.image.load("asteroid.png")
badguy = game.transform.scale(badguy, (50, 50))
badguys=[]
SPAWNENEMY=10
CLOCK=11
game.time.set_timer(SPAWNENEMY,800)
game.time.set_timer(CLOCK,1000)
font=game.font.Font(None,20)
timetext=font.render("Time: 0", 0, black)
while gameon:
screen.fill(bgcolor)
event=game.event.poll()
if event.type==SPAWNENEMY:
bgbox = game.Rect(badguy.get_rect())
bgbox.x = r.randint(50,800)
bgbox.y = r.randint(50,600)
badguys.append(bgbox)
if event.type==game.QUIT:
gameon=False;
for bg in badguys:
'''
This is where I tried to put the mocment code,
but I was unableto get randmom movments,
only for asteroids to randomly appear or consistently
move in one direction something like "bg.x+=2"
'''
for bg in badguys:
screen.blit(badguy,(bg.x,bg.y))
game.display.flip()
I apologize if its a little long, I don't know what else I can cut out to create an MCV.
Here's how to do it using vectors. Each item in the badguy list is now a pair of items, its current position and an associated speed vector. Note that the position itself is also a vector (aka a "position vector").
Updating the current position is accomplished by simply adding each badguy's speed vector to its current position. i.e. bg[0] += bg[1].
import pygame as game
import pygame.math as math
from pygame.time import Clock
import random as r
game.init()
game.display.set_caption("Asteroids")
screen = game.display.set_mode([800, 600])
time = 0
gameon = True
bgcolor = game.color.Color("#f6cb39")
black = game.color.Color("black")
clock = Clock()
badguy = game.image.load("asteroid.png")
badguy = game.transform.scale(badguy, (50, 50))
badguys = []
SPAWNENEMY = 10
CLOCK = 11
game.time.set_timer(SPAWNENEMY, 800)
game.time.set_timer(CLOCK, 1000)
font=game.font.Font(None,20)
timetext=font.render("Time: 0", 0, black)
while gameon:
screen.fill(bgcolor)
event = game.event.poll()
if event.type == SPAWNENEMY:
# Select a random initial position vector.
posn = math.Vector2(r.randint(50, 800), r.randint(50, 600))
# Create a random speed vector.
speed = r.randint(1, 10)
dx = r.random()*speed * r.choice((-1, 1))
dy = r.random()*speed * r.choice((-1, 1))
vector = math.Vector2(dx, dy)
# Each badguy item is a [position, speed vector].
badguys.append([posn, vector])
if event.type == game.QUIT:
gameon = False;
for bg in badguys:
# Update positions.
bg[0] += bg[1] # Update position using speed vector.
for bg in badguys:
screen.blit(badguy, bg[0])
clock.tick(60)
game.display.flip()
So each asteroid in your game is represented by a Rect, stored in badguys.
With a Rect, you're able to store a postion and a size (since a Rect has the attributes x, y, width and height).
Now, you want to store additional information/state for each asteroid, so only using a Rect is not enough. You need a different data structure that holds more fields.
Since you use python, the fitting data structure is a class that is able to hold the random vector.
But let's think a little bit further. Since you use pygame, pygame already offers a class for represting your game objects, and that class is called Sprite.
So here we go (note the comments in the code):
import pygame
import random
screen = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
class Asteroid(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
# let's create an image of an asteroid by drawing some lines
self.image = pygame.Surface((50, 50))
self.image.set_colorkey((11, 12, 13))
self.image.fill((11, 12, 13))
pygame.draw.polygon(self.image, pygame.Color('grey'), [(0, 11), (20, 0), (50, 10), (15, 22), (27, 36), (10, 50), (0, 11)], 1)
# Let's store a copy of that image to we can easily rotate the image
self.org_image = self.image.copy()
# The rect is used to store the position of the Sprite
# this is required by pygame
self.rect = self.image.get_rect(topleft=(x, y))
# Let's create a random vector for the asteroid
self.direction = pygame.Vector2(0, 0)
while self.direction.length() == 0:
self.direction = pygame.Vector2(random.uniform(-1, 2), random.uniform(-1, 2))
# Also we want a constant, random speed
self.direction.normalize_ip()
self.speed = random.uniform(0.1, 0.3)
# we additionaly store the position in a vector, so the math is easy
self.pos = pygame.Vector2(self.rect.center)
# Aaaaaaaaaand a random rotation, because why not
self.rotation = random.uniform(-0.3, 0.3)
self.angle = 0
def update(self, dt):
# movement is easy, just add the position and direction vector
self.pos += self.direction * self.speed * dt
self.angle += self.rotation * dt
self.image = pygame.transform.rotate(self.org_image, self.angle)
# update the rect, because that's how pygame knows where to draw the sprite
self.rect = self.image.get_rect(center=self.pos)
SPAWNENEMY = pygame.USEREVENT + 1
pygame.time.set_timer(SPAWNENEMY, 800)
asteroids = pygame.sprite.Group()
dt = 0
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
quit()
if e.type == SPAWNENEMY:
asteroids.add(Asteroid(random.randint(50, 200), random.randint(50, 200)))
screen.fill(pygame.Color('black'))
asteroids.draw(screen)
asteroids.update(dt)
pygame.display.flip()
dt = clock.tick(60)