I am trying to understand the tilemaps in pygame, and to learn how to use Tiled Map Editor, but I can't succeed to load the map in the pygame.
Here is the code:
import pygame
import pytmx
pygame.init()
display = pygame.display.set_mode((600,400))
clock = pygame.time.Clock()
gameMap = pytmx.TiledMap("map.tmx")
while(True):
clock.tick(60)
keys = pygame.key.get_pressed()
for event in pygame.event.get():
if(event.type == pygame.QUIT):
quit()
if(keys[pygame.K_ESCAPE]):
quit()
for layer in gameMap.visible_layers:
for x, y, gid, in layer:
tile = gameMap.get_tile_image_by_gid(gid)
if(tile != None):
display.blit(tile, (x * gameMap.tilewidth, y * gameMap.tileheight))
pygame.display.update()
It keep giving me this error:
Traceback (most recent call last):
File "main.py", line 27, in
display.blit(tile, (x * gameMap.tilewidth, y * gameMap.tileheight))
TypeError: argument 1 must be pygame.Surface, not tuple
I know that when I print the tile in console this is what I get
(a tuple):
('img/NES - Super Mario Bros - Tileset.png', (0, 144, 16, 16), TileFlags(flipped_horizontally=False, flipped_vertically=False, flipped_diagonally=False)).
What is the simplest method to successfully load a tilemap in pygame and what am I doing wrong?
use:
gameMap = pytmx.load_pygame("map.tmx")
instead of:
gameMap = pytmx.TiledMap("map.tmx")
I think that the following covers most of the mechanics of rendering the mapSurface and drawing something on it with update and loadMap. I don't have any real TMX files to test with. ymmv. Good luck.
import time
import pygame
import pytmx
class TiledMap():
""" This is creating the surface on which you make the draw updates """
def __init__(self):
self.gameMap = pytmx.load_pygame("map.tmx", pixelalpha=True)
self.mapwidth = self.gameMap.tilewidth * self.gameMap.width
self.mapheight = self.gameMap.tileheight * self.gameMap.height
def render(self, surface):
for layer in self.gameMap.visible_layers:
if isinstance(layer, pytmx.TiledTileLayer):
for x, y, gid in layer:
tile = self.gameMap.get_tile_image_by_gid(gid)
if tile:
surface.blit(tile, (x * self.gameMap.tilewidth, y * self.gameMap.tileheight))
def make_map(self):
mapSurface = pygame.Surface((self.mapwidth, self.mapheight))
self.render(mapSurface)
return mapSurface
pygame.init()
class Display():
""" This is the class that makes the changes that you want to display. You would add most of your changes here. """
def __init__(self):
self.displayRunning = True
self.displayWindow = pygame.display.set_mode((600, 400))
self.clock = pygame.time.Clock()
def update(self):
pygame.display.set_caption("{:.2f}".format(self.clock.get_fps()))
pygame.display.update()
def loadMap(self):
self.map = TiledMap()
self.map_img = self.map.make_map()
self.map_rect = self.map_img.get_rect()
def displayLoop(self):
self.clock.tick()
self.update()
self.loadMap()
# Here is the start of the main driver
runDisplay = Display()
runDisplay.update()
runDisplay.loadMap()
time.sleep(60)
If you want it to run in a loop then you would change that bottom driver block to something like:
runDisplay = Display()
while runDisplay.displayRunning is True:
runDisplay.displayLoop()
Related
Currently, I am loading all images at the start of the program. Is there a better solution for this? As some images may not be used at all throughout the entire game (I'm currently making a main menu).
Main.py
import pygame as pg
from Menu import Menu
pg.init()
"""Displays screen"""
winWidth = 1920
winHeight = 1080
size = (winWidth, winHeight)
win = pg.display.set_mode(size)
"""Load images"""
bg_img = pg.image.load('Pic/td-gui/PNG/menu/bg.png')
title_img = pg.image.load('transtitle.png')
settings_img = pg.image.load('Pic/td-gui/PNG/menu/button_settings.png')
play_img = pg.image.load('Pic/td-gui/PNG/menu/button_play.png')
reg_img = pg.image.load('Pic/td-gui/PNG/menu/button_registration.png')
quit_img = pg.image.load('Custom/Menu/Quit.png')
rope_img = pg.image.load('Pic/td-gui/PNG/menu/rope_small.png')
changeDiff_img = pg.image.load('Custom/Menu/Change_Difficulty.png')
score_img = pg.image.load('Custom/Menu/Scoreboard.png')
selectmap_img = pg.image.load('Custom/Menu/Select_map.png')
tutorial_img = pg.image.load('Custom/Menu/Tutorial.png')
"""Puts loaded images into the correct size"""
bg_img = pg.transform.scale(bg_img,(winWidth, winHeight))
title_img = pg.transform.scale(title_img, (750, 75))
play_img = pg.transform.scale(play_img, (250, 250))
rope_img = pg.transform.scale(rope_img, (27, 290))
clock = pg.time.Clock()
win.blit(bg_img, (0, 0))
mainMenu = True
settingsMenu = False
run = True
running = True
while run:
clock.tick(30)
pg.display.update()
for event in pg.event.get():
if event.type == pg.QUIT:
run = False
if not run:
pg.quit()
"""Places correct images in specified location"""
title = Menu(winWidth // 2 - 375, winHeight // 2 - 500, title_img)
if mainMenu:
reg_button = Menu(winWidth // 2 - 150, winHeight // 2 - 100, reg_img)
ropeR = Menu(winWidth // 2 - 100, 200, rope_img)
ropeL = Menu(winWidth // 2 + 70, 200, rope_img)
scoreboard_button = Menu(winWidth//2 - 330, winHeight//2 + 275, score_img)
play_button = Menu(winWidth // 2 - 125, winHeight // 2 - 375, play_img)
reg_button = Menu(winWidth // 2 - 161, winHeight // 2 - 100, reg_img)
quit_button = Menu(winWidth//2 + 30, winHeight//2 + 275, quit_img)
changeDiff_button = Menu(winWidth//2 - 330, winHeight//2 + 80, changeDiff_img)
selectmap_button = Menu(winWidth//2 + 30, winHeight//2 + 80, selectmap_img)
settings_button = Menu(winWidth - 200, winHeight - 200, settings_img)
"""Draws images in specified location and makes buttons if specified"""
title.draw_img(win)
if reg_button.draw_button(win):
print('Reg button clicked')
ropeL.draw_img(win)
ropeR.draw_img(win)
if scoreboard_button.draw_button(win):
print("options Clicked")
if play_button.draw_button(win):
print("Play Clicked")
if quit_button.draw_button(win):
pg.quit()
if changeDiff_button.draw_button(win):
print('Change Difficulty button pressed')
if selectmap_button.draw_button(win):
print('Select Map button preseed')
if settings_button.draw_button(win):
print('Settings button pressed')
mainMenu = False
settingsMenu = True
if settingsMenu:
win.blit(bg_img, (0, 0))
title.draw_img(win)
Menu.py
import pygame as pg
pg.init()
class Menu():
def __init__(self, x, y, img):
self.img = img
self.rect = self.img.get_rect()
self.rect.y = y
self.rect.x = x
self.clicked = False
def draw_button(self, win):
action = False
#get mouse position
pos = pg.mouse.get_pos()
#check mouseover and clicked conditions
if self.rect.collidepoint(pos):
if pg.mouse.get_pressed()[0] == 1 and not self.clicked:
action = True
self.clicked = True
if pg.mouse.get_pressed()[0] == 0:
action = False
#draw image
win.blit(self.img, (self.rect.x, self.rect.y))
return action
def draw_img(self, win):
win.blit(self.img, (self.rect.x, self.rect.y))
Say I'm making a music button, which is accessible through the settings button, I would currently load the music button image at the start of the program. Is there a way to make it so the music image would only be loaded once the settings button has been pressed?
To give a quick and simple overview of what you could do, here's an example. You define an abstract Scene class. Then, every "scene" in your game inherits from it. Each scene has a setup method that loads everything the scene needs (or might need).
import pygame
class Scene:
def __init__(self):
""" Create all attributes """
def setup(self):
""" Load everything in and initialize attributes """
def handle_events(self, events):
""" Handle the events for this scene """
def update(self, dt):
""" Run logic """
def render(self, screen):
""" Draw to the screen """
class Menu(Scene):
def __init__(self):
""" Create all attributes """
self.image1 = None
self.image2 = None
# ... and so on ...
def setup(self):
""" Load everything in and initialize attributes """
self.image1 = pygame.image.load("my_image1.png")
self.image2 = pygame.image.load("my_image2.png")
# ... and so on ...
def handle_events(self, events):
""" Handle the events for this scene """
def update(self, dt):
""" Run logic """
def render(self, screen):
""" Draw to the screen """
screen.blit(self.image1, (0, 0))
# ... and so on ...
class Game(Scene):
def __init__(self):
""" Create all attributes """
self.player = None
self.enemies = None
# ... and so on ...
def setup(self):
""" Load everything in and initialize attributes """
self.player = Player(position=(100, 100), image=pygame.image.load("player.png"))
self.enemies = [
Enemy(position=(200, 400), pygame.image.load("enemy1.png")),
Enemy(position=(200, 500), pygame.image.load("enemy2.png")),
]
# ... and so on ...
def handle_events(self, events):
""" Handle the events for this scene """
for event in events:
if event.type == pygame.KEY_DOWN:
print('PAM!')
def update(self, dt):
""" Run logic """
self.player.update(dt)
for enemy in self.enemies:
enemy.update()
def render(self, screen):
""" Draw to the screen """
screen.blit(self.player.image, self.player.position)
for enemy in self.enemies:
screen.blit(enemy.image, enemy.position)
# ... and so on ...
Here's roughly how a game loop would look like.
def main():
screen = pygame.display.set_mode((1024, 1024))
clock = pygame.time.Clock()
running = True
scene = Menu()
scene.setup()
while running:
dt = clock.tick(60)
events = list(pygame.event.get())
scene.handle_events(events)
scene.update(dt)
scene.render(screen)
# Have some way of determining when to switch scene.
if should_switch_to_game:
scene = Game()
scene.setup()
elif should_switch_to_menu:
scene = Menu()
scene.setup()
# ... and so on ...
This is a basic starting point, but there are other ways to handle the situation, like having an asset manager that dynamically load and unload assets based on usage. However, these are quite complicated and will make development and debugging much harder. It's always best to keep things simple when you can. If you can load all images at the beginning of the program, then I'd suggest doing so.
Also, remember that there's nothing that's "best" or "optimized" as all you're doing is tradeoffs. If you're trying to optimize for memory, then you're often making your program slower. If you're optimizing for speed, then you're often making your program use more memory.
If you are loading things just when you need them, you're going to have a loading time switching between different views in your menu. This is often not something you want as the user expects it to be fast. Switching from the game to the menu, it's more forgiving to have a couple of extra ms. But if you feel you want to go the other way then different views in the menu could be considered different scenes.
In general, no it's not a problem to load images that are never used, as long as the images are small. It's better to load them all at once so that the interface never has to wait for loading.
However, if you have a very large amount of images or your images are very big, it might be worth loading some images only when you need them. You could do something like this when you start your music field:
def start_music_menu():
global music_icon # Sorry about the global
if music_icon is None:
music_icon = pygame.image.load('...')
In this case your interface will freeze while loading the extra image. You can also use threading or asyncio to keep your interface responsive while a image is loading.
Properly use OOP concept make separate class for various loading image and then use them accordingly .
I use Python 3.7.4 and Pygame 1.9.6. I'm trying to create just a test game so I can save data like a score per say using pickle this is my first time. But I'll get down that path once I'm done with the test game.
I'm getting an error two separate occasions in ball(ballX[i], ballY[i], i) and in screen.blit(ballImg, (x, y), i). It's a TypeError: arugument 1 must be pygame.Surface, not list.
Full Trace Back:
Traceback (most recent call last):
File "C:Test/main.py", line 128, in <module>
ball(ballX[i], ballY[i], i)
File "C:/Test/main.py", line 66, in ball
screen.blit(ballImg, (x, y), i)
The code:
import pygame
import random
# Ball
ballImg = []
ballX = []
ballY = []
num_of_balls = 4
for i in range(num_of_balls):
ballImg.append(pygame.image.load('ball.png'))
ballX.append(random.randint(0, 736))
ballY.append(random.randint(50, 535))
'''This code is above the while running loop'''
def ball(x, y, i):
screen.blit(ballImg, (x, y), i)
running = True
while running:
'''The key stuff and what not'''
'''I have the screen color load before the image appears'''
for i in range(num_of_balls):
ball(ballX[i], ballY[i], i)
pygame.display.update()
I'm not understanding what I'm doing wrong in my function/list or evening bliting the image. I feel like I'm doing everything right filling out the parameters, etc. I appreciate the advice. So thank you.
A few things:
As #Ewong mentioned, you are passing ballImg to blit instead of ballImg[i]
You're not processing OS messages, so the program will freeze
Try this code:
import pygame
import random
scr_width = 800
scr_height = 600
screen = pygame.display.set_mode((scr_width, scr_height))
# Ball
ballImg = []
ballX = []
ballY = []
num_of_balls = 4
for i in range(num_of_balls):
ballImg.append(pygame.image.load('ball.png'))
ballX.append(random.randint(0, 736))
ballY.append(random.randint(50, 535))
#'''This code is above the while running loop'''
def ball(x, y, i):
screen.blit(ballImg[i], (x, y))
running = True
while running:
#'''The key stuff and what not'''
#'''I have the screen color load before the image appears'''
for i in range(num_of_balls):
ball(ballX[i], ballY[i], i)
for event in pygame.event.get(): # process OS messages
if event.type == pygame.QUIT:
pygame.quit()
break
pygame.display.update()
I am following the instructions of a pygame project on youtube, this is the video, I am on the project called "Snake", and in the description of the video, you can find what time it starts and the actual code. I am about 10 minutes into the video. I will show you the code that I have written so far:
# Snake project on python
import math
import random
import pygame
import tkinter as tk
from tkinter import messagebox
class cube(object):
rows = 0
w = 0
def __init__(self, start,dirnx=1, dirny=0, colour=(255,0,0)):
pass
def move(self, dirnx, dirny):
pass
def draw(self, surface, eyes=False):
pass
class snake(object):
def __init__(self, color, pos):
pass
def move(self):
pass
def reset(self, pas):
pass
def addCube(self):
pass
def draw(self, surface):
pass
def drawGrid(w, rows, surface):
sizeBtwn = w // rows
x = 0
y = 0
for l in range(rows):
x = x + sizeBtwn
y = y + sizeBtwn
pygame.draw.line(surface, (255,255,255), (x,0), (x,w))
pygame.draw.line(surface, (255,255,255), (0,y), (w,y))
def redrawWindow(surface):
global rows, width
surface.fill(0,0,0)
drawGrid(width, rows, surface)
pygame.display.update()
def randomSnack(rows, items):
pass
def message_box(subject, content):
pass
def main():
global width, rows
width = 500
rows = 20
win = pygame.display.set_mode((width, width))
s = snake((255, 0, 0), (10, 10))
flag = True
clock = pygame.time.Clock()
while flag:
pygame.time.delay(50)
clock.tick(10)
redrawWindow(win)
main()
When I run the code the tutorial says I should be getting a grid (shown in 55:32 of the video). Instead, I am receiving this message:
Windows PowerShell
Try the new cross-platform PowerShell https://aka.ms/pscore6
PS C:\Users\holca\Desktop\Snake> & C:/Users/holca/AppData/Local/Programs/Python/Python38-32/python.exe c:/Users/holca/Desktop/Snake/snake.py
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
Traceback (most recent call last):
File "c:/Users/holca/Desktop/Snake/snake.py", line 77, in <module>
main()
File "c:/Users/holca/Desktop/Snake/snake.py", line 74, in main
redrawWindow(win)
File "c:/Users/holca/Desktop/Snake/snake.py", line 51, in redrawWindow
surface.fill(0,0,0)
ValueError: invalid rectstyle object
PS C:\Users\holca\Desktop\Snake>
I am using VSCode, and I have 2 warning messages talking about some unused variables, but I doubt they have to do anything with the problem. Am I missing something?
You have to specify the color by a tuple:
surface.fill(0,0,0)
surface.fill( (0, 0, 0) )
Explanation:
The first argument of fill() is the color (tuple). The other arguments are optional. The 2nd argument is an optional rectangle specifying area to be filled. The 3rd arguments are optional flags.
Hence the instruction surface.fill(0,0,0) can be read as:
surface.fill(0, rect=0, special_flags=0)
The 2nd argument (rect=0) causes the error
ValueError: invalid rectstyle object
Now below what i did is simply created a sprite as shown..
futher to do something interesting,thought of threading i added a thread which will check the cursor position, hence update global x & y resulting in change of sprite position that is trigered by display.update
I am newbie so i might me wrong in many ways ...so please bear me....
a lot thanks in advance
from pygame import *
from main import *
import threading
import sys
#lets add our sprites
(x,y) = (0, 0)
class threading1(threading.Thread):
def run(self):
global x,y
clockobj1 = pygame.time.Clock()
while (True):
clockobj1.tick(6)
(x,y)=pygame.mouse.get_pos()
display.update()
class sprites(pygame.sprite.Sprite ):
def __init__(self,color= redd, width=120, height=120):
super(sprites,self).__init__()
self.image = pygame.Surface((width,height))
self.image.fill(color)
self.rect=self.image.get_rect()
self.rect.move_ip(x,y)
display.update()
if __name__ == '__main__':
clockobj = pygame.time.Clock()
init()
mainwin = pygame.display.set_mode((720,640))
sprite1 = sprites()
spritegrp = pygame.sprite.Group()
spritegrp.add(sprite1)
spritegrp.update()
mainwin.fill(blue)
spritegrp.draw(mainwin)
threadingobj = threading1()
threadingobj.start()
x = True
while(x):
display.update()
for evt in event.get() :
if (evt.type == QUIT) :
quit()
x=False
clockobj.tick(40)
***BELOW IS MY LATEST CODE----------UPDATED AS PER ANSWERS***PLEASE CHECK
import pygame
from main import *
import threading
import sys
# lets add our sprites
class Sprites(pygame.sprite.Sprite ):
def __init__(self, color=redd, width=120, height=120):
super().__init__()
self.image = pygame.Surface((width, height))
self.image.fill(color)
self.rect = self.image.get_rect()
def updaterect(self):
print("i m in updatereact")
print(pygame.mouse.get_pos())
self.rect.center= pygame.mouse.get_pos()
pygame.display.update()
if __name__ == '__main__':
clockobj = pygame.time.Clock()
pygame.init()
mainwin = pygame.display.set_mode((720,640))
sprite1 = Sprites()
sprite1.updaterect()
spritegrp = pygame.sprite.Group()
spritegrp.add(sprite1)
spritegrp.update()
mainwin.fill(blue)
spritegrp.draw(mainwin)
x = True
while x:
sprite1.updaterect()
pygame.display.update()
for evt in pygame.event.get() :
if evt.type == pygame.QUIT :
quit()
x=False
Threading will only just complicate things. Get rid of it, and take self.rect.move_ip(x,y) and display.update() out of the __init__ for the class. Make a function in the class called update(). This function will move the rect just by saying. self.rect.center = pygame.mouse.get_pos(). Then in the main game loop, put sprite1.update() in there or update it by using the group name instead (as you did with spritegrp.update() ) and then call display.update() there too instead of in the function.
Other things:
super().__init__() doesn't need any args
if and while loops don't need parentheses
you don't need to import main (I may be wrong about this but I don't think so)
from module import * is a bad practice in general, but if you are going to do it (I don't recommend it), you can't put module.method anywhere. You did from pygame import *, but still put pygame.mouse and others like that. Maybe you meant from pygame.locals import * ?
colons don't have a space in between them and the word, i.e. for evt in event.get() : is bad
Indentation should also be the length of a tab, or four spaces. (What IDE are you using? Most do it automatically.)
variables assignments should have spaces: x = False
I'm trying to make a basic (Mario style) game but my sprite(plumber) doesn't appear, it could be hidden behind background? i'm not exactly sure, i am not getting any errors either.
import pygame
import sys
import itertools
import pygame
from pygame.sprite import Sprite
cloud_background = pygame.image.load('clouds.bmp')
brick_tile = pygame.image.load('brick_tile.png')
pink = (255, 64, 64)
w = 640
h = 480
screen = pygame.display.set_mode((w, h))
running = 1
def setup_background():
screen.fill((pink))
screen.blit(cloud_background,(0,0))
brick_width, brick_height = brick_tile.get_width(), brick_tile.get_height()
for x,y in itertools.product(range(0,640,brick_width),
range(390,480,brick_height)):
# print(x,y)
screen.blit(brick_tile, (x, y))
pygame.display.flip()
while running:
setup_background()
event = pygame.event.poll()
if event.type == pygame.QUIT: sys.exit()
class plumber(sprite):
def __init__(
self, screen, img_filename, init_position,
init_direction, speed):
Sprite.__init__(self)
self.screen = screen
self.speed = speed
self.base_image = pygame.image.load(Mario_sideways_sprite_2xL.png).convert_alpha()
self.image = self.base_image
self.pos = 50,50
First problem found is that you must modify
pygame.image.load(Mario_sideways_sprite_2xL.png)
with something like.
pygame.image.load("Mario_sideways_sprite_2xL.png")
Besides this, the code has many problems that impedes it to work. For example,
you do not instantiate your plumber class.
class plumber(sprite) should be plumber(Sprite) (still better Plumber(Sprite))
You need something like:
myplumber = Plumber()
allsprites = pygame.sprite.RenderPlain((myplumber, ....))
clock = pygame.time.Clock()
You could see here the main parts of a simple program like yours.