so I have this gun here where it rotates towards my mouse x, and mouse y position but the problem (VIDEO) is the gun turns upside down. Is there a way I could make the image of my gun not upside down and do the same movement as my gun facing the right side? Like the gun for the left side is upside down for some reason and I don't have an idea on how to do this at all
Gun Image:
my gun draw(self)
def draw(self,drawX,drawY):
self.rect.topleft = (drawX,drawY)
# the gun's hitbox
# rotating the gun
dx = self.look_at_pos[0] - self.rect.centerx
dy = self.look_at_pos[1] - self.rect.centery
angle = (180/math.pi) * math.atan2(-dy, dx)
gun_size = self.image.get_size()
pivot = (8, gun_size[1]//2)
blitRotate(window, self.image, self.rect.center, pivot, angle)
def lookAt( self, coordinate ):
self.look_at_pos = coordinate
my full gun class
class handgun():
def __init__(self,x,y,height,width,color):
self.x = x
self.y = y
self.height = height
self.width = width
self.color = color
self.rect = pygame.Rect(x,y,height,width)
# LOL THESE IS THE HAND
self.shootsright = pygame.image.load("hands.png")
self.image = self.shootsright
self.rect = self.image.get_rect(center = (self.x, self.y))
self.look_at_pos = (self.x, self.y)
self.isLookingAtPlayer = False
self.look_at_pos = (x,y)
self.hitbox = (self.x + -18, self.y, 46,60)
def draw(self,drawX,drawY):
self.rect.topleft = (drawX,drawY)
# the gun's hitbox
# rotating the gun
dx = self.look_at_pos[0] - self.rect.centerx
dy = self.look_at_pos[1] - self.rect.centery
angle = (180/math.pi) * math.atan2(-dy, dx)
gun_size = self.image.get_size()
pivot = (8, gun_size[1]//2)
blitRotate(window, self.image, self.rect.center, pivot, angle)
def lookAt( self, coordinate ):
self.look_at_pos = coordinate
this is where the rotation happens like how my gun will rotate:
def blitRotate(surf, image, pos, originPos, angle):
# calcaulate the axis aligned bounding box of the rotated image
w, h = image.get_size()
sin_a, cos_a = math.sin(math.radians(angle)), math.cos(math.radians(angle))
min_x, min_y = min([0, sin_a*h, cos_a*w, sin_a*h + cos_a*w]), max([0, sin_a*w, -cos_a*h, sin_a*w - cos_a*h])
# calculate the translation of the pivot
pivot = pygame.math.Vector2(originPos[0], -originPos[1])
pivot_rotate = pivot.rotate(angle)
pivot_move = pivot_rotate - pivot
# calculate the upper left origin of the rotated image
origin = (pos[0] - originPos[0] + min_x - pivot_move[0], pos[1] - originPos[1] - min_y + pivot_move[1])
# get a rotated image
rotated_image = pygame.transform.rotate(image, angle)
# rotate and blit the image
surf.blit(rotated_image, origin)
my full code: script
andy help is appreciated thank you!
You need to mirror the gun image once it turns past 90 degrees anti-clockwise from the positive x-axis.
You can do this using pygame.transform.flip() which takes in three parameters:
The surface objects (In this case, your gun image)
Whether the image should be flipped horizontally (No)
Whether the image should be flipped vertically (Yes)
So you'll want to have a variable which indicates where the gun is facing:
gunDirection = "right"
And also code which will flip the image whenever the gun changes direction:
if(angle > 90 and gunDirection != "left"):
gunDirection = "left"
self.image = pygame.transform.flip(self.image, False, True)
if(angle < 90 and gunDirection != "right"):
gunDirection = "right"
self.image = pygame.transform.flip(self.image, False, True)
This should all be in the gun class, if you're using radians the angle would be 0.5pi
Related
This question already has answers here:
How do I rotate an image around its center using Pygame?
(6 answers)
Closed last year.
I have created a class for the zombie I blit into the screen, but the rotation does not work at all, it rotates a lot and moves the image around, is there some way to rotate the image around its center? how could I change the rotate def so that it works properly?
class zombieObj:
def __init__(self, x, y, vel, angle):
tempZombie = pygame.image.load('zombieSprite.png')
self.zombieSpriteSurface = pygame.transform.scale(tempZombie, (64, 64))
self.x = x
self.y = y
self.vel = 1
self.angle = angle
def rotate(self, image, angle):
self.zombieSpriteSurface = pygame.transform.rotate(image, angle)
and this is how I called it in the loop:
zombieSprite.angle = zombieSprite.angle + 5
zombieSprite.rotate(zombieSprite.zombieSpriteSurface, zombieSprite.angle)
See How do I rotate an image around its center using PyGame?. The trick is to get the center of the image before rotation and set it after rotation. Also, you need to rotate the original image to avoid distortion:
class ZombieObj:
def __init__(self, x, y, vel, angle):
tempZombie = pygame.image.load('zombieSprite.png')
self.originalImage = pygame.transform.scale(tempZombie, (64, 64))
self.vel = 1
self.angle = 0
self.rect = self.originalImage.get_rect(topleft = (x, y))
self.roatate(angle)
def rotate(self, angle):
self.angle = angle
self.zombieSpriteSurface = pygame.transform.rotate(self.originalImage, self.angle)
self.rect = self.zombieSpriteSurface.get_rect(center = self.rect.center)
If you want to increase the rotation by a certain angle, you need to add the additional angle to the current angle:
class ZombieObj:
# [...]
def rotate(self, deltaAngle):
self.angle += deltaAngle
self.zombieSpriteSurface = pygame.transform.rotate(self.originalImage, self.angle)
self.rect = self.zombieSpriteSurface.get_rect(center = self.rect.center)
When I draw the image and the rect Wof the images, then the upper left corner of the rectangle is exactly in the center of the image.
def blitRotateCenter(image, left, top, angle):
rotated_image = pygame.transform.rotate(image, angle)
new_rect = rotated_image.get_rect(center = image.get_rect(center = (left, top)).center)
screen.blit(rotated_image, new_rect)
self.image = pygame.image.load("Bilder/car.png")
self.rect = self.image.get_rect()
blitRotateCenter(auto.image, auto.rect.x, auto.rect.y, auto.wagen_winkel)
draw.rect(screen,red,auto.rect)
Just return new_rect from blitRotateCenter and use it to draw the rectangle:
def blitRotateCenter(image, left, top, angle):
rotated_image = pygame.transform.rotate(image, angle)
new_rect = rotated_image.get_rect(center = image.get_rect(center = (left, top)).center)
screen.blit(rotated_image, new_rect)
return new_rect
new_auto_rect = blitRotateCenter(auto.image, auto.rect.x, auto.rect.y, auto.wagen_winkel)
draw.rect(screen,red, new_auto_rect)
However, if you want to draw a rotated rectangle, see Getting rotated rect of rotated image in Pygame.
I am trying to create a pygame environment with various shapes of sprites but my code seems not working. Here is what I have:
class Object(pygame.sprite.Sprite):
def __init__(self, position, color, size, type):
# create square sprite
pygame.sprite.Sprite.__init__(self)
if type == 'agent':
self.image = pygame.Surface((size, size))
self.image.fill(color)
self.rect = self.image.get_rect()
else:
red = (200,0,0)
self.image = pygame.display.set_mode((size, size))
self.image.fill(color)
self.rect = pygame.draw.circle(self.image, color,(), 20)
# initial conditions
self.start_x = position[0]
self.start_y = position[1]
self.state = np.asarray([self.start_x, self.start_y])
self.rect.x = int((self.start_x * 500) + 100 - self.rect.size[0] / 2)
self.rect.y = int((self.start_y * 500) + 100 - self.rect.size[1] / 2)
Does anyone notice any issues with the Object class?
You have to create a pygame.Surface, instead of creating a new window (pygame.display.set_mode).
The pixel format of the Surface must include a per-pixel alpha (SRCALPHA). The center point of the circle must be the center of the Surface. The radius
must be half the size of the Surface:
self.image = pygame.Surface((size, size), pygame.SRCALPHA)
radius = size // 2
pygame.draw.circle(self.image, color, (radius, radius), radius)
Class Object:
class Object(pygame.sprite.Sprite):
def __init__(self, position, color, size, type):
# create square sprite
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((size, size), pygame.SRCALPHA)
self.rect = self.image.get_rect()
if type == 'agent':
self.image.fill(color)
else:
radius = size // 2
pygame.draw.circle(self.image, color, (radius, radius), radius)
# initial conditions
self.start_x = position[0]
self.start_y = position[1]
self.state = np.asarray([self.start_x, self.start_y])
self.rect.x = int((self.start_x * 500) + 100 - self.rect.size[0] / 2)
self.rect.y = int((self.start_y * 500) + 100 - self.rect.size[1] / 2)
This question already has answers here:
How do I rotate an image around its center using Pygame?
(6 answers)
How do I make my player rotate towards mouse position?
(1 answer)
Closed 2 years ago.
I am trying to make my sprites rotate towards the player, but the rotation is out of place. Here is a video of the behaviour.
I am not sure how to fix this,
Sprite class
#-------------------------------- enemy shoots left and right
shotsright = pygame.image.load("canss.png")
class enemyshoot:
def __init__(self,x,y,height,width,color):
self.x = x
self.y =y
self.height = height
self.width = width
self.color = color
self.rect = pygame.Rect(x,y,height,width)
self.health = 10
self.hitbox = (self.x + -20, self.y + 30, 31, 57)
#-------------------------------------------------------
# Make a Reference Copy of the bitmap for later rotation
self.shootsright = pygame.image.load("canss.png")
self.shootsright = pygame.transform.scale(self.shootsright,(self.shootsright.get_width()-150,self.shootsright.get_height()-150))
self.image = self.shootsright
self.rect = self.image.get_rect()
self.position = pygame.math.Vector2( (200, 180) )
self.isLookingAtPlayer = False
def draw(self):
self.rect.topleft = (self.x,self.y)
window.blit(self.image, self.rect)
self.hits = (self.x + 20, self.y, 28,60)
pygame.draw.rect(window, (255,0,0), (self.hitbox[0], self.hitbox[1] - 60, 100, 10)) # NEW
pygame.draw.rect(window, (0,255,0), (self.hitbox[0], self.hitbox[1] - 60, 100 - (5 * (10 - self.health)), 10))
self.hitbox = (self.x + 200, self.y + 200, 51, 65)
def lookAt( self, coordinate ):
# Rotate image to point in the new direction
delta_vector = coordinate - self.position
radius, angle = delta_vector.as_polar()
self.image = pygame.transform.rotate(self.shootsright, -angle)
# Re-set the bounding rectangle and position since
# the dimensions and centroid will have (probably) changed.
current_pos = self.rect.center
self.rect = self.image.get_rect()
self.rect.center = current_pos
black = (0,0,0)
enemyshooting = []
platformGroup = pygame.sprite.Group
platformList = []
level = [" p p p p p ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",]
for iy, row in enumerate(level):
for ix, col in enumerate(row):
if col == "p":
new_platforms = enemyshoot(ix*10, iy*50, 10,10,(255,255,255))
enemyshooting.append(new_platforms)
this is the rotating part
class enemyshoot:
def __init__(self,x,y,height,width,color):
# [................]
#-------------------------------------------------------
# Make a Reference Copy of the bitmap for later rotation
self.shootsright = pygame.image.load("canss.png")
self.shootsright = pygame.transform.scale(self.shootsright,(self.shootsright.get_width()-150,self.shootsright.get_height()-150))
self.image = self.shootsright
self.rect = self.image.get_rect()
self.position = pygame.math.Vector2( (200, 180) )
def lookAt( self, coordinate ):
# Rotate image to point in the new direction
delta_vector = coordinate - pygame.math.Vector2(self.rect.center)
radius, angle = delta_vector.as_polar()
self.image = pygame.transform.rotate(self.shootsright, -angle)
# Re-set the bounding rectangle and position since
# the dimensions and centroid will have (probably) changed.
current_pos = self.rect.center
self.rect = self.image.get_rect()
self.rect.center = current_pos
This is where I call the LookAt function to face the player:
# so instead of this
for enemyshoot in enemyshooting:
if not enemyshoot.isLookingAtPlayer:
enemyshoot.lookAt((playerman.x, playerman.y))
The rotation is out of place, and I can't figure out how to fix it. I am trying to make the mouth of the cannon rotate towards the player because that's where the bullets will append from.
I concur with everything #Rabbid76 says in the above answer.
I suspect part of your problem may be that the "human readable" part of the bitmap is not centered around the bitmap's centroid. Thus when rotated, it "sweeps" through an arc, rather than being rotated "around itself". (The preserving of the centre co-ordinate is an important step here to maintain a smooth rotation about the centroid of the object).
Consider the two bitmaps (the image on the right has a large 3/4 transparent section top-left):
Both are rotated around their centroid, but since the visible part on the 2nd image is not centred, it rotates weirdly.
So ensure your actual bitmap is centred within itself.
Reference Code:
import pygame
import random
# Window size
WINDOW_WIDTH = 800
WINDOW_HEIGHT = 400
WINDOW_SURFACE = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE
DARK_BLUE = ( 3, 5, 54 )
class RotationSprite( pygame.sprite.Sprite ):
def __init__( self, image, x, y, ):
pygame.sprite.Sprite.__init__(self)
self.original = image
self.image = image
self.rect = self.image.get_rect()
self.rect.center = ( x, y )
# for maintaining trajectory
self.position = pygame.math.Vector2( ( x, y ) )
self.velocity = pygame.math.Vector2( ( 0, 0 ) )
def lookAt( self, co_ordinate ):
# Rotate image to point in the new direction
delta_vector = co_ordinate - self.position
radius, angle = delta_vector.as_polar()
self.image = pygame.transform.rotozoom( self.original, -angle, 1 )
# Re-set the bounding rectagle
current_pos = self.rect.center
self.rect = self.image.get_rect()
self.rect.center = current_pos
### initialisation
pygame.init()
pygame.mixer.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), WINDOW_SURFACE )
pygame.display.set_caption( "Rotation Example" )
### Missiles!
rotation_sprites = pygame.sprite.Group()
rotation_image1 = pygame.image.load( 'rot_offcentre_1.png' )
rotation_image2 = pygame.image.load( 'rot_offcentre_2.png' )
rotation_sprites.add( RotationSprite( rotation_image1, WINDOW_WIDTH//3, WINDOW_HEIGHT//2 ) )
rotation_sprites.add( RotationSprite( rotation_image2, 2*( WINDOW_WIDTH//3 ), WINDOW_HEIGHT//2 ) )
### Main Loop
clock = pygame.time.Clock()
done = False
while not done:
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
done = True
elif ( event.type == pygame.MOUSEBUTTONUP ):
# On mouse-click
pass
# Record mouse movements for positioning the paddle
mouse_pos = pygame.mouse.get_pos()
for m in rotation_sprites:
m.lookAt( mouse_pos )
rotation_sprites.update()
# Update the window, but not more than 60 FPS
window.fill( DARK_BLUE )
rotation_sprites.draw( window )
pygame.display.flip()
# Clamp FPS
clock.tick_busy_loop(60)
pygame.quit()
self.position is set in the constructor, but it is not updated. Use the self.rect.center to compute the direction vector:
delta_vector = coordinate - pygame.math.Vector2(self.rect.center)
Use math.atan2 to compute the angle of rotation:
(See How do I make my player rotate towards mouse position?)
angle = (180 / math.pi) * math.atan2(-delta_vector.x, -delta_vector.y)
Setting the rectangle position in draw struggles with rotating the sprite in lookAt. Note the size of the rotated rectangle is enlarged. See How do I rotate an image around its center using Pygame?.
I recommend to set the look-at position in lookAt and to compute the rotated image in draw:
class enemyshoot:
def __init__(self,x,y,height,width,color):
# [...]
self.look_at_pos = (x, y)
def draw(self):
self.rect = self.shootsright.get_rect(topleft = (self.x, self.y))
dx = self.look_at_pos[0] - self.rect.centerx
dy = self.look_at_pos[1] - self.rect.centery
angle = (180 / math.pi) * math.atan2(-dx, -dy)
self.image = pygame.transform.rotate(self.shootsright, angle)
self.rect = self.image.get_rect(center = self.rect.center)
window.blit(self.image, self.rect)
self.hits = (self.x + 20, self.y, 28,60)
pygame.draw.rect(window, (255,0,0), (self.hitbox[0], self.hitbox[1] - 60, 100, 10)) # NEW
pygame.draw.rect(window, (0,255,0), (self.hitbox[0], self.hitbox[1] - 60, 100 - (5 * (10 - self.health)), 10))
self.hitbox = (self.x + 200, self.y + 200, 51, 65)
def lookAt(self, coordinate):
self.look_at_pos = coordinate
In my game I am creating a turret (a type of machine gun you use on the ground). The problem is that I am using a joystick to move the character, when the joystick is downwards the y speed is positive (so it can move downwards) the opposite is for if you move upwards. Then if checks your current angle and sees which direction you are pointing in if one of the if statements happen then it will allow you to move. What the main issue is that when I move my joystick upwards the gun points dowanrds.
I have already tried making a variable that stores the direction but that lead to the same problem so I discarded that idea and went back to the one I had before. There is also a turret stand where the turret is drawn onto
joystick = pygame.joystick.Joystick(0)
joystick.init()
turret_stand_pic = pygame.image.load("C:/knuckles_pictures/turret_stand.png").convert_alpha()
class Turret_stand():
def __init__(self, x, y, picture, picture_tag):
self.xpos = x
self.ypos = y
self.picture = picture
super().__init__()
self.picture = pygame.transform.scale(self.picture, (200, 200))
self.rect = self.picture.get_rect()
self.rect.x = self.xpos
self.rect.y = self.ypos
self.tag = picture_tag
def draw(self):
screen.blit(self.picture, (self.xpos, self.ypos))
turret_gun_pic = pygame.image.load("C:/knuckles_pictures/turret_gun.png").convert_alpha()
class Turret_gun():
def __init__(self, x, y, picture, picture_tag):
self.xpos = x
self.ypos = y
self.picture = picture
super().__init__()
self.picture = pygame.transform.scale(self.picture, (200, 80))
self.rect = self.picture.get_rect()
self.rect.x = self.xpos
self.rect.y = self.ypos
self.tag = picture_tag
self.previous_angle = 0
self.angle = -90
self.speed_x = 0
self.speed_y = 0
self.facing = "left"
def rotate(self, angle):
if angle != self.angle:
self.picture = pygame.transform.rotate(self.picture, angle)
"""self.angle += angle
self.previous_angle = self.angle"""
def draw(self):
if self.angle == 0:
screen.blit(self.picture, (self.xpos+70, self.ypos-70))
elif self.angle == -90:
screen.blit(self.picture, (self.xpos, self.ypos))
turret = Turret_gun(500, 370, turret_gun_pic, "turret")
turret_stand = Turret_stand(500, 400, turret_stand_pic, "turret stand")
while True:
[...]
if joystick:
move = 3
axis_x_two, axis_y_two = (joystick.get_axis(3), joystick.get_axis(2))
if abs(axis_x_two) > 0.1:
turret.speed_x = move * axis_x_two
turret.speed_y = move * axis_y_two
turret.speed_y = round(int(turret.speed_y))
turret.speed_x = round(int(turret.speed_x))
if turret.angle == -90 and turret.speed_y == -3 and turret.speed_x <= 1 and turret.speed_x >= -1:
turret.rotate(90)
if turret.angle == 0 and turret.speed_x == -3 and turret.speed_y <= 1 and turret.speed_y >= -1:
turret.rotate(-90)
turret.update()
turret.draw()
The actual results are that when you push the joystick upwards the machine gun points downwards. Heres what I mean:
[![enter image description here][1]][1]
In this case the turret ends up pointing downards:
[1]: https://i.stack.imgur.com/Aheix.jpg
[![enter image description here][2]][2]
What I should expect is that when I move the joystick upwards the turret points upwards.
[2]: https://i.stack.imgur.com/jFyg5.jpg
Sometimes the gun does not show when the joystick is pointing right:
Basically, you need to reverse the direction that you rotate the image. So, to flip it, rotate it an additional 180 degrees.
Change the line in your rotate method to
self.picture = pygame.transform.rotate(self.picture, angle+180)
I don't know your exact setup, but this might work only for the up/down case. The gun might still point right if you put the stick left. If this happens, change it to
self.picture = pygame.transform.rotate(self.picture, 180-angle)
This doesn't just flip it, the direction of rotation is reversed. Increasing the angle actually decreases the rotation.
Again, that might not work. It could point right if you move the stick up. If so, try changing the 180 to another number, like 90 or 270. This offsets the rotation by 90 degrees in one direction or the other.
self.picture = pygame.transform.rotate(self.picture, 90-angle)
or
self.picture = pygame.transform.rotate(self.picture, 270-angle)