I have two turtle objects, gamer and bot. I'm controlling gamer with the mouse. I want that in the same time I control it, bot could move independently. Is it possible?
import turtle as t
gamer = t.Turtle()
bot = t.Turtle()
...
...
def move_gamer(x, y):
if gamer.xcor() < -bc_x or gamer.ycor() < -bc_y or gamer.xcor() > bc_x or gamer.ycor() > bc_y:
screen.done()
gamer.ondrag(None)
gamer.goto(x, y)
gamer.ondrag(move_gamer)
def move_bot():
bot.forward(10)
def ondrag_event(x, y):
move_gamer(x, y)
move_bot()
gamer.ondrag(ondrag_event)
screen.mainloop()
This should roughly do what you describe. One turtle is controlled by the user, the other is on a timer and moves (mostly) independently:
from turtle import Screen, Turtle
CURSOR_SIZE = 20
bc_x, bc_y = 400, 400
def move_gamer(x, y):
gamer.ondrag(None)
if -bc_x/2 < gamer.xcor() < bc_x/2 and -bc_y/2 < gamer.ycor() < bc_y/2:
gamer.goto(x, y)
gamer.ondrag(move_gamer)
def move_bot():
bot.setheading(bot.towards(gamer))
bot.forward(10)
screen.ontimer(move_bot, 100)
def ondrag_event(x, y):
move_gamer(x, y)
move_bot()
screen = Screen()
box = Turtle('square')
box.color('pink')
box.shapesize(bc_y / CURSOR_SIZE, bc_x / CURSOR_SIZE)
gamer = Turtle('circle')
gamer.penup()
gamer.goto(bc_x/3, bc_y/3)
bot = Turtle('turtle')
bot.penup()
bot.goto(-bc_x/3, -bc_y/3)
gamer.ondrag(move_gamer)
move_bot()
screen.mainloop()
I threw in an extra turtle to delineate the arena -- this can be done other ways.
Related
How to make draw like this with turtle?
right now my code looks like that:
class OperationsOnSets():
def __init__(self):
self.font_style = ("Times New Roman", 40, "bold")
def move_turtle_pos(self, x, y, turtle):
turtle.up()
turtle.setpos(x, y)
turtle.down()
def set_d(self):
turtle = Turtle()
turtle.pensize(2)
turtle.speed(2)
self.move_turtle_pos(-100, -50, turtle)
turtle.circle(200)
self.move_turtle_pos(100, -50, turtle)
turtle.circle(200)
sleep(5)
turtle.mainloop()
example = OperationsOnSets()
example.set_d()
and here is result
I though about pasting image, or make algorithm that would draw a lines, but I don`t know how to realize it. So I hope that someobody of you will help me with it...
My philosophy of turtles, is avoid the math by getting the turtle to do the hard work for you. Not quite perfect, but pretty close:
from turtle import Screen, Turtle
RADIUS = 200
NUMBER_LINES = 14
class OperationsOnSets():
def __init__(self):
self.turtle = Turtle()
self.turtle.pensize(2)
self.turtle.fillcolor(screen.bgcolor())
def move_turtle_pos(self, x, y):
self.turtle.penup()
self.turtle.setpos(x, y)
self.turtle.pendown()
def set_d(self):
self.move_turtle_pos(-RADIUS/2, -RADIUS)
points = []
for _ in range(NUMBER_LINES):
self.turtle.circle(RADIUS, 180 / NUMBER_LINES)
points.append(self.turtle.position())
for _ in range(NUMBER_LINES):
self.turtle.circle(RADIUS, 180 / NUMBER_LINES)
position = self.turtle.position()
self.turtle.goto(points.pop())
self.turtle.goto(position)
self.move_turtle_pos(RADIUS/2, -RADIUS)
self.turtle.begin_fill()
self.turtle.circle(RADIUS)
self.turtle.end_fill()
self.move_turtle_pos(-RADIUS/2, -RADIUS)
self.turtle.circle(RADIUS, 180)
self.turtle.hideturtle()
screen = Screen()
example = OperationsOnSets()
example.set_d()
screen.mainloop()
We make the turtle stop along its way around the circle to record points we'll need to finish the drawing.
The Point of the game is to make all the circles disappear when they Collide but for some reason some circles aren't disappearing? - Thank you for your help in advance!
import turtle
import random
import math
import time
# Setting up the Screen
ms = turtle.Screen()
ms.bgcolor("red")
ms.title("Space Rocket Minigame #Rafa94")
# Using class functions/Methods
# subclass
class Border(turtle.Turtle):
def __init__(self): # class constrcutor
turtle.Turtle.__init__(self) # adding our Objects attributes all starting with "self"
self.penup()
self.hideturtle()
self.speed(0)
self.color("silver")
self.pensize(5)
def draw_border(self):
self.penup()# getting our pen to start drawing
self.goto(-300, -300)
self.pendown()
self.goto(-300, 300)
self.goto(300, 300)
self.goto(300, -300)
self.goto(-300, -300)
class Player(turtle.Turtle): # since it got inherited this class becomes a Superclass
def __init__(self): # self is our only argument here but it will have multiple attributes
turtle.Turtle.__init__(self) # since we are using the Turtle module, we are able to use it's built in functions
self.penup()# our attributes
self.speed(0)
self.shape("triangle")
self.color("black")
self.velocity = 0.1
def move(self):
self.forward(self.velocity)
# Border Checking
if self.xcor() > 290 or self.xcor() < -290: # Left side is -290 Right side is 290 we also want the coordinates x and y to be below 300 to not go over our border
self.left(60)
if self.ycor() > 290 or self.ycor() < -290:
self.left(60)
def turnleft(self):
self.left(30)
def turnright(self):
self.right(30)
def increasespeed(self):
self.velocity += 1
class Goal(turtle.Turtle): # Sub Class
def __init__(self):
# since we are using the Turtle module we are able to use it's built in functions
turtle.Turtle.__init__(self)
self.penup() # our attributes
self.speed(0)
self.shape("circle")
self.color("green")
self.velocity = 3 #xcor #ycor
self.goto(random.randint(-250, 250), random.randint(-250, 250)) # we are making our turtle "go to" X & Y coordinates by -250 and 250 only randomly. We also have our random module here aswell
self.setheading(random.randint(0, 360)) # setting the heading to see in which direction i want it to go
def jump(self): # Jump = Collidee
self.goto(random.randint(-250, 250), random.randint(-250, 250)) # "jump" stands for Collidee so if the circle "jumps" with player it will move to random postion by 250 and -25
self.setheading(random.randint(0, 360)) # from where it collidee's it goes 360 moves location 360 Right
def move(self): # we copyed the same method cause it will be doing the same movements as the player we want it to go "forward" with our set "speed" & also check for our borders we set
self.forward(self.velocity)
# Border Checking
if self.xcor() > 290 or self.xcor() < -290: # Left side is -290 Right side is 290 we also want the coordinates x and y to be below 300 to not go over our border
self.left(60)
if self.ycor() > 290 or self.ycor() < -290:
self.left(60)
# Collision checking function/Method
# Uses the Pythgorean Theorem to measure the distance between two objects
def isCollision(t1, t2): # t1 = turtle1 t2 = turtle also when a function starts with "is" isCollision most likely it will be a Boolean of True or False
a = t1.xcor()-t2.xcor() # xcor = Right -xcor = Left/ when they collide the xcor is 0
b = t1.ycor()-t2.ycor() # ycor = Right -ycor = Left/ when they collide the ycor is 0
distance = math.sqrt((a ** 2) + (b ** 2))
if distance < 30:
return True
else:
return False
# Create class instances
player = Player() # after creating a class must make instances to call it in other words make an Object of the class
border = Border() # sub class
#goal = Goal() #sub class
# Draw our border
border.draw_border()
#create multiple goals
goals = [] # Creating a list of goals
for count in range(6): # We are making the code repeat 6 times
goals.append(Goal()) # each time the code runs it puts a goal the end 6 times
# Set keyboard bindings
ms.listen()
ms.onkey(player.turnleft, "Left")
ms.onkey(player.turnright, "Right")
ms.onkey(player.increasespeed, "Up")
# speed game up
ms.tracer(0.1)
# main loop
while True:
ms.update()
player.move() # these two are class methods
#goal.move() # the reason we copyed like we said is cause it's gunna have the exact same movements as our player!
# we want the goal to be True to in our while loop in order for the code to be excuted
for goal in goals:
# Basically saying If there is a collision between the player and goal we the want the goal to "jump" / Function in our while True loop
goal.move()
if isCollision(player, goal):
goal.jump() # baiscally saying if the goal collide's move or "jump" to other location
time.sleep(0.005)
As far as I can tell, your circles (goals) disappear and reappear elsewhere as you designed them to do. One possibiliy is that since you move the circles to a random location, that location can be close to where they disappeared from, making it appear that nothing happened.
Generally, your code is a mess. Below is my rewrite of it to bring some structure and style to it. As well as take more advantage of what turtle offers:
from turtle import Screen, Turtle
from random import randint
WIDTH, HEIGHT = 600, 600
CURSOR_SIZE = 20
class Border(Turtle):
def __init__(self): # class initializer
super().__init__(visible=False)
self.penup()
self.color("silver")
self.pensize(5)
def draw_border(self):
self.goto(-WIDTH/2, -HEIGHT/2)
self.pendown()
for _ in range(2):
self.forward(WIDTH)
self.left(90)
self.forward(HEIGHT)
self.left(90)
class Player(Turtle):
def __init__(self):
super().__init__(shape="triangle")
self.color("black")
self.penup()
self.velocity = 0.1
def move(self):
self.forward(self.velocity)
if not (CURSOR_SIZE - WIDTH/2 < self.xcor() < WIDTH/2 - CURSOR_SIZE and CURSOR_SIZE - HEIGHT/2 < self.ycor() < HEIGHT/2 - CURSOR_SIZE):
self.undo() # undo forward motion
self.left(60)
self.forward(self.velocity) # redo forward motion in new heading
def turn_left(self):
self.left(30)
def turn_right(self):
self.right(30)
def increase_speed(self):
self.velocity += 1
class Goal(Turtle):
def __init__(self):
super().__init__(shape="circle")
self.color("green")
self.penup()
self.velocity = 3
self.jump()
def jump(self):
while self.distance(player) < CURSOR_SIZE * 10: # make sure we move far away from player
self.goto(randint(CURSOR_SIZE - WIDTH/2, WIDTH/2 - CURSOR_SIZE), randint(CURSOR_SIZE - HEIGHT/2, HEIGHT/2 - CURSOR_SIZE))
self.setheading(randint(0, 360))
def move(self):
self.forward(self.velocity)
if not (CURSOR_SIZE - WIDTH/2 < self.xcor() < WIDTH/2 - CURSOR_SIZE and CURSOR_SIZE - HEIGHT/2 < self.ycor() < HEIGHT/2 - CURSOR_SIZE):
self.undo() # undo forward motion
self.left(60)
self.forward(self.velocity) # redo forward motion in new heading
def isCollision(t1, t2):
return t1.distance(t2) < CURSOR_SIZE
def move():
player.move()
for goal in goals:
goal.move()
if isCollision(player, goal):
goal.jump()
screen.update()
screen.ontimer(move, 10)
# Setting up the Screen
screen = Screen()
screen.bgcolor("red")
screen.title("Space Rocket Minigame #cdlane")
screen.tracer(False)
# Create class instances
Border().draw_border()
player = Player()
# Create multiple goals
goals = [Goal() for _ in range(6)]
# Set keyboard bindings
screen.onkey(player.turn_left, "Left")
screen.onkey(player.turn_right, "Right")
screen.onkey(player.increase_speed, "Up")
screen.listen()
move()
screen.mainloop()
How do you make turtle move without using turtle.goto(x,y) but turtle.speed(speed) and turtle.heading(angle)? I need this for a game I am making. Where the mouse is, I want to make it go in that direction. But when I change it, it goes to that place then to my mouse:
import turtle
screen = turtle.Screen()
screen.title("Test")
screen.bgcolor("white")
screen.setup(width=600, height=600)
ship = turtle.Turtle()
ship.speed(1)
ship.shape("triangle")
ship.penup()
ship.goto(0,0)
ship.direction = "stop"
ship.turtlesize(3)
turtle.hideturtle()
def onmove(self, fun, add=None):
if fun is None:
self.cv.unbind('<Motion>')
else:
def eventfun(event):
fun(self.cv.canvasx(event.x) / self.xscale, -self.cv.canvasy(event.y) / self.yscale)
self.cv.bind('<Motion>', eventfun, add)
def goto_handler(x, y):
onmove(turtle.Screen(), None)
ship.setheading(ship.towards(x, y)) #this is where you use the x,y cordinates and I have seat them to got to x,y and set heading
ship.goto(x,y)
onmove(turtle.Screen(), goto_handler)
onmove(screen, goto_handler)
If you only setheading and speed it just turns that way and does not move. If you try this code it works -- it is just that I use ship.goto(x, y) which makes it go to (x, y). But when you change your mouse when it is moving, it first goes to (x, y) then to your new mouse position. I pretty much just want it to just follow the mouse but I can not do that.
I believe the code below gives you the motion you desire. It only uses onmove() to stash the target's position and uses an ontimer() to aim and move the turtle. It also stops when the target has been enveloped:
from turtle import Screen, Turtle, Vec2D
def onmove(self, fun, add=None):
if fun is None:
self.cv.unbind('<Motion>')
else:
def eventfun(event):
fun(Vec2D(self.cv.canvasx(event.x) / self.xscale, -self.cv.canvasy(event.y) / self.yscale))
self.cv.bind('<Motion>', eventfun, add)
def goto_handler(position):
global target
onmove(screen, None)
target = position
onmove(screen, goto_handler)
def move():
if ship.distance(target) > 5:
ship.setheading(ship.towards(target))
ship.forward(5)
screen.ontimer(move, 50)
screen = Screen()
screen.title("Test")
screen.setup(width=600, height=600)
ship = Turtle("triangle")
ship.turtlesize(3)
ship.speed('fast')
ship.penup()
target = (0, 0)
onmove(screen, goto_handler)
move()
screen.mainloop()
When clicking within the random squares, it says "You clicked!" only when it draws the next square, not when you initially click.
import turtle
import random
import time
t1 = turtle.Turtle()
t2 = turtle.Turtle()
s = turtle.Screen()
def turtle_set_up():
t1.hideturtle()
t2.hideturtle()
t2.penup()
s.tracer(1, 0)
def draw():
for i in range(2):
t1.fd(400)
t1.lt(90)
t1.fd(400)
t1.lt(90)
def drawbox():
t2.pendown()
for i in range(2):
t2.fd(50)
t2.lt(90)
t2.fd(50)
t2.lt(90)
t2.penup()
def box1():
X = random.randint(0, 350)
Y = random.randint(0, 350)
Xleft = X
Xright = X + 50
Ytop = Y + 50
Ybottom = Y
t2.goto(X, Y)
drawbox()
Here is where it checks if your mouse click is within the box drawn. If it is, it erases the old box for a new one to be drawn every 3 seconds.
I don't understand why it waits to say "You clicked!" until the 3 seconds are up for the loop instead of saying it when you initially click.
I cant place a break command after the print statement that prints "You clicked!" because it's outside the loop.
between here:
t_end = time.time() + 60 * 0.05
while time.time() < t_end:
def get_mouse_click_coor(x, y):
X_click = x
Y_click = y
if Xright > X_click > Xleft and Ytop > Y_click > Ybottom:
print('You clicked!')
s.onclick(get_mouse_click_coor)
t2.clear()
and here:
def starter():
turtle_set_up()
draw()
while 1:
box1()
starter()
Applications with interactive GUI (Graphical User Interface) are event-based, which means that they perform their actions when some events happen. For such applications, if you create a waiting loop for a given amount of time (as does your code) the whole application will be blocked for this amount of time. That's why the print is only executed after the 3s delay.
All GUI libraries include a scheme to activate some timer events. For the turtle API, there is a on_timer(func, delay) method that calls a function func after some delay (expressed in milliseconds). The idea is to repeatedly call your drawbox function every 3000ms. So, your code will be based on two main callback functions: get_mouse_click called on click events, and drawbox called on timer events. Here is the modified code, I propose:
import turtle
import random
t1 = turtle.Turtle()
t2 = turtle.Turtle()
s = turtle.Screen()
def turtle_set_up():
t1.hideturtle()
t2.hideturtle()
s.tracer(1, 0)
s.onclick(get_mouse_click) # define the 'click' event callback
def draw():
for i in range(2):
t1.fd(400)
t1.lt(90)
t1.fd(400)
t1.lt(90)
def drawbox():
global box # store box coordinates as global variable
X = random.randint(0, 350) # so that they are available for testing
Y = random.randint(0, 350) # in the mouse click callback
box = (X, Y, X+50, Y+50)
t2.clear() # clear previous box before drawing new one
t2.penup()
t2.goto(X, Y)
t2.pendown()
for i in range(2):
t2.fd(50)
t2.lt(90)
t2.fd(50)
t2.lt(90)
s.ontimer(drawbox, 3000) # define timer event callback
def get_mouse_click(x, y):
if box[0] <= x <= box[2] and box[1] <= y <= box[3]:
print('You clicked!')
def starter():
turtle_set_up()
draw()
drawbox()
starter()
Computers have to run in sequence so it can only process one line at a time, so, unless i'm mistaken, your program gets 'caught' on the timer then runs through the program and back to the start.
you could develop a while loop with a nested if statement that gets datetime from the device, if turtles have datetime
I believe you can simplify this problem. Primarily by making a turtle be the inner box rather than drawing the inner box. This simplifies drawing, erasing and event handling. Avoid invoking the tracer() method until you have a working program as it only complicates debugging. We can also stamp rather than draw the border.
If we simply want to be able to click on the inner box and have it randomly move to a new location:
from turtle import Turtle, Screen
from random import randint
BORDER_SIZE = 400
BOX_SIZE = 50
CURSOR_SIZE = 20
def move_box():
x = randint(BOX_SIZE/2 - BORDER_SIZE/2, BORDER_SIZE/2 - BOX_SIZE/2)
y = randint(BOX_SIZE/2 - BORDER_SIZE/2, BORDER_SIZE/2 - BOX_SIZE/2)
turtle.goto(x, y)
def on_mouse_click(x, y):
print("You clicked!")
move_box()
screen = Screen()
turtle = Turtle('square', visible=False)
turtle.shapesize(BORDER_SIZE / CURSOR_SIZE)
turtle.color('black', 'white')
turtle.stamp()
turtle.shapesize(BOX_SIZE / CURSOR_SIZE)
turtle.onclick(on_mouse_click)
turtle.penup()
turtle.showturtle()
move_box()
screen.mainloop()
If we want to make the program more game-like and require the user to click on the inner box within 3 seconds of each move, or lose the game, then we can introduce the ontimer() event as #sciroccorics suggests:
from turtle import Turtle, Screen
from random import randint
BORDER_SIZE = 400
BOX_SIZE = 50
CURSOR_SIZE = 20
CLICK_TIMEOUT = 3000 # milliseconds
def move_box():
x = randint(BOX_SIZE/2 - BORDER_SIZE/2, BORDER_SIZE/2 - BOX_SIZE/2)
y = randint(BOX_SIZE/2 - BORDER_SIZE/2, BORDER_SIZE/2 - BOX_SIZE/2)
turtle.goto(x, y)
screen.ontimer(non_mouse_click, CLICK_TIMEOUT)
def on_mouse_click(x, y):
global semaphore
print("You clicked!")
semaphore += 1
move_box()
def non_mouse_click():
global semaphore
semaphore -= 1
if semaphore < 1:
turtle.onclick(None)
turtle.color('black')
print("Game Over!")
screen = Screen()
semaphore = 1
turtle = Turtle('square', visible=False)
turtle.shapesize(BORDER_SIZE / CURSOR_SIZE)
turtle.color('black', 'white')
turtle.stamp()
turtle.shapesize(BOX_SIZE / CURSOR_SIZE)
turtle.onclick(on_mouse_click)
turtle.penup()
turtle.showturtle()
move_box()
screen.mainloop()
I have written a turtle program in python, but there are two problems.
It goes way too slow for larger numbers, I was wonder how I can speed up turtle.
It freezes after it finishes and when clicked on, says 'not responding'
This is my code so far:
import turtle
#Takes user input to decide how many squares are needed
f=int(input("How many squares do you want?"))
c=int(input("What colour would you like? red = 1, blue = 2 and green =3"))
n=int(input("What background colour would you like? red = 1, blue = 2 and green =3"))
i=1
x=65
#Draws the desired number of squares.
while i < f:
i=i+1
x=x*1.05
print ("minimise this window ASAP")
if c==1:
turtle.pencolor("red")
elif c==2:
turtle.pencolor("blue")
elif c==3:
turtle.pencolor("green")
else:
turtle.pencolor("black")
if n==1:
turtle.fillcolor("red")
elif n==2:
turtle.fillcolor("blue")
elif n==3:
turtle.fillcolor("green")
else:
turtle.fillcolor("white")
turtle.bk(x)
turtle.rt(90)
turtle.bk(x)
turtle.rt(90)
turtle.bk(x)
turtle.rt(90)
turtle.bk(x)
turtle.rt(90)
turtle.up()
turtle.rt(9)
turtle.down()
By the way: I am on version 3.2!
Set turtle.speed("fastest").
Use the turtle.mainloop() functionality to do work without screen refreshes.
Disable screen refreshing with turtle.tracer(0, 0) then at the end do turtle.update()
Python turtle goes very slowly because screen refreshes are performed after every modification is made to a turtle.
You can disable screen refreshing until all the work is done, then paint the screen, it will eliminate the millisecond delays as the screen furiously tries to update the screen from every turtle change.
For example:
import turtle
import random
import time
screen = turtle.Screen()
turtlepower = []
turtle.tracer(0, 0)
for i in range(1000):
t = turtle.Turtle()
t.goto(random.random()*500, random.random()*1000)
turtlepower.append(t)
for i in range(1000):
turtle.stamp()
turtle.update()
time.sleep(3)
This code makes a thousand turtles at random locations, and displays the picture in about 200 milliseconds.
Had you not disabled screen refreshing with turtle.tracer(0, 0) command, it would have taken several minutes as it tries to refresh the screen 3000 times.
https://docs.python.org/2/library/turtle.html#turtle.delay
For reference, turtle being slow is an existing problem.
Even with speed set to max, turtle can take quite a long time on things like fractals.
Nick ODell reimplemented turtle for speed here: Hide Turtle Window?
import math
class UndrawnTurtle():
def __init__(self):
self.x, self.y, self.angle = 0.0, 0.0, 0.0
self.pointsVisited = []
self._visit()
def position(self):
return self.x, self.y
def xcor(self):
return self.x
def ycor(self):
return self.y
def forward(self, distance):
angle_radians = math.radians(self.angle)
self.x += math.cos(angle_radians) * distance
self.y += math.sin(angle_radians) * distance
self._visit()
def backward(self, distance):
self.forward(-distance)
def right(self, angle):
self.angle -= angle
def left(self, angle):
self.angle += angle
def setpos(self, x, y = None):
"""Can be passed either a tuple or two numbers."""
if y == None:
self.x = x[0]
self.y = x[1]
else:
self.x = x
self.y = y
self._visit()
def _visit(self):
"""Add point to the list of points gone to by the turtle."""
self.pointsVisited.append(self.position())
# Now for some aliases. Everything that's implemented in this class
# should be aliased the same way as the actual api.
fd = forward
bk = backward
back = backward
rt = right
lt = left
setposition = setpos
goto = setpos
pos = position
ut = UndrawnTurtle()