Game Development Community

Creating Delay within a For loop

by Aaron Scovel · in Torque X 2D · 11/15/2010 (11:14 pm) · 17 replies

I'm looking for a way to create a delay in my For loop

for (int i = 0; i < _cutScene.Count; i++)
            {
                ProcessSSAsset(i, position);
            }

As the code is right now, ProcessSSAsset will run x amount of times (x being the _cutScene.Count), and this all happens almost instantly. My goal is to delay the code from being run until ProcessSSAsset is fully processed.

Any Ideas?

I suppose I could replace ProcessSSAsset(i, position); with the actual methods code, but is this the best way to achieve my goal?

Aaron

About the author

Previously a PHP/MySQL Programmer/Web Developer of 10 years. In Aug 2010 I decided to change careers, and this is were I landed! I also parent 3 kids full time. TopNotched.com


#1
11/16/2010 (4:41 am)
Unless ProcessSSAsset() is running asynchronously, the for loop should wait for it to return before advancing i. Are you having problems with the code inside that method?
#2
11/16/2010 (5:22 am)
Thanks Jason, I wasn't aware of that.
#3
11/16/2010 (5:25 pm)
Ok, Here is part of the code I have in ProcessSSAsset

while (_process)
            {
                if (!_beginDisplayTimer)
                {
                    if (_processObject.VisibilityLevel >= 1.00f)
                    {
                        _processObject.VisibilityLevel = 1.00f;
                        //_processObject.Visible = true;
                        _beginDisplayTimer = true;
                        _process = false;
                    }
                    else
                    {
                        _processObject.VisibilityLevel += ((CutSceneAssets)_cutScene[currentIndex]).AssetFadeInSpeed * (Game.Instance.Engine.GameTime.ElapsedRealTime.Milliseconds * 0.001f);
                    }
                }
            }

I am having troubles fading _processObject.VisibilityLevel in my loop, when I break the code everything seems to be working good, but when I run it without breaks, it instantly sets the visibilityLevel to 1.

This code works if I place it in the processTick and use dt.

AssetFadeInSpeed = 1.00f. Changing this to .001 still shows the visibility as 100% instantly.

I must need to update the draw during each loop?
#4
11/16/2010 (6:44 pm)
Your "for" loop code is going to run faster then the render loop will ever be able to keep up with. You have a couple options to ensure each frame is show.
1.) pause in between each iteration of the for loop giving the render pipeline a chance to show you changes. (this is effectively what you are doing when placing it on a dt limited processTick method)
2.) change this entire sequence to a predefined animation and let the animation manager handle the coordination with the render manager.
3.) if you want to coordinate frames of an animation dynamically (i.e. not sure how long a particular frame will take to complete its animation) use an event based solution.

Hope this helps.
#5
11/16/2010 (7:26 pm)
@Aaron: this sounds like something you should be doing in a 'tick' method. One way to achieve this is to register an object with the processList so you get a tick callback every time the engine ticks. On each tick you can progress the visibility change based on the elapsed time for that tick. Alternatively you could create an XNA component (as opposed to a tx one) and 'tick' the visibility in it's Update method.
#6
11/16/2010 (10:36 pm)
Thanks Jon & Duncan!

Jon: "pause in between each iteration of the for loop giving the render pipeline a chance to show you changes."

Duncan: "One way to achieve this is to register an object with the processList so you get a tick callback every time the engine ticks. On each tick you can progress the visibility change based on the elapsed time for that tick."

These both sound like they would solve my problem, do you guys have any code examples you could post for these?
#7
11/17/2010 (12:50 am)
public virtual void ProcessTick(Move move, float dt)
{
fadeElapsed += dt;
SceneObject.VisibilityLevel = fadeElapsed/MaxFadeInTime;
}
private float MaxFadeInTime= 3;
private float fadeElapsed = 0;
remember dt always equates to seconds past... so its actually a pretty easy way to time stuff.
#8
11/17/2010 (1:54 am)
@Will: You just turned on my brain again, thanks bud, I keep forgetting that I can update method values from within the ProcessTick. I just need to add
ProcessList.Instance.AddTickCallback(Owner, this);
inside my method right?

