언리얼 개발자용 가이드
개요
OVERDARE는 멀티플레이 게임의 제작부터 배포까지 한 번에 처리할 수 있는 강력한 게임 제작 플랫폼입니다. 언리얼 엔진5를 기반으로 제작되었지만, UGC 제작 환경에 최적화된 구조를 제공하여 언리얼5 대비 훨씬 낮은 학습 곡선과 크리에이터 친화적인 인터페이스, 그리고 간소화된 제작 파이프라인을 제공합니다.
이 문서는 언리얼 개발자들이 OVERDARE Studio에 빠르게 적응할 수 있도록 돕기 위해 작성된 문서입니다. OVERDARE를 이용하여 멀티플레이 게임 제작의 새로운 가능성을 탐험해 보세요!
인터페이스

Viewport
Viewport
Outliner
Level Browser
Details
Properties
Content Browser
X
Output Log
Output Log
언리얼에서는 Script와 머테리얼, 메쉬 같은 모든 에셋들이 Content Browser 패널에서 관리되지만, OVERDARE Studio에서는 Script도 Level Browser에서 관리되며, Mesh나 Image, Audio와 같은 외부에서 임포트한 에셋들은 Asset Manager를 통해 별도로 관리됩니다.
단축키 차이
Q
Ctrl + 1
Select Tool
W
Ctrl + 2
Move Tool
E
Ctrl + 3
Rotate Tool
R
Ctrl + 4
Scale Tool
Alt + P
F5
Play
Esc
Shift + F5
Stop
3D 월드 좌표계

언리얼과 OVERDARE는 서로 다른 축 구성을 사용하기 때문에 좌표계가 완전히 다릅니다. 이러한 차이로 인해 캐릭터와 카메라의 Forward 방향을 설정하거나, 이동 및 회전을 계산할 때 기준이 되는 축이 반대가 될 수 있으며, 동일한 연산이라도 결과가 상이하게 나타납니다. 따라서 두 좌표계를 변환하거나 연동할 때는 각 축의 방향 차이를 명확히 이해하고, 적절한 변환 과정을 거쳐야 합니다.
에셋 임포트

클라이언트 기반인 언리얼에서는 드래그&드롭으로 외부 에셋을 간편하게 임포트할 수 있습니다. 반면, OVERDARE Studio는 크리에이터를 위한 플랫폼으로 외부 에셋을 사용할 때 서버 업로드 과정을 거치도록 설계되었습니다. 이를 통해 크리에이터는 에셋을 쉽게 공유하고 게임 제작에 활용할 수 있습니다.
외부 에셋을 임포트하려면 OVERDARE Studio 상단 메뉴에서 Import 버튼을 클릭해야 합니다. 에셋을 Import하면 서버로 업로드되고, 서버에서 처리가 완료되면 Asset Drawer에 표시됩니다.
오브젝트 구조 차이

