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!