zoukankan      html  css  js  c++  java
  • pygame-KidsCanCode系列jumpy-part9-使用spritesheet

    做过前端的兄弟应该都知道css sprite(也称css精灵),这是一种常用的减少http请求次数的优化手段。把很多小图拼成一张大图,只加载1次,然后用css定位到不区的区域,从而展示不同的图片。游戏中也是类似的道理,如下图:

    每一个小图片,都有自己的x,y,width,height信息,通常会放在一个xml中,类似:

      1 <TextureAtlas imagePath="sprites.png">
      2     <SubTexture name="bronze_1.png" x="707" y="296" width="84" height="84"/>
      3     <SubTexture name="bronze_2.png" x="826" y="206" width="66" height="84"/>
      4     <SubTexture name="bronze_3.png" x="899" y="116" width="50" height="84"/>
      5     <SubTexture name="bronze_4.png" x="670" y="406" width="14" height="84"/>
      6     <SubTexture name="bubble.png" x="0" y="1662" width="211" height="215"/>
      7     <SubTexture name="bunny1_hurt.png" x="382" y="946" width="150" height="174"/>
      8     <SubTexture name="bunny1_jump.png" x="382" y="763" width="150" height="181"/>
      9     <SubTexture name="bunny1_ready.png" x="614" y="1063" width="120" height="191"/>
     10     <SubTexture name="bunny1_stand.png" x="690" y="406" width="120" height="201"/>
     11     <SubTexture name="bunny1_walk1.png" x="678" y="860" width="120" height="201"/>
     12     <SubTexture name="bunny1_walk2.png" x="692" y="1458" width="120" height="207"/>
     13     <SubTexture name="bunny2_hurt.png" x="411" y="1866" width="150" height="174"/>
     14     <SubTexture name="bunny2_jump.png" x="416" y="1660" width="150" height="181"/>
     15     <SubTexture name="bunny2_ready.png" x="581" y="1265" width="121" height="191"/>
     16     <SubTexture name="bunny2_stand.png" x="584" y="0" width="121" height="201"/>
     17     <SubTexture name="bunny2_walk1.png" x="584" y="203" width="121" height="201"/>
     18     <SubTexture name="bunny2_walk2.png" x="678" y="651" width="121" height="207"/>
     19     <SubTexture name="cactus.png" x="707" y="134" width="117" height="160"/>
     20     <SubTexture name="carrot.png" x="820" y="1733" width="78" height="70"/>
     21     <SubTexture name="carrot_gold.png" x="814" y="1661" width="78" height="70"/>
     22     <SubTexture name="carrots.png" x="812" y="554" width="54" height="49"/>
     23     <SubTexture name="cloud.png" x="0" y="1152" width="260" height="134"/>
     24     <SubTexture name="coin_bronze.png" x="329" y="1390" width="60" height="61"/>
     25     <SubTexture name="coin_gold.png" x="244" y="1981" width="61" height="61"/>
     26     <SubTexture name="coin_silver.png" x="307" y="1981" width="61" height="61"/>
     27     <SubTexture name="flame.png" x="900" y="1733" width="41" height="80"/>
     28     <SubTexture name="flyMan_fly.png" x="566" y="510" width="122" height="139"/>
     29     <SubTexture name="flyMan_jump.png" x="568" y="1534" width="122" height="135"/>
     30     <SubTexture name="flyMan_stand.png" x="568" y="1671" width="122" height="139"/>
     31     <SubTexture name="flyMan_still_fly.png" x="692" y="1667" width="120" height="132"/>
     32     <SubTexture name="flyMan_still_jump.png" x="698" y="1801" width="120" height="128"/>
     33     <SubTexture name="flyMan_still_stand.png" x="707" y="0" width="120" height="132"/>
     34     <SubTexture name="gold_1.png" x="698" y="1931" width="84" height="84"/>
     35     <SubTexture name="gold_2.png" x="829" y="0" width="66" height="84"/>
     36     <SubTexture name="gold_3.png" x="897" y="1574" width="50" height="84"/>
     37     <SubTexture name="gold_4.png" x="645" y="651" width="15" height="84"/>
     38     <SubTexture name="grass1.png" x="868" y="1877" width="58" height="57"/>
     39     <SubTexture name="grass2.png" x="784" y="1931" width="82" height="70"/>
     40     <SubTexture name="grass_brown1.png" x="534" y="1063" width="58" height="57"/>
     41     <SubTexture name="grass_brown2.png" x="801" y="752" width="82" height="70"/>
     42     <SubTexture name="ground_cake.png" x="0" y="576" width="380" height="94"/>
     43     <SubTexture name="ground_cake_broken.png" x="0" y="0" width="380" height="94"/>
     44     <SubTexture name="ground_cake_small.png" x="218" y="1456" width="201" height="100"/>
     45     <SubTexture name="ground_cake_small_broken.png" x="262" y="1152" width="200" height="100"/>
     46     <SubTexture name="ground_grass.png" x="0" y="288" width="380" height="94"/>
     47     <SubTexture name="ground_grass_broken.png" x="0" y="384" width="380" height="94"/>
     48     <SubTexture name="ground_grass_small.png" x="213" y="1662" width="201" height="100"/>
     49     <SubTexture name="ground_grass_small_broken.png" x="382" y="204" width="200" height="100"/>
     50     <SubTexture name="ground_sand.png" x="0" y="672" width="380" height="94"/>
     51     <SubTexture name="ground_sand_broken.png" x="0" y="1056" width="380" height="94"/>
     52     <SubTexture name="ground_sand_small.png" x="208" y="1879" width="201" height="100"/>
     53     <SubTexture name="ground_sand_small_broken.png" x="382" y="102" width="200" height="100"/>
     54     <SubTexture name="ground_snow.png" x="0" y="768" width="380" height="94"/>
     55     <SubTexture name="ground_snow_broken.png" x="0" y="480" width="380" height="94"/>
     56     <SubTexture name="ground_snow_small.png" x="213" y="1764" width="201" height="100"/>
     57     <SubTexture name="ground_snow_small_broken.png" x="382" y="306" width="200" height="100"/>
     58     <SubTexture name="ground_stone.png" x="0" y="96" width="380" height="94"/>
     59     <SubTexture name="ground_stone_broken.png" x="0" y="192" width="380" height="94"/>
     60     <SubTexture name="ground_stone_small.png" x="382" y="408" width="200" height="100"/>
     61     <SubTexture name="ground_stone_small_broken.png" x="232" y="1288" width="200" height="100"/>
     62     <SubTexture name="ground_wood.png" x="0" y="960" width="380" height="94"/>
     63     <SubTexture name="ground_wood_broken.png" x="0" y="864" width="380" height="94"/>
     64     <SubTexture name="ground_wood_small.png" x="218" y="1558" width="200" height="100"/>
     65     <SubTexture name="ground_wood_small_broken.png" x="382" y="0" width="200" height="100"/>
     66     <SubTexture name="jetpack.png" x="563" y="1843" width="133" height="160"/>
     67     <SubTexture name="jetpack_item.png" x="852" y="1089" width="65" height="77"/>
     68     <SubTexture name="lifes.png" x="868" y="1936" width="52" height="71"/>
     69     <SubTexture name="lighting_blue.png" x="895" y="453" width="55" height="114"/>
     70     <SubTexture name="lighting_yellow.png" x="897" y="0" width="55" height="114"/>
     71     <SubTexture name="mushroom_brown.png" x="814" y="1574" width="81" height="85"/>
     72     <SubTexture name="mushroom_red.png" x="812" y="453" width="81" height="99"/>
     73     <SubTexture name="particle_beige.png" x="563" y="2005" width="58" height="41"/>
     74     <SubTexture name="particle_blue.png" x="852" y="1168" width="51" height="49"/>
     75     <SubTexture name="particle_brown.png" x="820" y="1877" width="44" height="48"/>
     76     <SubTexture name="particle_darkBrown.png" x="784" y="2003" width="54" height="43"/>
     77     <SubTexture name="particle_darkGrey.png" x="826" y="1364" width="51" height="43"/>
     78     <SubTexture name="particle_green.png" x="800" y="1003" width="48" height="46"/>
     79     <SubTexture name="particle_grey.png" x="623" y="2005" width="38" height="41"/>
     80     <SubTexture name="particle_pink.png" x="829" y="86" width="53" height="46"/>
     81     <SubTexture name="portal_orange.png" x="0" y="1288" width="230" height="82"/>
     82     <SubTexture name="portal_orangeParticle.png" x="262" y="1254" width="20" height="20"/>
     83     <SubTexture name="portal_yellow.png" x="0" y="1372" width="230" height="82"/>
     84     <SubTexture name="portal_yellowParticle.png" x="284" y="1254" width="20" height="20"/>
     85     <SubTexture name="powerup_bubble.png" x="826" y="134" width="71" height="70"/>
     86     <SubTexture name="powerup_bunny.png" x="826" y="1220" width="71" height="70"/>
     87     <SubTexture name="powerup_empty.png" x="894" y="1661" width="71" height="70"/>
     88     <SubTexture name="powerup_jetpack.png" x="820" y="1805" width="71" height="70"/>
     89     <SubTexture name="powerup_wings.png" x="826" y="1292" width="71" height="70"/>
     90     <SubTexture name="silver_1.png" x="584" y="406" width="84" height="84"/>
     91     <SubTexture name="silver_2.png" x="852" y="1003" width="66" height="84"/>
     92     <SubTexture name="silver_3.png" x="899" y="1219" width="50" height="84"/>
     93     <SubTexture name="silver_4.png" x="662" y="651" width="14" height="84"/>
     94     <SubTexture name="smoke.png" x="879" y="1364" width="51" height="48"/>
     95     <SubTexture name="spikeBall1.png" x="534" y="763" width="142" height="148"/>
     96     <SubTexture name="spikeBall_2.png" x="464" y="1122" width="148" height="141"/>
     97     <SubTexture name="spikeMan_jump.png" x="736" y="1063" width="114" height="155"/>
     98     <SubTexture name="spikeMan_stand.png" x="814" y="1417" width="90" height="155"/>
     99     <SubTexture name="spikeMan_walk1.png" x="704" y="1256" width="120" height="159"/>
    100     <SubTexture name="spikeMan_walk2.png" x="812" y="296" width="90" height="155"/>
    101     <SubTexture name="spike_bottom.png" x="894" y="206" width="51" height="87"/>
    102     <SubTexture name="spike_top.png" x="885" y="752" width="51" height="87"/>
    103     <SubTexture name="spikes_bottom.png" x="147" y="1988" width="95" height="53"/>
    104     <SubTexture name="spikes_top.png" x="232" y="1390" width="95" height="53"/>
    105     <SubTexture name="spring.png" x="420" y="1558" width="145" height="77"/>
    106     <SubTexture name="springMan_hurt.png" x="800" y="860" width="110" height="141"/>
    107     <SubTexture name="springMan_stand.png" x="801" y="609" width="110" height="141"/>
    108     <SubTexture name="spring_in.png" x="0" y="1988" width="145" height="57"/>
    109     <SubTexture name="spring_out.png" x="434" y="1265" width="145" height="110"/>
    110     <SubTexture name="sun1.png" x="534" y="913" width="142" height="148"/>
    111     <SubTexture name="sun2.png" x="421" y="1390" width="148" height="142"/>
    112     <SubTexture name="wingMan1.png" x="382" y="635" width="174" height="126"/>
    113     <SubTexture name="wingMan2.png" x="0" y="1879" width="206" height="107"/>
    114     <SubTexture name="wingMan3.png" x="0" y="1559" width="216" height="101"/>
    115     <SubTexture name="wingMan4.png" x="0" y="1456" width="216" height="101"/>
    116     <SubTexture name="wingMan5.png" x="382" y="510" width="182" height="123"/>
    117     <SubTexture name="wing_left.png" x="558" y="651" width="85" height="74"/>
    118     <SubTexture name="wing_right.png" x="571" y="1458" width="85" height="74"/>
    119 </TextureAtlas>
    View Code

    上面这张图+这份xml,在pygame中我们称为spritesheet,通过xml中的name,找到对应的坐标、尺寸信息,就能取出指定的小图片,先研究下如何用python解析xml:

     1 from os import path
     2 from xml.dom.minidom import parse
     3 
     4 xml_file_path = path.join(path.dirname(__file__), "../img/spritesheet_jumper.xml")
     5 print(xml_file_path)
     6 
     7 dom_tree = parse(xml_file_path)
     8 root_textures = dom_tree.documentElement
     9 sub_textures = root_textures.getElementsByTagName("SubTexture")
    10 
    11 for texture in sub_textures:
    12     print("name:", texture.getAttribute("name"), ",x:",
    13           texture.getAttribute("x"), ",y:", texture.getAttribute("y"),
    14           ",", texture.getAttribute("width"), ",height:", texture.getAttribute("height"))
    View Code

    注:相对目录结构,参考下图:

    上面这几行代码,就能把xml解析出来,输出类似下面这样:

    ...
    name: bronze_1.png ,x: 707 ,y: 296 , 84 ,height: 84
    name: bronze_2.png ,x: 826 ,y: 206 , 66 ,height: 84
    name: bronze_3.png ,x: 899 ,y: 116 , 50 ,height: 84
    name: bronze_4.png ,x: 670 ,y: 406 , 14 ,height: 84
    ...

    知道了xml如何解析,下面就是如何取出指定小图片,借助pygame的最小框架,做一个测试:

     1 import pygame
     2 from os import path
     3 from xml.dom.minidom import parse
     4 
     5 # 游戏中的一些常量定义
     6 SIZE = (WIDTH, HEIGHT) = (380, 380)
     7 FPS = 10
     8 
     9 # 颜色常量定义
    10 BLACK = 0, 0, 0
    11 WHITE = 255, 255, 255
    12 
    13 # 初始化
    14 pygame.init()
    15 pygame.mixer.init()
    16 
    17 # 窗口、标题等初始化
    18 screen = pygame.display.set_mode(SIZE)
    19 pygame.display.set_caption("My Game")
    20 clock = pygame.time.Clock()
    21 
    22 spritesheet_image_file_name = path.join(path.dirname(__file__), "../img/spritesheet_jumper.png")
    23 spritesheet_xml_file_name = path.join(path.dirname(__file__), "../img/spritesheet_jumper.xml")
    24 spritesheet_image = pygame.image.load(spritesheet_image_file_name)
    25 spritesheet_image.set_colorkey(BLACK)
    26 spritesheet_dom_tree = parse(spritesheet_xml_file_name)
    27 root_textures = spritesheet_dom_tree.documentElement
    28 sub_textures = root_textures.getElementsByTagName("SubTexture")
    29 dic_image = {}
    30 
    31 
    32 # 解析xml,找出指定图片的坐标、尺寸信息
    33 def get_image_rect(img_name):
    34     if dic_image.get(img_name):
    35         return dic_image[img_name]
    36     for texture in sub_textures:
    37         name = texture.getAttribute("name")
    38         if img_name == name:
    39             dic_image[img_name] = pygame.Rect(
    40                 int(texture.getAttribute("x")),
    41                 int(texture.getAttribute("y")),
    42                 int(texture.getAttribute("width")),
    43                 int(texture.getAttribute("height"))
    44             )
    45             return dic_image[img_name]
    46 
    47 
    48 # 获取指定图片的Surface对象,同时支持旋转+缩放
    49 def get_image(img_name, angle=0, scale=1.0):
    50     rect = get_image_rect(img_name);
    51     image = pygame.Surface((rect.width, rect.height))
    52     image.blit(spritesheet_image, (0, 0), rect)
    53     image.set_colorkey(BLACK)
    54     new_image = pygame.transform.rotozoom(image, angle, scale)
    55     new_image.set_colorkey(BLACK)
    56     return new_image
    57 
    58 
    59 # 循环入口
    60 running = True
    61 while running:
    62 
    63     # 设置帧数
    64     clock.tick(FPS)
    65 
    66     # 事件处理
    67     for event in pygame.event.get():
    68         # 退出处理
    69         if event.type == pygame.QUIT:
    70             running = False
    71 
    72     # (update) 游戏更新逻辑(比如:改动角色的位置或一些重要变量等,这里仅演示更新当前时间)
    73     image1 = get_image("bunny1_hurt.png", 0, 0.5)
    74     image2 = get_image("bunny1_walk1.png", 45, 0.5)
    75     image3 = get_image("bunny2_stand.png", -45, 0.5)
    76 
    77     # (draw)渲染屏幕
    78     screen.fill(BLACK)  # 先准备一块黑布
    79     screen.blit(image1, (0, 0), image1.get_rect())
    80     screen.blit(image2, (200, 200), image2.get_rect())
    81     screen.blit(image3, (200, 50), image3.get_rect())
    82 
    83     # 屏幕内容刷新
    84     pygame.display.update()
    85 
    86 # 循环结束后,退出游戏
    87 pygame.quit()
    View Code

    运行效果如下:

    好了,前戏足够了,回到jumpy的游戏中来,在sprites.py中,新建一个类:

     1 class Spritesheet:
     2     def __init__(self, png_file_name, xml_file_name):
     3         self.sprite_sheet = pg.image.load(png_file_name)
     4         self.sprite_sheet_dom_tree = parse(xml_file_name)
     5         self.dic_image = {}
     6         self.root_textures = self.sprite_sheet_dom_tree.documentElement
     7         self.sub_textures = self.root_textures.getElementsByTagName("SubTexture")
     8 
     9     def get_image_rect(self, img_name):
    10         if self.dic_image.get(img_name):
    11             return self.dic_image[img_name]
    12         for texture in self.sub_textures:
    13             name = texture.getAttribute("name")
    14             if img_name == name:
    15                 self.dic_image[img_name] = pg.Rect(
    16                     int(texture.getAttribute("x")),
    17                     int(texture.getAttribute("y")),
    18                     int(texture.getAttribute("width")),
    19                     int(texture.getAttribute("height"))
    20                 )
    21                 return self.dic_image[img_name]
    22 
    23     def get_image(self, img_name):
    24         rect = self.get_image_rect(img_name)
    25         image = pg.Surface((rect.width, rect.height))
    26         image.blit(self.sprite_sheet, (0, 0), rect)
    27         image = pg.transform.scale(image, (rect.width // 3, rect.height // 3))
    28         image.set_colorkey(BLACK, 1)
    29         return image
    View Code

    同时原有的Player.py类,初始化时,image换成加载图片(终于可以换掉单调的小方块了)

    class Player(pg.sprite.Sprite):
        def __init__(self, game):
            ...
            self.image = self.game.spritesheet.get_image("bunny1_stand.png")
            self.rect = self.image.get_rect()
            ...

    最后看看main.py这个主类,还记得上一回,为了加载历史最高得分,新增了一个load_data函数吗?现在又用到了它了:

    1     def load_data(self):
    2         # 加载历史最高分
    3         ...
    4         # load spritesheet
    5         self.spritesheet = Spritesheet(path.join(self.dir, "../img/spritesheet_jumper.png"),
    6                                        path.join(self.dir, "../img/spritesheet_jumper.xml"))
    View Code

    源码:https://github.com/yjmyzz/kids-can-code/tree/master/part_09

  • 相关阅读:
    HDUOJ---1863畅通工程
    HDUOJ---1879 继续畅通工程
    HDUOJ---1102Constructing Roads
    HDUOJ---1102Constructing Roads
    hdu--DFS
    poj1611---The Suspects
    nyoj-----幸运三角形
    HDUOJ --2523
    HDUOJ---1195Open the Lock
    HDUOJ----2952Counting Sheep
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/pygame-kidscancode-part9-use-spritesheet.html
Copyright © 2011-2022 走看看