How can i reload a sprite on my game in pygame? - python-3.x

I'm doing a project in python the goal is to make a little game.
In my main menu I have 3 buttons and I found a UI pack which gives me a sprite of buttons with one unpressed and one pressed. I want it to change the sprite when the button is pushed, but when I reload it doesn't work.
Does anyone have an idea of what is wrong?
def main():
pygame.init()
screen = pygame.display.set_mode((800, 500))
pygame.display.set_caption('POKEMON')
bg = pygame.image.load("./assets/img/lspkm.png")
btn_new_Game = btn.Button("./assets/custombtn/btnbcnewgame.png", 305, 270, screen)
btn_continue = btn.Button("./assets/custombtn/btnbccontinue.png", 305, 340, screen)
btn_quit = btn.Button("./assets/custombtn/btnbcquit.png", 305, 410, screen)
screen.blit(bg, (0, 0))
btn_new_Game.draw()
btn_continue.draw()
btn_quit.draw()
while 1:
for event in pygame.event.get():
if event.type == MOUSEBUTTONDOWN:
x, y = event.pos
son = pygame.mixer.Sound("./assets/UIpack/Bonus/click2.ogg")
if btn_quit.rect.collidepoint(x, y):
btn_quit.image = pygame.image.load("./assets/custombtn/btnacquit.png")
btn_quit.draw()
pygame.time.delay(3000)
return
Edit1:
As you can see, I have a button object to help me manage this:
class Button :
def __init__(self, image, x, y, screen):
self.image = pygame.image.load(image)
self.width = self.image.get_width()
self.height = self.image.get_height()
self.rect = pygame.Rect(x, y, self.width, self.height)
self.screen = screen
def move(self, dx, dy):
self.rect.x += dx
self.rect.y += dy
def draw(self):
self.screen.blit(self.image, (self.rect.x, self.rect.y))
def update(self, image):
self.image = pygame.image.load(image)
self.screen.blit(self.image, (self.rect.x, self.rect.y))

It seems like you have the button in this, and you only need to add the sprite changing on click.
You can create a list of button sprites i.e.
button1 = pygame.image.load('/resources/button1.png')
button2 = pygame.image.load('/resources/button2.png')
buttonClicked = pygame.image.load('/resources/buttonDark.png')
buttonList = [button1,button2,buttonClicked]
and then in your main game loop when you detect that the button has been pressed (mousebuttonup on the same position as the sprite)
you can execute this:
# pseudocode for you
when button = clicked
i = 2
screen.blit(buttonList[i])
cheers!

Related

How do I pass a rect into pygame.display.update() to update a specific area of the window?

On the documentation page for pygame.display.update(), it says you can pass a rect into the method to update part of the screen. However, all of the examples I see just pass an existing rect from an image or shape in the program. How can I tell it to update an area on the screen directly? For example, when drawing a rectangle, I could use a rect argument of (100,200,30,40). This would draw a rectangle with a top at 200, a left at 100, a width of 30, and a height of 40. How can I pass a similar argument into pygame.display.update()? I tried pygame.display.update((100,200,30,40)), but this updates the entire window.
Just define a rect and pass it to pygame.display.update() to update only this specific region of the display. You can also pass a list of rects.
import random
import pygame as pg
from pygame.math import Vector2
# A simple sprite, just to have something moving on the screen.
class Ball(pg.sprite.Sprite):
def __init__(self, screen_rect):
super().__init__()
radius = random.randrange(5, 31)
self.image = pg.Surface((radius*2, radius*2), pg.SRCALPHA)
pg.draw.circle(self.image, pg.Color('dodgerblue1'), (radius, radius), radius)
pg.draw.circle(self.image, pg.Color('dodgerblue3'), (radius, radius), radius-2)
self.rect = self.image.get_rect(center=screen_rect.center)
self.vel = Vector2(random.uniform(-2, 2), random.uniform(-2, 2))
self.pos = Vector2(self.rect.center)
self.screen_rect = screen_rect
self.lifetime = 350
def update(self):
self.pos += self.vel
self.rect.center = self.pos
self.lifetime -= 1
if not self.screen_rect.contains(self.rect) or self.lifetime <= 0:
self.kill()
def main():
screen = pg.display.set_mode((800, 600))
screen.fill((20, 40, 70))
pg.display.update()
screen_rect = screen.get_rect()
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
# Pass this rect to `pg.display.update` to update only this area.
update_rect = pg.Rect(50, 50, 500, 400)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
all_sprites.add(Ball(screen_rect))
all_sprites.update()
screen.fill((20, 50, 90))
all_sprites.draw(screen)
# Update only the area that we specified with the `update_rect`.
pg.display.update(update_rect)
clock.tick(60)
if __name__ == '__main__':
pg.init()
main()
pg.quit()

