How would you change the direction of a rotating image/rect in Pygame? Applying positive and negative degree values works but it seems to only be able to rotate one direction throughout my window. Is there a way to ensure a change in direction of rotation?
Perhaps change up rotation of a spinning image every 5 seconds, or if able to change the direction of the spin when hitting a X or Y axis.
I've added some code below.
It seems like switching movement directions is easy with rect.move_ip as long as I specify a speed and have location clause, it does what I want. Unfortunately rotation is't like that. Here I'l adding angles to make sure it spins, but no matter what I try, I'm unable to negate the rotation.
def rotate_image(self): #rotate image
orig_rect = self.image.get_rect()
rot_image = pygame.transform.rotate(self.image, self.angle)
rot_rect = orig_rect.copy()
rot_rect.center = rot_image.get_rect().center
rot_image = rot_image.subsurface(rot_rect).copy()
return rot_image
def render(self):
self.screen.fill(self.bg_color)
self.rect.move_ip(0,5) #Y axis movement at 5 px per frame
self.angle += 5 #add 5 anglewhen the rect has not hit one of the window
self.angle %= 360
if self.rect.left < 0 or self.rect.right > self.width:
self.speed[0] = -self.speed[0]
self.angle = -self.angle #tried to invert the angle
self.angle -= 5 #trying to negate the angle rotation
self.angle %= 360
self.screen.blit(self.rotate_image(),self.rect)
pygame.display.flip()
I would really like to know how to invert rotation of a image. You may provide your own examples.
As far as I know, there is only one way of rotating the image using
pygame.transform.rotate(surface, angle)
You can check the official documentation
http://www.pygame.org/docs/ref/transform.html
Here is the example code:-
screen = pygame.display.set_mode((640,480))
surf = pygame.image.load("/home/image.jpeg").convert()
while True:
newsurf = pygame.transform.rotate(surf, -90)
screen.blit(newsurf, (100,100))
pygame.display.flip()
time.sleep(2)
This code will keep on rotating the image after every 2 seconds by 90 degrees in clockwise direction.
Hope this helps..
Related
I have a problem with my pygame script in which the way I check for collisions doesn't seem to work with the x axis and presents some very frustrating and downright stupid errors. The way it works is I first check for movement in the axis of choice, here is an example in the y axis. I then get a list of tiles I collide with and check to see if there are any collisions and the tile's collision index is for the top of itself:
if self.velocity.y > 0:
collide_rect = pygame.sprite.spritecollide(self, self.tile_group, False)
if collide_rect and collide_rect[0].top_collide == True:
self.position.y = collide_rect[0].rect.top + 1
self.velocity.y = 0
The last two lines set the position of the player, set at the bottom left of it's rectangle, to the top of the tile + 1 pixel to avoid jittering. I then set the y velocity to 0. But here is the code I have for the x axis:
if self.velocity.x > 0:
collide_rect = pygame.sprite.spritecollide(self, self.tile_group, False)
if collide_rect and collide_rect[0].left_collide == True:
self.rect.right = collide_rect[0].rect.left
If I run this code and collide to the left of a tile, I stop for a moment and go through the tile. If I also set the x velocity to zero, the same thing happens. If I set the x velocity to -5, the player jitters inside of the tile and in the ground as well. And worst of all, if the tile has top and side collision, the player glitches out on top of it due to how the player is technically inside the tile to avoid the horrible jittering the collision causes otherwise, and ends up being shot to the left most area of the tile. This is most frustrating as I just want to make a platformer game in pygame, like Mario or Sonic, with basic collision that makes sense.
I've searched google and stack overflow for answers to my problem, but the most I found was a tutorial for collision in the y axis, which works for me just fine. The second best was saying basically just search up proper game design docs, which is the most unhelpful advice I have heard since that's why I'm here. (BTW, I use a 2D vector system for my movement) The expected result I wish to have is the player staying in place when running up against a wall without clipping into the ground or wall visibly. Here is the link to the file I'm using to create this.
Similar questions have been asked many times, and the answer is still the same. You need to separate the motion and collision detection along the X-axis and Y-axis. First, perform the fall and jump and limit the vertical position according to the obstacles. Then move the player horizontally and limit the horizontal position of the player according to the obstacles.
If you do not separate the directions, the direction you treat first always "wins". e.g. if you fall (even minimally) and move to the left at the same time, the player is always automatically levitated onto each object, since the vertical collision control also detects a collision and lifts the player to the next level.
Minimal example based on your code:
class Player(pygame.sprite.Sprite):
# [...]
def update(self):
self.acceleration = vector(0, self.VERTICAL_ACCELERATION)
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
self.acceleration.x -= self.HORIZONTAL_ACCELERATION
if keys[pygame.K_d]:
self.acceleration.x += self.HORIZONTAL_ACCELERATION
self.acceleration.x -= self.velocity.x * self.HORIZONTAL_FRICTION
self.velocity += self.acceleration
# Motion and collision detection along the y-axis
self.position.y += self.velocity.y + .5 * self.acceleration.y
self.rect.bottomleft = self.position
collide_rect = pygame.sprite.spritecollide(self, self.tile_group, False)
if collide_rect:
if self.velocity.y > 0:
self.velocity.y = 0
self.position.y = collide_rect[0].rect.top
self.rect.bottom = self.position.y
elif self.velocity.y < 0:
self.velocity.y = 0
self.rect.top = collide_rect[0].rect.bottom
self.rect.bottom = self.position.y
# Motion and collision detection along the x-axis
self.position.x += self.velocity.x + .5 * self.acceleration.x
self.rect.bottomleft = self.position
collide_rect = pygame.sprite.spritecollide(self, self.tile_group, False)
if collide_rect:
if self.velocity.x > 0:
self.rect.right = collide_rect[0].rect.left
self.position.x = self.rect.x
self.velocity.x = 0
elif self.velocity.x < 0:
self.rect.left = collide_rect[0].rect.right
self.position.x = self.rect.x
self.velocity.x = 0
def jump(self):
self.rect.y += 1
if pygame.sprite.spritecollide(self, self.tile_group, False):
print("jump")
self.velocity.y -= 10
self.rect.y -= 1
I am making a game where there are two players and they can shoot each other. Their movement will be defined by a rotation around a fixed point, the point will be(600, 300), which is the center of our screen. The player will keep rotating around the point as long as they are pressing a certain button(which is keep providing force to our player) else they will fall(due to gravity). I think it would help to think of it as a ball attached to a point using a string. The string is attached as long as a button is pressed and gets unattached as soon as the button is released and the ball flies off. Here is my player class
class Player:
def __init__(self):
self.pos = [500, 200]
self.width = 30
self.height = 30
self.player = pygame.image.load("player.png").convert_alpha()
self.player = pygame.transform.scale(self.player, (self.width, self.height))
self.rect = self.player.get_rect()
self.rotated_player = None
self.anguler_vel = 0
self.Fg = 0.05
self.Fp = 0
self.arm_length = 0
Fp is the force perpendicular to the force of gravityFg. Fg is the force which is pulling it down on our player. Fp is defined by math.sin(theta) * Fg. I am keeping track of Fp because i want the player to keep moving in the direction of rotation after its unattatched from the string. arm_length is the length of the string.
I have a Point class, which is the point about which our player will rotate. Here's the point class.
class Point:
def __init__(self,x, y):
self.pos = [x, y]
dx = self.pos[0] - player.pos[0]
dy = self.pos[1] - player.pos[1]
self.angle = math.atan2(dy, dx)
Now, i need help with the actual rotation itself. I am aware that adding a certain value to the angle every single frame would make it go around. But how would i make it go around a certain point that i specify and how would the arm length tie into this?. I find that it is really difficult to implement all of this because the y-axis is flipped and all the positional values have to be scaled down when using them in calculations because of the FPS rate. Any help on how this is done would be appreciated as well. Thanks
When you use pygame.transform.rotate the size of the new rotated image is increased compared to the size of the original image. You must make sure that the rotated image is placed so that its center remains in the center of the non-rotated image. To do this, get the rectangle of the original image and set the position. Get the rectangle of the rotated image and set the center position through the center of the original rectangle. e.g.:
def rotate_center(image, rect, angle):
rotated_image = pygame.transform.rotate(image, angle)
new_rect = rotated_image.get_rect(center = rect.center)
return rotated_image, new_rect
screen.blit(*rotate_center(image, image_rect, angle))
Alos see How do I rotate an image around its center using PyGame? and How to rotate an image(player) to the mouse direction?
I would like to position my image and rect, but they are already moving back and forth. Can I still manually position their start point at this stage, when they are already on the move?
Currently the Image bounces back and forth on the screen. I would like to position the start point of this motion to an arbitrary point, say (0,100) of my window. Is there a way to set this positioning of my image and rect so that it can keep it's movement but just start from another point? Currently it's stuck on the top, I've tried a few things that didn't work:
self.image = pygame.image.load('picture.jpg')
self.rect = self.image.get_rect()
self.screen.fill(self.bg_color)
self.rect.move_ip(5,0) #only moving on the x axis
if self.rect.left < 0 or self.rect.right > self.width:
self.speed[0] = -self.speed[0]
self.screen.blit(self.image,self.rect)
pygame.display.flip()
Is there a way where I can set a starting position for my Surface object and rectangle?
Sure. Just do
self.rect.move_ip(0, 100)
to move to (0,100).
See the pygame.Rect docs for more.
This question already has answers here:
Add scrolling to a platformer in pygame
(4 answers)
Closed 4 years ago.
So, I'm making a 2d topviewed game in Python with Pygame. I've been trying to create a camera movement that would keep the player in the center of the screen. How would I do this? I'd like to have the "map" in a single surface, that will be blit to the screen surface. If doing so I could just build the map once and then just somehow adjust it's position so that the player will always stay in the center of the screen. My player updates it's position like this:
def update(self, dx=0, dy=0):
newpos = (self.pos[0] + dx, self.pos[1] + dy) # Calculates a new position
entityrect = pygame.Rect(newpos, self.surface.get_size()) # Creates a rect for the player
collided = False
for o in self.objects: # Loops for solid objects in the map
if o.colliderect(entityrect):
collided = True
break
if not collided:
# If the player didn't collide, update the position
self.pos = newpos
return collided
I found this, but that was for a sideviewed platformer. So my map would look something like this:
map1 = pygame.Surface((3000, 3000))
img = pygame.image.load("floor.png")
for x in range(0, 3000, img.get_width()):
for y in range(0, 3000, img.get_height()):
map1.blit(img, (x, y))
So how would I do the camera movement? Any help would be appreciated.
PS. I hope you can understand what I'm asking here, english is not my native language. =)
Well, you didn't show how you draw your map or your player, but you can do something like this:
camera = [0,0]
...
def update(self, dx=0, dy=0):
newpos = (self.pos[0] + dx, self.pos[1] + dy) # Calculates a new position
entityrect = pygame.Rect(newpos, self.surface.get_size())
camera[0] += dx
camera[1] += dy
...
Then you draw your map like this
screen.blit(map1, (0,0),
(camera[0], camera[1], screen.get_width(), screen.get_height())
)
This way the map will scroll in the opposite direction of the camera, leaving the player still.
If you wan't the player to move in your world, but not to move in the screen, you can do something like this:
screen.blit(player, (player.pos[0]-camera[0], player.pos[1]-camera[1]))
I'm making a puzzle game that requires the user to 'draw' circles onto a background to get a ball to the exit. They create circles by holding their mouse button, the circle grows; when it is big enough, they let go and it is 'punched' into the physical space and balls then react to it.
I have a problem, however, that when two circles are intersecting (so a ball should pass through), if the intersection is not larger than the diameter of the ball the ball collides with the interior of the circle as usual.
This may be a little hard to comprehend, so here's a link to the screencast showing the problem (You can't embed videos on Stack Overflow): http://www.youtube.com/watch?v=3dKyPzqTDhs
Hopefully that made my problem clear. Here is the Python / PyGame code for the Ball and Circle classes:
class Ball():
def __init__(self, (x,y), size, colourID):
"""Setting up the new instance"""
self.x = x
self.y = y
self.size = size
self.exited = False
self.colour = setColour(colourID)
self.thickness = 0
self.speed = 0.01
self.angle = math.pi/2
def display(self, surface):
"""Draw the ball"""
# pygame.gfxdraw.aacircle(screen,cx,cy,new_dist,settings['MINIMAP_RINGS'])
if self.exited != True:
pygame.draw.circle(surface, self.colour, (int(self.x), int(self.y)), self.size, self.thickness)
def move(self):
"""Move the ball according to angle and speed"""
self.x += math.sin(self.angle) * self.speed
self.y -= math.cos(self.angle) * self.speed
(self.angle, self.speed) = module_physicsEngine.addVectors((self.angle, self.speed), gravity)
self.speed *= drag
And the Circle class:
class Circle():
def __init__(self, (x,y), size, colourID):
"""Set up the new instance of the Circle class"""
self.x = x
self.y = y
self.size = size
self.colour = setColour(colourID)
self.thickness = 2
self.angle = 0 # Needed for collision...
self.speed = 0 # detection against balls
def display(self, surface):
"""Draw the circle"""
pygame.draw.circle(surface, self.colour, (int(self.x), int(self.y)), self.size, self.thickness)
Within the main loop of the game (while running == True: etc.), this code is used to perform actions on each ball:
for b in balls:
b.move()
for i, ball in enumerate(balls):
for ball2 in balls[i+1:]:
collideBalls(ball, ball2)
collideCircle(b) # <---------------- This is the important line
collideExit(b)
b.display(screen)
And finally, the collideCircle(b) function, which is called once per ball to check for collisions with the interior of a circle, and also to check if the circles are intersecting.
def collideCircle(ball):
"""Check for collision between a ball and a circle"""
hit = False
closestDist = 0
for c in circles:
# Code cannot be replaced with physicsEngine.collideTest because it
# is slightly differnt, testing if ball [ball] inside a circle [c]
dx = c.x - ball.x
dy = c.y - ball.y
distance = math.hypot(dx, dy)
if distance <= c.size - ball.size:
# If BALL inside any CIRCLE
hit = False
break
else:
# If we're outside of a circle.
if closestDist < c.size - (distance - ball.size):
hit = c
closestDist = (c.size - (distance - ball.size))
if hit:
module_physicsEngine.circleBounce(hit, ball)
Ok, so I know that this has been a bit of a long and talky question, but I think you have all the information needed. Is the solution to make the balls interact correctly something to do with the line if distance <= c.size - ball.size:?
Anyway, thanks in advance!
Nathan out.
TL;DR - Watch the youtube video, and let me know why it's not working.
The problem is with unintended hits rather than missed ones. What you really want to check is if all parts of the ball are covered by some circle, while the check you're doing is if any circle only partially overlaps - but an override if any circle fully covers the ball.
I figure for any potential hit point, i.e. closest inner wall of a circle, let that point "walk" along the wall by checking its distance from all other circles. Should it then leave the ball, it was a false hit.
First you find the list of circles that touch the ball at all. As before, if any of them cover it, you can skip the rest of the checks. Also find the closest wall point to the ball for the circles. For each of those closest wall points, if it overlaps another circle, move it to the intersection point which is closest to the ball but further away than the current point. Discard it if it's outside the ball. Repeat the procedure for all circles, since more than two may overlap. Also note that the moving of the point may cause it to enter new circles.
You could precompute the intersection points and discard any that are a ball radius inside of any other circle.
This can surely be improved on, but it's a start, I think. I suspect a bug involving the case when both intersection points of a pair of circles overlap the ball, but a walk chain leads one of them outside the ball. Perhaps the initial collision points should be replaced only by both intersection points, not the closest.
I watched the video and I like the game principle. :)
Maybe the problem is that you break out of the loop as soon as you encounter a circle that encloses the ball. I'm referring to the snippet
if distance <= c.size - ball.size:
# If BALL inside any CIRCLE
hit = False
break
Why would you not check all of the other circles, in that case? There might be another circle yet unchecked that causes a hit.
Btw, I wouldn't say if condition == True:, that's unpythonic. Just say if condition:.