{"id":15357699,"url":"https://github.com/nauja/ue4-chest2d-sample","last_synced_at":"2025-04-15T07:11:49.159Z","repository":{"id":139652922,"uuid":"268330428","full_name":"Nauja/ue4-chest2d-sample","owner":"Nauja","description":"Sample of an interactable 2D chest done in Unreal Engine 5 with Paper2D","archived":false,"fork":false,"pushed_at":"2023-11-14T18:30:32.000Z","size":9254,"stargazers_count":6,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-15T07:11:44.067Z","etag":null,"topics":["chest","multiplayer","paper2d","rpc","ue4","ue5"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Nauja.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2020-05-31T17:34:20.000Z","updated_at":"2024-12-24T19:48:07.000Z","dependencies_parsed_at":"2023-07-23T08:45:22.803Z","dependency_job_id":null,"html_url":"https://github.com/Nauja/ue4-chest2d-sample","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nauja%2Fue4-chest2d-sample","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nauja%2Fue4-chest2d-sample/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nauja%2Fue4-chest2d-sample/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nauja%2Fue4-chest2d-sample/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Nauja","download_url":"https://codeload.github.com/Nauja/ue4-chest2d-sample/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249023730,"owners_count":21199960,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chest","multiplayer","paper2d","rpc","ue4","ue5"],"created_at":"2024-10-01T12:38:08.745Z","updated_at":"2025-04-15T07:11:49.141Z","avatar_url":"https://github.com/Nauja.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ue4-chest2d-sample\n\n![UE5](https://img.shields.io/badge/UE5-5.2+-blue)\n[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/Nauja/ue4-chest2d-sample/master/LICENSE)\n\nSample of an interactable 2D chest done in Unreal Engine 5 with Paper2D.\n\n![Preview](https://github.com/Nauja/ue4-chest2d-sample/raw/media5.2/preview.gif)\n\nThis project is an example of how to write an interactable chest in a Paper2D game, with the constraint of\nbeing fully replicated over network.\n\nPrerequisites:\n  * [Pixel Perfect 2D Sample](https://github.com/Nauja/ue4-pixelperfect2d-sample)\n\nFor an Unreal Engine 4 version, check the branch [ue4.25](https://github.com/Nauja/ue4-chest2d-sample/tree/ue4.25).\n\n## Table of contents:\n\n- [RPC to replicate **Interact** action](#rpc-to-replicate-Interact-action)\n- [Detect possible interaction on server and client](#detect-possible-interaction-on-server-and-client)\n- [Replicate the state of our chest](#replicate-the-state-of-our-chest)\n\n## RPC to replicate Interact action\n\nPressing the **Interact** button client-side simply calls an RPC server-side to handle the action.\nIn a multiplayer game, the client may be totally desynchronized and may not see the correct state\nof the game. This step is necessary to let the server validate whether or not the client can interact\nwith something and to let the server perform the action.\n\nHere is the definition in **ASampleCharacter.h**:\n\n```cpp\nUFUNCTION()\nvoid Interact();\n\nUFUNCTION(reliable, Server, WithValidation)\nvoid Server_Interact();\n```\n\nHere is the implementation in **ASampleCharacter.cpp**:\n\n```cpp\nvoid ASampleCharacter::Interact()\n{\n    if (GetLocalRole() \u003c ROLE_Authority) {\n        Server_Interact();\n    }\n    else\n    {\n        TSet\u003cAActor*\u003e Actors;\n        GetOverlappingActors(Actors, TSubclassOf\u003cASampleInteractableActor\u003e());\n        for (auto Actor : Actors)\n        { \n            static_cast\u003cASampleInteractableActor*\u003e(Actor)-\u003eInteract(this);\n        }\n    }\n}\n\nbool ASampleCharacter::Server_Interact_Validate()\n{\n    return true;\n}\n\nvoid ASampleCharacter::Server_Interact_Implementation()\n{\n    Interact();\n}\n```\n\nPlease note that this RPC has to be called from an Actor owned by the client for it to works.\nPer consequence it would not be possible to move it directly into the class of our\ninteractable Actor. Missing this point can give you a hard time trying to figure out why\nthe RPC is not called on server.\n\n## Detect possible interaction on server and client\n\nTo visually show when the player can use the **Interact** action, we have to detect\nwhen the character is overlapping the chest. To do this, simply use the two **NotifyActorBeginOverlap**\nand **NotifyActorEndOverlap** to detect and track overlaps with Actors.\nThis step is necessary to let the client compute itself whether or not it can interact\nwith something and give and feedback to the player.\n\nHere is the definition in **SampleInteractableActor.h**:\n\n```cpp\nvoid NotifyActorBeginOverlap(class AActor* Other) override;\nvoid NotifyActorEndOverlap(class AActor* Other) override;\n\n/** Called when at least one actor can interact with this interactable **/\nUPROPERTY(BlueprintAssignable, Category=\"Sample\")\nFOnBeginInteractableDelegate OnBeginInteractable;\n\n/** Called when no more actors can interact with this interactable **/\nUPROPERTY(BlueprintAssignable, Category=\"Sample\")\nFOnEndInteractableDelegate OnEndInteractable;\n\n/** Called when at least one actor can interact with this interactable **/\nUFUNCTION(BlueprintNativeEvent, Category = \"Sample\")\nvoid NotifyBeginInteractable();\n\n/** Called when no more actors can interact with this interactable **/\nUFUNCTION(BlueprintNativeEvent, Category = \"Sample\")\nvoid NotifyEndInteractable();\n```\n\nNote that the custom **NotifyBeginInteractable** and **NotifyEndInteractable** functions are used to react to the\noverlap directly from C++, while **OnBeginInteractable** and **OnEndInteractable** are used to react\nfrom Blueprint.\n\nHere is the implementation in **SampleInteractableActor.cpp**:\n\n```cpp\nvoid ASampleInteractableActor::NotifyActorBeginOverlap(class AActor* Other)\n{\n    Super::NotifyActorBeginOverlap(Other);\n\n    if (IsValid(Other) \u0026\u0026 !IsPendingKill() \u0026\u0026 bIsEnabled)\n    {\n        if (OverlappingActors.Num() == 0)\n        {\n            NotifyBeginInteractable();\n        }\n        OverlappingActors.Add(Other);\n    }\n}\n\nvoid ASampleInteractableActor::NotifyActorEndOverlap(class AActor* Other)\n{\n    Super::NotifyActorEndOverlap(Other);\n\n    if (IsValid(Other) \u0026\u0026 !IsPendingKill() \u0026\u0026 bIsEnabled)\n    {\n        OverlappingActors.Remove(Other);\n        if (OverlappingActors.Num() == 0)\n        {\n            NotifyEndInteractable();\n        }\n    }\n}\n\nvoid ASampleInteractableActor::NotifyBeginInteractable_Implementation()\n{\n    OnBeginInteractable.Broadcast();\n}\n\nvoid ASampleInteractableActor::NotifyEndInteractable_Implementation()\n{\n   OnEndInteractable.Broadcast();\n}\n```\n\nIn this sample you can see two buttons indicating when you can interact with the chest:\n\n![LevelInteractButtons](https://github.com/Nauja/ue4-chest2d-sample/raw/media5.2/editor-levelinteractbutton.png)\n\nThey are bound to the chest from Blueprint using **OnBeginInteractable** and **OnEndInteractable** delegates:\n\n![InteractButtonsBP](https://github.com/Nauja/ue4-chest2d-sample/raw/media5.2/editor-interactbutton.png)\n\n## Replicate the state of our chest\n\nIn this sample, we use a boolean **bIsEnabled** to tell whether the chest can be interacted with or not.\nOnce someone interacted with the chest, this boolean becomes **false** and is later resetted to **true** to make\nthe interaction available once again. This step is necessary to synchronize the client with the server.\n\nHere is the definition in **SampleInteractableActor.h**:\n\n```cpp\n/** [Server] Set if the interaction is enabled or not */\nUFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = \"Sample\")\nvoid SetIsEnabled(bool State);\n \n/** [Client] Called when bIsEnabled get replicated */\nUFUNCTION()\nvirtual void OnRep_IsEnabled();\n\n/** Indicate if the interaction is enabled **/\nUPROPERTY(replicated, ReplicatedUsing = OnRep_IsEnabled)\nbool bIsEnabled;\n```\n\nAnd the implementation in **SampleInteractableActor.cpp**:\n\n```cpp\nvoid ASampleInteractableActor::GetLifetimeReplicatedProps(TArray\u003cFLifetimeProperty\u003e\u0026 OutLifetimeProps) const\n{\n    Super::GetLifetimeReplicatedProps(OutLifetimeProps);\n    DOREPLIFETIME(ASampleInteractableActor, bIsEnabled);\n}\n\nvoid ASampleInteractableActor::SetIsEnabled_Implementation(bool State)\n{\n    bIsEnabled = State;\n\n    // If re-enabled, check currently overlapping actors\n    if (bIsEnabled)\n    {\n        OverlappingActors.Empty();\n        GetOverlappingActors(OverlappingActors, TSubclassOf\u003cASampleCharacter\u003e());\n        if (OverlappingActors.Num() == 0)\n        {\n            NotifyEndInteractable();\n        }\n        else\n        {\n            NotifyBeginInteractable();\n        }\n    }\n    else\n    {\n        NotifyEndInteractable();\n    }\n}\n\nvoid ASampleInteractableActor::OnRep_IsEnabled()\n{\n    SetIsEnabled(bIsEnabled);\n}\n```\n\nThe key is to use the **ReplicatedUsing** attribute to call a function when **bIsEnabled** get replicated.\nWhen the function is called, we simply detect overlapping Actors again.\n\nNow to also replicate the visual state of our chest (closed or opened), we mark its components as replicated:\n\n```cpp\nASampleChestActor::ASampleChestActor(const FObjectInitializer\u0026 ObjectInitializer)\n    : Super(ObjectInitializer)\n    , bIsOpened(false)\n    , ChestConfig(nullptr)\n{\n    ClosedComponent = ObjectInitializer.CreateDefaultSubobject\u003cUPaperSpriteComponent\u003e(this, ClosedSpriteComponentName);\n    ClosedComponent-\u003eSetCollisionEnabled(ECollisionEnabled::NoCollision);\n    ClosedComponent-\u003eSetIsReplicated(true);\n    ClosedComponent-\u003eSetupAttachment(RootComponent);\n\n    OpenedComponent = ObjectInitializer.CreateDefaultSubobject\u003cUPaperSpriteComponent\u003e(this, OpenedSpriteComponentName);\n    OpenedComponent-\u003eSetCollisionEnabled(ECollisionEnabled::NoCollision);\n    OpenedComponent-\u003eSetIsReplicated(true);\n    OpenedComponent-\u003eSetupAttachment(RootComponent);\n    bReplicates = true;\n}\n```\n\n## Credits\n\nSprites are coming from [The Spriters Resource](https://www.spriters-resource.com/).\n\nFont from [FontSpace](https://www.fontspace.com/atlantis-international-font-f31357).\n\n## License\n\nLicensed under the [MIT](LICENSE) License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnauja%2Fue4-chest2d-sample","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnauja%2Fue4-chest2d-sample","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnauja%2Fue4-chest2d-sample/lists"}