I am using pygame.rect.move() to move a button to a certain position on instantiation. Then from the list of objects to blit that exists the menu button is called and all functions that need to be called each frame. The button does NOT move.
The rect should perfectly fit the button.
Is my use of pygame.rect.move() incorrect?
Here's the class:
class menuButton(mainMenu):
def setpos(self):
self.r = self.r.move(self.p)
def __init__(self,i,e,s,p, ai, ao,sa):
self.t = "Menu Button"
self.i = pygame.image.load(i)
self.r = self.i.get_rect()
self.e = e
self.ai = ai
self.ao = ao
self.p = p
self.a = 0
self.i.set_alpha(sa)
pygame.transform.scale(self.i,s)
objects.append(self)
print "%s has been instantiated." % (self.t)
def logic(self):
pass
def animations(self):
if self.ai == "FADE_IN":
if(self.i.get_alpha() < 255):
self.i.set_alpha(self.i.get_alpha() + 1)
def update(self):
self.r = self.i.get_rect()
r = True
for obj in objects:
if isinstance(self,type(obj)) == False:
r = False
if r == True:
if self.a == 0:
self.setpos()
print self.r
self.a += 1
self.logic()
self.animations()
screen.blit(self.i,self.r)
pygame.display.flip()
I'm not using .convert() at the end of an image import because it breaks the images.
I'm not using sprites because I want more control.
I have also written test programs and it seems to have worked, and I've re-written the main menu button class 3 times. Same issue.
m not using .convert() at the end of an image import because it breaks the images.
In what way? You probably want the alpha version http://www.pygame.org/docs/ref/surface.html#pygame.Surface.convert_alpha
I'm not using sprites because I want more control.
You can derive from Sprite , then you can add anything you want, yet still use sprite.Groups
A side note on the names, a Menu would contain Buttons. But buttons deriving from menu doesn't make sense.
move() returns a new rect that has moved, move_ip() modifies the existing rect.
Or you can use the rect properties
def setpos(self):
self.r.topleft = self.p
What are you doing with this code?
r = True
for obj in objects:
if isinstance(self,type(obj)) == False:
r = False
if r == True:
if self.a == 0:
self.setpos()
print self.r
self.a += 1
Related
This question already has answers here:
Pygame mouse clicking detection
(4 answers)
Closed 1 year ago.
I am trying to make a button class in pygame and python but i dont know how i put code for when i will click the button it will execute code
the code i want to execute is :-
num1 = 1
num2 = 100
num2 = int(rand)-1
rand = (num1+num2)//2
text_screen(str(rand),"#DEA33E",font2,795,185)
pygame.display.update()
and this is the Button class
class Button2:
def __init__(self,text,width,height,pos,elevation):
# Core Attributes
self.pressed = False
self.elevation = elevation
self.dynamic_elevation = elevation
self.original_y_pos = pos[1]
# top rectangle
self.top_rect = pygame.Rect(pos,(width,height))
self.top_color = "#475F77"
# bottom rectangle
self.bottom_rect = pygame.Rect(pos,(width,elevation))
self.bottom_color = "#354B5E"
# text
self.text_surf = font3.render(text, True, "#FFFFFF")
self.text_rect = self.text_surf.get_rect(center = self.top_rect.center)
def draw(self):
# elevation logic
ot = 1
ot = ot + 0
self.top_rect.y = self.original_y_pos - self.dynamic_elevation
self.text_rect.center = self.top_rect.center
self.bottom_rect.midtop = self.top_rect.midtop
self.bottom_rect.height = self.top_rect.height + self.dynamic_elevation
pygame.draw.rect(screen,self.bottom_color,self.bottom_rect,border_radius = 12)
pygame.draw.rect(screen,self.top_color,self.top_rect,border_radius=12)
screen.blit(self.text_surf,self.text_rect)
self.check_click()
def check_click(self):
mouse_pos = pygame.mouse.get_pos()
if self.top_rect.collidepoint(mouse_pos):
self.top_color = "#D74B4B"
if pygame.mouse.get_pressed()[0]:
self.dynamic_elevation = 0
self.pressed = True
else:
self.dynamic_elevation = self.elevation
if self.pressed == True:
self.pressed = False
also i want to do so like i can use the rand and num1,num2 variable outside the class
Read about Classes and Instance Objects. A class has no variables, it has attributes. YYou have to create one (or more) instances of the class, then you can access the attributes of the class:
button = Button2(...)
if button.pressed:
# do somthing
# [...]
I have this code that saves the position and the rect from an object (usally small, like a drawing) and then spawns them (blits) in my screen. I encountered that if I put too many objects or too big, I´m guessing the rects collide and then the game crashes, but also, sometimes, even if I have not many objects, it can crash because this occurance.
How could I solve this problem? I´m guessing adding an if sentence so it checks that is not as near as to crash the game or something like that, where I save the rects of the images is in the for i in self.game_images: :
class GameScene(Scene):
def __init__(self, game, images, main_image, next_scene):
super().__init__(next_scene)
self.game = game
self.main_image = main_image
self.game_images = images
# Fade effect set-up
self.fade = False
self.fade_time = 0
self.current_alpha = 255
self.part = 1
self.record_text = font.render('Atiende',True, PURPLE)
self.correct_image_rect = None
# Trying to use colliderect so it doesnt overlap
# this is the same code as before but adapted to use the gameimage class and the rects stored there
self.rects = []
for i in self.game_images:
position_set = False
while not position_set:
x = random.randint(100,950)
y = random.randint(100,600)
i.rect.x = x
i.rect.y = y
margin = 5
rl = [rect.inflate(margin*2, margin*2) for rect in self.rects]
if len(self.rects) == 0 or i.rect.collidelist(rl) < 0:
self.rects.append(i.rect)
position_set = True
# this makes a number and object pair, and allows us to set the correct rects for the correct gameimage classes
for i, rect in enumerate(self.rects):
self.game_images[i].rect = rect
# this is the fade stuff from before that was in draw. It really belongs here tbh
def update(self, dt):
if self.part == 1 and self.fade:
self.fade_time += dt
if self.fade_time > fade_timer:
self.fade_time = 0
self.main_image.set_alpha(self.current_alpha)
self.record_text.set_alpha(self.current_alpha)
# Speed whichin the image dissapears
self.current_alpha -= 5
if self.current_alpha <= 0:
self.fade = False
self.part = 2
else:
# we reset the main image alpha otherwise it will be invisible on the next screen (yeah, this one caught me out lol!)
self.main_image.set_alpha(255)
# draw is similar to before, but a bit more streamlined as the fade stuff is not in update
def draw(self, screen):
super().draw(screen)
if self.part == 1:
screen.blit(self.record_text, (550, 20))
screen.blit(self.main_image.image, (580, 280))
else:
# Second half
text2 = font.render('¿Qué has visto?',True, PURPLE)
screen.blit(text2, (400,5))
# Show all similar images
for game_image in self.game_images:
game_image.draw(screen)
# We associate the correct rect to the correct image, to pass it later to the CORRECT Screen
self.correct_image_rect = self.game_images[self.game_images.index(self.main_image)].rect
# again we pass the event to the game object the same as with the other classes
def get_event(self, event):
if self.part == 2:
if self.game.level == 13:
self.game.game_over = True
if self.correct_image_rect.collidepoint(event.pos):
return 'CORRECT'
for rect in self.rects:
if not self.correct_image_rect.collidepoint(event.pos) and rect.collidepoint(event.pos):
return 'INCORRECT'
You end up in an infinite loop, because you try to add all the objects at once. If the algorithm cannot find a random position for an object that does not collide with another object, the loop does not terminate.
Create the objects one by one in the update method. The update method is continuously called in the application loop. Create on object per frame. There may be times when not all objects can be generated, but you can avoid the infinite loop:
class GameScene(Scene):
def __init__(self, game, images, main_image, next_scene):
super().__init__(next_scene)
self.game = game
self.main_image = main_image
self.game_images = images
# Fade effect set-up
self.fade = False
self.fade_time = 0
self.current_alpha = 255
self.part = 1
self.record_text = font.render('Atiende',True, PURPLE)
self.correct_image_rect = None
# Trying to use colliderect so it doesnt overlap
# this is the same code as before but adapted to use the gameimage class and the rects stored there
self.rects = []
# this is the fade stuff from before that was in draw. It really belongs here tbh
def update(self, dt):
if len(self.rects) < len(self.game_images):
i = len(self.rects)
x = random.randint(100,950)
y = random.randint(100,600)
self.game_images[i].rect.x = x
self.game_images[i].rect.y = y
margin = 5
rl = [rect.inflate(margin*2, margin*2) for rect in self.rects]
if len(self.rects) == 0 or self.game_images[i].rect.collidelist(rl) < 0:
self.rects.append(self.game_images[i].rect)
if self.part == 1 and self.fade:
self.fade_time += dt
if self.fade_time > fade_timer:
self.fade_time = 0
self.main_image.set_alpha(self.current_alpha)
self.record_text.set_alpha(self.current_alpha)
# Speed whichin the image dissapears
self.current_alpha -= 5
if self.current_alpha <= 0:
self.fade = False
self.part = 2
else:
# we reset the main image alpha otherwise it will be invisible on the next screen (yeah, this one caught me out lol!)
self.main_image.set_alpha(255)
# [...]
I am new to python and wanted to create a game to test some knowledge. Please bare with my mistakes. I would also really appreciate an example written out if possible so I can wrap my head around the concept.
I couldn't figure out how to post the entire code without formatting nightmares so I put it in github sorry if this is against terms!
I removed most of the dialogue because it was very very long.
Some background about the same:
It was written as a text-based only game first, then I heard about pygame wanted to figure out how to put the game into the window. So first I created all of the code, which ran perfectly fine even with sound effects / music, but then after trying to figure out pygame's game loop, I can't figure out how to change the background image.
I have 5 scenes that I want 5 images for, that change when I change the scene with user input. Even now, the music changes, but the background images don't change after blitting the first one in the 'naming' scene. I don't really understand how the pygame loop works in conjunction with the game engine I have currently. I know it's looping through the pygame loop as I tested it with printing things, but when trying to add images or saying if scene == naming: blit image, it doesn't work. I also tried putting the images into each of the scenes. I also tried putting the images into a class called background with all 5 scenes and images to each. Can't figure it out.
Any help would be greatly appreciated!
-M
Here is an example of how to use scenes. This is not the only way of doing it, but i though it would be easy to use/understand. I also noticed in your code that you used input(). which is annoying if your using a window, so i also added a button and a textbox input that kinda works.
import pygame as pg
pg.init() # initialize the pg
win = pg.display.set_mode((1080, 720)) # create the game window
pg.display.set_caption("Puppy Love")
white = (255,255,255)
black = (0,0,0)
clock = pg.time.Clock()
fps = 60
class Scene:
def __init__(self):
self.sceneNum = 1
def Scene_1(self):
win.fill(white)
if next_scene_button.draw(): #if clicked button
self.sceneNum += 1
name_Box.Draw()
#anything else you want to draw
def Scene_2(self):
win.fill((0,0,255))
if next_scene_button.draw():
self.sceneNum = 1
#...
def Draw_Scene(self):
if self.sceneNum == 1:
self.Scene_1()
elif self.sceneNum == 2:
self.Scene_2()
class Button:
def __init__(self,x,y,w,h,text):
self.x = x
self.y = y
self.w = w
self.h = h
self.text = text
self.font = pg.font.Font(pg.font.match_font("Calibri"),20)
def draw(self):
button_clicked = False
col = (150,150,150)
if mouse_pos[0] > self.x and mouse_pos[0] < self.x + self.w:
if mouse_pos[1] > self.y and mouse_pos[1] < self.y + self.h:
col = (200,200,200)
if click:
button_clicked = True
pg.draw.rect(win,col,(self.x,self.y,self.w,self.h))
obj = self.font.render(self.text,True, black)
win.blit(obj,(self.x + (self.w-obj.get_width())//2,self.y + (self.h-obj.get_height())//2)) #center the text
return button_clicked #return if the button was clicked or not
class TextBox:
def __init__(self,x = 0, y = 0, w = 0, h = 0, text = "", background = False, font_size = 30, font = "Calibri", text_colour = (0,0,0)):
self.x = x
self.y = y
self.w = w
self.h = h
self.text_colour = text_colour
self.text = text
self.background = background
self.font = pg.font.Font(pg.font.match_font(font),font_size)
def Add_char(self,key):
if key == 8:
self.text = self.text[:-1]
else:
char = pg.key.name(key)
self.text += char
def Draw(self):
if self.background:
pg.draw.rect(win, self.background, (self.x,self.y,self.w,self.h))
obj = self.font.render(self.text,True,self.text_colour)
win.blit(obj,(self.x,self.y))
def Get_pos(self):
return (self.x,self.y)
name_Box = TextBox(x=100, y=200, w=150, h=40, background=(200,200,200))
Scenes = Scene()
next_scene_button = Button(400,400,100,50,"next scene")
click = False
mouse_pos = [0,0]
running = True
while running:
clock.tick(fps)
Scenes.Draw_Scene()
mouse_pos = pg.mouse.get_pos()
click = False
pg.display.update() #no diference between .flip() and .update()
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
running = False
if event.type == pg.MOUSEBUTTONDOWN:
click = True
if event.type == pg.KEYDOWN:
if Scenes.sceneNum == 1:
name_Box.Add_char(event.key)
The main thing to take away from this is to only use one pygame.display.flip(). Hopefully you can use this to improve your program
I have been trying to use some "key released" functions from libraries like pynput and so, but I ran in to the problem that if I try using them together with a library called pyglet, that is a library for window-based apps, it won't let me and the program would crash.
I was wondering if there is any way detect key releases without libraries.
P.S: I've tried using the on_key_release function from pyglet but it was buggy for me and even though I wrote something for it to upon key release it usually didn't do it. I have checked my code a thousand times and it's not a problem on my part.
Pressing = False # var to indicate when the user is doing a long press and when he is not
#window.event
def on_key_release(symbol, modifiers):
global Pressing
if((symbol == key.A) | (symbol == key.S) | (symbol == key.W) | (symbol == key.D)):
Pressing = False
pass
and that code causes my player to freeze after i start moving him, and it does this even if i do nothing and just leach the whole on_key_release dunction empty. really weird.
So, your issue is most likely that you're doing Pressing = False if any key is released. Forcing your player object to freeze due to Pressing being False as soon as you release any of your keys.
To work around this, you should store the states of your keys in a variable (I'll call it self.keys), and just before you render stuff, update/check the keys and update your player object accordingly.
Here's a working example of movement on a player object (a red square in this case):
from pyglet import *
from pyglet.gl import *
key = pyglet.window.key
class main(pyglet.window.Window):
def __init__ (self, width=800, height=600, fps=False, *args, **kwargs):
super(main, self).__init__(width, height, *args, **kwargs)
self.x, self.y = 0, 0
self.keys = {}
self.mouse_x = 0
self.mouse_y = 0
square = pyglet.image.SolidColorImagePattern((255, 0, 0, 255)).create_image(40, 40)
self.player_object = pyglet.sprite.Sprite(square, x=self.width/2, y=self.height/2)
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_mouse_motion(self, x, y, dx, dy):
self.mouse_x = x
def on_key_release(self, symbol, modifiers):
try:
del self.keys[symbol]
except:
pass
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
self.keys[symbol] = True
def render(self):
self.clear()
## Movement logic,
# check if key.W is in self.keys (updated via key_press and key_release)
if key.W in self.keys:
self.player_object.y += 1
if key.S in self.keys:
self.player_object.y -= 1
if key.A in self.keys:
self.player_object.x -= 1
if key.D in self.keys:
self.player_object.x += 1
self.player_object.draw()
self.flip()
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
if __name__ == '__main__':
x = main()
x.run()
I've moved away from #window.event because it's easier for me to just copy paste this class/inheritance example since I had it laying around. But you could apply this logic any way you want. Just make sure you don't defined a solid state of pressing or not pressing, check individual keys and update accordingly before you render.
I am having a little trouble with this project. I have to create a pendulum using key handles and the code I have for the key's up and down don't seem to be working. "up" is suppose to make the pendulum go faster and "down" makes it go slower. This is the code that I have so far. can somebody please help.
from tkinter import * # Import tkinter
import math
width = 200
height = 200
pendulumRadius = 150
ballRadius = 10
leftAngle = 120
rightAngle = 60
class MainGUI:
def __init__(self):
self.window = Tk() # Create a window, we may call it root, parent, etc
self.window.title("Pendulum") # Set a title
self.canvas = Canvas(self.window, bg = "white",
width = width, height = height)
self.canvas.pack()
self.angle = leftAngle # Start from leftAngle
self.angleDelta = -1 # Swing interval
self.delay = 200
self.window.bind("<Key>",self.key)
self.displayPendulum()
self.done = False
while not self.done:
self.canvas.delete("pendulum") # we used delete(ALL) in previous lab
# here we only delete pendulum object
# in displayPendulum we give the tag
# to the ovals and line (pendulum)
self.displayPendulum() # redraw
self.canvas.after(self.delay) # Sleep for 100 milliseconds
self.canvas.update() # Update canvas
self.window.mainloop() # Create an event loop
def displayPendulum(self):
x1 = width // 2;
y1 = 20;
if self.angle < rightAngle:
self.angleDelta = 1 # Swing to the left
elif self.angle > leftAngle:
self.angleDelta = -1 # Swing to the right
self.angle += self.angleDelta
x = x1 + pendulumRadius * math.cos(math.radians(self.angle))
y = y1 + pendulumRadius * math.sin(math.radians(self.angle))
self.canvas.create_line(x1, y1, x, y, fill="blue", tags = "pendulum")
self.canvas.create_oval(x1 - 2, y1 - 2, x1 + 2, y1 + 2,
fill = "red", tags = "pendulum")
self.canvas.create_oval(x - ballRadius, y - ballRadius,
x + ballRadius, y + ballRadius,
fill = "green", tags = "pendulum")
def key(self,event):
print(event.keysym)
print(self.delay)
if event.keysym == 'up':
print("up arrow key pressed, delay is",self.delay)
if self.delay >10:
self.delay -= 1
if event.keysym == 'Down':
print("Down arrow key pressed,delay is",self.delay)
if self.delay < 200:
self.delay += 1
if event.keysym=='q':
print ("press q")
self.done = True
self.window.destroy()
MainGUI()
The root of the problem is that the event keysym is "Up" but you are comparing it to the all-lowercase "up". Naturally, the comparison fails every time. Change the if statement to if event.keysym == 'Up':
Improving the animation
In case you're interested, there is a better way to do animation in tkinter than to write your own infinite loop. Tkinter already has an infinite loop running (mainloop), so you can take advantage of that.
Create a function that draws one frame, then have that function arrange for itself to be called again at some point in the future. Specifically, remove your entire "while" loop with a single call to displayFrame(), and then define displayFrame like this:
def drawFrame(self):
if not self.done:
self.canvas.delete("pendulum")
self.displayPendulum()
self.canvas.after(self.delay, self.drawFrame)
Improving the bindings
Generally speaking, your code will be marginally easier to manage and test if you have specific bindings for specific keys. So instead of a single catch-all function, you can have specific functions for each key.
Since your app supports the up key, the down key, and the "q" key, I recommend three bindings:
self.window.bind('<Up>', self.onUp)
self.window.bind('<Down>', self.onDown)
self.window.bind('<q>', self.quit)
You can then define each function to do exactly one thing:
def onUp(self, event):
if self.delay > 10:
self.delay -= 1
def onDown(self, event):
if self.delay < 200:
self.delay += 1
def onQuit(self, event):
self.done = True
self.window.destroy()