Forum Archive

fps drops rapid when "boss" SpriteNode appears

Bjucha

Hello, so I gotten pretty far with my "game", And I thought that now would be the time to make it a bit more challenge by adding a boss. But for some reason when the boss arrives the fps in the game drops more and more, and I don't understand way since all that is happening is that when a timer enter 0 (The distance to the boss in the game) the boss appears.

Here is the whole code:

from scene import *
from math import sin, cos, pi
import random, ui, time

A = Action

#Timme = 100

class Boss (SpriteNode):
    def __init__(self, **kwargs):
        SpriteNode.__init__(self, 'spc:UFOBlue', **kwargs)
        self.scale = 2.5
        self.destroyed = False


class Enemy (SpriteNode):
    def __init__(self, **kwargs):
        img = random.choice(['spc:EnemyBlack1', 'spc:EnemyRed1', 'spc:EnemyBlue1' ])
        SpriteNode.__init__(self, img, **kwargs)
        self.destroyed = False

class SuperEnemy (SpriteNode):
    def __init__(self, **kwargs):
        SpriteNode.__init__(self, 'spc:EnemyBlack4', **kwargs)
        self.scale = 1.5
        self.destroyed = False

class PowerUp (SpriteNode):
    def __init__(self, **kwargs):
        SpriteNode.__init__(self, 'spc:PowerupRedStar', **kwargs)

class Gamefield (Scene):
    def setup (self):
        self.items = []
        self.bossfire = []
        self.background_color = '#0c1037'
        score_font = ('Futura', 20)
        self.distance_label = LabelNode('Distance to boss', score_font, parent=self)
        self.lasers = []
        #self.timme = LabelNode('1000', font=('Helvetica', 20),parent=self)
        self.distance_label.position = (self.size.w/3.9, self.size.h - 20)
        self.distance = 500
        self.blasers = []

        self.ship = SpriteNode('spc:PlayerShip3Red')
        self.ship.position = (100,40)
        self.add_child(self.ship) 

    def touch_began(self, touch):
        x, y = touch.location
        move_action = Action.move_to(x, y , 0.8, TIMING_SINODIAL)
        self.ship.run_action(move_action)
        self.shoot_laser()

    def check_laser_collisions(self):
        for laser in list(self.lasers):
            if not laser.parent:
                self.lasers.remove(laser)
                continue
            for item in self.items:
                if not isinstance(item, Enemy):
                    continue
                if item.destroyed:
                    continue
                if laser.position in item.frame:
                    self.enemy_smash(item)
                    self.lasers.remove(laser)

                    laser.remove_from_parent()
                    break



    def enemy_smash(self, enemy):


        enemy.destroyed = True              

        enemy.remove_from_parent()
        #self.score += 1
        debree = SpriteNode('spc:PlayerShip1Damage1', parent=self)
        debree.position = enemy.position
        debree.z_position = - 1
        debree.run_action(A.move_by(10, 10, 1.6, TIMING_EASE_OUT))
        debree.run_action(A.sequence(A.wait(1), A.remove()))

    def enemy_hit(self, super_enemy):                                           
        super_enemy.remove_from_parent()
        super_enemy.destroyed = True                
        debree = SpriteNode('spc:PlayerShip3Damage1', parent=self)
        debree.position = super_enemy.position
        debree.z_position = - 1
        debree.run_action(A.move_by(10, 10, 1.6, TIMING_EASE_OUT))
        debree.run_action(A.sequence(A.wait(1), A.remove()))    
    #   self.score += 1


    def update(self):

        self.evil_laser_hit()
        self.check_item_collisions()

        if random.random() < 0.009:
            self.spawn_item()

        self.check_laser_collisions()   
        self.check_laser_hit()
        if random.random() < 0.004:
            self.spawn_enemy()
        #   self.evil_laser()

        self.distance -= 1
        self.distance_label.text = str(self.distance)
        if self.distance <= 0:

            self.distance_label.text = 'Boss appears!'
            self.spawn_boss()


    def spawn_boss(self):
        boss = Boss(parent=self)    

        boss.position = (100,600)





    def spawn_enemy(self):
        super_enemy = SuperEnemy(parent=self)
        super_enemy.position = (random.uniform(10, self.size.w-10), self.size.h + 30) 
        h = random.uniform(2.0, 19.0)
        actions = [A.move_to(random.uniform(0, self.size.w), -100, h), A.remove()]
        super_enemy.run_action(A.sequence(actions))

        self.items.append(super_enemy)
        blasers = SpriteNode('spc:LaserBlue11', parent=self)
        blasers.position = super_enemy.position + (0, 30)
        blasers.z_position = -1
        b = random.uniform(10.0, 250)
        actions = [A.move_to(b, 100, 2 * 5), A.remove()]                                                    
        blasers.run_action(A.sequence(actions))
        self.blasers.append(blasers)



    def check_laser_hit(self):
        for laser in list(self.lasers):
            if not laser.parent:
                self.lasers.remove(laser)
                continue
            for item in self.items:
                if not isinstance(item, SuperEnemy):
                    continue
                if item.destroyed:
                    continue
                if laser.position in item.frame:
                    self.enemy_hit(item)
                    self.lasers.remove(laser)
                    laser.remove_from_parent()

                    break



    def spawn_item(self):

        enemy = Enemy(parent=self)
        enemy.position = (random.uniform(10, self.size.w-10), self.size.h + 30) 

        d = random.uniform(2.0, 19.0) 

        actions = [A.move_by(0, -(self.size.h + 60), d), A.remove()]
        enemy.run_action(A.sequence(actions))

        self.items.append(enemy)
        if self.distance <= 0:
            enemy.remove_from_parent() # Working?



    def check_item_collisions(self):

        for item in list(self.items):
            distance = abs(item.position - self.ship.frame.center())
            if item.parent == None: # Working!!!
                continue

            if distance < 60:
                exit()

    def evil_laser_hit(self):
        for blaser in list(self.blasers):
            if not blaser.parent:
                self.blasers.remove(blaser)
                continue

            if blaser.position in self.ship.frame:
                exit()
                blaser.remove_from_parent()

                    #break

    def shoot_laser(self):

        lasers = SpriteNode('spc:LaserRed10', parent=self)
        lasers.position = self.ship.position + (0, 30)
        lasers.z_position = -1
        actions = [A.move_by(0, self.size.h, 1.3 * self.speed), A.remove()]
        lasers.run_action(A.sequence(actions))
        self.lasers.append(lasers)


