멋쟁이사자처럼 X 넥슨 MOD Suppoters Hackathon 3주차 회고

ssunn·2022년 5월 28일
0

이번 시간에는 스크립트와 네트워크에 대해 배웠다.
전반적으로 UI보다는 스크립트 위주였다. 내용 자체는 UI만 다루던 것보다 어려웠지만 코딩은 쉬지 않고 계속 해왔기 때문에 오히려 이해하기는 좀 더 쉬웠던 것 같다.

스크립트의 이해

스크립트의 언어는 Lua에 MOD만의 추가된 형태를 띄고 있다.

script 추가

MyDesk > 마우스 우클릭 > Create Scripts > Create Script

스크립트 사용 예시

Lua 스크립트 아주 기본적인 문법

log("Hello World!")

--변수 선언
local number = 1 
local sum = 0

log(sum)

--함수
void Sum()
{
	--반복문 선언
	for count = 1, 10, 1 do  --1부터 10까지 1씩 증가
	--조건문
		if count%2 == 0 then
			sum = sum + count
		end
	end
}

주로 사용하는 스크립트

Create Component

컴포넌트 스크립트는 특정 객체에 추가를 해야 스크립트가 돌아감. 또한 콘솔창에 로그를 찍게 되면 스크립트를 추가한 객체의 수만큼 로그가 찍힘.
Property 설정 할 수 있음. 이 때 설정한 property는 property창에 노출됨.
함수에서 내가 속한 component의 property 또는 함수를 가져다 쓸 때 self.컴포넌트명으로 컴포넌트를 불러와 사용할 수 있음.

자주 쓰는 함수
OnBeginPlay : 컴포넌트가 초기화가 될 때(처음 시작할 때) 처리하는 로직
OnUpdate : 프레임마다 주기적으로 불리는 함수
OnEndPlay : 끝날 때 처리하는 로직

Create Logic
로직은 따로 어떤 객체에 추가를 해주지 않아도 프로그램상에 하나만 존재하기 때문에 콘솔창에 결과가 뜸.

void OnBeginPlay()
{
	--현재 컴포넌트의 엔티티의 transformComponent의 position 값을 갖고옴
	local myEntityPosition = self.Entity.TransformComponent.Position
	log(myEntityPosition)

	--컴포넌트가 적용된 엔티티의 위치 변경
	for count=1, 10, 1 do
		myEntityPosition.x = myEntityPosition.x + 0.5
		myEntityPosition.y = myEntityPosition.y + 0.5
		wait(1)
	end

	--컴포넌트가 적용된 엔티티의 크기 변경
	self.Entity.TransformComponent.Scale.x = 2
	self.Entity.TransformComponent.Scale.y = 2
}

네트워크의 이해 - Property와 Function의 실행 제어

MOD에서는 기본적으로 서버-클라이언트 모델을 지원하고 있다.

클라이언트 : 접속된 각각의 유저
서버 : 클라이언트의 요청을 받는 서버
각 클라이언트는 모두 서버와 연결되어 있다.

Entity를 생성하게 되면 서버와 클라이언트 모두에 엔티티가 생성된다.
서버와 클라이언트가 네트워크로 연결이 되어 있긴 하지만 각각의 엔티티(컴포넌트)는 다른 객체이다. 따라서 어디서 설정을 하느냐에 따라 설정값이 서로 달라질 수 있고 이로 인해 동기화 문제가 발생할 수 있다.

여기서 동기화란?
어느 한 쪽의 값이 달라졌을 때 양쪽의 값을 모두 바꾸는 행동을 취하는 것.

고전적인 방법으로는 어느 한쪽이 달라졌을 때 다른 한쪽 또한 바꿔주는 행동을 일일히 수행해야하는데 이 과정이 상당히 번거롭다.
따라서 MOD에서는 이를 쉽게 해결하기 위해 많은 고민을 거쳤다고 한다.

동기화 방향

