So i have this platforming game that im programming in pygame. I have a jump method and i want to add a dely between jumps. That way i dont keep jumping into the ceiling. Heres the code
def jump(self):
if (self.onGround == True):
return
self.velocity = 10
self.onGround = False
This is what calls it in the main game loop:
if (event.key==pygame.K_UP):
player.jump()
Keep track of the last time you jumped, or of the next time you're allowed to jump (with pygame.time):
def __init__(self, …):
# ...
self.next_jump = 0
def jump(self):
if pygame.time.get_ticks() < self.next_jump:
return
# ...
self.next_jump = pygame.time.getticks() + 500
Depending on which type of loop you've gone with (traditional event loop, frame-rate-limited loop, unlimited-frame-rate loop) there may be better answers involving, e.g., setting a timer or using a Clock, but this will work with any type.
Related
I have already implemented the invincibility / immunity part for when bullet collides with tank, now I want the sprite to "flash"/less opacity/invisible while immune. Here is the relevant code and TODOs where I want to implement a change in the tank image.
class Tank(GamePhysicsObject):
# Handle respawn
self.time_since_death = pygame.time.get_ticks()
self.immune = False
self.immune_time = 3000
def collision_bullet_tank(arb, space, data):
# Set immunity, save time since death, start flashing
tank.parent.immune = True
tank.parent.time_since_death = pygame.time.get_ticks()
tank.parent.start_flashing()
def post_update(self):
# If tank has been immune for some time, make tank vulnerable again
current_time = pygame.time.get_ticks()
if current_time - self.time_since_death > self.immune_time:
self.immune = False
# TODO: Stop flashing
Basically, my question is how can I make a visual sprite change?
Edit: More specifically, what do I type here?
def start_flashing(self):
""" Call this function to make the tank flash. """
#TODO: White layer opacity 50% ?
1
The simplest solution is to draw the object Tank only in every 2nd frame as long as self.immune is set:
class Tank(GamePhysicsObject):
def __init__(self):
self.immune = False
self.frame_count = 0
def draw(self)
draw_tank = True
if self.immune:
self.frame_count += 1
draw_tank = self.frameCount % 2 == 0
if draw_tank:
# draw the tank
When I try to fire my knife in my game, it doesn't work. I am not very experienced in pygame. This is my first game that I'm making. It worked the first time, but now I try to call a new knife in a function and it doesn't work. I have tried a lot but it won't work. Here's my is my code:
# first I do this
realknifeImg = pygame.image.load('realknife.png')
Realknife_state = "ready"
# then this
def fire_realknife(x, y):
global Realknife_state
Realknife_state = "fire"
screen.blit(realknifeImg, (x, y))
# and finally this
def outro():
RealknifeX = 750
RealknifeY = 400
fire_realknife(RealknifeX, RealknifeY)
if Realknife_state == "fire":
fire_realknife(RealknifeX, RealknifeY)
RealknifeX += 1
The position of the knife is continuously initialized in the outro. Set to position before the function and use the global statement to change the position in the function:
RealknifeX = 750
RealknifeY = 400
def outro():
global RealknifeX, RealknifeY
fire_realknife(RealknifeX, RealknifeY)
if Realknife_state == "fire":
RealknifeX += 1
You need to use a rect for this. It is the standard way of moving
images in pygame.
Instead of having all these variables you can just say:
r_knife_rect = realKnifeImg.get_rect()
r_knife_rect.x = 750
r_knife_rect.y = 400
Then whenever you want to move it call:
r_knife_rect.x += 1
Pygame wont like you using a tuple of (x,y) in your blit, it likes
rect style objects instead. To blit just say:
screen.blit(realKnifeImg, real_knife_rect)
Edit:
Using this also means your knife has a premade hitbox.
I am attempting to make pong using Turtle, I have most things working, however, I would like to implement the ability to hold a key to move the bumpers up and down, rather than having to tap the key multiple times. These are my functions for movement so far.
def lbump_move_up():
x = lbump.ycor()
x += bumpspeed
if x > 240:
x = 240
lbump.sety(x)
def lbump_move_down():
x = lbump.ycor()
x -= bumpspeed
if x < -240:
x = -240
lbump.sety(x)
def rbump_move_up():
x = rbump.ycor()
x += bumpspeed
if x > 240:
x = 240
rbump.sety(x)
def rbump_move_down():
x = rbump.ycor()
x -= bumpspeed
if x < -240:
x = -240
rbump.sety(x)
turtle.listen()
turtle.onkey(lbump_move_up,'w')
turtle.onkey(rbump_move_up,'Up')
turtle.onkey(lbump_move_down,'s')
turtle.onkey(rbump_move_down,'Down')
turtle.onkey(ball_move,'Return')
I don't have a complete answer to this, as I came here looking for one myself. However, I have some progress on it that someone else could finish...
You can create a new class that calls like a recursive function as follows:
class function2:
def __init__(self,fun,args=None):
self.fun=fun
self.args=args
def begin_loop(self):
self.looping=True
self.loop()
def loop(self):
self.fun(self.args)
if self.looping: self.loop()
def end_loop(self):
self.looping=False
Now to tie it to your example, you can convert your functions into function2s and thus call them onkeypress as follows:
l_up=function2(lbump_move_up)
r_up=function2(rbump_move_up)
l_down=function2(lbump_move_down)
r_down=function2(rbump_move_down)
Wn=turtle.Screen()
Wn.onkeypress(l_up.begin_loop,'w')
Wn.onkeypress(r_up.begin_loop,'Up')
Wn.onkeypress(l_down.begin_loop,'s')
Wn.onkeypress(r_down.begin_loop,'Down')
Wn.onkeyrelease(l_up.end_loop,'w')
Wn.onkeyrelease(r_up.end_loop,'Up')
Wn.onkeyrelease(l_down.end_loop,'s')
Wn.onkeyrelease(r_down.end_loop,'Down')
Wn.listen()
So to the issue: RecursionErrors
If you want your function to cause a small change, then you'll need to hold the button for a long time and that causes RecursionErrors - which at least in this case don't seem to be affected by try/except statements.
Once again, sorry for the incomplete solution, but I feel like you can probably get away with larger movements in pong.
I had the same issue, but found the "onkeypress" function in the Turtle documentation (https://docs.python.org/3/library/turtle.html#turtle.onkeypress), which resolved the issue for me.
turtle.onkeypress(fun, key=None)
"onkeypress", will move the turtle as long as the key is pressed, while "onkey" will move it once when the key is released.
Example:
from turtle import Turtle, Screen
turtle = Turtle("turtle")
turtle.penup()
screen = Screen()
screen.listen()
def move_forward():
turtle.forward(20)
def move_backward():
turtle.backward(20)
screen.onkeypress(move_forward, "w")
screen.onkey(move_backward, "s")
screen.exitonclick()
I'm a beginner and was wondering if there was any way to finish the current animation before the program closes. Here is the sample of the code:
if playerHealth <= 0: # If the player dies
expl = Explosion(hit.rect.center, 'lg')
all_sprites.add(expl) # Show animation of him blowing up
running = False # End the game
Basically the running = False code would run before the animation(expl) starts. Is there a better way of showing this animation fully?
This sounds like a case for using a callback function. Pygame's animation class has the on_finished property which is intended to be assigned to a callback. The callback would be called once the animation is finished playing and would stop the game. Here's an example Explosion class.
class Explosion:
def __init__(self, rect, size, cb_func):
self.animate_explosion(cb_func)
...
def animate_explosion(self, cb_func):
# start animation here
...
# when animation finishes
self.on_finished = cb_func()
And then within your game logic you have something like the following:
def callback():
running = False
if playerHealth <= 0: # If the player dies
expl = Explosion(hit.rect.center, 'lg', callback())
all_sprites.add(expl) # Show animation of him blowing up
Probably You need more modifications, but one of them is:
if playerHealth <= 0 and not blowing_up_animation_started: # If the player dies
expl = Explosion(hit.rect.center, 'lg')
all_sprites.add(expl) # Show animation of him blowing up
blowing_up_animation_started = True
blowing_up_animation_finished = False
if blowing_up_animation_finished:
running = False # End the game
I've been putting together an isometric tile-based RPG using Python and the Pyglet library. I've run into the following problem, however:
My player movement is based on positions on the three-dimensional array that consists of tiles. To limit movement speed, I use a global variable: TILE_TO_TILE_DELAY = 200.
TILE_TO_TILE_DELAY is supposed to be the amount of time in milliseconds it takes for the player to move from one tile to another. During this time, they should not be able to make a new movement.
The system I've been using is that I have a timer class, like this:
import time
def GetSystemTimeInMS(): #Return system time in milliseconds
systime = round((time.clock()*1000), 0)
return systime
class Timer:
def __init__(self,time):
#time = time for which the timer runs
self.time = time
self.start_time = 0
self.stop_time = 0
def StartTimer(self):
#start_time = time at which the timer was started
self.start_time = GetSystemTimeInMS()
self.stop_time = self.start_time + self.time
def TimerIsStopped(self):
if GetSystemTimeInMS() >= self.stop_time:
return True
else:
return False
The player class has a Timer object:
self.MoveTimer = Timer(TILE_TO_TILE_DELAY)
When the player presses the W-key, a function is called that checks for player.MoveTimer.TimerIsStopped(). If it returns True, it calls player.MoveTimer.StartTimer() and starts a new movement to the next position.
In the even loop, the update(dt) function is set to happen 30 times a second:
def update(dt):
if player.MoveTimer.TimerIsStopped()
player.UpdatePosition()
pyglet.clock.schedule_interval(update, 1/30)
Now, by my logic, update(dt) should check 30 times a second whether or not enough time has passed to warrant the player a new movement event. However, for some reason the player moves much faster at lower FPS.
When my FPS is around 30, the player moves much faster than in areas where there are less tile sprites, pumping the framerate to 60. In areas where FPS is high, the player indeed by my measurements moves almost twice as slowly.
I just cannot figure it out, nor did I find anything off the internet after a day of searching. Some help would be much appreciated.
Edit: The code that starts the MoveTimer:
def StartMovement(self, new_next_pos):
self.RequestedMovement = False
if self.GetCanMoveAgain():
self.SetNextPos(new_next_pos)
self.SetMoveDirection(self.GetDirection())
#Start the timer for the movement
self.MoveTimer.StartTimer()
self.SetIsMoving(True)
self.SetStartedMoving(True)
self.SetWalking(True)
self.SetNeedUpdate(True)
self.MOVE_EVENT_HANDLER.CreateMoveEvent()
GetCanMoveAgain() returns the value of player.can_move_again, which is set back to True by the UpdatePosition() in update(dt)
Alright, I fixed the problem, how ever I'm still unsure about what caused it. Maybe it was some sort of a rounding error with milliseconds, having to do with more checks being made to the clock when the FPS is higher. Anyways, the solution:
Instead of using the system clock, I decided to use Pyglet's own "dt" argument in their update functions:
The dt parameter gives the number of seconds (due to latency, load and timer inprecision, this might be slightly more or less than the requested interval).
The new timer looks like this:
class dtTimer: #A timer based on the dt-paremeter of the pyglet event loop
def __init__(self,time):
self.time = time
self.time_passed = 0
def StartTimer(self):
self.time_passed = 0
def UpdateTimer(self, dt):
self.time_passed += dt*1000
def GetTime(self):
return self.time
def GetTimePassed(self):
if not self.TimerIsStopped():
return self.time_passed
else:
return 0
def TimerIsStopped(self):
if self.time_passed > self.time:
return True
else:
return False
When the loop attempts to update the player's position, if the TimerIsStopped returns false, dt is simply added to the Timer's self.time_passed. This has fixed the issue: the time it takes for the player to move is now constant.
Thanks for looking at my issue.