Pygame Display Update Glitch? - python

I have some code that is kinda funky. Basically, the variable gameStart is in a variable that uses the function, cursorOver, which finds and detects where and if the mouse button is and if it is pressed. I have 3 buttons and I want each button to become larger when the cursor is over the button. The first button implementation works. However, if I try to add another button, the button becomes enlarged, however, the button starts flickering.
window.blit(background,(0,0))
window.blit(title,(175,200))
pygame.draw.rect(window,PURPLE,(50,400,200,100),0)
pygame.draw.rect(window,PURPLE,(300,400,200,100),0)
pygame.draw.rect(window,PURPLE,(550,400,200,100),0)
close()
mouseX,mouseY = pygame.mouse.get_pos()
mouseClick = pygame.mouse.get_pressed()[0]
gameStartC = cursorOver(50,400,200,100,mouseX,mouseY)
instructionStartC = cursorOver(300,400,200,100,mouseX,mouseY)
objectiveStartC = cursorOver(550,400,200,100,mouseX,mouseY)
nextStartC = cursorOver(580,250,175,100,mouseX,mouseY)
if gameStartC == True:
while True:
pygame.draw.rect(window,PURPLE,(25,375,250,150),0)
pygame.display.update()
break
else:
pygame.draw.rect(window,PURPLE,(50,400,200,100),0)
pygame.display.update()
#this is the part where the code becomes glitchy
if instructionStartC == True:
while True:
pygame.draw.rect(window,PURPLE,(275,375,250,150),0)
pygame.display.update()
break
else:
pygame.draw.rect(window,PURPLE,(300,400,200,100),0)
pygame.display.update()

It's simply because you call pygame.display.update() multiple times.
You should create a standard game loop that typically does these three things:
handle input
update state
draw to screen
and then repeats.
In the 'draw to screen'-step, you draw all your sprites/rects/whatever to the screen surface, and then eventually call pygame.display.update() once at the end.
Calling pygame.display.update() multiple times, not clearing the screen between iterations of the loop and creating multiple unnecessary event loops are common beginner mistakes that lead to those kind of glitches IMHO.
So in your case, the code should probably look more like this:
if gameStartC:
pygame.draw.rect(window,PURPLE,(25,375,250,150),0)
else:
pygame.draw.rect(window,PURPLE,(50,400,200,100),0)
if instructionStartC:
pygame.draw.rect(window,PURPLE,(275,375,250,150),0)
else:
pygame.draw.rect(window,PURPLE,(300,400,200,100),0)
pygame.display.update()
I don't know what you expected the while-loops to do, and maybe you should use pygames Rect and Sprite classes. It will make your life easier.

Related

Pygame screen looping back

I am making a game in python, which has a main menu, followed by a difficulty select screen. When I select the play game button, the screen changes to the difficulty select screen as intended, but when I click a difficulty, it returns to the main menu screen, which is fully functional, leading me to believe that the function is continuing to run. If I then return to the difficulty select screen and select a difficulty a second time the game progresses as expected. No other button leads to this error which leads me to believe the error is in the select difficulty function itself.
def gameIntro(): # game intro loop
playButton=classes.Button(100,200,275,350,selectDifficulty) #classes is external file which contains a button class. params (x,y,w,h,action=None)
leaderButton=classes.Button(700,200,275,350,leaderboard)
quitButton=classes.Button(1000,200,250,350,quit)
instructButton=classes.Button(425,200,275,350,instructions)
spriteGroup = pygame.sprite.Group(playButton, leaderButton, quitButton, instructButton)
while not(playButton.loop and leaderButton.loop and quitButton.loop and instructButton.loop): #all of button objects have loop set to false, until they are clicked
eventLoop(spriteGroup) #runs an event handler for all of objects in spriteGroup
mainMenuScreen = pygame.image.load('Menu.jpg') # loads the menu Screens
mainMenuScreen = pygame.transform.scale(mainMenuScreen, (displayWidth, displayHeight)) #adjusts image to be same size as display
gameDisplay.blit(mainMenuScreen, (0, 0)) #displays the image
pygame.display.update() #updates the display
clock.tick(fps) #increments the clock by the fps amount to keep the screen changing
def selectDifficulty(): #selects the difficulty so that the appropriate AI is loaded
import Main
easyButton=classes.Button(70,150,300,400,Main.easyAIMaker)
mediumButton=classes.Button(450,150,300,400)
hardButton=classes.Button(800,150,300,400)
spriteGroup = pygame.sprite.Group(easyButton,mediumButton,hardButton)
while not (easyButton.loop and mediumButton.loop and hardButton.loop): #loops the screen while a choice hasnt been made
eventLoop(spriteGroup)
difficultyScreen = pygame.image.load('difficulty screen.jpg')
difficultyScreen = pygame.transform.scale(difficultyScreen, (displayWidth, displayHeight))
gameDisplay.blit(difficultyScreen, (0, 0))
pygame.display.update()
clock.tick(fps)
Problem was I didn't realise how python treated imported code. At the very end of intro.py the gameintro() function is called to start the game. When I imported intro.py this code was rerun causing the loop. By shielding game intro inside:
Def main():
gameIntro()
If __name__=="__main__":
Main()
The gameintro function only runs once

