Best way to use timer to wait - for regeneration, firing bullets, patrolling,etc

Hi,



I am trying to use timer in a way that if I want to wait 30 seconds to regenerate ammo and medical packs or want to space enemy fire to half second per bullet (instead of firing 20 bullets in two seconds) or when enemy guards patrol from point A to B then wait for 2-3 seconds and then patrol from B to C. Initially I tried to create a separate timer and then use its wait(long time) - timer.wait(5) (snippet below). But this slows down the program down. I cannot use normal timer(in BaseGame) i.e. timer as it is used in update method and so I cannot stop it/use wait/send it to sleep for 5 seconds…  What is the best way to wait a few seconds before executing something??




float regenerationTime = updateTimer.getTimeInSeconds() + 5;
                        try
                        {
                            updateTimer.wait(5);
                               
                            float currentTime = updateTimer.getTimeInSeconds();
                            System.out.println(regenerationTime);
                            System.out.println(currentTime);
       
                            if((currentTime - regenerationTime) >= 0)
                            {
                                regenerateAmmo();
                            }
               
                        }
               
                        catch(java.lang.InterruptedException e)
                        {
                            System.err.println(e);       
                        }
       
                        scene.updateGeometricState(0f, true);
                        scene.updateRenderState();



Thanks,

Dev

One very simple way of doing it without using threads is to keep a queue (or priority queue or something) with end-time for several events, and simply execute them in the update method whenever you absolute time (which you can get with the timer or tpf) exceeds the value of the events in the queue… This way, you won't have to block until something happens.



The other one, is using timer exactly in the same way you do, but do that in a separate thread, the problem with that, is that if you need to execute something after the wait, it will have to somehow be injected in the main (GL) thread. Perhaps using StandardGame would help you?

Gracias senoir duenez… unfortunately none of the solutions presented are feasible





These events, firing a bullet a half second, regeneration of packs, etc are interlinked to other events e.g. regeneration of health and ammo supplies occurs when they are taken by a player or bot(as shown in code below)… and its not certain when they will occur so end time cannot be specified. I am looking for a simple way to say - on collision save that time, once current time equals 5 seconds plus time of collision regenerate()… But the obvious problem is if I do "float regenerationTime = timer.getTimeInSeconds() + 5;" - the regeneration time will always be 5 seconds greater than "float currentTime = timer.getTimerInSeconds();"… However, on your idea of absolute time I did find a solution for some of these listed problems - regeneration problem can be sorted if on collision timer can be reset to 0 and after 5 seconds the thing can regenerate(see the code below). But the only problem I am having is on reset the timer it does not restart?? The loop of currentTime  >= 5 is not executed?? I dont understand why is this?




ArrayList<Spatial> children = n1.getChildren();
            ArrayList<Spatial> toKill = new ArrayList<Spatial>();
           
            if( ammo <= 12 ) //less than or equal
            {
                for( Spatial s : children )
                {
               
                    if( s.hasCollision(player1, false) )
                    {           
                        ammoCollectedSound.play();
                        toKill.add( s );
                        System.out.println("Collision Detected with the box");
                        ammo = ammo + 10;
                        System.out.print("Player Ammunition: " + ammo);

                        try
                        {
                            timer.reset();
                            float currentTime = timer.getTimeInSeconds();
                            System.out.println(currentTime);
       
                            if(currentTime >= 5)
                            {
                                regenerateAmmo();
                            }
                        }
                        catch(Exception e)
                        {
                            System.err.println(e);
                        }
                       
       
                        scene.updateGeometricState(0f, true);
                        scene.updateRenderState();
                    }
                }
               
                for( Spatial s : toKill )
                s.removeFromParent();
                toKill.clear();



Unfortunately I cannot switch to StandardGame as my game is at an advanced stage and I do not have time to study the StandardGame or switch the code...
hawk2k8 said:

I cannot use normal timer(in BaseGame) i.e. timer as it is used in update method and so I cannot stop it/use wait/send it to sleep for 5 seconds.. 


I am not sure if i understand your problem, but with the following method you can use the existing timer there is no need to reset or wait for anything.
I usually do it like this:


// time of last update
private float lastUpdate = 0;
// spawn rate in seconds
private float updateRate = 5f;
    public void update(float tpf) {
        if (lastUpdate + spawnRate < Timer.getTimer().getTimeInSeconds()) {
            // do something
            
            lastUpdate= Timer.getTimer().getTimeInSeconds();
        }
    }



The only problem that can appear, is that if your Game runs for 10781329274479959411466260781329 years .....  ;)

Nice… and simple(dont forget)  XD …  just what I was looking for!! however one small glitch here similar to which I was referring to above…



have you noticed that the Timer.getTimer().getTimerInSeconds() for some reason does not start at 0 - e.g. it is always 13 seconds or 15 seconds or more rendering the initial precondition useless… Also for some reason, after second time it decides not to work… I tried it with all the children of the node… Hence if we insert a reset so it starts from 0 or in someway on collision we make the timer start from 0 the code should work perfectly at least for the first problem…