How draw a dot on canvas on click event Tkinter Python

I have the following piece of code that takes an image within a canvas and then whenever I click the paint function draws a dot over it.
Everything is working fine except that the paint function is not working as expected.
Desirable output
Click event draws a dot. No need to drag the on click event
Actual output
I have to drag the on mouse click event to see a drawing on the canvas.
I guess there might be a slight problem with the paint function. However, I haven't been able to know what it is exactly.
from tkinter import *
from PIL import Image, ImageTk
class Main(object):
def __init__(self):
self.canvas = None
def main(self):
master = Tk()
# Right side of the screen / image holder
right_frame = Frame(master, width=500, height=500, cursor="dot")
right_frame.pack(side=LEFT)
# Retrieve image
image = Image.open("./image/demo.JPG")
image = image.resize((800, 700), Image.ANTIALIAS)
photo = ImageTk.PhotoImage(image)
# Create canvas
self.canvas = Canvas(right_frame, width=800, height=700)
self.canvas.create_image(0, 0, image=photo, anchor="nw")
self.canvas.pack()
self.canvas.bind("<B1-Motion>", self.paint)
mainloop()
def paint(self, event):
python_green = "#476042"
x1, y1 = (event.x - 1), (event.y - 1)
x2, y2 = (event.x + 1), (event.y + 1)
self.canvas.create_oval(x1, y1, x2, y2, fill=python_green, outline=python_green, width=10)
if __name__ == "__main__":
Main().main()
Fix:
Added these two methods:
def on_button_press(self, event):
self.x = event.x
self.y = event.y
def on_button_release(self, event):
python_green = "#476042"
x0,y0 = (self.x, self.y)
x1,y1 = (event.x, event.y)
changed canvas to this:
self.canvas.bind("<ButtonPress-1>", self.on_button_press)
self.canvas.bind("<ButtonRelease-1>", self.on_button_release)
When you only click and don't move the mouse, B1-Motion isn't triggering.
To bind to mouse press (as well as mouse moving), you can add self.canvas.bind("<ButtonPress-1>", self.paint) before the mainloop.

Python Tkinter fixing text location on scrolling screen

