This is my Sprite1.py program which I import to my main.py program. It contains all the classes I use in my main.py. I tried to create bullets based on a program I saw online but it doesn't work for me. Could anyone help me figure out how to successfully make it work? I don't know how to add the Bullet class from my Sprite.py program to the main.py one.
import pygame
import sys
import os
import time
import random
from pygame import mixer
from pygame.locals import *
def showStartScreen(surface):
show = True
while show == True:
startbg = pygame.image.load(os.path.join('images', 'Starting_scr.png'))
surface.blit(startbg, (0,0))
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
show = False
pygame.init()
class Player(pygame.sprite.Sprite):
'''
Spawn a player
'''
def __init__(self):
pygame.sprite.Sprite.__init__(self)
super().__init__()
self.movex = 0
self.movey = 0
self.frame = 0
self.images = []
self.imagesleft = []
self.imagesright = []
self.alpha = (0,0,0)
self.ani = 4 # animation cycles
for i in range(1,5):
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
img.convert_alpha()
img.set_colorkey(self.alpha)
self.imagesright.append(img)
self.image = self.imagesright[0]
self.rect = self.image.get_rect()
#self.rect = self.image.get_rect(center=pos)
self.all_sprites = all_sprites
self.add(self.all_sprites)
self.bullets = bullets
self.bullet_timer = .1
for i in range(1,5):
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
img = pygame.transform.flip(img, True, False)
img.convert_alpha()
img.set_colorkey(self.alpha)
self.imagesleft.append(img)
self.image = self.imagesleft[0]
self.rect = self.image.get_rect()
#self.rect = self.image.get_rect(center=pos)
self.all_sprites = all_sprites
self.add(self.all_sprites)
self.bullets = bullets
self.bullet_timer = .1
def control(self,x,y):
'''
control player movement
'''
self.movex += x
self.movey -= y
self.bullets
def update(self, dt):
'''
Update sprite position
'''
self.rect.center = pg.mouse.get_pos()
self.rect.x = self.rect.x + self.movex
self.rect.y = self.rect.y + self.movey
# moving left
if self.movex < 0:
self.frame += 1
if self.frame > 3*self.ani:
self.frame = 0
self.image = self.imagesleft[self.frame//self.ani]
# moving right
if self.movex > 0:
self.frame += 1
if self.frame > 3*self.ani:
self.frame = 0
self.image = self.imagesright[self.frame//self.ani]
mouse_pressed = pg.mouse.get_pressed()
self.bullet_timer -= dt # Subtract the time since the last tick.
if self.bullet_timer <= 0:
self.bullet_timer = 0 # Bullet ready.
if mouse_pressed[0]: # Left mouse button.
# Create a new bullet instance and add it to the groups.
Bullet(pygame.mouse.get_pos(), self.all_sprites, self.bullets)
self.bullet_timer = .1 # Reset the timer.
class Bullet(pygame.sprite.Sprite):
def __init__(self, pos, *sprite_groups):
super().__init__(*sprite_groups)
BULLET_IMG = pygame.image.load(os.path.join('images','fireball.png')).convert_alpha()
self.image = BULLET_IMG
self.rect = self.image.get_rect(center=pos)
self.pos = pygame.math.Vector2(pos)
self.vel = pygame.math.Vector2(0, -450)
self.damage = 10
def update(self, dt):
# Add the velocity to the position vector to move the sprite.
self.pos += self.vel * dt
self.rect.center = self.pos # Update the rect pos.
if self.rect.bottom <= 0:
self.kill()
This is my main.py program
import pygame
import os
import time
from pygame import mixer
from pygame.locals import *
import Sprite1
width = 960
height = 720
fps = 40 # frame rate
#ani = 4 # animation cycles
clock = pygame.time.Clock()
pygame.init()
main = True
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISLAYSURF = pygame.display.set_mode((width,height))
surface = pygame.display.set_mode([width,height])
pygame.display.set_caption('B.S.G.!!!')
background = pygame.image.load(os.path.join('images','Bg.png')).convert()
backdropbox = surface.get_rect()
pygame.mixer.music.load('.\\sounds\\Fairy.mp3')
pygame.mixer.music.play(-1, 0.0)
player = Sprite1.Player() # spawn player
fire = Sprite1.Bullet()
player.rect.x = 50
player.rect.y = 500
player_list = pygame.sprite.Group()
player_list.add(player)
steps = 10 # how fast to move
Sprite.showStartScreen(surface)
while main == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit(); sys.exit()
main = False
#if event.type == pygame.MOUSEBUTTONDOWN:
#if Bullet.bullet_timer <= 0:
#Bullet.bullet_timer = 0 # Bullet ready.
#if mouse_pressed[0]: # Left mouse button.
## Create a new bullet instance and add it to the groups.
#Bullet(pg.mouse.get_pos(), self.all_sprites, self.bullets)
#Bullet.bullet_timer = .1 # Reset the timer.
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player.control(-steps,0)
if event.key == pygame.K_RIGHT:
player.control(steps,0)
if event.key == pygame.K_UP:
player.rect.y -= 100
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
player.control(steps,0)
if event.key == pygame.K_RIGHT:
player.control(-steps,0)
if event.key == pygame.K_UP:
player.rect.y += 100
if event.key == ord('q'):
pygame.quit()
sys.exit()
main = False
surface.blit(background, (0, 0))
player.update()
player_list.draw(surface) #refresh player position
pygame.display.flip()
clock.tick(fps)
You already import the sprite file in your main.py on line 6:
import Sprite1
So now your code can call functions from this file:
Sprite1.showStartScreen( window )
Alternatively, instead on line 6 you could use:
from Sprite1 import *
And then your code can reference everything in Sprite1.py as if it was defined in the main.py itself.
The import section of the Python Documentation makes for fairly dry reading, but might just be worth a quick look if you get stuck.
Your bullet sprite code looks mostly OK now, and should work. It seems to be adding the newly created sprite to a bullets - which should be a SpriteGroup, but the supplied code does not show a definition of this.
Typically to move the bullet sprites, the code would call bullets.update() inside the main loop every frame. I can see the code calling player.update() in the last few lines of the main loop. Maybe it just needs a similar call to update() for the bullets group.
This question already has answers here:
Pygame image transparency confusion
(1 answer)
How can I make an Image with a transparent Backround in Pygame?
(2 answers)
Closed last year.
i have this python with pygame please don't mind the comment with ignore. every things work fine but i need to know how to make the player lower than the tree. I already tried to display character first and then the tree but the problem is when i import another TILED TMX it has a blackbackground i don't see the setting of being transparent.
brief information: LoadFileMap is where tmx opened and then it display the map here:
makemaps = LoadFileMap.make_map(scroll[0],scroll[1])
display.blit(makemaps,(0,0))
Example
my Main code:
import pygame, sys, os, random, math
clock = pygame.time.Clock()
from pygame.locals import *
import data.engine as e
import data.Mapload as Map
import data.Makemap as M
#Setting of the Windows
pygame.init()
pygame.display.set_caption("Pineapple")
WINDOWS_SIZE = (800, 608)
screen = pygame.display.set_mode(WINDOWS_SIZE,0,32)
display=pygame.Surface((304,256))
# pygame.mouse.set_visible(False)
#Loading the Animations & Particles
e.load_animations('data/images/Entities/')\
#Create random map
# LoadFileMap = Map.createfileworld("data/Map") Ignore
LoadFileMap = M.TilledMap('data/untitled.tmx')
tile_rects=[]
#Variables for the character
'''Player Status'''
for tile_object in LoadFileMap.tmxdata.objects:
if tile_object.name == 'Player':
player = e.entity(tile_object.x,tile_object.y,14,26,'player')
if tile_object.name == 'Tree':
tile_rects.append(pygame.Rect(tile_object.x,tile_object.y,tile_object.width,tile_object.height))
'''Camera'''
true_scroll = [player.x-304/2,player.y-256/2]
'''Character Movement'''
moving_right = False
moving_left = False
moving_up = False
moving_down =False
while True:
display.fill((146,244,255))
#Scroll Camera movement
true_scroll[0] += ((player.get_center()[0]-304/2)-true_scroll[0])/10
true_scroll[1] += ((player.get_center()[1]-256/2)-true_scroll[1])/10
if true_scroll[0] < 0:
true_scroll[0] = 0
if true_scroll[0] > 496:
true_scroll[0] = 496
if true_scroll[1] > 544:
true_scroll[1] = 544
if true_scroll[1] < 0:
true_scroll[1] = 0
scroll = true_scroll.copy()
scroll[0] = int(scroll[0])
scroll[1] = int(scroll[1])
#Rendering the map and rects
makemaps = LoadFileMap.make_map(scroll[0],scroll[1])
display.blit(makemaps,(0,0))
#Character Movement
player_movement = [0,0]
if moving_up == True:
player_movement[1] -= 2
if moving_down == True:
player_movement[1] += 2
if moving_right == True:
player_movement[0] += 2
if moving_left == True:
player_movement[0] -= 2
collision_types= player.move(player_movement,tile_rects)
player.display(display,scroll)
#Buttons
for event in pygame.event.get():
if event.type==QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_d:
moving_right = True
if event.key == K_a:
moving_left = True
if event.key == K_w:
moving_up = True
if event.key == K_s:
moving_down =True
if event.type == KEYUP:
if event.key == K_d:
moving_right = False
if event.key == K_a:
moving_left = False
if event.key == K_w:
moving_up = False
if event.key == K_s:
moving_down =False
screen.blit(pygame.transform.scale(display,WINDOWS_SIZE),(0,0))
pygame.display.update()
clock.tick(60)
this is the file where it renders the map (Makemaps.py)
import pygame, sys, os
from pygame.locals import *
import pytmx
class TilledMap:
def __init__(self, filename):
tm = pytmx.load_pygame(filename, pixelalpha=True)
self.width= tm.width*tm.tilewidth
self.height= tm.height*tm.tileheight
self.tmxdata = tm
def render(self,Surface,scrollx,scrolly):
ti = self.tmxdata.get_tile_image_by_gid
for layer in self.tmxdata.visible_layers:
test = str(layer)
if isinstance(layer,pytmx.TiledTileLayer):
for x, y, gid, in layer:
tile = ti(gid)
if tile:
Surface.blit(tile,(x*self.tmxdata.tilewidth-scrollx,y*self.tmxdata.tileheight-scrolly))
def make_map(self,scrollx,scrolly):
temp_surface = pygame.Surface((self.width,self.height))
self.render(temp_surface,scrollx,scrolly)
return temp_surface
class obstacle:
def __init__(game,x,y,w,h):
self.group = game.Tree
# pygame.sprite.Sprite.__init__(self.groups)
self.game = game
self.rect = pygame(x,y,w,h)
self.x = x
self.y = y
self.rect.x=x
self.rect.y=y
I am creating a basic 2d overhead view game, and am currently working to make the character "face" the mouse as I move it.
Here is my code so far:
class Player(pygame.sprite.Sprite):
def __init__(self,x,y):
super(Player,self).__init__()
self.image=player_image
self.original_image=self.image
self.rect=self.image.get_rect(center=(x,y))
self.change_x=0
self.change_y=0
self.speed=7.5
self.inventory={'wood':0}
def changespeed(self,x,y):
self.change_x+=x
self.change_y+=y
def update(self,rect):
player.rotate()
self.rect.x+=self.change_x
self.rect.y+=self.change_y
screen.blit(self.image,(rect.x,rect.y))
def check(self):
global tick
if pygame.mouse.get_pressed()[0]:
for tree in resources:
tree_rect=tree.rect
if self.rect.colliderect(tree_rect) and tick>=9:
self.inventory['wood']+=1
tick=0
def rotate(self):
mouse_x, mouse_y = pygame.mouse.get_pos()
rel_x, rel_y = mouse_x - self.rect.x, mouse_y -self.rect.y
angle = (180 / math.pi) * -math.atan2(rel_y, rel_x)
self.image = pygame.transform.rotate(self.original_image, int(angle))
self.rect = self.image.get_rect(center=(self.rect.x,self.rect.y))
To do this, I followed this answer. It supposedly calculated the vector between the mouse and the player, and turned the character around toward the mouse each step, but for me the sprite wobbled around when the mouse moved, and turned invisible.
Full code:
import pygame
from pygame.locals import *
import sys
import math
import pygame.gfxdraw
import random
pygame.init()
black=(0,0,0)
white=(255,255,255)
forest=(34,139,34)
red=(255,0,0)
blue=(0,0,255)
green=(0,255,0)
light_green=(0,120,0)
dark_green=(0,90,0)
skin=(255,224,189)
yellow=(255,255,0)
wood_image=pygame.image.load('./assets/images/wood.png')
stone_image=pygame.image.load('./assets/images/stone.png')
player_image=pygame.image.load('./assets/images/player.png')
tick=0
def terminate():
pygame.quit()
sys.exit()
def drawTextcenter(text,font,screen,x,y,color):
textobj=font.render(text,True,color)
textrect=textobj.get_rect(center=(x,y))
screen.blit(textobj,textrect)
def drawText(text, font, surface, x, y,color):
textobj=font.render(text, 1, color)
textrect=textobj.get_rect()
textrect.topleft=(x, y)
surface.blit(textobj, textrect)
class Button(object):
global screen_width,screen_height,screen
def __init__(self,x,y,width,height,text_color,background_color,text):
self.rect=pygame.Rect(x,y,width,height)
self.x=x
self.y=y
self.width=width
self.height=height
self.text=text
self.text_color=text_color
self.background_color=background_color
self.angle=0
def check(self):
return self.rect.collidepoint(pygame.mouse.get_pos())
def draw(self):
pygame.draw.rect(screen, self.background_color,(self.rect),0)
drawTextcenter(self.text,font,screen,self.x+self.width/2,self.y+self.height/2,self.text_color)
pygame.draw.rect(screen,self.text_color,self.rect,3)
class Bar(object):
def __init__(self,x,y,length,color):
self.rect=pygame.Rect(x,y,length,17.5)
self.image=pygame.draw.rect(screen,white,(self.rect),)
self.x=x
self.y=y
self.width=100
self.height=17.5
self.color=color
self.multiplier=length/100
def draw(self,num):
rect=pygame.Rect(self.x,self.y,num*self.multiplier,self.height)
pygame.draw.rect(screen,self.color,self.rect,3)
pygame.draw.rect(screen,self.color,rect,0)
class Player(pygame.sprite.Sprite):
def __init__(self,x,y):
super(Player,self).__init__()
self.image=player_image
self.original_image=self.image
self.rect=self.image.get_rect(center=(x,y))
self.change_x=0
self.change_y=0
self.speed=7.5
self.inventory={'wood':0}
def changespeed(self,x,y):
self.change_x+=x
self.change_y+=y
def update(self,rect):
self.rotate(camera)
self.rect.x += self.change_x
self.rect.y += self.change_y
rect = camera.apply(self)
screen.blit(self.image, rect)
def check(self):
global tick
if pygame.mouse.get_pressed()[0]:
for tree in resources:
tree_rect=tree.rect
if self.rect.colliderect(tree_rect) and tick>=9:
self.inventory['wood']+=1
tick=0
def rotate(self,camera):
mouse_x, mouse_y = pygame.mouse.get_pos()
mouse_x -= camera.state.x
mouse_y -= camera.state.y
rel_x, rel_y = mouse_x - self.rect.centerx, mouse_y -self.rect.centery
angle = -math.degrees(math.atan2(rel_y, rel_x))
self.image = pygame.transform.rotate(self.original_image, angle)
self.rect = self.image.get_rect(center=self.rect.center)
class Tree(pygame.sprite.Sprite):
def __init__(self,x,y):
super(Tree,self).__init__()
self.rect=pygame.Rect(x,y,100,100)
self.rect.x=x
self.rect.y=y
def update(self,rect):
pygame.gfxdraw.filled_circle(screen,rect.x,rect.y,80,light_green)
pygame.gfxdraw.aacircle(screen,rect.x,rect.y,80,light_green)
class Rock(pygame.sprite.Sprite):
def __init__(self):
pass
class Camera(object):
def __init__(self,camera_func,width,height):
self.camera_func=camera_func
self.state=pygame.Rect(0,0,width,height)
def apply(self,target):
return target.rect.move(self.state.topleft)
def update(self,target):
self.state=self.camera_func(self.state,target.rect)
def complex_camera(camera, target_rect):
l, t = target_rect.center
_,_,w,h = camera
l,t,_,_ = -l+screen_width/2, -t+screen_height/2, w, h
l = min(0, l)
l = max(-(camera.width-screen_width), l)
t = max(-(camera.height-screen_height), t)
t = min(0, t)
return pygame.Rect(l, t, w, h)
clock=pygame.time.Clock()
font=pygame.font.SysFont(None,40)
screen_width=1440
screen_height=800
screen=pygame.display.set_mode([screen_width,screen_height])
pygame.display.set_caption('Survival')
total_level_width=screen_width*5
total_level_height=screen_height*5
camera = Camera(complex_camera, total_level_width, total_level_height)
player=Player(random.randint(100,7900),random.randint(100,5900))
friendlies=pygame.sprite.Group()
friendlies.add(player)
player_health_bar=Bar(125,15,200,green)
player_health=100
player_food_bar=Bar(437.5,15,200,red)
player_food=100
player_thirst_bar=Bar(750,15,200,blue)
player_thirst=100
player_energy_bar=Bar(1100,15,200,yellow)
player_energy=100
resources=pygame.sprite.Group()
wood=False
inventory={}
for i in range(1000):
tree=Tree(random.randint(100,7900),random.randint(100,5900))
resources.add(tree)
done1=False
while not done1:
screen.fill(black)
font=pygame.font.SysFont(None, 90)
text_width,text_height=font.size('Survival')
drawText('Survival', font, screen, (screen_width/2-text_width/2), (screen_height / 2-375),white)
font=pygame.font.SysFont(None, 40)
start_button=Button(screen_width/2-125,650,250,50,white,black,'Start')
start_button.draw()
back_button=Button(screen_width/2-125,725,250,50,white,black,'Back')
back_button.draw()
pygame.display.flip()
done2=False
while not done2:
for event in pygame.event.get():
if event.type==QUIT:
terminate()
elif event.type==pygame.MOUSEBUTTONDOWN:
if start_button.check()==True:
done3=False
pause_back=False
while not done3:
tick+=1
for event in pygame.event.get():
if event.type==pygame.QUIT:
terminate()
elif event.type==pygame.KEYDOWN:
if event.key==pygame.K_a:
player.changespeed(-(player.speed), 0)
elif event.key==pygame.K_d:
player.changespeed(player.speed, 0)
elif event.key==pygame.K_w:
player.changespeed(0, -(player.speed))
elif event.key==pygame.K_s:
player.changespeed(0, player.speed)
elif event.key==pygame.K_p:
font=pygame.font.SysFont(None, 90)
text_width,text_height=font.size('Paused')
drawText('Paused', font, screen, (screen_width / 2-(text_width/2)), (screen_height / 2-375),white)
resume_button=Button(screen_width/2-125,650,250,50,white,black,'Resume')
resume_button.draw()
back_button.draw()
pygame.display.flip()
back=False
while not back:
for event in pygame.event.get():
if event.type==QUIT:
terminate()
elif event.type==pygame.MOUSEBUTTONDOWN:
if resume_button.check()==True:
back=True
elif back_button.check()==True:
done3=True
done2=True
pause_back=True
back=True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_a:
player.changespeed(player.speed, 0)
elif event.key == pygame.K_d:
player.changespeed(-(player.speed), 0)
elif event.key == pygame.K_w:
player.changespeed(0, player.speed)
elif event.key == pygame.K_s:
player.changespeed(0, -(player.speed))
camera.update(player)
if player.rect.x<0:
player.rect.x=0
if player.rect.right>total_level_width:
player.rect.right=total_level_width
if player.rect.y<0:
player.rect.y=0
if player.rect.bottom>total_level_height:
player.rect.bottom=total_level_height
screen.fill(dark_green)
player.update(camera)
for resource in resources:
resource.update(camera.apply(resource))
player_health_bar.draw(player_health)
player_food_bar.draw(player_food)
player_thirst_bar.draw(player_thirst)
#player_endergy_bar.draw(player_energy)
font=pygame.font.SysFont(None, 40)
drawText('Health:',font,screen,20,10,green)
drawText('Food:',font,screen,350,10,red)
drawText('Thirst:',font,screen,650,10,blue)
#drawText('Energy:', font, screen, 975, 10, yellow)
pygame.draw.rect(screen,forest,(50,675,100,100))
pygame.draw.rect(screen,forest,(175,675,100,100))
pygame.draw.rect(screen,forest,(300,675,100,100))
#player.check()
for item in player.inventory:
if item=='wood' and player.inventory['wood']>0:
wood=True
if wood:
screen.blit(wood_image,(62.5,687.5))
drawTextcenter(str(player.inventory['wood']),font,screen,100,735,black)
pygame.display.flip()
clock.tick(100)
if pause_back==True:
break
if pause_back==True:
break
screen.fill(black)
font=pygame.font.SysFont(None, 90)
text_width,text_height=font.size("Game Over")
drawText('Game Over', font, screen, (screen_width/2-text_width/2), (screen_height / 2-200),white)
font=pygame.font.SysFont(None, 40)
retry_button=Button(screen_width/2-125,650,250,50,white,black,'Retry')
retry_button.draw()
back_button.draw()
pygame.display.flip()
back=False
while not back:
for event in pygame.event.get():
if event.type==QUIT:
terminate()
elif event.type==pygame.MOUSEBUTTONDOWN:
if retry_button.check()==True:
back=True
if back_button.check()==True:
back=True
done2=True
done3=True
elif back_button.check()==True:
done2=True
done1=True
I haven't checked out your complete program because it's too much code, but I can see a mistake in the Player class. You're assigning the (self.rect.x,self.rect.y) (the top left) coordinates to the center of the new rect in the rotate method, but you have to assign the center coords of the previous rect.
self.rect = self.image.get_rect(center=(self.rect.x,self.rect.y))
Change the line above to:
self.rect = self.image.get_rect(center=self.rect.center)
Here's a minimal, complete example:
import math
import pygame as pg
class Player(pg.sprite.Sprite):
def __init__(self, pos):
super(Player,self).__init__()
x, y = pos
self.image = pg.Surface((50, 30), pg.SRCALPHA)
pg.draw.polygon(
self.image,
pg.Color('dodgerblue1'),
((1, 1), (49, 15), (1, 29)))
self.original_image = self.image
self.rect=self.image.get_rect(center=(x,y))
self.change_x=0
self.change_y=0
self.speed=7.5
self.inventory={'wood':0}
def changespeed(self,x,y):
self.change_x+=x
self.change_y+=y
def update(self):
self.rotate()
self.rect.x+=self.change_x
self.rect.y+=self.change_y
def rotate(self):
mouse_x, mouse_y = pg.mouse.get_pos()
rel_x, rel_y = mouse_x - self.rect.x, mouse_y -self.rect.y
angle = (180 / math.pi) * -math.atan2(rel_y, rel_x)
self.image = pg.transform.rotate(self.original_image, angle)
# rect.x and .y are the topleft coords, but you need the to
# pass the center coordinates of the previous rect to the new one.
self.rect = self.image.get_rect(center=self.rect.center)
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
player = Player((300, 200))
all_sprites.add(player)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.KEYDOWN:
if event.key == pg.K_d:
player.change_x = 4
elif event.key == pg.K_a:
player.change_x = -4
elif event.type == pg.KEYUP:
if event.key == pg.K_d:
player.change_x = 0
elif event.key == pg.K_a:
player.change_x = 0
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
The problem is caused by the camera. You have to apply the camera offset to the mouse position as well.
Here's a simple solution. First, pass the camera instead of the rect to the player.update method.
player.update(camera)
Pass it to the rotate method as well.
def update(self, camera):
self.rotate(camera)
self.rect.x += self.change_x
self.rect.y += self.change_y
rect = camera.apply(self)
screen.blit(self.image, rect)
In the rotate method you have to subtract the camera position from the mouse position to get the world coordinates.
def rotate(self, camera):
mouse_x, mouse_y = pygame.mouse.get_pos()
# Subtract the camera offset from the mouse position.
mouse_x -= camera.state.x
mouse_y -= camera.state.y
# Use the center coordinates of the rect.
rel_x, rel_y = mouse_x - self.rect.centerx, mouse_y -self.rect.centery
angle = -math.degrees(math.atan2(rel_y, rel_x))
self.image = pygame.transform.rotate(self.original_image, angle)
# Set the new center to the previous center coords.
self.rect = self.image.get_rect(center=self.rect.center)
I also had to modify the complex_camera function, because it should work with the center coordinates.
def complex_camera(camera, target_rect):
# The camera works with the center instead of
# the topleft coordinates now.
l, t = target_rect.center
_,_,w,h = camera
l,t,_,_ = -l+screen_width/2, -t+screen_height/2, w, h
l = min(0, l)
l = max(-(camera.width-screen_width), l)
t = max(-(camera.height-screen_height), t)
t = min(0, t)
return pygame.Rect(l, t, w, h)