New to Python trying to make game with pygame - python

I made some code for my game using PyGame to spawn an obstacle and im new to coding. It wouldn't spawn the obstacles and so i put a print statement to see when it spawns and it does print but doesn't show up on screen. This is not all the code but instead a minimalistic version of it that only shows the problematic code. no errors show up when running the code
import pygame as pg
import random
pg.init()
obstacle1 = pg.image.load('download1.jpeg')
obstacley = 600#tells object to spawn at top of screen
spawn = random.randint(1,10)
spawned = 0
if spawn == 1 and spawned == 0:
spawn = 0
Obstaclex = random.randint(600,800)#determines where on the top of the screen it spawns with rng
obstaclesize = random.randint(1,5)# determines obstacletype because there are 5 obstacle types that i havent included in this to be as simple as possbile
obstaclespeed = random.randint(3,8)#determines obstaclespeed using rng
spawned = 1
if obstaclesize == 1:
gameDisplay.blit(obstacle1,(obstacley,Obstaclex))
obstacley -= obstaclespeed #changes y position to lower it down the screen to hit player
print ("i have spawned")

You have to blit all the existing obstacles in the game loop in ever frame, not just when creating a new obstacle.
Create an obstacle list (obstacle_list) and append the coordinates of the new obstacle to the list. Draw all the obstacles in the main application loop:
obstacle_list = []
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# [...]
if len(obstacle_list) < spawn:
x = random.randint(600,800)
y = 600
size = random.randint(1,5)
speed = random.randint(3,8)
obstacle_list.append((x, y, size, speed))
# [...]
# move obstacles
for i in range(len(obstacle_list)):
x, y, size, speed = obstacle_list[i]
new_y = y - speed
obstacle_list[i] = (x, new_y, size, speed)
# [...]
# draw obstacles
for x, y, size, speed in obstacle_list:
gameDisplay.blit(obstacle1, (x, y))
# [...]

Related

How to create and move 2 or more objects simultaneously

