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.
Related
I can't figure out why the code below renders the sprite without transparency (with the rest of the sprite filled with black). I'm almost a beginner to programming so it might be very obvious but no other answers to similar questions here helped me. The file has transparency and I used convert_alpha() when loading the file.
import pygame
class Piece(pygame.sprite.Sprite):
def __init__(self, kind, posx, posy, sheet, color):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
# Create an image for the piece and fill it with the image
# TO DO: cut the right type of piece from a sheet, preferably before constructing the object
self.image = pygame.Surface([128, 128])
self.image.blit(sheet, [0, 0])
self.kind = kind # piece type, designated with an uppercase letter, eg. Q for queen
self.color = color # W or B for white or black
# Fetch the rectangle object that has the dimensions of the image
# Update the position of this object by setting the values of rect.x and rect.y
self.rect = self.image.get_rect()
self.rect.x = posx
self.rect.y = posy
class App:
def __init__(self):
self._running = True
self._display_surf = None
self.size = self.weight, self.height = 1024, 768
self.sprites = pygame.sprite.Group() # all sprites in the app to iterate on
self.spritesheet = 0 # this will be loaded later
self.bgcolor = [200, 200, 200]
def on_init(self):
pygame.init()
self._display_surf = pygame.display.set_mode(self.size, pygame.HWSURFACE | pygame.DOUBLEBUF)
self._running = True
self.spritesheet = pygame.image.load("resources/w_queen_png_shadow_128px.png", "r").convert_alpha()
self.sprites.add(Piece("Q", 64, 64, self.spritesheet, "W"))
def on_event(self, event):
if event.type == pygame.QUIT:
self._running = False
def on_loop(self):
pass
def on_render(self):
self._display_surf.fill(self.bgcolor)
self.sprites.draw(self._display_surf)
pygame.display.flip()
def on_cleanup(self):
pygame.quit()
def on_execute(self):
if self.on_init() is False:
self._running = False
while self._running:
for event in pygame.event.get():
self.on_event(event)
self.on_loop()
self.on_render()
self.on_cleanup()
if __name__ == "__main__":
game = App()
game.on_execute()
If you are copying an image with a per pixel alpha format on another pygame.Surface onto another surface, you need to ensure that the target Surface has a per pixel alpha format, too. If the target cannot save the alpha channel, the transparency will be lost. Set the SRCALPHA flag to create a surface with an image format that includes a per-pixel alpha.
self.image = pygame.Surface([128, 128])
self.image = pygame.Surface([128, 128], pygame.SRCALPHA)
class Piece:
class Piece(pygame.sprite.Sprite):
def __init__(self, kind, posx, posy, sheet, color):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
# Create an image for the piece and fill it with the image
# TO DO: cut the right type of piece from a sheet, preferably before constructing the object
self.image = pygame.Surface([128, 128], pygame.SRCALPHA)
self.image.blit(sheet, [0, 0])
self.kind = kind # piece type, designated with an uppercase letter, eg. Q for queen
self.color = color # W or B for white or black
# Fetch the rectangle object that has the dimensions of the image
# Update the position of this object by setting the values of rect.x and rect.y
self.rect = self.image.get_rect()
self.rect.x = posx
self.rect.y = posy
I wanted to ask you for your help. I am following tutorial about creating alien game but I modified it a little bit and I cannot get it to work properly. I have value for alien_speed in settings pyfile. I am modifying it in method increase_speed and I am printing it (and it is actually growing like I want it to). But aliens have still the same speed. I don't understand why it is not working. Can maybe someone point me in right direction ?
settings.py:
import pygame
resolution_width = 1280
resolution_height = 720
class Settings:
"""A class to store all settings for Space Impact."""
def __init__(self):
"""Initialize the game's static settings."""
# Screen settings
# This line is needed to avoid error: No video mode has been set
self.screen = pygame.display.set_mode((0, 0))
self.screen_width = resolution_width
self.screen_height = resolution_height
self.bg_image = pygame.image.load("images/background_1.png").convert()
# Bullet settings
self.bullet_speed = self.screen_width*0.01
self.bullet_width = self.screen_width*0.02
self.bullet_height = self.screen_height*0.02
self.bullet_color = (0, 0, 0)
# How quickly the game speeds up
self.speedup_scale = 999
# Ship Settings
self.ships_limit = 3
self.initialize_dynamic_settings()
def initialize_dynamic_settings(self):
self.alien_speed = self.screen_width*0.003
def increase_speed(self):
"""Increase speed settings."""
self.alien_speed *= self.speedup_scale
print(self.alien_speed)
alien.py:
import pygame
from pygame.sprite import Sprite
from settings import Settings
import random
class Alien(Sprite):
"""A class to represent a single alien."""
def __init__(self, space_impact):
"""Initialize alien and set it's starting position."""
super().__init__()
self.screen = space_impact.screen
self.settings = Settings()
# Load the alien image and set it's rect attribute.
self.index = 0
self.timer = 0
self.image = []
self.image.append(pygame.image.load('images/alien_1.png'))
self.image.append(pygame.image.load('images/alien_2.png'))
self.image = self.image[self.index]
self.image = pygame.transform.scale(self.image, (80 * int(self.settings.screen_width * 0.0019),
40 * int(self.settings.screen_width*0.0019)))
self.rect = self.image.get_rect()
random_height = random.uniform(0.01, 0.85)
random_width = random.uniform(1.1, 2)
self.rect.x = int(self.settings.screen_width * random_width)
self.rect.y = int(self.settings.screen_height * random_height)
# Store the alien's exact horizontal position.
self.x = float(self.rect.x)
def update(self):
"""Move the alien to left side."""
self.x -= self.settings.alien_speed
self.rect.x = self.x
if self.timer >= 0 and self.timer <= 25:
self.timer += 1
self.index = 0
elif self.timer >= 26 and self.timer < 50:
self.timer += 1
self.index = 1
else:
self.timer = 0
if self.index == 0:
self.image = pygame.image.load("images/alien_1.png")
if self.index == 1:
self.image = pygame.image.load("images/alien_2.png")
self.image = pygame.transform.scale(self.image, (80 * int(self.settings.screen_width * 0.0019),
40 * int(self.settings.screen_width * 0.0019)))
Edit: Of course in my main file I am calling function self.settings.increase_speed()
Edit2:
import pygame
resolution_width = 1280
resolution_height = 720
class Settings:
"""A class to store all settings for Space Impact."""
screen_width = resolution_width
alien_speed = screen_width * 0.003
speedup_scale = 3
alien_speed *= speedup_scale
def __init__(self):
"""Initialize the game's static settings."""
# Screen settings
# This line is needed to avoid error: No video mode has been set
self.screen = pygame.display.set_mode((0, 0))
self.screen_width = resolution_width
self.screen_height = resolution_height
self.bg_image = pygame.image.load("images/background_1.png").convert()
# Bullet settings
self.bullet_speed = self.screen_width*0.01
self.bullet_width = self.screen_width*0.02
self.bullet_height = self.screen_height*0.02
self.bullet_color = (0, 0, 0)
# How quickly the game speeds up
self.speedup_scale = 3
# Ship Settings
self.ships_limit = 3
def increase_speed(self):
"""Increase speed settings."""
global alien_speed
global speedup_scale
alien_speed *= speedup_scale
print(self.alien_speed)
Edit3:
I managed to fix it thanks to your comments. Thank you :)
Settings seems to contain global settings, but each Alien creates its own instance of Settings:
class Alien(Sprite):
"""A class to represent a single alien."""
def __init__(self, space_impact):
# [...]
self.settings = Settings()
That means that each Alien has its own Settings and thus alien_speed.
Either you've to update the settings in each instance of Alien, that means you've to call increase_speed() for each Alien separately.
Or just pass a singleton instance of Settings to Aliens. That makes it sufficient to update the singleton instance:
class Alien(Sprite):
"""A class to represent a single alien."""
def __init__(self, space_impact, settings):
"""Initialize alien and set it's starting position."""
super().__init__()
self.screen = space_impact.screen
self.settings = settings
# [...]
In "main":
alien = Alien(space_impact, self.settings)
Another option would be to turn the attributes of the class Settings to class attributes. Instance attribute are unique to each instance, but class attributes are shared by all instances.
I'm trying to create a basic game, but I'm fairly new to the python programming scene. I've come across a problem where with two classes (a player and enemy class), I want to access class variables like health from the player and enemy, and vice versa. What are some ways of doing this?
Here's the code to better emphasize what I'm asking:
class Player(object):
def __init__(self, image):
self.x = 100
self.y = 240
self.health = 30
self.defense = 25
self.image = image
self.black = (0, 0, 0)
self.draw()
def draw(self):
screen.blit(self.image, (self.x, self.y))
line = pygame.draw.rect(screen, self.black, (80, 300, 100, 5))
def attack(self):
pass
class Enemy(object):
def __init__(self, image):
self.x = 480
self.y = 240
self.health = 20
self.defense = 15
self.image = image
self.black = (0, 0, 0)
self.draw()
def draw(self):
screen.blit(self.image, (self.x, self.y))
line = pygame.draw.rect(screen, self.black, (460, 300, 100, 5))
def attack(self):
pass
Basically, I don't understand how I can take something like the "self.health" from one class, and easily access it from the other class. I've tried some methods of using return methods etc., but I'm curious if there are any simple ways to do this. Help is appreciated!
This code creates a class, and in the special __init__ method, it assigns values to various member variables.
class Player(object):
def __init__(self, image):
self.x = 100
self.y = 240
self.health = 30
self.defense = 25
self.image = image
self.black = (0, 0, 0)
self.draw()
These member variables are also called properties in Python. If you have a reference to a Player instance (an instance of the Player class):
p = Player()
You can access the properties all you like:
print(p.health)
Perhaps you need some kind of "main loop" or controller class that has access to players and enemies and can set their properties accordingly:
class MainLoop(object):
def __init__(self):
self.player_1 = Player()
self.enemy_1 = Enemy()
def run(self):
if fight(): # fight() is some function of your game
self.player_1.health -= 10
self.enemy_1.health -= 20
I am creating a small game that creates a tower.
The tower has two components, the base and the gun. In order to do so I am trying to pass the value of self.x into the __init__ function of a subclass. I will further add an update() function once I have a solution.
Here is my code. (Poorly formatted and incomplete... sorry!)
import pygame
from math import atan2
screen = pygame.display.set_mode((800, 800))
pygame.display.set_caption('Foongus v0.1')
pygame.init()
class home_base:
def __init__(self, x, y):
class base:
def __init__(self, x, y):
self.image = pygame.image.load('base.png')
screen.blit(self.image, (x, y))
class gun:
def __init__(self, x, y):
self.image = pygame.image.load('gun.png')
screen.blit(self.image, (x, y))
home = home_base(300, 300)
while True:
screen.fill((0,0,0))
pygame.display.update()
The following code should help guide you in the direction you should be going:
import math
import pygame
def main():
screen = pygame.display.set_mode((800, 800))
pygame.display.set_caption('Foondus v0.1')
pygame.init()
home = HomeBase(300, 300, screen)
while True:
screen.fill((0, 0, 0))
pygame.display.update()
class HomeBase:
def __init__(self, x, y, screen):
self.x, self.y, self.screen = x, y, screen
self.base_image = pygame.image.load('base.png')
self.gun_image = pygame.image.load('gun.png')
self.screen.blit(self.base_image, (self.x, self.y))
self.screen.blit(self.gun_image, (self.x, self.y))
if __name__ == '__main__':
main()
you wouldn't usually define a class inside the __init__ method of another class. instead, I would do something along these lines:
class A(object):
def __init__(self, instance_classB):
# here self is an instance of A and instance_classB an instance of B
pass
class B(object):
def __init__(self):
# self is the newly created instance of B
self._instance_classA = A(self)
# create an instance of B - calls B.__init__ on the new instance
b = B()
that way, you have instances of class B have a reference to an instance of class A in self._instance_classA, where each instance of class A has a backwards reference to the instance of class B it belongs to passed into the constructor.
I've got all my code working on its own. I need to start linking it all to buttons though.
QUESTION: trying to setup multiple buttons as sprites for collision purposes. Don't know how to do it outside of a class.
I have buttons working in seperate classes, but cannot get them to work in the same class for the obvious reason of, the self.image of the second one is overwriting the first one.
class Icons(pygame.sprite.Sprite):
def __init__(self, *args):
pygame.sprite.Sprite.__init__(self, *args)
self.image = pygame.image.load("images/airbrushIC.gif").convert()
self.rect = self.image.get_rect()
ic1 = self.image
self.rect.x = 50
self.rect.y = 490
self.image = pygame.image.load("images/fillIC.gif").convert()
self.rect = self.image.get_rect()
ic2 = self.image
self.rect.x = 10
self.rect.y = 540
def update(self):
pygame.mouse.get_pos()
pygame.mouse.get_pressed()
This code doesn't have to be a class. But I do not know how to make the images a sprite without being inside of a class. Any help would be appriciated thanks!
Instead of Icons you should have a generic Icon class.
Then you can create an instance of Icon for each button.
class Icon(pygame.sprite.Sprite):
def __init__(self, image_name, pos, cb, cb_data, *args):
pygame.sprite.Sprite.__init__(self, *args)
self.image = pygame.image.load("images/" + image_name).convert()
self.rect = self.image.get_rect()
self.rect.x = pos[0]
self.rect.y = pos[1]
this.cb = cb # function to call when button is pressed
this.cb_data = cb_data # data to pass to the function
def pressed():
this.cb(cb_data)
Then in you main function you create the buttons:
ic1 = Icon("airbrushIC.gif", (50, 490), button_pressed, "airbrushIC")
ic2 = Icon("fillIC.gif", (10, 540), button_pressed, "fillIC")
buttons = [ic1, ic2]
def button_pressed(data):
print "Button pressed:" + str(data)
Last, for every mouse down event you look for a button collition:
for b in buttons:
if b.rect.collidepoint(event.pos):
b.pressed()