Creating a Cycle

Hi there,
I am confused as to how to create a cycle. For instance I have this…

var mouseDown : boolean = false;

function OnMouseDown () {
	mouseDown = true;
	fireWpn();
}
function fireWpn()
{
	print("fireWpn(): mouseDown = " + mouseDown);
	if(mouseDown == false)
	return;
	 
	print("fireWpn");
	loadBullet();
}
function loadBullet()
{	
	print("loadBullet(): mouseDown = " + mouseDown);

	yield WaitForSeconds(0.5);
	fireWpn();
}
function onMouseUp()
{
	mouseDown = false;
}

But at max, while mouse is being held down for a long amount of time, it stops calling fireWpn() after 2 cycles. mouseDown initiates 1, then loadBullet initates 2. I am expecting it to loop and loop and lopp until I ask it not to via onMouseUp().

Of course, this version below causes an error.

var mouseDown : boolean = false;

function OnMouseDown () {
	mouseDown = true;
	fireWpn();
}
function fireWpn()
{
	print("fireWpn(): mouseDown = " + mouseDown);
	if(mouseDown == false)
	return;
	 
	print("fireWpn");
	loadBullet();

	yield WaitForSeconds(0.5);
	fireWpn();
}

function onMouseUp()
{
	mouseDown = false;
}

Can someone tell me how to correctly create a looping schedule?

OnMouseDown only fires on the frame in which you clicked. You’d have to just set the flags with OnMouseDown and OnMouseUp, kick off a coroutine and loop on that flag.

Why not just make fire weapon a while loop?

while (mouseDown) {
   print("fireWpn(): mouseDown = " + mouseDown);
   print("fireWpn");
   print("loadBullet(): mouseDown = " + mouseDown);
   yield WaitForSeconds(0.5);
}

Easy enough to solve. Two slightly different solutions that solve the same problem.

var m_delayBetweenShots : float;

var m_firingCountdown : float ;

function Reset()
{
    m_delayBetweenShots = 2.0f;
}

function Update()
{
    m_firingCountdown -= Time.deltaTime;
    if (Input.GetMouseButtonDown(0) == true)
    {
        AttemptToFire();
    }

}

function AttemptToFire()
{
    if (m_firingCountdown <= 0.0f)
    {
        FireWeapon();
    }

}

function FireWeapon()
{
    m_firingCountdown = m_delayBetweenShots;
    // do whatever it is you do here when your weapon fires, e.g. play a sound effect, instantiate a game object, decrement ammunition counter
}
var m_delayBetweenShots : float;

var m_lastFireTime : float;

function Reset()
{
    m_delayBetweenShots = 2.0f;
}

function Update()
{
    if (Input.GetMouseButtonDown(0) == true)
    {
        AttemptToFire();
    }

}

function AttemptToFire()
{
    if ((Time.time - m_lastFireTime) >= m_delayBetweenShots)
    {
        FireWeapon();
    }

}

function FireWeapon()
{
    m_lastFireTime = Time.time;
    // do whatever it is you do here when your weapon fires, e.g. play a sound effect, instantiate a game object, decrement ammunition counter
}

ok, so I will have to either use an update or while to check the status?
Ok.

Now, @GargerathSunman mentioned using while(mouseDown) without checking, is that an actual call? Anyways, much appreciated, thanks.

edit.
So I have found a work around, but again it requires me using a lot of if statements for every possibility. It is not that bad since I could really only have a couple of dozen possibilities. But I really have to find a way to have the cycle working infinitely.

As I say, I have tried both versions offered by Justin and I understand there logic, but they only execute once.

Regardless, thanks.

///working code

