Calling another pygame-zero program from a main pygame-zero window - python

I want to create multiple small games in pygame zero, with a main window on which you have buttons to click - when clicking a button it would start a new window and a new subgame (coded on its own in another file). The secondary games should be able to return if the game was won or lost. Is this possible using pygame-zero? I am thinking the subgame should be encapsulated in some function to be able to give a return value, but I am not sure if this is doable in pygame-zero since it calls some functions itself... any idea?
Or am I better off adding some kind of game state in the main program and do everything there like this?
def update():
if state == 'game1':
#all of the game1 update code here
elif state == 'game2':
#all of the game2 update code here
#etc
def draw():
if state == 'game1':
#all of the game1 draw code here
elif state == 'game2':
#all of the game2 draw code here
#etc

I think you are definitely better off by implementing a game state. I'd use an object-oriented approach like:
class GameState:
def update():
pass
def draw():
pass
class Game1(GameState):
def update():
# ...
def draw():
# ...
class GameChooser(GameState):
def update():
# choose game here
global state
if ...:
state = Game1()
def draw():
# ...
state = GameChooser()
def update():
state.update()
def draw():
state.draw()

Related

Kivy togglebutton only turning sound on/off once

Ive tried looking for an answer to this without much luck.
Im trying to build an application in kivy that starts and stops a sound as the button is toggled. The sound does what I want the first time the button is toggled, but the second time the button only starts the sound, but does not stop the sound.
Here is my code so far.
'''code'''
class MainApp(App):
def build(self):
layout = BoxLayout(padding=10)
self.oceanButton = ToggleButton(text='Ocean',
background_normal='C:/Users/micha/Desktop/Code/Soothing Sounds/picture/oceanpic.jpg')
self.oceanButton.bind(on_press=self.on_press_button)
layout.add_widget(self.oceanButton)
return layout
def on_press_button(self, *args):
waveSound = SoundLoader.load(
'C:/Users/micha/Desktop/Code/Soothing Sounds/sounds/ocean.wav'
)
if self.oceanButton.state == 'down':
waveSound.play()
waveSound.loop = True
print('On')
else:
waveSound.stop()
print('Off')
The problem is that your on_press_button() method always creates a new instance of Sound (using SoundLoader). So, when the ToggleButton state is not down, it calls the stop() method on that new instance, and the Sound that was created on the previous call continues to play.
You can fix that by keeping a reference to the created Sound instance, and using that instance to call stop():
def on_press_button(self, *args):
if self.oceanButton.state == 'down':
self.waveSound = SoundLoader.load(
'C:/Users/micha/Desktop/Code/Soothing Sounds/sounds/ocean.wav'
)
self.waveSound.play()
self.waveSound.loop = True
print('On')
else:
self.waveSound.stop()
print('Off')

How to set canvas camera position in python 3.7