/ time of last update
private float lastUpdate = 0;
// spawn rate in seconds
private float updateRate = 5f;
public void update(float tpf)
{
 if(collision)
{
        Timer.getTimer().reset();   //so it starts from 0 making the loop condition worthwhile
        if (lastUpdate + spawnRate < Timer.getTimer().getTimeInSeconds())
       {
            // do something
            
            lastUpdate= Timer.getTimer().getTimeInSeconds();
        }
    }



IMO you should really use Controllers for what you want to accomplish. A Controller gets the "time since last frame" as a parameter to it's update method, you can easily accumulate that to check how much time has passed since the controller has been activated. It should be very straightforward to implement, and comes with the additional bonus of tidying up your software model.

I see two seperate models here, one being a business, the other being a graphics rule.



The first rule, being respawn, is a business rule and should be kept out of the scenegraph. I would use a seperate thread around a queue system like previously mentioned. Then use a queue within the scenegraph where concurrent actions can be placed. I would place this as a business rule as if this was to be multiplayer, the respawn would be initiated by the server.



The firing of bullets and chaining events can be accomplished using controllers as this is a graphics only rule. On a collision hit you may want to post the event to the business rule as this may warrant damage calculation.

@ hevee - I am already using controllers for the bullets. Its fine if I try and fire bullets - keybinding manager - but how do I automate that for enemy/bots? If I use the method without keybinding manager then 20 bullets fire in about 2 secs(method can be seem below)… How do I space them??




private void shootAtPlayer()
    {
        if(ammoGuard >0)
        {
           
                //"bullet" + increment++ - it is used to keep track of number of bullets
                //fired - the title for every bullet will increment with next fired e.g. bullet1, bullet2, etc
                bulletGuard = new Box("bullet from Guard" + increment++, new Vector3f(), 0.07f, 0.07f, 0.15f);
                bulletGuard.setModelBound(new BoundingBox());
                bulletGuard.updateModelBound();         
                bulletGuard.setLocalTranslation(new Vector3f(guardNode.getLocalTranslation()));
                bulletMaterial = display.getRenderer().createMaterialState();
                bulletMaterial.setEmissive(ColorRGBA.blue);
                bulletMaterial.setShininess(5);
                bulletGuard.setRenderState(bulletMaterial);
                bulletGuard.updateGeometricState(0, true);
                scene.attachChild(bulletGuard);
                bulletGuard.updateRenderState();  
                bulletGuard.addController(new BulletMoverGuard(bulletGuard, new Vector3f(guardNode.getLocalRotation().getRotationColumn(2)))); //camNode.getCamera().getDirection())                                           
                gunFireSound.setWorldPosition(guardNode.getLocalTranslation());
                gunFireSound.play();   
                ammoGuard = ammoGuard-1;   
                scene.updateGeometricState(0, true);
                                                           
        }
    }

//the controller BulletMoverGuard is same as in HelloIntersection...



@ theprism - I understand both models and business model and multithreading would be useful if I am doing respawning of the player - I am just respawning ammo - it would be a lot to go through just to generate medical and ammunition packs... With regards to graphics rule - I am using controllers for bullet firing but as explained above if I was to let a bot/automated enemy fire it - how would I controlled the speed of bullets being fired?

in that case, jusrt a simple com.jme.scene.Controller should do it, add in the bullet ( spatial ) the start time, start position,  end time and the end postion. just look for demos using controller and base it on the most appropiate

Thanks to all the great suggestions and a putting in lots and lots of time and tweaking different things, I finally figured it out!!! I took Core-Dumps idea and modified it slightly and it works perfectly!!! Its really simple and works perfectly.





float lastUpdate = 1f;                    // the number of seconds we want to wait
float time = Timer.getTimer().getTimeInSeconds();
           
if(lastUpdate <= time)
{
        //Do something
        Timer.getTimer().reset();
}



I think we should incorporate this into a tutorial or some example. This is the best way of waiting few seconds without having to use multithreading. Anyway thank you to all - duenez, Core-Dump, hevee and theprism!! Appreciate all the help.  XD

You should not be doing a reset on the timer. Store (and subtract) the value for this specific case instead. (your approach can't be used for more than one task)

Not unless they are spawning a new timer for EACH action (not recommended)

basixs said:

Not unless they are spawning a new timer for EACH action (not recommended)

err, Timer.getTimer() cannot spawn any new timers...

Went back to Core-Dumps method and tried it on something simple this time like shooting instead of respawning of items which had its own issues and its worked well. So owe apologies to Core-Dump as the method does work well. So ended up implementing the following:




private float lastUpdate = 0f;
private float spacing = 1f;

if(ammoGuard >0)
{
      float time = Timer.getTimer().getTimeInSeconds();
       if(spacing + lastUpdate <= time)           
       {
             // shooting code
             lastUpdate = time;              
       }
}

you shall be forgiven :wink:

@Core-Dump said: <br /> <br /> I am not sure if i understand your problem, but with the following method you can use the existing timer there is no need to reset or wait for anything.<br /> I usually do it like this:<br /> <br /> <p><br /> // time of last update<br /> private float lastUpdate = 0;<br /> // spawn rate in seconds<br /> private float updateRate = 5f;<br />     public void update(float tpf) {<br />         if (lastUpdate + spawnRate < Timer.getTimer().getTimeInSeconds()) {<br />             // do something<br />             <br />             lastUpdate= Timer.getTimer().getTimeInSeconds();<br />         }<br />     }<br /> </p><br /> <br /> The only problem that can appear, is that if your Game runs for 10781329274479959411466260781329 years .....  ;)