I know the title is not very clear, but I don't know what else I can say.
I have a player attacking, and when he is done attacking, I start a timer for 1 second, so we have to wait one second before attacking again. It wasn't working (we could attack only once) and I didn't know why, so I added print(self.between_two_attacks())and everything worked fine, I could attack, wait one second and attack again.
Here is the program, I don't know if it is enough because I have no idea where the bug comes from.
def between_two_attacks(self):
if self.after_attack_max == 0:
self.after_attack_max = pg.time.get_ticks() + 1000
print("set timer")
else:
after_attack = pg.time.get_ticks()
print("start timer")
if after_attack >= self.after_attack_max:
print("can attack again")
self.can_attack = True
self.after_attack_max = 0
def draw(self, win):
print(self.between_two_attacks())
if (self.attackcount + 1) >= 5:
self.attackcount = 0
self.between_two_attacks()
self.action = STAND_UP
self.arme = LIGHTSABER_OUT
self.stops_attacking = True
self.can_attack = False
if self.action == ATTACKING:
win.blit...
Run = True
While Run:
for event in pg.event.get():
if event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE and player.can_attack == True:
player.action = ATTACKING
If anything isn't clear in this part of the program, just tell me and I'll try to explain. Thanks for your help :)
The method between_two_attacks has to be invoked, before the state of self.can_attack is retrieved. self.can_attack is set in between_two_attacks. If the method is not called, the self.can_attack will never become True.
When you do print(self.between_two_attacks()), the self.between_two_attacks() is called.
Furthermore, the method can be simplified:
self.can_attack has to be set if self.after_attack_max == 0 or if the current time is greater than self.after_attack_max.
If self.can_attack is set then compute the restart the timer. If it is not set then it has to be evaluated. Initially self.after_attack_max is 0. If the current time is greater than self.after_attack_max, attacks have to be allowed and the timer has to be started again:
def between_two_attacks(self):
current_time = pg.time.get_ticks()
if self.can_attack:
self.after_attack_max = current_time + 1000
elif current_time > self.after_attack_max:
self.can_attack = True
self.after_attack_max = current_time + 1000
Note, self.after_attack_max is only set in between_two_attacks, do not reset it anywhere else.
When you print self.between_two_attacks() the self function gets called and executed. Now, if you added the whole line it means that this function was not executed at this point before and now it is, so if you remove print and left only function at the same location you should get the same behaviour.
I do not know if I manage to explain my thought so here as a quick example:
x = 5
def change_x():
global x
x+=10
print(x)
print(change_x())
print(x)
if you remove print() you will get same result.
Related
if keyboard.h and start == False:
start = True
print("hello?")
dotty_move()
if keyboard.h and start == True:
start = False
print("bye")
dot1.pos = x, y
dot2.pos = xi, yi
def dotty_move():
global speed, speedi, start
if start == True:
animate(dot1, duration = speedi, pos = (xi, 0), on_finished = dot_move())
animate(dot2, duration = speed, pos = (x, 0), on_finished = dot_move())
def dot_move():
global speed, speedi, start, x, y, xi, yi, dot1
if start == True:
animate(dot1, pos=(0, 425), on_finished = dotty_move())
print("test")
animate(dot2, duration = speed, pos = (x, 425), on_finished = dotty_move())
dot_move()
Hello! Everything before the dotty_move() subroutine (I have really bad names for my subroutines haha) is in the update() subroutine.
I am trying to have the input of the key "h" only happen once, but it seems to input 3 times if I press it for a split second. I think this is because it's in the update class, but I couldn't find another way to check for the key press.
This code is meant to make two dots in pre-set x coordinates move up and down over and over with a defined speed whenever you press "h". If you press "h" again it is supposed to stop this. The reason I think this isn't working at the moment is because of the issue I stated earlier: it's turning on and off so it never does anything.
But I don't know. If anyone can help it, would be really appreciated. Thanks :)
The issue is here:
if keyboard.h and start == False:
start = True
print("hello?")
dotty_move()
if keyboard.h and start == True:
start = False
print("bye")
Execute this code in your mind:
Is keyboard.h is pressed and start = false (yes it is!)
start := true
And then straight away immediately after:
Is keyboard.h is pressed and start = true (yes, we just set it above!)
start := false
So as soon as start is set, it becomes un-set.
Probably just an "else-if" (elif:) will fix it.
if keyboard.h and start == False:
start = True
print("hello?")
dotty_move()
elif keyboard.h and start == True:
start = False
print("bye")
Because only one of the logical expressions will match.
You may also have timing related issues, where your program is running so fast, it still turns off straight after turning on. To handle this, maybe measure the time between on/off with something like pygame.time.get_ticks().
next_click_time = 0 # time [h] can be pressed again
# ...
if keyboard.h:
time_now = pygame.time.get_ticks()
if ( time_now > next_click_time ):
next_click_time = time_now + 300 # milliseconds in the future
start = not start # invert start
if ( start ):
# turning on
print("hello?")
dotty_move()
else:
# turning off
print("end")
# player is a class object (class Game)
def the_hunt(target_number, player):
timer_thread = Thread(target=timer, args=(player, ))
timer_thread.start()
while True:
# when time is zero sec it will terminate the game
if not timer_thread.is_alive():
print("The Hunt is Over. You ran out of time!")
player.game_status(False)
return False
# check for the win
if player._score == player.win_points:
timer_thread.cancel()
player.game_status(True)
def timer(obj: Game):
sleep_duration = 60
while sleep_duration > 0:
time.sleep(1)
obj.duration_reduce(1)
sleep_duration -= 1
error I get
AttributeError: 'Thread' object has no attribute 'cancel'
when I used
timer_thread = Thread(target=timer)
instead of
timer_thread = Thread(target=timer, args=(player, ))
and
def timer():
it worked but with the given code where I have used class object, it gives an error.
I am not using threading in class just passing a value to the class
Since the Thread class has no cancel method, it should always produce the AttributeError.
Are you perhaps confusing it with a Timer (which is a subclass of Thread)? That does have a cancel method.
with all your help I was able to find my answer
#Guy gave the link to the question from which I manage to get the answer
def the_hunt(target_number, player):
stop_threads = False
timer_thread = Thread(target=timer, args=(player, lambda: stop_threads))
while True:
# when time is zero sec it will terminate the game
if not timer_thread.is_alive():
print("The Hunt is Over. You ran out of time!")
player.game_status(False)
return False
# check for the win
if player._score == player.win_points:
#timer_thread.cancel()
stop_threads = True
player.game_status(True)
def timer(obj: Game, stop):
sleep_duration = 60
while sleep_duration > 0:
time.sleep(1)
obj.duration_reduce(1)
sleep_duration -= 1
if stop():
break
it is not giving me the error and it is working as I wanted
I am writing a short program to display cards in a round. I suspect that it is the length of the code which prevents the final 'OK' submit on P3 (the last player's submission) from executing properly: at which point the program sometimes will evaluate the winner and clear the round, but most of the time instead will freeze.
I have tried clock.tick(low fps), pygame.event.pump(), and pygame.event.clear(). Any leads would be much appreciated.
# Round loop begins. Finish until all hands are empty.
while not self.game.get_is_last_round():
player = self.game.get_player(self.game.get_player_turn())
hand = player.order_hand(player.get_hand(),
self.game.get_round_level(),
self.game.get_round_trump_suit())
ok_clicked_2 = False
pygame.event.pump()
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.deal_running = False
self.is_running = False
pygame.display.quit()
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
play = player.get_play()
click = pygame.mouse.get_pressed(num_buttons=3)
pos = pygame.mouse.get_pos()
# Used DeMorgan's law to resolve error
ok_clicked_2 = (OK1_X < pos[0] < OK1_X + B_W) and (OK1_Y < pos[1] < OK1_Y + B_H) and click[0]
b1, card = self.check_hand(pos, player)
b2, play_card = self.check_play(pos, player)
if b1:
hand.remove(card)
play.append(card)
player.set_play(
player.order_hand(play, self.game.get_round_level(),
self.game.get_round_trump_suit()))
player.set_hand(
player.order_hand(hand, self.game.get_round_level(),
self.game.get_round_trump_suit()))
if b2:
play.remove(play_card)
hand.append(play_card)
player.set_play(
player.order_hand(play, self.game.get_round_level(),
self.game.get_round_trump_suit()))
player.set_hand(player.order_hand(hand, self.game.get_round_level(),
self.game.get_round_trump_suit()))
clock.tick(100)
surface.blit(background, (0, 0))
if len(self.game.get_player(0).get_hand()) == 25:
self.game.set_is_first_round(True)
else:
self.game.set_is_first_round(False)
if len(self.game.get_player(0).get_hand()) == 0:
self.game.set_is_last_round(True)
else:
self.game.set_is_last_round(False)
if self.game.get_play_in_turn() != NUM_PLAYERS:
pygame.event.pump()
clock.tick(100)
if len(hand) <= 1:
width = 0
x = (BG_WIDTH - CARD_WIDTH) // 2
elif len(hand) >= 8:
width = (BG_WIDTH - SIDE_W - CARD_WIDTH) // (len(hand) - 1)
x = BG_WIDTH // 2 - (CARD_WIDTH + (width * (len(hand) - 1))) // 2
else:
width = CARD_WIDTH
x = (BG_WIDTH - (CARD_WIDTH * len(hand))) // 2
surface.blit(background, (0, 0))
self.blit_backs()
self.blit_round()
self.show_ok()
self.show_hand(x, ROW3h, width, hand)
self.show_hand(CARD_POSITIONS[0][0], CARD_POSITIONS[0][1], SLIM_WIDTH, play)
if ok_clicked_2:
for card in play:
hand.append(card)
player.set_hand(player.order_hand(hand, self.game.get_round_level(),
self.game.get_round_trump_suit()))
# If player is first to start a round, he/she has a different validity check.
# (Sets the pattern for the cycle)
if player.get_begins_cycle():
valid = self.game.check_validity(True) # is_first
else:
valid = self.game.check_validity(False) # Is not first to play in the round
if not valid: # Clear holding if invalid
if (play == []) or (player.get_play() == []):
print("\nYou must make a play.\n")
else:
print("Invalid play. Try again.")
if not player.get_begins_cycle():
valid_plays = player.get_valid_plays(self.game.get_pattern(),
self.game.get_round_trump_suit())
print("Valid plays: \n")
for temp_play_idx in range(len(valid_plays)):
temp_play = valid_plays[temp_play_idx]
print("[", end='')
for temp_card_idx in range(len(temp_play)):
valid_plays[temp_play_idx][temp_card_idx].show_card("", '')
if temp_card_idx != len(temp_play) - 1:
print(", ", end='')
print("]")
# Clear the current player's selection and restore hand to its original content
cycle_order = self.game.get_cycle_order()
cycle = self.game.get_cycle()
for player_order in range(len(cycle_order)):
if player == cycle_order[player_order]:
cycle_order.remove(player)
cycle.pop()
self.game.set_cycle_order(cycle_order)
self.game.set_cycle(cycle)
else: # Valid play on submit
# Special case for HIGH_SUIT play, play lowest card if another player has greater
play = self.game.check_high_suit(play)
# If friend card played, establish and print teammates
# TODO: auto-designate friends if the last round
# has been reached (friends buried in treasure case)
# TODO: determine whether friend is "dead"
self.game.check_for_friends()
cycle = self.game.get_cycle()
cycle.append(play)
self.game.set_cycle(cycle)
cycle_order = self.game.get_cycle_order()
cycle_order.append(player)
self.game.set_cycle_order(cycle_order)
# self.clear_positions()
for card in play:
hand.remove(card)
player.set_hand(
player.order_hand(hand, self.game.get_round_level(),
self.game.get_round_trump_suit()))
self.game.next_player_turn()
self.game.set_play_in_turn(self.game.get_play_in_turn() + 1)
print(self.game.get_play_in_turn())
play = []
else:
self.game.set_play_in_turn(0)
# Distribute any points in the round to round winner
self.update_cycle_points()
for p in self.game.get_players():
for card in p.get_play():
discard = self.game.get_discard()
discard.append(card)
p.set_play([])
pygame.event.clear()
clock.tick(100)
pygame.display.update()
I think it's time for a code-cleanup, then your issue will go away (or you'll find it).
Currently the main loop is a big mix-up of event handling, screen-painting and game engine. Try to separate these parts out.
Move some of the in-loop processing out to functions - like the block after if ok_clicked_2:. It may help to make a data structure in which you store the game-state, then have the events change that game state. When it comes time to draw the game to the screen, the painting code can query the state, acting accordingly.
In terms of your actual lockup, if self.game.get_play_in_turn() == NUM_PLAYERS nothing is painted to the screen. Is this intentional? Add some print()s to your code so you can know the execution flow (or learn to use the python debugger).
I think the biggest step forward would be to move all the screen painting to one section of the main loop, something like:
# Render the screen
print( "Rendering Screen" )
surface.blit(background, (0, 0))
self.blit_backs()
self.blit_round()
# etc. for all other things, score, buttons, ...
clock.tick(60)
pygame.display.update()
You seem to be handling the events OK, so it would probably be better to remove the calls to pygame.event.pump() and pygame.event.clear(). You don't need these.
Following Kingsley's advice, I organized the code by function: rendering screen, game engine, and event handling. I would provide MRE as Random Davis suggests, but that would include 5 integrated files which would take too long to pare down.
It turns out that the problem lay in a piece of code which is called separately: "update_cycle_points()". Within, there is a while loop which does not contain an event handler. The solution was to change it to a for loop, which Pygame seems to process without error (does not freeze because it does not expect event handling there).
I also removed pygame.event.clear() and pump() functions without problems.
I'm trying to run some animations using pygame and pyganim and I want them to run when a key is pressed for a certain period of time and then stop. This is what I've got so far but I cant get the if block at the bottom to trigger, which should stop the animation. I'm assuming the time_now variable keeps updating so it's never greater the time_end. What is wrong with my code?
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_r:
time_now = time.time()
time_end = time.time() + 5
runRight.stop()
runLeft.stop()
rollRight.stop()
rollLeft.stop()
standStill.stop()
unsheathSword.play()
if time_now >= time_end:
print('stop')
unsheathSword.stop()
standStill.play()
First, your indentation is wrong. The line while True: should not be on the same indentation level as the second line.
Your example doesn't work because of
time_now = time.time()
time_end = time.time() + 5
It's never the case that time_now >= time_end; if you plug in the values, it's time.time() >= time.time() + 5, which is obviously always False. That's like saying that 55 >= 55 + 5, which is 55 >= 60, which is always False.
You also define both times with time.time() and at the same place that you use them, which will never let one of them evolve in time separately compared to the other. I suggest splitting the event trigger from the event:
unsheathingSword = False
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_r:
time_unsheath_end = time.time() + 5
unsheathingSword = True
runRight.stop()
runLeft.stop()
rollRight.stop()
rollLeft.stop()
standStill.stop()
unsheathSword.play()
if unsheathingSword and time.time() >= time_unsheath_end:
print('stop')
unsheathSword.stop()
standStill.play()
unsheathingSword = False
Here unsheathingSword = False prevents that second block from infinitely executing. There are better ways of doing this, but I hope this will help you for now.
An additional benefit of spliting the things that trigger something from the handling of the consequences, is that it allows for much easier coding later on.
Putting these two blocks of code into functions is probably even better.
For example:
def main():
if something == True:
player()
elif something_else == True:
computer()
def player():
# do something here
check_winner() # check something
computer() # let the computer do something
def check_winner():
check something
if someone wins:
end()
def computer():
# do something here
check_winner() # check something
player() # go back to player function
def end():
if condition:
# the player wants to play again:
main()
elif not condition:
# the player doesn't want to play again:
# stop the program
# whatever i do here won't matter because it will go back to player() or computer()
main() # start the program
My problem is that if a certain condition becomes true (in the function check_winner) and function end() executes it will go back to computer() or player() because there's no line that tells the computer to stop executing player() or computer(). How do you stop functions in Python?
A simple return statement will 'stop' or return the function; in precise terms, it 'returns' function execution to the point at which the function was called - the function is terminated without further action.
That means you could have a number of places throughout your function where it might return.
Like this:
def player():
# do something here
check_winner_variable = check_winner() # check something
if check_winner_variable == '1':
return
second_test_variable = second_test()
if second_test_variable == '1':
return
# let the computer do something
computer()
In this example, the line do_something_else() will not be executed if do_not_continue is True. Control will return, instead, to whichever function called some_function.
def some_function():
if do_not_continue:
return # implicitly, this is the same as saying `return None`
do_something_else()
This will end the function, and you can even customize the "Error" message:
import sys
def end():
if condition:
# the player wants to play again:
main()
elif not condition:
sys.exit("The player doesn't want to play again") #Right here
def player(game_over):
do something here
game_over = check_winner() #Here we tell check_winner to run and tell us what game_over should be, either true or false
if not game_over:
computer(game_over) #We are only going to do this if check_winner comes back as False
def check_winner():
check something
#here needs to be an if / then statement deciding if the game is over, return True if over, false if not
if score == 100:
return True
else:
return False
def computer(game_over):
do something here
game_over = check_winner() #Here we tell check_winner to run and tell us what game_over should be, either true or false
if not game_over:
player(game_over) #We are only going to do this if check_winner comes back as False
game_over = False #We need a variable to hold wether the game is over or not, we'll start it out being false.
player(game_over) #Start your loops, sending in the status of game_over
Above is a pretty simple example... I made up a statement for check_winner using score = 100 to denote the game being over.
You will want to use similar method of passing score into check_winner, using game_over = check_winner(score). Then you can create a score at the beginning of your program and pass it through to computer and player just like game_over is being handled.
Maybe you are looking yield, its same as return but it stops function's execution rather than terminating functions, You can look at generators here.