I was trying to create a function that will load an image and move the image downwards while loading another image and still moving with the same speed as the first. But I don't know how to do it, all I can do for now is to load the next image when the y coordinate of the first is at 500
def aliens():
s = 0
i = "345678"
c = 1
b = int("".join(random.sample(i, c)))
h = 1
while h < 2:
while s <= b:
alien = pygame.image.load("ali.jpg")
alien = pygame.transform.scale(alien, (100, 100))
pos = "0123456789"
l = 3
x = int("".join(random.sample(pos, l)))
y = 0
if x > 500:
h = 3
crash = False
s +=1
while not crash :
scn.blit(alien, (x, y))
y +=2
pygame.display.flip()
if y > 500:
crash = True
The core of your problem is the fact that you are trying to do too many things in the same function. Try to follow the SRP (Single Responsability Principle).
Personnally I would use OOP (Object Oriented Programming) here but I don't know if you are familar with the concept, so first let's stick to the basics.
Here is how I would separate things up, if I wasn't allowed to use OOP:
First I would define two constants:
ALIEN_SIZE = 100
ALIEN_IMG = pg.image.load("green_square.png").convert()
ALIEN_IMG = pg.transform.scale(ALIEN_IMG, (ALIEN_SIZE, ALIEN_SIZE))
Since you are always using the same values, there is no need to load your image all the time. Loading your image just once will have a very positive impact on game's performances.
(Note that I used a diffrent image since you did not provide the image you use but feel free to put the link to your image back)
Then I would define an alien list:
aliens = []
I don't know how you plan to spawn aliens in your project ? For this example I used a custom event with a timer:
# Custom event
SPAWN_ALIEN = pg.USEREVENT
pg.time.set_timer(SPAWN_ALIEN, 2000)
# This function handles all pygame events
def handle_evt():
for evt in pg.event.get():
if evt.type == pg.QUIT:
exit()
if evt.type == SPAWN_ALIEN:
spawn_alien()
# This function spawns an alien
def spawn_alien():
pos = [rd.randint(0, screen.get_width() - ALIEN_SIZE), 0]
aliens.append(pos)
To make aliens move, I use a update() function:
# update aliens position and check if they leave the screen
def update():
for alien in aliens:
move_alien(alien)
if out_of_screen(alien):
kill_alien(alien)
# This function update alien's y position
def move_alien(alien):
alien[1]+=1
# This function remove the given alien from the aliens list
def kill_alien(alien):
aliens.remove(alien)
# This function returns True if the given position is outside screen's boundries
def out_of_screen(pos):
x, y = pos
if x < 0 or x > screen.get_width():
return True
elif y < 0 or y > screen.get_height():
return True
return False
Finally to render things :
def draw():
# Clear screen
screen.fill((0,0,0))
# Draw aliens
for alien in aliens:
screen.blit(ALIEN_IMG, alien)
Here is the full code, so you can see how everything is interacting :
# General imports
import pygame as pg
import random as rd
import sys
# Init
pg.init()
# Vars & consts
screen = pg.display.set_mode((500, 500))
pg.display.set_caption("Example")
FPS = 60
clock = pg.time.Clock()
ALIEN_SIZE = 100
ALIEN_IMG = pg.image.load("green_square.png").convert()
ALIEN_IMG = pg.transform.scale(ALIEN_IMG, (ALIEN_SIZE, ALIEN_SIZE))
# Custom event
SPAWN_ALIEN = pg.USEREVENT
pg.time.set_timer(SPAWN_ALIEN, 2000)
# Main functions
def update():
for alien in aliens:
move_alien(alien)
if out_of_screen(alien):
kill_alien(alien)
def draw():
# Clear screen
screen.fill((0,0,0))
# Draw aliens
for alien in aliens:
screen.blit(ALIEN_IMG, alien)
def handle_evt():
for evt in pg.event.get():
if evt.type == pg.QUIT:
exit()
if evt.type == SPAWN_ALIEN:
spawn_alien()
def exit():
pg.quit()
sys.exit()
# Other functions
def spawn_alien():
pos = [rd.randint(0, screen.get_width() - ALIEN_SIZE), 0]
aliens.append(pos)
def move_alien(alien):
alien[1]+=1
def kill_alien(alien):
aliens.remove(alien)
def out_of_screen(pos):
x, y = pos
if x < 0 or x > screen.get_width():
return True
elif y < 0 or y > screen.get_height():
return True
return False
# Main loop
if __name__ == '__main__':
aliens = []
spawn_alien()
while True:
handle_evt()
update()
draw()
clock.tick(FPS)
pg.display.flip()
And just in case, here is an OOP approach :
# General imports
import pygame as pg
import random as rd
import sys
# Init
pg.init()
# Vars
screen = pg.display.set_mode((500, 500))
pg.display.set_caption("Example")
FPS = 60
clock = pg.time.Clock()
# Class
class Alien():
LIST = []
SIZE = 100
IMG = pg.image.load("green_square.png").convert()
IMG = pg.transform.scale(IMG, (SIZE, SIZE))
def __init__(self, screen_width):
self.img = Alien.IMG
self.pos = (rd.randint(0, screen_width - Alien.SIZE), 0)
# Add alien to the list
Alien.LIST.append(self)
def move(self):
x, y = self.pos
self.pos = (x, y+1)
def has_crashed(self, boundries):
x, y = self.pos
if x < 0 or x > boundries[0]:
return True
elif y < 0 or y > boundries[1]:
return True
return False
def kill(self):
Alien.LIST.remove(self)
# Custom event
SPAWN_ALIEN = pg.USEREVENT
pg.time.set_timer(SPAWN_ALIEN, 2000)
# Main functions
def update():
for alien in Alien.LIST:
alien.move()
if alien.has_crashed(screen.get_size()):
alien.kill()
def draw():
# Clear screen
screen.fill((0,0,0))
# Draw aliens
for alien in Alien.LIST:
screen.blit(alien.img, alien.pos)
def handle_evt():
for evt in pg.event.get():
if evt.type == pg.QUIT:
exit()
if evt.type == SPAWN_ALIEN:
spawn_alien()
def exit():
pg.quit()
sys.exit()
# Other functions
def spawn_alien():
Alien(screen.get_width())
# Main loop
if __name__ == '__main__':
while True:
handle_evt()
update()
draw()
clock.tick(FPS)
pg.display.flip()
I hope all those informations can help you solve your problem. It's hard to know if this, really is the behavior you wanted for your aliens. That's because your variables names are not exactly explicit. One letter variable names are rarely a good idea. But anyway even if you need them to move a little diffrently, you can take inspiration of this code organization.
Have fun !

Generate tiles in piano tiles consecutively in pygame