function OnMouseDown()
{
	timer = 0;
	///this partucular chunk will handle a Gazzoo Space Blaster. 
	///this means that OnMouseDown ... wpn powered up on timer
	///onMouseUp, wpn projectile released and projectile power is varient on timer size.
	///this bigger, the more powerful.
	
	wpnFiring = true;
}
function OnMouseUp()
{	
	wpnFiring = false;
	
}
function Update()
{
	if(wpnFiring == true)
	{
		timer++;
		print("timer " + timer);
		if(timer == 25)
		{
			print("===========25 beyatch!");
			fireBullet();
		}
		if(timer == 50)
		{
			print("===========50 beyatch!");
			fireBullet();
		}
		if(timer == 75)
		{
			print("===========75 beyatch!");
			fireBullet();
		}
		if(timer == 100)
		{
			print("===========100 beyatch!");
			fireBullet();
		}
		if(timer == 125)
		{
			print("===========125 beyatch!");
			fireBullet();
		}
		if(timer == 150)
		{
			print("===========150 beyatch!");										
			fireBullet();
		}
	}
}
function fireBullet()
{
	print("fireBullet YES!!!!!!!!!!");
	print("fireBullet YES!!!!!!!!!!");
	print("fireBullet YES!!!!!!!!!!");
	print("fireBullet YES!!!!!!!!!!");		
}

//untested US.

//If mouse is pressed.

function Start() {

    while (true) {
        if (Input.GetMouseButton(0)) {
            print("FIRE... my PANTS are on FIRE!");  
            yield WaitForSeconds(0.5f);
        } else yield;

    } // This bracket pair could probably be ditched in this example.
}

//if mouse is pressed on the object.

var mouseDown = false;

function Start() {

    while (true) {
        if (mouseDown) {
            print("FIRE... my PANTS are on FIRE!");
            yield WaitForSeconds(0.5f);
        } else yield;

    } // This bracket pair could probably be ditched in this example.
}

function OnMouseDown() {
    mouseDown = true;
}

function OnMouseUp() {
    mouseDown = false;
}

Justin, mate I like you and we are here today to stage an intervention. DON’T DO IT THAT WAY, it always ends up more complicated and obfuscated than coroutines or Invoke.

Death to Update. :wink: However the simplest and most efficient way in this case is InvokeRepeating.

var delayBetweenShots = .5;

function OnMouseDown() {
    InvokeRepeating ("Shoot", .001, delayBetweenShots); // don't use 0 for the first number
}

function OnMouseUp() {
    CancelInvoke ("Shoot");
}

function Shoot () {
    print ("bang");
}

This won’t work, since it’s framerate-dependent. You’re counting frames, not time. (If you were to do it this way for some reason anyway, you’d simply set the timer back to 0 instead of making a zillion if statements.)

–Eric

I know, which is why I left you your opening; I dislike using strings to cancel things so I left it alone.

Why reset when you can simply use modulus maths?! :slight_smile:

On the contrary, you have very little control over co-routines, when they fire, etc. Co-routines good for the simple stuff, but nothing ever really ends up being simple in a real, production-ready game. Co-routines basically give you nothing you don’t already have, and take a way stuff you do.