How to move from one screen to another, with any input from the user, in pygame? [duplicate]

I'm making a little game and I want to make another window separately from my main one.
I have the the main game in a main window, and I want to open a new window and do a little animation when the user does something.
In my example code below, when the user presses "a" I want it to open a new window and blit to there.
Here I set up the two windows: (I know this doesnt work, its what I'm asking how to do)
SCREEN_X = 400
SCREEN_Y = 400
BSCREEN_X = 240
BSCREEN_Y = 160
BATTLE_SCENE = pygame.display.set_mode((BSCREEN_X, BSCREEN_Y))
SCREEN = pygame.display.set_mode((SCREEN_X, SCREEN_Y))
and then the program:
def run_ani ():
#Do animation, blitting to BATTLE_SCENE
return
def main_game():
ending=False
while ending==False:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT: ending=True
if event.type == KEYDOWN: # key down or up?
if event.key == K_ESCAPE:
ending=True # Time to leave
print("Stopped Early by user")
elif event.key == K_a:
run_ani()
#Normal screen motion, blitting to SCREEN
if ending: pygame.quit()
return
So far what this does is draws the main screen, then when A is pressed, it stops drawing the main screen animations, but still draws the other animations on the main screen and draws in the top left corner.
I'm pretty sure it does this because I am setting BATTLE_SCENE to be smaller than the main screen, thus when blitting to BATTLE_SCENE it blits to the area I created (240x160) in the top corner of the main screen.
However I want BATTLE_SCENE to be a seperate window, so that when I press 'a' it will pop up, do its thing, then close or at least go behind the main screen.
How to do this? Is it even possible?
Do you really need multiple windows? I mean, do you really need them?
If yes, then you should probably use pyglet/cocos2d instead.
To have multiple windows in pygame, you need multiple processes (one for each window). While this is doable, it's not worth the efford. You'll need IPC to exchange data between the windows, and I guess your code will become error-prone and ugly.
Go with pyglet when you need more than one window.
The better solution is probably to divide your game into scenes. Create multiple scenes so that each one represent one stage of the game, something like MenuScene, MainScene, BattleScene, GameOverScene, OptionScene etc.
Then let each of those scenes handle input/drawing of that very part of the game.
MenuScene handles drawing and input etc. of the game's menu
MainScene handles drawing and input etc. of the running game
BattleScene handles drawing and input etc. of whatever you do in run_ani
In your mainloop, just pass control over to the current scene by implementing the methods draw(), handle_event(), and update().
Some example code to get the idea:
scenes = {'Main': MainScene(),
'Battle': BattleScene()} #etc
scene = scenes['Main']
class MainScene():
...
def handle_event(self, event):
if event.type == KEYUP:
if event.key == K_a:
scene = scenes['Battle']
...
class BattleScene():
...
def draw(self):
# draw your animation
def update(self):
# if animation is over:
scene = scenes['Main']
...
def main_game():
ending=False
While Not ending:
clock.tick(30)
for event in pygame.event.get():
scene.handle_event(event)
scene.update()
scene.draw()
This is an easy way to cleanly seperate the game logic and allow context switching.
======================================Edit=========================================
Actually it won't work. Apperantly pygame only supports one display screen, and when you initialize another, it will close the first. You will stay with two varibles, which in fact are the same surface. You can have instead the game increasing the window size and playing the battle scene on the side of it, to do this, you can call the pygame.display.set_mode() again with different values. The varible which references the display screen will still be usable, as it change its reference to the new one. After the scene is over you can decrease the window back the same way.
==================================================================================
What basically happens is you run a loop, and each iteration of it is rendering and displaying a new frame.
When you call a function inside a loop, it doesn't continue to run until you finish running the function.
One way to solve this problen is just keep calling the function that updates the battle scene in the main loop.
Another way is by using threading. Threading is basically running multiple scripts ("Threads") in the same time.
Luckily, python already implemented this for us with the threading module.
It's too long for me to explain the module here, but you can learn it here. It might be a little complex if you haven't use threads before, but after some time it will be easier.
And If you want to learn more about threading you can go here.
Specificly here, you can have two threads, one for each loop/window, and run them in the same time.
Hope I helped you!
Yes, that is possible. SDL2 is able to open multiple windows. In the example folder you can take a look at "video.py".
https://github.com/pygame/pygame/blob/main/examples/video.py
"This example requires pygame 2 and SDL2. _sdl2 is experimental and will change."

How to blit on the screen only once at mouse position

I have been trying to blit an image to my screen using the mouse position. This is my code.
#Placing the farm
if farm:
if pygame.mouse.get_pressed()[0]:
hi = True
if hi:
screen.blit(farm_image,mouse_pos)
So basically I want it to blit an image to screen only once at the mouse_position when the mouse is clicked., however it displays the image not once but moves with the mouse_pos. What would I do to make the image only be displayed once at the mouse_position. I do have a main loop before this.
What I have tried is setting hi to False after the image is blit, but that erases the image latter. Thank you for any answers you can provide!
Building on PythonMaster's answer something like that might work and ensure the farm remains blitted at every frame in case you refresh all your screen with a background every time:
#Placing the farm
if farm:
if pygame.mouse.get_pressed()[0] == False: #resets the first_click to False everytime the button is released
first_click = False
if pygame.mouse.get_pressed()[0]:
if first_click == False:
first_click = True
else:
pass
if first_click == True: #set's the farm position it will keep updating the position of the farm as you drag the mouse, if you do not want that set first_click to False here.
farm_pos = mouse_pos
try: #using try here, prevents errors when farm_pos is not yet defined
screen.blit(farm_image,farm_pos)
except:
print 'farm position not defined yet, click to place'
You could try using another variable to check to see if it was your first click or not:
if pygame.mouse.get_pressed()[0]:
if first_click == False:
hi = true
first_click = True
else:
pass
Make sure to define first_click before you use it. This code uses first_click to see if you clicked once already. Its starting value is False and changes to True once the mouse has clicked. This also changes hi into True while disallowing a second click as first_click is no longer False. This leads to the else and pass statements, which do practically nothing.
You can also use event checking instead of state checking when you only want a single click. This is especially useful because it means you won't miss a click (which might happen if the user clicks and releases the mouse at a moment when your code isn't checking for user input). Pygame has the events MOUSEBUTTONDOWN and MOUSEBUTTONUP which both give you the position of the mouse and the button that was clicked.
How your incorporate this into your code depends on the rest of your code, e.g. if you use the pygame event queue somewhere else already. Small example:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if farm:
screen.blit(farm_image,(event.pos[0], event.pos[1]))
You may use a variable to keep track of the state once the farm has been placed as suggested by PythonMaster and Sorade, or better yet, store the position and visible state of the farm in a farm object (Sprite) and simply blit it to its position whenever its state is set to visible.