OVERDARE Studio는 언리얼과 유사한 오브젝트 기반 구조를 사용합니다. 각 오브젝트는 용도에 따라 역할이 결정된 타입 기반 구조로 설계되어 있으며, 기능이 오브젝트 타입 안에 미리 정의되어 있습니다. 따라서 크리에이터는 적절한 타입의 오브젝트를 선택한 뒤 속성을 조정하는 방식으로 기능을 제어하게 됩니다.
예를 들어, 언리얼에서 StaticMeshActor에 Simulate Physics 속성으로 물리 기능을 제공하는 것처럼, OVERDARE Studio에서는 Part 오브젝트에 Anchored 속성으로 물리 기능을 제공합니다.
Transform
언리얼에서는 Transform 속성을 통해 Position, Rotation, Scale 속성을 직접 제어하며, 로컬 및 월드 좌표계를 명확히 구분하여 사용할 수 있습니다.
반면 OVERDARE에서는 Position과 Orientation도 제공되지만, 일반적으로 CFrame을 통해 위치와 회전을 함께 다루며, 스케일은 별도로 Size 속성에서 관리됩니다. 특히 OVERDARE는 기본적으로 모든 위치와 회전 연산이 월드 좌표 기준으로 수행되며, 로컬 좌표 변환이 필요한 경우는 직접 계산해야 합니다.
이처럼 언리얼은 구성 요소별 속성이 명확히 분리되어 있고, OVERDARE는 위치와 회전을 하나의 CFrame으로 묶어 처리한다는 점에서 구조적인 차이를 보입니다.
자세히 알아보기
좌표계Collision
언리얼에서는 StaticMeshActor의 Collision Preset과 Collision Response를 통해 매우 세밀한 충돌 동작을 설정할 수 있습니다. 각 오브젝트는 여러 충돌 채널(Channel)에 대해 Block/Overlap/Ignore 등 서로 다른 반응 타입을 가질 수 있으며, 이러한 규칙들이 매트릭스 형태로 구성되어 높은 수준의 충돌 제어가 가능하지만, 구조가 복잡해 설정 난이도가 높은 편입니다.
반면, OVERDARE에서는 모든 Part의 CanCollide 속성을 통해 충돌 여부를 제어합니다. Hit이나 Overlap 같은 충돌 이벤트는 모두 Touched 이벤트로 처리되며, CanCollide가 비활성화 상태이면 Overlap 이벤트처럼 동작합니다. OVERDARE에서도 충돌 그룹을 사용하여 충돌 필터링을 설정할 수 있습니다.
자세히 알아보기
Collision GroupsPhysics
언리얼에서는 StaticMeshActor의 Simulate Physics 속성을 활성화함으로써 물리 엔진(Chaos)의 영향을 받도록 설정합니다. 물리 제어는 AddForce, AddImpulse, AddTorque 같은 함수 호출을 통해 힘·충격·회전력을 기반으로 자연스러운 물리 제어가 가능합니다.
OVERDARE에서는 Part의 Anchored 속성을 비활성화한 오브젝트는 중력, 충돌, 마찰 등 물리 영향을 받으며, 물리 기반의 동작 제어는 LinearVelocity, AngularVelocity, VectorForce 등과 같은 전용 물리 객체를 통해 구현됩니다.
자세히 알아보기
물리Camera
언리얼에서는 씬 안에 여러 CameraActor를 배치해 두고, 플레이어가 바라보는 실제 시점은 PlayerCameraManager를 통해 결정됩니다. 특히 여러 개의 카메라를 미리 배치해두고 카메라를 전환하는 방식으로 시점을 변경할 수 있습니다.
반면 OVERDARE에서는 Workspace.CurrentCamera를 통해 현재 활성화된 단일 카메라를 제어하며, 시스템적으로 하나의 카메라만 존재하고 항상 활성 상태로 유지됩니다. 카메라는 기본적으로 플레이어의 Humanoid를 따라가지만, CameraType 속성을 Scriptable로 변경하면 직접 위치나 회전을 제어할 수 있습니다. 이때 언리얼처럼 여러 카메라의 전환 구조가 아닌, CurrentCamera의 CFrame, FieldOfView 등의 속성을 직접 변경하는 방식으로 시점을 변경합니다.
자세히 알아보기
카메라Niaraga
언리얼에서는 Niagara 시스템을 사용해 고급 파티클 및 VFX를 구성합니다. Niagara System 안에서 여러 Emitter와 Spawn, Update, Render, Collision 등 다양한 모듈을 조합해 복잡한 이펙트를 제작할 수 있습니다.
반면 OVERDARE에서는 ParticleEmitter, Beam, Trail 등 이펙트 객체를 Part에 부착해서 사용하며, 각 이펙트 타입은 별도의 객체로 구분되어 있고 설정 가능한 속성도 제한적입니다. 예를 들어 ParticleEmitter는 속도, 방향, 색상, 생존 시간 등 일부 속성만 설정할 수 있으며, 언리얼처럼 다중 모듈을 조합하는 방식은 지원되지 않습니다. 또한 파티클은 별도의 위치 설정 없이 파티클이 부착된 Part를 기준으로 방출됩니다.
자세히 알아보기
VFXUI
언리얼에서는 UMG(User Widget) 기반의 위젯 시스템을 사용하며, Widget Blueprint로 제작됩니다. UI 구성은 Canvas Panel, Vertical/Horizontal Box, Size Box 등 다양한 레이아웃 패널을 조합해 배치하며, 각 위젯의 위치와 크기는 앵커·정렬·오프셋으로 조정합니다.
반면 OVERDARE에서 모든 UI 요소는 ScreenGui, SurfaceGui 등으로 구성됩니다. ScreenGui는 HUD나 메뉴처럼 화면에 고정된 UI를 구현하며, UI 요소는 픽셀 값과 비율(스케일)로 구성된 UDim2 값을 통해 배치됩니다.
자세히 알아보기
GUI폰
언리얼에서는 게임 내에서 조작 가능한 캐릭터를 Pawn 또는 Character 클래스를 통해 구현합니다. Pawn은 플레이어 입력을 받을 수 있는 최소 단위의 조종 가능한 객체이며, Character는 여기에 CapsuleCollider, MovementComponent, 애니메이션, 이동 로직이 기본 포함된 확장형 클래스입니다.
반면 오버데어에서는 조작 가능한 캐릭터 개념이 Humanoid를 포함한 Model로 표현됩니다. Humanoid는 이동, 점프, 애니메이션, 체력 시스템 등 캐릭터가 갖는 대부분의 로직을 엔진 레벨에서 제공하며, 플레이어는 이 Humanoid가 포함된 캐릭터 모델을 자동으로 소유하게 됩니다. 이동 입력이나 카메라 제어 역시 기본적으로 Humanoid 시스템이 처리하며, 크리에이터는 Part를 추가하거나 Humanoid 속성을 수정하는 방식으로 캐릭터 동작을 확장합니다.
자세히 알아보기
캐릭터액터
언리얼에서는 게임 세계를 구성하는 기본 단위가 Actor이며, 월드에 배치되는 모든 오브젝트는 Actor를 기반으로 확장됩니다. Actor는 Transform(위치·회전·스케일)을 가지며, 여러 컴포넌트를 조합하여 기능을 구성하는 구조로 설계되어 있습니다.
반면 오버데어에서는 게임 오브젝트의 기본 단위가 Part 또는 Model로 구성됩니다. Part는 자체적으로 물리, 충돌, 렌더링 속성을 모두 포함한 기본 오브젝트이며, 언리얼처럼 컴포넌트를 조합하는 개념보다는 속성이 내장된 단일 객체에 가깝습니다. 여러 개의 Part를 묶어 기능을 구성할 때는 Model을 사용하며, 스크립트나 Attachment, Constraint 등을 추가해 동작을 확장합니다.
Level Sequencer
언리얼에서는 Level Sequencer를 사용해 컷신, 카메라 연출, 애니메이션, 오브젝트 이동, 조명 변화 등 씬 전체의 타임라인 기반 연출을 구성할 수 있습니다. Sequencer는 여러 트랙을 조합해 Actor의 위치·회전, 머티리얼 파라미터, 카메라 변환, 사운드 재생까지 광범위하게 제어할 수 있으며, 복수의 오브젝트를 하나의 타임라인에서 동기화하는 시네마틱 제작 도구로 활용됩니다.
반면, OVERDARE에서는 Animation Editor를 통해 캐릭터 애니메이션만 제작 가능하며, UI나 기타 오브젝트에 대한 에디터 기반 애니메이션 제작은 아직 지원되지 않습니다. 또한, Animator 상태 머신이 존재하지 않으며, 모든 애니메이션 재생과 제어는 스크립트를 통해 직접 구현해야 합니다. 예를 들어, 캐릭터의 이동, 공격, 감정 표현 등은 코드 기반으로 애니메이션을 수동 재생하거나 상태 관리를 로직으로 처리해야 합니다.
자세히 알아보기
애니메이션 에디터캐릭터 애니메이션스크립트
OVERDARE Studio는 게임 개발 언어로 Luau 스크립트를 제공합니다. Luau는 경량화된 스크립트 언어로, 배우기 쉬운 간결한 문법과 빠른 실행 속도를 자랑하며 높은 유연성을 제공합니다. 이러한 특성 덕분에 Luau는 C# 스크립트보다 접근성과 생산성이 뛰어나며, 초보 개발자부터 숙련된 개발자까지 누구나 쉽게 활용할 수 있습니다.
특징
자료형 명시
O
X
한정자 종류
private, public, static 등
local, global
객체 지향(OOP)
클래스, 인터페이스, 상속, 다형성 등을 통해 객체 지향 프로그래밍 지원
객체 지향을 직접적으로 지원하지 않으며, Metatable로 유사 객체 구현
코드 구조
.h / .cpp 파일 구조, 클래스 단위 설계 필수
단순한 함수 기반 구조로 클래스가 없으며, Table을 통해 데이터와 함수를 구조화
함수
클래스 멤버 함수 중심
함수를 변수처럼 사용 가능 (First-class function)
Collection
TArray, TMap, TSet 등 언리얼 컨테이너 사용
Table
switch문 유무
O
X
주석
//
--
여러줄 주석
/*와 */
--[[와 ]]--
세미콜론
필수
생략
코드의 흐름
루아 스크립트는 동적 타입 언어로, 스크립트가 위부터 순차적으로 실행됩니다. 전방 참조가 지원되지 않으며, 참조하려는 함수나 변수가 먼저 정의되어 있어야 합니다. 이러한 특성은 Lua의 설계가 간결성과 런타임 성능을 중시하기 때문입니다.
PrintText("Hello, Lua Script!") -- 에러 발생 (전방 참조 불가)
local function PrintText(message)
print(message)
end
PrintText("Hello, Lua Script!") -- 동작 가능다른 스크립트의 변수/함수 호출
변수나 함수를 global으로 선언하면 어디서든 접근할 수 있습니다. 단, global 변수는 어디서든 수정 가능하므로, 코드의 안정성이 떨어질 수 있습니다.
_G.GlobalText = "Hello, World!" -- 전역 변수 선언
-- 전역 함수 선언
function _G.GlobalFunction()
print("This is a global function!")
endprint(_G.GlobalText) -- 전역 변수 접근
_G.GlobalFunction() -- 전역 함수 호출모듈 스크립트를 이용하여 테이블에 변수와 함수를 담아서 반환하는 방식으로 외부에서 사용할 수 있습니다. 이는 전역 변수를 사용하는 것보다 안전하고, 코드의 구조화를 돕습니다.
local SomeModule = {} -- 테이블 생성
-- 모듈 변수
SomeModule.Text = "Hello from module!"
-- 모듈 함수
function SomeModule:Function()
print("This is a function inside a module!")
end
return SomeModule -- 테이블 반환local ReplicatedStorage = game:GetService("ReplicatedStorage")
local SomeModule = require(ReplicatedStorage.SomeModule) -- 모듈 로드
print(SomeModule.Text) -- 모듈 변수 접근
SomeModule.Function() -- 모듈 함수 호출또는 BindableEvent를 사용하여 서버와 서버, 클라이언트와 클라이언트처럼 같은 환경에 있는 스크립트끼리 통신을 처리할 수 있습니다.
자세히 알아보기
모듈 스크립트BindableEvent실행 위치와 실행 순서
OVERDARE는 멀티플레이 환경을 기반으로 설계되었기 때문에, 스크립트의 위치에 따라 용도와 실행 여부가 달라집니다. 예를 들어, 카메라나 GUI와 같은 클라이언트 전용 기능은 클라이언트에서만 동작하며, 게임 로직 처리나 오브젝트 이동 등 동기화가 필요한 기능은 서버에서 처리해야 합니다. 이러한 구조는 클라이언트와 서버 간 역할을 명확히 분리하여 효율적이고 안정적인 멀티플레이 동작을 보장합니다.
반면, 언리얼은 이러한 위치 기반 실행 구조를 사용하지 않습니다. 언리얼의 로직은 주로 이벤트 기반(Event-driven)으로 동작하며, BeginPlay, Input 이벤트, Delegate, Timer 등 개발자가 호출하는 순서나 이벤트 트리거에 따라 실행 흐름이 결정됩니다.
자세히 알아보기
스크립트 개요서버와 클라이언트간 통신
OVERDARE는 멀티플레이 게임 기반으로 설계되어 서버에서 실행되는 Script와 클라이언트에서 실행되는 LocalScript를 조합하여 게임이 구현됩니다. 서버와 클라이언트 간의 통신은 RemoteEvent를 사용하여 처리할 수 있습니다.
자세히 알아보기
서버-클라 통신스크립트 기능 비교
print
#include "Example.h"
void AExample::BeginPlay()
{
Super::BeginPlay();
UE_LOG(LogTemp, Warning, TEXT("Hello, World!"));
}print("Hello, World!")Start Event
#include "Example.h"
void AExample::BeginPlay()
{
Super::BeginPlay();
UE_LOG(LogTemp, Warning, TEXT("Start!"));
}print("Start!")Update Event
#include "Example.h"
void AExample::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
Timer += DeltaTime;
}local RunService = game:GetService("RunService")
local Timer = 0
local function UpdateEvent(deltaTime)
Timer = Timer + deltaTime
end
RunService.Heartbeat:Connect(UpdateEvent)오브젝트 참조
#include "Example.h"
#include "EngineUtils.h"
void AExample::BeginPlay()
{
Super::BeginPlay();
AActor* Found = nullptr;
for (TActorIterator<AActor> It(GetWorld()); It; ++It)
{
if (It->GetName() == TEXT("Orc"))
{
Found = *It;
break;
}
}
}local Workspace = game:GetService("Workspace")
local Orc = Workspace.Monster.OrcTransform
#include "Example.h"
void AExample::BeginPlay()
{
Super::BeginPlay();
SetActorLocation(FVector(500.f, 0.f, 0.f));
SetActorRotation(FRotator(0.f, 90.f, 0.f));
SetActorScale3D(FVector(0.5f, 0.5f, 0.5f));
}local Part = script.Parent
Part.Position = Vector3.new(500, 0, 0)
Part.Orientation = Vector3.new(0, 90, 0)
Part.Size = Vector3.new(50, 50, 50)충돌 이벤트
#include "Example.h"
#include "Components/BoxComponent.h"
AExample::AExample()
{
UBoxComponent* Box = CreateDefaultSubobject<UBoxComponent>(TEXT("Box"));
RootComponent = Box;
Box->OnComponentHit.AddDynamic(this, &AExample::OnHit);
Box->OnComponentBeginOverlap.AddDynamic(this, &AExample::OnBeginOverlap);
Box->OnComponentEndOverlap.AddDynamic(this, &AExample::OnEndOverlap);
}
void AExample::OnHit(UPrimitiveComponent*, AActor* OtherActor, UPrimitiveComponent*, FVector, const FHitResult&)
{
if (OtherActor)
{
UE_LOG(LogTemp, Warning, TEXT("Collision started with : %s"), *OtherActor->GetName());
}
}
void AExample::OnBeginOverlap(UPrimitiveComponent*, AActor* OtherActor, UPrimitiveComponent*, int32, bool, const FHitResult&)
{
if (OtherActor)
{
UE_LOG(LogTemp, Warning, TEXT("Collision ongoing with : %s"), *OtherActor->GetName());
}
}
void AExample::OnEndOverlap(UPrimitiveComponent*, AActor* OtherActor, UPrimitiveComponent*, int32)
{
if (OtherActor)
{
UE_LOG(LogTemp, Warning, TEXT("Collision ended with : %s"), *OtherActor->GetName());
}
}local Part = script.Parent
local function onTouched(otherPart)
print(Part.Name, "Touched :", otherPart.Name)
end
Part.Touched:Connect(onTouched)
local function onTouchEnded(otherPart)
print(Part.Name, "Touch Ended :", otherPart.Name)
end
Part.TouchEnded:Connect(onTouchEnded)Create & Destroy
#include "Example.h"
#include "Engine/World.h"
void AExample::BeginPlay()
{
Super::BeginPlay();
if (!PrefabClass) return;
FActorSpawnParameters Params;
AActor* NewObj = GetWorld()->SpawnActor<AActor>(PrefabClass, FVector(300.f, 0.f, 0.f), FRotator::ZeroRotator, Params);
if (!NewObj) return;
NewObj->SetActorLabel(TEXT("NewObject"));
NewObj->AttachToActor(this, FAttachmentTransformRules::KeepWorldTransform);
NewObj->Destroy();
}local Workspace = game:GetService("Workspace")
local Part = Workspace.Part
local ClonedPart = Part:Clone()
ClonedPart.name = "NewPart"
ClonedPart.Parent = Part
ClonedPart.Position = Vector3.new(300, 0, 0)
Part:Destroy()SetTimer
#include "Example.h"
#include "TimerManager.h"
void AExample::BeginPlay()
{
Super::BeginPlay();
GetWorld()->GetTimerManager().SetTimer(
TimerHandle,
this,
&AExample::OnTimer,
2.0f,
false
);
}
void AExample::OnTimer()
{
UE_LOG(LogTemp, Warning, TEXT("Hello, World!"));
}local function SomeCoroutine()
wait(2)
print("Hello, World!")
end
local co = coroutine.create(SomeCoroutine)
coroutine.resume(co) 참고 자료
OVERDARE Studio에서 제공하는 스크립트 기능을 확인하려면 아래 문서를 참고하세요.
📚API Reference개발지원
디스코드 OVERDARE Creator Community Server에 참여하여 개발 관련 문의나 정보 공유, 커뮤니티 활동 등 게임 제작에 적극 활용하세요!
Last updated