run(Gamefield(), PORTRAIT, show_fps = True)
JonB

you are spawning the boss whenever distance <=0 . distance counts down every update(). but I don't see you resetting distance. Meaning when distance goes to 0, boss is spawned every update(), or 60 bosses per second. pretty soon you have thousands of Boss nodes attached to your scene!

I think you either need to reset distance, or set a flag that the boss is already spawned, etc. or only spawn the boss when distance==0, not <=.

Bjucha

@JonB thanks I did not think of that, will try to fix it Thank you

ccc

Thousands of bosses... Nightmare!!

Bjucha

@JonB Thanks the solution was simple, I just didn't see it, @ccc yeah one big bad boss is enough
But now He needs some cool firepower. Will update you when he is ready for a fight

Bjucha

Ok so now the fps is fixed and the boss is just one big UFO.
But now I have a new problem(No suprise there) When the boss appears he fires one laser shot,
but no more. What I'm trying to do is to make him fire lasers all the time (after all he is the boss) but I cant call such a function from update() since the boss is a class and not in the gamefield,
My question: Is there a way in update() to point to spawn:boss() function (or is there a way inside the that function to create something that update all the time)
Sorry for the long post, basically: how do my boss keep firing until he is removed from scene?

omz

@Bjucha You could make the boss a property of your scene (i.e. set self.boss = ... when you spawn it). That way, the boss could have a last_shot_time property, you check that in update, and if it's bigger than, say, 1 second, you set last_shot_time to the current time, and fire a shot.

Bjucha

@omz hmm I really sorry but I don't really understand. what do you mean with property of my scene? and shall that property be inside the spawn_boss()
function? or in the gamefield?

ccc
def spawn_boss(self):
    self.boss = Boss(parent=self, position = (100, 600))
Bjucha

Haven't been able to get it to work yet, but Im currently trying a different approach that could work.

flight_714

I realize this is a few weeks old, but had a few thoughts.

I typically add scene objects of a shared type to a list and then iterate over a method of their class in update.

So at the end up the enemy class init I would add 'enemies.append(self)' and then in the scene update method add 'for enemy in enemies': enemy.cooldown() or something to that to that effect.

Obviously, you'd have to write a bit more AI methods for the enemy ships, so you'd probably want to subclass them to save space.

Bjucha

@flight_714

Thanks for the tip. I did get it to work by using
```
counter = 10
while counter != 0:
Action.wait(2)
self.items.append(boss)
bossfires = SpriteNode('spc:BoltBronze', parent=self)
bossfires.position = boss.position + (-50, 30)
bossfires.z_position = -1
b = random.uniform(10.0, 2500)
actions = [A.move_to(b, 100, 2 * 2), A.remove()]

        bossfires.run_action(A.sequence(actions))
        self.bossfires.append(bossfires)
        Action.wait(2)
        counter = counter - 1```

This works but is not really "perfect" for me. But will look at what you suggested and see if I can use it.