Is there a way to make the turtles movement smoother - python

I'm making a game where you control a space ship, and currently, its movement is very hard to look at, because it's quite janky. Is there a command to make the movement smoother and more natural? I'm using
python with turtle on repl.it, and onrelease doesn't seem to work. Any answer is appreciated!
import turtle
you = turtle.Turtle()
keycoms = turtle.Screen()
you.penup()
def w():
you.fd(5)
def s():
you.bk(3)
def a():
you.lt(5)
def d():
you.rt(5)
keycoms.onkey(w,"w")
keycoms.onkey(s,"s")
keycoms.onkey(a,"a")
keycoms.onkey(d,"d")
keycoms.listen()

One way this is often solved is to make more, smaller movements, with a slight time delay between each one. This gives the illusion of flowing movement rather than a jump - something like the following (there may be a better way to actually code it, but this gives you an idea):
def w():
for _ in range(5):
you.fd(1)
sleep(0.1)

Simply change your onkey(...) to onkeypress(...):
import turtle
you = turtle.Turtle()
keycoms = turtle.Screen()
you.penup()
def w():
you.fd(5)
def s():
you.bk(3)
def a():
you.lt(5)
def d():
you.rt(5)
keycoms.onkeypress(w,"w")
keycoms.onkeypress(s,"s")
keycoms.onkeypress(a,"a")
keycoms.onkeypress(d,"d")
keycoms.listen()
keycoms.mainloop()
The Scrren.onkey() method will allow the user to press down on a specific key for as long as the user wants, and the function command to the assigned action will only execute once.
Screen.onkeypress(), on the other hand, will execute the command as many times as the user holds down the key.

Related

Python -- Why are my if statements not running inside while loop?

I'm new to python and decided to practice by building a game similar to snake with the turtle library. I was able to initiate the turtle to continually move forward with a while True loop, but now I'm having trouble with getting the turtle to break this loop to make turns.
I have tried various different ways of writing my conditionals but I can't seem to figure out where the issue is. Thanks in advance!
import turtle
window = turtle.Screen()
snake = turtle.Turtle()
snake.speed(1)
snake.penup()
#Functions that move the snake:
def forward():
while True:
snake.forward(.7)
def left():
snake.left(90)
def right():
snake.right(90)
#Movement functions all put together:
def movesnake():
while True:
entry = input()
if entry == 'w':
forward()
if entry == 'a':
left()
if entry == 'd':
right()
movesnake()
Try this approach to move:
def forward():
turtle.forward(how-many-pixels-forward)
turtle.onkey(forward,'what-key-you-want-to-listen-to') #when key pressed call forward function
https://www.geeksforgeeks.org/turtle-onkey-function-in-python/
The turtle module provides turtle graphics primitives, in both object-oriented and procedure-oriented ways. Because it uses Tkinter for the underlying graphics, it needs a version of Python installed with Tk support.
turtle.onkey()
This function is used to bind fun to the key-release event of the key. In order to be able to register key-events, TurtleScreen must have focus.
You can try increasing the value of the forward function:
def forward():
while True:
snake.forward(5)
Take a look at this book
https://argentinaenpython.com/quiero-aprender-python/doma-de-serpientes-para-ninos_swfk-es-linux-0.0.4.pdf

Clearing the screen for text based rpg in python