Trying to scroll a graphic on the screen while keeping the text in the same place. The text shows where the mouse is located. I've thought about the idea of scrolling the text in the opposite direction of the screen scroll but I'm not sure if there is an easier way of doing it and if I have to scroll the text the opposite way I'm not sure how to set the initial text pointer so I can come back and recall/reset it. I'm only wanting it to show the position on not as will be doing other things in the near future.
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.keys = dict.fromkeys(('Left', 'Right', 'Up', 'Down'))
self.canvas = tk.Canvas(self, background="bisque", width=700, height=700)
self.canvas.pack(fill="both", expand=True)
self.canvas.configure(scrollregion=(-1000, -1000, 1000, 1000))
self.looper() # start the looping
def keypress(self,event):
if event.keysym in self.keys:
# event type 2 is key down, type 3 is key up
self.keys[event.keysym] = event.type == '2'
def looper(self):
if self.keys['Up']:
self.canvas.yview_scroll(-2,'units')
if self.keys['Down']:
self.canvas.yview_scroll(2,'units')
if self.keys['Left']:
self.canvas.xview_scroll(-2,'units')
if self.keys['Right']:
self.canvas.xview_scroll(2,'units')
self.after(5, self.looper) # set the refresh rate here ... ie 20 milliseconds. Smaller number means faster scrolling
def on_press(self, event):
self.last_x = event.x
self.last_y = event.y
self.startx, self.starty = self.canvas.canvasx(event.x),self.canvas.canvasy(event.y)
def on_motion(self, event):
self.canvas.delete("sx")
self.startx, self.starty = self.canvas.canvasx(event.x),self.canvas.canvasy(event.y)
px = round(-(((1000-self.startx) * .00015) + 69.3),5)
py = round((45.05-((1000+self.starty) * .00015)),5)
self.canvas.create_text(400,-400, text = str(px), fill = "black", tags = "sx")
self.canvas.create_text(475,-400, text = str(py), fill = "black", tags = "sx")
def button_motion(self,event):
delta_x = event.x - self.last_x
delta_y = event.y - self.last_y
self.last_x = event.x
self.last_y = event.y
self.canvas.xview_scroll(-delta_x, "units")
self.canvas.yview_scroll(-delta_y, "units")
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
The simplest solution is to use a widget that is not embedded in the canvas for the text so that it won't scroll when the canvas scrolls. Create a Label with it's parent being the canvas, and then use place to superimpose the label on top of the canvas.

Positioning an Entity in a Class (Pygame)

I'm trying to blit an image of grass onto my game in a specific area, but I'm having trouble doing so because it's in a class. I want to do something like this..
grass_platform.draw(screen, (200, 200))
NOTE: I know this actually doesn't work
Here's my code...
import pygame
pygame.init()
#Screen Size
screen_size = [1024,576]
#Display Window
screen = pygame.display.set_mode(screen_size)
pygame.display.set_caption('The Adventures of Fresco the Explorer')
#Clock
clock = pygame.time.Clock()
#Colors
black = (0,0,0)
white = (255,255,255)
#Game Start
gameStart = False
#Backgrounds
forest = pygame.image.load('forest.png')
#Gravity
gravity =-10
fall = True
#Player
fresco = pygame.image.load('fresco v2.png').convert()
fresco = pygame.transform.scale(fresco,(32,136))
class Player(pygame.sprite.Sprite):
def __init__(self, x, y, filename):
pygame.sprite.Sprite.__init__(self)
self.x = x
self.y = y
self.image = fresco
self.rect = self.image.get_rect()
def update (self):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
self.rect.x -= 6
self.image = pygame.transform.flip(fresco, True, False)
elif event.key == pygame.K_d:
self.rect.x += 6
self.image = pygame.transform.flip(fresco, False, False)
elif event.key == pygame.K_w:
self.rect.y -= 20
def draw(self, screen):
screen.blit(self.image, self.rect)
player = Player(0, 0, 'fresco v2.png')
#Grass Platform
grass = pygame.image.load('grass.png')
grass = pygame.transform.scale(grass, (90, 90))
class Grass (pygame.sprite.Sprite):
def __init__(self, filename):
self.image = grass
self.rect = self.image.get_rect()
def draw(self, screen):
screen.blit(self.image,self.rect)
grass_platform = Grass('grass.png')
#Game Loop
while not gameStart:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameStart = True
#Background Blitted
screen.blit(forest,(0,0))
#Falling Event
if fall == True:
if gravity:
player.rect.y -= gravity
#Class Blitted
player.update()
player.draw(screen)
grass_platform.draw(screen)
#Updates Screen
pygame.display.update()
#FPS
clock.tick(30)
pygame.quit()
I dont know if i understood you correctly but if you mean just passing a coordinate parameter and blitting to that position that should be quite easy.
class Grass (pygame.sprite.Sprite):
def __init__(self, filename):
self.image = grass
self.rect = self.image.get_rect()
def draw(self, screen, pos):
screen.blit(self.image, pos)
and then when you draw() you just do this:
grass_platform.draw(screen, (300, 200))
or on the other hand if you want to keep the position you could add
this code to the init method:
def __init__(self, filename, x, y):
self.x = x
self.y = y
and then acces it later
def draw(self, screen):
window.blit(img, (self.x, self.y))
Some notes:
Player and Grass are subclasses from Sprite, so they have already a draw function which does exactly what you are doing. Just remove them.
the following part
#Falling Event
if fall == True:
if gravity:
player.rect.y -= gravity
is specific to the Player class and should be moved to the Player class' update function
When using the Sprite class, the rect attribute is used to store the position of the sprite, hence the x and y attribute in your Player class don't make much sense. Remove them, and use the x and y parameters to change the x and y attribute of the rect instead:
self.rect = self.image.get_rect(x=x, y=y)
Hence, if you want to draw grass_platform at a specific position, just alter it's rect, e.g.:
grass_platform.rect.topleft = (200, 200)
instead of calling draw and update on each of your sprites manually, just but them into a Group, and call draw and update on that Group.

