I created this function that randomly spawn a dot, but because its in my game loop the dot just does not stay where it is. I want it to spawn a dot and then wait 6 seconds and spawn another one randomly, but the first still being there. Because there is other things happening on the screen at the same time I cant use time.sleep(6). Any help solving this would be really appreciated.
def random_spawn():
image_x = random.randrange(122, 476)
image_y = random.randrange(90, 350)
screen.blit(Green, (image_x, image_y))
Don't use time.sleep. In general, in game programming, you never want to use time.sleep for any reason.
In your case, you just need to check a timer or clock every so often, and if the time is up, run your spawn function.
This is what a typical game loop will look like:
while True:
# get/handle player input
# update game state
# update sprites
# draw background
# draw sprites
# flip display
In your case, when you update your game state, you should check how much time has passed since you last spawned your random sprite. If it has been longer than 6 seconds, spawn a new one.
It will look something like this:
if time.clock() - last_spawn_time > 6.0:
random_spawn()
I think a way to fix this would be to only randomly spawn a dot if certain conditions are met. So you can define a function that randomly generates new coordinates outside of your main function (and returns them). Then your main game loop will take care of rendering the dot on the screen if certain conditions are met. For example:
image_x, image_y = get_random_coordinates()
while True:
if time.clock() - last_spawn_time > 6.0:
image_x, image_y = get_random_coordinates()
last_spawn_time = time.clock()
screen.blit(Green, (image_x, image_y))
So the idea is that you draw the same random coordinates, and keep drawing those same coordinates until the time between your last spawning and now is more than 6 seconds.
If you are trying to avoid using threads, and just using a while loop, perhaps this might work:
import time
def my_function():
print 'it has been about 6 seconds'
prev_time_stamp = time.time()
while True:
#do something
#do something else
if time.time() > prev_time_stamp + 6:
prev_time_stamp = time.time()
my_function()
#do something else
import random module:
import random
create random coordinates:
rand_X = (random.randrange(0, display_width - image_width)
rand_Y = (random.randrange(0, display_height - image_height)
blit image:
gameDisplay.blit(yourimage,(randAppleX,randAppleY))
Related
I'm controlling turtle animation by using tracer(0, 0) and stamping the turtle cursor itself.
However, it refreshes strangely, it's suppose to be 1 second between any animation (with sleep) but it seems to sometimes hang for two second, then a couple of second later zaps into position:
The code is:
import turtle
import time
screen = turtle.Screen()
screen.listen()
screen.tracer(0, 0)
pen = turtle.Turtle()
pen.speed('fastest')
pen.shape("circle")
pen.penup()
pen.hideturtle()
next = [0, 8]
time.sleep(10)
while True:
pen.goto(next[0]*20, next[1]*20)
screen.clear()
pen.stamp()
screen.update()
time.sleep(1)
next = [next[0], next[1]-1]
Seems like simply sleeping is not a good idea. I seem to also have trouble getting it to work with threading. How do I it works with a predefined time like 1 second in this case?
Depending on the efficiency of your computer, time.sleep(1) would have different durations.
Instead of relying on the efficiency of your computer to adjust the sleep duration, you can use the perf_counter method to detect how long your program has been running from one part to another.
Basically, once you run the program, the time tracking starts. Where ever you call time.perf_counter(), it will return the amount of time for the start of the program to reach the place you called time.perf_counter().
import turtle
import time
screen = turtle.Screen()
screen.listen()
screen.tracer(0, 0)
pen = turtle.Turtle()
pen.speed('fastest')
pen.shape("circle")
pen.penup()
pen.hideturtle()
nxt = [0, 8]
time.sleep(10)
while True:
start = time.perf_counter() # Find the total performance time from the start of the program to here
pen.goto(nxt[0]*20, nxt[1]*20)
screen.clear()
pen.stamp()
screen.update()
while time.perf_counter() - start < 1: # Keep looping until the amount of performance time from right under "while True" to this line reaches 1 second
pass
nxt = [nxt[0], nxt[1]-1]
Using sleep and while True: loops in an event-driven world like turtle is a bad idea. We can instead use a timer event to produce the effect you describe. While we're at it, we're going to toss stamping as that's not adding anything, and neither are clear() and listen():
from turtle import Screen, Turtle
CURSOR_SIZE = 20
def drop():
screen.update()
turtle.sety(turtle.ycor() - CURSOR_SIZE)
screen.ontimer(drop, 1000) # 1 second
screen = Screen()
screen.tracer(False)
turtle = Turtle()
turtle.shape('circle')
turtle.penup()
turtle.sety(160)
screen.ontimer(drop, 10_000) # 10 seconds
screen.mainloop()
Of course, you need to add some logic to avoid restarting the timer event if/when the circle falls off the bottom of the screen. By using while True: loops, and sleep() you potentially block events, like user keystrokes or window closing, from reaching turtle.
One minor inaccuracy here is the drop() method takes some minimal time to run -- to factor that out, if needed/desired, you need to adjust the timing based on clock time or something like time.perf_counter() as #AnnZen demonstrates, but not in a pass loop!
EDIT:
I need to save each and every pixels the turtle moves through as it moves from say(-305,305) to (205,305).
I want to save each and every of those coordinates on the path in a variable. Is there anyway to do it?
I.
You need some kind of mechanism for intervention to the drawing process,
similar to the update function in the Tkinter library and the callback functions of the OpenGL library.
There is an ontimer function in the Turtle library, which can perform some action
(in your case - insertion into a list) after a given time period.
This method is shown below. It works but have some limitations:
1. it doesn't work if drawing speed is fastest ( turtle.speed('fastest') ),
2. it doesn't want to insert a very first pair of coordinates in a list - you have to do this insertion by yourself,
3. Some parts of a drawing may have gaps. When replaying from a list, which stores coordinates only, such situation is not processed (you will get a drawing without gaps).
import turtle as tr
from turtle import *
time = 3 # 3 milliseconds period
status = False
coord=[] # List of coordinates
sx, sy = -305, 305 # Start point
ex, ey = 205, 305 # End point
def func(): # Timer callback function
if status:
coord.append([tr.xcor(),tr.ycor()]) # list of lists
# coord.append(pos()) # list of tuples
ontimer(func, t = time)
def redraw(): # Replay function
tr.up()
tr.goto(coord[0][0], coord[0][1])
tr.down()
for i in range(1, len(coord)):
tr.goto(coord[i][0], coord[i][1])
def restart2(): # Keyboard callback function
tr.reset()
redraw()
tr.screensize(768, 512) # Preliminary actions
tr.setup(width = 1.0, height = 1.0)
tr.onkeypress(restart2)
tr.listen()
tr.up()
tr.goto(sx,sy)
coord.append([sx, sy]) # Manual recording of the first point - list of lists case
#coord.append(tr.pos()) # list of tuples case
status = True
tr.ontimer(func, t = time) # Start timer
tr.down()
tr.goto(ex, ey) # Main drawing process
status = False # "Stop" timer (timer pause for this example)
tr.up()
tr.home()
coord.append([tr.xcor(), tr.ycor()]) # Manual recording of the first point after gap
status = True
tr.ontimer(func, t = time) # Continue timer
tr.down()
while True: # Examlpe from the Turtle library docs
tr.forward(200)
tr.left(170)
if abs(tr.pos()) < 1:
break
tr.home()
tr.circle(50)
tr.circle(120, 180)
status = False # "Stop" timer
print(coord) # print to console
tr.done()
Press any key for replay path from the list.
II.
A lot of drawings have analytical solutions. It is much simpler then store a lot of coordinates.
It is probably simpler (but not faster?) to store the coordinates of the beginning and the end of drawing parts and to calculate desired intermediate points analytically.
I'm looking to display a message on the screen when a player wins, and get the text to display for 5 seconds, and then go back to the main menu start screen. Using the time.delay function however, my screen pauses and then displays the text in a flash, but then immediately goes to the startscreen. Is there a more efficient way of getting the text to be displayed for long enough to be read?
Below is the function I use to actually display the message:
def winnerPlayerOne():
screen.fill(PINK)
winnerP1Message = winnerFont.render("Congrats Player 1. You Win!", True, WHITE)
screen.blit(winnerP1Message, ((400 - (winnerP1Message.get_width()/2)),(300 - (winnerP1Message.get_height()/2))))
pygame.display.update()
pygame.time.delay(5000)
startscreen()
And below this is how I call this function, within the main loop:
if playeroneScore == 5:
winnerPlayerOne()
if playertwoScore == 5:
winnerPlayerTwo()
Any help would be greatly appreciated!
Benjamin's answer probably will work fine in your case. But if you want something that doesn't interfere with your game's visuals, I would consider setting a timer like so...
WITHIN GAME LOOP:
if gameWonScreen:
screen.fill(PINK)
winnerP1Message = winnerFont.render("Congrats Player 1. You Win!", True, WHITE)
screen.blit(winnerP1Message, ((400 - (winnerP1Message.get_width()/2)),(300 - (winnerP1Message.get_height()/2))))
timer = timer + elapsed/1000
elapsed = fpsClock.tick(FPS)
if timer > timeWinScreen:
gameWonScreen = false
Initialize 'timeWinScreen' to the desired message duration at the start of the application and set 'timer' to '0' and gameWonScreen to 'true' when the player wins. Using elapsed = fpsClock.tick(FPS) will hold the time value since the last tick. You don't need to use it for this process (you could just use a fraction of your FPS) but using 'elapsed' is good practice because it helps with smoothing animations of certain objects.
Try out pygame.time.wait(5000). It should behave more in the way you are expecting. It does prevent any code running in the background as well, but that didn't seem like it would be an issue for your use case.
Try this:
Remember that in the class below, time is not the way you measure time in pygame. It is the number of loop happened in the main loop.
class disp_txt:
def __init__(self,text,time,destination):
self.text = text
self.time = time
self.destination = destination
self.shfnt = pygame.font.SysFont("comicsens",30,False)
self.shtxt = self.shfnt.render(self.text,0,(255,0,0))
def show(self,surface):
if self.time>0:
surface.blit(self.shtxt,self.destination)
self.time -= 1
hint = disp_txt("text",100,(100,400)) #example
hint.show(screen) #in the main loop or where you are drawing
I'm trying to make an instance class (Block) spawn when the mouse is clicked and fade out slowly, and i want to be able to spawn an infinite number of blocks that fade out provided that the mouse is clicked fast enough.
To do this, I want to initiate the fade() function when the instance is first spawned and set ticks to 255 and set the alpha to ticks.
However, when using a while loop, the function completes itself without updating the display because the program gets constrained to the while loop in the fade() function.
Can someone help me with calling the fade function 255 times per instance?
import pygame,sys,time,Blockm
from pygame.locals import *
black,white=(0,0,0),(255,255,255)
size=w,h=1400,800
screen=pygame.display.set_mode(size)
pygame.init()
class Block(object):
sprite = None
def __init__(self, x, y):
if not Block.sprite:
Block.sprite = pygame.image.load("Block.png").convert_alpha()
self.rect = Block.sprite.get_rect(top=y, left=x)
self.fade()
def fade(self):
ticks=255
Block.sprite.set_alpha(ticks)
while ticks!=0:
ticks-=1
print(ticks)
while True:
blocks = []
mmraw=pygame.mouse.get_pos()
mmx,mmy=mmraw[0]-(pygame.image.load("Block.png").get_width())/2,mmraw[1]-(pygame.image.load("Block.png").get_width())/2
for event in pygame.event.get():
if event.type== pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
print('click')
blocks.append(Block(mmx,mmy))
for block in blocks:
screen.blit(block.sprite, block.rect)
print('trying to blit')
print(Block.sprite.get_offset())
pygame.display.update()
I have one solution for you that ought to accomplish what you want it to, but I concede that it does not answer the question exactly as asked.
The first and most hindering problems you may be having are that you need to use pygame.image.load("Block.png").convert() and not pygame.image.load("Block").convert_alpha(), since .convert_alpha() doesn't work with .set_alpha(..).
It's also possible that you aren't noticing when blocks fade in any solution because screen isn't being refreshed prior to update. New, faded blocks are being drawn over 'brighter' ones, producing no difference (overlapping blocks notwithstanding). I've added a stopgap to my solution code below that fills the screen with blue, but I imagine you'll want something different. It's marked with a comment.
What I would suggest is having each Block process its alpha locally during a call from the main loop in your for block in blocks: block before you blit it. This version of your code should give you the result you want, although it does it using just the main loop, rather than parallel loops like you were asking about...
import pygame,sys,time,Blockm
from pygame.locals import *
black,white=(0,0,0),(255,255,255)
size=w,h=1400,800
screen=pygame.display.set_mode(size)
pygame.init()
class Block(object):
sprite = None
def __init__(self, x, y):
if not Block.sprite:
Block.sprite = pygame.image.load("Block.png").convert()
# ^ MODIFIED: .convert_alpha() won't recognize the effects of .set_alpha(), so use regular .convert() instead.
self.rect = Block.sprite.get_rect(top=y, left=x)
self.ticks = 255 # NEW: Set a local tick count.
# REMOVED `self.fade()`: This runs on main now.
def fade(self):
## REPURPOSED METHOD: Runs each frame that the Block is active. Called on main loop.
# MODIFIED BLOCK: Update local tick count before setting the class's sprite alpha.
self.ticks -= 1
Block.sprite.set_alpha(self.ticks) # MOVED, MODIFIED: uses local tick count for alpha.
print(self.ticks) # UPDATED: Using local ticks.
blocks = [] # MOVED: Make a list for the Blocks, but don't clear it on frame.
while True:
mmraw=pygame.mouse.get_pos()
mmx,mmy=mmraw[0]-(pygame.image.load("Block.png").get_width())/2,mmraw[1]-(pygame.image.load("Block.png").get_width())/2
# ^ There may be a tidier way of doing this. Described in solution body...
for event in pygame.event.get():
if event.type== pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
print('click')
blocks.append(Block(mmx,mmy))
screen.fill((22,22,222))
# ^ NEW: Fill the screen with some backdrop so that the erasure is obvious!
# If you don't do this, you'll be blitting faded images over brighter ones and there will be no real change!!
for block in blocks:
block.fade() # NEW: Update this block.
if block.ticks < 1: # NEW BLOCK: If the block has become invisible...
blocks.remove(block) # NEW: Expunge this invisible block.
continue # NEW: Immediately move on to the next block.
screen.blit(Block.sprite, block.rect)
print('trying to blit')
print(Block.sprite.get_offset())
pygame.display.update()
There's a small problem with it, which is that vanishing Blocks trigger a 'flicker' in other remaining Blocks (or at least the next one in blocks) and I'm not sure why or how.
While I was looking, I found a few other things you might want to consider:
...in class Block::
-Consider using sprite = pygame.image.load("Block.png").convert() instead of sprite = None. This way, you can use something like mmx,mmy = mmraw[0] - Block.sprite.get_rect.centerx, mmraw[1] - Block.sprite.get_rect().centery instead of loading the image for a moment, just to know its size. Since all Blocks use the same graphic, it shouldn't make a difference, and this way, you won't have to reacquire the offset if you change Block.sprite during runtime!
-Consider assigning a copy of Block's sprite to each instance instead of using the class's Surface. This will take up more processing power, but only momentarily if you use it as a return instead. For instance:
class Block(object):
...
def fade(self):
sprite = Block.sprite.copy()
sprite.set_alpha(self.ticks)
self.ticks -= 1
return sprite
...
while True: # main loop
...
for block in blocks:
screen.blit(block.fade(), block.rect) # Although there are more Pythonic ways of writing this.
Alternatively, you could use screen.blit(sprite, self.rect) in Block.fade() rather than on main and forego the return entirely. Because the alpha is set locally, it won't have to be reset every time fade runs, and Block's unbound sprite can stay fresh!
Anyway, I hope this solves your problem, even if it does not (exactly) answer your question!
You probably want to use threads. These allow you to run multiple things at once. For your code, change it like this:
from threading import Thread
import pygame, sys, time, Blockm
from pygame.locals import *
black = (0,) * 3
white = (255,) * 3
size = w, h = 1400, 800
screen = pygame.display.set_mode(size)
pygame.init()
class Block(object):
sprite = None
def __init__(self, x, y):
if not Block.sprite:
Block.sprite = pygame.image.load("Block.png").convert_alpha()
self.rect = Block.sprite.get_rect(top=y, left=x)
Thread(target=self.fade) # Made this a Thread so it runs separately.
def fade(self):
for tick in range(255, 0, -1):
Block.sprite.set_alpha(tick)
print(tick)
def game():
while True:
# The contents of that while True loop at the end
def main():
Thread(target=Main)
if __name__ == "__main__":
main()
This also adds an entry point to your program, which was lacking. Also, Block.fade wouldn't actually do what you want, as you only set_alpha once. I fixed that with a for loop instead. Also, note that you can now just return to break out of the whole game.
I'm trying to write a python game loop that hopefully takes into account FPS. What is the correct way to call the loop? Some of the possibilities I've considered are below. I'm trying not to use a library like pygame.
1.
while True:
mainLoop()
2.
def mainLoop():
# run some game code
time.sleep(Interval)
mainLoop()
3.
def mainLoop():
# run some game code
threading.timer(Interval, mainLoop).start()
4.
Use sched.scheduler?
If I understood correctly you want to base your game logic on a time delta.
Try getting a time delta between every frame and then have your objects move with respect to that time delta.
import time
while True:
# dt is the time delta in seconds (float).
currentTime = time.time()
dt = currentTime - lastFrameTime
lastFrameTime = currentTime
game_logic(dt)
def game_logic(dt):
# Where speed might be a vector. E.g speed.x = 1 means
# you will move by 1 unit per second on x's direction.
plane.position += speed * dt;
If you also want to limit your frames per second, an easy way would be sleeping the appropriate amount of time after every update.
FPS = 60
while True:
sleepTime = 1./FPS - (currentTime - lastFrameTime)
if sleepTime > 0:
time.sleep(sleepTime)
Be aware thought that this will only work if your hardware is more than fast enough for your game. For more information about game loops check this.
PS) Sorry for the Javaish variable names... Just took a break from some Java coding.