Python Turtle - Issue with custom shape in Snake program - python

I have a python program using turtle to make the game "Snake". It is fully functional. At this point I'm adding in .gif images to spruce it up.
The problem arises when I'm trying to use a custom shape as the head of the snake. If I use a basic shape from turtle like "triangle" or "arrow" it works fine. It turns correctly and is printed over the first body segment as intended. When I change it to my custom image, the problem is that it is printed out under the first body segment and cannot turn.
Sample of controls
if direction == "up":
if snake_direction != "down":
snake_direction = "up"
head_stamper.setheading(90)
Stampers used to make numerous sections, and a head over the first segment.
for segment in snake:
stamper.goto(segment[0], segment[1]) #These are the body segments, they work fine.
stamper.stamp()
head_stamper.goto(new_head)
Showing both stampers here.
# Stamper for each body section
stamper = turtle.Turtle()
stamper.shape(bod_segment)
stamper.shapesize(25 / 20)
stamper.penup()
# Special stamper just for the snake's head.
head_stamper = turtle.Turtle()
# head_stamper has no issues when I make the shape "arrow" or "triangle", etc.
head_stamper.shape(head_segment)
stamper.shapesize(25 / 20)
head_stamper.penup()
I think this is all the code relevant to the problem.

When I change it to my custom image, the problem is that it is printed
out under the first body segment and cannot turn.
As far as not turning, this is addressed in the Python turtle documentation of register_shape() aka addshape():
Note: Image shapes do not rotate when turning the turtle, so they do
not display the heading of the turtle!
As far as the overlap problem, I can only guess. Generally the rule in turtle is the last thing that moved is on top. So turning your generic head shape lands it on top, but since your image shape doesn't actually turn, it winds up on the bottom. Again, just a guess.

Related

Pygame: Snap-to-grid/board in chess

Is there a way in pygame for sprites, when dragged, to snap to an invisible (or visible) grid? Kinda like drag 'n drop? If so, how? I've been trying to do some sprite overlap, but it's too tedious to make an if-statement for each of the grid-lines. So how can I make a snap-to-grid drag-n-drop sprite? This is for a chess program. I'll appreciate your help.
Make an array of corners of the chess board using for loops.
corners = []
for x in range(edgeRight, edgeLeft, interval):
for y in range(edgeTop, edgeBottom, interval):
corners.append((x,y))
Then, make an event listener. When the piece is being dragged around, insert this code into whatever while statement you have:
px, py = Piece.Rect.topleft //using tuples to assign multiple vars
for cx, cy in corners:
if math.hypot(cx-px, cy-py) < distToSnap:
Piece.setPos((cx,cy))
break
I have no idea what your actual code is, but this should give you an idea. Again, pygame has no snap-to-grid functionality.
So this can be done quite simply by using the round function in python.
sprite.rect.x = ((round(sprite.rect.x/gridSquareSize))*gridSquareSize)
sprite.rect.y = ((round(sprite.rect.y/gridSquareSize))*gridSquareSize)
This manipulates the round function, by rounding your sprite's coordinates to the nearest grid square.

Sprite Sheets Pygame/Python

So I am making a game in pygame and really need to get sprite sheets working. Every sprite sheet example I've seen however, is either terrible and I have no idea how to use it or is not a class. I thought that by pressing in the direction of the arrow keys(which is the movement in my game) I could just draw it to the player objects x and y values. This works for the first arrow key and if you press another after that it errors with
TypeError: argument 1 must be pygame.Surface, not bool
My code is:
posi=[player.rect.x,player.rect.y]
if left1:
posi=[player.rect.x,player.rect.y]
screen.blit(left,posi)
if right1:
posi=[player.rect.x,player.rect.y]
screen.blit(right,posi)
if down1:
posi=[player.rect.x,player.rect.y]
screen.blit(down,posi)
if up1:
posi=[player.rect.x,player.rect.y]
screen.blit(up,posi)
and I pretty much set left1,down1,right1 or up1 equal to true if you press an arrow key
It looks like you are not using screen.blit correctly. The first argument should be the image you are blitting (in other words, the picture of the sprite) and the second argument should be a tuple coordinate.
Go into your code and check what left is: I bet that it is not an image, so substitute it with the sprite's image. The coordinates are fine, though.
Pygame docs (see for help with surfaces): pygame home

When saving turtle graphics to an .eps file, the background color shows on the screen but is not saved in the .eps file

