I am trying to create a simple game with turtle:
A turtle controlled by the user can shoot bullets when space is pressed
An enemy turtle is created at a random position
If the enemy is hit by the bullet, the enemy is destroyed and moves to a new position.
If the bullet leaves the screen, it disappears.
While the bullet is moving, the player should still be able to move.
If the enemy is not destroyed in 20 seconds, the player looses.
Therefore, I need some events to be controlled with the keyboard and others that get triggered after a certain time. I cannot think about a way of doing this without a loop inside which I check the distance between the bullet and the enemy, but if I do it like that, I cannot control the main turtle during the loop.
import turtle as trtl
from random import randint
import time
class Game():
def __init__(self):
self.scr = trtl.Screen()
self.scr.update()
self.player = trtl.Turtle()
self.player.shape('turtle')
self.player.penup()
trtl.onkeypress(self.forward,'w')
trtl.onkeypress(self.backwards,'s')
trtl.onkeypress(self.left,'a')
trtl.onkeypress(self.right,'d')
trtl.onkeypress(self.shoot,'space')
trtl.listen()
self.enemy = trtl.Turtle()
self.enemy.shape('square')
self.enemy.penup()
self.enemy.speed(0)
self.move_enemy()
self.bullet = trtl.Turtle()
self.bullet.penup()
self.bullet.hideturtle()
self.bulletShot = False
def forward(self):
self.player.forward(5)
def backwards(self):
self.player.back(5)
def left(self):
self.player.left(6)
def right(self):
self.player.right(6)
def shoot(self):
if self.bulletShot == False:
self.bullet.speed(0)
self.bullet.goto(self.player.pos())
self.bullet.seth(self.player.heading())
self.bullet.showturtle()
self.bulletShot = True
def move_enemy(self):
x = randint(-300,300)
y = randint(-300,300)
self.enemy.hideturtle()
self.enemy.goto(x,y)
self.enemy.showturtle()
def play(self):
startTime = time.time()
print(time.time() - startTime)
while time.time() - startTime < 20:
if self.bulletShot:
self.bullet.forward(1)
collision = self.bullet.distance(self.enemy.pos()) < 10
isIn = (self.bullet.pos()[0] <= 300 and
self.bullet.pos()[0] >= -300 and
self.bullet.pos()[1] <= 300 and
self.bullet.pos()[1] >= -300)
if not(isIn):
self.bullet.hideturtle()
self.bulletShot = False
elif collision:
self.bullet.hideturtle()
self.bulletShot = False
self.move_enemy()
startTime = time.time()
self.player.write('You loose')
self.scr.exitonclick()
game = Game()
game.play()
I tried a simpler version in which one turtle moves automatically in a loop and another turtle is controlled with the keyboard, and it works well.
import turtle as trtl
def up():
jane.sety(jane.pos()[1] + 10)
def down():
jane.sety(jane.pos()[1] - 10)
scr = trtl.Screen()
scr.update()
bob = trtl.Turtle()
bob.penup()
bob.seth(180)
bob.setx(300)
bob.speed(1)
jane = trtl.Turtle()
jane.penup()
trtl.onkeypress(up,'w')
trtl.onkeypress(down,'s')
trtl.listen()
while True:
if bob.pos()[0] > -300:
bob.forward(10)
else:
break
scr.exitonclick()
Is there a way of fixing this with turtle?
Let's redesign the game to work with turtle's event system by using a timer event to control the action while still using your timer to limit play time:
from turtle import Screen, Turtle
from random import randint
import time
class Game():
def __init__(self):
self.startTime = -1
self.screen = Screen()
self.screen.tracer(False)
self.player = Turtle()
self.player.shape('turtle')
self.player.penup()
self.enemy = Turtle()
self.enemy.shape('square')
self.enemy.penup()
self.move_enemy()
self.bullet = Turtle()
self.bullet.hideturtle()
self.bullet.penup()
self.bulletShot = False
self.screen.onkeypress(self.forward, 'w')
self.screen.onkeypress(self.backwards, 's')
self.screen.onkeypress(self.left, 'a')
self.screen.onkeypress(self.right, 'd')
self.screen.onkeypress(self.shoot, 'space')
self.screen.listen()
def forward(self):
self.player.forward(5)
self.screen.update()
def backwards(self):
self.player.back(5)
self.screen.update()
def left(self):
self.player.left(6)
self.screen.update()
def right(self):
self.player.right(6)
self.screen.update()
def shoot(self):
if not self.bulletShot:
self.bullet.setposition(self.player.position())
self.bullet.setheading(self.player.heading())
self.bullet.showturtle()
self.bulletShot = True
self.screen.update()
def move_enemy(self):
x = randint(-300, 300)
y = randint(-300, 300)
self.enemy.goto(x, y)
self.screen.update()
def play(self):
if self.startTime == -1:
self.startTime = time.time()
if self.bulletShot:
self.bullet.forward(1)
x, y = self.bullet.position()
if not(-300 <= x <= 300 and -300 <= y <= 300):
self.bullet.hideturtle()
self.bulletShot = False
elif self.bullet.distance(self.enemy.pos()) < 10:
self.bullet.hideturtle()
self.bulletShot = False
self.move_enemy()
self.startTime = time.time()
self.screen.update()
if time.time() - self.startTime > 20:
self.player.write('You loose!')
self.screen.update()
else:
self.screen.ontimer(self.play, 10)
screen = Screen()
game = Game()
game.play()
screen.mainloop()
Still bit crude but should be playable. You had a self.scr.update() call in your original code but without an initial call to tracer(), it does nothing. Here we're using tracer() and update() to speed up and smooth out the motion by manually controlling all screen updates.
I managed to solve it without changing much the code and without making the function play() recursive. The problem was that if the if inside de while needs an else, otherwise, the key press is not recorded. So I gave it something to do and now it works as I want.
while time.time() - startTime < 20:
if self.bulletShot:
self.bullet.forward(3)
...
else:
self.scr.update()
Related
In python
import time
from turtle import Turtle, Screen
# screen height and width
height = 600
width = 600
# snake initial positinn
x_coordinate = 0
y_coordinate = 0
game_on = True
screen = Screen()
screen.bgcolor("black")
screen.setup(height=height, width=width)
screen.title("Snake Game")
full_snake = []
screen.tracer(0)
# create a snake 3X3
for _ in range(3):
snake = Turtle("square")
snake.color("white")
snake.penup()
snake.goto(x_coordinate, y_coordinate)
x_coordinate -= 20
full_snake.append(snake)
snake_head = full_snake[0]
# function to operate the snake
def up():
if snake_head.heading() != 270:
snake_head.setheading(90)
def down():
if snake_head.heading() != 90:
snake_head.setheading(270)
def right():
if snake_head.heading() != 180:
snake_head.setheading(0)
def left():
if snake_head.heading() != 0:
snake_head.setheading(180)
screen.listen()
screen.onkey(up, "w")
screen.onkey(down, "s")
screen.onkey(right, "d")
screen.onkey(left, "a")
# function for snakes part to attached
def attached():
for i in range(len(full_snake) - 1, 0, -1):
new_x = full_snake[i - 1].xcor()
new_y = full_snake[i - 1].ycor()
full_snake[i].goto(new_x, new_y)
snake_head.fd(20)
# to move the snake
while game_on:
snake_head.speed(1)
screen.update()
time.sleep(0.1)
for snake in full_snake:
attached()
screen.exitonclick()
in this code snake speed is fast, I want to slow down to normal where I can placed speed() method or any method
Turtle module from python
expected output : speed of snake to be normal without detaching other two blocks
.................................................................................................................................................................................................................................................................................................................................
You can try the ontimer() method instead to control the snake movement. I modified your code a bit. You can check if the snake hits the wall too and set game_on to false in the move_snake function.
import time
from turtle import Turtle, Screen
# screen height and width
height = 600
width = 600
# snake initial position
x_coordinate = 0
y_coordinate = 0
game_on = True
screen = Screen()
screen.bgcolor("black")
screen.setup(height=height, width=width)
screen.title("Snake Game")
full_snake = []
screen.tracer(0)
# create a snake 3X3
for _ in range(3):
snake = Turtle("square")
snake.color("white")
snake.penup()
snake.goto(x_coordinate, y_coordinate)
x_coordinate -= 20
full_snake.append(snake)
snake_head = full_snake[0]
# function to operate the snake
def up():
if snake_head.heading() != 270:
snake_head.setheading(90)
def down():
if snake_head.heading() != 90:
snake_head.setheading(270)
def right():
if snake_head.heading() != 180:
snake_head.setheading(0)
def left():
if snake_head.heading() != 0:
snake_head.setheading(180)
screen.listen()
screen.onkey(up, "w")
screen.onkey(down, "s")
screen.onkey(right, "d")
screen.onkey(left, "a")
# function for snake parts to attached
def attached():
for i in range(len(full_snake) - 1, 0, -1):
new_x = full_snake[i - 1].xcor()
new_y = full_snake[i - 1].ycor()
full_snake[i].goto(new_x, new_y)
snake_head.fd(20)
# New function to move the snake
def move_snake():
attached()
screen.update()
# updated the height and width variables to fit the actual screen size (useful when screen is resized)
height = screen.window_height()
width = screen.window_width()
# check if the snake head hits the wall (19 or 20 pixels away looks fine but can be ajusted) then set game_on to false and stop the function execution
if snake_head.xcor() >= width/2 - 20 or snake_head.xcor() <= -width/2 + 19 or snake_head.ycor() >= height/2 - 19 or snake_head.ycor() <= -height/2 + 20:
game_on = False
return
# timer to recursively call the move_snake function every 500 milliseconds
screen.ontimer(move_snake, 500) # adapt to your desired speed (the lower the faster)
# start moving the snake
move_snake()
screen.exitonclick()
I'm having an issue with my snakegame on the collision detection side. What I wrote originally to do so was this:
snakecollisionchecklist = len(snake.snake_coord_list)
snakecollisionchecklistset = len(set(snake.snake_coord_list))
if snakecollisionchecklist != snakecollisionchecklistset:
return
The idea being that if any segment position in the snake was equal to another segment position it would abort the program. The problem I found was that the .position() function I was using to find the position of each segment returned a 10 decimal float value, and for whatever reason this wasn't consistent even if the snake was sharing the same place. Converting it to int doesnt work either as that sometimes will make 60 59 etc. if the variance is high/low. To deal with this I put this together, but I feel like this isn't the most efficient way to handle this:
values_to_convert = segments.position()
xvaluestoconvert = round(values_to_convert[0],2)
yvaluestoconvert = round(values_to_convert[1],2)
self.snake_coord_list[segloc] = (xvaluestoconvert, yvaluestoconvert)
This just takes the individual pieces of the position() and forces it to be rounded. I also had a similar issue with trying to make food not spawn inside the snake, which ended up looking like this:
newloc= positionlist[0]
while newloc in positionlist:
randomx = round((random.randint(-7,7) * 20))
randomy = round((random.randint(-7,7) * 20))
newloc = [randomx, randomy]
self.goto(float(randomx),float(randomy))
print(f"{randomx} and {randomy}")
But then I still get overlap.
Is there a better way to do this? If you're curious about full code it's here:
gamesetup.py
import turtle as t
class FullSnake:
def __init__(self) -> None:
self.snake_part_list = []
self.snake_coord_list = [(-40,0),(-60,0), (-80,0)]
self.moment_prior_coord_list = [(-40,0),(-60,0), (-80,0)]
for nums in range(3):
new_segment = t.Turtle("circle")
new_segment.penup()
new_segment.color("white")
self.snake_part_list.append(new_segment)
new_segment.goto(self.snake_coord_list[nums])
self.snake_head = self.snake_part_list[0]
self.headingverification = 0
def add_segment(self,):
"""This adds a segment to the snake. the segment added should be initialized to be add the end of the list."""
new_segment = t.Turtle("circle")
new_segment.penup()
new_segment.color("white")
self.snake_part_list.append(new_segment)
#self.snake_coord_list.append((self.snake_part_list[0].xcor(),self.snake_part_list[0].ycor()))
#self.snake_coord_list.append(new_segment)
# current_final_seg = self.snake_part_list[-2]
current_final_seg_pos = self.moment_prior_coord_list[-1]
#self.move_snake()
new_segment.goto(current_final_seg_pos[0],current_final_seg_pos[1])
self.snake_coord_list.append(current_final_seg_pos)
def right(self):
if self.headingverification != 180:
self.snake_head.setheading(0)
# time.sleep(0.031)
# self.move_snake()
#ime.sleep(0.05)
def up(self):
if self.headingverification != 270:
self.snake_head.setheading(90)
# time.sleep(0.031)
#self.move_snake()
def left(self):
if self.headingverification != 0:
self.snake_head.setheading(180)
# time.sleep(0.031)
# self.move_snake()
def down(self):
if self.headingverification != 90:
self.snake_head.setheading(270)
#time.sleep(0.031)
# self.move_snake()
def move_snake(self):
"""moves snake. snake moves forward 20 units, and prior units get updated"""
self.moment_prior_coord_list = list(self.snake_coord_list)
for seg_num in range(len(self.snake_part_list)-1,0,-1):
new_x = round(self.snake_part_list[seg_num-1].xcor(),2)
new_y = round(self.snake_part_list[seg_num-1].ycor(),2)
self.snake_part_list[seg_num].goto(new_x, new_y)
self.snake_head.forward(20)
#print(self.snake_head.position())
for segments in self.snake_part_list:
segloc = self.snake_part_list.index(segments)
#for some reason segments.position() a varied float, so this just forces it to be samesies
values_to_convert = segments.position()
xvaluestoconvert = round(values_to_convert[0],2)
yvaluestoconvert = round(values_to_convert[1],2)
self.snake_coord_list[segloc] = (xvaluestoconvert, yvaluestoconvert)
print(self.snake_coord_list)
main.py:
import turtle as t
from gamesetup import FullSnake
import time
import food
import score_board as sb
screen = t.Screen()
screen.setup(food.screensize[0],food.screensize[1])
screen.bgcolor("Black")
screen.title("My Snake Game")
screen.tracer(0)
snakefood = food.Food()
snake = FullSnake()
scoreboard = sb.ScoreBoard()
screen.listen()
screen.onkey(snake.up,"Up")
screen.onkey(snake.down,"Down")
screen.onkey(snake.right,"Right")
screen.onkey(snake.left,"Left")
#game_is_on = True
#while game_is_on:
def snakemovefct():
snake.move_snake()
screen.update()
#what happens when you hit food, add to length of snake, increase score and move pellet to place that snake isnt
if snake.snake_head.distance(snakefood) <5:
snake.add_segment()
snakefood.refresh(snake.snake_coord_list)
scoreboard.score_event()
screen.update()
time.sleep(0.1)
#set gameover if you hit boundary
if snake.snake_head.xcor() > 150 or snake.snake_head.xcor() < -150 or snake.snake_head.ycor() > 150 or snake.snake_head.ycor() < -150:
scoreboard.game_over()
return
#check collision
snakecollisionchecklist = len(snake.snake_coord_list)
snakecollisionchecklistset = len(set(snake.snake_coord_list))
if snakecollisionchecklist != snakecollisionchecklistset:
scoreboard.game_over()
return
#this makes sure you cant press up and left when moving right to go left
snake.headingverification = snake.snake_head.heading()
#keep snake moving in loop, if no recursion it only moves once
screen.ontimer(snakemovefct,150)
screen.update()
screen.ontimer(snakemovefct,150)
screen.mainloop()
food.py
from turtle import Turtle
import random
screensize = (340, 340)
class Food(Turtle):
def __init__(self) -> None:
super().__init__()
self.shape("square")
self.penup()
#self.shapesize(stretch_len=0.5, stretch_wid=0.5)
self.color("red")
self.speed("fastest")
self.refresh([(20,0),(0,0), (-20,0)])
def refresh(self, positionlist):
newloc= positionlist[0]
while newloc in positionlist:
randomx = "{:.2f}".format(random.randint(-7,7) * 20)
randomy = "{:.2f}".format(random.randint(-7,7) * 20)
newloc = [randomx, randomy]
self.goto(float(randomx),float(randomy))
print(f"{randomx} and {randomy}")
scoreboard.py
import turtle as t
class ScoreBoard(t.Turtle):
def __init__(self) -> None:
super().__init__()
self.hideturtle()
self.penup()
self.pencolor("white")
self.speed("fastest")
self.score = 0
self.goto(0,120)
self.write(f"Current score: {self.score}", False, align="center")
def score_event(self):
self.score +=1
self.clear()
self.write(f"Current score: {self.score}", False, align="center")
def game_over(self):
self.goto(0,0)
self.write("GAME OVER", False, align="center")
im working on a game in python arcade, where the player runs around, shoots zombies, etc. very basic game. just recently, i started implementing the spawn over time part, but i get an error that just makes no sense:
AttributeError: 'MyGame' object has no attribute 'total_time'
it makes no sense because i have stated self.total_time in MyGame.
how do i fix this?
import arcade
import random
import math
import arcade.gui
import time
import timeit
SPRITE_SCALING = 0.35
SPRITE_SCALING_LASER = 0.8
SCREEN_WIDTH = 1280
SCREEN_HEIGHT = 720
SCREEN_TITLE = "zombier shooter"
ENEMY_COUNT = 20
BULLET_SPEED = 30
MOVEMENT_SPEED = 5
SPRITE_SPEED = 1
INDICATOR_BAR_OFFSET = 32
ENEMY_ATTACK_COOLDOWN = 1
PLAYER_HEALTH = 5
SCENE_MENU = 'SCENE_MENU'
SCENE_GAME = 'SCENE_GAME'
class QuitButton(arcade.gui.UIFlatButton):
def on_click(self, event: arcade.gui.UIOnClickEvent):
arcade.exit()
class Player(arcade.Sprite):
def update(self):
""" moves the player """
# move player.
self.center_x += self.change_x
self.center_y += self.change_y
# check for out of bounds
if self.left < 0:
self.left = 0
elif self.right > SCREEN_WIDTH - 1:
self.right = SCREEN_WIDTH - 1
if self.bottom < 0:
self.bottom = 0
elif self.top > SCREEN_HEIGHT - 1:
self.top = SCREEN_HEIGHT - 1
class Enemy(arcade.Sprite):
"""
This class represents the enemies on our screen.
"""
def follow_sprite(self, player_sprite):
"""
This function will move the current sprite towards whatever
other sprite is specified as a parameter.
"""
if self.center_y < player_sprite.center_y:
self.center_y += min(SPRITE_SPEED, player_sprite.center_y - self.center_y)
elif self.center_y > player_sprite.center_y:
self.center_y -= min(SPRITE_SPEED, self.center_y - player_sprite.center_y)
if self.center_x < player_sprite.center_x:
self.center_x += min(SPRITE_SPEED, player_sprite.center_x - self.center_x)
elif self.center_x > player_sprite.center_x:
self.center_x -= min(SPRITE_SPEED, self.center_x - player_sprite.center_x)
class MyGame(arcade.Window):
"""
main game class
"""
def __init__(self, width, height, title):
"""
initialises stuff
"""
# call the parent class initializer
super().__init__(width, height, title)
self.scene = SCENE_MENU
# variables that will hold sprite lists
self.player_list = None
# set up the player info
self.player_sprite = None
# track the current state of what key is pressed
self.left_pressed = False
self.right_pressed = False
self.up_pressed = False
self.down_pressed = False
# --- Required for all code that uses UI element,
# a UIManager to handle the UI.
self.manager = arcade.gui.UIManager()
self.manager.enable()
# Set background color
arcade.set_background_color(arcade.color.DARK_BLUE_GRAY)
# Create a vertical BoxGroup to align buttons
self.v_box = arcade.gui.UIBoxLayout()
# Create the buttons
start_button = arcade.gui.UIFlatButton(text="Start Game", width=200)
self.v_box.add(start_button.with_space_around(bottom=20))
settings_button = arcade.gui.UIFlatButton(text="Settings", width=200)
self.v_box.add(settings_button.with_space_around(bottom=20))
# Again, method 1. Use a child class to handle events.
quit_button = QuitButton(text="Quit", width=200)
self.v_box.add(quit_button)
# --- Method 2 for handling click events,
# assign self.on_click_start as callback
start_button.on_click = self.on_click_start
# --- Method 3 for handling click events,
# use a decorator to handle on_click events
#settings_button.event("on_click")
def on_click_settings(event):
print("Settings:", event)
# Create a widget to hold the v_box widget, that will center the buttons
self.manager.add(
arcade.gui.UIAnchorWidget(
anchor_x="center_x",
anchor_y="center_y",
child=self.v_box)
)
def setup(self):
""" Set up the game and initialize the variables. """
# sprite lists
self.player_list = arcade.SpriteList()
self.enemy_list = arcade.SpriteList()
self.bullet_list = arcade.SpriteList()
#setup timer
self.total_time = 0.0
# setup score
self.score = 0
self.score_text = None
# setup health info
self.health = 5
self.health_text = None
self.dead = None
# set up the player
self.player_sprite = Player(":resources:images/animated_characters/female_person/femalePerson_idle.png",
SPRITE_SCALING)
self.player_sprite.center_x = 50
self.player_sprite.center_y = 50
self.player_list.append(self.player_sprite)
def on_draw(self):
""" render the screen. """
# clear the screen
self.clear()
if self.scene == SCENE_MENU:
self.manager.draw()
elif self.scene == SCENE_GAME:
# draw all the sprites.
self.player_list.draw()
self.enemy_list.draw()
self.bullet_list.draw()
# put score text on the screen
output = f"Score: {self.score}"
arcade.draw_text(output, 10, 20, arcade.color.WHITE, 14)
# put helth text on the screen
output = f"Health: {self.health}"
arcade.draw_text(output, 10, 40, arcade.color.WHITE, 14)
if self.health <= 0:
self.player_sprite.remove_from_sprite_lists()
# put u died text on the screen
output = f"YOU DIED"
arcade.draw_text(output, 500, 400, arcade.color.RED, 50)
output = f"Click to Exit"
arcade.draw_text(output, 550, 300, arcade.color.BLACK, 30)
def on_click_start(self, event):
self.setup()
self.scene = SCENE_GAME
self.manager.disable()
print("Start:", event)
def on_mouse_press(self, x, y, button, modifiers):
""" Called whenever the mouse button is clicked. """
if self.health <= 0:
exit()
# create a bullet
bullet = arcade.Sprite(":resources:images/space_shooter/laserBlue01.png", SPRITE_SCALING_LASER)
# Position the bullet at the player's current location
start_x = self.player_sprite.center_x
start_y = self.player_sprite.center_y
bullet.center_x = start_x
bullet.center_y = start_y
# Get from the mouse the destination location for the bullet
# IMPORTANT! If you have a scrolling screen, you will also need
# to add in self.view_bottom and self.view_left.
dest_x = x
dest_y = y
# Do math to calculate how to get the bullet to the destination.
# Calculation the angle in radians between the start points
# and end points. This is the angle the bullet will travel.
x_diff = dest_x - start_x
y_diff = dest_y - start_y
angle = math.atan2(y_diff, x_diff)
# Angle the bullet sprite so it doesn't look like it is flying
# sideways.
bullet.angle = math.degrees(angle)
print(f"Bullet angle: {bullet.angle:.2f}")
# Taking into account the angle, calculate our change_x
# and change_y. Velocity is how fast the bullet travels.
bullet.change_x = math.cos(angle) * BULLET_SPEED
bullet.change_y = math.sin(angle) * BULLET_SPEED
# Add the bullet to the appropriate lists
self.bullet_list.append(bullet)
def update_player_speed(self):
# calculate speed based on the keys pressed
self.player_sprite.change_x = 0
self.player_sprite.change_y = 0
if self.up_pressed and not self.down_pressed:
self.player_sprite.change_y = MOVEMENT_SPEED
elif self.down_pressed and not self.up_pressed:
self.player_sprite.change_y = -MOVEMENT_SPEED
if self.left_pressed and not self.right_pressed:
self.player_sprite.change_x = -MOVEMENT_SPEED
elif self.right_pressed and not self.left_pressed:
self.player_sprite.change_x = MOVEMENT_SPEED
def on_update(self, delta_time):
""" updates values n stuff """
if self.scene == SCENE_GAME:
# call update to move the sprite
self.player_list.update()
# Call update on all sprites
self.bullet_list.update()
# go through each bullet
for bullet in self.bullet_list:
# check each bullet to see if it hit a zombie
hit_list = arcade.check_for_collision_with_list(bullet, self.enemy_list)
# if it did, remove the bullet
if len(hit_list) > 0:
bullet.remove_from_sprite_lists()
# for each enemy we hit with a bullet, remove enemy and add to the score
for enemy in hit_list:
enemy.remove_from_sprite_lists()
self.score += 1
# if bullet goes off screen, then remove it
if bullet.bottom > self.width or bullet.top < 0 or bullet.right < 0 or bullet.left > self.width:
bullet.remove_from_sprite_lists()
for enemy in self.enemy_list:
Enemy.follow_sprite(enemy, self.player_sprite)
# create a list of all sprites that had a collision with the player.
hit_list = arcade.check_for_collision_with_list(self.player_sprite, self.enemy_list)
# go through each sprite, if it got hit, then remove the sprite and lower score and health
for enemy in hit_list:
enemy.remove_from_sprite_lists()
self.score -= 1
self.health -= 1
# Accumulate the total time
self.total_time += delta_time
# Calculate minutes
minutes = int(self.total_time) // 60
# Calculate seconds by using a modulus (remainder)
seconds = int(self.total_time) % 60
# Calculate 100s of a second
seconds_100s = int((self.total_time - seconds) * 100)
if self.total_time > 5:
for i in range(5):
# enemy texture
enemy = arcade.Sprite(":resources:images/animated_characters/zombie/zombie_idle.png", SPRITE_SCALING)
enemy.center_x = random.randrange(SCREEN_WIDTH)
enemy.center_y = random.randrange(SCREEN_HEIGHT)
self.enemy_list.append(enemy)
self.total_time = 0.0
def on_key_press(self, key, modifiers):
"""called when user presses a key. """
if key == arcade.key.UP:
self.up_pressed = True
self.update_player_speed()
elif key == arcade.key.DOWN:
self.down_pressed = True
self.update_player_speed()
elif key == arcade.key.LEFT:
self.left_pressed = True
self.update_player_speed()
elif key == arcade.key.RIGHT:
self.right_pressed = True
self.update_player_speed()
def on_key_release(self, key, modifiers):
"""called when user releases a key. """
if key == arcade.key.UP:
self.up_pressed = False
self.update_player_speed()
elif key == arcade.key.DOWN:
self.down_pressed = False
self.update_player_speed()
elif key == arcade.key.LEFT:
self.left_pressed = False
self.update_player_speed()
elif key == arcade.key.RIGHT:
self.right_pressed = False
self.update_player_speed()
def main():
""" Main function """
MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
arcade.run()
if __name__ == "__main__":
main()
You defined self.total_time in setup() and not in __init__(), which should be a red-flag, because it could happen that self.total_time is actually accessed before setup() is called. And exactly this happened.
The error appears in on_update() when you try to run self.total_time += delta_time - in order for this line to work, self.total_time has to be initialised before - but setup() wasn't executed before, so self.total_time 'does not exist' yet.
So you can fix your code by moving the line self.total_time = 0.0 from setup() to __init__() - this way you make sure that you create this variable once the class gets initialised and before anything else gets executed.
If you plan on using variables in a class across different methods it's better to define them in __init__() to avoid such problems.
This is the error I am getting:
invalid command name "1771926755840move"
while executing
"1771926755840move"
("after" script)
invalid command name "1771922102464move"
while executing
"1771922102464move"
("after" script)
invalid command name "1771947147520move"
while executing
"1771947147520move"
("after" script)
Tcl_AsyncDelete: async handler deleted by the wrong thread
Below is my code:
from turtle import *
from random import *
from freegames import vector
from playsound import playsound
import winsound
from tkinter import *
from timeit import default_timer as timer
from datetime import timedelta
from threading import Event
"""
def paused():
Event.wait()
def unpaused():
Event.release()
"""
root = Tk()
root.title("OPTIONS")
def stop():
bye()
winsound.PlaySound(None, winsound.SND_PURGE)
def gamingarea():
bird = vector(0, 0)
balls = []
winsound.PlaySound('C:/Users/SONY/Desktop/cs.wav', winsound.SND_LOOP + winsound.SND_ASYNC)
start = timer()
def tap(x, y):
lift = vector(0, 30)
bird.move(lift)
def inside(point):
return -200 < point.x < 200 and -200 < point.y < 200
def draw(alive):
clear()
goto(bird.x, bird.y)
if alive:
dot(20, "green")
else:
dot(20, "red")
winsound.PlaySound(None, winsound.SND_PURGE)#when bird is red, music stops
end = timer()
color("orange")
write(str(timedelta(seconds=end-start)), align = "center", font=("Comic Sans MS", 20, "normal", "bold"))
for ball in balls:
goto(ball.x, ball.y)
dot(20, "black")
update()
def move():
bird.y -= 5
for ball in balls:
ball.x -= 3
if randrange(10) == 0:
y = randrange(-199, 199)
ball = vector(199, y)
balls.append(ball)
while len(balls) > 0 and not inside(balls[0]):
balls.pop(0)
if not inside(bird):
draw(False)
return
for ball in balls:
if abs(ball - bird) < 15:
draw(False)
return
draw(True)
ontimer(move, 50)
title("Dodgy Ball")
setup(420, 420)
hideturtle()#turtle is hidden
up()
tracer(False)#Used to turn off animations. When false, animations are turned off. It also turns off automatic updates
onscreenclick(tap)#calling the tap function when touchpad is touched
move()
bgcolor("blue")
done()
startbutton = Button(root, text = "START", padx=100,pady=100, command=gamingarea)
endbutton = Button(root, text = "KILL!", padx=100,pady=100, command=stop)
#pausebutton = Button(root, text = "PAUSE!", padx = 100, pady = 100, command=paused)
#unpausebutton = Button(root, text = "UNPAUSE", padx = 100, pady = 100, command = unpaused)
startbutton.pack()
endbutton.pack()
#pausebutton.pack()
#unpausebutton.pack()
root.mainloop()
PS: It's a game I am designing with my friend
Thanks
Problem is only when game is running (bird is alive) and you try to kill game.
Whey you press kill it then it runs turtle.bye() which deletes all objects and it removes window but it doesn't stop ontimer() which still want to run move() and draw objects (but they don't exists any more and this gives message invalid command name "1771926755840move").
When you press kill it then it should set variable ie. running = False
def stop():
global running
running = False
which should be checked in move to stop loop and close window
if running:
# repeate loop and run `move` again
turtle.ontimer(move, 50)
else:
# stop loop and close window
turtle.bye()
#winsound.PlaySound(None, winsound.SND_PURGE)
My version with reduced modules.
I create own class vector so I don't need freegames which I don't know.
I use datetime instead of timeit because then end-start gives me directly timedelta.
I commented winsound because it works only with Windows but I uses Linux.
I added code for paused, unpased and it doesn't need Threading.
#from turtle import * # PEP8: `import *` is not preferred
#from random import * # PEP8: `import *` is not preferred
#from tkinter import * # PEP8: `import *` is not preferred
import turtle
import tkinter as tk
import random
import datetime
#import winsound
# --- classes ---
class vector():
def __init__(self, x, y):
self.x = x
self.y = y
def move(self, other):
self.x += other.x
self.y += other.y
def __sub__(self, other):
return vector(self.x - other.x, self.y - other.y)
def __abs__(self):
return (self.x**2 + self.y**2)**0.5
# --- functions ---
def paused():
global pause
pause = True
def unpaused():
global pause
pause = False
def stop():
global running
if running:
# stop loop `ontimer(move, 50)`
running = False
else:
# exit when bird is dead
turtle.bye()
#winsound.PlaySound(None, winsound.SND_PURGE)
def gamingarea():
global running
global pause
running = True
pause = False
bird = vector(0, 0)
balls = []
#winsound.PlaySound('C:/Users/SONY/Desktop/cs.wav', winsound.SND_LOOP + winsound.SND_ASYNC)
start = datetime.datetime.now()
def tap(x, y):
lift = vector(0, 30)
bird.move(lift)
def inside(point):
return -200 < point.x < 200 and -200 < point.y < 200
def draw(alive):
global running
turtle.clear()
turtle.goto(bird.x, bird.y)
if alive:
turtle.dot(20, "green")
else:
turtle.dot(20, "red")
#winsound.PlaySound(None, winsound.SND_PURGE)#when bird is red, music stops
turtle.color("orange")
end = datetime.datetime.now()
diff = end-start
turtle.write(str(diff.seconds), align = "center", font=("Comic Sans MS", 20, "normal", "bold"))
running = False
for ball in balls:
turtle.goto(ball.x, ball.y)
turtle.dot(20, "black")
turtle.update()
def move():
if not pause:
bird.y -= 5
for ball in balls:
ball.x -= 3
if random.randrange(10) == 0:
y = random.randrange(-199, 199)
ball = vector(199, y)
balls.append(ball)
while len(balls) > 0 and not inside(balls[0]):
balls.pop(0)
if not inside(bird):
draw(False)
return
for ball in balls:
if abs(ball - bird) < 15:
draw(False)
return
draw(True)
if running:
# repeate loop `ontimer(move, 50)`
turtle.ontimer(move, 50)
else:
# stop loop `ontimer(move, 50)` and exit
turtle.bye()
#winsound.PlaySound(None, winsound.SND_PURGE)
turtle.title("Dodgy Ball")
turtle.setup(420, 420)
turtle.hideturtle() # turtle is hidden
turtle.up()
turtle.tracer(False) # Used to turn off animations. When false, animations are turned off. It also turns off automatic updates
turtle.onscreenclick(tap) # calling the tap function when touchpad is touched
move()
turtle.bgcolor("blue")
turtle.done()
# --- main ---
root = tk.Tk()
root.title("OPTIONS")
start_button = tk.Button(root, text="START", padx=100,pady=100, command=gamingarea)
end_button = tk.Button(root, text="KILL!", padx=100,pady=100, command=stop)
pause_button = tk.Button(root, text="PAUSE!", padx=100, pady=100, command=paused)
unpause_button = tk.Button(root, text="UNPAUSE", padx=100, pady=100, command=unpaused)
start_button.pack(fill='both')
end_button.pack(fill='both')
pause_button.pack(fill='both')
unpause_button.pack(fill='both')
root.mainloop()
Frankly, if you use tkinter to display buttons then you could use tkinter.Canvasto draw objects, andtkinter.bind()to runtap, and root.after()instead ofontimer()to run loop - and then you don't needturtle`
I am trying to make a game where I can press space to shoot my bullet. There is a lot of lag. I am trying to figure out how to make the bullet disappear after it exits the screen. There is also a lot of lag. I am pretty sure the lag is from the bullet not resetting the screen after reaching the end. I imported time but I just can't seem to figure out how to use time.
This is what I tried:
# Importing GUI library
import turtle
import time
bullets = []
bullets2 = []
# Set speed, GUI, creates new turtle
t_speed = 5
bullet_speed = 50000
screen = turtle.Screen()
t = turtle.Turtle()
t2=turtle.Turtle()
screen.setup(700, 400)
# Arrow Keys as turtle movement
def forward():
t.forward(t_speed)
def left():
t.left(t_speed)
def right():
t.right(t_speed)
def back():
t.back(t_speed)
def forward2():
t2.forward(t_speed)
def left2():
t2.left(t_speed)
def right2():
t2.right(t_speed)
def back2():
t2.back(t_speed)
# Shoot a turtle from a turtle
def shoot():
# sets position of bullet at x and y of t
# spawn turtle at x and y
bullet = turtle.Turtle()
bullet.ht()
bullet.penup()
# Launch him
screen.addshape("uparrow.png")
bullet.shape("uparrow.png")
bullet.setheading(t.heading())
bullet.setposition(t.xcor(), t.ycor())
bullet.st()
bullet.forward(700)
bullet.speed(bullet_speed)
#TODO: Make less laggy by deleting bullet object after x seconds
def shoot2():
# set a timer
current = time.localtime().tm_sec
# sets position of bullet at x and y of t
# spawn turtle at x and y
bullet = turtle.Turtle()
bullets2.append(bullet)
bullet.ht()
bullet.penup()
# Launch him
screen.addshape("uparrow.png")
bullet.shape("uparrow.png")
bullet.setheading(t2.heading())
bullet.setposition(t2.xcor(), t2.ycor())
bullet.st()
bullet.forward(700)
bullet.speed(bullet_speed)
#TODO: Make less laggy by deleting bullet object after x seconds
#new = time.localtime().tm_sec
#if new > current + 3:
#bullets2.
def playGame():
# TODO: Health Bar
# TODO: Characters
t.penup()
t.setheading(5)
t2.penup()
# TODO
still_alive = True
# Movement
screen.onkey(back, "Down")
screen.onkey(left, "Left")
screen.onkey(right, "Right")
screen.onkey(shoot, "space")
screen.onkey(forward, "Up")
screen.onkey(back2, "S")
screen.onkey(left2, "A")
screen.onkey(right2, "D")
screen.onkey(shoot2, "Z")
screen.onkey(forward2, "W")
# Game Engine
screen.listen()
t.mainloop()
def gameStart():
# Title
print("Welcome to my game!")
# Menu; press Q to quit, Press p to play
startGame = True
while startGame == True:
inp = raw_input("Press p to play or Press q to quit")
if inp == "q":
exit()
elif inp == "p":
playGame()
startGame = False
else:
print("Incorrect prompt")
# Instructions
print("Use Arrow Keys to move. Press space to shoot")
def main():
gameStart()
if __name__ == "__main__":
main()
The object should disappear after some time.
Your progam appears to be a collection of misconceptions glued together with bad code management. Your bullet not only has lag, but it locks out all other action while it's in motion! Let's toss the time module idea and instead take advantage of turtle's own timer events to really let the bullets fly:
from turtle import Screen, Turtle
# Arrow Keys as turtle movement
def forward_1():
player_1.forward(player_speed)
def back_1():
player_1.back(player_speed)
def left_1():
player_1.left(player_speed)
def right_1():
player_1.right(player_speed)
def shoot_1():
screen.onkey(None, "space") # disable handler inside handler!
shoot(player_1)
screen.onkey(shoot_1, "space")
def forward_2():
player_2.forward(player_speed)
def left_2():
player_2.left(player_speed)
def right_2():
player_2.right(player_speed)
def back_2():
player_2.back(player_speed)
def shoot_2():
screen.onkey(None, "z")
shoot(player_2)
screen.onkey(shoot_2, "z")
def travel(bullet, milliseconds):
bullet.forward(bullet_speed)
if milliseconds:
screen.ontimer(lambda b=bullet, s=milliseconds - 100: travel(b, s), 100)
else:
bullet.hideturtle()
bullets.append(bullet)
# Shoot a bullet from a player
def shoot(player):
# sets position of bullet at x and y of player
# spawn turtle at x and y
if bullets:
bullet = bullets.pop(0)
else:
bullet = Turtle(visible=False)
# bullet.shape("uparrow.png")
bullet.shape('arrow')
bullet.speed('fastest')
bullet.penup()
# Launch him
bullet.color(player.fillcolor())
bullet.setheading(player.heading())
bullet.setposition(player.position())
bullet.showturtle()
travel(bullet, 2000)
bullets = []
# Set speed, GUI, creates new turtle
player_speed = 10
bullet_speed = 15
screen = Screen()
screen.setup(700, 400)
# screen.addshape("uparrow.png")
player_1 = Turtle('triangle')
player_1.speed('fastest')
player_1.color('red', 'pink')
player_1.penup()
player_1.setheading(90)
player_2 = Turtle('triangle')
player_2.speed('fastest')
player_2.color('blue', 'cyan')
player_2.penup()
player_2.setheading(270)
# Movement
screen.onkey(forward_1, "Up")
screen.onkey(back_1, "Down")
screen.onkey(left_1, "Left")
screen.onkey(right_1, "Right")
screen.onkey(shoot_1, "space")
screen.onkey(forward_2, "w")
screen.onkey(back_2, "s")
screen.onkey(left_2, "a")
screen.onkey(right_2, "d")
screen.onkey(shoot_2, "z")
# Game Engine
screen.listen()
screen.mainloop()