Announcement

Collapse
No announcement yet.

Open Door with Tick() or Timeline?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Open Door with Tick() or Timeline?

    Dear experts,

    I have a door that I open using the Tick() function in my Actor
    class:

    Code:
    // Called every frame
    void AFloorSwitch::Tick(float DeltaTime)
    {
    Super::Tick(DeltaTime);
    
    TimeToSet += GetWorld()->GetDeltaSeconds();
    
    APawn* MyPawn = GetWorld()->GetFirstPlayerController()->GetPawn();
    if (TriggerBox->IsOverlappingActor(MyPawn))
    {
    RaiseDoor();
    }
    }
    
    void AFloorSwitch::RaiseDoor()
    {
    FVector TargetLocation = FVector(InitialDoorLocation.X, -280.0f, 0.0f);
    
    TargetLocation.Z = FMath::Clamp(FMath::Lerp(0.0f,1.0f,TimeToSet*20),0.0f,200.0f);
    UE_LOG(LogTemp,Warning,TEXT("TargetLocation: %f,%f,%f"),TargetLocation.X,TargetLocation.Y,TargetLocation.Z);
    Door->SetRelativeLocation(TargetLocation);
    }
    I was able to do the same using a Timeline in Blueprint and saw that Timeline is also
    available in C++

    Question: what is more efficient for raising/opening a door? I assume it is using a Timeline,
    correct? I read that using Tick() should be avoided if at all possible.

    Thank you,
    Peter

    #2
    I mean, the timeline needs to be updated somehow. If you update the timeline on tick then it doesn't matter. You could change your current code to keep updating on tick but getting the position from a timeline instead of a LERP. That would have the same result you have right now except it would just be more complicated.

    You could change your code to use OnOverlap events to start and stop the door opening/closing instead of checking for overlap every tick. That would be better than what you are doing right now. Then you'll need a timer to update the door position.

    Whether that timer will update a timeline or not is a matter of preference. Since you are just doing a simple LERP then using timeline is probably overkill. Timelines are helpful if you need curves and need to start/stop/revert that curve. If not you could just keep using your LERP instead.

    If I were you I would just change to overlap events instead of tick. But keep using the LERP instead of a timeline. That's how I usually do these things. Unless you really want a curve instead of a LERP. You could still use data from the curve without a timeline, but the timeline would make it more convenient.
    Last edited by vib; 05-11-2020, 08:31 AM.

    Comment


      #3
      Firing off a timer, doing it in Tick, or setting up a timeline all amounts to the same thing really. Do it however is the most readable, debuggable, and maintainable for your work flow.

      Comment


        #4
        +1 to vib. You should bind a function for your door overlap and use a lerp unless you want some defined movement, like a broken door that jitters back and forth as it struggles to open.

        I recommend checking that the door collided with a collision object type of your pawn, instead of checking if your current pawn overlapped. It will save a tiny tiny bit of memory, but more importantly this will allow all objects with the pawn collision object type to raise the door. What you have now will only allow the first pawn in a multiplayer game, which you may or may not care about but it's nice to dot your T's.

        Comment


          #5
          Originally posted by Lexeon View Post
          unless you want some defined movement, like a broken door that jitters back and forth as it struggles to open.
          And even that can be accomplished with a float curve.

          Comment


            #6
            Thank you so much for all your answers. The only question I have is when I use for example
            "NotifyActorBeginOverlap" how do I get the DeltaTime?
            Can I just keep
            Code:
            TimeToSet += GetWorld()->GetDeltaSeconds();
            in my Tick function and use the same function "RaiseDoor"?

            Thank you again,
            Peter

            Comment


              #7
              You can move the time variable in the overlap function. GetWorld()->GetDeltaSeconds() is perfectly valid there.
              You can set the time when the overlap occurred and just compare the difference in time between current time and your set time, instead of setting a time variable every tick.

              Comment


                #8
                Hi Lexeon

                I can't wrap my head around this:
                Originally posted by Lexeon View Post
                You can set the time when the overlap occurred and just compare the difference in time between current time and your set time, instead of setting a time variable every tick.
                Getting the DeltaTime in the "NotifyActorBeginOverlap" is no problem with GetWorld()->GetDeltaSeconds(). It gives me one time value, one float value.
                But how do I make it continuously update the time difference for the Lerp?

                EDIT: I am still at work, so no UE4 :-(
                But I wrote a little Java program and put a lerp function into a while statement. As long as alpha <= 1.0f my while loop
                creates values and lerps from A to B.
                So maybe such a while statement will work in C++ and UE4 as well, I will try that tonight....
                Last edited by peterw1505; 05-15-2020, 06:23 PM. Reason: added comment about Java program

                Comment


                  #9
                  Originally posted by peterw1505 View Post
                  Hi Lexeon

                  But how do I make it continuously update the time difference for the Lerp?
                  UGameplayStatics has a function GetTimeSeconds(). You save the time a pawn overlapped in your overlap function, then you compare that with the current time within RaiseDoor(). You can divide the difference in time by a scaling variable since lerp uses a 0..1 range so a larger scale value results in a slower door. No need for deltatime in this situation.
                  https://docs.unrealengine.com/en-US/...nds/index.html
                  Last edited by Lexeon; 05-17-2020, 05:07 AM.

                  Comment


                    #10
                    Originally posted by Lexeon View Post

                    UGameplayStatics has a function GetTimeSeconds(). You save the time a pawn overlapped in your overlap function, then you compare that with the current time within RaiseDoor(). You can divide the difference in time by a scaling variable since lerp uses a 0..1 range so a larger scale value results in a slower door. No need for deltatime in this situation.
                    https://docs.unrealengine.com/en-US/...nds/index.html
                    Hello Lexeon,

                    I am not getting anywhere with this. I have the following now:

                    Code:
                    void AFloorSwitch::NotifyActorBeginOverlap(AActor* OtherActor)
                    {
                    Super::NotifyActorBeginOverlap(OtherActor);
                    APawn* MyPawn = GetWorld()->GetFirstPlayerController()->GetPawn();
                    if (MyPawn)
                    {
                    OverlapTime = UGameplayStatics::GetTimeSeconds(GetWorld());
                    RaiseDoor(OverlapTime);
                    }
                    }
                    And this is the RaiseDoor() method:

                    Code:
                    void AFloorSwitch::RaiseDoor(float OverlappedTime)
                    {
                    FVector TargetLocation = FVector(InitialDoorLocation.X, -280.0f, 0.0f);
                    
                    float CurrentTime = UGameplayStatics::GetTimeSeconds(GetWorld()) + 3.0f;
                    float Difference = CurrentTime - OverlappedTime;
                    float Alpha = Difference / 0.01f;
                    
                    UE_LOG(LogTemp, Warning, TEXT("OverlapTime: %f, CurrentTime: %f, Difference: %f, Alpha: %f"),OverlapTime,CurrentTime,Difference,Alpha);
                    
                    while(Alpha > 0)
                    {
                    UE_LOG(LogTemp,Error,TEXT("Alpha equals: %f"),Alpha);
                    TargetLocation.Z = FMath::Clamp(FMath::Lerp(0.0f, 280.0f, Alpha), 0.0f, 280.0f);
                    Door->SetRelativeLocation(TargetLocation);
                    Alpha--;
                    }
                    UE_LOG(LogTemp, Warning, TEXT("TargetLocation: %f,%f,%f"), TargetLocation.X, TargetLocation.Y, TargetLocation.Z);
                    }
                    It counts down my Alpha value and it raises the door. The door just opens really fast. Could you please point out what I am doing wrong?
                    I tried it with 120FPS and 30 FPS and the door raises with the same speed.

                    Thank you again, Peter

                    Comment


                      #11
                      ^ with that while loop it looks like you are interpolating the door all at once in a single tick().

                      What you need to do is update the door interpolation every tick() with a new time elapsed.

                      Comment


                        #12
                        -Remove the while-loop from RaiseDoor().
                        The door moves fast because you are doing the entire lerp in one frame. A while-loop will continue to execute as much as it can within a single frame.

                        -Your RaiseDoor() function should be called in Tick() every frame, not once in overlap. Within your overlap function, you set a boolean bRaiseDoor instead.
                        When you hit the overlap function, setting bRaiseDoor to true will allow RaiseDoor() within Tick() to execute (provided you put RaiseDoor() in an if-statement) for
                        as long as bRaiseDoor is true.

                        -For your alpha, I recommend dividing by a variable instead of just 0.01 so you can easily tweak the value in the editor and find the right speed.
                        The door should take one second to open with a divisor of 1, so a value of 0.01 would open the door in 100 seconds.

                        -When TargetLocation.Z equals 280.f, set bRaiseDoor to false. This will stop RaiseDoor() from executing every frame since the door is already opened.
                        The target location "280.f" could use its own exposed variable too so you can have different door sizes.

                        You can do all of this to close the door as well, of course.

                        Comment


                          #13
                          Hello Lexeon,

                          Thank you for your valuable input and sticking with me and my NOOB questions.
                          I have the door working now:


                          Code:
                          // Called every frame
                          void AFloorSwitch::Tick(float DeltaTime)
                          {
                          Super::Tick(DeltaTime);
                          
                          if (bRaiseDoor)
                          {
                          TimeToSet += DeltaTime;
                          RaiseDoor(TimeToSet);
                          }
                          }
                          Code:
                          void AFloorSwitch::NotifyActorBeginOverlap(AActor* OtherActor)
                          {
                          Super::NotifyActorBeginOverlap(OtherActor);
                          APawn* MyPawn = GetWorld()->GetFirstPlayerController()->GetPawn();
                          if (MyPawn)
                          {
                          bRaiseDoor = true;
                          }
                          }
                          Code:
                          void AFloorSwitch::RaiseDoor(float TimeValue)
                          {
                          
                          FVector TargetLocation = FVector(InitialDoorLocation.X, -280.0f, ClosedZ);
                          Alpha = TimeToSet / AlphaDivisor;
                          UE_LOG(LogTemp, Warning, TEXT("TimeToSet: %f, Alpha: %f"), TimeToSet, Alpha);
                          
                          TargetLocation.Z = FMath::Clamp(FMath::Lerp(0.0f, TargetHeightDoor, Alpha), 0.0f, TargetHeightDoor);
                          
                          if (TargetLocation.Z == TargetHeightDoor)
                          {
                          bRaiseDoor = false;
                          //set TimeToSet to 0, otherwise upon next Overlap the door will raise very fast
                          TimeToSet = 0.0f;
                          }
                          Door->SetRelativeLocation(TargetLocation);
                          
                          UE_LOG(LogTemp, Warning, TEXT("TargetLocation: %f,%f,%f"), TargetLocation.X, TargetLocation.Y, TargetLocation.Z);
                          }
                          In "NotifyActorEndOverlap()" I am currently just calling a CloseDoor() function which shuts the door in one go. I have to work on lerping that too
                          and probably use another bool "bCloseDoor".

                          Not sure how to close a question in this forum and give points...

                          Best regards, Peter

                          Comment


                            #14
                            Originally posted by peterw1505 View Post
                            Not sure how to close a question in this forum and give points...
                            No need, threads are typically always an open-ended discussion. They're commonly necro'd after years of inactivity due to new information and ideas.

                            The answerhub is where the point system lies.


                            Originally posted by peterw1505 View Post
                            I am currently just calling a CloseDoor() function which shuts the door in one go. I have to work on lerping that too
                            and probably use another bool "bCloseDoor".
                            You can modify your RaiseDoor() function to handle an open and a closed door location instead of just the open door location. Perhaps you could use a boolean 'bIsDoorOpen' to determine the state of the door and what location to lerp to.
                            I think you have this down enough to figure the rest out on your own.
                            Last edited by Lexeon; 05-28-2020, 05:11 PM.

                            Comment

                            Working...
                            X