Pygame:从精灵表中显示这些船及其运动的最佳方式是什么? [英] Pygame: What is the best way to display these ships and their movement from sprite sheet?

查看:24
本文介绍了Pygame:从精灵表中显示这些船及其运动的最佳方式是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面附上我正在使用的精灵表之一.现在,我想知道移动时显示带有动画的船只的最佳方式.这些船有 9 个框架.我找到了带有弃用代码的旧示例,或者将图像分解为单个文件的示例.我希望有人向我展示使用这些精灵的最佳方式.感谢您抽出宝贵时间.

Attached below is one of the sprite sheets I am using. Right now, I am wondering the best way to display the ships with their animation when moving. The ships have 9 frames. I have found old examples with deprecated code, or examples that have you break the images into individual files. I would love for someone to show me the best way to use these sprites as they are. Thank you for your time.

推荐答案

什么是最好的方法 无法回答,因为最终那是主观的;取决于你自己的目标.最棒的表演?最简单的代码?最灵活的代码?

What is the best way can't be answered, because in the end that's subjective; and depends on your own goals. Best performance? Easiest code? Most flexible code?

你可以从我在上次无聊的会议中一起破解的类似内容开始:

You could start with something like this I hacked together during my last boring meeting:

import pygame
import random
from pygame import Vector2

SPRITE_SHEET = None

GREEN_SHIP  = pygame.Rect(0, 292, 32, 32)
RED_SHIP    = pygame.Rect(0, 324, 32, 32)
BLUE_SHIP   = pygame.Rect(0, 356, 32, 32)
YELLOW_SHIP = pygame.Rect(0, 388, 32, 32)


class EnemyController:

    def __init__(self):
        self.direction = Vector2(1, 0)

    def update(self, sprite, events, dt):
        if not pygame.display.get_surface().get_rect().contains(sprite.rect):
            self.direction *= -1
        sprite.direction = self.direction

class PlayerController:

    movement = {
        pygame.K_UP:    Vector2( 0, -1),
        pygame.K_DOWN:  Vector2( 0,  1),
        pygame.K_LEFT:  Vector2(-1,  0),
        pygame.K_RIGHT: Vector2( 1,  0)
    }

    def update(self, sprite, events, dt):
        pressed = pygame.key.get_pressed()
        v = Vector2(0, 0)
        for key in PlayerController.movement:
            if pressed[key]:
                v += PlayerController.movement[key]

        sprite.direction = v

        for e in events:
            if e.type == pygame.KEYDOWN:
                if e.key == pygame.K_SPACE:
                    sprite.groups()[0].add(Explosion(sprite.pos))

class Animation:
    def __init__(self, frames, speed, sprite):
        self.sprite = sprite
        self.speed = speed
        self.ticks = 0
        self.frames = frames
        self.running = 0
        self.start()

    def cycle_func(self, iterable):
        saved = []
        for element in iterable:
            yield element
            saved.append(element)
        if hasattr(self.sprite, 'on_animation_end'):
            self.sprite.on_animation_end()
        while saved:
            for element in saved:
                yield element
            if hasattr(self.sprite, 'on_animation_end'):
                self.sprite.on_animation_end()
    def stop(self):
        self.running = 0
        if self.idle_image:
            self.sprite.image = self.idle_image

    def start(self):
        if not self.running:
            self.running = 1
            self.cycle = self.cycle_func(self.frames)
            self.sprite.image = next(self.cycle)

    def update(self, dt):
        self.ticks += dt
        if self.ticks >= self.speed:
            self.ticks = self.ticks % self.speed
            if self.running:
                self.sprite.image = next(self.cycle)

class AnimatedSprite(pygame.sprite.Sprite):

    def __init__(self, pos, frames, speed):
        super().__init__()
        self.animation = Animation(frames, speed, self)
        self.rect = self.image.get_rect(center=pos)
        self.pos = Vector2(pos)
        self.animation.start()

    def update(self, events, dt):
        self.animation.update(dt)

class Explosion(AnimatedSprite):

    frames = None

    def __init__(self, pos):
        if not Explosion.frames:
            Explosion.frames = parse_sprite_sheet(SPRITE_SHEET, pygame.Rect(0, 890, 64, 64), 6, 4)

        super().__init__(pos, Explosion.frames, 50)

    def on_animation_end(self):
        self.kill()