Having problems making Pong in Python with Pygame. Paddle extends when I move it

So like many beginner python programmers, I have decided to code a pong game. This is my second attempt. The first was hard coded with no classes and few functions, so I have started from scratch. I currently have a paddle class and a main loop. I have coded the functionality for the paddles to move up and down, but I have a problem. When I press the keys to move the paddles, the just extend up and down, they don't actually move. Here is my code thus far:
#PONG GAME IN PYTHON WITH PYGAME
import pygame
import time
pygame.init()
white = (255, 244, 237)
black = (0, 0, 0)
largeFont = pygame.font.Font("pongFont.TTF", 75)
mediumFont = pygame.font.Font("pongFont.TTF", 50)
smallFont = pygame.font.Font("pongFont.TTF", 25)
displayWidth = 800
displayHeight = 600
gameDisplay = pygame.display.set_mode((displayWidth, displayHeight))
pygame.display.set_caption("Pong")
FPS = 60
menuFPS = 10
clock = pygame.time.Clock()
#Paddle Class
class Paddle:
def __init__(self, player):
self.length = 100
self.width = 8
self.yVelocity = 0
self.y = (displayHeight - self.length) / 2
#Puts player 1 paddle on left and player 2 on right
if player == 1:
self.x = 3 * self.width
elif player == 2:
self.x = displayWidth - 4 * self.width
#Did paddle hit top or bottom?
def checkWall(self):
if self.y <= 0:
return "top"
elif self.y >= displayHeight - self.length:
return "bottom"
def stop(self):
self.yVelocity = 0
def moveUp(self):
if self.checkWall() == "top":
self.stop()
else:
self.yVelocity = -self.width
def moveDown(self):
if self.checkWall() == "bottom":
self.stop()
else:
self.yVelocity = self.width
#Draw the paddle
def draw(self):
self.y += self.yVelocity
gameDisplay.fill(white, rect = [self.x, self.y, self.width, self.length])
paddle1 = Paddle(1)
paddle2 = Paddle(2)
gameFinish = False
#Main Loop
while not gameFinish:
#Event Loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
#Get all pressed keys
keyPressed = pygame.key.get_pressed()
#Move paddle1 if s or w is pressed
if keyPressed[pygame.K_w]:
paddle1.moveUp()
elif keyPressed[pygame.K_s]:
paddle1.moveDown()
else:
paddle1.stop()
#Move paddle2 if UP or DOWN is pressed
if keyPressed[pygame.K_UP]:
paddle2.moveUp()
elif keyPressed[pygame.K_DOWN]:
paddle2.moveDown()
else:
paddle2.stop()
paddle1.draw()
paddle2.draw()
pygame.display.update()
clock.tick(FPS)
Thanks in advance to anyone who can help!
Clear the screen before, you are drawing a new paddle when the old paddle is still there.
Use something like gameDisplay.fill(white) to clear your screen.
This is because you already have the paddle sprite on when you move it. Effectively what you want to do is destroy the old paddle and then draw the old one, otherwise the old one will still exists when you create the new one, making a merging effect.

Resources