I would like to have a rectangle move between 2 points, so when the rectangle reaches point A it then moves back to point B. I am able to create a rectangle and animate the rectangle to move to a point but I'm unsure how I set up an 'if statement' to read the rectangles position and move it back.
def animate_the_button(self, *kwargs):
self.rect = Rectangle(pos=(75,10), size=(10,50))
self.canvas.add(self.rect)
anim = Animation(pos=(75,700))
anim.start(self.rect)
To call a function or method when an animation completes, use bind() with on_complete:
anim.bind(on_complete=self.animate_way_back)
Where animate_way_back() would be a method that sends the rectangle on its way back. See the documentation on Animation.
But since you seem to want it to keep bouncing back and forth, a repeating sequence of animations seems a better fit:
anim = Animation(pos=(75,700)) + Animation(pos=(75,10))
anim.repeat = True
anim.start(self.rect)
Related
I am making a simple project in python pyglet that involves drawing a large amount of entities and tiles to the screen, typically ~77K tiles at a time, to do this, I use two batches and have every tile as a sprite, where its x,y position on the screen is its x,y position in the world.
The problem comes when I try to implement some sort of side-scrolling feature into it, to avoid asking an XY question, I figure I should just ask what the best way to do this is.
I have tried many ways to increase performance:
Moving around glViewport(), but batches do not draw entities outside of the original size.
Updating all the sprites co-ordinates every tick, which insanely slow
Drawing all sprites to another Texture, but I haven't found anything in the documentation about this, the blit_into method gives me "Cannot blit to a texture.
The camera class, update() is called every tick
class Camera:
def __init__(self):
self.world_sprites = []
self.world_x = 0
self.world_y = 0
def add_sprite(self, spr: tools.WorldSprite):
self.world_sprites.append(spr)
def update(self):
for spr in self.world_sprites:
spr.update_camera(self)
The update_camera method inside of the WorldSprite class
def update_camera(self, cam):
self._x = self.px - cam.world_x
self._y = self.py - cam.world_y
self._update_position()
It works, it's just very, very slow.
Sorry if this is a big question.
I am trying to create a simulator. (referring to John Zelle's graphics.py)
Basically, my object will make use of graphics.py to display the object as a circle. Then, using the .move method in the class in graphics.py, the object will move in the x direction and y direction. If the object is currently drawn, the circle is adjusted to the new position.
Moving just one object can easily be done with the following codes:
win = GraphWin("My Circle", 100, 100)
c = Circle(Point(50,50), 10)
c.draw(win)
for i in range(40):
c.move(30, 0) #speed=30
time.sleep(1)
win.close()
However, I want the program to display multiple circles at once that moves at different speed. I've created a Circle object class which takes speed as an input, and a list with 3 Circle objects in it
circle = []
circle1 = Car(40)
circle2= Car(50)
circle3 = Car(60)
In summary, my question is, how do make use of this list such that I am able to display and move multiple circles in one window at once using the methods available in graphics.py?
That all depends on how you create your Car class, but nothing stops you from using the same code to move multiple circles in the same refresh cycle, e.g.:
win = GraphWin("My Circle", 1024, 400)
speeds = [40, 50, 60] # we'll create a circle for each 'speed'
circles = [] # hold our circles
for speed in speeds:
c = Circle(Point(50, speed), 10) # use speed as y position, too
c.draw(win) # add it to the window
circles.append((c, speed)) # add it to our circle list as (circle, speed) pair
for i in range(40): # main animation loop
for circle in circles: # loop through the circles list
circle[0].move(circle[1], 0) # move the circle on the x axis by the defined speed
time.sleep(1) # wait a second...
win.close()
Of course, if you're already going to use classes, you might as well implement move() in it so your Car instances can remember their speed and then just apply it when you call move() on them in a loop.
I'm looking for the easiest way to implement this. I'm trying to implement platforms (with full collision detection) that you can draw in via mouse. Right now I have a line drawing function that actually draws small circles, but they're so close together that they more or less look like a line. Would the best solution be to create little pygame.Rect objects at each circle? That's going to be a lot of rect objects. It's not an image so pixel perfect doesn't seem like an option?
def drawGradientLine(screen, index, start, end, width, color_mode):
#color values change based on index
cvar1 = max(0, min(255, 9 * index-256))
cvar2 = max(0, min(255, 9 * index))
#green(0,255,0), blue(0,0,255), red(255,0,0), yellow(255,255,0)
if color_mode == 'green':
color = (cvar1, cvar2, cvar1)
elif color_mode == 'blue':
color = (cvar1, cvar1, cvar2)
elif color_mode == 'red':
color = (cvar2, cvar1, cvar1)
elif color_mode == 'yellow':
color = (cvar2, cvar2, cvar1)
dx = end[0] - start[0]
dy = end[1] - start[1]
dist = max(abs(dx), abs(dy))
for i in xrange(dist):
x = int(start[0]+float(i)/dist*dx)
y = int(start[1]+float(i)/dist*dy)
pygame.draw.circle(screen, color, (x, y), width)
That's my drawing function. And here's my loop that I have put in my main game event loop.
i = 0
while (i < len(pointList)-1):
drawGradientLine(screen, i, pointList[i], pointList[i + 1], r, mode)
i += 1
Thanks for any help, collision detection is giving me a huge headache right now (still can't get it right for my tiles either..).
Any reason you want to stick with circles?
Rectangles will make the line/rectangle a lot more smooth and will make collision detecting a lot easier unless you want to look into pixel perfect collision.
You also don't seem to save your drawn objects anywhere (like in a list or spritegroup), so how are you going to check for collision?
Here's a leveleditor I did for game awhile back, it's not perfect, but it works:
https://gist.github.com/marcusmoller/bae9ea310999db8d8d95
How it works:
The whole game level is divided up into 10x10px grid for easier drawing
The leveleditor check if the mouse is being clicked and then saves that mouse position
The player now moves the mouse to another position and releases the mouse button, the leveleditor now saves that new position.
You now have two different coordinates and can easily make a rectangle out of them.
Instead of creating a whole bunch of rect objects to test collision against, I'm going to recommend creating something called a mask of the drawn-in collideable object, and test for collision against that. Basically, a mask is a map of which pixels are being used and which are not in an image. You can almost think of it as a shadow or silhouette of a surface.
When you call pygame.draw.circle, you are already passing in a surface. Right now you are drawing directly to the screen, which might not be as useful for what I'm suggesting. I would recommend creating a rect which covers the entire area of the line being drawn, and then creating a surface of that size, and then draw the line to this surface. My code will assume you already know the bounds of the line's points.
line_rect = pygame.Rect(leftmost, topmost, rightmost - leftmost, bottommost - topmost)
line_surf = pygame.Surface((line_rect.width, line_rect.height))
In your drawGradientLine function, you'll have to translate the point coordinates to the object space of the line_surf.
while (i < len(pointList)-1):
drawGradientLine(line_surf, (line_rect.x, line_rect.y), i, pointList[i], pointList[i+1], r, mode)
i += 1
def drawGradientLine(surf, offset, index, start, end, width, color_mode):
# the code leading up to where you draw the circle...
for i in xrange(dist):
x = int(start[0]+float(i)/dist*dx) - offset[0]
y = int(start[1]+float(i)/dist*dy) - offset[1]
pygame.draw.circle(surf, color, (x, y), width)
Now you'll have a surface with the drawn object blitted to it. Note that you might have to add some padding to the surface when you create it if the width of the lines you are drawing is greater than 1.
Now that you have the surface, you will want to create the mask of it.
surf_mask = pygame.mask.from_surface(line_surf)
Hopefully this isn't getting too complicated for you! Now you can either check each "active" point in the mask for collision within a rect from your player (or whatever other objects you want to collide withe drawn-in platforms), or you can create a mask from the surface of such a player object and use the pygame.Mask.overlap_area function to check for pixel-perfect collision.
# player_surf is a surface object I am imagining exists
# player_rect is a rect object I am imagining exists
overlap_count = surf_mask.overlap_area(player_surf, (line_rect.x - player_rect.x, line_rect.y - player_rect.y))
overlap_count should be a count of the number of pixels that are overlapping between the masks. If this is greater than zero, then you know there has been a collision.
Here is the documentation for pygame.Mask.overlap_area: http://www.pygame.org/docs/ref/mask.html#pygame.mask.Mask.overlap_area
I have been experimenting with Pygame, and have come across a problem that I could not find the answer to. In this paste, my basic game framework is exhibited. How can i complete my ballSnapLeft() definition correctly?
Edit: I am not looking for my code to be completed, but I am looking for someone to explain how the 'Rect' class(?) works, and how it could be applied.
Edit2: I have tried to use the x and y coordinates to do so, but I think there is a simpler way that can actually work, instead of using brute coordinates.
From Making Games With Python and Pygame:
myRect.left The int value of the X-coordinate of the left side of the
rectangle.
myRect.right
The int value of the X-coordinate of the right side of the rectangle.
myRect.top
The int value of the Y-coordinate of the top side of the rectangle.
myRect.bottom
The int value of the Y-coordinate of the bottom side.
Because all of these attributes return integers, that's probably why your code isn't working.
Also, if your goal with ballSnapLeft() is to move the ball to a position away from the player, ballRect.right = playerRect.left - distance would only change the X coordinate of the rect. To make the ball also move in the Y coordinate you could do something like
def ballSnapTop():
ballRect.top = playerRect.bottom - distance
Are you getting an error when you execute ballRect.right = playerRect.left - (0, 1)?
ballRect.right and ballRect.left, along with the related top, bottom, width, height values, are int types and can't have tuples added or subtracted from them.
You might want to take a look at the pygame.Rect documentation, and consider using pygame.Rect.move(x,y) which will shift the coordinates of the rectangle for you.
It's also worth noting that if you change, for example, myRect.topleft, then the corresponding top, left, bottom, etc... values will change as well so that the rect translates and preserves its size.
What would be the best way to use negative coordinates in pygame?
At the moment I have a surface that is 1.5 times the original surface then everything that needs to be drawn is shifted up by a certain amount (to ensure the negative coordinates become positive) and drawn.
Is there an easier/alternate way of doing this?
A simple solution is to write a linear mapping function from world coordinates to pygame screen coordinates
def coord(x,y):
"Convert world coordinates to pixel coordinates."
return 320+170*x, 400-170*y
and use it when drawing all world objects. Have a look here for a complete example.
There is no way to move the origin of a surface from 0,0.
Implement your own drawing class which transforms all the coordinates passed in into the space of the surface.
If it's similar to an RPG map situation, where you have world coordinates and screen coordinates:
use a function that translates world to local, and vice versa.
But I wasn't sure I'd you were looking for Rect's properties?
rect.bottomright = (width, height) # bottom right at window corner
If you want to use center coordinates to blit, vs top left being (0,0)
ship.rect.center = (20, 30) # don't need to translate by adding w/2 to topleft
See also: Rect.move_ip()