I am new to Python and have been working with the turtle module as a way of learning the language.
Thanks to stackoverflow, I researched and learned how to copy the image into an encapsulated postscript file and it works great. There is one problem, however. The turtle module allows background color which shows on the screen but does not show in the .eps file. All other colors, i.e. pen color and turtle color, make it through but not the background color.
As a matter of interest, I do not believe the import of Tkinter is necessary since I do not believe I am using any of the Tkinter module here. I included it as a part of trying to diagnose the problem. I had also used bgcolor=Orange rather than the s.bgcolor="orange".
No Joy.
I am including a simple code example:
# Python 2.7.3 on a Mac
import turtle
from Tkinter import *
s=turtle.Screen()
s.bgcolor("orange")
bob = turtle.Turtle()
bob.circle(250)
ts=bob.getscreen()
ts.getcanvas().postscript(file = "turtle.eps")
I tried to post the images of the screen and the .eps file but stackoverflow will not allow me to do so as a new user. Some sort of spam prevention. Simple enough to visualize though, screen has background color of orange and the eps file is white.
I would appreciate any ideas.
Postscript was designed for making marks on some medium like paper or film, not raster graphics. As such it doesn't have a background color per se that can be set to given color because that would normally be the color of the paper or unexposed film being used.
In order to simulate this you need to draw a rectangle the size of the canvas and fill it with the color you want as the background. I didn't see anything in the turtle module to query the canvas object returned by getcanvas() and the only alternative I can think of is to read the turtle.cfg file if there is one, or just hardcode the default 300x400 size. You might be able to look at the source and figure out where the dimensions of the current canvas are stored and access them directly.
Update:
I was just playing around in the Python console with the turtle module and discovered that what the canvas getcanvas() returns has a private attribute called _canvas which is a <Tkinter.Canvas instance>. This object has winfo_width() and winfo_height() methods which seem to contain the dimensions of the current turtle graphics window. So I would try drawing a filled rectangle of that size and see if that gives you what you want.
Update 2:
Here's code showing how to do what I suggested. Note: The background must be drawn before any other graphics are because otherwise the solid filled background rectangle created will cover up everything else on the screen.
Also, the added draw_background() function makes an effort to save and later restore the graphics state to what it was. This may not be necessary depending on your exact usage case.
import turtle
def draw_background(a_turtle):
""" Draw a background rectangle. """
ts = a_turtle.getscreen()
canvas = ts.getcanvas()
height = ts.getcanvas()._canvas.winfo_height()
width = ts.getcanvas()._canvas.winfo_width()
turtleheading = a_turtle.heading()
turtlespeed = a_turtle.speed()
penposn = a_turtle.position()
penstate = a_turtle.pen()
a_turtle.penup()
a_turtle.speed(0) # fastest
a_turtle.goto(-width/2-2, -height/2+3)
a_turtle.fillcolor(turtle.Screen().bgcolor())
a_turtle.begin_fill()
a_turtle.setheading(0)
a_turtle.forward(width)
a_turtle.setheading(90)
a_turtle.forward(height)
a_turtle.setheading(180)
a_turtle.forward(width)
a_turtle.setheading(270)
a_turtle.forward(height)
a_turtle.end_fill()
a_turtle.penup()
a_turtle.setposition(*penposn)
a_turtle.pen(penstate)
a_turtle.setheading(turtleheading)
a_turtle.speed(turtlespeed)
s = turtle.Screen()
s.bgcolor("orange")
bob = turtle.Turtle()
draw_background(bob)
ts = bob.getscreen()
canvas = ts.getcanvas()
bob.circle(250)
canvas.postscript(file="turtle.eps")
s.exitonclick() # optional
And here's the actual output produced (rendered onscreen via Photoshop):
I haven't found a way to get the canvas background colour on the generated (Encapsulated) PostScript file (I suspect it isn't possible). You can however fill your circle with a colour, and then use Canvas.postscript(colormode='color') as suggested by #mgilson:
import turtle
bob = turtle.Turtle()
bob.fillcolor('orange')
bob.begin_fill()
bob.circle(250)
bob.begin_fill()
ts = bob.getscreen()
ts.getcanvas().postscript(file='turtle.eps', colormode='color')
Improving #martineau's code after a decade
import turtle as t
Screen=t.Screen()
Canvas=Screen.getcanvas()
Width, Height = Canvas.winfo_width(), Canvas.winfo_height()
HalfWidth, HalfHeight = Width//2, Height//2
Background = t.Turtle()
Background.ht()
Background.speed(0)
def BackgroundColour(Colour:str="white"):
Background.clear() # Prevents accumulation of layers
Background.penup()
Background.goto(-HalfWidth,-HalfHeight)
Background.color(Colour)
Background.begin_fill()
Background.goto(HalfWidth,-HalfHeight)
Background.goto(HalfWidth,HalfHeight)
Background.goto(-HalfWidth,HalfHeight)
Background.goto(-HalfWidth,-HalfHeight)
Background.end_fill()
Background.penup()
Background.home()
BackgroundColour("orange")
Bob=t.Turtle()
Bob.circle(250)
Canvas.postscript(file="turtle.eps")
This depends on what a person is trying to accomplish but generally, having the option to select which turtle to use to draw your background to me is unnecessary and can overcomplicate things so what one can do instead is have one specific turtle (which I named Background) to just update the background when desired.
Plus, rather than directing the turtle object via magnitude and direction with setheading() and forward(), its cleaner (and maybe faster) to simply give the direct coordinates of where the turtle should go.
Also for any newcomers: Keeping all of the constants like Canvas, Width, and Height outside the BackgroundColour() function speeds up your code since your computer doesn't have to recalculate or refetch any values every time the function is called.

