Create/manage two displays in the same window in Pygames - python

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()
...

Related

Python Can't load background images in pygame

i try to load backgroud.png using pygame.image.load(),but i get nothing.here is my code,please help me ,thanks.
import pygame
pygame.init()
# screen
screen = pygame.display.set_mode((480, 700))
# 1.load_image
bg = pygame.image.load("./images/background.png")
# 2.blit
screen.blit(bg, (0, 0))
# 3.update
pygame.display.update()
while True:
pass
pygame.quit()
here is my sreen:it get nothing
With a game you're making all stuff that needs to be refreshed needs to be in the main game loop, your problem is, is you are drawing the image outside that game loop, meaning it gets drawn once then cleared and never drawn again.
To fix your code this is how you would write it:
import pygame
pygame.init()
# screen
screen = pygame.display.set_mode((480, 700))
# 1.load_image
bg = pygame.image.load("./images/background.png")
while True:
# 2.blit
screen.blit(bg, (0, 0))
# 3.update
pygame.display.update()
pygame.quit()
But notice how the bg=pygame.image... is outside the loop, this is because if it was inside the loop it would create a new instance of that image every time the loop happens.
The main game loop works by looping through all your functions and other stuff and then doing again and again, and again.
A game loop is how fps works, basically it is the measurement of how many times per second that game loop happens.
Make sure whenever you are doing anything in the loop it actually has a place there, for example loading an image doesn't, but updating where the player is on the screen does.
If you want to have a look at a good game loop that can be applied to most game engines this website will help you. But don't look at the most complex one when using pygame as it isn't built for that. Fix Your Timestep!
But your original problem of the image not loading isn't the case, it was loading but you were drawing your image in the wrong way, if you want a basic tutorial on pygame watch these videos: Game Development in Python 3 With PyGame - 1 - Intro
A better system to ease development
import pygame
bg = None
def load_resources():
bg = pygame.image.load("./images/background.png")
# all other resources
def render():
screen.blit(bg, (0,0))
def update():
# all logic updates for example movement of entities.
### start of game
load_resources()
while True:
update()
render()
pygame.display.update()
pygame.quit()
I think you should not write pass inside while loop because of it the output window will stop responding. Also you should write the screen blit and display.update inside while loop and you should write the correct image extension in the path. You can also write the full path of the file like ==> pygame.image.load(r"C:\Users\Desktop\back_ground.jpg")
import pygame
pygame.init()
# screen
screen = pygame.display.set_mode((480, 700))
# 1.load_image
bg = pygame.image.load("back_ground.jpg")
while True:
# 2.blit
screen.blit(bg, (0, 0))
# 3.update
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()

Pygame - Text won't appear on the window

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()

Pygame Lightning animation using pygame.timer.set_timer()

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.

Images distorted using pygame

Trying to build my first pygame project and wanted to import the following map. Used image:
My code is:
import pygame
pygame.init()
size = (1300, 700)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My Game")
done = False
clock = pygame.time.Clock()
map = pygame.image.load('map.jpg')
map = map.convert()
map = pygame.transform.scale(map, (466,700))
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill((255,255,255))
screen.blit(map, (430, 0))
pygame.display.flip()
clock.tick(60)
pygame.quit()
and the output looks like:
I have also tried converting the jpg to a bmp but still the same results. I cannot figure out what is distorting the image, trying with other images it appears that the outputted images is slightly wider than the original. I suspect that pygame is just reading the width wrong and then putting the pixels into an array with that width.
Any advice on fixing this would be much appreciated.
There is a bug in SDL_image (which Pygame is based on) that corrupts images in OS X 10.11.
https://bugzilla.libsdl.org/show_bug.cgi?id=3154
Currently, the only known workaround is to downgrade to SDL_image 1.2.10, but I have not been able to confirm.

Why Does PyGame Trail the Image? [duplicate]

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.

Categories

Resources