서버는 하나이지만 클라이언트는 여러 개일 수 있다.
또한 서버에서 특정 프로퍼티의 값을 바꾸면 그 프로퍼티를 갖고 있는 클라이언트의 프로퍼티 또한 전부 값이 변경된다.
반면 클라이언트에서는 독자적으로 값을 바꿔도 다른 클라이언트나 서버에 영향을 미치지 않는다.
따라서 동기화는 서버 → 클라이언트 단방향으로 진행된다.

Property 실행 제어

기본적으로 property는 동기화 옵션을 설정할 수 있게 해준다.

클릭해서 변경 가능.

[Sync] : 동기화가 되는 property
[None] : 동기화 되지 않는 Property

동기화 옵션을 바꿨을 때 내부에서 어떤 일이 일어나는지는 몰라도 된다.
any, table 등 동기화가 지원되지 않는 type도 있다.
변수 앞의 타입을 클릭하면 설정할 수 있는 변수 타입들이 나온다.

Function 실행 제어

사용하는 로직 대부분은 서버 기반으로 돌아가게 되어있음
서버에서 로직의 흐름을 관리하고 중간중간 변경된 사항을 각 클라이언트에 전달.
값이 변경되었을 때 변경사항을 클라이언트가 확인하고 싶을 때 OnSyncProperty로 확인할 수 있다 → 이 함수는 값을 받는 입장이기 때문에 Client only

함수는 호출한 쪽이 서버인지 클라이언트인지에 따라 동작하는 공간이 다름.
따라서 실행 제어를 통해 공간을 활성화 시켜주어야함.
공간을 활성화하면 해당 function이 특정한 속성을 가지게 됨.
공간 활성화로는 다음과 같이 5가지 속성이 있으며, 이렇게 함수를 요청하는 과정을 실행제어를 통해 한번에 보낸다.

Client : 서버가 클라이언트의 함수를 호출하면 서버와 연결된 클라이언트들에게 함수 호출을 요청함. 클라이언트에 있는 함수가 실행됨.
ClientOnly : 클라이언트에만 불릴 수 있음. 서버에서는 부를 수 없음.
Server: 클라이언트 쪽에서 서버의 함수를 호출하면 서버 쪽을 신호를 보내 서버 안에서 함수가 실행되게 함.
ServerOnly : 서버에만 불릴 수 있음. 클라이언트에서는 부를 수 없음. 대부분의 로직는 서버에서 서버 기준으로 짜게 됨 → 주로 서버 위주의 행위를 많이 하게 됨
Multicast : 서버와 클라이언트 양쪽 모두 실행을 함.

[코드 실행 예시]
1. 서버가 서버 메시지를 뿌림
2. 클라이언트에서 메시지를 뿌려달라고 요청 후 바로 다음 실행.
3. 클라이언트에 도착하면 클라이언트가 메시지를 받은 순간 요청을 실행.
4. 서버에서 요청했을 때 클라이언트까지 요청이 도달하는 시간이 서버가 다음 코드를 실행하는 시간보다 짧으면 클라이언트가 먼저 찍힘. 더 길면 다음 코드인 end가 먼저 찍히고 클라이언트가 찍힘.



wait(2)을 써주지 않았을 때 CLIENT 뒤에 파라미터로 받은 문자열이 출력되지 않는 현상 발생.

⇒ 서버에서 엔티티를 생성하고 클라이언트에도 엔티티가 생성되기까지 시간이 걸림. 클라이언트에서 엔티티가 생성되는 도중에 서버에서 엔티티의 값을 넣어 전송하면 클라이언트에서 값을 받지 못하는 현상이 발생함(타이밍 이슈)

정리

서버가 흐름을 관장하고 클라이언트는 서버에서 받아온 것을 보여준다.
서버에 엔티티가 생성이 됐을 때 그 엔티티들이 각 클라이언트 내에도 생성이 된다.
특정 클라이언트 안에서 생성된 엔티티는 그 클라이언트 안에서만 생성된다.

[대표적인 예]
UI
입력
effect (보통 서버로 제어. 특정 클라이언트에서만 연출하고 싶을 땐 로컬 엔티티를 사용)
최적화 - 서버에 엔티티가 너무 많을 때 비용 때문에 최적화 시 로컬 엔티티를 사용함

profile
BackEnd Developer

0개의 댓글