• 小学生python游戏编程arcade----烟花粒子


    前言

    接上篇文章继续解绍arcade游戏编程的基本知识。粒子系统

    烟花粒子

    1、Vector向量类

    1.1 arcade中的向量类

    RGB = Union[Tuple[int, int, int], List[int]]
    RGBA = Union[Tuple[int, int, int, int], List[int]]
    Color = Union[RGB, RGBA]
    Point = Union[Tuple[float, float], List[float]]
    NamedPoint = namedtuple(“NamedPoint”, [“x”, “y”])

    Vector = Point

    1.2 应用
    def __init__(
            self,
            filename_or_texture: arcade.FilenameOrTexture,
            change_xy: Vector,
    
    • 1
    • 2
    • 3
    • 4

    2、绘制粒子所有纹理图片

    2.1 给定直径和颜色的圆的纹理

    SPARK_textures = [arcade.make_circle_texture(6, clr) for clr in Color_list]

    2.2 arcade.make_circle_texture函数原码
    def make_circle_texture(diameter: int, color: Color, name: str = None) -> Texture:
        """
        返回具有给定直径和颜色的圆的纹理
        """
    
        name = name or _build_cache_name("circle_texture", diameter, color[0], color[1], color[2])
    
        bg_color = (0, 0, 0, 0)  # fully transparent
        img = PIL.Image.new("RGBA", (diameter, diameter), bg_color)
        draw = PIL.ImageDraw.Draw(img)
        draw.ellipse((0, 0, diameter - 1, diameter - 1), fill=color)
        return Texture(name, img)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    2.3 make_soft_circle_texture 函数原码
    def make_soft_circle_texture(diameter: int, color: Color, center_alpha: int = 255, outer_alpha: int = 0,
                                 name: str = None) -> Texture:
        """
        Return a :class:`Texture` of a circle with the given diameter and color, fading out at its edges.
    
        :param int diameter: Diameter of the circle and dimensions of the square :class:`Texture` returned.
        :param Color color: Color of the circle.
        :param int center_alpha: Alpha value of the circle at its center.
        :param int outer_alpha: Alpha value of the circle at its edges.
        :param str name: Custom or pre-chosen name for this texture
    
        :returns: New :class:`Texture` object.
        :rtype: arcade.Texture
        """
        # TODO: create a rectangle and circle (and triangle? and arbitrary poly where client passes
        # in list of points?) particle?
        name = name or _build_cache_name("soft_circle_texture", diameter, color[0], color[1], color[2], center_alpha,
                                         outer_alpha)  # name must be unique for caching
    
        bg_color = (0, 0, 0, 0)  # fully transparent
        img = PIL.Image.new("RGBA", (diameter, diameter), bg_color)
        draw = PIL.ImageDraw.Draw(img)
        max_radius = int(diameter // 2)
        center = max_radius  # for readability
        for radius in range(max_radius, 0, -1):
            alpha = int(lerp(center_alpha, outer_alpha, radius / max_radius))
            clr = (color[0], color[1], color[2], alpha)
            draw.ellipse((center - radius, center - radius, center + radius - 1, center + radius - 1), fill=clr)
    
        return Texture(name, img)
    
    • 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 公共纹理代码
    Color_list = (  # rainbon 彩虹
        arcade.color.ELECTRIC_CRIMSON,
        arcade.color.FLUORESCENT_ORANGE,
        arcade.color.ELECTRIC_YELLOW,
        arcade.color.ELECTRIC_GREEN,
        arcade.color.ELECTRIC_CYAN,
        arcade.color.MEDIUM_ELECTRIC_BLUE,
        arcade.color.ELECTRIC_INDIGO,
        arcade.color.ELECTRIC_PURPLE,
    )
    
    SPARK_textures = [arcade.make_circle_texture(6, clr) for clr in Color_list]
    # 火花_对
    SPARK_list = [
        [SPARK_textures[0], SPARK_textures[3]],
        [SPARK_textures[1], SPARK_textures[5]],
        [SPARK_textures[7], SPARK_textures[2]],
    ]
    ROCKET_smoke_texture = arcade.make_soft_circle_texture(15, arcade.color.GRAY)
    PUFF_texture = arcade.make_soft_circle_texture(80, (40, 40, 40))
    FLASH_texture = arcade.make_soft_circle_texture(70, (128, 128, 90))
    # 云 纹理
    CLOUD_textures = [
        arcade.make_soft_circle_texture(50, arcade.color.WHITE),
        arcade.make_soft_circle_texture(50, arcade.color.LIGHT_GRAY),
        arcade.make_soft_circle_texture(50, arcade.color.LIGHT_BLUE),
    ]
    # 星星 纹理
    STAR_textures = [
        arcade.make_soft_circle_texture(8, arcade.color.WHITE),
        arcade.make_soft_circle_texture(8, arcade.color.PASTEL_YELLOW),
    ]
    
    • 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

    3 效果图

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述
    粒子大小改大后
    在这里插入图片描述

    4 代码

    """
    粒子焰花
    使用焰火展示演示发射器和粒子的“真实世界”用途
    """
    import arcade
    from arcade import Point, Vector
    from arcade.utils import _Vec2  # bring in "private" class
    import os
    import random
    import pyglet
    
    SCREEN_width = 800
    SCREEN_height = 600
    
    Color_list = (  # rainbon 彩虹
        arcade.color.ELECTRIC_CRIMSON,
        arcade.color.FLUORESCENT_ORANGE,
        arcade.color.ELECTRIC_YELLOW,
        arcade.color.ELECTRIC_GREEN,
        arcade.color.ELECTRIC_CYAN,
        arcade.color.MEDIUM_ELECTRIC_BLUE,
        arcade.color.ELECTRIC_INDIGO,
        arcade.color.ELECTRIC_PURPLE,
    )
    
    SPARK_textures = [arcade.make_circle_texture(6, clr) for clr in Color_list]
    # 火花_对
    SPARK_list = [
        [SPARK_textures[0], SPARK_textures[3]],
        [SPARK_textures[1], SPARK_textures[5]],
        [SPARK_textures[7], SPARK_textures[2]],
    ]
    ROCKET_smoke_texture = arcade.make_soft_circle_texture(15, arcade.color.GRAY)
    PUFF_texture = arcade.make_soft_circle_texture(80, (40, 40, 40))
    FLASH_texture = arcade.make_soft_circle_texture(70, (128, 128, 90))
    # 云 纹理
    CLOUD_textures = [
        arcade.make_soft_circle_texture(50, arcade.color.WHITE),
        arcade.make_soft_circle_texture(50, arcade.color.LIGHT_GRAY),
        arcade.make_soft_circle_texture(50, arcade.color.LIGHT_BLUE),
    ]
    # 星星 纹理
    STAR_textures = [
        arcade.make_soft_circle_texture(8, arcade.color.WHITE),
        arcade.make_soft_circle_texture(8, arcade.color.PASTEL_YELLOW),
    ]
    # 旋转器 高
    SPINNER_height = 75
    
    # 旋转器  emitter 发射器
    def make_spinner():
        spinner = arcade.Emitter(
            center_xy=(SCREEN_width / 2, SPINNER_height - 5),
            emit_controller=arcade.EmitterIntervalWithTime(0.025, 2.0),
            particle_factory=lambda emitter: arcade.FadeParticle(
                filename_or_texture=random.choice(STAR_textures),
                change_xy=(0, 6.0),
                lifetime=0.2
            )
        )
        spinner.change_angle = 16.28
        return spinner
    
    
    def make_rocket(emit_done_cb):
        """当烟花炮弹升入天空时显示烟雾轨迹的发射器"""
        rocket = RocketEmitter(
            center_xy=(random.uniform(100, SCREEN_width - 100), 25),
            emit_controller=arcade.EmitterIntervalWithTime(0.04, 2.0),
            particle_factory=lambda emitter: arcade.FadeParticle(
                filename_or_texture=ROCKET_smoke_texture,
                change_xy=arcade.rand_in_circle((0.0, 0.0), 0.08),
                scale=0.5,
                lifetime=random.uniform(1.0, 1.5),
                start_alpha=100,
                end_alpha=0,
                mutation_callback=rocket_smoke_mutator
            ),
            emit_done_cb=emit_done_cb
        )
        rocket.change_x = random.uniform(-1.0, 1.0) #
        rocket.change_y = random.uniform(5.0, 7.25)
        return rocket
    
    
    def make_flash(prev_emitter):
        """当烟花炮弹爆炸时显示短暂闪光的返回发射器"""
        return arcade.Emitter(
            center_xy=prev_emitter.get_pos(),
            emit_controller=arcade.EmitBurst(3),
            particle_factory=lambda emitter: arcade.FadeParticle(
                filename_or_texture=FLASH_texture,
                change_xy=arcade.rand_in_circle((0.0, 0.0), 3.5),
                lifetime=0.15
            )
        )
    
    
    def make_puff(prev_emitter):
        """返回发射器,用于生成烟花炮弹爆炸后留下的细微烟雾云"""
        return arcade.Emitter(
            center_xy=prev_emitter.get_pos(),
            emit_controller=arcade.EmitBurst(4),
            particle_factory=lambda emitter: arcade.FadeParticle(
                filename_or_texture=PUFF_texture,
                change_xy=(_Vec2(arcade.rand_in_circle((0.0, 0.0), 0.4)) + _Vec2(0.3, 0.0)).as_tuple(),
                lifetime=4.0
            )
        )
    
    
    class AnimatedAlphaParticle(arcade.LifetimeParticle):
        """在三个不同的alpha级别之间设置动画的自定义粒子"""
    
        def __init__(
                self,
                filename_or_texture: arcade.FilenameOrTexture,
                change_xy: Vector,
                start_alpha: int = 0,
                duration1: float = 1.0,
                mid_alpha: int = 255,
                duration2: float = 1.0,
                end_alpha: int = 0,
                center_xy: Point = (0.0, 0.0),
                angle: float = 0,
                change_angle: float = 0,
                scale: float = 1.0,
                mutation_callback=None,
        ):
            super().__init__(filename_or_texture,
                             change_xy,
                             duration1 + duration2,
                             center_xy,
                             angle,
                             change_angle,
                             scale,
                             start_alpha,
                             mutation_callback)
            self.start_alpha = start_alpha
            self.in_duration = duration1
            self.mid_alpha = mid_alpha
            self.out_duration = duration2
            self.end_alpha = end_alpha
    
        def update(self):
            super().update()
            if self.lifetime_elapsed <= self.in_duration:
                u = self.lifetime_elapsed / self.in_duration
                self.alpha = arcade.clamp(arcade.lerp(self.start_alpha, self.mid_alpha, u), 0, 255)
            else:
                u = (self.lifetime_elapsed - self.in_duration) / self.out_duration
                self.alpha = arcade.clamp(arcade.lerp(self.mid_alpha, self.end_alpha, u), 0, 255)
    
    
    class RocketEmitter(arcade.Emitter):
        """自定义发射器类,向发射器添加重力以表示烟花外壳上的重力"""
    
        def update(self):
            super().update()
            # 重力
            self.change_y += -0.05
    
    
    class Game(arcade.Window):
        def __init__(self):
            super().__init__(SCREEN_width, SCREEN_height, '粒子焰火')
    
            file_path = os.path.dirname(os.path.abspath(__file__))
            os.chdir(file_path)
    
            arcade.set_background_color(arcade.color.BLACK)
            self.emitters = []
    
            self.launch_firework(0)
            arcade.schedule(self.launch_spinner, 4.0)
    
            stars = arcade.Emitter(
                center_xy=(0.0, 0.0),
                emit_controller=arcade.EmitMaintainCount(20),
                particle_factory=lambda emitter: AnimatedAlphaParticle(
                    filename_or_texture=random.choice(STAR_textures),
                    change_xy=(0.0, 0.0),
                    start_alpha=0,
                    duration1=random.uniform(2.0, 6.0),
                    mid_alpha=128,
                    duration2=random.uniform(2.0, 6.0),
                    end_alpha=0,
                    center_xy=arcade.rand_in_rect((0.0, 0.0), SCREEN_width, SCREEN_height)
                )
            )
            self.emitters.append(stars)
    
            self.cloud = arcade.Emitter(
                center_xy=(50, 500),
                change_xy=(0.15, 0),
                emit_controller=arcade.EmitMaintainCount(60),
                particle_factory=lambda emitter: AnimatedAlphaParticle(
                    filename_or_texture=random.choice(CLOUD_textures),
                    change_xy=(_Vec2(arcade.rand_in_circle((0.0, 0.0), 0.04)) + _Vec2(0.1, 0)).as_tuple(),
                    start_alpha=0,
                    duration1=random.uniform(5.0, 10.0),
                    mid_alpha=255,
                    duration2=random.uniform(5.0, 10.0),
                    end_alpha=0,
                    center_xy=arcade.rand_in_circle((0.0, 0.0), 50)
                )
            )
            self.emitters.append(self.cloud)
    
        def launch_firework(self, delta_time):
            launchers = (
                self.launch_random_firework,
                self.launch_ringed_firework,
                self.launch_sparkle_firework,
            )
            random.choice(launchers)(delta_time)
            pyglet.clock.schedule_once(self.launch_firework, random.uniform(1.5, 2.5))
    
        def launch_random_firework(self, _delta_time):
            """以随机颜色爆炸的简单烟花"""
            rocket = make_rocket(self.explode_firework)
            self.emitters.append(rocket)
    
        def launch_ringed_firework(self, _delta_time):
            """"具有基本爆炸和不同颜色圆环形烟花"""
            rocket = make_rocket(self.explode_ringed_firework)
            self.emitters.append(rocket)
    
        def launch_sparkle_firework(self, _delta_time):
            """火花闪烁的烟花"""
            rocket = make_rocket(self.explode_sparkle_firework)
            self.emitters.append(rocket)
    
        def launch_spinner(self, _delta_time):
            """启动喷射火花的旋转器"""
            spinner1 = make_spinner()
            spinner2 = make_spinner()
            spinner2.angle = 180
            self.emitters.append(spinner1)
            self.emitters.append(spinner2)
    
        def explode_firework(self, prev_emitter):
            """烟花炮弹爆炸时发生的动作,生成典型的烟花"""
            self.emitters.append(make_puff(prev_emitter))
            self.emitters.append(make_flash(prev_emitter))
    
            spark_texture = random.choice(SPARK_textures)
            sparks = arcade.Emitter(
                center_xy=prev_emitter.get_pos(),
                emit_controller=arcade.EmitBurst(random.randint(30, 40)),
                particle_factory=lambda emitter: arcade.FadeParticle(
                    filename_or_texture=spark_texture,
                    change_xy=arcade.rand_in_circle((0.0, 0.0), 9.0),
                    lifetime=random.uniform(0.5, 1.2),
                    mutation_callback=firework_spark_mutator
                )
            )
            self.emitters.append(sparks)
    
        def explode_ringed_firework(self, prev_emitter):
            """当烟花炮弹爆炸时发生的动作,产生环形烟花"""
            self.emitters.append(make_puff(prev_emitter))
            self.emitters.append(make_flash(prev_emitter))
    
            spark_texture, ring_texture = random.choice(SPARK_list)
            sparks = arcade.Emitter(
                center_xy=prev_emitter.get_pos(),
                emit_controller=arcade.EmitBurst(25),
                particle_factory=lambda emitter: arcade.FadeParticle(
                    filename_or_texture=spark_texture,
                    change_xy=arcade.rand_in_circle((0.0, 0.0), 8.0),
                    lifetime=random.uniform(0.55, 0.8),
                    mutation_callback=firework_spark_mutator
                )
            )
            self.emitters.append(sparks)
    
            ring = arcade.Emitter(
                center_xy=prev_emitter.get_pos(),
                emit_controller=arcade.EmitBurst(20),
                particle_factory=lambda emitter: arcade.FadeParticle(
                    filename_or_texture=ring_texture,
                    change_xy=arcade.rand_on_circle((0.0, 0.0), 5.0) + arcade.rand_in_circle((0.0, 0.0), 0.25),
                    lifetime=random.uniform(1.0, 1.6),
                    mutation_callback=firework_spark_mutator
                )
            )
            self.emitters.append(ring)
    
        def explode_sparkle_firework(self, prev_emitter):
            """烟花炮弹爆炸时发生的动作,产生闪闪发光的烟花"""
            self.emitters.append(make_puff(prev_emitter))
            self.emitters.append(make_flash(prev_emitter))
    
            spark_texture = random.choice(SPARK_textures)
            sparks = arcade.Emitter(
                center_xy=prev_emitter.get_pos(),
                emit_controller=arcade.EmitBurst(random.randint(30, 40)),
                particle_factory=lambda emitter: AnimatedAlphaParticle(
                    filename_or_texture=spark_texture,
                    change_xy=arcade.rand_in_circle((0.0, 0.0), 9.0),
                    start_alpha=255,
                    duration1=random.uniform(0.6, 1.0),
                    mid_alpha=0,
                    duration2=random.uniform(0.1, 0.2),
                    end_alpha=255,
                    mutation_callback=firework_spark_mutator
                )
            )
            self.emitters.append(sparks)
    
        def update(self, delta_time):
            # 在遍历列表时,防止列表发生变化(通常是通过回调)
            emitters_to_update = self.emitters.copy()
            # 更新云
            if self.cloud.center_x > SCREEN_width:
                self.cloud.center_x = 0
            # 更新
            for e in emitters_to_update:
                e.update()
            # 清除
            to_del = [e for e in emitters_to_update if e.can_reap()]
            for e in to_del:
                self.emitters.remove(e)
    
        def on_draw(self):
            self.clear()
            for e in self.emitters:
                e.draw()
            # 画距形,left, right, top, and bottom
            arcade.draw_lrtb_rectangle_filled(0, SCREEN_width, 25, 0, arcade.color.DARK_GREEN)
            mid = SCREEN_width / 2
            arcade.draw_lrtb_rectangle_filled(mid - 2, mid + 2, SPINNER_height, 10, arcade.color.DARK_BROWN)
    
        def on_key_press(self, key, modifiers):
            if key == arcade.key.ESCAPE:
                arcade.close_window()
    
    
    def firework_spark_mutator(particle: arcade.FadeParticle):
        """所有焰火火花共享的mutation_callback"""
        # 重力
        particle.change_y += -0.03
        # drag
        particle.change_x *= 0.92
        particle.change_y *= 0.92
    
    
    def rocket_smoke_mutator(particle: arcade.LifetimeParticle):
        particle.scale = arcade.lerp(0.5, 3.0, particle.lifetime_elapsed / particle.lifetime_original)
    
    
    if __name__ == "__main__":
        app = Game()
        arcade.run()
    
    
    • 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
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356

    源码获取

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

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

  • 相关阅读:
    机器学习笔记 十八:基于3种方法的随机森林模型分析房屋参数重要性
    浅谈Zk和Optimistic Rollups:原理、区别和前景
    GoogleTest环境配置以及应用
    【Java 进阶篇】Java Request 请求转发详解
    《Python编程:从入门到实战》学习笔记 第4章 操作列表
    [附源码]计算机毕业设计springboot海滨学院学生大创项目申报与审批系统
    大佬的职场经验
    发布自己的npm包注意事项
    全屏滚动插件Fullpage.js
    ImageReader回调YUV数据转换成JPEG图片
  • 原文地址:https://blog.csdn.net/fqfq123456/article/details/128055227