Pygame Help - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: Game Development (https://python-forum.io/forum-11.html) +--- Thread: Pygame Help (/thread-34146.html) |
Pygame Help - piznac - Jun-30-2021 Hello! I have been running into a issue with drawing rectangles, blitting text, querying a DB etc etc inside the game loop. The rectangles or text appear on screen for on fps (I think) and then disappears. The sql call,. calls every fps etc etc. I have tried to figure out how to do this outside the loop without success. I'm sure I am just missing something stupid. This is an example of a Yahtzee game I am "trying" to quickly throw together to learn. Inside the SetTableDice function it renders the "dice" but only for a quick second. Any insights? import sys import os import time import random from collections import Counter import pygame pygame.init() screen_width = 800 screen_height = 600 first_table_dice_x_position = 150; screen = pygame.display.set_mode((screen_width, screen_height)) smallfont = pygame.font.SysFont('Corbel',20) clock = pygame.time.Clock() game_over = False class Dice(): DiceOnTable = {'d1': 1, 'd2': 2, 'd3': 3, 'd4': 4, 'd5': 6} turn = False def __inti__(self): pass def RollDice(self): # self.DiceOnTable.clear() for k in self.DiceOnTable: dice = random.randint(1, 6) self.DiceOnTable[k] = dice self.turn = True # sort might be needed in the future # self.DiceOnTable = {k: v for k, v in sorted(self.DiceOnTable.items(), key=lambda item: item[1])} print(self.DiceOnTable) # while self.turn: # d1 = smallfont.render(str(self.DiceOnTable['d1']), True , (0,0,0)) # wtf = smallfont.render('WTF??', True , (0,0,0)) # screen.blit(wtf, (100,100)) # screen.blit(d1, (first_table_dice_x_position,250)) def SetTableDice(self): d1 = self.DiceOnTable['d1']; pygame.draw.rect(screen,(255,255,255),[first_table_dice_x_position,250,35,40]) d1 = smallfont.render(str(d1), True , (0,0,0)) screen.blit(d1, (first_table_dice_x_position,250)) # while self.turn: # time.sleep() # pygame.display.update() # time.sleep(2) # GameLoop() def CheckDice(self): for v in range(6): self.CheckForTop(v) self.CheckForChance() self.CheckForMultiples() self.CheckForLargeStraight() self.CheckForSmallStraight() def CheckForTop(self, num): NumOf = [] for v in self.DiceOnTable: if str(self.DiceOnTable[v]) == str(num): NumOf.append(1) HowMany = str(len(NumOf)) print('You have ' + HowMany + ' ' + str(num)) def CheckForMultiples(self): counts = Counter(self.DiceOnTable.values()) TwoOfAKind = False ThreeOfAKind = False for v in counts: if counts[v] == 5: print("You have a yahtzee with " + str(v)) if counts[v] == 4: print("You have a four of a kind with " + str(v)) if counts[v] == 2: TwoOfAKind = True print("You have a two of a kind with " + str(v)) if counts[v] == 3: ThreeOfAKind = True print("You have a three of a kind with " + str(v)) if TwoOfAKind and ThreeOfAKind: print("You have a full house") def CheckForLargeStraight(self): #the below could be done better - needs refactoring # self.DiceOnTable = {k: v for k, v in sorted(self.DiceOnTable.items(), key=lambda item: item[1])} print(self.DiceOnTable) if ((1 in self.DiceOnTable.values() and 2 in self.DiceOnTable.values() and 3 in self.DiceOnTable.values() and 4 in self.DiceOnTable.values() and 5 in self.DiceOnTable.values()) or (2 in self.DiceOnTable.values() and 3 in self.DiceOnTable.values() and 4 in self.DiceOnTable.values() and 5 in self.DiceOnTable.values() and 6 in self.DiceOnTable.values())): print('You have a large straight!') def CheckForSmallStraight(self): #the below could be done better - needs refactoring # self.DiceOnTable = {k: v for k, v in sorted(self.DiceOnTable.items(), key=lambda item: item[1])} if ((1 in self.DiceOnTable.values() and 2 in self.DiceOnTable.values() and 3 in self.DiceOnTable.values() and 4 in self.DiceOnTable.values()) or (2 in self.DiceOnTable.values() and 3 in self.DiceOnTable.values() and 4 in self.DiceOnTable.values() and 5 in self.DiceOnTable.values()) or (3 in self.DiceOnTable.values() and 4 in self.DiceOnTable.values() and 5 in self.DiceOnTable.values() and 6 in self.DiceOnTable.values())): print('You have a small straight!') def CheckForChance(self): total = 0 for v in self.DiceOnTable: total += self.DiceOnTable[v] print("Chance = " + str(total)) def CreateTableDice(self): # for k in self.DiceOnTable: pass def GameLoop(): while not game_over: screen.fill((0,102,0)) pygame.draw.rect(screen,(255,255,255),[530,20,250,550]) text = smallfont.render('Roll Dice' , True , (255,255,255)) pygame.draw.rect(screen,(0,0,0),[200,450,140,40]) screen.blit(text , (240,460)) # pygame.draw.rect(screen,(255,255,255),[first_table_dice_x_position,250,35,40]) # pygame.draw.rect(screen,(255,255,255),[first_table_dice_x_position+40,250,35,40]) # pygame.draw.rect(screen,(255,255,255),[first_table_dice_x_position+80,250,35,40]) # pygame.draw.rect(screen,(255,255,255),[first_table_dice_x_position+120,250,35,40]) # pygame.draw.rect(screen,(255,255,255),[first_table_dice_x_position+160,250,35,40]) # pygame.draw.rect(screen,(255,255,255),[first_table_dice_x_position+200,250,35,40]) mouse = pygame.mouse.get_pos() for event in pygame.event.get(): # print(event) if event.type == pygame.QUIT: sys.exit() if event.type == pygame.MOUSEBUTTONDOWN: if 200 <= mouse[0] <= 200+140 and 450 <= mouse[1] <= 450+40: dice = Dice() dice.RollDice() dice.CheckDice() dice.SetTableDice() if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: sys.exit() clock.tick(30) pygame.display.update() GameLoop() pygame.quit() quit() RE: Pygame Help - piznac - Jul-01-2021 So a little more insight. The rect is doing exactly what I told it to do. On click of the button it shows up,. but immediately goes away. How would I "pause" the game waiting on user input? Like the user clicking the dice to select them? RE: Pygame Help - BashBedlam - Jul-04-2021 On line 171 you reset dice to a new iteration of Dice each time the "roll dice' button is pressed. It might be best to do this only once, perhaps at the beginning of Gameloop like this:def GameLoop(): dice = Dice() while not game_over:The problem that you're having is because dice.SetTableDice () only updates the dice when "roll dice" is clicked. I recommend that you move it down with pygame.display.update () like this:clock.tick(30) dice.SetTableDice () pygame.display.update() RE: Pygame Help - piznac - Jul-04-2021 Quote:The problem that you're having is because dice.SetTableDice () only updates the dice when "roll dice" is clicked. I recommend that you move it down with pygame.display.update () like this: Ok at first I didn't understand this and dismissed it. But then I tried it,. and I'm having a hard time wrapping my head around,. but it does put me on a new track. So thank you for that! It seems I have a lot to learn about this game loop. RE: Pygame Help - noahcentineo - Jul-12-2021 So all I have to do now is update the variable's name. Furthermore, the variables must be distinct from the class name. If that is the only method to fix it, it will not work. I've tried changing several names, but each time I get the same problem. Maybe I'm mistaken. But, Gribouillis, thank you very much for your time. Refer to super mario bros. RE: Pygame Help - stephanilope - Nov-19-2022 Pressing the "roll dice' button should probably only be pressed once but I pressed it too many times, it must have been my mistake that led to the same error as you. I will try again following the instructions of BashBedlam slope ball RE: Pygame Help - Windspar - Nov-25-2022 Edit: Just realize this is an old thread. Maybe this will give you some idea. Haven't done score card yet. import pygame import random from itertools import count from pygame.sprite import Group, Sprite # Interface class State: def __init__(self, engine): self.engine = engine def on_draw(self, surface): pass def on_event(self, event): pass def on_update(self, delta, ticks): pass # Engine class DisplayEngine: def __init__(self, caption, width, height, flags=0): # Basic Pygame Setup pygame.display.set_caption(caption) self.surface = pygame.display.set_mode((width, height), flags) self.rect = self.surface.get_rect() self.clock = pygame.time.Clock() self.running = True self.delta = 0 self.fps = 60 # Handle what state is active. self._state = State(self) self.next_state = None # Main Loop def run(self, state=None): if state: self._state = state while self.running: # State Changer if self.next_state: self._state = self.next_state self.next_state = None # Event Loop for event in pygame.event.get(): # Just send the events to state to handle self._state.on_event(event) ticks = pygame.time.get_ticks() self._state.on_draw(self.surface) self._state.on_update(self.delta, ticks) pygame.display.flip() self.delta = self.clock.tick(self.fps) def quit(self): self.running = False class SimpleSprite(Sprite): def __init__(self, image): super().__init__() self.image = image self.rect = image.get_rect() class Callback: def __init__(self, callback, user_data=None): self.parent = None self.callback = callback self.user_data = user_data def link(self, parent): self.parent = parent return self def call(self): self.callback(self) class Pen: def __init__(self, font, color): self.font = font self.color = color def render(self, text): return self.font.render(text, 1, self.color) def write(self, text): return SimpleSprite(self.render(text)) class ButtonImages: def __init__(self, normal, hover, pushed): self.normal = normal self.hover = hover self.pushed = pushed class ButtonGroup: def __init__(self): self.buttons = Group() self.text = Group() def add(self, button): self.buttons.add(button) self.text.add(button.tsprite) def draw(self, surface): self.buttons.draw(surface) self.text.draw(surface) class Button(Sprite): def __init__(self, images, pen, text, position, callback): super().__init__() self.pen = pen self.images = images self.image = images.normal self.rect = self.images.normal.get_rect(topleft=position) self.is_mouse_over = False self.is_pushed = False self.callback = callback.link(self) self.update_text(text) def mouse_over(self, event): self.is_mouse_over = self.rect.collidepoint(event.pos) self.update_image() def click(self, event): self.mouse_over(event) if self.is_mouse_over: #self.is_pushed = False self.callback.call() def update_image(self): if self.is_pushed: self.image = self.images.pushed elif self.is_mouse_over: self.image = self.images.hover else: self.image = self.images.normal def update_text(self, text): self.tsprite = self.pen.write(text) self.tsprite.rect.center = self.rect.center # Store images. Because you want to create/load, convert, and/or size once. class Images: def __init__(self): self.dice = self.create_dice(40, 'snow', 'black', 3) self.hdice = self.create_dice(40, 'skyblue', 'black', 3) self.sdice = self.create_dice(40, 'lightgreen', 'black', 3) self.button_images = self.create_button((100, 30), 'blue', 'dodgerblue', 'navy') def create_dice(self, size, color, dot_color, dot_size): surface = pygame.Surface((size, size)) surface.fill(color) rect = surface.get_rect() inner = rect.inflate(-rect.centerx, -rect.centery) images = [] for i in range(1, 7): s = surface.copy() if i in [1, 3, 5]: pygame.draw.circle(s, dot_color, rect.center, dot_size) if i != 1: # 2-6 pygame.draw.circle(s, dot_color, inner.topleft, dot_size) pygame.draw.circle(s, dot_color, inner.bottomright, dot_size) if i > 3: pygame.draw.circle(s, dot_color, inner.topright, dot_size) pygame.draw.circle(s, dot_color, inner.bottomleft, dot_size) if i == 6: pygame.draw.circle(s, dot_color, inner.midleft, dot_size) pygame.draw.circle(s, dot_color, inner.midright, dot_size) images.append(s) return images def create_button(self, size, color, hcolor, pcolor): surface = pygame.Surface(size) surface.fill(color) hsurface = surface.copy() hsurface.fill(hcolor) psurface = surface.copy() psurface.fill(pcolor) return ButtonImages(surface, hsurface, psurface) class Die(Sprite): def __init__(self, images, position): super().__init__() # These are just reference self.images = images self.rect = images[0][0].get_rect(topleft=position) self.is_mouse_over = False self.is_selected = False self.roll() def roll(self): self.value = random.randint(1, 6) self.is_selected = False self.update_image() def mouse_over(self, mpos): self.is_mouse_over = self.rect.collidepoint(mpos) self.update_image() def selected(self): if self.is_mouse_over: self.is_selected = not self.is_selected self.update_image() def update_image(self): if self.is_selected: self.image = self.images[2][self.value - 1] elif self.is_mouse_over: self.image = self.images[1][self.value - 1] else: self.image = self.images[0][self.value - 1] class DisplayLines: def __init__(self, pen, position): self.position = position self.lines = [] self.pen = pen def add(self, text): self.lines.append(self.pen.render(text)) def clear(self): self.lines = [] def draw(self, surface): y = count(self.position[1], self.pen.font.get_linesize() + 2) for line in self.lines: surface.blit(line, (self.position[0], next(y))) class GameState(State): def __init__(self, engine): super().__init__(engine) self.dice_space = 50 self.dice_fromedgex = 30 self.dice_fromedgey = 30 self.images = Images() self.roll_button = self.create_roll_button() self.bgroup = ButtonGroup() self.dice = Group() self.set_up() def create_roll_button(self): pen = Pen(pygame.font.Font(None, 28), 'snow') pos = self.dice_space * 5 - 100 + self.dice_fromedgex, 80 callback = Callback(self.push_rolled) return Button(self.images.button_images, pen, "Roll", pos, callback) def set_up(self): images = self.images.dice, self.images.hdice, self.images.sdice for i in range(5): pos = self.dice_space * i + self.dice_fromedgex, self.dice_fromedgey self.dice.add(Die(images, pos)) self.bgroup.add(self.roll_button) pos = self.engine.rect.centerx, 10 pen = Pen(pygame.font.Font(None, 22), 'lawngreen') self.lines = DisplayLines(pen, pos) self.get_data() def push_rolled(self, callback): for d in self.dice: if d.is_selected: d.roll() self.get_data() def get_data(self): self.lines.clear() values = [d.value for d in self.dice] set_v = set(values) if len(set_v) == 1: self.lines.add("Yahtzee") if len(set_v) == 2: for v in set_v: if values.count(v) == 4: self.lines.add("4 of a kind") elif values.count(v) == 3: self.lines.add("Fullhouse") if len(set_v) == 3: data = [values.count(i) for i in set_v] if 3 in data: self.lines.add("3 of a kind") else: self.lines.add("two pair") if len(set_v) == 4: data = list(set_v) data.sort() if [1, 2, 3, 4] == data or [2, 3, 4, 5] == data or [3, 4, 5, 6] == data: self.lines.add("small straight") if len(set_v) == 5: data = list(set_v) data.sort() if [1, 2, 3, 4, 5] == data or [2, 3, 4, 5, 6] == data: self.lines.add("large straight") if ([1, 2, 3, 4] == data[:-1] or [2, 3, 4, 5] == data[:-1] or [2, 3, 4, 5] == data[1:] or [3, 4, 5, 6] == data[1:]): self.lines.add("small straight") # Override. From state interface def on_draw(self, surface): surface.fill('gray10') self.dice.draw(surface) self.bgroup.draw(surface) self.lines.draw(surface) # Override. From state interface def on_event(self, event): if event.type == pygame.QUIT: self.engine.quit() elif event.type == pygame.MOUSEMOTION: for d in self.dice: d.mouse_over(event.pos) for b in self.bgroup.buttons: b.mouse_over(event) elif event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: self.roll_button.click(event) for d in self.dice: d.selected() if __name__ == '__main__': pygame.init() engine = DisplayEngine("Dice", 800, 600) game = GameState(engine) engine.run(game) pygame.quit() |