This question already has answers here:
Pygame how to fix 'trailing pixels'?
(4 answers)
Closed 5 years ago.
I'm trying to develop a simple game in python using pygame and IDLE. I have, since yesterday, looked through a variety of sources in order to learn about the language (and programming in general), even so I have encountered some problems and misunderstandings of how it all works. So, if anyone could please advise me on how to proceed (or point me in the direction of some good learning material) then I would appreciate it greatly.
So far, I've got a small bit of code that forms the basis of my game idea, so I will post it here and list some of my problems.
import pygame
def main():
pygame.init()
logo = pygame.image.load("coolblack.jpg")
pygame.display.set_icon(logo)
pygame.display.set_caption("Battleship")
screenWidth = 800
screenHeight = 600
screen = pygame.display.set_mode((screenWidth, screenHeight))
bgd_image = pygame.image.load("grid.png")
#--------------------------------------------------------------------
#the image named 'image' should be above 'bgd_image' but below 'cv9'
#in fact, everything should be above bgd_image, especially 'cv9'
#--------------------------------------------------------------------
image = pygame.image.load("coolblack.jpg")
cv9 = pygame.image.load("ussessexcv9.gif").convert_alpha()
xposCv9 = 400
yposCv9 = 510
step_xCv9 = 1
step_yCv9 = 1
screen.blit(bgd_image, (0,0))
screen.blit(image, (400,300))
screen.blit(cv9, (xposCv9, yposCv9))
pygame.display.flip()
clock = pygame.time.Clock()
running = True
#---------------------------------------------
#I've got a pretty good idea (sort of) about
#what is happening in the section
#below this point, however it seems that
#the image 'cv9' creates a trail of itself
#every time it moves, so how could I make it
#so that it doesn't do so?
#---------------------------------------------
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
running = False
if xposCv9>screenWidth-64 or xposCv9<0:
step_xCv9 = -step_xCv9
if yposCv9>screenHeight-64 or yposCv9<0:
step_yCv9 = -step_yCv9
xposCv9 += step_xCv9
yposCv9 += step_yCv9
screen.blit(cv9, (xposCv9, yposCv9))
pygame.display.flip()
clock.tick(60)
if __name__=="__main__":
main()
The way that pygame works is that it has internally a representation of the screen which you are updating. So, it starts entirely black, then you do your first "blit". This will update the internal representation. Then when you call "pygame.display.flip" it shows that representation on the screen. However, this will not automatically "clear" the representation for you back to all black for your next frame. So, on the next frame, you blit again (slightly to the left, say), and the first blit remains, creating your "trail".
Therefore, for what you're doing, the best thing would be to in your loop, clear the internal representation of the screen before you start drawing the next frame. You can "clear" the screen by filling it with a single color, like so...
BLACK = (0,0,0)
....
screen.blit(cv9, (xposCv9, yposCv9))
pygame.display.flip()
clock.tick(60)
screen.fill(BLACK) # Add this to "clear" the screen.
Note that if you chose to go this route, this means you will need to redraw ALL of the elements every single frame (not just the ones that changed since the last frame).
BTW, in case you are wondering, there is a good reason for why the frame is not automatically cleared at the end. In some cases, it might be faster to only update the parts of the screen that update. This can cause performance speedups in some applications. However, it's probably best to start with clearing the screen as the example shows above.
Related
I was displaying some text on the window but when i executed the code, it only showed a blank black window. I had no idea what was wrong, as no exceptions occurred. It just would not work.
Well i am an absolute beginner who have just passed some Python course for a couple of weeks, so i was just playing around the pygame module, instead of having big plans like game developing.
I also tried to search for similar problems but they are all so complicated that I can not understand quite well due to the long pieces of code.
I checked and no syntax is wrong, the font file is present, and the names of the objects are in the right place i.e. they are not using the wrong methods.
i don't know what else i can try...
import pygame as pg
pg.init()
win = pg.display.set_mode((720,540))
consolas = pg.font.SysFont("Consolas.ttf", 100)
text = consolas.render("hello", False , (255,255,255))
win.blit(text , (0,0))
i expected the string "hello" will be blited on the surface with the size of 100 and color to be completely white, but the whole thing did not show up at all.
You have to call pygame.display.flip or pygame.display.update to actually update the screen with the content of the win surface.
Also, you should have a main loop that handles events by calling pygame.event.get. This ensures your window stays open. Also, if you don't process events, your window becomes unresponsive and maybe doesn't even draw anything (depending on your OS/window manager)
So add this to your code:
run = True
while run:
for e in pg.event.get():
if e.type == pg.QUIT:
run = False
pg.display.update()
You need to use pg.display.update() or pg.display.flip() after you have drawn the text i.e. after win.blit line. The difference and use of the two can be found here : Difference between pygame.display.update and pygame.display.flip
As people have already commented that you have to call pygame.display.update() or pygame.display.flip()
Here's the full code:
import pygame as pg
pg.init()
win = pg.display.set_mode((720,540))
consolas = pg.font.SysFont("Consolas.ttf", 100)
running = True
while running:
text = consolas.render("hello", False , (255,255,255))
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
win.blit(text , (0,0))
pg.display.flip()
pg.quit()
I am currently trying to produce an animation of lightning using pygame. I planned to have the lightning animation occur every 3 seconds using the pygame.timer.set_timer() module since I will have other animations occurring that are independent of the lightning animation.
For simplicity, I symbolized the lightning with a yellow triangle that elongates. Once it reaches past a certain point, I planned for the yellow triangle to stop, disappear for 3 seconds, before reoccurring again.
However, the lightning_event does not seem to be triggered every time within the while loop, and there is no delay happening.
I have looked at the invaders example using pygame.timer.set_timer(), although it does not use xy coordinates. I tried adjusting the invaders code to use xy coordinates, but it did not work either.
Another question I have is that if there is many events occurring at once, is using multithreading or multiprocessing a better option than using pygame.timer.set_timer()? If so, how can multithreading or multiprocessing be used with the pygame module? Especially because of python's GIL?
Here is my code for the lighting animation:
import pygame
pygame.init()
BLACK = (0,0,0)
WHITE = (255,255,255)
GREEN = (0,255,0)
RED = (255,0,0)
BLUE = (0,0,255)
YELLOW = (200,240,70)
size = (700,500)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Multithreading Project")
done = False
delay1 = 3000
clock = pygame.time.Clock()
def lightning(y_coord,y_coord_incr):
pygame.draw.polygon(screen,YELLOW,[[200,200],[100,y_coord],[300,y_coord]])
lightning_event = pygame.USEREVENT + 1
pygame.time.set_timer(lightning_event, delay1)
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == lightning_event:
if y_coord > 500:
y_coord = 200
screen.fill(WHITE)
lightning(y_coord,y_coord_incr)
y_coord = y_coord + y_coord_incr
pygame.display.flip()
clock.tick(60)
pygame.quit()
Thank you!
You could add a boolean variable (I called it lightning_anim_playing) and set it to True to start the animation.
While the animation is playing, increment the y_coord and draw the lightning.
Once the y_coord is greater than 500, start the timer for the next lightning (pygame.time.set_timer(lightning_event, delay1)) and set the boolean variable to False to stop the animation.
When the timer is done, stop it (pygame.time.set_timer(lightning_event, 0)), reset the y_coord and set the boolean variable to True again to start the next animation.
import pygame
WHITE = (255,255,255)
YELLOW = (200,240,70)
pygame.init()
screen = pygame.display.set_mode((700, 500))
clock = pygame.time.Clock()
delay1 = 3000
lightning_event = pygame.USEREVENT + 1
pygame.time.set_timer(lightning_event, delay1)
y_coord = 200
y_coord_incr = 3
lightning_anim_playing = False
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == lightning_event:
pygame.time.set_timer(lightning_event, 0) # Stop the timer.
lightning_anim_playing = True # Start the animation.
y_coord = 200 # Reset the y_coord.
if lightning_anim_playing:
# Update the y_coord.
y_coord += y_coord_incr
if y_coord > 500:
# Stop the animation.
lightning_anim_playing = False
# Start the timer for the next lightning.
pygame.time.set_timer(lightning_event, delay1)
screen.fill(WHITE)
# Draw the lightning if the animation is playing.
if lightning_anim_playing:
pygame.draw.polygon(screen,YELLOW,[[200,200],[100,y_coord],[300,y_coord]])
pygame.display.flip()
clock.tick(60)
pygame.quit()
You very rarely want to use multiprocessing or multithreading. For any graphical related code, it should be run in the main thread. Multiprocessing/multithreading only comes along if you're trying to load up or process large amounts of data while still keeping the window responsive. If you use other libraries such as for networking (twisted reactor) that have their own mainloop you can hijack their mainloop since pygame does not have a built in mainloop.
Pygame also has a more sequential api for handling events and you process all recent events before drawing to the screen so when events occur at the same time, you process them sequentially but any visual difference is seen once the screen is updated. The routine of fetching any recent events, handling them followed by updating the screen every frame is a rule of thumb for when making a Pygame app. Implementing multithreading/multiprocessing will usually reduce or have no effect on the performance of your program, all the while making it substantially more difficult to program and build upon. For animations you can also use timestamps using import time ; time.time() which allows you to not be restricted to pygame clock. As long as CPU intensive code is kept to a minimal between event fetch and draw cycles there is no reason to overcomplicate your code. You can always test your program during runtime for FPS drops to then selectively optimise your code.
As a side note, Pygame should not be used to make complicated games, more as a learning tool to develop your skills since Pygame is a somewhat limited and outdated package. For example, having multiple displays is tedious and is usually accomplished with multiprocessing, the Pygame event handler often does not give as much information as necessary (scroll wheel movement amount). A good alternative is Pyglet but is quite daunting to use at first.
I am just starting to look into Pygames, and am pretty armature when it comes to programming GUIs in general, but what I'm trying to create a simulation that is controlled by a user interface on the left side of the window. That is, both the simulation and the interface are on the same window as seen here:
I've been looking over a lot of similar questions/examples online, but they all are very in-depth and I really need something very dumbed down. That is to say, all I want to know is how to create the two displays on one window as shown above. I extracted some code for the "Simulation" part of it, which is generally outlined something like this:
import pygame
import math
pygame.font.init()
clock = pygame.time.Clock()
font = pygame.font.SysFont("", 20)
pygame.init()
width = 1000
height = 600
main_s = pygame.display.set_mode((width, height))
running = True
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
None
key = pygame.key.get_pressed()
As for the "Interface" component, I was planning on putting in a form using Tkinter, and using the input from that to manipulate the "Simulation".
I have seen some examples mention something about multiple scenes and classes. I don't know if that's what I'm looking for or not (since for whatever reason they never showed output), but as the code I copied above never mentions classes or scenes, I have no idea how that would be incorporated.
So before I run off and make things more complicated than necessary, I figured it was easier to simply ask: what is a simple outline to implement the configuration/interface I need?
Thanks,
Nathan
Sounds like the easiest thing would be to just create two separate surfaces and treat them as separate screens. For example, if you have a 800 x 600 window, you could create a 600 x 600 image and a 200 x 600 image.
Something like this...
...
actual_screen = pygame.display.set_mode((800, 600))
simulation_screen = pygame.Surface((600, 600))
interface_screen = pygame.Surface((200, 600))
...
while running:
# ... game code that renders to each surface as though
# they are separate screens ...
actual_screen.blit(simulation_screen, (0, 0))
actual_screen.blit(interface_screen, (600, 0))
pygame.display.flip()
...
Whenever I run my code the Python Window that shows up does not respond.
Is there something wrong with my code or do I have to re-install pygame and python?
I get a black pygame window and then it turns white and says not responding?
Also I am new to this so please make this as simple as possible. I tried looking everywhere for the answer but could not get it in a way that I could understand.
Please help me out. Thanks :)
1 - Import library
import pygame
from pygame.locals import *
2 - Initialize the game
pygame.init()
width, height = 640, 480
screen=pygame.display.set_mode((width, height))
3 - Load Images
player = pygame.images.load("resources/images/dude.png")
4 - keep looping through
while 1:
# 5 - clear the screen before drawing it again
screen.fill(0)
# 6 - draw the screen elements
screen.blit(player, (100,100))
# 7 - update the screen
pygame.display.flip()
# 8 - loop through the events
for event in pygame.event.get():
# check if the event is the X button
if event.type==pygame.QUIT:
# if it is quit the game
pygame.quit()
exit(0)
Don't import pygame.locals. It is actually unnecessary, since you are already importing pygame.
Also, as #furas said, it should be:
player = pygame.image.load("resources/images/dude.png")
Not:
player = pygame.images.load("resources/images/dude.png")
This will clear up some of the problems in your code.
From my personal experience,if you run pygame code from IDLE it often does not respond at all.Try saving your project as a .py file and then run it with python.exe.It always works for me.
And as furas said use
player = pygame.image.load("resources/images/dude.png")
instead of
player = pygame.images.load("resources/images/dude.png")
Replace that for loop with this
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
I have two files, one to generate a world, and another to run the main code. However, the main screen keeps crashing for no reason. I think the world gen may also be broken, but it does at least pass on valid data to the main code.
# Main loop.
while RUNNING:
# Fill the screen.
screen.fill((0,0,0))
# Event handling.
for eventa in event.get():
if eventa.type == QUIT:
RUNNING = f
screen.fill(SCREENCOLOR)
# Draw the world.
for tile in WORLD:
if tile.surface == None:
pass
else:
screen.blit(tile.surface,tile.location)
# Draw the character
screen.blit(PLAYER["image"],PLAYER["loc"])
# Pygame commands clear up.
clock.tick(FPS)
screen.flip()
This code doesn't even fill the screen with white. This may just be too much data to handle, sorry if it is.
World generator
Main code
Previous question
I'm fairly sure that you aren't inserting too many things onto the screen. I believe that the problem is far more simple. You have said screen.flip() However, a surface object has no attribute called flip. You must be confused with the function pygame.display.flip() If you use this instead, the game shall display its visual output.