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
How to Specify "Nullable" Return Type with Type Hints
Plot a Bar Using Matplotlib Using a Dictionary
Python Matplotlib Framework Under MACosx
Determine Complete Django Url Configuration
What Is Sys.Maxint in Python 3
Why Does @Foo.Setter in Python Not Work for Me
Best Way to Determine If a Sequence Is in Another Sequence
How to Integrate Flask & Scrapy
What's 0Xff for in Cv2.Waitkey(1)
Get All Object Attributes in Python
How to Schedule a Function to Run Every Hour on Flask
How to Call Python Code from C Code
Find Usa Phone Numbers in Python Script
How to Find Tags with Only Certain Attributes - Beautifulsoup
Why Does Python's Multiprocessing Module Import _Main_ When Starting a New Process on Windows