【问题标题】:How do I set a limit to the range of my bullet?如何设置子弹射程的限制?
【发布时间】:2020-06-29 15:39:48
【问题描述】:

我的游戏有一个可以左右移动和发射子弹的精灵。但是,如果我射出太多子弹(大约 50 颗左右),我的游戏就会开始滞后很多。我的精灵不再流畅地移动,游戏也无法正常运行。所以我认为问题在于创建的所有子弹都继续在我的屏幕外运行。这就是为什么我想知道如何为我的子弹设置范围限制。一旦超过该限制,它就会消失并退出程序,因此我的游戏不会滞后。但是,我对其他建议持开放态度,例如是否有办法在不设置范围限制的情况下让我的游戏不滞后。我已将我的游戏分成 2 个 .py 文件,一个 main.py 和一个 Sprite1.py,我将它们导入到我的 main 中。 这是我的 Sprite1.py 文件:

import pygame
import sys
import os
import time
from pygame import mixer
from pygame.locals import *

def showStartScreen(surface):
    show = True
    while (show == True):
        background = pygame.image.load(os.path.join('images', 'Starting_scr.png'))
        # rect = surface.get_rect()
        surface.blit(background, (0,0))
        pygame.display.flip()
        for event in pygame.event.get():
            if event.type == pygame.MOUSEBUTTONDOWN:
                show = False

