Multiple Displays in Pygame - python-3.x

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."

Related

Change mouse cursor when entering/exiting Area2d

I have tried every google result that seems relevant and cannot find a solution to something I'm sure is simpler than I'm making it. There's a possibility all of the results are outdated, as the official documents don't seem to offer what I'm seeing in search result suggestions.
When the mouse enters an Area2D, I want the mouse cursor to change to the hand pointing cursor. When I exit the Area2D, I want it to change back to the pointer. I am not looking to use custom cursors, just the basic ones already implemented in GODOT.
I know how to use mouse_entered/mouse_exited signals. I just can't determine the correct code and location to make the change happen.
When the mouse enters an Area2D, I want the mouse cursor to change to the hand pointing cursor. When I exit the Area2D, I want it to change back to the pointer. I am not looking to use custom cursors, just the basic ones already implemented in GODOT.
For that, the functions you want are Input.set_default_cursor_shape and Input.get_current_cursor_shape.
I know how to use mouse_entered/mouse_exited signals. I just can't determine the correct code and location to make the change happen.
You place the code in whatever signal handlers you connected. I remind you that you can connect the signals from the IDE in the "Node" panel, "Signals" tab. And in the code, you should see a green icon next to the func is connected to a signal. See also Signals.
For example, I connected them to a script in the same Area2D, and added this code:
func _on_Area2D_mouse_entered() -> void:
Input.set_default_cursor_shape(Input.CURSOR_POINTING_HAND)
func _on_Area2D_mouse_exited() -> void:
Input.set_default_cursor_shape(Input.CURSOR_ARROW)
Or if you want to store the previous CursorShape, you can do this:
var old_cursor_shape:int = Input.CURSOR_ARROW
func _on_Area2D_mouse_entered() -> void:
old_cursor_shape = Input.get_current_cursor_shape()
Input.set_default_cursor_shape(Input.CURSOR_POINTING_HAND)
func _on_Area2D_mouse_exited() -> void:
Input.set_default_cursor_shape(old_cursor_shape)
See CursorShape for the values. Note: There seems to be a bug in the current beta that cause these to not autocomplete.
Reason why mouse_entered/mouse_exited might not work.
Assuming they are correctly connected.
If there is any Control (for example a Container or a background) overlapping a Node2D - such as an Area2D - regardless if it is in front or behind visually. It will prevent it from taking the input. Thus, you must set its mouse_filter of the Control to Ignore.
And make sure the collision_layer and collision_mask of the Area2D are not empty (0), input_pickable is true, and of course the Area2D has a valid CollisionShape2D or CollisionPolygon2D. Otherwise mouse_entered/mouse_exited won't work.
Also, I remind you to pay attention to any errors reported by the IDE.
One thing I recommend you can do would be to make an Area2D as a child of the root node (most likely a Node2D) rename the new Area2D to whatever you want (as long as it isn't named Area2D. For the sake of teaching you, I'm gonna rename it to CursorBox) and attach a script to that new Area2D. Add a CollisionShape2D node as a child of the Area2D, set the shape as a circle, and set the radius to 0.5. Next, go to the top right and press the node button. Connect "Area Entered' and "Area Exited" to the CursorBox. Inside of your script, make sure your functions look like what I have written below.
extends Area2D
var mouseCheck = false
func _on_CursorBox_area_entered(area):
if area.name == ("Area2D"): #This `Area2D` its looking for would be the buttons Area2D
mouseCheck = true
func _on_CursorBox_area_exited(area):
if area.name == ("Area2D"):
mouseCheck = false
The mouse check is just there to see if it works, obviously put your code in there, whatever you need to put in. I would imagine you have some animation you want to play or something. If you have any more questions, just reply and we'll see what else we can do to fix it.

How do i keep a Turtle in the foreground while 'spawning' other turtles?

