Trying to Delay a Specific Function for Spawning Enemy After a Certain Amount of Time

Trying to delay a specific function for spawning enemy after a certain amount of time

In pygame exists a timer event. Use pygame.time.set_timer() to repeatedly create a USEREVENT in the event queue.. The time has to be set in milliseconds:

pygame.time.set_timer(pygame.USEREVENT, 1000) # 1 second

Note, in pygame customer events can be defined. Each event needs a unique id. The ids for the user events have to be between pygame.USEREVENT (24) and pygame.NUMEVENTS (32). In this case the value of pygame.USEREVENT is the event id for the timer event.

Receive the event in the event loop:

running = True
while run:

for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False

elif event.type == pygame.USEREVENT:
# [...]

The timer event can be stopped by passing 0 to the time argument of pygame.time.set_timer.

See also Spawning multiple instances of the same object concurrently in python.


Create a list of moles and add a random position to the list in mole_spawn_easy:

moles = []

def mole_spawn_easy():
molex = random.randint(50, 950)
moley = random.randint(450, 682)
moles.append((molex, moley))

Draw the moles in the main application loop:

while run:
# [...]

for pos in moles:
screen.blit(mole, pos)

See the example:

moles = []

def mole_spawn_easy():
molex = random.randint(50, 950)
moley = random.randint(450, 682)
moles.append((molex, moley))

pygame.time.set_timer(pygame.USEREVENT, 1000)

while run:

ax, ay = pygame.mouse.get_pos()

for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False

if event.type == pygame.USEREVENT:
counter -= 1
text = ("Time Left: " + str(counter)).rjust(3)
if counter > 0:
mole_spawn_easy()
else:
print("game over")

screen.blit(background, [0,0])

for pos in moles:
screen.blit(mole, pos)
screen.blit(aim, ((ax - 32 ),(ay - 32)))
screen.blit(font.render(text, True, (0, 0, 0)), (32, 48))

pygame.display.flip()
clock.tick(FPS)

I am trying to create an instance of a class every time 5 seconds pass in pygame, but instead it creates a ton of instances instead of 1

" I think it has something to do with the ticks,"

Yes of course. The condition if passed_time%5 == 0: is fulfilled for a second.

Set a time point when the first enemy has t o spawn. If the time point has exceeded, then spawn an enemy and set the next time point:

variable_name = 0
next_enemy_time = 0

running = True
while running:

if pg.time.get_ticks() > next_enemy_time:
next_enemy_time += 5000

variable_name += 1
speed += 0.01
make_enemy('enemy' + str(variable_name), random.choice(colors), 30, 30, random.randint(50, 100))

Alternatively you can use a timer event. Use pygame.time.set_timer() to repeatedly create an USEREVENT. e.g.:

milliseconds_delay = 5000 # 0.5 seconds
enemy_event = pygame.USEREVENT + 1
pygame.time.set_timer(enemy_event, milliseconds_delay)

Note, in pygame customer events can be defined. Each event needs a unique id. The ids for the user events have to be between pygame.USEREVENT (24) and pygame.NUMEVENTS (32). In this case pygame.USEREVENT+1 is the event id for the timer event, which spawns the enemies.

Create a new enemy when the event occurs in the event loop:

milliseconds_delay = 5000 # 0.5 seconds
enemy_event = pygame.USEREVENT + 1
pygame.time.set_timer(enemy_event, milliseconds_delay)
variable_name = 0

running = True
while running:

clock.tick(FPS)

for event in pygame.event.get():
if event.type == pg.QUIT:
running = False

elif event.type == enemy_event:
variable_name += 1
speed += 0.01
make_enemy('enemy' + str(variable_name), random.choice(colors), 30, 30, random.randint(50, 100))

# [...]

Waiting a specific amount of seconds to spawn an enemy

First let's take a look at Time.deltaTime:

The time in seconds it took to complete the last frame (Read Only).

Use this function to make your game frame rate independent.

In Unity whenever you need to formulate a time related scenario you have to use co-routines or else it's all up to your code logic to measure delta time.

In a simple coroutine we have the logic wrapped in a loop and we have a yield statement which value indicates the amount of time (frames) the coroutine should wait after each pass.

Update and FixedUpdate methods are Unity built-in coroutines which are being executed every Time.deltaTime and Time.fixedDeltaTime seconds respectively.

The logic of your spawning must have a coroutine in order to work since ExecuteWaveAction must use delta time.

int numberOfSpawns = spawnLocations.Length;
int currentSpawnToInstantiate = 0;
float timeLeftToSpawnEnemy = 0f;
WaveAction action = set this in advance;