... :-)
#9
11/17/2010 (3:09 am)
Ok, I have it all setup to update via the ProcessTick, but now I cant get the processTick to tick :-). I have setup breaks to check the code and everything seems to be working fine, its just not processing anything in the ProcessTick. (Its stuck in a loop, until the ProcessTick works, which it doesn't) I can post my full code if that would help.
#10
11/17/2010 (4:19 am)
I usually have ProcessList.Instance.AddTickCallback(SceneObject, this);
but i don't think that will change anything
#11
11/17/2010 (5:24 am)
Have you called ProcessList.Instance.AddTickCallback(SceneObject, this); inside a loop before?

ProcessSSAsset Method
public void ProcessSSAsset(int currentIndex, string position)
        {
            
            bool _beginDisplayTimer = false;
            bool _process = true;
            float _currentTime = 0.000f;
            // Update _currentIndex so ProcessTick knows what index to use.
            _currentIndex = currentIndex;
            // Find asset Object
            _processObject = TorqueObjectDatabase.Instance.FindObject<T2DSceneObject>(((CutSceneAssets)_cutScene[currentIndex]).AssetObjectID);

            // Get position type
            switch (position)
            {
                case "CenterScreen":
                    _processPosition = _camera.Position + ((CutSceneAssets)_cutScene[currentIndex]).AssetPositionOffset;
                    break;
                case "ThisPosition":
                    _processPosition = _sceneObject.Position + ((CutSceneAssets)_cutScene[currentIndex]).AssetPositionOffset;
                    break;
                case "AssetPosition":
                    _processPosition = ((CutSceneAssets)_cutScene[currentIndex]).AssetPosition + ((CutSceneAssets)_cutScene[currentIndex]).AssetPositionOffset;
                    break;
            }
            // Update asset position
            _processObject.Position = _processPosition;
            
            while (_process)
            {
                if (!_beginDisplayTimer)
                {
                    if (_processObject.VisibilityLevel >= 1.00f)
                    {
                        _visibilityUp = false;
                        _processObject.VisibilityLevel = 1.00f;
                        _beginDisplayTimer = true;
                    }
                    else
                    {
                        // Increase Visibility
                        _visibilityUp = true;
                        ProcessList.Instance.AddTickCallback(Owner, this);
                        //_processObject.VisibilityLevel += ((CutSceneAssets)_cutScene[currentIndex]).AssetFadeInSpeed * (Game.Instance.Engine.GameTime.ElapsedRealTime.Milliseconds * 0.001f);
                    }
                }
                else
                {
                    if (_currentTime >= ((CutSceneAssets)_cutScene[currentIndex]).AssetDisplayTime)
                    {
                        if (_processObject.VisibilityLevel <= 0.00f)
                        {
                            _visibilityDown = false;
                            _processObject.VisibilityLevel = 0.00f;
                            _activate = false;
                            _process = false;
                            // Unregister Asset
                            TorqueObjectDatabase.Instance.Unregister(_processObject);
                            
                        }
                        else
                        {
                            // Decrease Visibility
                            _visibilityDown = true;
                            ProcessList.Instance.AddTickCallback(Owner, this);
                            //_processObject.VisibilityLevel -= ((CutSceneAssets)_cutScene[currentIndex]).AssetFadeOutSpeed * (Game.Instance.Engine.GameTime.ElapsedRealTime.Milliseconds * 0.001f);
                        }
                    }
                    else
                    {
                        // ### todo replace and update with ProcessTick  // 
                        _currentTime += (Game.Instance.Engine.GameTime.ElapsedRealTime.Milliseconds * 0.001f);                         
                    }
                }
            }
        }

Inside ProcessTick
if (_visibilityUp)
                {
                    _processObject = TorqueObjectDatabase.Instance.FindObject<T2DSceneObject>(((CutSceneAssets)_cutScene[_currentIndex]).AssetObjectID);
                    _processObject.VisibilityLevel += ((CutSceneAssets)_cutScene[_currentIndex]).AssetFadeInSpeed * dt;
                }
                if (_visibilityDown)
                {
                    _processObject = TorqueObjectDatabase.Instance.FindObject<T2DSceneObject>(((CutSceneAssets)_cutScene[_currentIndex]).AssetObjectID);
                    _processObject.VisibilityLevel -= ((CutSceneAssets)_cutScene[_currentIndex]).AssetFadeOutSpeed * dt;
                }

In debug, _visibilityUp in the processTick becomes true, but since the processTick isnt running nothing happens.
#12
11/17/2010 (11:57 am)
I think you may have gotten mixed up here. Until you exit the while loop nothing is going to get ticked. You need to put the stuff that's inside the while loop into the processTick - not put the processTick into the while loop :-)
#13
11/17/2010 (5:26 pm)
So the process tick wont work at all during a method loop? C# cant multitask? I'm obviously still learning :-) This is the first time Ive needed "delay/time" within a method, but if this is true, then I need to rewrite some stuff.
#14
11/17/2010 (5:45 pm)
Looks like you are needing to split what your method does across several game ticks (i.e. over time). You will need to take a different approach to that therefore - you need to do a bit of the processing on each game tick.

There are various ways to go about that including, using Enums in a switch statement to do something different depending on the current 'state', or use the tx FSM class, or use the Enumerator/Iterator approach (which is more like working with separate threads... only not requiring separate threads).
#15
11/17/2010 (6:08 pm)
Ok, I know how to use Enums/switches, so using these will avoid the process tick from being stuck in a loop? (but still needs to be located in the processTick, correct?) Will I be able to run multiple loops (that overlap) using this approach?

The component is a two part system, SlideShow & CutScene Maker. The SlideShow side will run loops one after another, but the CutScene side will require loops being ran in order of a timeframe and depending on the duration of each, some may overlap. The system uses another component (Asset Component) that is attached to different scene objects, the data is then sent to the main "Master" component and stored into a multidimensional array which is then processed to display the series of events that make up the slide show or cut scene.
#16
11/18/2010 (9:52 am)
Sup Arron,
#17
11/18/2010 (10:47 am)
Ok, not sure about the total functionality you are trying to achieve but this sounds like you are attempting to rewrite the animation management classes. So, lets say you want to create a cut scene, is it really any different from a normal animation? Not really. so just import your total cut scene animation to the tx builder. Drag and drop it to get a T2DAnimated sprite attach your custom "cutscene state" tracking component. In your code, instance your animation in the "main" game loop when triggered, register it, and be sure the instancing component registers for tick callbacks in OnRegister. Your component then just needs to track the dt and accumulate the time that has passed and trigger different animation frames based on the time.

In psuedocode
--------------------------------------------------
//component code
processTick(time dt){//This IS your "for" loop
elapsedTime += dt;
//Change the animation state based on the elapsed time/state (pausing, advancing, speeding up)
if(elapsedtime<fadeInTime){
T2DAnimatedSprite(ParentSceneObject).animationController.advanceFrame;
T2DAnimatedSprite(parentSceneObject).visiblity ++;
else if (elapsedTime > fadeInTime && elapsedTime < playFramesSlowTime )
//logic to control the speed of animation controller's frame advancement
else if ///you get the idea

}
}

Onregister(){
//set the component default state if needed
//register this componenet for tick call backs
}


-----------------------------------------------
In your "main" game control loop
//get the tx builder sceneobject animation
//Register to torqueDb//This will trigger torqueEngine to send ticks independently into the component
//set the animation frame to 0 and pause the animation
-----------------------------------------------


After that the animation will just be controlled by logic in the processTickMethod of the component code.

Hope it helps,