This is how I would have solved it (C# because I can’t be bothered with the atrocity that is UnityScript):

class WeaponFire
{
    float next = 0f;

    public void AttempFire(float delay)
    {
        if ((next -= Time.deltaTime) <= 0f  Input.GetMouseButton(0))
        {
            next = delay;
            // Weapon fires
        }
    }
}

public class SomeScript : MonoBehaviour
{
    public float FireRate = 0.5f;
    WeaponFire weaponFire = new WeaponFire();

    void Update()
    {
        weaponFire.AttempFire(FireRate);
    }
}

Co-routines give you plenty of control, and in your given example you do not know when the ‘Update()’ is going to fire either - so that claim is false.

What they do do is take away your opportunity to stuff it up - you use ~3 statements to replace ‘yield WaitForSeconds(0.5f);’ - giving more risk. In my experience this gets worse as one tries to scale.

Oh, how misguided.

I know that update will fire every frame, and I know when (in the code) I’m calling AttemptFire. If I need to stop that call for whatever reason, I can. Ever tried passing a co-routine around between different objects in the game world, it is of course possible but create a very complicated logic flow. Maybe someone in the game can do something to stop you from firing, no you need to check that condition in the co-routine every time and somehow pass the data into it (which if course is possible).

An instance of a class that encapsulates some behavior gives you complete control over it. Co-routines also generate garbage that you don’t have control over, you can’t re-cycle them as you can with a normal object.

If someone seriously has a problem with implementing a proper timer in a base class that you can extend and override a method that gets called every time the timer expires, they should seriously consider what they are doing trying to build a game.

Also, co-routines are on the “slower” side, as in general it will take three method calls to do the invocation from of the co-routine (and probably a bit more, I don’t have access to the inner workings of unity).

If you end up using co-routines for everything like this and then put your game on a limited device like a phone or a pad, you could very well end up with a substantial slow down both from the overhead of the co-routine and all the mindless garbage it generates that has to be cleaned up by the GC.

That’s pretty convoluted, plus it’s adding the overhead of an Update call every frame just to count a timer down. Coroutines certainly don’t give you less control, and I use them quite heavily in production. They can remove spaghetti code and obfuscation if used sensibly. e.g., this code is pretty much completely self-documenting, is a 100% straightforward read from top to bottom, and doesn’t require creating any extra systems, plus it’s faster to write:

function Start () {
	while (true) {
		if (Input.GetMouseButton(0)) {
			Shoot();
			yield WaitForSeconds(.5);
		}
		else {
			yield;
		}
	}
}

(Basically what NPSF3000 wrote.)

On the contrary, you have less overhead with coroutines compared to Update calls. Replacing convoluted Update logic with simpler coroutines results in a speed up, speaking from experience.

–Eric

I know that the co-routine will fire every frame unless it’s waiting to reload. If I need to stop the co-routine for whatever reason I can.

Feel free to provide an example of this.

I’ve seen [and helped] professionals with published games write significantly more code than necessary thanks to the ‘count time method’. My complaint is based on experience, not theory. They were not wrapping the code into nice neat classes [which would have been acceptable, even if more time consuming overall].

A co-routine uses the Enumerator functionality - the same thing used for ‘for each’ loops. It’s not slow, and if unity have done their job write you can cut out tons of needless Update() function calls. If your argument is based on fact-less micro-optimizations…

Sounds like someone’s got problems with their architecture but blames co-routines instead of cleaning it up.


Thanks Eric for the real world expertise in the above post.

Holy smokes guys. I can’t believe my thread has opened up this conversation. Regardless, Eric’s invokeRepeating makes the most sense to me. It is the simplest and like good old Apple designed products, I like simple.

Co-routines, by their definition, give you less control then the most bare bone component in the entire language: An object. That you can do anything with.

I seriously hope you did not think I meant just wrapping that little component in a mono behavior and attaching it? But you said you spoke from experience so I suppose that is what you did?

I obviously had to put the call to .AttemptFire in context inside a behavior or something so the use would be apparent. Nowhere did I mention update calls vs. co-routines, I just said that doing a normal method call is a lot faster then going through a co-routine. Which also is true, by definition.

And if it really did speed up your code, what you just said. That means you either put every little piece of code in a separate behavior and had a lot of them attached to every object, or something is/was seriously wrong with your timer function.

You’re using C#.

You’re using Uniy3D.

You’ve already made it quite clear that control is less important than things like productivity. Yet without a shred of evidence or a single given use-case you appear to be rejecting some of the most basic OO principles. What’s up man?

http://en.wikipedia.org/wiki/Data_abstraction

Good :slight_smile: Now go hide while the big boys m̶a̶k̶e̶ ̶a̶s̶s̶e̶s̶ ̶o̶f̶ ̶t̶h̶e̶m̶s̶e̶l̶v̶e̶s̶… engage in meaningful debate :stuck_out_tongue:

You still missed then point, I know in the logical flow of the application when a method will fire. I don’t know that for a co-routine. This is a big deal on large systems.

Your point being that most people are stupid and should not be allowed to program, or what?

Oh, but it is pretty slow. First of all the Enumerator that gets generated is not equal to the one that you will find in say a Dictionary<K, V> or a List, and second of all it’s not stack-based like the ones you will find in the built in collections.

Enumerating over something by using yield return is slow.

I like how I point out a fact (co-routines are slower then method calls, they generate garbage also - which method calls don’t) and you respond with an attack on some hypothetical project I don’t even have ? I’m sorry but, what?

Yes sir!! It’s like family Christmas all over again.
::))(

What? How is this even relevant, you can create the abstraction yourself just as easily without a single hitch. Data abstractions is just a general concept, it has nothing to with using someone else implementation of it or your own.