Single Action on Object in N Seconds

edited May 2013 in Help request
I make an object to move from point A to point B. I need object to stop at point B.

I used to use model where on update I check if object is within acceptable distance from point B. If it is close enough, I stop the object (set its speed to zero). That has accuracy difficulty.

So, I decided to pre-calculate the time it takes to reach the destination. I add a one time timer to core clock. The callback in the timer simply stops the context object. It works nice.

Questions:

Is it ok to use core clock for this kind of task? or Should I create a custom clock (it seems like too much effort)?

I noticed that ScrollBase::BaseUpdate extracts per object clock with orxObject_GetClock. I am not sure of the concept. What is the term "Local Object Clock"? Is it a clock that starts at the moment object is created? I noticed it does not exist by default.

Here is code for the reference. I have not figured out how to use C++ methods as C function pointers:
void CastleSiege::MoveToDest(ScrollObject &obj, orxVECTOR worldDest)
{
    orxVECTOR currentPos, direction, directionN, speed;
    obj.GetPosition(currentPos);
    orxVector_Sub(&direction, &worldDest, &currentPos);
    orxVector_Normalize(&directionN, &direction);
    orxVector_Mulf(&speed, &directionN, 60.);
    speed.fZ = 0.;
    obj.SetSpeed(speed);
    
    orxFLOAT len = orxVector_GetSize(&direction);
    orxFLOAT lenSpeed = orxVector_GetSize(&speed);
    orxFLOAT estTime = len / lenSpeed;
    
    orxCLOCK *coreClock = orxClock_FindFirst(orx2F(-1.0f), orxCLOCK_TYPE_CORE);
    orxClock_AddTimer(coreClock, StopMovable, estTime, 1, &obj); // .GetOrxObject()
}

void StopMovable(const orxCLOCK_INFO *clockInfo, void *movable)
{
    ScrollObject *obj = static_cast<ScrollObject*>(movable); // failed to make it a reference
    obj->SetSpeed(orxVECTOR_0);
//    orxObject_SetSpeed((orxOBJECT*)movable, &orxVECTOR_0);
}

A side effect with this model is that I have to remove the clock if it is obsolete prior to reaching the destination (user selects another destination).

Comments

  • edited May 2013
    you can only use static class methods as C function pointers due to name mangling and hidden 'this' parameter
  • edited May 2013
    kulak wrote:
    Is it ok to use core clock for this kind of task? or Should I create a custom clock (it seems like too much effort)?

    Yes, it totally fine and there's actually a shortcut for adding timers to the core clock (as shown in the code snippet below).
    One only needs custom clocks either for optimization (and having a piece of code run at say 10Hz instead of 60Hz) or for doing local time stretching (more below).
    I noticed that ScrollBase::BaseUpdate extracts per object clock with orxObject_GetClock. I am not sure of the concept. What is the term "Local Object Clock"? Is it a clock that starts at the moment object is created? I noticed it does not exist by default.

    Yes, this is for doing per object time stretching. If you link a custom clock to an object, you can then modify that clock's DT and either slow down or speed up the update of that object. It'll also affect all the object components (like changing the pitch of sound playing on it, etc). Many objects can be linked to the same clock.

    Here's the version I propose for your method, assuming the timer's callback is declared as static in the CastleSiege class:
    void CastleSiege::MoveToDest(ScrollObject &obj, const orxVECTOR& worldDest)
    {
        orxVECTOR vPos, vTemp;
        orxFLOAT fSpeed, fDist;
    
        // Removes object's previous stop timer (if any)
        orxClock_RemoveGlobalTimer(StopMovable, orx2F(-1.0f), &obj);
    
        // Gets object's custom move speed, instead of the hardcoded value
        obj.PushConfigSection();
        fSpeed = orxConfig_GetFloat("MoveSpeed");
        obj.PopConfigSection();
    
        // Updates object's speed
        obj.GetPosition(vPos);
        orxVector_Sub(&vTemp, &worldDest, &vPos);
        vTemp.fZ = orxFLOAT_0;
        fDist = orxVector_GetSize(&vTemp);
        orxVector_Mulf(&vTemp, &vTemp, fSpeed / fDist);
        obj.SetSpeed(vTemp);
    
        // Adds stop timer
        orxClock_AddGlobalTimer(StopMovable, fDist / fSpeed, 1, &obj);
    }
    
    void orxFASTCALL CastleSiege::StopMovable(const orxCLOCK_INFO *clockInfo, void *movable)
    {
        ScrollObject *obj = static_cast<ScrollObject*>(movable); // failed to make it a reference
        obj->SetSpeed(orxVECTOR_0);
    }
    
    A side effect with this model is that I have to remove the clock if it is obsolete prior to reaching the destination (user selects another destination).

    As shown above, you can remove the previous timer before adding a new one, if there were no timer, it won't do anything.

    I typed that code directly in the forum, so it's not tested but it should be mostly fine, hopefully. :)
  • edited May 2013
    Timers are a lot more impressive than it looks.

    I was worried that ''orxClock_RemoveGlobalTimer'' would remove incorrect function, but it does not. I had 3 objects moving at the same time, I changed their directions while others are moving and each time objects behaved as expected.

    It appears that registration takes into consideration context object.
  • edited May 2013
    It uses the context indeed.
    It also does filtration based on the delay.
    -1 for the delay or orxNULL for the context will deactivate the respective filters.
    Parameters
    [in] _pfnCallback Concerned timer callback to remove
    [in] _fDelay Delay between 2 calls of the timer to remove, -1.0f for removing all occurences regardless of their respective delay
    [in] _pContext Context of the timer to remove, orxNULL for removing all occurrences regardless of their context
Sign In or Register to comment.