I have created a simple piano tiles game clone in pygame.
Everything working fine except the way i am generating tiles after every certain interval, but as the game speed increases this leaves a gap between two tiles.
In the original version of the game, there's no lag (0 distance ) between two incoming tiles.
Here's a preview of the game:
Currently I am generating tiles like this:
ADDBLOCK = pygame.USEREVENT + 1
ADDTIME = 650
pygame.time.set_timer(ADDBLOCK, ADDTIME)
if event.type == ADDBLOCK:
x_col = random.randint(0,3)
block = Block(win, (67.5 * x_col, -120))
block_group.add(block)
But with time, these tiles speed increase so there's remain a gap between generation of two tiles as shown by red line in the preview. Is there any way to generate tiles consecutively?
Source Code
Use a variable number to know how many tiles have been generated since the start. This variable will start with 0, then you will add 1 to this variable every time a tile is generated.
Then, you can use a variable like scrolling which increases continuously. You will add this scrolling to every tile y pos to render them.
Now you just have to add a tile which y position is like -tile_height - tile_height * number.
If that doesn't make sense to you, look at this MRE:
import pygame
from pygame.locals import *
from random import randint
pygame.init()
screen = pygame.display.set_mode((240, 480))
clock = pygame.time.Clock()
number_of_tiles = 0
tile_height = 150
tile_surf = pygame.Surface((60, tile_height))
tiles = [] # [column, y pos]
scrolling = 0
score = 0
speed = lambda: 200 + 5*score # increase with the score
time_passed = 0
while True:
click = None # used to click on a tile
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
exit()
if event.type == MOUSEBUTTONDOWN:
click = event.pos
screen.fill((150, 200, 255))
if scrolling > number_of_tiles * tile_height:
# new tile
# use "while" instead of "if" if you want to go at really high speed
tiles.append([randint(0, 3), -tile_height - number_of_tiles * tile_height])
number_of_tiles += 1
for x, y in tiles:
screen.blit(tile_surf, (60 * x, y + scrolling))
if y + scrolling > 480: # delete any tile that is no longer visible
tiles.remove([x, y])
if click is not None and Rect((60 * x, y + scrolling), tile_surf.get_size())
.collidepoint(click):
tiles.remove([x, y]) # delete any tile that has been clicked
score += 1 # used to calculate speed
scrolling += speed() * time_passed
pygame.display.flip()
time_passed = clock.tick() / 1000

Can somebady help me why errors exist in my pygame code about `ball moving`?

I will show my code below. I think when I def moveDot, something wrong I can feel it. However, I just code as my instructor's presentation. She's code can work and mine cannot. I don't know the reason. I think the order of moveDot's variances may have some problems. Is that right? I will appreciate anyone who can help me! Thanks a lot!
# Poke The Dots
# There are two dots that are moving on a 500 by 400 window
# There is a score board that displays the time in seconds
# since the game started
# If the player clicks inside the window, the dots disappear
# and reappear at some random location
# If the dots collide, the dots stop moving, the score stops
# changing and Game Over is displayed.
# Version 1
import pygame, sys, time
from pygame.locals import *
# User-defined classes
# User-defined functions
def main():
# Initialize pygame
pygame.init()
# Set window size and title, and frame delay
surfaceSize = (500, 400) # window size
windowTitle = 'Poke The Dots' #window title
frameDelay = 0.02 # smaller is faster game
# Create the window
surface = pygame.display.set_mode(surfaceSize, 0, 0)
pygame.display.set_caption(windowTitle)
# create and initialize red dot and blue dot
gameOver = False
color1=pygame.Color('red')
center1 = [50, 75]
radius1=30
speed1=[1,2]
color2=pygame.Color('blue')
center2=[200,100]
radius2=40
speed2=[2,1]
# Draw objects
pygame.draw.circle(surface, color1, center1, radius1, 0)
pygame.draw.circle(surface, color2,center2,radius2,0)
gameOver = update(surface, color1, center1, radius1, speed1, color2, center2, radius2, speed2)
# Refresh the display
pygame.display.update()
# Loop forever
while True:
# Handle events
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
# Handle additional events
# Update and draw objects for the next frame
#gameOver = update(center, surface)
# Refresh the display
pygame.display.update()
# Set the frame speed by pausing between frames
time.sleep(frameDelay)
def update(surface, color1, center1, radius1, speed1, color2, center2, radius2, speed2):
#Check if the game is over. If so, end the game and
#returnTrue. Otherwise, update the game objects, draw
#them, and return False.
#This is an example update function - replace it.
#- center is a list containing the x and y int coords
#of the center of a circle
#- surface is the pygame.Surface object for the window
erasecolor=pygame.Color('black')
if False: # check if the game is over
return True
else: # continue the game
surface.fill(erasecolor)
moveDot(surface, color1, center1, radius1)
moveDot(surface, color2, center2, radius2)
pygame.draw.circle(surface,color1,center1,radius1,0,0)
pygame.draw.circle(surface,color2,center2,radius2,0,0)
return False
def moveDot(surface,center,radius,speed):
size=surface.get_size()
for coord in range(0,2):
center[coord]=center[coord]+speed[coord]
if center [coord]<radius:
speed[coord]=-speed[coord]
if center[coord]+radius>size(coord):
speed[coord]=-speed[coord]
main()
The order of your arguments being passed when you call moveDot is incorrect. It should be
moveDot(surface, center1, radius1, speed)
For both statements. Speed should be the speed of movement of the circle.

