1. 스팀 플러그인 적용

2. https://partner.steamgames.com/ 스팀 SDK 다운로드
Steamworks - Build & Distribute Your Games on Steam
Steamworks is a set of tools and services that help game developers and publishers build their games and get the most out of distributing on Steam.
partner.steamgames.com
- redistributable_bin/steam_api.dll
- redistributable_bin/win64/steam_api64.dll
- redistributable_bin/steam_appid.txt
SDK에서 3개의 파일을
Binaries/Win64/ 또는 빌드된 실행 파일(.exe)이 들어간 폴더 옆에 배치
3. DefaultEngine.ini 파일에 다음 코드 추가

여기서 480은 테스트용 앱 ID이기 때문에
상용 게임이면 반드시 본인 앱 ID로 바꿔야 함
-> 여기까지 진행했을 때 스팀 오버레이가 나타나는 것을 확인
이후 세션을 생성하여 멀티플레이가 가능하도록 만들어 보려고 함
제가 생각했던 구현 방법은
1. https://vreue4.com/advanced-sessions-binaries 여기서 advanced-Sessions을 이용하여 BP를 통해 구현
2. C++을 통한 구현
2가지 중에 우선 C++을 통한 구현을 선택하여 진행
게임 인스턴스 -> 세션 생성, 참가, 세션 목록 찾기
#include "ZoneGameInstance.h"
#include "OnlineSubsystem.h"
#include "OnlineSubsystemSteam.h"
#include "OnlineSessionSettings.h"
#include "Interfaces/OnlineIdentityInterface.h"
#include "Kismet/GameplayStatics.h"
#include "OnlineMenuWidget.h"
#include "GameFramework/HUD.h"
#include "GameFramework/PlayerController.h"
void UZoneGameInstance::Init()
{
IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get();
if (Subsystem)
{
SessionInterface = Subsystem->GetSessionInterface();
}
}
void UZoneGameInstance::CreateRoom()
{
if (!SessionInterface.IsValid()) return;
IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get();
if (!Subsystem) return;
bool bIsUsingLAN = (Subsystem->GetSubsystemName().ToString() == "NULL");
FString UniqueSessionNameStr;
if (bIsUsingLAN)
{
UniqueSessionNameStr = FString::Printf(TEXT("LANSession_%d"), FDateTime::Now().GetTicks());
}
else
{
IOnlineIdentityPtr IdentityInterface = Subsystem->GetIdentityInterface();
if (!IdentityInterface.IsValid()) return;
TSharedPtr<const FUniqueNetId> UserId = IdentityInterface->GetUniquePlayerId(0);
if (!UserId.IsValid()) return;
UniqueSessionNameStr = FString::Printf(TEXT("SteamSession_%s"), *UserId->ToString());
}
CurrentSessionName = FName(*UniqueSessionNameStr);
FNamedOnlineSession* ExistingSession = SessionInterface->GetNamedSession(CurrentSessionName);
if (ExistingSession)
{
SessionInterface->OnDestroySessionCompleteDelegates.AddUObject(this, &UZoneGameInstance::OnSessionDestroyed);
SessionInterface->DestroySession(CurrentSessionName);
}
else
{
ActuallyCreateSession(bIsUsingLAN);
}
}
void UZoneGameInstance::OnCreateSessionComplete(FName SessionName, bool bWasSuccessful)
{
if (bWasSuccessful)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("Successfully!"));
UGameplayStatics::OpenLevel(GetWorld(), "LobbyMap", true, "listen");
}
}
void UZoneGameInstance::OnSessionDestroyed(FName DestroyedSessionName, bool bWasSuccessful)
{
if (DestroyedSessionName == CurrentSessionName)
{
IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get();
bool bIsUsingLAN = (Subsystem->GetSubsystemName().ToString() == "NULL");
ActuallyCreateSession(bIsUsingLAN);
}
}
void UZoneGameInstance::ActuallyCreateSession(bool bIsUsingLAN)
{
FOnlineSessionSettings SessionSettings;
SessionSettings.bIsLANMatch = bIsUsingLAN;
SessionSettings.NumPublicConnections = 4;
SessionSettings.bShouldAdvertise = true;
SessionSettings.bUsesPresence = !bIsUsingLAN;
bool bCreated = SessionInterface->CreateSession(0, CurrentSessionName, SessionSettings);
if (bCreated)
{
SessionInterface->OnCreateSessionCompleteDelegates.AddUObject(this, &UZoneGameInstance::OnCreateSessionComplete);
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, FString::Printf(TEXT("Session creation started: %s"), *CurrentSessionName.ToString()));
}
else
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Failed to start session creation"));
}
}
void UZoneGameInstance::Shutdown()
{
Super::Shutdown();
if (SessionInterface.IsValid())
{
FNamedOnlineSession* ExistingSession = SessionInterface->GetNamedSession(CurrentSessionName);
if (ExistingSession)
{
SessionInterface->DestroySession(CurrentSessionName);
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("Session destroyed on shutdown"));
}
}
}
void UZoneGameInstance::FindRooms()
{
CombinedSearchResults.Empty();
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Find"));
// LAN 세션 검색
LANSessionSearch = MakeShareable(new FOnlineSessionSearch());
LANSessionSearch->bIsLanQuery = true;
LANSessionSearch->MaxSearchResults = 10;
SessionInterface->OnFindSessionsCompleteDelegates.AddUObject(this, &UZoneGameInstance::OnLANFindSessionsComplete);
SessionInterface->FindSessions(0, LANSessionSearch.ToSharedRef());
// 온라인 세션 검색
OnlineSessionSearch = MakeShareable(new FOnlineSessionSearch());
OnlineSessionSearch->bIsLanQuery = false;
OnlineSessionSearch->MaxSearchResults = 10;
SessionInterface->OnFindSessionsCompleteDelegates.AddUObject(this, &UZoneGameInstance::OnOnlineFindSessionsComplete);
SessionInterface->FindSessions(0, OnlineSessionSearch.ToSharedRef());
}
void UZoneGameInstance::OnLANFindSessionsComplete(bool bWasSuccessful)
{
if (bWasSuccessful && LANSessionSearch.IsValid())
{
CombinedSearchResults.Append(LANSessionSearch->SearchResults);
}
CheckAndPopulateCombinedResults();
}
void UZoneGameInstance::OnOnlineFindSessionsComplete(bool bWasSuccessful)
{
if (bWasSuccessful && OnlineSessionSearch.IsValid())
{
CombinedSearchResults.Append(OnlineSessionSearch->SearchResults);
}
CheckAndPopulateCombinedResults();
}
void UZoneGameInstance::CheckAndPopulateCombinedResults()
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("check"));
// 둘 다 완료되었는지 확인
if (LANSessionSearch.IsValid() && OnlineSessionSearch.IsValid() &&
LANSessionSearch->SearchState == EOnlineAsyncTaskState::Done &&
OnlineSessionSearch->SearchState == EOnlineAsyncTaskState::Done)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow,
FString::Printf(TEXT("CombinedSearchResults count: %d"), CombinedSearchResults.Num()));
if (UWorld* World = GetWorld())
{
if (APlayerController* PC = World->GetFirstPlayerController())
{
if (UOnlineMenuWidget* MainMenu = Cast<UOnlineMenuWidget>(PC->GetHUD()))
{
MainMenu->PopulateSessionList(CombinedSearchResults);
}
}
}
}
}
void UZoneGameInstance::JoinRoom(const FOnlineSessionSearchResult& SearchResult)
{
SessionInterface->JoinSession(0, FName("MySession"), SearchResult);
SessionInterface->OnJoinSessionCompleteDelegates.AddUObject(this, &UZoneGameInstance::OnJoinSessionComplete);
}
void UZoneGameInstance::OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
FString Address;
if (SessionInterface->GetResolvedConnectString(SessionName, Address))
{
APlayerController* PC = GetFirstLocalPlayerController();
if (PC)
{
PC->ClientTravel(Address, ETravelType::TRAVEL_Absolute);
}
}
}
Lan이 아닌 스팀에서 스팀고유ID를 이용하여 세션을 생성하는 것은 테스트해보지 못함
세션 찾기시 목록들이 보이지 않음
메인 위젯 -> 버튼2개, 목록 리스트
#include "OnlineMenuWidget.h"
#include "SessionSlotWidget.h"
#include "OnlineSessionSettings.h"
#include "Components/ScrollBox.h"
#include "Components/Button.h"
#include "Kismet/GameplayStatics.h"
#include "ZoneGameInstance.h"
bool UOnlineMenuWidget::Initialize()
{
if (!Super::Initialize()) return false;
if (CreateRoomButton)
{
CreateRoomButton->OnClicked.AddDynamic(this, &UOnlineMenuWidget::OnCreateRoomClicked);
}
if (JoinRoomButton)
{
JoinRoomButton->OnClicked.AddDynamic(this, &UOnlineMenuWidget::OnJoinRoomClicked);
}
return true;
}
void UOnlineMenuWidget::PopulateSessionList(const TArray<FOnlineSessionSearchResult>& SearchResults)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow,
FString::Printf(TEXT("PopulateSessionList called with %d results"), SearchResults.Num()));
CurrentSearchResults = SearchResults;
SessionListScrollBox->ClearChildren();
for (int32 i = 0; i < SearchResults.Num(); ++i)
{
USessionSlotWidget* NewSlot = CreateWidget<USessionSlotWidget>(this, SessionSlotWidgetClass);
NewSlot->Setup(i, SearchResults[i].Session.OwningUserName);
NewSlot->OnJoinSessionClicked.AddDynamic(this, &UOnlineMenuWidget::OnSessionSlotClicked);
SessionListScrollBox->AddChild(NewSlot);
}
}
void UOnlineMenuWidget::OnSessionSlotClicked(int32 Index)
{
if (UZoneGameInstance* GI = Cast<UZoneGameInstance>(GetGameInstance()))
{
GI->JoinRoom(CurrentSearchResults[Index]);
}
}
void UOnlineMenuWidget::OnCreateRoomClicked()
{
if (UZoneGameInstance* GI = Cast<UZoneGameInstance>(UGameplayStatics::GetGameInstance(this)))
{
GI->CreateRoom();
}
}
void UOnlineMenuWidget::OnJoinRoomClicked()
{
if (UZoneGameInstance* GI = Cast<UZoneGameInstance>(UGameplayStatics::GetGameInstance(this)))
{
GI->FindRooms();
}
}
버튼 클릭시 실행되는 이벤트 배당
크게 2개의 C++ 클래스를 작성하였지만
advanced-Sessions을 이용하여 작성하신 분이 더 빠르게 구현해주셨기에
세션 생성및 참가의 C++구현은 프로젝트 완료 후 혼자 다시 해볼 예정입니다
'Unreal' 카테고리의 다른 글
| 스팀 친구 초대(실패) (1) | 2025.05.23 |
|---|---|
| 쿼터뷰 (0) | 2025.04.28 |
| FIFA 매칭 시스템 (0) | 2025.04.25 |
| 간단한 미니맵 만들기 (0) | 2025.04.11 |
| FPS / V-Sync (0) | 2025.04.02 |