How to get a .Rect to follow another based on the x,y x direction and y direction on the one in front?

I've been working on a snake code for quite a while now. It is still rather primitive and very very basic. I have gotten the collisions to work, food generation and very basic movement. What I'm struggling on is figuring out the movement. Whenever you press WASD or the Arrow Keys it would move the head, and the "body" of the snake follows the part in front of it. So snakecol[1] follows snakecol[0] and so on.
Thank you in advance for any tips and pointers on how to improve my code.
#W to move up, A to move to the left, S to move down, D to move right.
#Snake by Juan Jaramillo
import pygame
import sys
import random
import math
import time
pygame.init()
screen=pygame.display.set_mode((500,500))
point=3
def collision():
global foodx
global foody
global x
global y
global xd
global yd
global point
global snakecol
if snakecol[0].colliderect(foodcol):
foodx=random.randint(0,24)*20
foody=random.randint(0,24)*20
point+=1
snakecol.append(snakecol[len(snakecol)-1])
if xd==20:
snakecol[len(snakecol)-1].x-=20
if xd==-20:
snakecol[len(snakecol)-1].x+=20
if yd==20:
snakecol[len(snakecol)-1].y-=20
if yd==-20:
snakecol[len(snakecol)-1].y==20
print "Snake is",point,"long."
red=(255,0,0)
blue=(0,0,255)
green=(0,255,0)
black=(0,0,0)
block1=0
block2=1
count=0
screen.fill(black)
randFood=random.randint(0,24)
x=60
y=0
foodx=randFood*20
foody=randFood*20
snakecol=list()
snakecol.append(pygame.Rect(x,y,20,20))
snakecol.append(pygame.Rect(x-20,y,20,20))
snakecol.append(pygame.Rect(x-40,y,20,20))
foodcol=pygame.Rect(foodx,foody,20,20)
xd=[20]
yd=[0]
##snakecol=pygame.Rect(x,y,20,20)
foodcol=pygame.Rect(foodx,foody,20,20)
done=False
while not done:
screen.fill(black)
collision()
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
#controls
if block1==1:
if(pygame.key.get_pressed()[pygame.K_a]) or (pygame.key.get_pressed()[pygame.K_LEFT]):
yd[0]=0
xd[0]=-20
block1=0
block2=1
if block1==1:
if(pygame.key.get_pressed()[pygame.K_d]) or (pygame.key.get_pressed()[pygame.K_RIGHT]):
yd[0]=0
xd[0]=20
block1=0
block2=1
if block2==1:
if(pygame.key.get_pressed()[pygame.K_w]) or (pygame.key.get_pressed()[pygame.K_UP]):
xd[0]=0
yd[0]=-20
block1=1
block2=0
if block2==1:
if(pygame.key.get_pressed()[pygame.K_s]) or (pygame.key.get_pressed()[pygame.K_DOWN]):
xd[0]=0
yd[0]=20
block1=1
block2=0
#stop moving paddle
for m in range(0,len(snakecol)):
if snakecol[m].x<-20:
snakecol[m].x=500
if snakecol[m].x>500:
snakecol[m].x=-20
if snakecol[m].y<-20:
snakecol[m].y=500
if snakecol[m].y>500:
snakecol[m].y=-20
for m in range(0,len(snakecol)):
snakecol.pop
pygame.draw.rect(screen,(255,255,255),snakecol[m],0)
pygame.draw.rect(screen,(0,0,0),snakecol[m],1)
snakecol[m].x+=xd[0]
snakecol[m].y+=yd[0]
foodcol.x=foodx
foodcol.y=foody
pygame.draw.rect(screen,(255,255,255),(foodx,foody,20,20),0)
pygame.draw.rect(screen,(0,0,0),(foodx,foody,20,20),1)
pygame.display.flip()
time.sleep(0.4)
count+=1
pygame.quit()
The body blocks don't need to know the direction. You can just append a new head block (with the next x, y coords) to the list and remove the last block.
You could also use a collections.deque, pronounced "deck", a double ended queue which allows fast appends and pops on either end (although efficiency isn't really needed here, the code just looks a bit nicer).
A few more suggestions, you should check out how functions, classes and Pygame sprites work. There's a nice, free book about Python and Pygame called Program Arcade Games. Also, you shouldn't use global variables for everything, since they can make code really hard to understand (use classes).
Edit: To emphasize my answer above: Don't move the rects (the parts of the snake) in your list directly by changing their x and y values. Just pop or remove the last rect and append a new head rect. This will create the illusion of movement but the rects actually all stay at the same position.
I mentioned the deque, because it has efficient methods for popping and appending at the left end. For lists this is very inefficient, but I think it won't matter in your case, because the list won't grow too much and computers nowadays are very powerful.
Some code review (this should rather be in https://codereview.stackexchange.com/ though):
Line 132: You don't call pop because you forgot the parentheses. But if you actually called it, the snakecol list would shrink and the game would crash.
Handle the event loop first, then the game logic (e.g. collisions) and then the drawing. In a game class you would use different methods for these parts of the program.
You call pygame.key.get_pressed() several times. Instead assign the keys list to a variable keys = pygame.key.get_pressed() and use this variable later in the loop.
time.sleep is usually not used in Pygame (the game controls will seem to be unresponsive). There's the pygame.time.Clock class which you can use to limit the framerate and you can use a timer variable to keep track of when the snake can move. Instantiate a clock before the main loop clock = pygame.time.Clock() and in the loop you call clock.tick(enter_max_fps_here) every frame.

Pygame: Sprite changing due to direction of movement

I've just started learning how to use pygame yesterday. I was read this one book that was super helpful and followed all its tutorials and examples and stuff. I wanted to try making a really simple side scroller/platforming game but the book sorta jumped pretty fast into 3D modeling with out instructing how to make changing sprites for movement of up down left and right and how to cycle through animating images.
I've spent all today trying to get a sprite to display and be able to move around with up down left and right. But because of the simple script it uses a static image and refuses to change.
Can anyone give me some knowledge on how to change the sprites. Or send me to a tutorial that does?
Every reference and person experimenting with it ha always been using generated shapes so I'm never able to work with them.
Any help is very appreciated.
Added: before figuring out how to place complex animations in my scene I'd like to know how I can make my 'player' change to unmoving images in regards to my pressing up down left or right. maybe diagonal if people know its something really complicated.
Add: This is what I've put together so far. http://animania1.ca/ShowFriends/dev/dirmove.rar would there be a possibility of making the direction/action set the column of the action and have the little column setting code also make it cycle down in a loop to do the animation? (or would that be a gross miss use of efficiency?)
Here is a dumb example which alernates between two first images of the spritesheet when you press left/right:
import pygame
quit = False
pygame.init()
display = pygame.display.set_mode((640,480))
sprite_sheet = pygame.image.load('sprite.bmp').convert()
# by default, display the first sprite
image_number = 0
while quit == False:
event = pygame.event.poll()
no_more_events = True if event == pygame.NOEVENT else False
# handle events (update game state)
while no_more_events == False:
if event.type == pygame.QUIT:
quit = True
break
elif event.type == pygame.NOEVENT:
no_more_events = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
image_number = 0
elif event.key == pygame.K_RIGHT:
image_number = 1
event = pygame.event.poll()
if quit == False:
# redraw the screen
display.fill(pygame.Color('white'))
area = pygame.Rect(image_number * 100, 0, 100, 150)
display.blit(sprite_sheet, (0,0), area)
pygame.display.flip()
I've never really used Pygame before so maybe this code shoudln't really be taken as an example. I hope it shows the basics though.
To be more complete I should wait some time before updating, e.g. control that I update only 60 times per second.
It would also be handy to write a sprite class which would simplify your work. You would pass the size of a sprite frame in the constructor, and you'd have methodes like update() and draw() which would automatically do the work of selecting the next frame, blitting the sprite and so on.
Pygame seems to provide a base class for that purpose: link text.
dude the only thing you have to do is offcourse
import pygame and all the other stuff needed
type code and stuff..........then
when it comes to you making a spri
class .your class nam here. (pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.init(self)
self.image=pygame.image.load(your image and path)
self.rect=self.image.get_rect()
x=0
y=0
# any thing else is what you want like posistion and other variables
def update(self):
self.rect.move_ip((x,y))
and thats it!!!! but thats not the end. if you do this you will ony have made the sprite
to move it you need
I don't know much about Pygame, but I've used SDL (on which Pygame is based).
If you use Surface.blit(): link text
You can use the optional area argument to select which part of the surface to draw.
So if you put all the images that are part of the animation inside a single file, you can select which image will be drawn.
It's called "clipping".
I guess you will have a game loop that will update the game state (changing the current image of the sprite if necessary), then draw the sprites using their state.

Categories