I am writing a mario-like game (non-commercial, I'm just trying to brush up on coding) and I need to follow one of two players. Messing with canvas.move doesn't work for my needs, I was wondering if there was some way to actually control what position it renders from (such as one player offscreen and the camera moves to make him onscreen). My code:
from tkinter import *
import math,time
## Declare your many GAME CLASSES here
class Camera:
def __init__(self,game):
self.cnv=game.canvas
self.tracker=None
def track(self,player):
self.tracker=player
def run(self):
cords=self.cnv.coords(self.tracker.id)
self.cnv.move(ALL,250-cords[0],250-cords[1])
class Human:
def __init__(self,game):
game.tk.bind_all("<KeyPress>",self.press)
game.tk.bind_all("<KeyRelease>",self.release)
self.left=False
self.right=False
self.jump=False
def run(self,player):
if self.left:
player.left()
if self.right:
player.right()
if self.jump:
player.jump()
self.jump=False
def press(self,event):
if event.keysym=="Left":
self.left=True
if event.keysym=="Right":
self.right=True
if event.keysym=="Up":
self.jump=True
def release(self,event):
if event.keysym=="Left":
self.left=False
if event.keysym=="Right":
self.right=False
def hitx(self):
pass
def hity(self):
pass
class Computer:
def __init__(self):
self.direction=1
def run(self,player):
if player.onground:
if self.direction==1:
player.right()
if self.direction==-1:
player.left()
def hitx(self):
self.direction*=-1
def hity(self):
pass
class Player:
def __init__(self,startx,starty,game,controller):
self.id=game.canvas.create_image(250+startx,250+starty,anchor=CENTER,image=game.player_images[0])
self.game=game
self.xv=0
self.yv=0
self.controller=controller
self.speed=4
self.face=0
self.side=0
self.onground=False
def left(self):
self.xv-=self.speed
self.side=1
self.face=tick%2
def right(self):
self.xv+=self.speed
self.side=0
self.face=tick%2
def jump(self):
if self.onground:
self.yv=math.sqrt(self.speed)*-10
def look(self):
bricks=[]
for x in self.game.tileset:
coords=self.game.canvas.coords(x[0])
playercoords=self.game.canvas.coords(self.id)
xdifference=abs(coords[0]-playercoords[0])
ydifference=abs(coords[1]-playercoords[1])
if xdifference<250 and ydifference<250:
bricks.append(coords)
def run_slf(self):
if not self.onground:
self.face=2
self.set_face()
self.face=0
self.onground=False
self.game.canvas.move(self.id,self.xv,0)
## Work in progress: Do the X COLLISIONS FOR THE PLAYER
bounds=self.getbounds()
if len(self.game.canvas.find_overlapping(*bounds))>1:
while len(self.game.canvas.find_overlapping(*bounds))>1:
self.game.canvas.move(self.id,abs(self.xv)/self.xv*-1,0)
bounds=self.getbounds()
self.xv=0
self.controller.hitx()
self.game.canvas.move(self.id,0,self.yv)
## Work in progress: Do the Y COLLISIONS FOR THE PLAYER
bounds=self.getbounds()
if len(self.game.canvas.find_overlapping(*bounds))>1:
while len(self.game.canvas.find_overlapping(*bounds))>1:
self.game.canvas.move(self.id,0,abs(self.yv)/self.yv*-1)
bounds=self.getbounds()
self.yv=0
self.onground=True
self.controller.hity()
self.controller.run(self)
self.xv*=0.8
self.yv+=1
def set_face(self):
self.game.canvas.itemconfig(self.id,image=game.player_images[self.face+(self.side*3)])
def getbounds(self):
width=25
height=45
cords=self.game.canvas.coords(self.id)
return [cords[0]-width,cords[1]-height,cords[0]+width,cords[1]+height]
class Game:
def __init__(self,width,height):
self.tileset=[]
self.players=[]
self.tk=Tk()
self.brick_types={"regular":[False,PhotoImage(file="blocks/brick_basic.png")]}
self.tk.resizable(0,0)
self.canvas=Canvas(self.tk,width=width,height=height,background="white")
self.canvas.pack()
self.player_images=[PhotoImage(file="marioAnim/face.png"),PhotoImage(file="marioAnim/walk.png"),PhotoImage(file="marioAnim/jump.png"),PhotoImage(file="marioAnim/face-2.png"),PhotoImage(file="marioAnim/walk-2.png"),PhotoImage(file="marioAnim/jump-2.png")]
def run(self):
self.tk.update_idletasks()
self.tk.update()
for x in self.players:
x.run_slf()
def addbrick(self,x,y,tp):
self.tileset.append([self.canvas.create_image(x*50+250,y*50+250,anchor="nw",image=self.brick_types[tp][1]),self.brick_types[tp][0]])
def addline(self,x,y,xd,yd,length,tp):
for i in range(0,length):
self.addbrick(x+xd*i,y+yd*i,tp)
def addplayer(self,player):
self.players.append(player)
## Declare your GLOBAL VARIABLES here.
game=Game(500,500)
human=Human(game)
computer=Computer()
cplayer=Player(-50,0,game,computer)
hplayer=Player(50,0,game,human)
tick=0
camera=Camera(game)
camera.track(cplayer)
## BUILD TILESET
game.addline(-8,4,1,0,16,'regular')
game.addbrick(-5,3,'regular')
game.addbrick(4,3,'regular')
## ADD PLAYERS
game.addplayer(cplayer)
game.addplayer(hplayer)
while 1:
tick+=1
camera.run()
game.run()
time.sleep(0.02)
I am using python 3.7 with Tkinter.
You are asking how to programatically scroll the canvas. The xview and yview methods of the canvas control what portion of the full drawable area is visible at the current time: xview, xview_moveto, xview_scroll, yview, yview_moveto, and yview_scroll.
The xview_scroll and yview_scroll methods accept an integer amount, and then the string "units" or "pages". "units" refers to the distance defined by the xscrollincrement and yscrollincrement attributes. "pages" causes the window to scroll in increments of 9/10ths of the window width or height.
For example, if you want to be able to scroll by single pixels you can set xscrollincrement to 1, and the use xview_scroll to move left or right.
canvas.configure(xscrollincrement=1)
...
canvas.xview_scroll(1, "units")

Call a function every 1 seconds in python

class Viewer:
def __init__(self, width, height, arm_info, point_info, point_l):
self.list_x = [1,2,3,4]
self.list_y = [5,6,7,8]
self.i = 0
self.arm_info = arm_info
self.point_info = point_info
def on_key_press(self,symbol,modifiers):
if symbol == pyglet.window.key.Z:
self.point_info[:] = [self.list_x[self.i],self.list_y[self.i]]
self.i += 1
Here to update the point_info[:] I have to press the 'Z' everytime, I just want Z to be pressed once to update point_info[:] in every 1 second .
def on_key_press(self,symbol,modifiers):
if symbol == pyglet.window.key.Z:
for i in range(0,4):
self.point_info[:] = [self.list_x[i],self.list_y[i]]
time.sleep(1)
I have tried above but it doesn't work. How can I do this?
Here is the complete code, the render method is called from another module.
class Viewer(pyglet.window.Window):
def __init__(self, width, height, arm_info, point_info, point_l, mouse_in):
self.list_x = [100,150,200,300,400]
self.list_y = [100,150,200,300,400]
#initialisation of variables
def render(self):
pyglet.clock.tick()
self._update_arm()
self.switch_to()
self.dispatch_events()
self.dispatch_event('on_draw')
self.flip()
def on_draw(self):
#draws on the screen
def _update_arm(self):
#updates the coordinates of the arm , as it moves
def on_key_press(self, symbol, modifiers):
#HERE LIES THE PROBLEM
if symbol == pyglet.window.key.S:
for j in range(0,4):
self.point_info[:] = [self.list_x[j],self.list_y[j]]
print(self.point_info)
#below 2 lines are for drawing on the screen.
self.clear()
self.batch.draw()
j=+1
time.sleep(1)
I created a small runnable example code, on how to achieve this. The point is, that sleep() blocks the program flow. With pyglet, you have a convenient way to schedule future executions with the pyglet.clock.schedule* methods. You can use that, to call a specific function in the future.
Note: Actual, piglet is running a main loop in it's framework. If you hold the program at some position (like you do with sleep()), no further code can be executed meanwhile, therefore, no drawing can happen, if pyglet requires to call some required methods around the draw call. I guess, you are not supposed to call the on_draw() method by yourself.
import pyglet
from pyglet.window import key
from pyglet import clock
import random
window = pyglet.window.Window()
label = pyglet.text.Label('Hello, world',
font_name='Times New Roman',
font_size=36,
x=window.width//2, y=window.height//2,
anchor_x='center', anchor_y='center')
def updatePoint(dt):
label.x = random.random() * window.width//2
label.y = random.random() * window.height//2
#window.event
def on_key_press(symbol, modifiers):
if symbol == key.S:
print('The "S" key was pressed.')
for j in range(0,4):
clock.schedule_once(updatePoint, j)
#window.event
def on_draw():
window.clear()
label.draw()
if __name__ == '__main__':
pyglet.app.run()

Python variable not recognized in if-statement [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
Here is all the code, but in main(), a while loop checks if on_title_screen is true, and if it is, displays the title screen, but if not, displays the game. However, after starting the program, running the game, and returning to the title screen, attempting to press the start button makes both the code for if on_title_screen==True and elif on_title_screen==False run when only the first bit should run.
import random
import pygame
from pygame import *
import math
import sys
#Presets for window
size=width,height=500,500
Ag=-9.80665
clock = pygame.time.Clock()
white=(255,255,255)
blue=(0,0,255)
red=(255,0,0)
gray_bgColor=(190,193,212)
#Initialise pygame Surface as screen
pygame.init()
pygame.font.init()
screen=pygame.display.set_mode(size)
pygame.display.set_caption("Flappy Bird Replica")
def falling_loop():
for event in pygame.event.get():
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_UP:
preset.vY=-10
if preset.yPos>height-50:
preset.on_title_screen=True
preset.vY+=1
preset.yPos+=preset.vY
class presets():
#Holds all the "global" values for the game
vY=0
xPos,yPos=200,100
on_title_screen=True
class graphics():
#Holds the methods for loading/displaying graphics
def load_images(self):
#Loads the background and sprite images
self.background_image=pygame.image.load("flappy_background.png").convert()
self.bird_image=pygame.image.load("flappy_sprite.jpg").convert()
self.bird_image.set_colorkey(white)
self.birdHitBox=self.bird_image.get_rect()
def show_background(self):
#blits the background
screen.blit(self.background_image,[0,0])
def show_bird(self):
#blits the bird onto screen at xPos, yPos
screen.blit(self.bird_image,[preset.xPos,preset.yPos])
def refresh_display(self):
#updates the display
screen.blit(self.background_image,[0,0])
falling_loop()
self.show_bird()
class titleScreen():
#Holds the methods for the title screen/menu
def title(self):
#Sets up the title
titleText="Flappy Game"
titlePos=(0,0)
currentFont=pygame.font.SysFont("arialms",30,bold=True,italic=True)
renderTitle=currentFont.render(titleText,1,blue,gray_bgColor)
self.titlex,self.titley=currentFont.size(titleText)
screen.blit(renderTitle,titlePos)
def start(self):
#Sets up the start Button
startText="Start Game"
self.startPos=(0,self.titley)
currentFont=pygame.font.SysFont("arialms",25,bold=False,italic=False)
renderStart=currentFont.render(startText,1,blue,gray_bgColor)
self.startx,self.starty=currentFont.size(startText)
self.start_rect = pygame.Rect(self.startPos[0],self.titley,self.startx,self.starty)
screen.blit(renderStart,self.startPos)
def quit(self):
#Sets up the quit button
quitText="Quit"
self.quitPos=(0,self.starty+self.titley)
currentFont=pygame.font.SysFont("arialms",25,bold=False,italic=False)
renderQuit=currentFont.render(quitText,1,red,gray_bgColor)
self.quitx,self.quity=currentFont.size(quitText)
self.quit_rect = pygame.Rect(self.quitPos[0],self.titley+self.starty,self.quitx,self.quity)
screen.blit(renderQuit,self.quitPos)
def get_click(self):
#Gets mouse click and processes outcomes
for event in pygame.event.get():
if event.type==pygame.MOUSEBUTTONDOWN:
x,y=pygame.mouse.get_pos()
#Tests for start:
if self.start_rect.collidepoint(x,y):
print("start")
preset.on_title_screen=False
graphicsC.show_background()
elif self.quit_rect.collidepoint(x,y):
print("quit")
sys.exit()
#Assign objects to respective classes
preset=presets()
titleC=titleScreen()
graphicsC=graphics()
def setupTitle():
#bundles all title_screen functions
titleC.title()
titleC.start()
titleC.quit()
def main():
graphicsC.load_images()
graphicsC.show_background()
setupTitle()
while True:
clock.tick(30)
if preset.on_title_screen==False:
graphicsC.refresh_display()
print("working...")
elif preset.on_title_screen==True:
setupTitle()
titleC.get_click()
pygame.display.flip()
main()
You have to use self.xxx to make class attributes. You also need to make sure those values are defined when initializing the class using the __init__ method. So use:
class presets():
#Holds all the "global" values for the game
def __init__(self):
self.vY=0
self.xPos,self.yPos=200,100
self.on_title_screen=True
However, making 'global' variables this way probably isn't the best way, as Cyber mentions in the comments.

How to find is a canvas item is touching another canvas item, Tkinter

I have a program where the user controls a block and navigates through pipes, similar to the game flappy bird. I want to know if tkinter has a way of telling me if one rectangle touches or hits another. I know there is a find_overlapping method for the canvas, but when i use find_overlapping on the pipe's rectangle it gives me the id of the pipe! I want to know if canvas items have a way of knowing if another canvas item is touching it!
Thanks
Here is my code:
from Tkinter import *
from random import *
root=Tk()
c=Canvas(root,width=600,height=600)
speed=20
num=1
first=True
b=0
pipes=[]
done=False
class Pipe():
def __init__(self,canvas,pipes,length,width=75,color="Green",position=600,speed=5):
self.speed=speed
self.canvas=canvas
self.length=length
self.width=width
self.color=color
self.position=position
self.current1=self.canvas.create_rectangle((self.position,0,self.position+self.width,self.length),outline=self.color,fill=self.color)
self.current2=self.canvas.create_rectangle((self.position,600,self.position+self.width,self.length+150),outline=self.color,fill=self.color)
self.pipes=pipes
self.pipes.append(self.current1)
self.pipes.append(self.current2)
def move(self):
global done
if (len(self.canvas.find_overlapping(self.position,0,self.position+self.width,self.length))==1 and len(self.canvas.find_overlapping(self.position,600,self.position+self.width,self.length+150))==1) and not done:
self.position-=3
self.canvas.coords(self.current1,(self.position,0,self.position+self.width,self.length))
self.canvas.coords(self.current2,(self.position,600,self.position+self.width,self.length+150))
if self.position>-75:
self.canvas.after(self.speed,self.move)
else:
self.pipes.remove(self.current1)
self.pipes.remove(self.current2)
self.canvas.delete(self.current1)
self.canvas.delete(self.current2)
else:
print self.canvas.find_overlapping(self.position,0,self.position+self.width,self.length)
print
print self.canvas.find_overlapping(self.position,600,self.position+self.width,self.length+150)
done=True
class Player():
def __init__(self,canvas,x=150,y=300,size=40):
self.size=size
self.faller=True
self.x=x
self.y=y
self.fell=5
self.canvas=canvas
#For now
self.current=self.canvas.create_rectangle((self.x-20,self.y-20,self.x+20,self.y+20),tags="user",outline="Blue",fill="Blue")
self.canvas.after(100,self.fall)
self.canvas.bind("<1>",self.jump)
def fall(self):
global done
if self.faller and not done:
self.y+=self.fell
self.fell*=1.001
self.canvas.coords(self.current,(self.x-20,self.y-20,self.x+20,self.y+20))
self.canvas.after(30,self.fall)
elif done:
a=600-self.y+20
a/=50
while self.y<580:
self.y+=a
self.canvas.coords(self.current,(self.x-20,self.y-20,self.x+20,self.y+20))
def jump(self,e):
if not done:
self.faller=False
for x in range(10):
self.canvas.after(100,self.move)
self.faller=True
self.fell=5
def move(self):
self.y-=7.5
self.canvas.coords(self.current,(self.x-20,self.y-20,self.x+20,self.y+20))
def changey(self,a):
self.y=a
def run():
global b,first,done
if not done:
if first:
b=randint(5,450)
first=False
else:
if b<225:
b=randint(5,b+225)
if b>225:
b=randint(b-225,450)
else:
b=randint(0,600)
a=Pipe(c,pipes,b)
a.move()
c.after(2000,run)
else:
return
root.focus_set()
user=Player(c)
c.after(2000,run)
c.pack()
root.mainloop()
Canvas.find_overlapping return a tuple with all the ids of shapes in the bbox. If only your id is returned it might be because it is the sole one.
You can use it with the bbox of another shape like this : canvas.find_overlapping(*canvas.bbox(aShape)).
Note that overlapping works on rectangle and might be erroneous for instance on circles.

Categories

Resources