• 小学生python游戏编程arcade----精灵调用图片的两种类


    前言

    接上篇文章继续解绍arcade游戏编程的基本知识。精灵调用图片纹理的两种类的实现,一种直接引用图片文件,一种利用arcade.load_texture的过度,直接给精灵赋值。如上次写的,游戏升级时,通过程序换精灵的颜色,用后种方法定义的类较好。

    精灵调用图片的两种类

    1、第一种类的定义

    1.1 以文件名及缩放比例做初始参数
    class Enemy_tank(arcade.Sprite):
        def __init__(self, filename, scale, x, y, speed_to_player=0.2):
            super().__init__(filename, scale)
            self.center_x = x
            self.center_y = y
            self.word = 'book'
            self.hz = '书'
            self.life = 1  # 生命条数,即挨几颗子弹消失
            self.speed_to_player = speed_to_player  # 面向角色移动的速度
    
        def draw_word(self, x, y, fcolor=arcade.csscolor.WHITE_SMOKE, fsize=18, text=None):
            xs=fsize
            if text:
                arcade.draw_rectangle_filled(x+len(self.word)*xs//2-10,y+5,len(text)*xs,30,(128,138,135))
                arcade.draw_text(text, x, y, fcolor, fsize)
    
            else:
                arcade.draw_rectangle_filled(x+len(self.word)*xs//2-10,y+5,len(self.word)*xs, 30,(128,138,135))
                arcade.draw_text(self.word, x, y, fcolor, fsize)
    
        def update(self):
            super().update()
            if self.speed_to_player:
                # 敌人向角色移动变量
                self.change_x = math.cos(self.angle) * 0.2
                self.change_y = math.sin(self.angle) * 0.2
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    1.2 利用变换图片的颜色更换角色的使用
        def setup_enemy(self):
            # pass
            self.scene.add_sprite_list_after(LAYER_tanks, 'wj')
            if len(self.word_keys) == 0:
                if self.danyuan==len(self.gk[self.year]):
                    self.level += 1
                    self.year =self.years[self.level]
                else:
                    self.danyuan += 1
                    self.setup_word(self.danyuan, self.year)
    
            if len(self.word_keys) > 0:
                self.key = self.word_dict[self.word_keys[0]]  # 朋友
                # print('key',self.key)
                xsword = {}
                xsword[self.word_keys[0]] = self.word_dict[self.word_keys[0]]
                # 随机取3
                sankey = random.sample(self.word_dict.keys(), 3)
                if self.word_keys[0] in sankey:
                    sankey.remove(self.word_keys[0])
                # 增加两个敌人
                xsword[sankey[0]] = self.word_dict[sankey[0]]
                xsword[sankey[1]] = self.word_dict[sankey[1]]
    
                # 增加随机坦克
                posx=[]
                random_x= random.random()*SCREEN_width/3
                for jj in range(3):
                    tempx=random_x+400*jj
                    if tempx>self.end_of_map:
                        tempx=random.random()*SCREEN_width/2
                    posx.append(tempx)
                random_y = random.random() * self.top_of_map / 3
                random.shuffle(posx)  # 打乱顺序
                # print(posx)
                j=0
    
                # 转换文件来不及调用,时间问题??????
                tempcolorh = random.random()
                # print('color',tempcolorh)
                tempic = change_color_tmpic(Image.open("images/坦克2.png"), tempcolorh)
                tempfile = 'images/temp'+str(tempcolorh)+'.png'
                tempic.save(tempfile)
                for x in xsword:
                    tank = Enemy_tank(tempfile, 0.5, posx[j], random_y)
                    tank.word = x
                    tank.hz = xsword[x]
                    self.scene.add_sprite(LAYER_tanks, tank)
                    j +=1
                if (os.path.exists(tempfile)):
                    # 存在,则删除文件
                    os.remove(tempfile)
                # 去掉已显示单词
                self.word_keys.remove(self.word_keys[0])
    
    
    
            # 增加子弹层
            self.scene.add_sprite_list_after(LAYER_bullet, 'wj')
            # 增加爆炸粒子列表
            self.scene.add_sprite_list_after(LAYER_explosions, 'wj')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    1.3 代码分析

    转换文件来不及调用,时间问题??????

            tempcolorh = random.random()
            # print('color',tempcolorh)
            tempic = change_color_tmpic(Image.open("images/坦克2.png"), tempcolorh)
            tempfile = 'images/temp'+str(tempcolorh)+'.png'
            tempic.save(tempfile)
            for x in xsword:
                tank = Enemy_tank(tempfile, 0.5, posx[j], random_y)
                tank.word = x
                tank.hz = xsword[x]
                self.scene.add_sprite(LAYER_tanks, tank)
                j +=1
            if (os.path.exists(tempfile)):
                # 存在,则删除文件
                os.remove(tempfile)
            # 去掉已显示单词
            self.word_keys.remove(self.word_keys[0])
            此用需要借助临时文件,再生成精灵,中间还遇到个问题,使用tempfile做为过度赋值,结果游戏敌人坦克不变色,最后通过每次生成文件名不同,可以实现效果。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2、第二种方法,通过sprite的texture直接赋值

    2.1精灵的两种加载图片方式

    通过查看sprite的类的定义原码,如下

    class Sprite:
        """
        Class that represents a 'sprite' on-screen. Most games center around sprites.
        For examples on how to use this class, see:
        https://api.arcade.academy/en/latest/examples/index.html#sprites
    
        :param str filename: Filename of an image that represents the sprite.
        :param float scale: Scale the image up or down. Scale of 1.0 is none.
        :param float image_x: X offset to sprite within sprite sheet.
        :param float image_y: Y offset to sprite within sprite sheet.
        :param float image_width: Width of the sprite
        :param float image_height: Height of the sprite
        :param float center_x: Location of the sprite
        :param float center_y: Location of the sprite
        :param bool flipped_horizontally: Mirror the sprite image. Flip left/right across vertical axis.
        :param bool flipped_vertically: Flip the image up/down across the horizontal axis.
        :param bool flipped_diagonally: Transpose the image, flip it across the diagonal.
        :param str hit_box_algorithm: One of None, 'None', 'Simple' or 'Detailed'.
              Defaults to 'Simple'. Use 'Simple' for the :data:`PhysicsEngineSimple`,
              :data:`PhysicsEnginePlatformer`
              and 'Detailed' for the :data:`PymunkPhysicsEngine`.
        :param Texture texture: Specify the texture directly. 直接指定纹理
        :param float angle: The initial rotation of the sprite in degrees
    
        This will ignore all hit box and image size arguments.
    
            .. figure:: ../images/hit_box_algorithm_none.png
               :width: 40%
    
               hit_box_algorithm = "None"
    
            .. figure:: ../images/hit_box_algorithm_simple.png
               :width: 55%
    
               hit_box_algorithm = "Simple"
    
            .. figure:: ../images/hit_box_algorithm_detailed.png
               :width: 75%
    
               hit_box_algorithm = "Detailed"
    
        :param float hit_box_detail: Float, defaults to 4.5. Used with 'Detailed' to hit box
    
        Attributes:
            :alpha: Transparency of sprite. 0 is invisible, 255 is opaque.
            :angle: Rotation angle in degrees. Sprites rotate counter-clock-wise.
            :radians: Rotation angle in radians. Sprites rotate counter-clock-wise.
            :bottom: Set/query the sprite location by using the bottom coordinate. \
            This will be the 'y' of the bottom of the sprite.
            :boundary_left: Used in movement. Left boundary of moving sprite.
            :boundary_right: Used in movement. Right boundary of moving sprite.
            :boundary_top: Used in movement. Top boundary of moving sprite.
            :boundary_bottom: Used in movement. Bottom boundary of moving sprite.
            :center_x: X location of the center of the sprite
            :center_y: Y location of the center of the sprite
            :change_x: Movement vector, in the x direction.
            :change_y: Movement vector, in the y direction.
            :change_angle: Change in rotation.
            :color: Color tint the sprite
            :collision_radius: Used as a fast-check to see if this item is close \
            enough to another item. If this check works, we do a slower more accurate check. \
            You probably don't want to use this field. Instead, set points in the \
            hit box.
            :cur_texture_index: Index of current texture being used.
            :guid: Unique identifier for the sprite. Useful when debugging.
            :height: Height of the sprite.
            :force: Force being applied to the sprite. Useful when used with Pymunk \
            for physics.
            :hit_box: Points, in relation to the center of the sprite, that are used \
            for collision detection. Arcade defaults to creating a hit box via the \
            'simple' hit box algorithm \
            that encompass the image. If you are creating a ramp or making better \
            hit-boxes, you can custom-set these.
            :left: Set/query the sprite location by using the left coordinate. This \
            will be the 'x' of the left of the sprite.
            :position: A list with the (x, y) of where the sprite is.
            :right: Set/query the sprite location by using the right coordinate. \
            This will be the 'y=x' of the right of the sprite.
            :sprite_lists: List of all the sprite lists this sprite is part of.
            :texture: :class:`arcade.Texture` class with the current texture. Setting a new texture does \
            **not** update the hit box of the sprite. This can be done with \
            ``my_sprite.hit_box = my_sprite.texture.hit_box_points``. New textures will be centered \
            on the current center_x/center_y.
            :textures: List of textures associated with this sprite.
            :top: Set/query the sprite location by using the top coordinate. This \
            will be the 'y' of the top of the sprite.
            :scale: Scale the image up or down. Scale of 1.0 is original size, 0.5 \
            is 1/2 height and width.
            :velocity: Change in x, y expressed as a list. (0, 0) would be not moving.
            :width: Width of the sprite
    
        It is common to over-ride the `update` method and provide mechanics on
        movement or other sprite updates.
        """
    
        def __init__(
            self,
            filename: str = None,
            scale: float = 1,
            image_x: float = 0,
            image_y: float = 0,
            image_width: float = 0,
            image_height: float = 0,
            center_x: float = 0,
            center_y: float = 0,
            repeat_count_x: int = 1,  # Unused
            repeat_count_y: int = 1,  # Unused
            flipped_horizontally: bool = False,
            flipped_vertically: bool = False,
            flipped_diagonally: bool = False,
            hit_box_algorithm: Optional[str] = "Simple",
            hit_box_detail: float = 4.5,
            texture: Texture = None,
            angle: float = 0,
        ):
            """ Constructor """
            # Position, size and orientation properties
            self._width: float = 0.0
            self._height: float = 0.0
            self._scale: float = scale
            self._position: Point = (center_x, center_y)
            self._angle = angle
            self.velocity = [0.0, 0.0]
            self.change_angle: float = 0.0
    
            # Hit box and collision property
            self._points: Optional[PointList] = None
            self._point_list_cache: Optional[PointList] = None
            self._hit_box_shape: Optional[ShapeElementList] = None
            self._hit_box_algorithm = hit_box_algorithm
            self._hit_box_detail = hit_box_detail
            self._collision_radius: Optional[float] = None
    
            # Color
            self._color: RGB = (255, 255, 255)
            self._alpha: int = 255
    
            # Custom sprite properties
            self._properties: Optional[Dict[str, Any]] = None
    
            # Boundaries for moving platforms in tilemaps
            self.boundary_left: Optional[float] = None
            self.boundary_right: Optional[float] = None
            self.boundary_top: Optional[float] = None
            self.boundary_bottom: Optional[float] = None
    
            # Texture properties
            self._texture: Optional[Texture] = None
            self.textures: List[Texture] = []
            self.cur_texture_index: int = 0
    
            self.sprite_lists: List["SpriteList"] = []
            self.physics_engines: List[Any] = []
            self._sprite_list: Optional["SpriteList"] = None  # Used for Sprite.draw()
    
            # Pymunk specific properties
            self._pymunk: Optional[PyMunk] = None
            self.force = [0, 0]
    
            # Debug properties
            self.guid: Optional[str] = None
    
            # Sanity check values
            if image_width < 0:
                raise ValueError("Width of image can't be less than zero.")
    
            if image_height < 0:
                raise ValueError(
                    "Height entered is less than zero. Height must be a positive float."
                )
    
            if image_width == 0 and image_height != 0:
                raise ValueError("Width can't be zero.")
    
            if image_height == 0 and image_width != 0:
                raise ValueError("Height can't be zero.")
    
            if hit_box_algorithm not in ["Simple", "Detailed", "None", None]:
                raise ValueError(
                    "hit_box_algorithm must be 'Simple', 'Detailed', 'None' or None"
                )
    
            if texture:
                self._texture = texture
                self._textures = [texture]
                self._width = self._texture.width * scale
                self._height = self._texture.height * scale
            elif filename is not None:
                self._texture = load_texture(
                    filename,
                    image_x,
                    image_y,
                    image_width,
                    image_height,
                    flipped_horizontally=flipped_horizontally,
                    flipped_vertically=flipped_vertically,
                    flipped_diagonally=flipped_diagonally,
                    hit_box_algorithm=hit_box_algorithm,
                    hit_box_detail=hit_box_detail,
                )
                self.textures = [self._texture]
                # Ignore the texture's scale and use ours
                self._width = self._texture.width * scale
                self._height = self._texture.height * scale
    
            if self._texture and not self._points:
                self._points = self._texture.hit_box_points
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    2.2 两种方式,上述类是一种以文件形赋值的 # 添加角色.

    一以文件引用
    image_source = “images/bird.png”
    self.player_sprite = arcade.Sprite(image_source, CHARACTER_SCALING)
    self.player_sprite.center_x = 64
    self.player_sprite.center_y = 128
    self.player_list.append(self.player_sprite)
    二 以texture引用
    # 添加角色.
    # image_source = “images/bird.png”
    texture = arcade.load_texture(f"images/bird.png")
    # self.player_sprite = arcade.Sprite(image_source, CHARACTER_SCALING)
    self.player_sprite = arcade.Sprite(texture=texture, scale=CHARACTER_SCALING)
    self.player_sprite.center_x = 64
    self.player_sprite.center_y = 128
    self.player_list.append(self.player_sprite)

    2.3 修改类
    # 不调用文件,直接给texture给值
    class Enemy_tank_pic(arcade.Sprite):
        def __init__(self, texture, scale, x, y, speed_to_player=0.2):
            super().__init__(texture=texture, scale=scale)
            self.center_x = x
            self.center_y = y
            self.word = 'book'
            self.hz = '书'
            self.life = 1  # 生命条数,即挨几颗子弹消失
            self.speed_to_player = speed_to_player  # 面向角色移动的速度
    
        def draw_word(self, x, y, fcolor=arcade.csscolor.WHITE_SMOKE, fsize=18, text=None):
            xs=fsize
            if text:
                arcade.draw_rectangle_filled(x+len(self.word)*xs//2-10,y+5,len(text)*xs,30,(128,138,135))
                arcade.draw_text(text, x, y, fcolor, fsize)
    
            else:
                arcade.draw_rectangle_filled(x+len(self.word)*xs//2-10,y+5,len(self.word)*xs, 30,(128,138,135))
                arcade.draw_text(self.word, x, y, fcolor, fsize)
    
        def update(self):
            super().update()
            if self.speed_to_player:
                # 敌人向角色移动变量
                self.change_x = math.cos(self.angle) * 0.2
                self.change_y = math.sin(self.angle) * 0.2
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    2.4 调用第二种类的代码实现

    在这里插入图片描述

                # 转换文件来不及调用,时间问题??????
                tempcolorh = random.random()
                tempic = change_color_tmpic(Image.open("images/坦克2.png"), tempcolorh)
                tempfile = 'images/temp.png'
                tempic.save(tempfile)
                texture = arcade.load_texture(tempfile)
                for x in xsword:
                    tank = Enemy_tank_pic(texture, 0.5, posx[j], random_y)
                    tank.word = x
                    tank.hz = xsword[x]
                    self.scene.add_sprite(LAYER_tanks, tank)
                    j +=1
                # 去掉已显示单词
                self.word_keys.remove(self.word_keys[0])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    问题一样存在
    调整如下

                # 转换文件来不及调用,时间问题??????
                tempcolorh = random.random()
                tempic = change_color_tmpic(Image.open("images/坦克2.png"), tempcolorh)
                # tempfile = 'images/temp.png'
                # tempic.save(tempfile)
                # texture = arcade.load_texture(tempfile)
                for x in xsword:
                    tank = Enemy_tank_pic(tempic, 0.5, posx[j], random_y)
                    tank.word = x
                    tank.hz = xsword[x]
                    self.scene.add_sprite(LAYER_tanks, tank)
                    j +=1
                # 去掉已显示单词
                self.word_keys.remove(self.word_keys[0])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    出现问题:

      File "E:\pgame\arcadegame\tank.py", line 135, in __init__
        super().__init__(texture=texture, scale=scale)
      File "E:\pgame\venv\lib\site-packages\arcade\sprite.py", line 274, in __init__
        self._points = self._texture.hit_box_points
      File "E:\pgame\venv\lib\site-packages\PIL\Image.py", line 548, in __getattr__
        raise AttributeError(name)
    AttributeError: hit_box_points
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    源码获取

    关注博主后,私聊博主免费获取
    需要技术指导,育娃新思考,企业软件合作等更多服务请联系博主

    今天是以此模板持续更新此育儿专栏的第35/50次。
    可以关注我,点赞我、评论我、收藏我啦。

  • 相关阅读:
    【前端】使用tesseract插件识别提取图片中的文字
    排序算法图解(一):冒泡排序与冒泡排序的优化
    Java中如何将“日期字符串”转换为java对象呢?
    新建springboot demo(module)后,直接修改版本号, 观察变化
    不就是Java吗之数组的定义和使用
    Spring Cloud--从零开始搭建微服务基础环境【四】
    Linux(多用户下)查看cuda、cudnn版本、查看已经安装的cuda版本相关命令
    如何在Linux系统中使用Apache HTTP Server
    爬虫学习(05): 数据解析_bs4篇
    线性代数 --- 四个基本子空间(个人学习笔记)
  • 原文地址:https://blog.csdn.net/fqfq123456/article/details/128084037