I can't figure out how to move sideways and jump at the same time, only one or the other.
I have tried asyncio and multithreading/multiprocessing and couldn't get it to work. I am all out of ideas and can't find anymore online. I also have another issue where I can jump and if I reach the apex of the jump and hold a or d I can move side to side floating.
class Player():
def __init__(self,health,boosts,height,width):
self.health = health
self.booosts = boosts
self.height = height
self.width = width
def jump():
global gravtest, press
press.remove("'w'")
gravtest = False
y = player[0].ycor()
for i in range (1, 10):
player[0].sety(y+(i*5))
time.sleep(0.05)
#while player[0]
gravtest = True
# def powers(self, boosts):
import turtle as t
import time, random
from pynput.keyboard import Key, Listener
t.ht()
press = []
gravtest = True
wn = t.Screen()
wn.title("Jump Man")
wn.bgcolor("white")
wn.screensize(250, 250)
wn.setup(width=1.0, height=1.0)
player = [t.Turtle(), Player(100, [], 25, 25)]
player[0].speed(0)
player[0].shapesize(0.5)
player[0].shape("square")
player[0].color("black")
player[0].up()
player[0].goto(0, 0)
floor = t.Turtle()
floor.speed(0)
floor.shape("square")
floor.shapesize(100)
floor.color("black")
floor.up()
floor.goto(0, -1150)
def gravity():
global gravtest
if gravtest == True:
grav = 0
while player[0].distance(floor) > 1007:
y = player[0].ycor()
player[0].sety(y + grav)
if grav > -5:
grav -= 1
player[0].sety(y + grav)
gravtest = False
if player[0].distance(floor) < 1045:
player[0].sety(-145)
def show(key):
global press
if not(format(key) in press):
press.append(format(key))
print(key)
def rem(key):
global press
if format(key) in press:
press.remove(format(key))
def move():
global press
while "'a'" in press:
player[0].setx(player[0].xcor()-2)
while "'d'" in press:
player[0].setx(player[0].xcor()+2)
if press == '\'s\'':
print()
gravity()
if "'w'" in press:
jump()
with Listener(on_press = show, on_release = rem) as listener:
while 1:
move()
Your problem with moving and jumping is that you have separate loops for each that try to handle one kind of movement of the movement in one place. That won't work properly if other stuff (like jumping while moving, or moving while falling under gravity) are supposed to be possible.
Instead, you need to have just one main loop. On each run of that loop, you should do one frame worth of movement of whatever kinds is appropriate (e.g. moving horizontally, falling or jumping). This may require some bookkeeping to keep track of how long the vertical acceleration from a jump lasts.
I'd make the main loop something like this:
on_the_ground = True
jumping = False
while True:
horizontal_movement() # this is always possible if the buttons are pressed
if on_the_ground or jumping:
jump() # can start or continue a jump if a button is pressed
else:
gravity() # fall, if you're not grounded or accelerating in a jump
handle_collisions_etc()
time.sleep(1/FRAMERATE)
Note, I've made some assumptions about the game logic you want. If you don't want to be able to move horizontally while in the air (as you can in many platformer games), you'll need to change this a little bit.
The jump function will need to keep track of how long you've been jumping for, since you probably want the player to be limited in how high they can go. I'm not exactly sure I understand the logic of your current code, so I'll leave it up to you to figure out exactly what to track.
A final suggestion is to move some of the global variables into the Player class. Even the turtle object you're using to draw things could be stored as an attribute on the instance you create.
Related
I'm creating a simple game with Python using turtle package.
My aim is to have some balloons on the screen running from right to left and then when one of them is clicked to make it disappear.
What I have going wrong is that when I click one balloon, all of them are disappeared!
Here is my code
1- Main
from turtle import Screen
from balloon import Balloon
import time
screen = Screen()
screen.title('Balloons Nightmare')
screen.setup(width=600, height=600)
screen.bgpic(picname='sky-clouds.gif')
screen.tracer(0)
balloons_manager = Balloon()
current_x = 0
current_y = 0
screen.listen()
screen.onclick(fun=balloons_manager.explode_balloon, btn=1)
game_is_on = True
while game_is_on:
time.sleep(0.1)
screen.update()
balloons_manager.create_balloon()
balloons_manager.move_balloon()
screen.exitonclick()
2- balloon module
import random
from turtle import Turtle
COLORS = ["red", "yellow", "green", "blue", "black"]
MOVEMENT_SPEED = 2
class Balloon:
def __init__(self):
self.all_balloons = []
self.balloon_speed = MOVEMENT_SPEED
self.x = 0
self.y = 0
self.hidden = None
def create_balloon(self):
random_choice = random.randint(1, 9)
if random_choice == 1:
new_balloon = Turtle("circle")
new_balloon.penup()
new_balloon.turtlesize(stretch_wid=2, stretch_len=0.75)
new_balloon.color(random.choice(COLORS))
random_y_cor = random.randint(-50, 280)
new_balloon.goto(320, random_y_cor)
self.hidden = new_balloon.isvisible()
self.all_balloons.append(new_balloon)
def move_balloon(self):
for balloon in self.all_balloons:
balloon.backward(self.balloon_speed)
def explode_balloon(self, x, y):
for balloon in range(len(self.all_balloons)):
print(self.all_balloons[balloon].position())
self.all_balloons[balloon].hideturtle()
# for balloon in self.all_balloons:
# balloon.hideturtle()
I tried so many changes but nothing helped me, example of what I tried so far
getting the current x,y coordinates of the balloon so that on click to hide only the one with these coordinates but didn't work for me or I did something wrong with it
Any hints will be appreciated.
Thank you
I fixed the issue by adding an inner function in create_balloon function
def create_balloon(self):
random_choice = random.randint(1, 9)
if random_choice == 1:
new_balloon = Turtle("circle")
new_balloon.penup()
new_balloon.turtlesize(stretch_wid=2, stretch_len=0.75)
new_balloon.color(random.choice(COLORS))
random_y_cor = random.randint(-50, 280)
new_balloon.goto(320, random_y_cor)
def hide_the_balloon(x, y):
return new_balloon.hideturtle()
new_balloon.onclick(hide_the_balloon)
self.all_balloons.append(new_balloon)
Here's your code triggered by the click handler:
def explode_balloon(self, x, y):
for balloon in range(len(self.all_balloons)):
print(self.all_balloons[balloon].position())
self.all_balloons[balloon].hideturtle()
This loops over all balloons and hides them unconditionally.
You probably want to use an if in there to only conditionally trigger the hiding behavior. Compare the x and y coordinates of the click against the current balloon in the loop. Only hide the balloon if the distance is less than a certain amount (say, the radius of the balloon).
Another approach is to use turtle.onclick to add a handler function that will be trigged when the turtle is clicked.
Related:
How to see if a mouse click is on a turtle in python
Python find closest turtle via mouse click
Why is my Python Turtle program slowing down drastically the longer it runs? (since you're never removing turtles and constantly adding new ones, this is a good thread to take a look at)
So far, my snake game is doing somewhat okay. One issue is that for some reason the "turtle" isn't responding to the keystrokes, and I really don't know why. I tried a lot of different stuff but it was all useless. The main problem is that I am not entirely sure where the main issue is. What I know for sure is that the problem is most likely from my code, but I can't seem to find it. If you could assist me in solving this issue that would great.
import time
from turtle import Screen, Turtle
STARTING_X_POSITIONS = [0, -20, -40]
MOVEMENT_DISTANCE = 20
class Snake:
def __init__(self):
self.segments = []
self.create_snake()
self.head = self.segments[0]
def create_snake(self):
for i in range(3):
new_snake = Turtle('square')
new_snake.color('RoyalBlue')
new_snake.penup()
new_snake.goto(STARTING_X_POSITIONS[i], 0)
self.segments.append(new_snake)
def move(self):
# We Want The Loop To Start At Index (2) And Decrease Itself Till It Reaches Zero (Excluded)
for snake_index in range(len(self.segments) - 1, 0, -1):
x_pos = self.segments[snake_index - 1].xcor()
y_pos = self.segments[snake_index - 1].ycor()
self.segments[snake_index].goto(x_pos, y_pos)
self.segments[0].forward(MOVEMENT_DISTANCE)
def up(self):
self.head.setheading(90)
def down(self):
self.head.setheading(270)
def left(self):
self.head.setheading(180)
def right(self):
self.head.setheading(0)
def setup_screen(screen):
screen.bgcolor('black')
screen.title('Snake Game')
screen.setup(width=600, height=600)
screen.tracer(0)
def start_game(screen, snake):
setup_screen(screen)
game_on = True
while game_on:
screen.update()
time.sleep(0.1)
snake.move()
def control_snake(screen, snake):
screen.listen()
screen.onkey(key='Up', fun=snake.up)
screen.onkey(key='Down', fun=snake.down)
screen.onkey(key='Left', fun=snake.left)
screen.onkey(key='Right', fun=snake.right)
screen.exitonclick()
def main():
screen = Screen()
snake = Snake()
start_game(screen, snake)
control_snake(screen, snake)
if __name__ == '__main__':
main()
This is a good example of the importance of minimizing the code when you debug. Consider the code here:
def start_game(screen, snake):
game_on = True
while game_on: # infinite loop
screen.update()
time.sleep(0.1)
snake.move()
def control_snake(screen, snake):
# add key listeners, the failing behavior
def main():
# ...
start_game(screen, snake)
control_snake(screen, snake)
main calls start_game, but start_game has an infinite while loop in it. game_on is never set to False, and so control_snake will never be reached.
Try adding key listeners before you go into your infinite rendering loop, not after.
Moving control_snake ahead of start_game introduces a new problem, which is that screen.exitonclick() is part of control_snake, but if control_snake is called before start_game, then the screen.exitonclick() blocks and prevents start_game from running. So we need to remove screen.exitonclick().
But there's a better way to trigger repeated events than while/sleep, which is screen.ontimer. This lets you defer control back to your main loop and block on a screen.exitonclick() call. This post shows an example.
Taking a step back, here are a few other tips that address underlying misconceptions and root causes of your bugs.
It's a bit odd that setup_screen is called from start_game. I'd call setup_screen from main to decouple these. I can imagine a case where we want to set up the screen once, but restart the game multiple times, for example, after the snake dies.
In general, I'd worry less about breaking things out into functions until you have the basic code working. Don't write abstractions just because you've heard that functions longer than 5 or 6 lines are bad. The functions should have a clear, singular purpose foremost and avoid odd dependencies.
For example, control_snake should really be called add_snake_controls_then_block_until_exit or something like that, because not only does it add snake controls (it doesn't really "control the snake" exactly, it registers the controls that do so), it also blocks the whole script and runs turtle's internal update loop until the user clicks the window. This might sound pedantic, but if you'd named this function to state exactly what it does, the bug would be much more obvious, with the side benefit of clearer code in general.
Your game loop code:
while game_on:
screen.update()
time.sleep(0.1)
snake.move()
is a bit confusing to follow. The usual rendering sequence is:
update positions
rerender
sleep/defer control until the next update cycle
I suggest the clearer
while game_on:
snake.move() # update positions
screen.update() # render the frame
time.sleep(0.1) # defer/pause until the next tick
Another tip/rule of thumb is to work in small chunks, running your code often. It looks like you wrote a huge amount of code, then ran it for the first time and weren't sure where to begin debugging. If I were writing a snake game, I wouldn't worry about the tail logic until I've set up the head and established that it works, for example.
If you do wind up with a lot of code and a bug in spite of your best efforts, systematically add prints to see where control is reached. If you added a print in control_snake, you'd see it never gets called, which pretty much gives away the problem (and therefore its solution).
Another debugging strategy is to remove code until the problem goes away, then bring back the last chunk to see exactly what the problem was.
All that said, your Snake class seems purposeful and well-written.
Here's my rewrite suggestion:
import turtle
class Snake:
def __init__(self, grid_size, initial_x_positions):
self.grid_size = grid_size
self.create_snake(initial_x_positions)
def create_snake(self, initial_x_positions):
self.segments = []
for x in initial_x_positions:
segment = turtle.Turtle("square")
segment.color("RoyalBlue")
segment.penup()
segment.goto(x, 0)
self.segments.append(segment)
self.head = self.segments[0]
def move(self):
for i in range(len(self.segments) - 1, 0, -1):
x_pos = self.segments[i - 1].xcor()
y_pos = self.segments[i - 1].ycor()
self.segments[i].goto(x_pos, y_pos)
self.head.forward(self.grid_size)
def up(self):
self.head.setheading(90)
def down(self):
self.head.setheading(270)
def left(self):
self.head.setheading(180)
def right(self):
self.head.setheading(0)
def create_screen():
screen = turtle.Screen()
screen.tracer(0)
screen.bgcolor("black")
screen.title("Snake Game")
screen.setup(width=600, height=600)
screen.listen()
return screen
def main():
initial_x_positions = 0, -20, -40
frame_delay_ms = 80
grid_size = 20
screen = create_screen()
snake = Snake(grid_size, initial_x_positions)
screen.onkey(key="Up", fun=snake.up)
screen.onkey(key="Down", fun=snake.down)
screen.onkey(key="Left", fun=snake.left)
screen.onkey(key="Right", fun=snake.right)
def tick():
snake.move()
screen.update()
turtle.ontimer(tick, frame_delay_ms)
tick()
screen.exitonclick()
if __name__ == "__main__":
main()
Since there's no restarting condition or accompanying logic, this will probably need to be refactored to allow for a "game over" screen and resetting the snake or something like that, but at least it's solid and there aren't a lot of premature abstractions to have to reason about.
I got it working as follows
import turtle
STARTING_X_POSITIONS = [0, -20, -40]
MOVEMENT_DISTANCE = 20
frame_delay_ms = 80
class Snake:
def __init__(self, screen):
self.screen = screen
self.control_snake()
self.segments = []
self.create_snake()
self.head = self.segments[0]
def create_snake(self):
for i in range(3):
new_snake = turtle.Turtle('square')
new_snake.color('RoyalBlue')
new_snake.penup()
new_snake.goto(STARTING_X_POSITIONS[i], 0)
self.segments.append(new_snake)
def control_snake(self):
self.screen.onkey(key='Up', fun=self.up)
self.screen.onkey(key='Down', fun=self.down)
self.screen.onkey(key='Left', fun=self.left)
self.screen.onkey(key='Right', fun=self.right)
self.screen.listen()
def move(self):
# We Want The Loop To Start At Index (2) And Decrease Itself Till It Reaches Zero (Excluded)
for snake_index in range(len(self.segments) - 1, 0, -1):
x_pos = self.segments[snake_index - 1].xcor()
y_pos = self.segments[snake_index - 1].ycor()
self.segments[snake_index].goto(x_pos, y_pos)
self.segments[0].forward(MOVEMENT_DISTANCE)
def up(self):
self.head.setheading(90)
def down(self):
self.head.setheading(270)
def left(self):
self.head.setheading(180)
def right(self):
self.head.setheading(0)
class ScreenSetup:
def __init__(self):
self._screen = turtle.Screen()
self.setup_screen()
def setup_screen(self):
self._screen.bgcolor('black')
self._screen.title('Snake Game')
self._screen.setup(width=600, height=600)
self._screen.tracer(0)
#property
def screen(self):
return self._screen
def run_snake(snake, screen):
snake.move()
screen.update()
turtle.ontimer(lambda: run_snake(snake, screen), frame_delay_ms)
def main():
screen = ScreenSetup().screen
snake = Snake(screen)
run_snake(snake, screen)
screen.exitonclick()
if __name__ == '__main__':
main()
I have observed an unexpected behavior of the turtle graphics when turning the turtle by pressing keys on the keyboard. For example turning to the opposite direction resulted in a double line instead of following the already drawn line in the opposite direction or I was getting sometimes sharp and sometimes curved corners when turning or experienced both kinds of unexpected results:
.
I was not able to reproduce this behavior on a very simple example as it occured only if the change of turtle heading was invoked by pressing a key on a keyboard.
The question is, what is the reason for this behavior and how can it be avoided?
By the way: turning the turtle forth and back let it circle on these two lines it has drawn and changing the size of pen does not change the amount of the 'jump' to the side.
Below the code demonstrating this behavior:
import turtle
screen = turtle.Screen()
screen.bgcolor('green')
# Create the Blue Player Turtle and its controls
blue_player = turtle.Turtle()
blue_player.pencolor("blue")
blue_player.pensize(1)
blue_player.is_moving = False
def blue_up() : blue_player.setheading( 90);blue_player.is_moving=True
def blue_down() : blue_player.setheading(270);blue_player.is_moving=True
def blue_left() : blue_player.setheading(180);blue_player.is_moving=True
def blue_right(): blue_player.setheading( 0);blue_player.is_moving=True
screen.onkey(blue_up, "Up")
screen.onkey(blue_down, "Down")
screen.onkey(blue_left, "Left")
screen.onkey(blue_right, "Right")
# Move the Blue Player horizontally forth and back
blue_player.setheading(0)
blue_player.forward(100)
blue_player.setheading(180)
blue_player.forward(50)
# Allow pausing the game by pressing space
game_paused = False
def pause_game():
global game_paused
if game_paused: game_paused = False
else: game_paused = True
screen.onkey(pause_game, "space")
# Establishing a screen.ontimer() loop driving the turtles movement
def gameloop():
if not game_paused:
if blue_player.is_moving: blue_player.forward(1)
# check_collisions()
screen.ontimer(gameloop, 50) # 10ms (0.01s) (1000ms/10ms = 100 FPS)
gameloop()
screen.listen()
screen.mainloop()
Update 2022-06-22 : It seems that this and some other effects have something to do with the delay value in the `.ontimer() function and the distance for a move of the pen.
Update 2022-06-23: below the code I will be using where the described unexpected effect doesn't occur anymore as it differs a bit from the code provided in the answer:
from turtle import Screen, Turtle
screen = Screen()
# Create the Blue Player Turtle and its controls
blue_player = Turtle()
blue_player.pensize(5)
blue_player.moving = False # user-defined property
blue_player.turning = False # user-defined property
def turning(player, angle):
player.turning=True; player.setheading(angle); player.turning=False
if not player.moving: player.moving=True
def blue_up() : turning(blue_player, 90)
def blue_down() : turning(blue_player, 270)
def blue_left() : turning(blue_player, 180)
def blue_right(): turning(blue_player, 0)
screen.onkey(blue_up, "Up")
screen.onkey(blue_down, "Down")
screen.onkey(blue_left, "Left")
screen.onkey(blue_right, "Right")
# Move the Blue Player horizontally forth and back
blue_player.forward(100); blue_player.backward(50)
# Allow pausing the game by pressing space
screen.game_paused = False # user-defined property/attribute
def pause_game():
screen.game_paused = not screen.game_paused
screen.onkey(pause_game, "space")
# Establishing a screen.ontimer() loop driving the turtles movement
def gameloop():
if not screen.game_paused:
if not blue_player.turning:
if blue_player.moving:
blue_player.forward(3)
screen.ontimer(gameloop, 1) # 1 means 1 ms (0.001s)
gameloop()
screen.listen()
screen.mainloop()
In between I have found a way how to provoke the from time to time anyway occurring unexpected behavior also with the new version of the code. If you press the keys very fast in a sequence you get curved turns instead of sharp 90 degree edges.
In other words: the by cdlane proposed approach helps, but doesn't completely solve the problem of getting curved corners. so the question remains open to a final solution or at least to an explanation why a 100% solution is not possible.
Update 2022-06-24:
Finally it turns out that a 100% solution is possible and so simple that I still wonder how it comes that I haven't found it by myself before ( see the hopefully right answer provided by myself ).
The turtle heading animation turns in discrete steps, during which your ontimer() event is kicking in and advancing the turtle. We might be able to use tracer() and update() to fix this but let's try a simpler fix by defining a new state for the turtle, 'turning':
from turtle import Screen, Turtle
def blue_up():
blue_player.status = 'turning'
blue_player.setheading(90)
blue_player.status = 'moving'
def blue_down():
blue_player.status = 'turning'
blue_player.setheading(270)
blue_player.status = 'moving'
def blue_left():
blue_player.status = 'turning'
blue_player.setheading(180)
blue_player.status = 'moving'
def blue_right():
blue_player.status = 'turning'
blue_player.setheading(0)
blue_player.status = 'moving'
# Allow pausing the game by pressing space
game_paused = False
def pause_game():
global game_paused
game_paused = not game_paused
# Establishing a screen.ontimer() loop driving the turtles movement
def gameloop():
if not game_paused:
if blue_player.status == 'moving':
blue_player.forward(1)
# check_collisions()
screen.ontimer(gameloop, 50) # 10ms (0.01s) (1000ms/10ms = 100 FPS)
screen = Screen()
screen.bgcolor('green')
# Create the Blue Player Turtle and its controls
blue_player = Turtle()
blue_player.pencolor('blue')
blue_player.pensize(1)
blue_player.status = 'stationary' # user-defined property
# Move the Blue Player horizontally forth and back
blue_player.forward(100)
blue_player.backward(50)
screen.onkey(blue_up, 'Up')
screen.onkey(blue_down, 'Down')
screen.onkey(blue_left, 'Left')
screen.onkey(blue_right, 'Right')
screen.onkey(pause_game, 'space')
screen.listen()
gameloop()
screen.mainloop()
Check if this solves the problem sufficiently for your purposes.
The by cdlane provided answer explains the basics behind the mechanism of turtle turning. So the reason for the unexpected behavior is clear:
The turtle heading animation turns in discrete steps, during which an .ontimer() event is kicking in and advancing the turtle.
The fact that the by cdlane provided solution proposal doesn't really work in all cases put me on the path to a solution that seem to be resistent against any attempts to provoke the unexpected behavior. The drawn lines are now all perpendicular to each other and the painting pen moves only in the horizontal and vertical direction.
The very simple 'trick' that solves the problem is to check the current value of turtle heading in the loop moving the pen forward, so the only change needed to the code posted in the question is:
if blue_player.heading() in [0.0, 90.0, 180.0, 270.0]:
blue_player.forward(1)
It's so simple that I wonder how it comes I didn't think about it before ...
This has been edited, the original post was about blitting the splash screens
I'm working on a simple game using pygame, the game is almost complete however i'm having some issues with the splash screen. The current issue is that when the game is run, the splash screens blit like they are supposed to, however if the user clicks anywhere on the screen or tries to exit the game, it crashes. Does anybody know a way around this issue, what i am hoping for is that the user would even be able to exit the game or even skip the splash screen whilst they are being displayed.
def main():
'''Here the game is being initialised'''
pygame.init() #Initialising Pygame
pygame.key.set_repeat(1, 20) #Registers event every 20MS if a key is held down
countFont = pygame.font.Font(None,18) #Font being set
statusFont = pygame.font.Font(None,18) #Font being set
screen = pygame.display.set_mode([WINDOW_WIDTH, WINDOW_HEIGHT])
pygame.display.set_caption('Do Not Get Capped')
#Drawable surface
background = pygame.Surface(screen.get_size())
#Used for converting color maps
background = background.convert()
#Splashscreen
#image fades in
for i in range (225):
background.fill((0,0,0))
image = pygame.image.load("splash_screen1.png")
image.set_alpha(i)
logoimage = screen.blit(image,(0,0))
pygame.display.flip()
pygame.time.delay(2000)
for i in range (225):
background.fill((0,0,0))
image = pygame.image.load("splash_screen2.png")
image.set_alpha(i)
logoimage = screen.blit(image,(0,0))
pygame.display.flip()
pygame.time.delay(4000)
'''The main function which is called at the end of this code'''
class Game():
'''This class's purpose is to keep track of the current score'''
def __init__(self):
self.score=0
self.goalNumbers=0
class Gun(pygame.sprite.Sprite):
'''This is the gun that the user controls'''
def __init__(self):
'''This is the class contructor'''
pygame.sprite.Sprite.__init__(self)
self.image=pygame.image.load("turret.png") #Loading the gun's image
self.rect = self.image.get_rect() #Getting the image's Rect
self.rect.x = 240 #Setting the rect's X position
self.rect.y = 630 #Setting the rect's Y position
def moveGun(self,orientation):
'''This function allows the gun to move on the screen.
If the orientation is facing left and the gun is 5 pixels
away from the wall, the gun is moved left & vice-a-versa'''
if orientation=="left" and self.rect.x>5:
self.rect.x-=5
if orientation=="right" and self.rect.x<(480-self.rect.width):
self.rect.x+=5
class Projectile(pygame.sprite.Sprite):
'''This class sets up the projectile/bullets that are controlled
by the user'''
def __init__(self,gun):
pygame.sprite.Sprite.__init__(self)
self.image=pygame.image.load("bullet.png")
self.rect=self.image.get_rect()
'''The code below places the projectile on top of the gun'''
self.rect.x=gun.rect.x+(gun.rect.width/2)-(self.rect.width/2)
self.rect.y=gun.rect.y-gun.rect.height
def updateProjectile(self):
'''This checks if the projectile has reached the top of the screen
if it hasn't it will continue to move up. If it has it will be deleted'''
if self.rect.y>0-self.rect.height:
self.rect.y-=5
else:
self.kill()
class Objects(pygame.sprite.Sprite):
'''This class creates the objects, they are loaded from the computer
and assigned to variables'''
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.obj=random.randint(1,3)
if self.obj==1: imagefile="capped"
if self.obj==2: imagefile="notcapped1"
if self.obj==3: imagefile="notcapped2"
self.image=pygame.image.load(imagefile+".png")
self.rect=self.image.get_rect()
self.rect.y=-0-self.rect.height
self.rect.x=(random.randint(2,44)*10)
def updateProjectile(self,game):
'''This function allows for the objects to move down the screen'''
if self.rect.y<640:
self.rect.y+=7 #This controls the amount of pixels the objects move down thus contrlling the speed
else:
if self.obj==1:
'''Here the code is tracking the users score'''
game.score+=10 #This adds 10 to the game's score
game.goalNumbers+=1
else:
game.score-=50
self.kill()
def shot(self,game):
'''This function updates the score as well as removing the objects when they are hit by a projectile'''
if self.obj == 1:
game.score-=50
else:
game.score+=10
self.kill()
# Create initial object instances
'''Here i am creating objects based on the classes i created'''
game=Game()
gun=Gun()
sprites=pygame.sprite.Group()
sprites.add(gun)
obstacles=pygame.sprite.Group()
projectiles=pygame.sprite.Group()
'''This variable will be used to control when to come out of the loop, i will state when this happens belows'''
finish=False
clock=pygame.time.Clock() #Initialising the clock
tick=0
'''This is the start of the main while loop, this loop will continue
until the variable 'finish' becomes false'''
while finish == False:
clock.tick(30) #Loop will run 30 times a second
tick+=1
screen.fill(bColour)
'''Here the main events are being run'''
for event in pygame.event.get():
if event.type==pygame.QUIT:
'''If the user clicks the exit button, the finish variable is made True,
this means that rather than exiting the game, the user's score is displayed after
which the game closes'''
finish = True
if event.type==pygame.KEYDOWN:
'''Here the script is checking for KEYDOWN events, these are events triggered
when the user presses on a keyboard key. In this case events are triggered when the left, right
and space keys are pressed.'''
if event.key==pygame.K_LEFT:
gun.moveGun("left") #If this is activated the 'orientation' changes to 'left' which shunts the gun 5 pixels to the left
if event.key==pygame.K_RIGHT:
gun.moveGun("right") #'Orientation' changes to 'right' which shunts the gun 5 pixels to the right
if event.key==pygame.K_SPACE:
'''This triggers the projectiles function'''
projectile=Projectile(gun)
projectiles.add(projectile)
'''This controls the projectiles and objects moving around the window'''
for projectile in projectiles:
projectile.updateProjectile()
for obstacle in obstacles:
obstacle.updateProjectile(game)
if tick>60:
'''This controls at what rate the objects fall which is now once every two
seconds, this is because the loop runs in 30 second intervals and the
clock is ticking at 60 seconds'''
if len(obstacles)<10:
obstacle=Objects()
obstacles.add(obstacle)
tick=0
collisions=pygame.sprite.groupcollide(obstacles,projectiles,False,True)
'''Here the script is checking whether the objects are hit by a projectile
if they are, the 'shot' function is triggered'''
if collisions:
for obstacle in collisions:
obstacle.shot(game)
'''This block of code constantly updates the player's scores'''
scoreText=countFont.render('Your current score is:'+str(game.score),True,(255,255,255),bColour)
screen.blit(scoreText,(0,620))
statusText=statusFont.render('You have '+str(10-game.goalNumbers)+' more tries',True,(255,255,255),bColour)
screen.blit(statusText,(0,10))
'''This code below updates and blits the objects to the screen'''
sprites.draw(screen)
projectiles.draw(screen)
obstacles.draw(screen)
pygame.display.flip()
if game.goalNumbers>=10:
'''This if statement is checking whether 'obj1' has touched the floor 10 times
and if it did, the finish variable is made true thus ending the game'''
finish=True
'''This is the last piece of code visible to the user,
what happens here is that the final message showing the final score is shown'''
scoreCountHolder=pygame.image.load("scoreframe.png")
scoreCountHolder.convert_alpha()
left=90
top=250
screen.blit(scoreCountHolder,(left,top))
countFont=pygame.font.Font(None,52)
statusText=countFont.render('Your Score:'+str(game.score),True,bColour,(255,255,255))
screen.blit(statusText,(105,300))
pygame.display.flip()
while True:
'''This waits for the user to quit the game'''
for event in pygame.event.get():
if event.type==pygame.QUIT:
sys.exit()
if __name__ == '__main__':
'''The main function being called'''
main()
When you have a pygame application, you take all events from the input queue.
The operating system, adds new events onto the queue, while your application removes them.
Since you do not remove the events, the operating system thinks that your application froze.
To fix this, you should call pygame.event.pump()
From the pygame docs:
For each frame of your game, you will need to make some sort of call
to the event queue. This ensures your program can internally interact
with the rest of the operating system. If you are not using other
event functions in your game, you should call pygame.event.pump() to
allow pygame to handle internal actions.
This function is not necessary if your program is consistently
processing events on the queue through the other pygame.eventpygame
module for interacting with events and queues functions.
There are important things that must be dealt with internally in the
event queue. The main window may need to be repainted or respond to
the system. If you fail to make a call to the event queue for too
long, the system may decide your program has locked up.
What happens is that you put a frame, Pygame then expects you to take the events that happened in the mean time out of its event queue. If you don't the OS thinks, that application got stuck!
You need to handle the events while you show the splash screen, if you want to allow the user to skip the flash screen.
Here's a way around that problem, it allows you to add specific event handling for splash screen:
class SplashScreen(object):
def __init__(self):
super().__init__()
# Use numbers for ordering, make them continuous.
self.image_dict = {
1: "splash_screen1.png",
2: "splash_screen2.png",
}
# Current image we are on!
self.current_index = min(self.image_dict)
# Last image
self.end_index = max(self.image_dict)
# Minimum alpha value
self.alpha_min = 0
# Maximum alpha value
self.alpha_max = 225
# Current alpha value
self.alpha = self.alpha_min
self.show = True
# I leave it to you to figure out this function.
def get_image(self):
image_name = self.image_dict[self.current_index]
image = pygame.image.load(image_name)
image.set_alpha(self.alpha)
self.alpha += 1
if self.alpha > self.alpha_max:
self.alpha = self.alpha_min
self.current_index += 1
if self.current_index == self.end_index:
self.show = False
return image
splash_screen = SplashScreen()
while True:
# The ever-green event handling loop!
for event in pygame.event.get():
...
if splash_screen.show:
# Splash screen stuff, one frame at a time
background.fill((0, 0, 0))
img = splash_screen.get_image()
screen.blit(img, (0, 0))
else:
# Game stuff, no need to call display.flip here, called at the end.
...
# Called at the end after frame is ready!
pygame.display.flip()
Left as an exercise
You should load and add the loaded images in the image_dict in __init__. It's not a good idea to keep reloading images from disk each time you blit them.
I have Turtle commands in my code as you see below. I would like to put a timer on these commands inside an if statement. My current code just works like so:
'# WHEN USER PRESSES SPACE, LIGHT TURNS GREEN
'# WHEN LIGHT IS GREEN ARROW MOVES 50 PIXELS
player1.pendown()
player1.forward(50)
player2.pendown()
player2.forward(50)
The arrow really only moves once every time I press the space key.
I would like to turn that into a timer so the arrow would move every 60 milliseconds until the user presses the space key again.
I tried using an wn.ontimer but I keep on messing up something. Below is how the code looks now:
def advance_state_machine():
global state_num
if state_num == 0:
tess.forward(70)
tess.fillcolor("red")
state_num = 1
else:
tess.back(70)
tess.fillcolor("green")
state_num = 0
player1.pendown()
player1.forward(50)
player2.pendown()
player2.forward(50)
wn.onkey(advance_state_machine, "space")
wn.listen()
wn.mainloop()
Your description of your problem is inconsistent and your code is out of sync with your description. Here's what I believe you stated you wanted your code to do:
The turtle moves forward 1 step every 60 milliseconds and alternates between red/forward and green/backward whenever the space bar is pressed.
I believe this code implements that, a key event is used to detect the space bar and a timer event is used to keep the turtle in motion:
from turtle import Turtle, Screen, mainloop
def advance_state_machine():
global state_num
wn.onkey(None, 'space') # avoid overlapping events
if state_num > 0:
tess.fillcolor('green')
else:
tess.fillcolor('red')
state_num = -state_num
wn.onkey(advance_state_machine, 'space')
def advance_tess():
tess.forward(state_num)
wn.ontimer(advance_tess, 60)
wn = Screen()
tess = Turtle()
tess.fillcolor('red')
state_num = 1
wn.onkey(advance_state_machine, 'space')
wn.ontimer(advance_tess, 60)
wn.listen()
mainloop()
Make sure to click on the window to make it active before trying the space bar. Left unattended, the turtle will eventually go off screen.