Im creating a small retro space invaders type game and wanted to hide the turtles being spawned in the background of the program while having a "loading" screen in front. is their any way to do this? (This is the first question i've asked on here so i'm not sure what code i need to include)
As a rule, turtles can never appear behind anything drawn by turtles. I recommend you put up your "loading" screen message, and create your turtles invisibly:
turtle_4 = Turtle(visible=False)
Then, when you're ready, take down your loading screen message and use showturtle() to make your fully configured and positioned turtles visible.

How can the following errors be resolve in SCRATCH?

I created a bouncing ball game in scratch for CS50 PSET0.
The game works well as I expected, except for the following bug/errors:
The game can be paused by pressing the UP Arrow key on the keyboard, even BEFORE it is started or AFTER the game is over. THIS SHOULD ONLY WORK WHEN A LEVEL HAS BEEN SELECTED AND GAME STARTED AND NOT BEFORE OR AFTER IT IS STARTED. [WORKING ON IT]
I set the Paddle dragmode to not draggable, yet it's not working as you can still drag the paddle using the mouse. [SOLVED]
Falling eggs/bomb won't stop falling when the game is over or paused. [TRYING TO SOLVE IT BROUGHT A NEW BUG] One egg appears at the bottom of the screen after I paused and resume the game. I'm not sure how to hide that one stubborn egg.
On hitting the paddle or edge, the ball bouncing isn't very smooth and natural. It sometimes lag. [WORKING ON IT]
JUST CURIOUS: Why is the code "when I receive Setting btn Clicked" not working on the sound and music button sprite? [SOLVED]
I will appreciate all your kind suggestions.
Here's my project
NB: if the question seems unclear and you tend to understand it. Kindly feel free to suggest an edit. Thanks.
For point 1, you could test with an if statement whether the game is currently running. (Using the same variable you use to pause the game.)
As for the dragging of the paddle, I can't seem to drag it. That is, I can't drag it when I am playing, but I can when in the editor. This is always the case, you can drag any sprite around while editing, but not when playing without the editor open. This is the default behaviour of sprites as well, you do not have to declare it each time. A reason for you to want to drag sprites is when you use the Pen Extension to create a drawing program.
To stop the eggs from falling, you could pause the clones the same way you pause the ball when moving.
To let the ball turn more smoothly you could let it turn between an angle from -90° to 90° opposite to the surface it hits. To implement this would be quite difficult with a single line. You could, if you don't mind it hitting the edge instead of the border you built, split it so you include the built-in block: if on edge, bounce. The if-statement would then only include the touching of the paddle (and maybe the danger bar). In this statement, you would select an angle between -90 and 90 degrees.
For 5, there must've gone something wrong when you wanted to design this, the when I receive [setting btn clicked v] block is just not connected. :)

Working with pop up canvases