Blinking rectangle in pygame

I am trying to learn pyGame. And I ran into a problem. I have a rectangle which can be moved with arrow buttons. Then I created another thread which generates smaller rectangles which can be picked up. But when I run my game, the small generated rectangles blink too much. How can I make them be stable? I think I dont understand time concept here well. Could someone explain me etc
My code:
import pygame
import random
import threading
import thread
import sys
import time
pygame.init()
screen = pygame.display.set_mode((400, 300))
done = False
is_blue = True
entityList = []
x = 30
y = 30
clock = pygame.time.Clock()
class Entity():
def __init__(self, x, y):
self.x = x
self.y = y
def getX(self):
return self.x
def getY(self):
return self.y
def drawStuff(entityList):
# pygame.draw.rect(screen, (255, 100, 0), pygame.Rect(55, 45, 10, 10))
for x in range (0, entityList.__len__()):
pygame.draw.rect(screen, (255, 100, 0), pygame.Rect(entityList[x].getX(), entityList[x].getY(), 10, 10))
pygame.display.flip()
clock.tick(60)
class EntityManager(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
while True:
entityList = generateEntities()
drawStuff(entityList)
def endJob(self):
thread.exit()
time.sleep(2)
def detect_collision(x,y):
if x > 340:
x -= 1
if y > 240:
y -= 1
if y < 0:
y += 1
if x < 0:
x += 1
return x,y
def generateEntities():
itemlist = []
for x in range (0,4):
x = random.randint(1,339)
y = random.randint(1,239)
entity = Entity(x,y)
itemlist.append(entity)
return itemlist
entityList = generateEntities()
a = EntityManager()
a.setDaemon(True)
a.start()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
is_blue = not is_blue
pressed = pygame.key.get_pressed()
if pressed[pygame.K_UP]:
y -= 1
x,y = detect_collision(x, y)
if pressed[pygame.K_DOWN]:
y += 1
x,y = detect_collision(x, y)
if pressed[pygame.K_LEFT]:
x -= 1
x,y = detect_collision(x, y)
if pressed[pygame.K_RIGHT]:
x += 1
x,y = detect_collision(x, y)
screen.fill((0, 0, 0))
if is_blue: color = (0, 128, 255)
else: color = (255, 100, 0)
pygame.draw.rect(screen, color, pygame.Rect(x, y, 60, 60))
pygame.display.flip()
clock.tick(60)
clock.tick() actually means FPS (frames per second); instead of changing it, change another variable.
Watch this tutorial, it might help you understand FPS.
Issues about smoothness are sometimes about the size of your object and how many pixels you are moving it when key events are True. Be careful about that; imagine you have a little box and you are trying to move it. If you move it with big steps, it seems 'laggy', but if you move it with little steps then it may look smoother. Hope you understand what I mean.
For your second question please check this documentation.
Okay, I figured it out now. OMG, I feel such an idiot after this. But I forgive myself that I am also doing parallell learning of Python. I used to code in Java more.
Anyway: problem was that my drawStuff function drew things in function but when it left this function, it did nothing. Changes were made locally. I solved this problem by using global keyword. I made in my code changes that thread computes entity coordinates and then main loop does update. This leads me to another idea that maybe there should be some game info object which gets updated all the time and all rectangles info go to that game info object.
Anyway, soon I will edit this post code too to see what i did

Different colors for shapes through iterations in Python / Pygame?

I'm new to stackoverflow, but was hoping for a little insight from more advanced programmers. I am switching majors to Computer Science next semester and am taking an intro class learning some beginner's Python programming. I have already finished the program below (the assignment was to make a program that draws ovals on the window surface by filling in some of the professor's code, not too bad at all) but I wanted to add a little something extra: As you can see, I have the color of the ovals set to be random, but it stays the same until the program is restarted entirely i.e. all of the ovals are that particular color for the length of the program. With the code written the way it is, I can't figure out a way to get the color to change for each oval. Keep in mind, this is all for kicks, but if anyone's feeling especially helpful or creative, I'm curious to see what you have to say. Let me know if I can expound on anything. Thanks!
import pygame, random, sys
WINDOWWIDTH = 700
WINDOWHEIGHT = 700
BACKGROUNDCOLOR = (150,160,100)
#A different color every run
OVAL_COLOR = (random.randint (0,255),random.randint (0,255),
random.randint (0,255))
pygame.init()
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption("Mobile Ovals")
#The draw variable is used later to indicate the mouse is still pressed
ovals = []
completedOvals = []
finished = False
draw = False
startXY = (-1, -1)
while not finished:
for event in pygame.event.get():
if event.type == pygame.QUIT or (event.type == pygame.KEYUP and
event.key == pygame.K_ESCAPE):
finished = True
elif event.type == pygame.KEYDOWN:
pressed = pygame.key.get_pressed()
if pressed[pygame.K_F4] and (pressed[pygame.K_LALT] or
pressed[pygame.K_RALT]):
finished = True
elif event.type == pygame.MOUSEBUTTONDOWN:
startXY = event.pos
draw = True
elif event.type == pygame.MOUSEBUTTONUP:
draw = False
for oval in ovals:
completedOvals.append (oval)
if draw == True:
del ovals [:]
#The above function ensures only one oval is onscreen at any given time
endXY = event.pos
width = (abs(endXY[0]-startXY[0]))
height = (abs(endXY[1]-startXY[1]))
#The code below allows the user to drag any direction
if endXY[0] < startXY[0]:
left = endXY[0]
else:
left = startXY[0]
if endXY[1] < startXY[1]:
top = endXY[1]
else:
top = startXY[1]
ovals.append (pygame.Rect (left, top, width, height))
windowSurface.fill(BACKGROUNDCOLOR)
for oval in ovals:
pygame.draw.ellipse(windowSurface, OVAL_COLOR, oval)
for completedOval in completedOvals:
pygame.draw.ellipse(windowSurface, OVAL_COLOR, completedOval)
pygame.display.update()
pygame.quit()
Your problem is quite simple. You set OVAL_COLOR once. But every time you make reference to the variable OVAL_COLOR, you're not creating a new random color, you're re-using the RGB color that was randomly generated when you created the variable.
Now, the way your program is structured, you maintain a list of all complete ovals that you're re-drawing every time the draw variable is set to true. If you place the OVAL_COLOR variable inside the for loop, you will update the color with every mouse movement, changing the color of the oval being drawn, as well as the color of all the old ovals being re-drawn.
The solution to have a new random oval color is to set the variable OVAL_COLOR when the mouse button goes down. That way, the oval color won't change as you drag the mouse to adjust the oval. But, given the current structure of the program, you'll need to save the oval colors assigned to completed ovals, or you'll still have the oval color change each time.
When the mouse button is pressed down, we want a new random color for our circle. Generate a random value, which will be used every time the circle is re-drawn.
elif event.type == pygame.MOUSEBUTTONDOWN:
startXY = event.pos
OVAL_COLOR = (random.randint (0,255),random.randint (0,255),
random.randint (0,255))
draw = True
When the mouse button is released, save the coordinates for the oval, along with the color that it was drawn with.
elif event.type == pygame.MOUSEBUTTONUP:
draw = False
# print len(ovals) # (always ==1)
completedOvals.append ((ovals[-1], OVAL_COLOR))
When we iterate through these completed ovals, draw them with the same color each time.
for (completedOval, color) in completedOvals:
pygame.draw.ellipse(windowSurface, color, completedOval)
Create a simple Oval() class, that contains it's color, and size.
import pygame
from pygame.locals import *
class Oval(object):
"""handle, and draw basic ovals. stores Rect() and Color()"""
def __init__(self, startXY, endXY):
self.color = Color(random.randint(0,255), random.randint(0,255), random.randint(0,255))
self.rect = Rect(0,0,1,1)
self.coord_to_oval(startXY, endXY)
def draw(self):
pygame.draw.ellipse(windowSurface, self.color, self.rect)
def coord_to_oval(self, startXY, endXY):
width = (abs(endXY[0]-startXY[0]))
height = (abs(endXY[1]-startXY[1]))
#The code below allows the user to drag any direction
if endXY[0] < startXY[0]:
left = endXY[0]
else:
left = startXY[0]
if endXY[1] < startXY[1]:
top = endXY[1]
else:
top = startXY[1]
self.rect = Rect(left, top, width, height)
# main loop
while not finished:
for event in pygame.event.get():
# events, and creation:
# ... your other events here ...
elif event.type == MOUSEBUTTONDOWN:
startXY = event.pos
draw = True
elif event.type ==MOUSEBUTTONUP:
# on mouseup, create instance.
endXY = event.pos
oval_new = Oval(startXY, endXY)
completedOvals.append(oval_new)
# draw them:
for oval in ovals:
oval.draw()
for oval in completedOvals:
oval.draw()
I mostly left out your non-completed ovals. Was that to show the size before clicking?

Categories

Resources