class DirectionalImageSprite(pygame.sprite.Sprite):

    directions = [(1,0),(1,-1),(0,-1),(-1,-1),(-1,0),(-1,1),(0,1),(1,1),(0,0)]

    def __init__(self, pos, directional_images_rect):
        super().__init__()
        images = parse_sprite_sheet(SPRITE_SHEET, directional_images_rect, 9, 1)
        self.images = { x: img for (x, img) in zip(DirectionalImageSprite.directions, images) }
        self.direction = Vector2(0, 0)
        self.image = self.images[(self.direction.x, self.direction.y)]
        self.rect = self.image.get_rect(center=pos)
        self.pos = pos

class SpaceShip(DirectionalImageSprite):

    def __init__(self, pos, controller, directional_images_rect):
        super().__init__(pos, directional_images_rect)
        self.controller = controller
        self.speed = 3

    def update(self, events, dt):
        super().update(events, dt)

        if self.controller:
            self.controller.update(self, events, dt)

        self.image = self.images[(self.direction.x, self.direction.y)]
        if self.direction.length():
            self.pos = self.pos + self.direction.normalize() * self.speed

        self.rect.center = int(self.pos[0]), int(self.pos[1])

def parse_sprite_sheet(sheet, start_rect, frames_in_row, lines):
    frames = []
    rect = start_rect.copy()
    for _ in range(lines):
        for _ in range(frames_in_row):
            frame = sheet.subsurface(rect)
            frames.append(frame)
            rect.move_ip(rect.width, 0)
        rect.move_ip(0, rect.height)
        rect.x = start_rect.x
    return frames

def main():
    screen = pygame.display.set_mode((800, 600))
    global SPRITE_SHEET
    SPRITE_SHEET = pygame.image.load("ipLRR.png").convert_alpha()
    clock = pygame.time.Clock()
    dt = 0
    all_sprites = pygame.sprite.Group(
        SpaceShip((400, 300), PlayerController(), YELLOW_SHIP),
        SpaceShip((400, 100), EnemyController(), GREEN_SHIP)
    )

    while True:
        events = pygame.event.get()

        for e in events:
            if e.type == pygame.QUIT:
                return

        all_sprites.update(events, dt)

        screen.fill((0, 0, 0))
        all_sprites.draw(screen)
        pygame.display.flip()
        dt = clock.tick(120)

main()

我向函数 parse_sprite_sheet 传递图像,以及动画/相关子图像束第一帧的位置和大小(使用 Rect).另外,我传递了行中的图像数和行数(因为爆炸动画使用 4 行,每行 4 个图像).然后我使用 subsurface 在嵌套循环中获取我感兴趣的精灵表部分.

I pass the function parse_sprite_sheet the image, along with the position and size of the first frame of the animation/bunch of related sub-images (using a Rect). Also, I pass the number of images in the row and the number of rows (since the explosion animation uses 4 rows with 4 images each). Then I use subsurface the get the part of the sprite sheet I'm interested in in a nested loop.

Animation 类由它所附加的精灵更新,并在足够的时间过去后改变精灵的图像.

The Animation class is updated by the sprite it's attached to and changes the image of the sprite when enough time has passed.

还有一个名为 on_animation_end 的方法会在动画结束后在精灵上调用.爆炸完成后,我用它来杀死 Explosion 精灵.

Also a method named on_animation_end is called on the sprite once the animation ends. I use this to kill the Explosion sprites once the explosion is done.

对于SpaceShip 的定向图像,我在列表中定义了一次方向(按正确的顺序),然​​后通过创建字典为每个方向附加一个图像.

For the directional images of SpaceShip, I define the directions in a list once (in the correct order) and then attach each direction an image by creation a dictionary.

这样我就可以轻松查找正确的图像,因为 SpaceShip 的航向方向存储在 direction 属性中.

This way I can easily look up the correct image, since the direction the SpaceShip is heading is stored in the direction attribute.

基本上就是这样.您的精灵表中的某些动画有点棘手,因为图块的大小会发生变化,但这是可行的.

That's it basically. Some animations in your sprite sheet are a little more tricky, as the size of the tile changes, but it's doable.

你会明白的.

这篇关于Pygame:从精灵表中显示这些船及其运动的最佳方式是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