Trying to create a pop up canvas that will pop up when I hit the certain key combination and then it will display images that can be scrolled through using the arrow keys and then closed out with the escape key. This is all going on top of a canvas that has a map drawn on it that I can use the the arrow keys to move around the map. Since I'm having many issues with this that are all pretty much interrelated I figured I would just asking them all with one post instead of doing separate posts, it may come in very handy to have all the information in one place for other people as well.
Currently, I have tried several approaches to solve the problem, I do see one other possible option just a couple minutes ago since I finally got online this morning but it didn't seem to want to work.
Question 1: Where do you form the pop up canvas? In the original
def __init__(self, parent):
or do you normally wait until the time of the switch over to the pop up to form the pop canvas? I have tried it both ways and have seen problems with both approaches. Forming it at the start up causes the pop up to pop up on the screen right from the get go. I have seen on effbot.org that supposedly using the state='hidden' attribute exists but when I try to use it all I get is an error that says I can only use 'disabled' or 'normal'.
Question 2: So how do you hide the popup until you want to use it, if you create the pop up at the start of the program versus waiting until the pop up key combo is pushed?
I have tried sizing the pop up to 0x0 but I still see single pixel on the screen until I hit the key combo.
I have tried waiting until key combo is hit to bring up the pop up and that works fine until I get ready to change to the second image.
On effbot.org I saw and have tried several different combinations to try to 'disable' without destroying(which I don't want to do) the canvas underneath when the pop up comes up. The trouble I have is when I go and push an arrow key the underneath canvas still has the focus and it moves the map instead of change the image on the pop up. I have tried several of the effbot combinations...
self.canvas.config(state='disabled')
self.canvaspopup = Canvas(self, width=800, height=614)
self.canvaspopup.pack_propagate(0)
self.canvaspopup.place(x=284,y=52)
self.png = Label(self.canvaspopup)
self.png.pack()
self.canvaspopup.focus_set()
I have also tried
self.canvas.bind('<FocusOut>')
self.canvaspopup.bind('<FocusIn>')
And still no luck, the underneath canvas still has the focus and I'm still moving the map instead of advancing the image in the pop up. I am guessing given it was on the Events and Bindings page on effbot.org that I saw FocusIn/Out being talked about that I should put that in the .bind statement and not in the .config.
Question 3: How do I get the pop up to take the focus, pretty much should only need keyboard focus and no mouse focus?
I know I want to keep the pop up handy as I want to be able to click on the underlying map in different locations and have the program go to the internet and pull up data for those locations so the pop up is something that will be used several times throughout any one run of the program. I know from what I've seen I can use
self.canvaspopup.destroy()
But this destroys the canvas so I would have to recreate it again. So if I put the pop up creation at the beginning of the program...I'm screwed and can't recreate it. Which leads right back to Question 2.
Definitely a mess. I have most everything working fine, its just the interaction, and knowing the proper way of handling this kind of situation that has me stumped.
Since you are using place, you can use place_forget to hide the popup. For the focus, you use focus_set.
Here's an example:
import tkinter as tk
class PopupCanvas(tk.Canvas):
def __init__(self, parent, *args, **kwargs):
tk.Canvas.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.text_item=self.create_text(10, 10, anchor="nw")
self.bind("<Left>", self.handle_left)
self.bind("<Right>", self.handle_right)
self.bind("<Escape>", self.hide)
def handle_left(self, event=None):
self.itemconfigure(self.text_item, text="You clicked left")
def handle_right(self, event=None):
self.itemconfigure(self.text_item, text="You clicked right")
def show(self, event=None):
self.place(relx=.5, rely=.5, anchor="center")
self.focus_set()
def hide(self, event=None):
self.place_forget()
self.parent.focus_set()
root = tk.Tk()
label = tk.Label(root, text="Press 'p' to pop up a canvas, <escape> to hide it\n" +
"Press left and right arrows when the popup is visible")
canvas = tk.Canvas(root, width=400, height=400)
popup = PopupCanvas(canvas, width=200, height=200, background="pink")
label.pack(side="top", fill="x")
canvas.pack(side="top", fill="both", expand=True)
canvas.focus_set()
canvas.bind("<p>", popup.show)
root.mainloop()

Three.js First Person Controls moves the camera all the time

The game I'm designing currently requires a first person controller and luckily Three.js offers that class as well.
However I can't stop the camera from flying around. I know that the mouse movement causes the fly because it happens as soon as I move the mouse. But reading the js code,I cant find the attribue which causes this movement. Here is how I initiate the controls:
controls = new THREE.FirstPersonControls(camera);
controls.movementSpeed = 0.1;
controls.lookSpeed = 0.001;
controls.lookVertical = true;
I do not want the view direction to change when I am not moving the mouse.
any idea ?
Keep in mind that the FPS style mouse movement in webGL is usable rather only in a full screen mode. If an application runs in a standard windowed mode, the cursor is visible, and the application can not detect cursor movements that cross the edge of the window. This makes it impossible to look around in the FPS style (look movement stops when the cursors reaches the window edge).
This is probably the main reason why a PointerLockControls demo asks you to switch to the full screen mode.
With FirstPersonControls the look movement continues when the mouse reaches the edge. Such approach works well in the windowed mode.
You might want to use the PointerLockControls instead
See an example here:
https://github.com/mrdoob/three.js/blob/master/examples/misc_controls_pointerlock.html

Resources