omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    Welcome!

    This is the community forum for my apps Pythonista and Editorial.

    For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.


    Imported image as Scene background

    Pythonista
    6
    39
    11206
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Robert_Tompkins
      Robert_Tompkins last edited by

      Hi all, have been loving Pythonista for a few years now as a way to develop my programming skills.
      I recently transitioned from building UI’s for my circuit calculating programs into building games using the Scene module.

      I used OMZ’s little Alien shooter tutorial as a template to build a classic ‘space shooter’ game.
      Here’s the question:
      Is there a way (I know there is) to use my own imported image as the scene background?

      I’d like to replace the current background:

      self.background_color = ('#000000')
      

      With a more fitting background/scene.
      Essentially, I’d like to do this:

      self.background_color = ('deepSpace.png')
      

      But when that didn’t work (understandably), I tried:

      self.background = ('deepSpace.png')
      

      Etc.

      I guess I could create a new SpriteNode that extends across the x,y of my scene.. but wanted to be sure there wasn’t a better way to do this.

      Thanks,
      RHT

      JonB mikael nol_03 3 Replies Last reply Reply Quote 0
      • Robert_Tompkins
        Robert_Tompkins last edited by Robert_Tompkins

        So I ended up just rolling with this:

        class MyScene (Scene):
            def setup(self):
                self.background = SpriteNode('deepSpace.png', size = self.size, position = self.size / 2)
                self.add_child(self.background) 
        

        Now I just need to figure out how to get a higher quality image loaded in.
        It seems that larger files may take too long to load, resulting in failure to load texture.
        Or, if somebody has an example I could reference or tips, I would like to animate the background to give the impression of ‘life’.
        Maybe some twinkles, slight movement, etc.
        I haven’t found tutorials or good examples covering the use of shaders or textures to do this.
        Otherwise, I’ll consider this mission accomplished ..for now.

        1 Reply Last reply Reply Quote 0
        • mwsx
          mwsx last edited by mwsx

          Not sure what you’re stuck on but if you want to animate your background to give the impression of life what you really want to do is not animate the background itself but the children sprites that you will add into your background node.

          So if you want to twinkle little stars in your background, you will create your star spriteNode and add it as a child of your background node and run an action :move,rotate,scale etc.

          if you really want to animate the background and not objects that are on it then it’s all the same since background it’s a spritenode.

          About the image quality i don’t think it should be a problem to load a big image, it’s just one image.

          1 Reply Last reply Reply Quote 0
          • Robert_Tompkins
            Robert_Tompkins last edited by

            Yes, that will work great! Thanks for that idea.
            I’m starting to get the hang of using actions.
            Just had an aha moment earlier with them when I was trying to figure out a way to produce 3 new, smaller meteors originating from 1 destroyed meteor. Then I went all crazy resulting in 3^4 objects created from one in some cases.

            Anyway, yes, I will add some sprites to my background, maybe I’ll have the stars fadeIn/fadeOut depending on update(). Thanks!

            Regarding the image problem:
            I feel like I ruled out all other variables when I was testing it out. The original size of my image was ~4MB. I ended up taking a screenshot of it, importing it locally, passing the new IMG name, got the ‘failed to load texture’ error.
            So I rotated the image and took another screenshot of it, cropped it, resulting in an image < 1MB.
            Only then was I able to successfully load the texture.
            I’ll give it another shot, but I do remember seeing some function in the docs something like ‘load_texture’ meant to improve performance if used prior to presenting it. If I can find it, I’ll try that too.

            Again, thank you for the suggestions!

            1 Reply Last reply Reply Quote 0
            • Robert_Tompkins
              Robert_Tompkins last edited by

              Found this after an hour of digging:


              @JonB said:

              ohh, ok. scene Textures are opengl based, i think. So you cannot have a Texture that is larger in pixels that the graphics memory region, which is something like twice largest screen size (for retina displays).

              with ui.ImageContext(1024*2,1024*2) as ctx:
                 #ui.Image.named('IMG_0625.JPG').draw()
                 Texture(ctx.get_image())
              

              The above works, on my Ipad3. Anything up to 2048 in either row/col works, but even 2049x1 fails. so, that tells me the opengl textures must be <=2048 in either dimension.


              I’m currently using an iPhone 11 Pro Max.
              I went ahead and used his test method, implemented outside of and prior to any Scene related activity.

              with ui.ImageContext(1365,1365) as ctx: 
              ## ANYTHING > 1365 FOR EITHER ARGUMENT INVOKES ('ValueError': Could not load image) ON LINE 58: ‘background = SpriteNode(largeImage)‘
                  ui.Image.named('hqDeepSpace.png').draw()
                  largeImage = Texture(ctx.get_image())
                  background = SpriteNode(largeImage)
              

              hqDeepSpace.png specs:
              PNG Image
              2.6MB
              6000x4000 px

              My knowledge level in this area is minimal, so if somebody else is better equipped to solve this or find a work around, etc. let me know.
              Otherwise, I will stick with my lower quality image.

              cvp 1 Reply Last reply Reply Quote 0
              • cvp
                cvp @Robert_Tompkins last edited by

                @Robert_Tompkins you could set the Scene background as transparent and add an ImageView as subview, like in this topic

                Something like

                from objc_util import *
                from scene import *
                import ui
                
                glClearColor = c.glClearColor
                glClearColor.restype = None
                glClearColor.argtypes = [c_float, c_float, c_float, c_float]
                glClear = c.glClear
                glClear.restype = None
                glClear.argtypes = [c_uint]
                GL_COLOR_BUFFER_BIT = 0x00004000
                
                
                class ChristmasScene(Scene):
                    def setup(self):
                        objv = ObjCInstance(self.view)
                        objv.glkView().setOpaque_(False)
                        
                        sp = SpriteNode('emj:Christmas_Tree', anchor_point=(0,0), position=(500,300), parent=self)
                
                    def draw(self):
                        glClearColor(0, 0, 0, 0)
                        glClear(GL_COLOR_BUFFER_BIT)
                        
                w, h = ui.get_window_size()
                v = ui.ImageView(frame=(0,0,w,h))
                v.image = ui.Image.named('test:Peppers')
                gameview = SceneView()
                gameview.scene = ChristmasScene()
                
                gameview.add_subview(v)
                v.send_to_back()
                
                gameview.present('full_screen') 
                
                1 Reply Last reply Reply Quote 1
                • mwsx
                  mwsx last edited by mwsx

                  Here is my star method if you want.

                  
                  	def instantiate_star(self):
                  		self.star = SpriteNode('white_circle_2.png')
                  		self.star.z_position = random.choice([-1,0,2])
                  		self.star.alpha=0
                  		self.star.scale =0.01/2
                  		self.star.position = (random.uniform(0,self.size.w), random.uniform(0,self.size.h))
                  
                  		self.star.rotation= (random.uniform(0,360))
                  		d = random.uniform(0.5, 1)
                  		i = random.uniform(3,6)
                  		j = random.uniform(2,5)
                  		actions = [A.scale_by(random.uniform(0,0.01), i), A.fade_to(d,i), A.scale_to(random.uniform(0.02,0.0), j),A.fade_to(0,j),A.remove()]
                  		self.star.run_action(A.sequence(actions))
                  		self.starNode.add_child(self.star)
                  	
                  	
                  
                  	  
                  

                  You’ll just have to replace the sprite with some white circle image

                  1 Reply Last reply Reply Quote 1
                  • Robert_Tompkins
                    Robert_Tompkins last edited by

                    @cvp
                    Transparent? Again, another good idea that never crossed my mind. Even better, implementing the UI Module.
                    I started with UI, definitely prefer it over Tk. I have yet to (knowingly) blend UI, Scene objects into one program. If it works well, that will open a lot of doors and I’ll be able to reuse a ton of my old code. Thanks for the example!

                    @mwsx
                    Sweet, code snippets are the best learning tool for me. I’ll throw that in my code and tweak some stuff a bit. Thanks for the help!

                    Now all that’s left is for me to find a way to better handle the stuttering I’ve been experiencing when my game starts creating the ‘clusters’ of meteors.. Most likely this is just a limitation of developing a game like this via Python-mobile.

                    But the stuttering occurs when I shoot a meteor falling on the screen. This meteor has a chance to break into 3 smaller meteors, each of these has a chance to break into 3 more, and those 3 will do the same. For each of the smallest meteors broken, a star is created, that slowly falls along the x-axis.
                    Each star I had flashing between 2 colors to keep it from blending in with harmful debris, but removed that to improve performance. All of those objects can be created and destroyed in a matter of 200-500ms.
                    I’ve had to use async-wrappers for some of my UI programs, is this something that can be utilized here? Or will I just need to slow the baby making down a bit?

                    1 Reply Last reply Reply Quote 0
                    • mwsx
                      mwsx last edited by mwsx

                      I have way more things acting in my scene and everything’s running fine, everytime I saw a dropped in FPS it was becauseI did something wrong not because of any limitations. Maybe you're instantiating things in a loop or maybe you just have a print in update which makes the FPS drop pretty fast also.
                      Also do you remove the meteors from their parent once they get destroyed ?
                      If you could paste the meteor code I could check what’s the problem

                      1 Reply Last reply Reply Quote 0
                      • Robert_Tompkins
                        Robert_Tompkins last edited by

                        I do have a few things printing, though I make sure I don’t throw them in update() unless I toss a counter in there limiting it a bit.
                        Btw, that star node worked great! It goes perfect with my background. I had to tweak it a bit to reduce the number on-screen at a time because according to my wife, it’s “too distracting”.

                        Anyway, I’ll post the meteor section. Unfortunately, I created quite a few sprites from custom images I made in paint. So if you want the full code to experiment more, I’d have to give you a handful of crudely drawn images as well haha.

                        Also, I am just now realizing i reaaallyy need to clean this section up. I went ahead and added notes to it to reduce confusion!
                        Sorry about the mess :D

                        ############################################################
                            ## This gets called when a laser intersects with a non-destroyed Meteor
                            def destroy_meteor(self, meteor):
                                #print(f'Entered destroy_meteor() with meteor: {meteor}')
                                ########################################################
                                ## If meteor object is a Meteor:
                                ###### Play generic explosion sound
                                ###### Flag THIS meteor as destroyed
                                ###### Swap texture of meteor with a coin
                                ########## Creates 5 Sprites that disperse around meteor
                                ########## Essentially gives appearance of it shattering
                                if isinstance(meteor, Meteor):
                                    sound.play_effect('arcade:Explosion_2', 0.1)
                                    #print(f'isinstance(meteor, Meteor): item: {meteor}')
                                    meteor.destroyed = True
                                    meteor.texture = Texture('plf:Item_CoinBronze')
                                    #meteor.color = 'mediumspringgreen'
                                    for i in range(5):
                                        m = SpriteNode('spc:MeteorBrownMed1', parent=self)
                                        m.position = meteor.position + (random.uniform(-20, 20), random.uniform(-20, 20))
                                        angle = random.uniform(0, pi*2)
                                        dx, dy = cos(angle) * 80, sin(angle) * 80
                                        m.run_action(A.move_by(dx, dy, 0.6, TIMING_EASE_OUT))
                                        m.run_action(A.sequence(A.scale_to(0, 0.6), A.remove()))
                                ########################################################
                                ## If meteor object is a Meteor2:
                                ###### Play generic explosion sound
                                ###### 25% * stageNumber (stageNumber is an int between 1 and 20, so far)
                                ########## Create 3 new meteors (meteorMedium)
                                ########## Place them slightly above previous meteor to make it easier
                                ########## Disperse downward at a slower speed
                                ###### Create a special explosion visual to differentiate it from other meteors
                                if isinstance(meteor, Meteor2):
                                    sound.play_effect('arcade:Explosion_2', 0.25)
                                    if random.random() < 0.25 * self.stageNumber:
                                        for i in range(3):
                                            meteorMedium = MeteorMedium(parent=self)
                                            meteorMedium.destroyed = False
                                            meteorMedium.position = meteor.position + (random.uniform(-30, 30), (-20 + random.uniform(-5, 5)))
                                            downwardAngle = random.uniform(pi, pi*2)
                                            dx, dy = cos(downwardAngle) * 80, sin(downwardAngle) * 80
                                            d = random.uniform(8.0, 15.0)
                                            actions = [A.move_to(random.uniform(0, self.size.w), -25, d), A.remove()]
                                            meteorMedium.run_action(A.sequence(actions))
                                            self.items.append(meteorMedium)
                                    m = SpriteNode('shp:Explosion00', parent=self)
                                    m.position = meteor.position
                                    m.run_action(A.sequence(A.fade_to(0, 0.3, TIMING_EASE_OUT), A.remove()))
                                    meteor.destroyed = True
                                    meteor.remove_from_parent()
                                ########################################################
                                ## Same as Meteor2 above with slight tweaks
                                ## Creates 3 new meteors (meteorSmall)
                                if isinstance(meteor, MeteorMedium):
                                    sound.play_effect('arcade:Explosion_2', 0.5)
                                    if random.random() < 0.20 * self.stageNumber:
                                        for i in range(3):
                                            meteorSmall = MeteorSmall(parent=self)
                                            meteorSmall.destroyed = False
                                            meteorSmall.position = meteor.position + (random.uniform(-50, 50), (-20 + random.uniform(-5, 5)))
                                            downwardAngle = random.uniform(pi, pi*2)
                                            dx, dy = cos(downwardAngle) * 80, sin(downwardAngle) * 80
                                            d = random.uniform(7.0, 12.0)
                                            actions = [A.move_to(random.uniform(0, self.size.w), -25, d), A.remove()]
                                            meteorSmall.run_action(A.sequence(actions))
                                            self.items.append(meteorSmall)
                                    m = SpriteNode('shp:BlackSmoke00', scale = 0.75, parent=self)
                                    m.position = meteor.position
                                    m.run_action(A.sequence(A.fade_to(0, 0.3, TIMING_EASE_OUT), A.remove()))
                                    meteor.destroyed = True
                                    meteor.remove_from_parent()
                                ########################################################
                                ## Same as MeteorMedium above with slight tweaks
                                ## Creates 3 new meteors (meteorTiny)
                                if isinstance(meteor, MeteorSmall):
                                    sound.play_effect('arcade:Explosion_2', 0.75)
                                    if random.random() < 0.15 * self.stageNumber:
                                        for i in range(3):
                                            meteorTiny = MeteorTiny(parent=self)
                                            meteorTiny.destroyed = False
                                            meteorTiny.position = meteor.position + (random.uniform(-100, 100), random.uniform(-5, 5))
                                            downwardAngle = random.uniform(pi, pi*2)
                                            dx, dy = cos(downwardAngle) * 80, sin(downwardAngle) * 80
                                            d = random.uniform(6.0, 10.0)
                                            actions = [A.move_to(random.uniform(0, self.size.w), -25, d), A.remove()]
                                            meteorTiny.run_action(A.sequence(actions))
                                            self.items.append(meteorTiny)
                                    m = SpriteNode('shp:BlackSmoke00', scale = 0.50, parent=self)
                                    m.position = meteor.position
                                    m.run_action(A.sequence(A.fade_to(0, 0.3, TIMING_EASE_OUT), A.remove()))
                                    meteor.destroyed = True
                                    meteor.remove_from_parent()
                                ########################################################
                                ## If meteor object is a MeteorTiny:
                                ###### Play generic explosion sound
                                ###### Flag THIS tiny meteor as destroyed
                                ###### Swap texture of small meteor with a silver star
                                ###### Drop star slowly along X axis
                                if isinstance(meteor, MeteorTiny):
                                    sound.play_effect('arcade:Explosion_6', 1.0)
                                    meteor.destroyed = True
                                    meteor.texture = Texture('spc:StarSilver', scale = 0.10)
                                    #meteor.color = '#FA1BCA'
                                    d = random.uniform(5.0, 8.0)
                                    actions = [A.move_by(0, -(self.size.h + 30), d), A.remove()]
                                    meteor.run_action(A.sequence(actions)) ```
                        1 Reply Last reply Reply Quote 0
                        • Robert_Tompkins
                          Robert_Tompkins last edited by ccc

                          Aaa and here’s a copy without the comments.
                          Should be less confusing for the syntax highlighting algorithm.

                          def destroy_meteor(self, meteor):
                                  #print(f'Entered destroy_meteor() with meteor: {meteor}')
                                  if isinstance(meteor, Meteor):
                                      sound.play_effect('arcade:Explosion_2', 0.1)
                                      #print(f'isinstance(meteor, Meteor): item: {meteor}')
                                      meteor.destroyed = True
                                      meteor.texture = Texture('plf:Item_CoinBronze')
                                      #meteor.color = 'mediumspringgreen'
                                      for i in range(5):
                                          m = SpriteNode('spc:MeteorBrownMed1', parent=self)
                                          m.position = meteor.position + (random.uniform(-20, 20), random.uniform(-20, 20))
                                          angle = random.uniform(0, pi*2)
                                          dx, dy = cos(angle) * 80, sin(angle) * 80
                                          m.run_action(A.move_by(dx, dy, 0.6, TIMING_EASE_OUT))
                                          m.run_action(A.sequence(A.scale_to(0, 0.6), A.remove()))
                          
                                  if isinstance(meteor, Meteor2):
                                      sound.play_effect('arcade:Explosion_2', 0.25)
                                      if random.random() < 0.25 * self.stageNumber:
                                          for i in range(3):
                                              meteorMedium = MeteorMedium(parent=self)
                                              meteorMedium.destroyed = False
                                              meteorMedium.position = meteor.position + (random.uniform(-30, 30), (-20 + random.uniform(-5, 5)))
                                              downwardAngle = random.uniform(pi, pi*2)
                                              dx, dy = cos(downwardAngle) * 80, sin(downwardAngle) * 80
                                              d = random.uniform(8.0, 15.0)
                                              actions = [A.move_to(random.uniform(0, self.size.w), -25, d), A.remove()]
                                              meteorMedium.run_action(A.sequence(actions))
                                              self.items.append(meteorMedium)
                                      m = SpriteNode('shp:Explosion00', parent=self)
                                      m.position = meteor.position
                                      m.run_action(A.sequence(A.fade_to(0, 0.3, TIMING_EASE_OUT), A.remove()))
                                      meteor.destroyed = True
                                      meteor.remove_from_parent()
                          
                                  if isinstance(meteor, MeteorMedium):
                                      sound.play_effect('arcade:Explosion_2', 0.5)
                                      if random.random() < 0.20 * self.stageNumber:
                                          for i in range(3):
                                              meteorSmall = MeteorSmall(parent=self)
                                              meteorSmall.destroyed = False
                                              meteorSmall.position = meteor.position + (random.uniform(-50, 50), (-20 + random.uniform(-5, 5)))
                                              downwardAngle = random.uniform(pi, pi*2)
                                              dx, dy = cos(downwardAngle) * 80, sin(downwardAngle) * 80
                                              d = random.uniform(7.0, 12.0)
                                              actions = [A.move_to(random.uniform(0, self.size.w), -25, d), A.remove()]
                                              meteorSmall.run_action(A.sequence(actions))
                                              self.items.append(meteorSmall)
                                      m = SpriteNode('shp:BlackSmoke00', scale = 0.75, parent=self)
                                      m.position = meteor.position
                                      m.run_action(A.sequence(A.fade_to(0, 0.3, TIMING_EASE_OUT), A.remove()))
                                      meteor.destroyed = True
                                      meteor.remove_from_parent()
                          
                                  if isinstance(meteor, MeteorSmall):
                                      sound.play_effect('arcade:Explosion_2', 0.75)
                                      if random.random() < 0.15 * self.stageNumber:
                                          for i in range(3):
                                              meteorTiny = MeteorTiny(parent=self)
                                              meteorTiny.destroyed = False
                                              meteorTiny.position = meteor.position + (random.uniform(-100, 100), random.uniform(-5, 5))
                                              downwardAngle = random.uniform(pi, pi*2)
                                              dx, dy = cos(downwardAngle) * 80, sin(downwardAngle) * 80
                                              d = random.uniform(6.0, 10.0)
                                              actions = [A.move_to(random.uniform(0, self.size.w), -25, d), A.remove()]
                                              meteorTiny.run_action(A.sequence(actions))
                                              self.items.append(meteorTiny)
                                      m = SpriteNode('shp:BlackSmoke00', scale = 0.50, parent=self)
                                      m.position = meteor.position
                                      m.run_action(A.sequence(A.fade_to(0, 0.3, TIMING_EASE_OUT), A.remove()))
                                      meteor.destroyed = True
                                      meteor.remove_from_parent()
                          
                                  if isinstance(meteor, MeteorTiny):
                                      sound.play_effect('arcade:Explosion_6', 1.0)
                                      meteor.destroyed = True
                                      meteor.texture = Texture('spc:StarSilver', scale = 0.10)
                                      #meteor.color = '#FA1BCA'
                                      d = random.uniform(5.0, 8.0)
                                      actions = [A.move_by(0, -(self.size.h + 30), d), A.remove()]
                                      meteor.run_action(A.sequence(actions))
                          
                          1 Reply Last reply Reply Quote 0
                          • JonB
                            JonB last edited by

                            I am not 100 percent sure, but I think you may end up with better performance if you
                            create all of your textures at the start, as scene.Texture's, then instantiate your sprite nodes with those. I just am not sure whether SpriteNode is smart enough to figure out that the Image with the same name is the same image and this only create one UI.Image, and one scene.Texture-- probably not. So every time you create a new SpriteNode, there is a fair amount of work creating new textures.

                            It is probably best to create and save the textures first, up front, as named globals that can just be reused. SpriteNode accepts a Texture object.

                            Even better might be to create a set of SpriteNodes, that you reuse at different scale after they are destroyed.

                            Finally, for a large tiled object, there may be some performance benefit in using an EffectNode -- but I'm not sure.

                            Finally, check out @mikael's SpriteKit wrapper. This allows you to make use of physics, lighting, particle emitters, and so on.
                            https://forum.omz-software.com/topic/5802/spritekit-wrapper/18

                            1 Reply Last reply Reply Quote 0
                            • Robert_Tompkins
                              Robert_Tompkins last edited by

                              @JonB
                              Thanks for the tips! I went ahead and defined some global textures and pass those in instead. Performance didn’t get much better, so I’ll try the reusing at smaller scale approach.
                              I also tried increasing the Δy for the stars to help get them off-screen ASAP. This didn’t help much either.
                              This tells me the main contributor is likely the creation/destruction of a large amount of entities.
                              It doesn’t help that I usually have my ‘laser’ upgraded to the point that I am creating and firing (+Δy) ~40-50 projectiles ~5x/s.
                              So 200+ projectiles shot in a +Δy destroying many entities falling in -Δy that create as many new star entities falling in a different -Δy.

                              Thanks for that link! I’ll have to play with that after work. Looks like you contributed a fair bit to that as well.
                              If all fails, I’ll go through and clean up my code a bit. There’s plenty of room for improvement in that area.

                              mikael 1 Reply Last reply Reply Quote 0
                              • mikael
                                mikael @Robert_Tompkins last edited by

                                @Robert_Tompkins, sorry if I missed it, but did you try tiling the background, i.e. use a few composable images to build the background? There are suitable images on the internet, and this is efficient as the textures are only stored once.

                                1 Reply Last reply Reply Quote 0
                                • Robert_Tompkins
                                  Robert_Tompkins last edited by Robert_Tompkins

                                  @mikael
                                  No I have not tried that yet.
                                  I currently use a dark image of space with stars as my background(107KB) and add life to it using a heavily modified/toned down version of the method provided by @mwsx :

                                  def createDestroyStar(self):
                                          self.star = SpriteNode('shp:nova')
                                          #self.star.color = random.choice(self.listOfStarColors)
                                          if random.random() <= 0.25:
                                              self.star.color = '#ffefb3'
                                          else:
                                              self.star.color = '#FFFFFF'
                                          self.star.alpha = 0
                                          self.star.scale = 0.25/5
                                          self.star.position = (random.uniform(0,self.size.w), random.uniform(0,self.size.h))
                                          self.star.rotation = (random.uniform(0,360))
                                          noneToOne = random.uniform(0.0, 1)
                                          i = random.uniform(0,3)
                                          actions = [
                                              A.scale_by(random.uniform(0, 0.01), i),
                                              A.fade_to(noneToOne, i),
                                              A.scale_to(random.uniform(0.02, 0.0), noneToOne),
                                              A.fade_to(0, noneToOne),
                                              A.remove()
                                              ]
                                          self.star.run_action(A.sequence(actions))
                                          self.add_child(self.star) 
                                  

                                  I’m not sure how I would go about replicating this effect using multiple images. However, if you think this method/function is resource heavy, I would settle with just the image background.

                                  I did a few things to improve performance:

                                  • Scaled down effects to smaller size
                                  • Replaced the ‘falling’ animation for my stars with a 100ms animation that pulls each star into the ship, removing it from the scene.

                                  Overall, the performance is better than it has been. Considering the number of Sprites being generated and being destroyed in such a short amount of time, I think the stutter is reasonable.

                                  mikael 1 Reply Last reply Reply Quote 0
                                  • mikael
                                    mikael @Robert_Tompkins last edited by

                                    @Robert_Tompkins, for performance, you could consider showing the same or different picture 1-3 times on top of the background pic, as a semi-transparent layer, and maybe varying the location and transparency of these pictures. This could make the background ”live” without managing hundreds of individual objects.

                                    1 Reply Last reply Reply Quote 0
                                    • Robert_Tompkins
                                      Robert_Tompkins last edited by

                                      @mikael
                                      Man, you’re a genius. How do I not think of these things??
                                      Yes, I will give this a try.
                                      When I set my background_color == black, I can clearly see the number of individual objects being created representing stars. So even if I use 20 pictures, I assume I’ll see a difference.

                                      Before I do the tiling, I’ll remove the stars entirely and use a simple black background color to get a baseline with my current code. Thanks for the info and idea!

                                      1 Reply Last reply Reply Quote 0
                                      • Robert_Tompkins
                                        Robert_Tompkins last edited by

                                        Alright, so I did see significant improvement after making more changes. However... I would like some more info from those that can answer.

                                        I have an upgrade for my ‘rocket’ weapon that was meant to just explode and intersect with nearby meteors to trigger a call to the function handling this event. However, I went a different route as I couldn’t figure out how to do this. Here is effectively what I did:
                                        Created a new Class:

                                        class MiniRocket (SpriteNode):
                                            def __init__(self, **kwargs):
                                                img = 'plf:LaserPurpleDot'
                                                SpriteNode.__init__(self, img, scale = 0.05, **kwargs) 
                                        

                                        Here is my function that handles projectile collisions ( it is called via update() ):

                                        def check_laser_collisions(self):
                                                for projectile in list(self.projectiles):
                                                    if not projectile.parent:
                                                        self.projectiles.remove(projectile)
                                                        continue
                                                    for item in self.items:
                                                        if not isinstance(item, Meteor):
                                                            if not isinstance(item, Meteor2):
                                                                if not isinstance(item, Meteor3):
                                                                    if not isinstance(item, Meteor4):
                                                                        if not isinstance(item, Meteor5):
                                                                            if not isinstance(item, Meteor6):
                                                                                if not isinstance(item, MeteorMedium):
                                                                                    if not isinstance(item, MeteorSmall):
                                                                                        if not isinstance(item, MeteorTiny):
                                                                                            continue
                                                        if item.destroyed:
                                                            #print("Entered if item.destroyed:")
                                                            continue
                                                        #print("Reached if projectile.position in item.frame:")
                                                        if projectile.position in item.frame:
                                                            if isinstance(projectile, Rocket):
                                        #                        sound.play_effect('arcade:Explosion_2')
                                                                self.destroy_meteor(item, 1)
                                                                self.projectiles.remove(projectile)
                                                                projectile.remove_from_parent()
                                                                #m = SpriteNode(explosionTexture, scale = 0.50, color = '#ffaf57', parent=self)
                                                                #m.position = projectile.position
                                                                #m.run_action(A.sequence(A.fade_to(0, 0.3, TIMING_EASE_OUT), A.remove()))
                                                            if isinstance(projectile, MiniRocket):
                                                                #sound.play_effect('arcade:Explosion_2')
                                                                self.destroy_meteor(item)
                                                                projectile.collisionsLeft -= 1
                                                                if projectile.collisionsLeft <= 0:
                                                                    self.projectiles.remove(projectile)
                                                                    projectile.remove_from_parent()
                                                                #m = SpriteNode(explosionTexture, scale = 0.25, color = '#ffaf57', parent=self)
                                                                #m.position = projectile.position
                                                                #m.run_action(A.sequence(A.fade_to(0, 0.3, TIMING_EASE_OUT), A.remove()))
                                                            if isinstance(projectile, Laser):
                                                                self.destroy_meteor(item)
                                                                self.destroy_meteor(item)
                                                                projectile.collisionsLeft -= 2
                                                                if projectile.collisionsLeft <= 0:
                                                                    self.projectiles.remove(projectile)
                                                                    projectile.remove_from_parent()
                                                            else:
                                                                self.destroy_meteor(item)
                                                            #self.projectiles.remove(projectile)
                                                            #projectile.remove_from_parent()
                                                            break 
                                        

                                        Here is destroy_meteor() called above via “self.destroy_meteor(item, 1)”
                                        The second argument ‘1’ is used to indicate that a rocket collision occurred, which is different from other collisions, like from a ‘laser’, or ‘miniRocket’. It uses this to know it should create miniRockets.


                                        
                                        def destroy_meteor(self, meteor, isRocket = None):
                                                global brownMeteorMed
                                                global explosionTexture
                                                global smokeTextureMed
                                                global smokeTextureSmall
                                                global starTextureSilver
                                                #print(f'Entered destroy_meteor() with meteor: {meteor}')
                                                if isinstance(meteor, Meteor):
                                        #            sound.load_effect('arcade:Explosion_2')
                                        #            sound.play_effect('arcade:Explosion_2')
                                                    m = SpriteNode(explosionTexture, scale = 0.25, parent=self)
                                                    m.position = meteor.position
                                                    m.run_action(A.sequence(A.fade_to(0, 0.3, TIMING_EASE_OUT), A.remove()))
                                                    meteor.destroyed = True
                                                    meteor.remove_from_parent()
                                        
                                                if isinstance(meteor, Meteor2):
                                        #            sound.play_effect('arcade:Explosion_7')
                                                    if random.random() < 0.25 * self.stageNumber:
                                                        if self.stageNumber < 5:
                                                            meteor.livesRemaining -= 250
                                                        else:
                                                            if activeWeapon == 'laser':
                                                                meteor.livesRemaining -= 9
                                                            meteor.livesRemaining -= 1
                                                        if meteor.livesRemaining <= 0:
                                                            for i in range(2):
                                                                meteorMedium = MeteorMedium(parent=self)
                                                                meteorMedium.destroyed = False
                                                                meteorMedium.livesRemaining = 10 * self.stageNumber
                                                                meteorMedium.position = meteor.position + (random.uniform(-30, 30), (-20 + random.uniform(-5, 5)))
                                                                downwardAngle = random.uniform(pi, pi*2)
                                                                dx, dy = cos(downwardAngle) * 80, sin(downwardAngle) * 80
                                                                d = random.uniform(8.0, 15.0)
                                                                actions = [A.move_to(random.uniform(0, self.size.w), -25, d), A.remove()]
                                                                meteorMedium.run_action(A.sequence(actions))
                                                                self.items.append(meteorMedium)
                                                            m = SpriteNode(explosionTexture, scale = 0.25, parent=self)
                                                            m.position = meteor.position
                                                            m.run_action(A.sequence(A.fade_to(0, 0.3, TIMING_EASE_OUT), A.remove()))
                                                            meteor.destroyed = True
                                                            meteor.remove_from_parent()
                                                        else:
                                                            if meteor.livesRemaining <= (20 * self.stageNumber) * 0.25:
                                                                meteor.alpha = 0.25
                                                            elif meteor.livesRemaining <= (20 * self.stageNumber) * 0.50:
                                                                meteor.alpha = 0.5
                                                            elif meteor.livesRemaining <= (20 * self.stageNumber) * 0.75:
                                                                meteor.alpha = 0.75
                                                
                                                if isinstance(meteor, MeteorMedium):
                                        #            sound.play_effect('arcade:Explosion_7')
                                                    if random.random() < 0.20 * self.stageNumber:
                                                        if self.stageNumber < 5:
                                                            meteor.livesRemaining -= 250
                                                        else:
                                                            if activeWeapon == 'laser':
                                                                meteor.livesRemaining -= 9
                                                            meteor.livesRemaining -= 1
                                                        if meteor.livesRemaining <= 0:
                                                            m = SpriteNode(smokeTextureSmall, parent=self)
                                                            m.position = meteor.position
                                                            m.run_action(A.sequence(A.fade_to(0, 0.3, TIMING_EASE_OUT), A.remove()))
                                                            meteor.destroyed = True
                                                            meteor.remove_from_parent()
                                                            ## REMOVED BELOW BLOCK TO REDUCE STUTTERING FROM MANY OBJECTS ON SCREEN
                                                            """
                                                            for i in range(2):
                                                                meteorSmall = MeteorSmall(parent=self)
                                                                meteorSmall.destroyed = False
                                                                meteorSmall.livesRemaining = 10 * self.stageNumber
                                                                meteorSmall.position = meteor.position
                                                                downwardAngle = random.uniform(pi, pi*2)
                                                                dx, dy = cos(downwardAngle) * 80, sin(downwardAngle) * 80
                                                                d = random.uniform(7.0, 12.0)
                                                                actions = [A.move_to(random.uniform(0, self.size.w), -25, d), A.remove()]
                                                                meteorSmall.run_action(A.sequence(actions))
                                                                self.items.append(meteorSmall)
                                                                meteor.remove_from_parent()
                                                            m = SpriteNode(smokeTextureSmall, parent=self)
                                                            m.position = meteor.position
                                                            m.run_action(A.sequence(A.fade_to(0, 0.3, TIMING_EASE_OUT), A.remove()))
                                                            meteor.destroyed = True
                                                            meteor.remove_from_parent()
                                                            """
                                                        else:
                                            #                sound.play_effect('arcade:Hit_1')
                                                            if meteor.livesRemaining <= (10 * self.stageNumber) * 0.25:
                                                                meteor.alpha = 0.25
                                                            elif meteor.livesRemaining <= (10 * self.stageNumber) * 0.50:
                                                                meteor.alpha = 0.5
                                                            elif meteor.livesRemaining <= (10 * self.stageNumber) * 0.75:
                                                                meteor.alpha = 0.75
                                                    
                                                if isinstance(meteor, MeteorSmall):
                                        #            sound.play_effect('arcade:Explosion_7')
                                                    if random.random() < 0.15 * self.stageNumber:
                                                        if self.stageNumber < 5:
                                                            meteor.livesRemaining -= 250
                                                        else:
                                                            if activeWeapon == 'laser':
                                                                meteor.livesRemaining -= 9
                                                            meteor.livesRemaining -= 1
                                                        if meteor.livesRemaining <= 0:
                                        ############################################################################################
                                        ############################################################################################
                                                            m = SpriteNode(smokeTextureSmall, parent=self)
                                                            m.position = meteor.position
                                                            m.run_action(A.sequence(A.fade_to(0, 0.3, TIMING_EASE_OUT), A.remove()))
                                                            meteor.destroyed = True
                                                            meteor.remove_from_parent()
                                                            ## REMOVED BELOW BLOCK TO REDUCE STUTTERING FROM MANY OBJECTS ON SCREEN
                                                            """
                                                            for i in range(2):
                                                                meteorTiny = MeteorTiny(parent=self)
                                                                meteorTiny.destroyed = False
                                                                meteorTiny.livesRemaining = 10 * self.stageNumber
                                                                meteorTiny.position = meteor.position + (random.uniform(-100, 25), random.uniform(-5, 5))
                                                                meteorTiny.scale = 0.5
                                                                downwardAngle = random.uniform(pi, pi*2)
                                                                dx, dy = cos(downwardAngle) * 80, sin(downwardAngle) * 80
                                                                d = random.uniform(6.0, 10.0)
                                                                actions = [A.move_to(random.uniform(0, self.size.w), -25, d), A.remove()]
                                                                meteorTiny.run_action(A.sequence(actions))
                                                                self.items.append(meteorTiny)
                                                            m = SpriteNode(smokeTextureSmall, parent=self)
                                                            m.position = meteor.position
                                                            m.run_action(A.sequence(A.fade_to(0, 0.3, TIMING_EASE_OUT), A.remove()))
                                                            meteor.destroyed = True
                                                            meteor.remove_from_parent()
                                                            """
                                                        else:
                                            #                sound.play_effect('arcade:Hit_1')
                                                            if meteor.livesRemaining <= (10 * self.stageNumber) * 0.25:
                                                                meteor.alpha = 0.25
                                                            elif meteor.livesRemaining <= (10 * self.stageNumber) * 0.50:
                                                                meteor.alpha = 0.5
                                                            elif meteor.livesRemaining <= (10 * self.stageNumber) * 0.75:
                                                                meteor.alpha = 0.75
                                                    
                                                if isinstance(meteor, MeteorTiny):
                                                    if self.stageNumber < 5:
                                                        meteor.livesRemaining -= 50
                                                    else:
                                                        if activeWeapon == 'laser':
                                                            meteor.livesRemaining -= 9
                                                        meteor.livesRemaining -= 1
                                                    if meteor.livesRemaining <= 0:
                                                        meteor.destroyed = True
                                                        meteor.remove_from_parent()
                                                    #self.items.remove(meteor)
                                                
                                                if isinstance(meteor, Meteor3):
                                                    if self.stageNumber < 5:
                                                        meteor.livesRemaining -= 50
                                                    else:
                                                        if activeWeapon == 'laser':
                                                            meteor.livesRemaining -= 9
                                                        meteor.livesRemaining -= 1
                                                    if meteor.livesRemaining <= 0:
                                        #                sound.play_effect('arcade:Explosion_2')
                                                        m = SpriteNode(explosionTexture, scale = 0.25, parent=self)
                                                        m.position = meteor.position
                                                        m.run_action(A.sequence(A.fade_to(0, 0.3, TIMING_EASE_OUT), A.remove()))
                                                        meteor.destroyed = True
                                                        meteor.remove_from_parent()
                                                    else:
                                        #                sound.play_effect('arcade:Hit_1')
                                                        if meteor.livesRemaining <= (10 * self.stageNumber) * 0.25:
                                                            meteor.alpha = 0.25
                                                        elif meteor.livesRemaining <= (10 * self.stageNumber) * 0.50:
                                                            meteor.alpha = 0.5
                                                        elif meteor.livesRemaining <= (10 * self.stageNumber) * 0.75:
                                                            meteor.alpha = 0.75
                                                
                                                if isinstance(meteor, Meteor4):
                                                    if self.stageNumber < 5:
                                                        meteor.livesRemaining -= 100
                                                    else:
                                                        if activeWeapon == 'laser':
                                                            meteor.livesRemaining -= 9
                                                        meteor.livesRemaining -= 1
                                                    if meteor.livesRemaining <= 0:
                                        #                sound.play_effect('arcade:Explosion_2')
                                                        m = SpriteNode(explosionTexture, scale = 0.50, parent=self)
                                                        m.position = meteor.position
                                                        m.run_action(A.sequence(A.fade_to(0, 0.3, TIMING_EASE_OUT), A.remove()))
                                                        meteor.destroyed = True
                                                        meteor.remove_from_parent()
                                                    else:
                                        #                sound.play_effect('arcade:Hit_1')
                                                        if meteor.livesRemaining <= (20 * self.stageNumber) * 0.25:
                                                            meteor.alpha = 0.25
                                                        elif meteor.livesRemaining <= (20 * self.stageNumber) * 0.50:
                                                            meteor.alpha = 0.5
                                                        elif meteor.livesRemaining <= (20 * self.stageNumber) * 0.75:
                                                            meteor.alpha = 0.75
                                                
                                                if isinstance(meteor, Meteor5):
                                                    if self.stageNumber < 5:
                                                        meteor.livesRemaining -= 250
                                                    else:
                                                        if activeWeapon == 'laser':
                                                            meteor.livesRemaining -= 9
                                                        meteor.livesRemaining -= 1
                                                    if meteor.livesRemaining <= 0:
                                        #                sound.play_effect('arcade:Explosion_2')
                                                        m = SpriteNode(explosionTexture, scale = 0.75, parent=self)
                                                        m.position = meteor.position
                                                        m.run_action(A.sequence(A.fade_to(0, 0.3, TIMING_EASE_OUT), A.remove()))
                                                        meteor.destroyed = True
                                                        meteor.remove_from_parent()
                                                    else:
                                        #                sound.play_effect('arcade:Hit_1')
                                                        if meteor.livesRemaining <= (40 * self.stageNumber) * 0.25:
                                                            meteor.alpha = 0.25
                                                        elif meteor.livesRemaining <= (40 * self.stageNumber) * 0.50:
                                                            meteor.alpha = 0.5
                                                        elif meteor.livesRemaining <= (40 * self.stageNumber) * 0.75:
                                                            meteor.alpha = 0.75
                                                
                                                if isinstance(meteor, Meteor6):
                                                    if self.stageNumber < 5:
                                                        meteor.livesRemaining -= 250
                                                    else:
                                                        if activeWeapon == 'laser':
                                                            meteor.livesRemaining -= 9
                                                        meteor.livesRemaining -= 1
                                                    if meteor.livesRemaining <= 0:
                                        #                sound.play_effect('arcade:Explosion_2')
                                                        m = SpriteNode(explosionTexture, scale = 1.00, parent=self)
                                                        m.position = meteor.position
                                                        m.run_action(A.sequence(A.fade_to(0, 0.3, TIMING_EASE_OUT), A.remove()))
                                                        meteor.destroyed = True
                                                        meteor.remove_from_parent()
                                                    else:
                                        #                sound.play_effect('arcade:Hit_1')
                                                        if meteor.livesRemaining <= (80 * self.stageNumber) * 0.25:
                                                            meteor.alpha = 0.25
                                                        elif meteor.livesRemaining <= (80 * self.stageNumber) * 0.50:
                                                            meteor.alpha = 0.5
                                                        elif meteor.livesRemaining <= (80 * self.stageNumber) * 0.75:
                                                            meteor.alpha = 0.75
                                                
                                        #classmethod Action.move_to(x, y[, duration, timing_mode])
                                        #Creates an action that moves a node to a new position.
                                        
                                                    #actions = [A.move_to(self.ship.position[0], self.ship.position[1], d, TIMING_LINEAR), A.remove()]
                                                    #actions = [A.move_by(0, -(self.size.h + 30), d), A.remove()]
                                                    #meteor.run_action(A.sequence(actions))
                                                
                                                
                                                if isRocket == 1:
                                                    fragmentsToCreate = round(self.rocketFragmentsLevel)
                                                    fragmentMovementSpeed = 7.5 - (self.rocketFragmentsLevel * 0.1)
                                                    if fragmentMovementSpeed <= 1.0:
                                                        fragmentMovementSpeed = 1.0
                                                    #for i in range(fragmentsToCreate):
                                                    for i in range(fragmentsToCreate):
                                                        miniRocket = MiniRocket(parent=self)
                                                        miniRocket.collisionsLeft = self.rocketFragmentPenetrationLevel
                                                        miniRocket.scale = 0.25
                                                        miniRocket.color = '#07ff1c'
                                                        miniRocket.z_position = 0
                                                        #miniRocket.alpha = 0.25
                                                        miniRocket.position = meteor.position
                                                        #miniRocket.position = meteor.position + (random.uniform(-30, 30), (-20 + random.uniform(-5, 5)))
                                                        fullCircle = random.uniform(0, pi*2)
                                                        #upwardAngle = random.uniform(0, pi)
                                                        dx, dy = cos(fullCircle) * 250, sin(fullCircle) * 250
                                                        #d = 1.25
                                                        #miniRocket.rotation = fullCircle
                                                        actions = (A.sequence(A.move_by(dx, dy, fragmentMovementSpeed, TIMING_EASE_IN), A.remove()))
                                                        #actions2 = (A.sequence(A.fade_to(1.0, fragmentMovementSpeed, TIMING_EASE_IN), A.remove()))
                                                        miniRocket.run_action(A.group((actions)))
                                                        #, (actions2)))
                                                        self.projectiles.append(miniRocket)
                                        
                                        

                                        Here is my function that handles the creation of projectile objects (lasers, rockets, etc) that gets called if the user is touching the screen. (Depending on the upgrade level for the weapons fire rate, it may get called more often, I’ll post that code as well.)


                                        def fireWeapon(self, yOffset=None, customXOffset=None):
                                                listOfColors = ['#F00', '#F0F', '#00F', '#FF0', '#0FF']
                                                projectileCounter = 2
                                                xOffset = 0
                                                if yOffset != None:
                                                    yOffset = yOffset
                                                else:
                                                    yOffset = 10
                                                if customXOffset != None:
                                                    customXOffset = customXOffset
                                                else:
                                                    customXOffset = 0
                                                if self.activeWeapon == 'laser':
                                                    self.numberOfProjectileLevel = self.laserNumberOfLasersLevel
                                        
                                                elif self.activeWeapon == 'rocket':
                                                    self.numberOfProjectileLevel = 1
                                                    if len(self.listOfProjectiles) > 1:
                                                        self.listOfProjectiles.clear()
                                                    
                                                while self.numberOfProjectileLevel > len(self.listOfProjectiles):
                                                    self.listOfProjectiles.append(f'fire{projectileCounter}')
                                                    projectileCounter += 1
                                                    self.listOfProjectilesX.append(self.listOfXOffsets.pop(0))
                                                    
                                                xInProjectileCounter = len(self.listOfProjectiles) - 1
                                                
                                                if self.activeWeapon == 'rocket':
                                                    playSound = sound.play_effect('game:Woosh_1')
                                                    actions = [A.move_by(0, self.size.height * 0.75, 1.25 * self.speed), playSound, A.remove()]
                                                    #x = Rocket(parent=self)
                                                elif self.activeWeapon == 'laser':
                                                    playSound = sound.play_effect('arcade:Laser_6')
                                                    actions = [A.move_by(0, self.size.h/2 + (5*self.laserPowerLevel), 0.25 * self.speed), playSound, A.remove()]
                                                    #x = SpriteNode('spc:LaserBlue8', parent=self, y_scale = 0.10, x_scale = 0.25)
                                                
                                                for x in self.listOfProjectiles:
                                                    xOffset = self.listOfProjectilesX[xInProjectileCounter]
                                                    if self.activeWeapon == 'rocket':
                                                        x = Rocket(parent=self)
                                                    elif self.activeWeapon == 'laser':
                                                        x = Laser(parent=self)
                                                        x.z_position = 1
                                                        x.collisionsLeft = self.laserPowerLevel
                                                        #x = SpriteNode('spc:LaserBlue8', parent=self, y_scale = 0.10, x_scale = 0.25)
                                                    
                                                    """
                                                    if self.activeWeapon == 'rocket':
                                                        playSound = sound.play_effect('game:Woosh_1')
                                                        actions = [A.move_by(0, self.size.height / 2.0, 1.25 * self.speed), playSound, A.remove()]
                                                        x = Rocket(parent=self)
                                                        #x.scale = 0.025
                                                    """
                                                    """
                                                    if self.activeWeapon == 'rocket':
                                                        playSound = sound.play_effect('game:Woosh_1')
                                                        actions = [A.move_by(0, self.size.h, 1.50 * self.speed), playSound, A.remove()]
                                                        x = Rocket(parent=self)
                                                        x.scale = 0.025
                                                    """
                                                    """
                                                    if self.activeWeapon == 'laser':
                                                        playSound = sound.play_effect('arcade:Laser_6')
                                                        actions = [A.move_by(0, self.size.h/2, 0.75 * self.speed), playSound, A.remove()]
                                                        x = SpriteNode('spc:LaserBlue8', parent=self, y_scale = 0.10, x_scale = 0.25)
                                                    """
                                        
                                                        
                                                    x.position = self.ship.position + (xOffset + customXOffset, yOffset)
                                                    xInProjectileCounter -= 1
                                                    x.run_action(A.sequence(actions))
                                                    self.projectiles.append(x)
                                                    #print(f'x.collisionsLeft: {x.collisionsLeft}')
                                                    if self.numberOfProjectileLevel >= 5:
                                                        x.color = listOfColors[0]
                                                    if self.numberOfProjectileLevel >= 10:
                                                        x.color = listOfColors[1]
                                                    if self.numberOfProjectileLevel >= 15:
                                                        x.color = listOfColors[2]
                                                    if self.numberOfProjectileLevel >= 20:
                                                        x.color = listOfColors[3]
                                                    if self.numberOfProjectileLevel >= 25:
                                                        x.color = listOfColors[4]
                                                    if self.numberOfProjectileLevel >= 30:
                                                        x.color = 'white'
                                        

                                        Here is what gets called as long as the number of touches is >= 1.
                                        This is what calls the function I pasted above.


                                        
                                        def shouldFire(self):
                                                if self.game_over == False:
                                            
                                                    if self.activeWeapon == 'laser':
                                                        self.defaultWeaponTimer = self.laserFireRateLevel
                                                    elif self.activeWeapon == 'rocket':
                                                        self.defaultWeaponTimer = self.rocketFireRateLevel
                                                    elif self.activeWeapon == 'ropeLaser':
                                                        self.defaultWeaponTimer = self.ropeLaserFireRateLevel
                                                    if self.defaultWeaponTimer < 20:
                                                        self.maxSpeedLevel = 0
                                                    if self.defaultWeaponTimer >= 20 < 30:
                                                        self.maxSpeedLevel = 1
                                                        #self.defaultWeaponTimer = 0
                                                    if self.defaultWeaponTimer >= 30 < 40:
                                                        self.maxSpeedLevel = 2
                                                        #self.defaultWeaponTimer = 0
                                                    if self.defaultWeaponTimer >= 40 < 50:
                                                        self.maxSpeedLevel = 3
                                                        #self.defaultWeaponTimer = 0
                                                    if self.defaultWeaponTimer >= 50 < 60:
                                                        self.maxSpeedLevel = 4
                                                    if self.defaultWeaponTimer >= 60 < 70:
                                                        self.maxSpeedLevel = 5
                                                    if self.defaultWeaponTimer >= 70 < 80:
                                                        self.maxSpeedLevel = 6
                                                    if self.defaultWeaponTimer >= 80:
                                                        self.maxSpeedLevel = 7
                                                    #print(f'self.defaultWeaponTimer: {self.defaultWeaponTimer}')
                                                    #print(f'self.maxSpeedLevel: {self.maxSpeedLevel}')
                                                    if self.weaponTimer >= 100:
                                                        if self.maxSpeedLevel == 0:
                                                            self.fireWeapon()
                                                        elif self.maxSpeedLevel == 1:
                                                            self.fireWeapon(20, -5)
                                                            self.fireWeapon(20, 5)
                                                        elif self.maxSpeedLevel == 2:
                                                            self.fireWeapon(20, -5)
                                                            self.fireWeapon(20, 5)
                                                            self.fireWeapon()
                                                        elif self.maxSpeedLevel == 3:
                                                            self.fireWeapon(30, -10)
                                                            self.fireWeapon(30, 10)
                                                            self.fireWeapon(20, -5)
                                                            self.fireWeapon(20, 5)
                                                        elif self.maxSpeedLevel == 4:
                                                            self.fireWeapon(30, -10)
                                                            self.fireWeapon(30, 10)
                                                            self.fireWeapon(20, -5)
                                                            self.fireWeapon(20, 5)
                                                            self.fireWeapon()
                                                        elif self.maxSpeedLevel == 5:
                                                            self.fireWeapon(40, -15)
                                                            self.fireWeapon(40, 15)
                                                            self.fireWeapon(30, -10)
                                                            self.fireWeapon(30, 10)
                                                            self.fireWeapon(20, -5)
                                                            self.fireWeapon(20, 5)
                                                        elif self.maxSpeedLevel == 6:
                                                            self.fireWeapon(40, -15)
                                                            self.fireWeapon(40, 15)
                                                            self.fireWeapon(30, -10)
                                                            self.fireWeapon(30, 10)
                                                            self.fireWeapon(20, -5)
                                                            self.fireWeapon(20, 5)
                                                            self.fireWeapon()
                                                        elif self.maxSpeedLevel == 7:
                                                            self.fireWeapon(50, -20)
                                                            self.fireWeapon(50, 20)
                                                            self.fireWeapon(40, -15)
                                                            self.fireWeapon(40, 15)
                                                            self.fireWeapon(30, -10)
                                                            self.fireWeapon(30, 10)
                                                            self.fireWeapon(20, -5)
                                                            self.fireWeapon(20, 5)
                                                            
                                                        self.weaponTimer = self.defaultWeaponTimer
                                                    self.weaponTimer += 5
                                        
                                        

                                        I tried to include as much code as possible, but I did not clean it up, so excuse the mess and the excessive block comments. I was trying to troubleshoot, etc.

                                        Here is my question:
                                        How should I go about reducing the stutter caused by creating my MiniRockets?
                                        In general, is there a more efficient way to create many objects? In such a way that stuttering is minimal?
                                        I create up to 50 or so of the MiniRocket objects depending on the upgrade level. But these are created >3 times per second in some cases. So 150+ objects created and tracked.
                                        I reduced stuttering quite a bit by commenting out the MeteorSmall and MeteorTiny creations, which were originally set to create 3. The version above only creates 2 of the MeteorMedium objects to reduce stutter.
                                        Any help is appreciated! I hope that including this much code helps.
                                        Also, for people working on a similar game:
                                        Feel free to reuse anything that may be useful!

                                        1 Reply Last reply Reply Quote 0
                                        • JonB
                                          JonB last edited by

                                          How do you create your projectiles? By a call to SpriteNode? Or do you cache the objects?

                                          Maybe figure out what the max number of projectiles of each type can be on the screen at one time -- then create all of the sprites during setup. You would then have a list of onscreen and non-active rockets -- when you need to spawn 50 rockets, you pop 50 from the non active list, add to the scene, and append to the onscreen list. When each rocket is destroyed or goes off screen, you pop from the onscreen list, and add to the non-active list, ready to be reused. Just reset the position each time.
                                          If you ever run out of non-active rockets you can create new ones and append to the list, but just never destroy any.

                                          1 Reply Last reply Reply Quote 0
                                          • Robert_Tompkins
                                            Robert_Tompkins last edited by

                                            @Robert_Tompkins said:

                                            if isRocket == 1:
                                            fragmentsToCreate = round(self.rocketFragmentsLevel)
                                            fragmentMovementSpeed = 7.5 - (self.rocketFragmentsLevel * 0.1)
                                            if fragmentMovementSpeed <= 1.0:
                                            fragmentMovementSpeed = 1.0

                                            ^^ This is where the MiniRockets are created currently. ^^
                                            You can find the rest of the code above if you need it.
                                            But ‘self.rocketFragmentsLevel’ currently has no limit, though I can cap it at 99 or 100.
                                            The hard part will be calculating the max number on screen. ..unless I modify my save file to max out my levels, and add code to count the number on screen.. yea I’ll do that.

                                            I do create these outside of the Scene:

                                            class Rocket (SpriteNode):
                                                def __init__(self, **kwargs):
                                                    if path.exists('rocket.png'):
                                                        img = 'rocket.png'
                                                        SpriteNode.__init__(self, img, scale = 0.05, **kwargs)
                                                    else:
                                                        img = 'spc:PlayerLife3Blue'
                                                        SpriteNode.__init__(self, img, scale = 0.15, **kwargs)
                                            
                                            class MiniRocket (SpriteNode):
                                                def __init__(self, **kwargs):
                                                    img = 'plf:LaserPurpleDot'
                                                    SpriteNode.__init__(self, img, scale = 0.05, **kwargs) 
                                            

                                            I will try out what you mentioned, that sounds like it should do the trick!
                                            If that works well, I will do the same thing with the MeteorTiny creations as well.
                                            Thanks, @JonB
                                            This may take me a few days knowing me, but I’ll reply back with results!

                                            1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post
                                            Powered by NodeBB Forums | Contributors