void Start()
{
action = ...
ExecuteWaveAction();
}

void ExecuteWaveAction()
{
numberOfSpawns = spawnLocations.Length;
currentSpawnToInstantiate = 0;
timeLeftToSpawnEnemy = 0f;
i = 0;
}

Now look how the looped logic is being separated from the original method and being put in a coroutine:

int i = 0;
void Update()
{
//if spawning is active then continue...
if (i < action.quantityOfEnemysToSpawn)
{
if (timeLeftToSpawnEnemy < action.spawnRate)
{
timeLeftToSpawnEnemy += Time.deltaTime;
}
else
{
i++;

GameObject.Instantiate (action.enemyToSpawn, spawnLocations [currentSpawnToInstantiate].position, Quaternion.identity);
currentSpawnToInstantiate++;

timeLeftToSpawnEnemy = 0f;

if (currentSpawnToInstantiate >= numberOfSpawns)
currentSpawnToInstantiate = 0;
}
}
}

Note:

You may wonder why can't we use Time.deltaTime in a loop?

Answer is: because Time.deltaTime represents the time length of one frame (which is basically equal to the delay between each two consecutive executions of the same Update method) but a simple loop runs all at once in one frame and thus Time.deltaTime only tells us some other delay which our loop isn't working upon it.

In other words Unity does not wait inside a loop for a frame to end, but it waits after the execution of each coroutine (waiting time = frame length (i.e. Time.deltaTime or Time.fixedDeltaTime or manual coroutine waiting time).

Problems with time and the appearance of enemies (Python, Pygame)

But I get an error "'Game' object has no attribute 'seconds'"

Yes of course, because self.seconds is defined in the method time rather than the constructor of the class Game. When new is invoked the 1st time, then the attribute self.seconds doe not exist, because time hasn't been invoked before.

Anyway you approach won't work because, the condition if self.seconds >= 10: si fulfilled continuously and will cause that self.mobY is incremented many times.


Use pg.time.get_ticks() to the number of milliseconds since pygame.init() was called.

Define the time when the first MobY has to be created self.next_mob_time. when the time point has exceeded, then create an instance of MobY and increment the time by 10000.

current_time = pg.time.get_ticks()
if current_time > self.next_mob_time:
self.next_mob_time += 10000
self.mobY += 1

Invoke new in time. Furthermore the groups have to be created in the constructor of Game rather than in new:

class Game:
def __init__(self):
pg.init()
self.screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption(TITLE)
self.clock = pg.time.Clock()
pg.key.set_repeat(1, 15)
self.frame_count = 0
self.mobY = 0
self.next_mob_time = 0
self.all_sprites = pg.sprite.Group()
self.mobs = pg.sprite.Group()

def new(self):
current_time = pg.time.get_ticks()
if current_time > self.next_mob_time:
self.next_mob_time += 10000
self.mobY += 1
m = MobY()
self.all_sprites.add(m)
self.mobs.add(m)

# [...]

def time(self):
self.new()
self.seconds = pg.time.get_ticks() // 1000

# [...]

Changing delay spawn at runtime [C#]

You might use a single enemy-spawning coroutine instead of launching each coroutine for each enemy you want to spawn. This way you can easily control when to spawn an enemy and when to delay its spawning:

public class EnemySpawnScript : MonoBehaviour {

public void enemySpawn (GameObject player, LevelData level){
StartCoroutine(InstantiateObjects(player, level.enemyDataSpawner));
}

IEnumerator InstantiateObjects (GameObject player, IEnumerable<EnemyDataSpawn> enemyDataSpawnList){

foreach(var enemyDataSpawn in enemyDataSpawnList){

while( /* too many enemies of enemyDataSpawn type */ ) {
yield return new WaitForSeconds(enemyDataSpawn.delay);
}
// instantiate enemyDataSpawn
}

}
}

Stop SKAction after a certain amount of time

You are removing the wrong action. The action with key "enemy1" is:

SKAction.sequence([delay, actionMove, actionRemove])

which simply animates the enemy. Removing this only stops this sequence of actions temporarily. When addEnemy1 is called again, the sequence of action is added again.

The action that spawns the enemy is the action that you run on self here:

    run(SKAction.repeatForever(
SKAction.sequence([SKAction.run() { [weak self] in
self?.addEnemy1()
},
SKAction.wait(forDuration: 5.0)])))

Yo should give a key to that action instead:

...
},
SKAction.wait(forDuration: 5.0)])), withKey: "spawnEnemy")

and then remove that from self:

removeAction(forKey: "spawnEnemy")


Related Topics



Leave a reply



Submit