So, I am making a tower defense game using Python's turtle module and I just started off. For some reason though, the .tracer at the very beginning is creating an extra turtle for me for some reason. I don't know why. I have 3 classes, and so far, I have only defined 4 sprites: Helper, Enemy, Enemy, Enemy. When I remove the tracer, the turtle suddenly disappears. I accessed my turtle list and started to delete them. That didn't work. Here is the code:
import turtle as t
import random as r
wn = t.Screen()
wn.tracer(0)
class Sprite():
def __init__(self, color, speed, shape, ento, x, y):
self.sprite = t.Turtle()
self.color = color
self.speed = speed
self.shape = shape
self.ento = ento
self.x = x
self.y = y
def render(self, pen):
pen.speed(0)
pen.color(self.color)
pen.shape(self.shape)
pen.up()
pen.goto(self.x, self.y)
class Helper(Sprite):
def __init__(self, color, speed, damage, shape, x, y):
self.sprite = Sprite(color, speed, shape, 'Helper', x, y)
self.helper = t.Turtle()
self.color = color
self.speed = speed
self.shape = shape
self.ento = 'Helper'
self.damage = damage
self.x = x
self.y = y
def fire(self):
pass
class Enemy(Sprite):
def __init__(self, color, speed, health, shape, x, y):
self.sprite = Sprite(color, speed, shape, 'Enemy', x, y)
self.enemy = t.Turtle()
self.color = color
self.speed = speed
self.shape = shape
self.ento = 'Enemy'
self.health = health
self.x = x
self.y = y
self.state = 'frozen'
self.tick = 0
def move(self):
if self.state == 'frozen':
self.state = 'moving'
self.tick = 0
self.enemy.forward(self.speed)
self.x = self.enemy.xcor()
self.y = self.enemy.ycor()
self.tick += 1
helpers = []
for i in range(1):
test = Helper('green', 1, 1, 'square', 0, 0)
enemies = []
for i in range(3):
enemies.append(Enemy('red', 1, 1, 'circle', r.randint(-100, 100), r.randint(-100, 100)))
print(wn._turtles)
while True:
wn.update()
test.render(test.helper)
for enemy in enemies:
enemy.render(enemy.enemy)
enemy.move()
Why is this not working. I work on a windows computer. I am not really sure if this applies to only me or to everyone.
Your object code is a disaster. Consider this logic:
class Sprite():
def __init__(self, color, speed, shape, ento, x, y):
self.sprite = t.Turtle()
#...
class Helper(Sprite):
def __init__(self, color, speed, damage, shape, x, y):
self.sprite = Sprite(color, speed, shape, 'Helper', x, y)
Helper is a Sprite but it doesn't call super(), and instead has a Sprite inside it where the turtle that Sprite creates would have been? I'm lost. I think we need to make the code object-oriented instead of simply object-based:
from turtle import Screen, Turtle
from random import randint
class Sprite(Turtle):
def __init__(self, color, pace, shape, ento, x, y):
super().__init__()
self.color(color)
self.shape(shape)
self.penup()
self.pace = pace
self.ento = ento
self.x = x
self.y = y
def render(self):
self.goto(self.x, self.y)
class Helper(Sprite):
def __init__(self, color, pace, damage, shape, x, y):
super().__init__(color, pace, shape, 'Helper', x, y)
self.damage = damage
def fire(self):
pass
class Enemy(Sprite):
def __init__(self, color, pace, health, shape, x, y):
super().__init__(color, pace, shape, 'Enemy', x, y)
self.health = health
self.state = 'frozen'
self.tick = 0
def move(self):
if self.state == 'frozen':
self.state = 'moving'
self.tick = 0
self.forward(self.pace)
self.x = self.xcor()
self.y = self.ycor()
self.tick += 1
screen = Screen()
screen.tracer(0)
helpers = []
for _ in range(1):
helpers.append(Helper('green', 1, 1, 'square', 0, 0))
enemies = []
for _ in range(3):
enemies.append(Enemy('red', 1, 1, 'circle', randint(-100, 100), randint(-100, 100)))
while True:
helpers[0].render()
for enemy in enemies:
enemy.render()
enemy.move()
screen.update()
This clean up of your code appears to also clean up your extra turtle issue. I renamed speed to be pace to keep it from being confused with turtle's own speed method.
Related
I started playing a little with pygame and so far I'm not doing badly. I encountered a problem and managed to solve it. The solution is possible. It is not 100% correct. I want to implement an eat method when the head of the subject meets the food.
It is probably a relatively simple method when the rest of the value of X and Y are equal to the head of the snake being eaten.
For some reason I was not able to fully understand the overlap of pixels and I am not really correct in the method.
The problem at the moment is that there is no overlap between the pixels and "eating is not done".
BACKGROUND = pygame.image.load("background.jpg")
WIDTH, HEIGHT = BACKGROUND.get_width(), BACKGROUND.get_height()
pygame.font.init()
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
def checkForEat(self):
head = self.body[-1]
x = self.food.getPos()[0]
y= self.food.getPos()[1]
# if abs(head[0] - x ) < 9 and abs(head[1] - y ) < 9: -- This is my temporary solution
if head[0] == x and head[1] == y:
self.food = Food()
self.eat()
I try not to add too much unnecessary code.
class Food:
def __init__(self):
self.color = (5, 5, 255)
self.pos = (random.randint(10,WIDTH-50),random.randint(10,HEIGHT-50))
def draw(self,win):
pygame.draw.circle(win,self.color, self.pos, 5)
def getPos(self):
return self.pos
class Snake:
START_POS = (85, 85)
def __init__(self):
self.food = Food()
self.block_size = 11
self.x , self.y = self.START_POS
self.body = self.create_body()
def create_body(self):
body = []
for i in range(self.length):
body.append((85,85+i*self.block_size))
return body
def draw(self,win):
WIN.blit(BACKGROUND, (0, 0))
self.food.draw(win)
for i in range(self.length):
pygame.draw.circle(win, (255, 0, 0), self.body[i], 5)
I'm not adding the rest of the program.
Just saying that apart from the problem I wrote above everything works fine.
Use pygame.Rect/pygame.Rect.colliderect to check if the bounding rectangle of the food overlaps with the head of the snake:
class Food:
def __init__(self):
self.color = (5, 5, 255)
self.pos = (random.randint(10,WIDTH-50),random.randint(10,HEIGHT-50))
def draw(self,win):
pygame.draw.circle(win,self.color, self.pos, 5)
def getPos(self):
return self.pos
def getRect(self):
return pygame.Rect(self.pos[0]-5, self.pos[1]-5, 10, 10)
class Snake:
START_POS = (85, 85)
def __init__(self):
self.food = Food()
self.block_size = 11
self.x , self.y = self.START_POS
self.body = self.create_body()
# [...]
def checkForEat(self):
head = self.body[-1]
head_rect = pygame.Rect(head[0]-5, head[1]-5, self.block_size, self.block_size)
food_rect = self.food.getRect()
if food_rect.colliderect(head_rect):
self.food = Food()
self.eat()
Also see How do I detect collision in pygame?.
Alternatively you can compute the Euclidean distance between the circle center of the circles and compare the distance to the sum of the radii:
class Snake:
# [...]
def checkForEat(self):
dx = self.food.getPos()[0] - self.body[-1][0]
dy = self.food.getPos()[1] - self.body[-1][1]
dist_center = math.hypot(dx, dy)
if dist_center <= 20:
self.food = Food()
self.eat()
I am trying to make a circle which asks only for a center and radius. Here is my code:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def print_point(self):
print(f"Point: {self.x, self.y}")
class Circle:
def __init__(self, center, radius):
self.center = center
self.radius = radius
def print_circle(self):
print(f"Circle: {(self.center), self.radius}")
p1 = Point(150, 100)
c1 = Circle(p1, 75)
c1.print_circle()
What am I doing wrong?
You can assign the __repr__ method to your point class:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Point: {self.x, self.y}"
class Circle:
def __init__(self, center, radius):
self.center = center
self.radius = radius
def print_circle(self):
print(f"Circle: {((self.center)), self.radius}")
p1 = Point(150, 100)
c1 = Circle(p1, 75)
c1.print_circle()
It looks like you're not actually getting any info from the class that's being passed, and just trying to print the object itself. I haven't tested this code myself but try
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def print_point(self):
print(f"Point: {self.x, self.y}")
class Circle:
def __init__(self, center, radius):
self.center = center
self.radius = radius
def print_circle(self):
print(f"Circle: {((self.center.x),(self.center.y)), self.radius}")
p1 = Point(150, 100)
c1 = Circle(p1, 75)
c1.print_circle()
or use another function that returns the string to be printed:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def print_point(self):
print(f"Point: {self.x, self.y}")
def get_point(self):
return f'Point: {self.x, self.y}'
class Circle:
def __init__(self, center, radius):
self.center = center
self.radius = radius
def print_circle(self):
print(f"Circle: {self.center.get_point()},{self.radius}")
p1 = Point(150, 100)
c1 = Circle(p1, 75)
c1.print_circle()
I started learning Pygame and Tiled map editor. I have the following test:
Example
It looks fine in the editor, the objects that have the black BG are currently on layer_4 (but they do it regardless of layer count).
I already tried using convert_alpha on it, ticking and unticking the transparency color in Tiled when loading in the tile map.
This is the original picture:
Full picture
This is the part where I load in the picture:
def import_cut_graphics(path):
surface = pygame.image.load(path).convert_alpha()
tile_num_x = int(surface.get_size()[0] / TILESIZE)
tile_num_y = int(surface.get_size()[1] / TILESIZE)
cut_tiles = []
for row in range(tile_num_y):
for col in range(tile_num_x):
x = col * TILESIZE
y = row * TILESIZE
new_surface = pygame.Surface((TILESIZE, TILESIZE))
new_surface.blit(surface, (0, 0), pygame.Rect(x, y, TILESIZE, TILESIZE))
cut_tiles.append(new_surface)
return cut_tiles
And this is where I work with it:
class Level:
def __init__(self, level_data, surface):
# general setup
self.all_layers = import_cut_graphics("img\ProjectUtumno_full.png")
self.display_surface = surface
self.world_shift = 0
# terrain setup
layer1_layout = import_csv_layout(level_data["layer_1"])
self.layer1_sprites = self.create_tile_group(layer1_layout, 'layer_1')
# grass setup
layer2_layout = import_csv_layout(level_data["layer_2"])
self.layer2_sprites = self.create_tile_group(layer2_layout, "layer_2")
# crates
layer3_layout = import_csv_layout(level_data["layer_3"])
self.layer3_sprites = self.create_tile_group(layer3_layout, "layer_3")
# layer 4
layer4_layout = import_csv_layout(level_data["layer_4"])
self.layer4_sprites = self.create_tile_group(layer4_layout, "layer_4")
def create_tile_group(self, layout, type):
sprite_group = pygame.sprite.Group()
for row_index, row in enumerate(layout):
for col_index, val in enumerate(row):
if val != '-1':
x = col_index * TILESIZE
y = row_index * TILESIZE
if type == 'layer_1':
tile_surface = self.all_layers[int(val)]
sprite = StaticTile(TILESIZE, x, y, tile_surface)
sprite_group.add(sprite)
if type == "layer_2" :
tile_surface = self.all_layers[int(val)]
sprite = StaticTile(TILESIZE, x, y, tile_surface)
sprite_group.add(sprite)
if type == "layer_3" :
tile_surface = self.all_layers[int(val)]
sprite = StaticTile(TILESIZE, x, y, tile_surface)
sprite_group.add(sprite)
if type == "layer_4" :
tile_surface = self.all_layers[int(val)]
sprite = StaticTile(TILESIZE, x, y, tile_surface)
sprite_group.add(sprite)
return sprite_group
def run(self):
# run the entire game / level
self.camera()
# layer1
self.layer1_sprites.update(self.world_shift)
self.layer1_sprites.draw(self.display_surface)
# layer2
self.layer2_sprites.update(self.world_shift)
self.layer2_sprites.draw(self.display_surface)
# layer3
self.layer3_sprites.update(self.world_shift)
self.layer3_sprites.draw(self.display_surface)
# layer4
self.layer4_sprites.update(self.world_shift)
self.layer4_sprites.draw(self.display_surface)
This is my main game loop:
class Game:
def __init__(self):
pygame.init()
pygame.font.init()
self.screen = pygame.display.set_mode((WIN_WIDTH, WIN_HEIGHT))
self.clock = pygame.time.Clock()
self.screen_name = pygame.display.set_caption("New Game")
self.running = True
self.playing = False
self.level = Level(level_0, self.screen)
self.character_spritesheet = Spritesheet('img\MainCharacter\B_witch_idle.png')
def main(self):
# game loop
while self.running:
self.events()
self.update()
self.draw()
self.running = False
def draw(self):
self.screen.fill(BLACK)
self.level.run()
self.all_sprites.draw(self.screen)
self.clock.tick(FPS)
pygame.display.update()
I did try commenting the screen fill black and got the same result. I tried out set_colorkey with white/black colors but didn't work out.
I add my tile create class too:
class Tile(pygame.sprite.Sprite):
def __init__(self, size, x, y):
super().__init__()
self.image = pygame.Surface((size, size))
self.rect = self.image.get_rect(topleft=(x, y))
def update(self, shift):
self.rect.x += shift
class StaticTile(Tile):
def __init__(self, size, x, y, surface):
super().__init__(size, x, y)
self.image = surface
Thanks in advance if you can figure out why i have this problem.
So for anyone who's having this problem, I managed to solve it by this:
Inside my import_cut_graphics methode I added a set_colorkey(BLACK), turns out the problem occured when I was originally cutting the picture, therefore it didnt matter how much I change it at the latter phases since the problem was in an earlier stage.
def import_cut_graphics(path):
surface = pygame.image.load(path).convert_alpha()
tile_num_x = int(surface.get_size()[0] / TILESIZE)
tile_num_y = int(surface.get_size()[1] / TILESIZE)
cut_tiles = []
for row in range(tile_num_y):
for col in range(tile_num_x):
x = col * TILESIZE
y = row * TILESIZE
new_surface = pygame.Surface((TILESIZE, TILESIZE))
new_surface.blit(surface, (0, 0), pygame.Rect(x, y, TILESIZE, TILESIZE))
new_surface.set_colorkey(BLACK)
cut_tiles.append(new_surface)
return cut_tiles
I am in troubles, I am trying to make doors opening in my game.
I am using pygame, and pytmx, I have built a Level made with Rooms, and in each Room I have a Renderer using pytmx, what I want to achieve is for exemple on level 0 the player has to move to a door to open it and enter level 1, my goal is to launch and draw the animation when player hit the door.
I tried to make my Door object (his parent is pygame Sprite class) update, it has kind of worked but of course the object was modified and displayed, but the tiled map object also and was staying in the back of the map, so I tried to just not initialize my objects in the tile map but if so the door doesn't bit at all. Then I tried to modify the tile object image and to reload my rendering but still doesn't work, does anyone of you has any idea ?
Here is some parts of the code to let you have better understanding
"""
This is a test of using the pytmx library with Tiled.
"""
import pygame
import pytmx
class Renderer(object):
"""
This object renders tile maps from Tiled
"""
def __init__(self, filename):
tm = pytmx.load_pygame(filename, pixelalpha=True)
self.object_images = []
self.size = tm.width * tm.tilewidth, tm.height * tm.tileheight
self.tmx_data = tm
self.map_surface = self.make_map()
self.current_frame = 0
def render(self, surface):
tw = self.tmx_data.tilewidth
th = self.tmx_data.tileheight
print(self.tmx_data.tile_properties.items())
if self.tmx_data.background_color:
surface.fill(self.tmx_data.background_color)
for layer in self.tmx_data.layers:
if isinstance(layer, pytmx.TiledTileLayer):
for x, y, image in layer.tiles():
if image:
surface.blit(image.convert_alpha() , (x * tw, y * th))
elif isinstance(layer, pytmx.TiledObjectGroup):
for object in layer:
if object.image:
surface.blit(object.image.convert_alpha() , (object.x, object.y))
elif isinstance(layer, pytmx.TiledImageLayer):
if image:
surface.blit(image , (0, 0))
def make_map(self):
temp_surface = pygame.Surface(self.size)
self.render(temp_surface)
return temp_surface
def reload(self):
self.map_surface = self.make_map()
def update_object_image(self, object_id, surface):
pass
def get_layer(self, layer_name):
return self.tmx_data.get_layer_by_name(layer_name)
class Room:
def __init__(self, room_image, level_options):
self.renderer = Renderer(room_image)
self.surface = self.renderer.map_surface
self.rect = self.surface.get_rect()
self.level_options = level_options
self.obstacles = pygame.sprite.Group()
self.doors = pygame.sprite.Group()
self.load_obstacles()
self.load_doors()
def load_obstacles(self):
obstacles = self.level_options['obstacle_layers']
for obstacle in obstacles:
try:
items = self.renderer.get_layer(obstacle)
for x, y, image in items.tiles():
self.obstacles.add(Obstacle(x, y, image, SPRITE_SIZE))
except ValueError:
return
def load_doors(self):
doors = self.renderer.get_layer(self.level_options['door_layer'])
if isinstance(doors, pytmx.TiledTileLayer):
for x, y, image in doors.tiles():
if image:
self.doors.add(Door(self, x, y, image, SPRITE_SIZE, self.level_options['open_door_actions']))
elif isinstance(doors, pytmx.TiledObjectGroup):
for object in doors:
self.doors.add(Door(self, object.x, object.y, object.image, SPRITE_SIZE, self.level_options['open_door_actions'], object))
def update_doors(self):
for door in self.doors:
if door.object:
door.update()
#self.renderer.update_object_image(door.object.id, door.image)
class Wall(pygame.sprite.Sprite):
def __init__(self, x, y, image, sprite_size):
pygame.sprite.Sprite.__init__(self)
self.rect = pygame.Rect(x * sprite_size,y * sprite_size, sprite_size, sprite_size)
self.image = image
class Obstacle(pygame.sprite.Sprite):
def __init__(self, x, y, image, sprite_size):
pygame.sprite.Sprite.__init__(self)
self.rect = pygame.Rect(x * sprite_size,y * sprite_size, sprite_size, sprite_size)
self.image = image
class Door(pygame.sprite.Sprite):
def __init__(self, parent, x, y, image, sprite_size, open_actions, object = None):
self.parent = parent
pygame.sprite.Sprite.__init__(self)
self.object = object
if self.object:
self.rect = pygame.Rect(x ,y, self.object.width, self.object.height)
self.current_index = 0
self.load_animations()
else:
self.rect = pygame.Rect(x * sprite_size,y * sprite_size, sprite_size, sprite_size)
self.image = image
self.open_actions = open_actions
self.last_updated = 0
self.is_open = False
self.is_activated = False
def load_animations(self):
self.animations = []
filename = os.path.join(os.getcwd(), IMAGES_FOLDER, 'maps', 'animations', self.object.animated_image)
spritesheet = Spritesheet(filename, self.object.width, self.object.height)
for i in range(spritesheet.data['columns']):
self.animations.append(spritesheet.parse_sprite(i))
self.image = self.animations[self.current_index]
def open_animation(self):
if self.is_open:
return
if not self.object or not self.is_activated or not self.animations:
return
self.image = self.animations[self.current_index]
if self.current_index == len(self.animations) - 1:
self.is_open = True
def update(self):
if self.is_open:
if self.object:
self.image = self.animations[-1]
if self.is_activated:
if self.object:
now = pygame.time.get_ticks()
if now - self.last_updated > 200:
self.last_updated = now
self.current_index = (self.current_index + 1) % len(self.animations)
self.image = self.animations[self.current_index]
if not self.is_open:
self.image = self.animations[0]
def check_doors_state(self, player):
if 'any' in self.open_actions:
self.is_activated = True
for action in player.actions:
if action in self.open_actions:
self.is_activated = True
So right now what I am trying to do is to use an animated tile object and launch the animation from tmx data, but I can't even understand how to launch the animation at the first rendering.
Thank you in advance for you answers.
So I found a solution, I am not trying to change and reload the object tiles, I simply made my game sprites in groups, modified my generating and rendering of tiles.
Classes modified :
class Room:
def __init__(self, level, room_image, level_options):
self.level = level
self.renderer = Renderer(room_image)
self.surface = self.renderer.map_surface
self.tmx_data = self.renderer.tmx_data
self.rect = self.surface.get_rect()
self.level_options = level_options
self.walls = pygame.sprite.Group()
self.doors = pygame.sprite.Group()
#self.load_walls()
#self.load_doors()
def load_walls(self):
walls = self.renderer.get_layer(self.level_options['wall_layer'])
if isinstance(walls, pytmx.TiledObjectGroup):
for object in walls:
self.walls.add(Wall(object.x, object.y, object, SPRITE_SIZE))
def load_doors(self):
doors = self.renderer.get_layer(self.level_options['door_layer'])
if isinstance(doors, pytmx.TiledTileLayer):
for x, y, image in doors.tiles():
if image:
self.doors.add(Door(self, x, y, image, SPRITE_SIZE, self.level_options['open_door_actions']))
elif isinstance(doors, pytmx.TiledObjectGroup):
for object in doors:
self.doors.add(Door(self, object.x, object.y, object.image, SPRITE_SIZE, self.level_options['open_door_actions'], object))
def update(self):
self.doors.update()
def draw(self, display, camera = None):
self.doors.draw(self.surface)
if not camera:
display.blit(self.surface, self.rect)
else:
display.blit(self.surface, (self.rect.x - camera.offset.x, self.rect.y - camera.offset.y))
def get_opening_actions(self):
return self.level_options['open_door_actions']
class Wall(pygame.sprite.Sprite):
def __init__(self, game, x, y):
self.groups = game.all_sprites, game.walls
pygame.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.image = game.wall_img
self.rect = self.image.get_rect()
self.x = x
self.y = y
self.rect.x = x * SPRITE_SIZE
self.rect.y = y * SPRITE_SIZE
class Obstacle(pygame.sprite.Sprite):
def __init__(self, game, x, y, w, h):
self.groups = game.walls
pygame.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.rect = pygame.Rect(x, y, w, h)
self.hit_rect = self.rect
self.x = x
self.y = y
self.rect.x = x
self.rect.y = y
class Door(pygame.sprite.Sprite):
def __init__(self, game, x, y, w, h, open_actions, animated_image):
self.groups = game.all_sprites, game.doors
pygame.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.rect = pygame.Rect(x ,y, w, h)
self.current_index = 0
self.animated_image = animated_image
self.load_animations()
self.open_actions = open_actions
self.last_updated = 0
self.is_open = False
self.is_activated = False
def load_animations(self):
self.animations = []
filename = os.path.join(os.getcwd(), IMAGES_FOLDER, 'maps', 'animations', self.animated_image)
spritesheet = Spritesheet(filename, self.rect.width, self.rect.height)
for i in range(spritesheet.data['columns']):
self.animations.append(spritesheet.parse_sprite(i))
self.image = self.animations[self.current_index]
def open_animation(self):
if self.is_open:
return
if not self.is_activated or not self.animations:
return
now = pygame.time.get_ticks()
if now - self.last_updated > 15:
self.last_updated = now
self.current_index = (self.current_index + 1) % len(self.animations)
self.image = self.animations[self.current_index]
if self.current_index == len(self.animations) - 1:
self.current_index = -1
self.is_open = True
def update(self):
if not self.is_open:
self.image = self.animations[0]
if self.is_open:
self.image = self.animations[-1]
def check_doors_state(self, player):
if 'any' in self.open_actions:
self.is_activated = True
for action in player.actions:
if action in self.open_actions:
self.is_activated = True
Sprites groups generating, updating and rendering added (in my game class) :
def new(self, level = 0):
# initialize all variables and do all the setup for a new game
self.all_sprites = pygame.sprite.Group()
self.walls = pygame.sprite.Group()
self.doors = pygame.sprite.Group()
self.mobs = pygame.sprite.Group()
## LOAD LEVEL
self.level = Level(level)
self.room = self.level.current_room
for tile_object in self.room.tmx_data.objects:
if tile_object.name == 'player':
self.player = Player(self, tile_object.x, tile_object.y)
if tile_object.name == 'obstacle':
Obstacle(self, tile_object.x, tile_object.y, tile_object.width, tile_object.height)
if tile_object.name == 'wall':
Wall(self, tile_object.x, tile_object.y, tile_object.width, tile_object.height)
if tile_object.name == 'door':
Door(self, tile_object.x, tile_object.y, tile_object.width, tile_object.height, self.room.get_opening_actions(), tile_object.animated_image)
def update(self):
self.all_sprites.update()
def draw(self):
self.window.fill((0, 0, 0))
## DISPLAY MAP
self.level.draw_room(self.window, self.camera)
## DISPLAY SPRITES
for sprite in self.all_sprites:
self.window.blit(sprite.image, (sprite.rect.x - self.camera.offset.x, sprite.rect.y - self.camera.offset.y))
## REFRESH SCREEN
pygame.display.flip()
And here is the result (I am working on better hitbox for the player ^^)
door opening when player getting close to it
I am trying to make a 2D game in pygame, and have a camera class that has a surface attribute, and each time the cameras get updated their surface is updated. All graphics are blitted to game.worldSurface, and then the main camera take a picture of that and blits it to the display surface. However, when using other cameras, i am unable to blit to the worldsurface and get a locking error. i have tried .unlock(). what could be causing this?
import pygame
import pickle
class Tileset:
def __init__(self, location):
pass
class Tilemap:
def __init__(self):
pass
class Collisionmap(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
class Player(pygame.sprite.Sprite):
def __init__(self, spritesheet):
super().__init__()
self.spritesheet = pygame.image.load(spritesheet)
self.x = 0
self.y = 0
def draw(self, surface):
surface.blit(self.spritesheet, (self.x, self.y))
class Mob(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
class Camera:
def __init__(self):
self.x = 0
self.y = 0
self.width = 100
self.height = 100
self.surface = pygame.Surface((self.width, self.height))
def moveToSprite(self, sprite):
self.x = sprite.rect.centerx - WIDTH // 2
self.y = sprite.rect.centery - HEIGHT // 2
def update(self, world):
self.surface = world.subsurface((self.x, self.y, self.width, self.height))
class Level:
def __init__(self, terrarin, collision, mobs):
self.terrain = terrain
self.collision = collision
self.mobs = mobs
class Game:
def __init__(self):
pygame.init()
self.DISPLAYSURF = pygame.display.set_mode((0, 0))
self.mainCamera = Camera()
self.mainCamera.width = self.DISPLAYSURF.get_width()
self.mainCamera.height = self.DISPLAYSURF.get_height()
self.otherCameras = []
self.worldSurface = pygame.Surface((10000, 10000))
self.player = Player("marioSS.jpg")
self.otherCameras.append(Camera())
self.run()
def run(self):
while True:
for event in pygame.event.get():
pass
self.earlyUpdate()
self.update()
self.lateUpdate()
self.graphicsUpdate()
def update(self):
pass
def earlyUpdate(self):
pass
def lateUpdate(self):
pass
def graphicsUpdate(self):
for each in self.otherCameras:
each.update(self.worldSurface)
self.player.draw(self.worldSurface)
self.otherCameras[0].surface.unlock()
self.worldSurface.unlock()
self.worldSurface.blit(self.otherCameras[0].surface, (100, 100)) ##Error here
self.mainCamera.update(self.worldSurface)
self.DISPLAYSURF.blit(self.mainCamera.surface, (0, 0))
pygame.display.update()
x = Game()
Problem makes world.subsurface() in Camera.update()
It doesn't copy data from world to surface but it assigns access to original world. And later you have: camera.surface keeps access to world, and blit try to copy from camera.surface to world - so finally it tries to copy from world to world. And maybe it locks it.
But if in Camera.update() you use .copy()
self.surface = world.subsurface((self.x, self.y, self.width, self.height)).copy()
or blit it
self.surface.blit(world.subsurface((self.x, self.y, self.width, self.height)), (0,0))
then it works.
DOC: subsurface
subsurface(Rect) -> Surface
Returns a new Surface that shares its pixels with its new parent.