Creating a Time of Day handler

This recipe shows you how to use the concepts introduced in the previous recipes to create an actor that informs other actors of the passage of time within your game.

How to do it...

  1. Create a new Actor class called TimeOfDayHandler.
  2. Add a multicast delegate declaration to the header:
    DECLARE_MULTICAST_DELEGATE_TwoParams(FOnTimeChangedSignature, int32, int32)
  3. Add an instance of our delegate to the class declaration:
    FOnTimeChangedSignatureOnTimeChanged;
  4. Add the following properties to the class:
    UPROPERTY()
    int32 TimeScale;
    
    UPROPERTY()
    int32 Hours;
    UPROPERTY()
    int32 Minutes;
    
    UPROPERTY()
    float ElapsedSeconds;
  5. Add the initialization of these properties to the constructor:
    TimeScale = 60;
    Hours = 0;
    Minutes = 0;
    ElapsedSeconds = 0;
  6. Inside Tick, add the following code:
    ElapsedSeconds += (DeltaTime * TimeScale);
    if (ElapsedSeconds> 60)
    {
      ElapsedSeconds -= 60;
      Minutes++;
      if (Minutes > 60)
      {
        Minutes -= 60;
        Hours++;
      }
    
      OnTimeChanged.Broadcast(Hours, Minutes);
    }
  7. Create a new Actor class called Clock.
  8. Add the following properties to the class header:
    UPROPERTY()
    USceneComponent* RootSceneComponent;
    
    UPROPERTY()
    UStaticMeshComponent* ClockFace;
    UPROPERTY()
    USceneComponent* HourHandle;
    UPROPERTY()
    UStaticMeshComponent* HourHand;
    UPROPERTY()
    USceneComponent* MinuteHandle;
    UPROPERTY()
    UStaticMeshComponent* MinuteHand;
    
    UFUNCTION()
    void TimeChanged(int32 Hours, int32 Minutes);
    FDelegateHandleMyDelegateHandle;
  9. Initialize and transform the components in the constructor:
    RootSceneComponent = CreateDefaultSubobject<USceneComponent>("RootSceneComponent");
    ClockFace = CreateDefaultSubobject<UStaticMeshComponent>("ClockFace");
    HourHand = CreateDefaultSubobject<UStaticMeshComponent>("HourHand");
    MinuteHand = CreateDefaultSubobject<UStaticMeshComponent>("MinuteHand");
    HourHandle = CreateDefaultSubobject<USceneComponent>("HourHandle");
    MinuteHandle = CreateDefaultSubobject<USceneComponent>("MinuteHandle");
    auto MeshAsset = ConstructorHelpers::FObjectFinder<UStaticMesh>(TEXT("StaticMesh'/Engine/BasicShapes/Cylinder.Cylinder'"));
    if (MeshAsset.Object != nullptr)
    {
      ClockFace->SetStaticMesh(MeshAsset.Object);
      HourHand->SetStaticMesh(MeshAsset.Object);
      MinuteHand->SetStaticMesh(MeshAsset.Object);
    }
    RootComponent = RootSceneComponent;
    HourHand->AttachTo(HourHandle);
    MinuteHand->AttachTo(MinuteHandle);
    HourHandle->AttachTo(RootSceneComponent);
    MinuteHandle->AttachTo(RootSceneComponent);
    ClockFace->AttachTo(RootSceneComponent);
    ClockFace->SetRelativeTransform(FTransform(FRotator(90, 0, 0), FVector(10, 0, 0), FVector(2, 2, 0.1)));
    HourHand->SetRelativeTransform(FTransform(FRotator(0, 0, 0), FVector(0, 0, 25), FVector(0.1, 0.1, 0.5)));
    MinuteHand->SetRelativeTransform(FTransform(FRotator(0, 0, 0), FVector(0, 0, 50), FVector(0.1, 0.1, 1)));
  10. Add the following to BeginPlay:
    TArray<AActor*>TimeOfDayHandlers;
    UGameplayStatics::GetAllActorsOfClass(GetWorld(), ATimeOfDayHandler::StaticClass(), TimeOfDayHandlers);
    if (TimeOfDayHandlers.Num() != 0)
    {
      auto TimeOfDayHandler = Cast<ATimeOfDayHandler>(TimeOfDayHandlers[0]);
      MyDelegateHandle = TimeOfDayHandler->OnTimeChanged.AddUObject(this, &AClock::TimeChanged);
    }
  11. Lastly, implement TimeChanged as your event handler.
    void AClock::TimeChanged(int32 Hours, int32 Minutes)
    {
      HourHandle->SetRelativeRotation(FRotator( 0, 0,30 * Hours));
      MinuteHandle->SetRelativeRotation(FRotator(0,0,6 * Minutes));
    }
  12. Place an instance of TimeOfDayHandler and the AClock into your level, and play to see that the hands on the clock are rotating:
    How to do it...

How it works...

  1. TimeOfDayHandler contains a delegate which takes two parameters, hence the use of the TwoParams variant of the macro.
  2. Our class contains variables to store hours, minutes, and seconds, and the TimeScale, which is an acceleration factor used to speed up time for testing purposes.
  3. Inside the handler's Tick function, we accumulate elapsed seconds based on the time elapsed since the last frame.
  4. We check if the elapsed seconds have gone over 60. If so, we subtract 60, and increment Minutes.
  5. Likewise with Minutes—if they go over 60, we subtract 60, and increment Hours.
  6. If Minutes and Hours were updated, we broadcast our delegate to let any object that has subscribed to the delegate know that the time has changed.
  7. The Clock actor uses a series of Scene components and Static meshes to build a mesh hierarchy that resembles a clock face.
  8. In the Clock constructor, we parent the components in the hierarchy, and set their initial scale and rotations.
  9. In BeginPlay, the clock uses GetAllActorsOfClass() to fetch all the time of day handlers in the level.
  10. If there's at least one TimeOfDayHandler in the level, the Clock accesses the first one, and subscribes to its TimeChanged event.
  11. When the TimeChanged event fires, the clock rotates the hour and minute hands based on how many hours and minutes the time currently has.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.148.104.149