class Player(pygame.sprite.Sprite):
    def __init__(self, all_sprites):
        pygame.sprite.Sprite.__init__(self)
        self.movex = 0
        self.movey = 0
        self.frame = 0
        self.images = []
        self.imagesleft = []
        self.imagesright = []
        self.direction = "right"
        # self.rect = self.image.get_rect(center=pos)
        self.alpha = (0,0,0)
        self.ani = 4 # animation cycles
        self.all_sprites = all_sprites
        self.add(self.all_sprites)
        self.bullet_timer = .1

        for i in range(1,5):
            img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
            img.convert_alpha()
            img.set_colorkey(self.alpha)
            self.imagesright.append(img)
            self.image = self.imagesright[0]
            self.rect  = self.image.get_rect()

        for i in range(1,5):
            img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
            img = pygame.transform.flip(img, True, False)
            img.convert_alpha()
            img.set_colorkey(self.alpha)
            self.imagesleft.append(img)
            self.image = self.imagesleft[0]
            self.rect  = self.image.get_rect()

    def control(self,x,y):
        '''
        control player movement
        '''
        self.movex += x
        self.movey -= y

    def update(self, dt):
        '''
        Update sprite position
        '''
        self.rect.x = self.rect.x + self.movex
        self.rect.y = self.rect.y + self.movey

        # moving left
        if self.movex < 0:
            self.frame += 1
            if self.frame > 3*self.ani:
                self.frame = 0
            self.image = self.imagesleft[self.frame//self.ani]
            self.direction = "left"

        # moving right
        if self.movex > 0:
            self.frame += 1
            if self.frame > 3*self.ani:
                self.frame = 0
            self.image = self.imagesright[self.frame//self.ani]
            self.direction = "right"

        # self.rect.center = pygame.mouse.get_pos()


        keys = pygame.key.get_pressed()
        if keys[pygame.K_SPACE]:
            self.bullet_timer -= dt  # Subtract the time since the last tick.
        if self.bullet_timer <= 0:
            self.bullet_timer = 0  # Bullet ready.
            if keys:  # Left mouse button.
                # Create a new bullet instance and add it to the groups.
                if self.direction == "right":
                    Bullet([self.rect.x + self.image.get_width(), self.rect.y + self.image.get_height()/2], self.direction, self.all_sprites)
                else:
                    Bullet([self.rect.x, self.rect.y + self.image.get_height()/2], self.direction, self.all_sprites)
                self.bullet_timer = .1  # Reset the timer.

class Bullet(pygame.sprite.Sprite):

    def __init__(self, pos, direction, *sprite_groups):
        super().__init__(*sprite_groups)

        self.image = pygame.image.load(os.path.join('images','fireball.png'))
        self.rect  = self.image.get_rect()
        self.sound = pygame.mixer.music.play()
        self.pos = pygame.math.Vector2(pos)
        self.vel = pygame.math.Vector2(750, 0)
        self.direction = direction

    def update(self, dt):
        # Add the velocity to the position vector to move the sprite
        if self.direction == "right":
            #self.vel = pygame.math.Vector2(750, 0)
            self.image = pygame.image.load(os.path.join('images','fireball.png'))
            self.rect  = self.image.get_rect()
            self.pos += self.vel * dt
        else:
            #self.vel = pygame.math.Vector2(-750, 0)
            BULLET_IMG = pygame.image.load(os.path.join('images','fireball.png'))
            self.image = pygame.transform.flip(BULLET_IMG, True, False)
            self.rect  = self.image.get_rect()
            self.pos -= self.vel * dt

        #print(self.pos)
        self.rect.center = self.pos  # Update the rect pos.
        if self.rect.bottom <= 0:
            self.kill()

这是我的 main.py 文件:


import pygame
import os
import sys
import time
from pygame import mixer
import Sprite1

'''
Setup
'''
pygame.init()
width = 960
height = 720
fps = 40        # frame rate
#ani = 4        # animation cycles
clock = pygame.time.Clock()
pygame.display.set_caption('B.S.G.!!!')
surface = pygame.display.set_mode((width, height))
#pygame.mixer.music.load('.\\sounds\\Fairy.mp3')
#pygame.mixer.music.play(-1, 0.0)

pygame.mixer.music.load('.\\sounds\\Fireball.wav')

#direction = "right"

all_sprites = pygame.sprite.Group()
player = Sprite1.Player(all_sprites)

player.rect.x = 50
player.rect.y = 500

steps = 10      # how fast to move

Sprite1.showStartScreen(surface)

'''
Main loop
'''
main = True

while main == True:

    background = pygame.image.load(os.path.join('images', 'Bg.png'))
    surface.blit(background, (0,0))


    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
            main = False

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                player.control(-steps,0)
                #direction = "left"
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                player.control(steps,0)
                #direction = "right"
            if event.key == pygame.K_UP or event.key == ord('w'):
                player.rect.y -= 100
                #player.rect.y -= 10
                #player.movey == 10
                #player.movey == -10
                #time.sleep(1)
                #player.control(0,-steps)

        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                player.control(steps,0)
                #direction = "left"
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                player.control(-steps,0)
                #direction = "right"
            if event.key == pygame.K_UP or event.key == ord('w'):
                player.rect.y += 100
                #player.movey == -10
            if event.key == ord('q'):
                pygame.quit()
                sys.exit()
                main = False

    # dt = time since last tick in milliseconds.
    dt = clock.tick(60) / 1000
    all_sprites.update(dt)
    player.update(dt)
    all_sprites.draw(surface) #refresh player position
    pygame.display.flip()

P.S.:不要介意我的精灵如何跳跃,我现在只是这样写的。稍后我将让他在重力作用下真正跳跃。先谢谢了。

【问题讨论】:

    标签: python pygame sprite


    【解决方案1】:

    您已经检查了子弹是否离开屏幕顶部:

    if self.rect.bottom <= 0:
        self.kill()
    

    你可以改成

    if not pygame.display.get_surface().get_rect().colliderect(self.rect):
        self.kill()
    

    如果精灵根本不在屏幕上,就杀死它。

    但你的问题其实是这样的:

    def update(self, dt):
        # Add the velocity to the position vector to move the sprite
        if self.direction == "right":
            #self.vel = pygame.math.Vector2(750, 0)
            self.image = pygame.image.load(os.path.join('images','fireball.png'))
            self.rect  = self.image.get_rect()
            self.pos += self.vel * dt
        else:
            #self.vel = pygame.math.Vector2(-750, 0)
            BULLET_IMG = pygame.image.load(os.path.join('images','fireball.png'))
            self.image = pygame.transform.flip(BULLET_IMG, True, False)
            self.rect  = self.image.get_rect()
            self.pos -= self.vel * dt
        ...
    

    在这里,您为Bullet 类的每个实例加载一次fireball.png 图像 每一帧。您的目标是 60 fps,因此当有 50 个Bullet 实例时,您尝试每秒从磁盘加载文件 300 次。

    相反,您应该在启动时加载图像一次

    下面是它的样子:

    class Bullet(pygame.sprite.Sprite):
    
        IMAGE = None
        FLIPPED_IMAGE = None
    
        def __init__(self, pos, direction, *sprite_groups):
            super().__init__(*sprite_groups)
    
            # cache images
            if not Bullet.IMAGE:
                Bullet.IMAGE = pygame.image.load(os.path.join('images','fireball.png'))
                Bullet.FLIPPED_IMAGE = pygame.transform.flip(Bullet.IMAGE, True, False)
    
            if direction == "right":
                self.vel = pygame.math.Vector2(750, 0)
                self.image = Bullet.IMAGE 
            else:
                self.vel = pygame.math.Vector2(-750, 0)
                self.image = Bullet.FLIPPED_IMAGE 
    
            # suspicious... Should use the Sound class instead
            # self.sound = pygame.mixer.music.play()
            self.pos = pygame.math.Vector2(pos)
            self.rect  = self.image.get_rect(center=pos)
    
        def update(self, dt):
            # Add the velocity to the position vector to move the sprite
            self.pos += self.vel * dt
            self.rect.center = self.pos  # Update the rect pos.
            if not pygame.display.get_surface().get_rect().colliderect(self.rect):
                self.kill()
    

    此外,您应该只使用pygame.mixer.music.play() 播放背景音乐(因为使用music.play() 一次只能播放一个文件)。对于音效,最好使用Sound class

    【讨论】:

    • 非常感谢。虽然你的回答有一个错误。当我运行它时,它说:AttributeError: 'Bullet' object has no attribute 'direction'。我发现你必须在 : super().__init__(*sprite_groups) 下添加:self.direction = direction。
    • @M3lon_sauc3 啊,是的,我的错。该行应为if direction == "right": 而不是if self.direction == "right":。我更新了我的答案。
    【解决方案2】:

    您可以检查子弹是否在屏幕中,如果不在则将其删除。

    for bullet in bullets[:]:
        if not surface.get_rect().collidepoint(bullet.pos):
            #remove the bullet
    

    或者,您可以在一定时间后将其删除。在子弹类的__init__ 方法下,您可以添加start = time.time() 以记录它何时产生。

    class Bullet(pygame.sprite.Sprite):
    
        def __init__(self, pos, direction, *sprite_groups):
            super().__init__(*sprite_groups)
    
            self.image = pygame.image.load(os.path.join('images','fireball.png'))
            self.rect  = self.image.get_rect()
            self.sound = pygame.mixer.music.play()
            self.pos = pygame.math.Vector2(pos)
            self.vel = pygame.math.Vector2(750, 0)
            self.direction = direction
            self.start = time.time()
    

    然后在你的主循环中创建一个变量now = time.time() 来跟踪当前时间。您可以通过删除生成时间来确定子弹存在多长时间,即self.start 来自now

    def kill(self, now, lifetime):
        if now - self.start > lifetime:
            #remove bullet
    

    【讨论】:

    • 您从哪里获得bullets 列表?为什么要遮蔽Sprite 类的kill 方法?
    • 我没有仔细研究他的代码。我只是想提供一个关于如何完成的总体思路。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-24
    • 2013-01-02
    • 2017-03-03
    • 1970-01-01
    • 2019-08-17
    • 1970-01-01
    相关资源
    最近更新 更多