I am trying to make a text based rpg game in python. I just started and this is what my code looks so far:
class Game:
def __init__(self):
self.map = [[" " for i in range(50)]for i in range(30)]
def draw_map(self):
for i in range(len(self.map)):
print(self.map[i])
def add_stuff(self, thing, x, y):
self.map[y][x] = thing
game = Game()
class Player:
def __init__(self):
self.x = 1
self.y = 1
self.player = "ME"
def draw(self):
game.add_stuff(self.player, self.x, self.y)
def move(self):
pass
player = Player()
player.draw()
game.draw_map()
I was trying to find a way to implement a game loop in some way. To do this i was thinking of running the draw_map() and clearing the screen right away and printing the map again, a bit like real game loops. However i am having problems doing this. Based on other answers to other similar questions, i managed to produce the following code(it just shows the main loop and subprocess is imported as sp).
while True:
game.draw_map()
sp.call("cls", shell = True)
However this is not working for me. It simply dosent do anything. I also tried using clear function from clear_screen` module in a similar way and i cant figure out why this wouldnt work. Thanks for any help
so based on your previous comments you want to clear the screen in a Python interpreter. There is no "command" to clear the screen just like this. But you can use a loop to print some new lines until your screen is blank again.
def clear(lines):
for l in range(lines):
print()
while True:
game.draw_map()
clear(35)
time.sleep(0.05)
You may need to increase or decrease the amount of lines cleared. I hope this works for you. P.S.: I would use a normal console.
https://en.wikipedia.org/wiki/Interpreter_(computing)#Efficiency:
Efficiency:
The main disadvantage of interpreters is that an interpreted program typically runs slower than if it had been compiled. The difference in speeds could be tiny or great; often an order of magnitude and sometimes more. It generally takes longer to run a program under an interpreter than to run the compiled code but it can take less time to interpret it than the total time required to compile and run it. This is especially important when prototyping and testing code when an edit-interpret-debug cycle can often be much shorter than an edit-compile-run-debug cycle.
If your problem is clearing the console window, you can just use
import os
os.system('cls')
in place of
sp.call("cls", shell = True)
(Assuming you are working on windows)
EDIT for clarity:
This is the new loop after the change:
while True:
game.draw_map()
os.system('cls')

how to add a def function with a while true loop

I've added a function definition to tell my turtle to jump when you press the space bar. There is also a while True loop in my code, and whenever the space button is pressed, the while True loop freezes momentarily until the jump is finished and then carries on.
I've tried adding the function definition in the while True loop and outside. I can only put the function definition before the while True loop, because if I put it after the while True, the code will never reach it.
#making the player jump
def jump():
player.fd(100)
player.rt(180)
player.fd(100)
player.lt(180)
turtle.listen()
turtle.onkey(jump, "space")
I'm expecting the while True loop to not freeze, but wherever I've tried putting the def, it doesn't seem to work.
I also saw another answer to something similar to this, but didn't understand how I could apply that to my code.
Any other suggestions would be great.
Until you get that async stuff to work, here's a minimalist implementation using turtle's own timer event to keep an obstacle moving forward, even when jumping:
from turtle import Screen, Turtle
def jump():
screen.onkey(None, 'space') # disable handler inside handler
if jumper.ycor() == 0: # if jumper isn't in the air
jumper.forward(150)
jumper.backward(150)
screen.onkey(jump, 'space')
def move():
hurdle.forward(6)
if hurdle.xcor() > -400:
screen.ontimer(move, 100)
jumper = Turtle('turtle')
jumper.speed('slowest')
jumper.penup()
jumper.setheading(90)
hurdle = Turtle('turtle')
hurdle.speed('fastest')
hurdle.penup()
hurdle.setheading(180)
hurdle.setx(400)
screen = Screen()
screen.onkey(jump, 'space')
screen.listen()
move()
screen.mainloop()
You can find several, more fleshed out versions of this on SO by searching for '[turtle-graphics] jump'
Avoid using a while True: loop in turtle's event-based environment.
I expected that it can be hard to do it with async but I built example which works.
Inside jump I use asyncio.sleep so this way when one turtle is sleeping then other turtle can walk. Without asyncio.sleep first turtle walk all the time.
import asyncio
import turtle
t1 = turtle.Turtle()
t2 = turtle.Turtle()
async def jump1():
while True:
t1.fd(100)
await asyncio.sleep(0.01)
t1.left(90)
async def jump2():
while True:
t2.fd(100)
await asyncio.sleep(0.01)
t2.right(90)
tasks = [jump1(), jump2()]
# Python 3.6.7
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
# Python 3.7
#asyncio.run(asyncio.wait(tasks))
But building something more complex can be more difficult.
I also found aioturtle - turtle which use async. Maybe it will be easier to use aioturtle.
The problem is the following:
the code for the jump event is called synchronously. That means, everything has to wait for jump() to finish before continuing. this can maybe be fixed by declaring / calling the jump() method asynchronously. This website does a good job of explaining what asynchronous python means and how to implement it: https://hackernoon.com/asynchronous-python-45df84b82434
in short, here's how it would be implemented(in python 3.3 or higher):
async def jump():
#your code here
this just makes the jump() function run asynchronously.
now, whenever you call it, you have to call it like this:
await jump()
this might not work, depending on your exact setup.
i hope this helps.
please, ask me if you have any further questions.
EDIT: example

Python 3 turtle speed of drawing

I am creating a game in python using turtle but I am unable to control the speed of the turtle in the loop as the speed of the turtle is 0. It is supposed to run like flash but it is running at normal speed
import turtle
c=turtle.Screen()
a=turtle.Turtle()
a.speed(0)
b=True
def ch( a , d):
global b
b = False
while b:
a.fd(1)
c.onclick(ch)
c.mainloop()
speed(0) can only speed up the animations a bit.
Try using c.tracer(0, 0)
This fully disables all animations, and should result it a bit more of a speed up. Although, to refresh the screen you'll need to call c.update()
First, your code is structured incorrectly. You don't need to call onclick() in a loop, it simply sets a handler function so only needs to be called once. Also, mainloop() should be running the events, not called after the events are over.
I don't believe you're going get any more speed out of this code unless you increase the forward distance. Simply increading to fd(3) will make a noticeable differece. My rework of your code:
from turtle import Turtle, Screen
def click_handler(x, y):
global flag
flag = False
def turtle_forward():
if flag:
turtle.forward(3)
screen.ontimer(turtle_forward, 0)
flag = True
screen = Screen()
screen.onclick(click_handler)
turtle = Turtle()
turtle.speed('fastest')
turtle_forward()
screen.mainloop()

trying to draw over sprite or change picture pyglet

I am trying to learn pyglet and practice some python coding with a questionnaire thing, but I am unable to find a way to make the background picture be removed or drawn on top of or something for 10 seconds. I am new and am lacking in a lot of the knowledge I would need, thank you for helping!
import pyglet
from pyglet.window import Window
from pyglet.window import key
from pyglet import image
import time
card1 = False
cat_image = pyglet.image.load("cat.png")
dog_image = pyglet.image.load("dog.png")
image = pyglet.image.load("backg.png")
background_sprite = pyglet.sprite.Sprite(image)
cat = pyglet.sprite.Sprite(cat_image)
dog = pyglet.sprite.Sprite(dog_image)
window = pyglet.window.Window(638, 404, "Life")
mouse_pos_x = 0
mouse_pos_y = 0
catmeme = pyglet.image.load("catmeme.png")
sprite_catmeme = pyglet.sprite.Sprite(catmeme)
#window.event
def on_draw():
window.clear()
background_sprite.draw()
card_draw1(63, 192, 385, 192)
def card1():
while time.time() < (time.time() + 10):
window.clear()
sprite_catmeme.draw()
#window.event
def card_draw1(x1, y1, x2, y2):
cat.set_position(x1, y1)
dog.set_position(x2, y2)
cat.draw()
dog.draw()
def card_draw2():
pass
#window.event
def on_mouse_press(x, y, button, modifiers):
if x > cat.x and x < (cat.x + cat.width):
if y > cat.y and y < (cat.y + cat.height):
card1()
game = True
while game:
on_draw()
pyglet.app.run()
There's a few flaws in the order and in which you do things.
I will try my best to describe them and give you a piece of code that might work better for what your need is.
I also think your description of the problem is a bit of an XY Problem which is quite common when asking for help on complex matters where you think you're close to a solution, so you're asking for help on the solution you've come up with and not the problem.
I'm assuming you want to show a "Splash screen" for 10 seconds, which happens to be your background? And then present the cat.png and dog.png ontop of it, correct?
If that's the case, here's where you probably need to change things in order for it to work:
The draw() function
It doesn't really update the screen much, it simply adds things to the graphical memory. What updates the screen is you or something telling the graphics library that you're done adding things to the screen and it's time to update everything you've .draw()'n. So the last thing you need in the loop would be window.flip() in order for the things you've drawn to actually show.
Your things might show if you try to wiggle the window around, it should trigger a re-draw of the scene because of how the internal mechanics of pyglet work..
If you don't call .flip() - odds are probable that the redraw() call will never occur - which again, is a internal mechanism of Pyglet/GL that tells the graphics card that something has been updated, we're done updating and it's time to redraw the scene.
a scene
This is the word most commonly used for what the user is seeing.
I'll probably throw this around a lot in my text, so it's good to know that this is what the user is seeing, not what you've .draw()'n or what's been deleted, it's the last current rendering of the graphics card to the monitor.
But because of how graphical buffers work we've might have removed or added content to the memory without actually drawing it yet. Keep this in mind.
The pyglet.app.run() call
This is a never ending loop in itself, so having that in a while game: loop doesn't really make sense because .run() will "hang" your entire application, any code you want to execute needs to be in def on_draw or an event that is generated from within the graphical code itself.
To better understand this, have a look at my code, i've pasted it around a couple of times here on SO over the years and it's a basic model of two custom classes that inherits the behavior of Pyglet but lets you design your own classes to behave slightly differently.
And most of the functionality is under on_??? functions, which is almost always a function used to catch Events. Pyglet has a lot of these built in, and we're going to override them with our own (but the names must be the same)
import pyglet
from pyglet.gl import *
key = pyglet.window.key
class CustomSprite(pyglet.sprite.Sprite):
def __init__(self, texture_file, x=0, y=0):
## Must load the texture as a image resource before initializing class:Sprite()
self.texture = pyglet.image.load(texture_file)
super(CustomSprite, self).__init__(self.texture)
self.x = x
self.y = y
def _draw(self):
self.draw()
class MainScreen(pyglet.window.Window):
def __init__ (self):
super(MainScreen, self).__init__(800, 600, fullscreen = False)
self.x, self.y = 0, 0
self.bg = CustomSprite('bg.jpg')
self.sprites = {}
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
elif symbol == key.C:
print('Rendering cat')
self.sprites['cat'] = CustomSprite('cat.png', x=10, y=10)
elif symbol == key.D:
self.sprites['dog'] = CustomSprite('dog.png', x=100, y=100)
def render(self):
self.clear()
self.bg.draw()
for sprite_name, sprite_obj in self.sprites.items():
sprite_obj._draw()
self.flip()
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
x = MainScreen()
x.run()
Now, this code is kept simple on purpose, the full code I usually paste on SO can be found at Torxed/PygletGui, the gui.py is where most of this comes from and it's the main loop.
What I do here is simply replace the Decorators by using "actual" functions inside a class. The class itself inherits the functions from a traditional pyglet.window.Window, and as soon as you name the functions the same as the inherited onces, you replace the core functionality of Window() with whatever you decide.. In this case, i mimic the same functions but add a few of my own.
on_key_press
One such example is on_key_press(), which normally just contain a pass call and does nothing, here, we check if key.C is pressed, and if so - we add a item to self.sprites.. self.sprites just so happen to be in our render() loop, anything in there will be rendered ontop of a background.
Here's the pictures I used:
(named bg.jpg, cat.png, dog.png - note the different file endings)
class:CustomSprite
CustomSprite is a very simple class designed to make your life easier at this point, nothing else. It's very limited in functionality but the little it do is awesome.
It's soul purpose is to take a file name, load it as an image and you can treat the object like a traditional pyglet.sprite.Sprite, meaning you can move it around and manipulate it in many ways.
It saves a few lines of code having to load all the images you need and as you can see in gui_classes_generic.py you can add a heap of functions that's "invisible" and normally not readily availbale to a normal sprite class.
I use this a bunch! But the code gets complicated real fast so I kept this post simple on purpose.
the flip function
Even in my class, I still need to use flip() in order to update the contents of the screen. This is because .clear() clears the window as you would expect, that also triggers a redraw of the scene.
bg.draw() might in some cases trigger a redraw if the data is big enough or if something else happens, for instance you move the window.
but calling .flip() will tell the GL backend to force a redraw.
Further optimizations
There's a thing called batched rendering, basically the graphic card is designed to take enormous ammounts of data and render it in one go, so calling .draw() on several items will only clog the CPU before the GPU even gets a chance to shine. Read more about Batched rendering and graphics! It will save you a lot of frame rates.
Another thing is to keep as little functionality as possible in the render() loop and use the event triggers as your main source of coding style.
Pyglet does a good job of being fast, especially if you only do things on event driven tasks.
Try to avoid timers, but if you really do need to use time for things, such as removing cat.png after a certain ammount of time, use the clock/time event to call a function that removes the cat. Do not try to use your own t = time() style of code unless you know where you're putting it and why. There's a good timer, I rarely use it.. But you should if you're starting off.
This has been one hell of a wall of text, I hope it educated you some what in the life of graphics and stuff. Keep going, it's a hurdle to get into this kind of stuff but it's quite rewarding once you've mastered it (I still haven't) :)

Categories