텐센트 PUER TypeScript 라이브러리
소개의 말 : 중국에서 게임개발을 하면서 경험?한 바로는 콘텐트 프로그래밍은 주로 스크립트 기반에서 개발하는 경향을 띄고 있다고 말 할 수 있겠습니다. 2022년 출시 했던 드레곤헤어 침묵의 신 프로젝트에서도 거의 모든 콘텐트 프로그래밍은 파이썬이었고 Wrapper 는 XPython 이었죠. 엔진팀에서는 XPython 코어를 더 합리적이고 빠르게 처리되는 부분을 담당했었고 클라이언트팀은 툴셋을 제외하고는 모든걸 파이썬으로 프로그래밍 했었습니다. 2016년 출시 했던 레볼루션 이라는 오픈월드 MMORPG 역시 콘텐트는 모두 파이썬이었구요. 상해에서는 Lua 였고... 엔진은 유니티 엔진 또는 언리얼 엔진이거나 자체개발 엔진이었는데요.. 텐센트에서 공개 하고 있는 TypeScript 라이브러리도 관심있게 볼 만 합니다. 일단 콘텐트 대부분을 파이썬이나 타입스크립트등으로 처리하는 이유에 대해서 왜 그렇게 하는지 물어봤는데요.
중국은 IOS 부분에 대한 중요도가 꽤 높은 편이기 때문에 즉각 패치에 대한 회사 정책으로 이런 스크립트로 주로 개발을 한다고 합니다. 심사를 통하지 않고 패치 하는 것이 서비스 측면에서 매우 중요하다고 생각 한다고 해요. 아마도 스마일게이트의 크로스파이어의 성공 역사에 대해서 다들 아실거에요. 텐센트는 유저 참여를 엄청나게 중요하게 생각하고 있는 회사입니다. 왜냐면 퍼블리셔를 하면서 유저의 요구를 즉각 반영한다는 정책이 지금의 텐센트를 있게 만들었거든요. 텐센트 개발 스튜디오는 잘 몰라도 퍼블리싱 사업을 담당하는 텐센트 그룹 내 회사는 그룹 내에서도 꽤 입지가 높구요. 책 한권에 달하는 퍼블리싱 기술 정책 가이드북도 엄청 나지만... 자사 개발 게임도 5% 이내 기술통과를 하지 못하면 영영 서비스 할 수 없는 ... 서비스 허들 하나만큼은 한국 퍼블리셔 빰싸다구 10년 후려 칠 정도의 하드코어 수준이죠.
중국의 초대형 게임사인 넷이즈와 텐센트 모두 그러한 부분을 비슷한것 같습니다. 저는 넷이즈에서는 다양한 게임개발에 참여 하면서 경험을 해 봤지만 아쉽게 텐센트에서의 경험은 없네요. 저도 조만간 언리얼엔진으로 작은 게임을 만들려고 하는데요. 타입스크립트로 해 보려고 해서 이렇게 공유 해 볼 기회를 갖습니다.
깃허브
https://github.com/Tencent/puerts
설치
소스코드 설치 부분
1. 리포지토리를 복제합니다:
git clone https://github.com/Tencent/puerts.git
2. 퓨어티에스 디렉터리를 프로젝트의 플러그인 디렉터리에 puerts/unreal에서 복사합니다. 언리얼 데모를 참조하여 안내를 받을 수 있습니다.
3. Download V8:
4. 다운로드한 V8을 YourProject/Plugins/Puerts/ThirdParty에 압축을 풀고 다운로드한 버전에 따라 JsEnv.build.cs의 UseV8Version 설정을 변경합니다.
릴리스 패키지 설치 방법
릴리스 페이지로 이동하여 필요한 버전을 찾으세요. 이 페이지에는 Unity용 릴리스 패키지도 포함되어 있으며, 언리얼 엔진 버전은 "Unreal"로 시작됩니다.
사용 중인 UE 버전과 일치하는 패키지를 다운로드하여 YourProject/Plugins에 압축을 풉니다. V8 라이브러리는 이미 포함되어 있습니다.
Notes
Mac 사용자: '휴지통으로 이동' 문제가 발생하는 경우 다음 명령을 실행하세요:
cd Plugins/Puerts/ThirdParty
find . -name "*.dylib" | xargs sudo xattr -r -d com.apple.quarantine
블루프린트 전용 프로젝트: "플러그인 'Puerts' 모듈을 찾을 수 없어 로드하지 못했습니다."라는 오류가 표시되는 경우 순수 블루프린트 프로젝트는 플러그인을 자동으로 컴파일하지 않기 때문입니다. Puerts에는 C++ 소스 코드가 포함되어 있으므로 C++ 클래스를 추가하여 블루프린트 프로젝트를 C++ 프로젝트로 변환해야 합니다. 또는 컴파일 도중 Puerts를 포함하여 UE 엔진을 컴파일할 수도 있습니다.
가상 머신 전환
Puerts는 여러 스크립트 백엔드를 지원합니다: V8, quickjs, nodejs.
- V8: 깔끔한 ECMAScript 구현을 제공합니다.
- Quickjs: 패키지 크기 요구 사항이 엄격한 시나리오에 적합합니다.
- Nodejs: V8 버전보다 더 많은 npm 모듈을 지원하지만 패키지 크기가 더 커집니다.
Quickjs 백엔드 다운로드
Nodejs 백엔드 다운로드
다운로드한 백엔드를 다음 위치에 압축을 풉니다. YourProject/Plugins/Puerts/ThirdParty.
수정 JsEnv.Build.cs:
- Quickjs 백엔드를 사용하려면 UseQuickjs를 true로 설정합니다.
- Nodejs 백엔드를 사용하도록 UseNodejs를 설정합니다.
Puerts - 언리얼 엔진 사용자 메뉴얼
아래는 인칸타 게임즈에서 원본 문서를 번역한 버전입니다. 번역은 주로 구글 번역을 사용했지만, 구글 번역이 놓친 부분을 이해하기 위해 수작업으로 수정했습니다.
다음 두 가지 방법 중 하나로 사용을 시작하세요.
방법 1: 가상 머신을 직접 시작하기
- 필요에 따라 하나 이상의 가상 머신을 시작합니다(예: GameInstance)여러 가상 머신이 시작되면 이러한 가상 머신은 서로 격리됩니다
- 스크립트 로직의 진입점으로 Start 함수를 사용하여 스크립트 시작( 메인 함수와 유사.)
- 시작은 스크립트 획득을 위한 매개변수로 일부 데이터에 전달될 수 있습니다.
- 가상 머신의 스크립트는 Puer 규칙(이후 장)과 엔진에 따라 대화형일 수 있습니다.
UCLASS()
class PUERTS_UNREAL_DEMO_API UTsGameInstance : public UGameInstance
{
public:
TSharedPtr<puerts::FJsEnv> JsEnv;
virtual void OnStart() override {
JsEnv = MakeShared<puerts::FJsEnv>();
TArray<TPair<FString, UObject*>> Arguments;
Arguments.Add(TPair<FString, UObject*>(TEXT("GameInstance"), this));
JsEnv->Start("QuickStart", Arguments);
}
virtual void Shutdown() override {
JsEnv.Reset();
}
};
방법 2: 자동 바인딩 모드
이 모드의 장점은 UE 에디터에서 클래스를 식별할 수 있다는 점입니다.
- 명령줄에서 플러그인/푸어 디렉터리로 들어가서 다음 명령을 실행하여 이 모드의 열기 및 종속성 설치를 완료합니다.
node enable_puerts_module.js
예를 들어, 그런 수업이 있습니다:
import * as UE from 'ue'
class TS_Player extends UE.Character {
}
export default TS_Player;
그런 다음 UE 에디터에서 선택할 수 있습니다.
- UE 로 식별 가능, 생성자 지원, 블루프린트 오버라이드 메서드 오버라이드 지원, 축 맵 축 지원, 액션 이벤트 지원, RPC 지원
class TS_Player extends UE.Character {
FpsCamera: UE.CameraComponent;
//...
Constructor() {
let FpsCamera = this.CreateDefaultSubobjectGeneric<UE.CameraComponent>("FpsCamera", UE.CameraComponent.StaticClass());
FpsCamera.SetupAttachment(this.CapsuleComponent, "FpsCamera");
//...
}
MoveForward(axisValue: number): void {
this.AddMovementInput(this.GetActorForwardVector(), axisValue, false);
}
MoveRight(axisValue: number): void {
this.AddMovementInput(this.GetActorRightVector(), axisValue, false);
}
ReceiveBeginPlay(): void {
//...
}
//...
}
두 모드 간의 관계
- 자동 바인딩 모드는 자체 시작 가상 머신 모드를 기반으로 하며, PuertsModule은 가상 머신을 시작한 다음 자동 바인딩 블루프린트, 코드 증분 컴파일, 증분 새로 고침 기능을 수행합니다.
- 둘 다 공존할 수 있지만 수동으로 시작한 가상 머신과 퓨어티에스 모듈의 가상 머신은 동일하지 않으며 서로 격리되어 있다는 점을 염두에 두어야 합니다.
자동 바인딩 모드
포멧
타입스크립트는 아래 세 가지 사항을 충족하며, UE 에디터에서 클래스를 식별할 수 있습니다.
- 이 클래스는 UE의 클래스 또는 다른 상속 UE의 클래스를 상속합니다.
- 파일 이름에서. ts 접미사를 제거합니다.
- 이 클래스 내보내기를 기본값으로 설정합니다.
생성자
표준 타입스크립트 생성자와 달리, 자동 바인딩 모드는 주로 UE 초기화 생성자.
class TsTestActor extends UE.Actor {
tickCount: number;
// Note that inheriting the JS class of the UE class, the constructor must be capitalized
Constructor() {
this.PrimaryActorTick.bCanEverTick = true;
tickCount = 0;
}
}
- 생성자에서 호출해야 하는 CreateDefaultSubobject와 같은 일부 UE는 생성자에서 호출할 수 있습니다.
- 클래스가 생성자를 정의하는 경우, 멤버 변수의 초기화는 타입스크립트에 의해 인수되며, UE 에디터에서 설정한 값은 유효하지 않습니다.
- 생성자가 정의되지 않은 경우 UE 에디터에서 멤버 변수를 수동으로 설정할 수 있도록 지원합니다.
- 생성자는 UE 멤버를 초기화할 때만 UE가 호출하는 생성자입니다.
- 이 함수에서는 블루프린트가 없는 레이블에 대한 변수 초기화와 같은 JS 초기화 작업을 수행할 수 없습니다.
- 가상 머신에 과부하가 걸리면 생성자가 다시 실행되지 않더라도 이러한 리소스는 실패하므로 이 함수에서 폐쇄 함수 생성과 같은 JS 리소스를 적용할 수 없습니다.
- 현재 액터에서 컴포넌트의 프로퍼티를 수정하는 것은 지원되지 않는데, 오브젝트 생성 이후 SpawnActor 가 Component 로 리셋되기 때문입니다.:생성자 세팅 컴포넌트 어트리뷰트가 유효하지 않습니다.
자동 바인딩 모드 지원 데이터 유형
자동 바인딩 모드에서 선언한 유형이 지원하는 필드와 메서드만 UE에서 식별할 수 있습니다.
직접 매핑된 유형
- void
- number
- string
- bigint
- boolean
- UObject derived class under the UE module
- enumerate
- UStruct
- TArray
- TSet
- TMap
- TSubclassOf (class reference)
- TSoftObjectPtr (soft object reference)
- TSoftClassPtr (soft class reference)
참고: 함수 반환 유형은 반환값이 없음을 선언합니다. 함수가 반환 유형을 선언하지 않으면 임의의 유형을 반환하는 것과 같으며 자동 semi-butam 모드는 임의의 유형을 지원하지 않습니다.
다음은 몇 가지 필드와 방법입니다:
class TsTestActor extends UE.Actor {
tickCount: number;
actor: UE.Actor;
map: UE.TMap<string, number>;
arr: UE.TArray<UE.Object>;
set: UE.TSet<string>;
Add(a: number, b: number): number {
return a + b;
}
e: UE.ETickingGroup;
clsOfWidget: UE.TSubclassOf<UE.Widget>;
softObject: UE.TSoftObjectPtr<UE.Actor>;
softClass: UE.TSoftClassPtr<UE.Actor>;
}
유형 어노테이션
언리얼은 타입스크립트보다 더 세밀하게 조정된 타입을 가지고 있기 때문에 (즉 타입스크립트의 숫자는 바이트 , 인트 , 플로트 ,.double)와 동일한 논리적 아이디어를 표현하므로, Puerts가 해당 유형을 C++로 적절히 변환할 수 있도록 추가 어노테이션이 필요합니다. 다음은 몇 가지 예시입니다:
class TsTestActor extends UE.Actor {
//@cpp:text
Foo(): string {
return "hello";
}
Bar(p1:number/*@cpp:int*/): void {
}
//@cpp:name
Field: string;
}
- Foo 반환 값은 FText
- Bar의 매개 변수는 int
- Field 는 FName 유형입니다 .
- 현재 지원되는 유형 어노테이션은 다음과 같습니다:
- text
- name
- int
- byte
기타 어노테이션
유형 어노테이션 외에도 Puerts는 다른 어노테이션도 지원합니다.
- @no-blueprint 는 UE 블루프린트에서 사용할 수 없는 메서드나 필드를 나타냅니다 .
class TsTestActor extends UE.Actor {
//@no-blueprint
TsOnlyMethod():void {
}
//@no-blueprint
TsOnlyField: number;
}
rpc
데코레이터를 사용하여 메서드 및 필드의 RPC 속성을 설정할 수 있습니다.
참고: TypeScript 데코레이터는 기본적으로 활성화되지 않으므로 tsconfig . json에서 실험적 데코레이터 속성을 true로 설정해야 합니다.
- rpc.flags는 필드 및 메서드에 대한 플래그를 설정하는 데 사용됩니다.
- rpc.condition 는 필드에 대한 복제 조건을 설정하는 데 사용됩니다.
class TsTestActor extends UE.Actor {
@rpc.flags(rpc.PropertyFlags.CPF_Net | rpc.PropertyFlags.CPF_RepNotify)
@rpc.condition(rpc.ELifetimeCondition.COND_InitialOrOwner)
dint: number;
@rpc.flags(rpc.FunctionFlags.FUNC_Net | rpc.FunctionFlags.FUNC_NetClient)
Fire(): void {
}
@rpc.flags(rpc.FunctionFlags.FUNC_Net | rpc.FunctionFlags.FUNC_NetServer | rpc.FunctionFlags.FUNC_NetReliable)
FireServer(): void {
}
// If the field sets CPF_RepNotify, you need to add `OnRep_fieldname()` method.
OnRep_dint(): void {
}
}
가상머신 스위칭
Puerts는 V8과 quickjs 두 가지 가상 머신을 모두 지원하며, V8은 현재 두 가지 버전이 있습니다.
- UE 4.24 이하의 경우 Android 플랫폼은 V8 버전 7 .4.288, 기타 플랫폼은 버전 7 .7.299를 사용합니다.
- UE 4.25 이상의 경우, 각 플랫폼은 V8 버전 8 .4.371.19를 사용할 수 있습니다.
- 크기가 크고 까다로운 장면의 경우 퀵JS를 선택할 수 있습니다 (여기서는 '까다롭다'가 아니라 '패키지 크기를 줄이고 확장 가능한 성능이 필요한 경우'라는 의미로 번역한 것 같습니다).
기본적으로 제공되는 첫 번째 V8 버전입니다 .
V8 버전 8 .4.371. 19를 사용하려면V8 GitHub Actions로 이동하여 최신 빌드 V8 아티팩트를 다운로드하세요. 플러그인/서드파티/제3자 ( 또는 적절한 위치)에 내용을 압축을 풉니다. 그런 다음 JsEnv .Build.cs 파일에서 UseNewV8을 true로 설정합니다.
quickjs를 사용하려면quickjs GitHub 작업으로 이동하여 컴파일된 quickjs를 다운로드하고.Plugins/Puerts/ThirdParty ( 또는 적절한 위치) 에 압축을 풀고 JsEnv .Build.cs 파일에서 UseQuickJs를 true로 설정합니다.
vscode_debug
VSCode를 사용한 디버깅
자동 바인딩 모드 디버그 구성
- 메인 메뉴에서 편집 -> 프로젝트 설정을 선택하고 플러그인 -> 푸어 설정 페이지에서 디버그 활성화 설정을 활성화합니다.
- JS가 코드를 실행하기 전에 디버거가 연결될 때까지 기다리게 하려면 위의 설정에서 디버거 대기를 활성화합니다.
- 디버거로 VSCode를 실행하여 최대한 빨리 연결하더라도 디버거 연결 핸드셰이크가 완료되는 데 최소 100ms가 걸릴 수 있습니다.
- 이 설정을 활성화하지 않으면 JS 스크립트가 즉시 실행되므로 디버거가 제때 연결되지 않아 중단점에 도달하지 않을 수 있습니다. 이 설정을 활성화하면 Puerts가 JS 코드를 실행하기 전에 디버거가 연결될 때까지 기다리도록 지시하여 아무것도 놓치지 않도록 할 수 있습니다.
가상 머신 모드 생성 이후, 디버그 구성
인칸타이 여기 있습니다. 이 섹션이 실제로 무엇을 위한 것인지 잘 모르겠습니다. 가상 머신을 위한 추가 작업이 필요한 것 같나요? 이 코드의 대부분은 위에서 언급한 '자동 바인딩 모드'인 PuertsModule .cpp에 이미 포함되어 있습니다. 이 모드가 무엇인지 잘 모르겠습니다.
- FJsEnv 수신 디버그 포트 생성
// 8080 Debug port
GameScript = MakeShared<puerts::FJsEnv>(
std::make_unique<puerts::DefaultJSModuleLoader>(TEXT("JavaScript")),
std::make_shared<puerts::FDefaultLogger>(), 8080
);
- 대기 중인 디버거 링크 차단
GameScript = MakeShared<puerts::FJsEnv>(
std::make_unique<puerts::DefaultJSModuleLoader>(TEXT("JavaScript")),
std::make_shared<puerts::FDefaultLogger>(), 8080
);
GameScript->WaitDebugger();
//...
GameScript->Start("QuickStart", Arguments);
VSCode 및 UE 에디터 주의 사항
디버거 자동 연결
다른 JavaScript 프로젝트와 마찬가지로, 인스펙터가 실행 중인 프로세스가 감지되면 자동으로 디버거를 첨부하도록 VSCode를 구성할 수 있습니다. 여기서 크게 달라지는 것은 없지만 어쨌든 번역은 다음과 같습니다:
VSCode에서 사용자 설정을 열고 자동 첨부를 검색한 다음 디버그 > 노드 자동 첨부를 켜짐으로 설정합니다.
최신 버전의 VSCOde에서는 자동 첨부 필터와 자동 첨부 스마트 필터라는 두 가지 설정을 구성해야 작동할 수 있습니다. 특정 포트에 디버거를 연결하도록 launch .json에 지정할 수도 있습니다.
느린 디버거 성능
기본적으로 언리얼은 백그라운드에서 CPU 사용량 감소가 활성화된 상태로 제공됩니다. 언리얼 엔진이 아닌 다른 작업에 집중할 때 에디터의 많은 기능이 느려집니다. 블렌더, 마야, 서브스턴스 페인터 등 다른 프로그램을 열 때 전체 렌더링 사이클을 실행하지 않아 컴퓨터 속도가 이유 없이 느려지는 것을 방지할 수 있다는 장점이 있습니다.
하지만 엔진 대신 VSCode에서 디버거를 실행하고 VSCode에 집중하면 30-120 FPS가 아닌 4 FPS처럼 매우 느리게 실행됩니다. 디버깅하는 동안 이 설정을 활성화하면 JS/TS 코드를 살펴보는 동안 게임이 최고 속도로 계속 실행되도록 할 수 있습니다.
이 설정은 에디터 개인설정의 일반 > 퍼포먼스에서 찾을 수 있습니다.
타입스크립트와 엔진 상호작용
아래는 인칸타 게임즈에서 원본 문서를 번역한 버전입니다. 번역은 주로 구글 번역을 사용했지만, 구글 번역이 놓친 부분을 이해하기 위해 수작업으로 수정했습니다.
모든 C++/블루프린트 유형인 UCLASS, UPROPERTY, UFUNCTION, USTRUCT, UENUM은 TypeScript로 직접 액세스할 수 있습니다.
그러나 위에서 설명한 관련 UXXXXX가 표시되지 않은 C++ 구조체(속성, 메서드, 클래스, 구조체, 열거형)의 경우 template_binding.md의 지침을 따라야 합니다.
Generate TypeScript declaration files
- C++/블루프린트는 타입스크립트에서 호출되며, C++ 클래스는 상주 메모리이고, 블루프린트는 수동 로딩이 필요하며, 다른 객체의 메서드/속성 액세스가 필요한 등의 문제가 있습니다.
- declaration 을 하려면 다음 버튼을 클릭.
- 또는 콘솔 명령을 사용하여 선언 파일을 생성할 수도 있습니다: Puerts.Gen
- 플러그인은 현재 UE5 EA에 표시되는 버튼을 지원하지 않으므로, 콘솔 명령을 사용해야 합니다.
Members and functions
// Object construct
let obj = new UE.MainObject();
// Member visit
console.log("before set", obj.MyString)
obj.MyString = "PPPPP";
console.log("after set", obj.MyString)
// Simple type parameter function
let sum = obj.Add(100, 300);
console.log('sum', sum)
// Complex type parameter function
obj.Bar(new UE.Vector(1, 2, 3));
// Quote type parameter function
let vectorRef = $ref(new UE.Vector(1, 2, 3))
obj.Bar2(vectorRef);
obj.Bar($unref(vectorRef));
// Static function
let str1 = UE.JSBlueprintFunctionLibrary.GetName();
let str2 = UE.JSBlueprintFunctionLibrary.Concat(', ', str1);
UE.JSBlueprintFunctionLibrary.Hello(str2);
// enumerate
obj.EnumTest(UE.EToTest.V1);
Blueprint & Other Resources Load
// Load a blueprint class
let bpClass = UE.Class.Load('/Game/StarterContent/TestBlueprint.TestBlueprint_C');
// UE.XXX.Load is equivalent to writing LoadObject<XXX> in C++, so UE.Class.Load is equivalent to C++'s LoadObject<UCLASS>()
let bpActor = world.SpawnActor(bpClass, undefined, UE.ESpawnActorCollisionHandlingMethod.Undefined, undefined, undefined) as UE.TestBlueprint_C;
// Load a particle system
let bulletImpact = UE.ParticleSystem.Load("/Game/BlockBreaker/ParticleSystems/PS_BulletImpact");
// Load a static mesh
let rifle = UE.StaticMesh.Load("/Game/BlockBreaker/Meshes/SM_Rifle");
TArray, TSet, TMap
Create
// TArray<int>
let a2 = UE.NewArray(UE.BuiltinInt);
// TArray<FString>
let a3 = UE.NewArray(UE.BuiltinString);
// TArray<UActor>
let a4 = UE.NewArray(UE.Actor);
// TArray<FVector>
let a5 = UE.NewArray(UE.Vector);
// TSet<FString>
let s1 = UE.NewSet(UE.BuiltinString);
// TMap<FString, int>
let m1 = UE.NewMap(UE.BuiltinString, UE.BuiltinInt);
액세스, IDE의 자동 프롬프트를 눌러 컨테이너에 액세스합니다(여기서 Incanta: 이 구글 번역이 무엇을 의미하는지 잘 모르겠지만, 위의 예제에서 생성된 객체를 사용하는 데모가 있는 것 같습니다).
a2.Add(888);
a2.Set(0, 7);
console.log(a2.Num());
m1.Add("John", 0)
m1.Add("Che", 1)
console.log(m1.Get("John"))
ArrayBuffer
네트워크 메시지를 처리하려면 다음이 필요합니다.
Access a C++ FArrayBuffer in TypeScript
다음과 같은 C++ 코드가 있는 경우:
UPROPERTY()
FArrayBuffer ArrayBuffer;
// Set content
ArrayBuffer.Data = "hello";
ArrayBuffer.Length = 5;
타입스크립트에서 ArrayBuffer로 액세스하는 방법은 다음과 같습니다:
let ab = obj.ArrayBuffer;
let u8a1 = new Uint8Array(ab);
for (var i = 0; i < u8a1.length; i++) {
console.log(i, u8a1[i]);
}
TypeScript Passing FArrayBuffer to C++
아래와 같이 FArrayBuffer를 받는 C++ 함수가 있는 경우:
void UMainObject::ArrayBufferTest(const FArrayBuffer& Ab) const
{
UE_LOG(LogTemp, Warning, TEXT("Ab(%p, %d)"), Ab.Data, Ab.Length);
}
타입스크립트에서 이 함수를 호출/에뮬레이트하려면 Uint8Array 를 사용하면 됩니다:
var arr = new Uint8Array([21,31]);
obj.ArrayBufferTest(arr);
Callback
C++ 엔진 코드/모듈은 블루프린트에서 함수/이벤트를 호출/발동하는 방식과 유사하게 Dynamic_Delegate 및 Dynamic_Multicast_Delegate 를 통해 TypeScript 를 능동적으로 호출할 수 있습니다.
- UI 사용자 작업, 웹 메시지를 TypeScript로 알림 가능
- TypeScript 함수를 C++ 호출로 내보내려면 다음을 사용할 수도 있습니다.
C++ 선언은 다음과 같습니다.
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FNotifyWithInt, int32, A);
DECLARE_DYNAMIC_DELEGATE_RetVal_OneParam(FString, FNotifyWithStringRet, FString, A);
DECLARE_DYNAMIC_DELEGATE_OneParam(FNotifyWithRefString, FString&, A);
UCLASS()
class PUERTS_UNREAL_DEMO_API AMyActor : public AActor
{
GENERATED_BODY()
public:
UPROPERTY()
FNotifyWithInt NotifyWithInt;
UPROPERTY()
FNotifyWithRefString NotifyWithRefString;
UPROPERTY()
FNotifyWithStringRet NotifyWithStringRet;
//...
};
C++ 함수가 델리게이트를 트리거할 때 호출되도록 TypeScript를 바인딩하는 방법은 다음과 같습니다.
function MutiCast1(i) {
console.warn("MutiCast1<<<", i);
}
function MutiCast2(i) {
console.warn("MutiCast2>>>", i);
}
actor.NotifyWithInt.Add(MutiCast1);
actor.NotifyWithInt.Add(MutiCast2);
actor.NotifyWithRefString.Bind((strRef) => {
console.log("NotifyWithRefString", $unref(strRef));
$set(strRef, "out to NotifyWithRefString"); // Reference parameter output
});
actor.NotifyWithStringRet.Bind((inStr) => {
return "////" + inStr;
});
아래 C++ 코드는 타입스크립트에서 바인딩된 함수를 호출하는 이벤트를 트리거합니다.
NotifyWithInt.Broadcast(0);
NotifyWithStringRet.ExecuteIfBound("hi...");
if (NotifyWithRefString.IsBound())
{
FString Str = TEXT("hello john che ");
NotifyWithRefString.Execute(Str);
UE_LOG(LogTemp, Warning, TEXT("NotifyWithRefString out ? %s"), -Str);
}
Extend function
언리얼에는 UFUNCTION 태그가 없는 C++ 함수가 많은데, 이 API 호출은 어떻게 하나요? 두 가지 방법이 있습니다:
- 확장 기능(권장)
- 공개되지 않은 코드 생성기인 Puerts를 사용하여 랩 코드를 생성합니다(여기서는 인칸타, 구글 번역이 무슨 뜻인지 잘 모르겠지만 아래 예시가 권장되는 방법이라는 데 동의합니다).
UObject ::GetClass 와 UObject ::FindFunction 을 예로 들어보겠습니다. 기본적으로 아래 코드는 함수에 대한 C++ 래퍼를 생성하여 블루프린트/타입스크립트에 노출하는 것입니다.
C++ extension
// ObjectExtension.h
UCLASS()
class UObjectExtension : public UExtensionMethods
{
GENERATED_BODY()
UFUNCTION(BlueprintCallable, Category = "ObjectExtension")
static UClass * GetClass(UObject * Object);
UFUNCTION(BlueprintCallable, Category = "ObjectExtension")
static UFunction * FindFunction(UObject * Object, FName InName);
};
// ObjectExtension.cpp
#include "ObjectExtension.h"
UClass * UObjectExtension::GetClass(UObject * Object)
{
return Object->GetClass();
}
UFunction * UObjectExtension::FindFunction(UObject * Object, FName InName)
{
return Object->FindFunction(InName);
}
Important:
- UExtensionMethods에서 클래스 상속을 새로 구축했습니다.
- 인칸타가 여기 있습니다. 이것은 Puerts에 내장된 클래스로, UBlueprintFunctionLibrary 클래스를 확장합니다. 아래 타입스크립트 예제에서 멋진 객체 .GetClass() 구문을 사용할 수 있도록 Puerts가 UExtensionMethods의 하위 클래스에서 추가 처리를 수행한다고 가정합니다. 꽤 멋진 기능입니다.
- 함수의 첫 번째 인수는 확장된 클래스입니다.
타입스크립트로 액세스하는 경우 객체에 액세스하는 멤버 메서드도 비슷합니다.
참고: 새로운 C++ 확장 함수에 대한 TypeScript 선언 파일을 다시 생성해야 합니다 .
let cls = obj.GetClass();
let func = obj.FindFunction("Func");
Template-based static binding
아래는 인칸타 게임즈에서 원본 문서를 번역한 버전입니다. 번역은 주로 구글 번역을 사용했지만, 구글 번역이 놓친 부분을 이해하기 위해 수작업으로 수정했습니다.
Overview
이 문서에서는 일반적인 C++ 클래스/구조체, 즉 UCLASS에 대해 설명하며, 업프로퍼티, UFUNCTION 및 호출 속성을 선택 해제합니다.
이 문서는 비UE 환경(예: 서버, 유니티 등), C++ 클래스/구조체 액세스에도 적용됩니다.
Support features:
- Constructor
- Static function
- Member variables
- Member function
- Constructor / static function / member function supports overload
- Support inheritance
- Generate TypeScript declaration
- The UE type is not marked UPROPERTY. After the UFUNCTION member declares, it will seamlessly appear in the original category.
- Support JS object mapping to C++ JSObject, JSObject can get/set the properties, call the JS function.
- Support JS function mapping to std::function
- Support custom converter
Important Notes
다른 게임 모듈과 같이 JsEnv 모듈 외부에서 이 기능을 사용하려면 다음을 수행해야 합니다:
- 동적 라이브러리 버전 V8 라이브러리, 전환 방법을 사용합니다:
- 공식 웹사이트에 접속하여 V8 을 지원하는 라이브러리를 다운로드 하고 Plugins/Puerts/ThirdParty/ (or respective folder) 여기에 압축을 풉니다.
- JsEnv.Build.cs 파일을 찾아 UseNewV8을 true로 변경합니다.
- 모듈의 *.Build.cs
- JsEnv 모듈에 종속성 추가하기
Examples
Hello World
가장 간단한 일반적인 C++ 클래스를 예로 들어 보겠습니다.
// Calc.h
class Calc
{
public:
static int32_t Add(int32_t a, int32_t b)
{
return a + b;
}
};
우리는 다음과 같이 선언합니다.
#include "Calc.h"
#include "Binding.hpp"
// A macro defined in Binding.hpp which
// creates a converter (either V8 or Pesapi) for your class
// which helps build the translation layer
UsingCppType(Calc);
struct AutoRegisterForCPP
{
AutoRegisterForCPP()
{
puerts::DefineClass<Calc>()
.Function("Add", MakeFunction(&Calc::Add)) // There's also `.Method(...)` and `.Property(...)`
.Register();
}
};
// Completes the automatic registration with puerts. When this calls
// when the module is loaded, it will call the constructor defined above,
// executing the registration with puerts
AutoRegisterForCPP _AutoRegisterForCPP__;
C++를 컴파일하고, 언리얼 에디터를 열고, 버튼이나 커맨드로 TypeScript 정의를 생성하여 TypeScript에서 호출합니다. 그런 다음 TypeScript에서 다음을 수행할 수 있습니다:
import * as cpp from 'cpp'
let Calc = cpp.Calc;
// static function
console.log(Calc.Add(12, 34));
정적 함수 선언
프레젠테이션은 정적 함수인. Function(이름, <함수 참조>)입니다. 정적 함수를 등록하려면 여러 가지 방법으로 참조할 수 있습니다:
- 함수에 과부하가 걸리지 않음: MakeFunction (&Calc::Add)
- 함수에 과부하가 걸리지 않지만 매개변수를 확인하려고 합니다: MakeCheckFunction(&Calc::Add)
- 기능이 과부하 상태이지만 그 중 하나만 선택하려고 합니다 :SelectFunction(float (*)(float, float), &Calc::Add)
- 기능이 과부하 상태이며 여러 개의 과부하를 사용하려고 합니다:
CombineOverloads(
MakeOverload(void(*)(), &TestClass::Overload),
MakeOverload(void(*)(int32_t), &TestClass::Overload),
MakeOverload(void(*)(int32_t, int32_t), &TestClass::Overload),
MakeOverload(void(*)(std::string, int32_t), &TestClass::Overload)
)
맴버 변수
class TestClass
{
public:
int32_t X;
int32_t Y;
};
Statement
puerts::DefineClass<TestClass>()
.Property("X", MakeProperty(&TestClass::X))
.Property("Y", MakeProperty(&TestClass::Y))
.Register();
생성자
class TestClass
{
public:
TestClass();
TestClass(int32_t InX, int32_t InY);
};
Statement
puerts::DefineClass<TestClass>()
.Constructor(CombineConstructors(
MakeConstructor(TestClass, int32_t, int32_t),
MakeConstructor(TestClass)
))
.Register();
생성자가 하나만 있는 경우 다음과 같이 단순화할 수 있습니다.
puerts::DefineClass<AdvanceTestClass>()
.Constructor<int>() // if only one Constructor
.Register();
멤버 펑션
class TestClass
{
public:
int32_t OverloadMethod();
int32_t OverloadMethod(int32_t a);
uint32_t OverloadMethod(uint32_t a);
int64_t OverloadMethod(int64_t a);
TestClass *GetSelf();
};
Statement
puerts::DefineClass<TestClass>()
.Method("OverloadMethod", CombineOverloads(
MakeOverload(int32_t(TestClass::*)(), &TestClass::OverloadMethod),
MakeOverload(int32_t(TestClass::*)(int32_t), &TestClass::OverloadMethod),
MakeOverload(uint32_t(TestClass::*)(uint32_t), &TestClass::OverloadMethod),
MakeOverload(int64_t(TestClass::*)(int64_t), &TestClass::OverloadMethod)
))
.Method("GetSelf", MakeFunction(&TestClass::GetSelf))
.Register();
상속
class BaseClass
{
public:
void Foo(int p);
};
class TestClass : public BaseClass
{
public:
};
Statement
puerts::DefineClass<BaseClass>()
.Method("Foo", MakeFunction(&BaseClass::Foo))
.Register();
puerts::DefineClass<TestClass>()
.Extends<BaseClass>()
.Register();
JS 객체를 JsObject에 매핑하고 JS 객체 속성을 가져오거나 설정합니다.
#include "JsObject.h"
class AdvanceTestClass
{
public:
AdvanceTestClass(int A);
void JsObjectTest(FJsObject Object);
};
void AdvanceTestClass::JsObjectTest(FJsObject Object)
{
auto P = Object.Get<int>("p");
UE_LOG(LogTemp, Warning, TEXT("AdvanceTestClass::JsObjectTest({p:%d})"), P);
Object.Set<std::string>("q", "john");
}
타입스크립트에서 사용
import * as cpp from 'cpp'
// js object
let obj = new cpp.AdvanceTestClass(100);
let j:any = {p:100};
obj.JsObjectTest(j);
console.log(j.q);
JS 함수 매핑 JsObject 및 콜백
// class decl ...
void AdvanceTestClass::CallJsObjectTest(FJsObject Object)
{
auto Ret = Object.Func<float>(1024, "che");
UE_LOG(LogTemp, Warning, TEXT("AdvanceTestClass::CallJsObjectTest Callback Ret %f"), Ret);
}
타입스크립트에서 사용
let obj = new cpp.AdvanceTestClass(100);
obj.CallJsObjectTest((i, str) => {
console.log(i, str);
return 1.01;
})
JS 함수를 std::함수에 매핑하기
//class decl ...
void AdvanceTestClass::StdFunctionTest(std::function<int(int, int)> Func)
{
int Ret = Func(88, 99);
UE_LOG(LogTemp, Warning, TEXT("AdvanceTestClass::StdFunctionTest Callback Ret %d"), Ret);
}
타입스크립트에서 사용
let obj = new cpp.AdvanceTestClass(100);
obj.StdFunctionTest((x:number, y:number) => {
console.log('x=' + x + ",y=" + y);
return x + y;
})
Supplement Engine Classes
UFUNCTION 수정자가 없는 CreateDefaultSubobject, GetName, GetOuter, GetClass, GetWorld와 같은 메서드가 있는 UObject와 같은 기존 클래스를 보완하려는 경우입니다.
이를 위해 C++를 추가합니다:
#include "CoreMinimal.h"
#include "Binding.hpp"
#include "UEDataBinding.hpp"
UsingUClass(UObject)
UsingUClass(UWorld) // for return type
UsingUClass(UClass)
UsingUClass(USceneComponent)
puerts::DefineClass<UObject>()
#if ENGINE_MAJOR_VERSION >= 4 && ENGINE_MINOR_VERSION >= 23
.Method("CreateDefaultSubobject", SelectFunction(UObject* (UObject::*)(FName, UClass*, UClass*, bool , bool), &UObject::CreateDefaultSubobject))
#else
.Method("CreateDefaultSubobject", SelectFunction(UObject* (UObject::*)(FName, UClass*, UClass*, bool, bool, bool), &UObject::CreateDefaultSubobject))
#endif
.Method("GetName", SelectFunction(FString (UObjectBaseUtility::*)() const, &UObjectBaseUtility::GetName))
.Method("GetOuter", MakeFunction(&UObject::GetOuter))
.Method("GetClass", MakeFunction(&UObject::GetClass))
.Method("GetWorld", MakeFunction(&UObject::GetWorld))
.Register();
참고: 일반 C++ 클래스는 이 파일의 앞부분에 명시된 것처럼 (즉, UObject/UClass의 자손이 아닌) 다른 클래스입니다. UClass의 자손인 무언가를 수정하는 경우, 위와 같이 UsingUClass 매크로를 사용해야 합니다. 마찬가지로 UStruct를 보완하는 경우 UsingUStruct를 사용해야 합니다.
ue.d.ts를 재생성하면 UE.Object의 유형 선언에 위의 메서드가 추가된 것을 확인할 수 있습니다:
class Object {
constructor(Outer?: Object, Name?: string, ObjectFlags?: number);
ExecuteUbergraph(EntryPoint: number): void;
CreateDefaultSubobject(p0: string, p1: $Nullable<Class>, p2: $Nullable<Class>, p3: boolean, p4: boolean) : Object;
GetName() : string;
GetOuter() : Object;
GetClass() : Class;
GetWorld() : World;
static StaticClass(): Class;
static Find(OrigInName: string, Outer?: Object): Object;
static Load(InName: string): Object;
}
이후에는 위의 메서드를 객체에서 직접 사용할 수 있습니다.
엔진(또는 순수 C++) 호출 스크립트
주로 엔진이 스크립트의 특정 함수(예: 평가)나 UI 이벤트, 네트워크 메시지 및 기타 콜백 알림 시나리오를 능동적으로 호출하는 데 사용됩니다.
C++
DYNAMIC_DELEGATE
UClass 아래의 DYNAMIC_DELEGATE 필드
엔진 측에서는 DYNAMIC_DELEGATE, DYNAMIC_MULTICAST_DELEGATE를 통해 TypeScript를 능동적으로 호출할 수 있습니다.
- UI 사용자 동작, 네트워크 메시지를 이걸 통해 타입스크립트에 알릴 수 있습니다.
C++ 정의
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FNotifyWithInt, int32, A);
DECLARE_DYNAMIC_DELEGATE_RetVal_OneParam(FString, FNotifyWithStringRet, FString, A);
DECLARE_DYNAMIC_DELEGATE_OneParam(FNotifyWithRefString, FString&, A);
UCLASS()
class PUERTS_UNREAL_DEMO_API AMyActor : public AActor
{
GENERATED_BODY()
public:
UPROPERTY()
FNotifyWithInt NotifyWithInt;
UPROPERTY()
FNotifyWithRefString NotifyWithRefString;
UPROPERTY()
FNotifyWithStringRet NotifyWithStringRet;
//...
};
타입스크립트 바인딩
function MutiCast1(i) {
console.warn("MutiCast1<<<", i);
}
function MutiCast2(i) {
console.warn("MutiCast2>>>", i);
}
actor.NotifyWithInt.Add(MutiCast1)
actor.NotifyWithInt.Add(MutiCast2)
actor.NotifyWithRefString.Bind((strRef) => {
console.log("NotifyWithRefString", $unref(strRef));
$set(strRef, "out to NotifyWithRefString");//引用参数输出
});
actor.NotifyWithStringRet.Bind((inStr) => {
return "////" + inStr;
});
C++ 트리거
NotifyWithInt.Broadcast(0);
NotifyWithStringRet.ExecuteIfBound("hi...");
if (NotifyWithRefString.IsBound())
{
FString Str = TEXT("hello john che ");
NotifyWithRefString.Execute(Str);
UE_LOG(LogTemp, Warning, TEXT("NotifyWithRefString out ? %s"), *Str);
}
UClass 아래의 DYNAMIC_DELEGATE 필드가 아닌 경우
C++ 코드
void UMainObject::PassJsFunctionAsDelegate(FCallback Callback) const
{
auto Ret = Callback.Execute(TEXT("John"));
UE_LOG(LogTemp, Warning, TEXT("John ? %d"), Ret);
Ret = Callback.Execute(TEXT("Che"));
UE_LOG(LogTemp, Warning, TEXT("Che ? %d"), Ret);
}
모드 1:toDelegate(소유자: UE.Object, 함수: 함수)
import {toDelegate} from 'puerts';
function IsJohn(str:string) : boolean {
return str == "John";
}
//소유자는 UObject이며, 소유자는 릴리스될 때 IsJohn을 자동으로 릴리스합니다.
obj.PassJsFunctionAsDelegate(toDelegate(owner, IsJohn));
모드 2:toManualReleaseDelegate(func:Function)
import {toManualReleaseDelegate, releaseManualReleaseDelegate} from 'puerts';
function IsJohn(str:string) : boolean {
return str == "John";
}
obj.PassJsFunctionAsDelegate(toManualReleaseDelegate(IsJohn));
//작업을 완료하면 수동으로 해제해야 하며, 그렇지 않으면 메모리 누수가 발생합니다.
releaseManualReleaseDelegate(IsJohn);
모드3:toDelegate(obj: UE.Object, funcName: string)
import {toDelegate} from 'puerts';
//객체는 UObject이고 IsJohn은 이 UObject의 UFunction입니다.
obj.PassJsFunctionAsDelegate(toDelegate(obj, "IsJohn"));
std::function
일반 C++에서는 Delegate와 유사한 std::함수가 선호됩니다.
void AdvanceTestClass::StdFunctionTest(std::function<int(int, int)> Func)
{
int Ret = Func(88, 99);
UE_LOG(LogTemp, Warning, TEXT("AdvanceTestClass::StdFunctionTest Callback Ret %d"), Ret);
}
TypeScript 액서스
obj2.StdFunctionTest((x:number, y:number) => {
console.log('x=' + x + ",y=" + y);
return x + y;
})
블루프린트
DYNAMIC_DELEGATE
C++ 챕터에서 소개한 DYNAMIC_DELEGATE도 사용할 수 있습니다.
정적 스크립트 메서드
!이 접근 방식을 사용하려면 UE 클래스 상속 기능이 켜져 있어야 합니다.
import * as UE from "ue";
class TsUtils extends UE.BlueprintFunctionLibrary {
public static Sum(a: number, b: number, c: number): number {
return a + b + c
}
public static GetBool() :boolean {
return true;
}
}
export default TsUtils
위의 TsUtil은 일반 블루프린트 함수 라이브러리처럼 블루프린트에서 사용할 수 있습니다.
멤버 스크립트 메서드
!이 접근 방식을 사용하려면 UE 클래스 상속 기능이 켜져 있어야 합니다.
성공하면 ts 클래스를 일반 블루프린트 클래스처럼 사용할 수 있습니다.
자세한 내용은 UE 클래스 함수 상속을 참조하세요.
Mixin멤버 메서드 호출 기반
이 기능은 UE 클래스 상속 기능을 활성화하거나 활성화하지 않은 상태에서 사용할 수 있습니다.
mixin그 후에는 블루프린트 메서드가 같은 이름의 ts 메서드로 재정의됩니다.
따라서 ReceiveBeginPlay와 같은 엔진 알림을 받거나 블루프린트에 빈 함수를 추가하고 ts로 오버라이드한 후 해당 빈 함수를 호출하면 적절한 ts 메서드로 이동하는 것과 동일합니다.
uclass_extends
엔진 클래스 함수 상속
이 기능을 활성화하면 특정 쓰기 스타일을 가진 클래스를 UE 에디터에서 인식할 수 있습니다.
- 명령줄에서 플러그인/푸어스 디렉터리를 열고 다음 명령을 실행하여 모드 활성화 및 종속성 설치를 완료합니다.
node enable_puerts_module.js
예를 들어 이러한 클래스를 들 수 있습니다:
import * as UE from 'ue'
class TS_Player extends UE.Character {
}
export default TS_Player;
그런 다음 UE 에디터에서 선택할 수 있습니다
- UE 에서 인식할 수 있는 클래스, 생성자 지원, 오버라이드할 수 있는 블루프린트 메서드 지원, 축 매핑 축 지원, 액션 이벤트, RPC 지원
class TS_Player extends UE.Character {
FpsCamera: UE.CameraComponent;
//...
Constructor() {
let FpsCamera = this.CreateDefaultSubobjectGeneric<UE.CameraComponent>("FpsCamera", UE.CameraComponent.StaticClass());
FpsCamera.SetupAttachment(this.CapsuleComponent, "FpsCamera");
//...
}
MoveForward(axisValue: number): void {
this.AddMovementInput(this.GetActorForwardVector(), axisValue, false);
}
MoveRight(axisValue: number): void {
this.AddMovementInput(this.GetActorRightVector(), axisValue, false);
}
ReceiveBeginPlay(): void {
//...
}
서식지정
타입스크립트는 다음 세 가지 기준을 충족하는 경우에만 UE 에디터에서 인식할 수 있습니다.
- 이 클래스는 UE의 클래스 또는 UE에서 상속하는 다른 클래스로부터 상속합니다;
- 클래스 이름은 접미사 .ts가 제거된 파일 이름과 동일합니다;
- 이 클래스 기본값을 내보냅니다.
제약조건
- 블루프린트 상속은 지원되지 않으며, 네이티브 클래스 상속만 지원됩니다.
- UserWidget 또는 그 서브클래스를 상속해도
수명주기
이 모드에서는 UE 유형을 상속하는 타입스크립트 유형의 오브젝트의 라이프사이클이 엔진에 의해 관리됩니다.
다음 예시를 예로 들어 보겠습니다:
let obj = getsomeobject();
setTimeout(() => {
console.log(obj.XXX);
}, 1000);
오브젝트는 클로저를 통해 참조됩니다. 이 오브젝트가 일반 UE 오브젝트인 경우 puerts는 오브젝트에 대한 강력한 참조를 추가하고, UE 타입을 상속하는 TypeScript 타입의 오브젝트인 경우 오브젝트에 대한 참조를 추가하지 않습니다.
생성자 ( Constructor)
표준 타입스크립트 생성자와 달리, 상속 엔진 클래스 패턴의 UE 초기화에 의해 호출되는 생성자는 첫 글자가 대문자(예: Constructor)여야 합니다.
class TsTestActor extends UE.Actor {
tickCount: number;
//注意,继承UE类的js类,构造函数必须大写开头
Constructor() {
this.PrimaryActorTick.bCanEverTick = true;
tickCount = 0;
}
}
- 생성자는 생성자에서 호울해야 하는 것으로 UE 가 자격을 부여하는 API(ex. CreateDefaultSubobject)를 호출할 수 있습니다.
- 클래스가 생성자를 정의하는 경우, 클래스 멤버 변수의 초기화는 TypeScript 가 맡게 되며, 이 시점에서 UE 에디터에서 설정한 값은 무효화됩니다.
- 생성자가 정의되지 않은 경우 UE 에디터에서 멤버 변수 값의 수동 설정 지원
- 생성자는 UE가 호출하는 생성자이며 UE 멤버의 초기화에만 사용됩니다.
- 이 함수에서는 블루프린트 없음으로 표시된 변수의 초기화와 같은 js 초기화를 수행할 수 없습니다.
- 이 함수에서 폐쇄 함수 생성과 같은 js 리소스를 요청할 수 없습니다. 이러한 리소스는 VM을 다시 로드한 후 무효화되지만 생성자는 다시 실행되지 않습니다.
- 현재 액터의 생성자에서 컴포넌트 프로퍼티를 수정하는 것은 지원되지 않는데, SpawnActor 가 오브젝트 생성 후 컴포넌트를 리셋하면 생성자가 컴포넌트 프로퍼티를 유효하지 않게 설정하기 때문입니다.
- AsyncLoadingThreadEnabled가 켜져 있으면 생성자에서 CreateDefaultSubobject를 호출할 수 없으며, 그렇지 않으면 생성자가 호출을 지연한 후 생성자 시간에 더 이상 호출되지 않으므로 CreateDefaultSubobject가 오류를 보고합니다.
상속 엔진 클래스 패턴에서 지원되는 데이터 유형.
상속 엔진 클래스 패턴에서 지원하는 유형으로 선언된 필드와 메서드만 UE에서 인식할 수 있습니다.
直接映射的类型
UE 모듈의 void, 숫자, 스트링, 빅인트, 부울, UObject 파생 클래스, 열거형, UStruct, TArray, TSet, TMap, TSubclassOf(클래스 레퍼런스), TSoftObjectPtr(소프트 오브젝트 레퍼런스), TSoftClassPtr (소프트 클래스 레퍼런스)
참고: 반환 타입이 무효로 선언된 함수는 반환값이 없으며, 반환 타입을 선언하지 않은 함수는 자동 하프딘 모드에서 지원되지 않는 아무 타입이나 반환하는 것과 동일합니다.
다음은 몇 가지 필드 및 방법의 예.
class TsTestActor extends UE.Actor {
tickCount: number;
actor: UE.Actor;
map: UE.TMap<string, number>;
arr: UE.TArray<UE.Object>;
set: UE.TSet<string>;
Add(a: number, b: number): number {
return a + b;
}
e: UE.ETickingGroup;
clsOfWidget: UE.TSubclassOf<UE.Widget>;
softObject: UE.TSoftObjectPtr<UE.Actor>;
softClass: UE.TSoftClassPtr<UE.Actor>;
}
유형 어노테이션
TypeScript와 UE 사이의 데이터 유형은 풍부하지 않으므로 둘은 일대일로 매핑되지 않습니다(예: UE의 바이트, 인티, 플로트는 TypeScript의 숫자에 해당). 그렇다면 필요한 유형을 생성하도록 puerts에 어떻게 지시할까요? puerts는 유형 어노테이션을 제공하며, 다음은 몇 가지 예입니다. 다음은 몇 가지 예시입니다:
class TsTestActor extends UE.Actor {
//@cpp:text
Foo(): string {
return "hello";
}
Bar(p1:number/*@cpp:int*/): void {
}
//@cpp:name
Field: string;
}
- Foo의 반환값은 FText
- Bar의 매개변수는 int
- Field의 유형은 FName
- 현재 유형 어노테이션에서 지원되는 유형:text,name,int,byte
기타 참고사항
유형 어노테이션 외에도 퓨어티에스는 다른 어노테이션도 지원합니다.
- @no-blueprint
UE 에디터에서 인식되지 않으며 메서드와 필드를 모두 사용할 수 있음을 나타냅니다.
class TsTestActor extends UE.Actor {
//@no-blueprint
TsOnlyMethod():void {
}
//@no-blueprint
TsOnlyField: number;
}
uproperty ufunction
import * as UE from 'ue'
import { uproperty,uparam,ufunction } from 'ue';
class TsTestActor extends UE.Actor
{
//Note: Direct invocation from UE is not allowed; instead, import uproperty, ufunction, uparam and then make the call.
//@UE.uproperty.umeta(UE.uproperty.Category="TEST Property") //Error
@uproperty.umeta(uproperty.ToolTip="Test Value")
@uproperty.uproperty(uproperty.BlueprintReadOnly,uproperty.Category="TEST Category")
TestValue:number;
@ufunction.ufunction(ufunction.BlueprintPure)
public update(AA:number , BB:number):void
{
}
}
export default TsTestActor;
rpc
Decorator 를 통해 메서드 및 필드의 RPC 속성을 설정 할 수 있음.
참고: TypeScript의 데코레이터는 기본적으로 켜져 있지 않으므로 tsconfig.json에서 실험적 데코레이터 속성을 true로 설정해야 합니다.
- rpc.flags
필드, 메서드에 대한 플래그 설정
- rpc.condition
필드에 대한 복제 조건 설정
class TsTestActor extends UE.Actor {
@rpc.flags(rpc.PropertyFlags.CPF_Net | rpc.PropertyFlags.CPF_RepNotify)
@rpc.condition(rpc.ELifetimeCondition.COND_InitialOrOwner)
dint: number;
@rpc.flags(rpc.FunctionFlags.FUNC_Net | rpc.FunctionFlags.FUNC_NetClient)
Fire(): void {
}
@rpc.flags(rpc.FunctionFlags.FUNC_Net | rpc.FunctionFlags.FUNC_NetServer | rpc.FunctionFlags.FUNC_NetReliable)
FireServer(): void {
}
//如果字段设置了CPF_RepNotify,需要增加“OnRep_字段名”为名字的方法
OnRep_dint(): void {
}
}
demos
예제
나만의 가상 머신 구축
- 가상 머신 구축하기 예: 사용자는 (하나 이상의) 가상 머신을 직접 구축할 수 있습니다.
- TsGameInstance.cpp:게임 인스턴스(필요에 따라 다른 곳에 구축할 수도 있음)에서 가상 머신을 구축하는 데모를 보여줍니다.
엔진 클래스 함수 상속
엔진 클래스 상속 기능이 켜져 있으면 시스템은 엔진 클래스를 상속하는 TypeScript의 런타임 환경으로 (기본) 가상 머신을 시작합니다. 추가 가상 머신도 시작하면 이러한 가상 머신은 서로 격리됩니다.
엔진 및 C++와의 TypeScript 상호 작용 예시
가상 머신 구축 예제에서 설명했지만, 실제로는 모든 가상 머신에서 작동합니다.
- QuickStart.ts : 타입스크립트와 UE4 엔진이 서로 호출하는 데모를 시연합니다.
- 엔진 클래스를 상속하는 타입스크립트에서는 기본 VM이 이 파라미터를 전달하지 않기 때문에 argv.getByName("GameInstance")이 정의되지 않은 값을 반환합니다.
- NewContainer.ts : 데모 컨테이너 생성
- AsyncTest.ts : 블루프린트 비동기 로딩, 지연을 비동기/대기 상태로 캡슐화한 지연
- UsingWidget.ts : UI 로딩, 이벤트 바인딩 및 데이터 가져오기 데모
- UsingMixin.ts : Mixin 기능데모
- 调用普通c++类
- TestClass.h : 기본 예제 C++ 클래스 정의
- TestClass.h : 고급 예제 C++ 클래스 정의
- TestClassWrap.cpp : 바인딩(타입스크립트로 내보내기) 선언
- CDataTest.ts : 타입스크립트 호출 데모
실행하려면 TsGameInstance.cpp의 항목을 해당 타입스크립트 이름으로 변경합니다(접미사 없이 이 예제의 기본값은 QuickStart입니다).
편집기 확장 기능
puerts를 사용하여 에디터 확장 프로그램을 작성할 수도 있으며, nodejs 버전의 puerts를 사용하는 경우 많은 수의 npm 라이브러리가 에디터의 빠른 개발에 도움이 됩니다.
예제 프로젝트 링크
- Main.ts : 메뉴, 도구 모음, 도구 체인 드롭다운 버튼, 컨텍스트 메뉴, 명령줄 확장을 보여줍니다.
- DemoWindow.ts : IMGUI 사용 시연(선택 사항)
- NodejsDemo.ts : Nodejs API의 사용법을 보여줍니다.