Pygame - Store surface as image

This is just my second day at pygame, and I am not much aware about many functions. The following is a sample of what I am doing on my surface(window)
for c in nStations:
pygame.time.wait( 20 )
pygame.draw.circle( window, c.tColor, c.tPosition, c.iRadius )
pygame.display.update()
This is looped for n number of circles(in a tuple) to be displayed. Now, this will be repeated a total of 5-6 times. I wish to store/save the surface window as an image(or sub-surface) and put it as a thumbnail/link at the top-right-corner of my window.
Is it possible? Or is pygame not a good library for this? I tried working on cocos2d too, but it's documentation is very poor as compared to pygame.
In case my question didn't make any-sense anywhere, please reply. I'll keep updating it.
P.S. I've seen python.surface library functions. and surface.copy seems to be what I am looking for, but can I store the surface as a thumbnail/image before copying and editing it further?
EDIT->After reading jsbueno's reply, what I did:
tThumbs.append( pygame.transform.smoothscale( window, (32, 32) ) )
# the above statement inside drawing-circle loop
i = 1
for thumb in tThumbs:
window.blit( thumb, (1050, 36*i) )
pygame.display.update()
i += 1
pygame.time.wait( 200 )
The above seems to work perfectly for me. Thanks. :)
Indeed - all drawing and transofmr functions in Pygame accept a "surface" as the first parameter. Actually, in pygame, a "surface" is an image.
The display window is just a surface as well - although a specialized one, whose contents are reflected on screen. But you can use pygame.transform.smoothscale to get a thumbnail copy of your screen surface, and then, the blit method of your screen object itself to paste it on screen.
If this is going to be done continuoulsy, the pasted corner ould also be replicated, like a video camera pointing to its own output - so the "right way" to do it is: keep yoru drawing ina separate, in memory surface, blit it to the screen surface, and then blit the thumbnail above it.

How do I use gluLookAt properly?

I don't want to get into complex trigonometry to calculate rotations and things like that for my 3D world so gluLookAt seems like a nice alternative. According to the documentation all I need to do is place 3 coordinates for the cameras position, three for what I should be looking at and an "up" position. The last made no sense until I assumed it had to be at right angles with the line of sight in the direction the top of the screen should be.
It doesn't work like that at all. I have some python code. This is the code which initialises some data and some mode code for when I enter this part of the game:
def init(self):
self.game_over = False
self.z = -30
self.x = 0
def transfer(self):
#Make OpenGL use 3D
game.engage_3d(45,0.1,100)
gluLookAt(0,0,-30,0,0,0,0,1,0)
"game.engage_3d(45,0.1,100)" basically sets up the projection matrix to have a 45 degree angle of view and near and far coordinates of 0.1 and 100.
The first gluLookAt puts the camera in the correct position, nicely.
I have a cube drawn with the centre of (0,0,0) and it works fine without gluLookAt. Before I draw it I have this code:
gluLookAt(self.x,0,self.z,0,0,0,0,1,0)
if game.key(KEY_UP):
self.z += 2.0/game.get_fps()
if game.key(KEY_DOWN):
self.z -= 2.0/game.get_fps()
if game.key(KEY_LEFT):
self.x += 2.0/game.get_fps()
if game.key(KEY_RIGHT):
self.x -= 2.0/game.get_fps()
Now from that, the up position should always be the same as it's always at right angles. What I'd have thought it would do is move forward and back the z-axis with the up and down keys and left and right through the x-axis with the left and right keys. What actually happens, is when I use the left and right keys, the cube will rotate around the "eye" being accelerated by the keys. The up key causes another cube from nowhere to slice through the screen and hit the first cube. THe down key brings the mysterious cloned cube back. This can be combined with the rotation to give a completely different outcome as the documentation said would arise.
What on earth is wrong?
Thank you.
(The intuition behind the "up" vector in gluLookAt is simple: Look at anything. Now tilt your head 90 degrees. Where you are hasn't changed, the direction you're looking at hasn't changed, but the image in your retina clearly has. What's the difference? Where the top of your head is pointing to. That's the up vector.)
But to answer your question: gluLookAt calls should not be concatenated. In other words, the only pattern in which it's OK to use gluLookAt if you don't know exactly how it works is the following:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(...);
# do not touch the modelview matrix anymore!
It seems from your code that you're doing something like this:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(...);
# some stuff..
gluLookAt(...);
This will generate weird results, because gluLookAt multiplies the current matrix by the viewing matrix it computes. If you want to concatenate transformations you're really better off figuring out how to make glTranslate, glScale and glRotatef work for you. Even better, you should learn how the coordinate transformations work and stick to glMultMatrix.

Categories