웹에서의 게임 엔진 - 파트 2 - 데이터 지향 시스템

소개



여러분, 오랜만입니다. 그러나 여기에서 다시 이 3D 웹 엔진에 대한 일련의 게시물을 계속하고 있습니다. 첫 번째 부분을 놓친 경우: 여기에서 얻으십시오: .

데이터 기반 시스템으로



마지막 부분에서는 다음과 같은 시스템 및 구성 요소를 등록했습니다.

const threeJsContext = new ThreeJSContext();

scene.registerSystem(new TransformSystem());
scene.registerSystem(new ThreeJsDynamicMeshSystem(threeJsContext));
scene.registerSystem(new UpAndDownSinSystem());

// cube
const cubeId = "cube";
scene.addEntityForSystem<Transform>(TransformSystem.TYPE, {
    position: {x: 10, y: 10, z: 10,},
    rotation: {x: 0, y: 0, z: 0, w: 1,}
}, cubeId);


당신이 나에게 묻는다면 정말 유연하지 않습니다. 우리는 웹에 있기 때문에 이 모든 것을 일반 인수 및 선언으로 리팩터링하여 장면의 요소를 생성할 엔진에 JSON 파일을 제공할 수 있습니다.

따라서 먼저 해당 시스템의 구현에 의존하지 않도록 각 시스템에 대한 일반 매개변수를 정의해야 합니다.

이것은 통과하는 것보다

new BoxGeometry(5, 5, 5)


하지만 오히려

{
    type: "BoxGeometry",
    width: 5,
    height: 5,
    depth: 5,
}


이것은 실제 구현에서 장면의 선언 부분을 분리하는 이점이 있습니다. 즉, 원하는 경우 물리 라이브러리를 변경하는 맞춤형 webgl 어댑터로 ThreeJ를 교체할 수 있습니다.

여기에 모든 정의를 표시하지는 않겠지만 일반적으로 다음과 같이 보입니다.

import {PackagedQuaternion} from "./Transform";

export type BodyType = "STATIC" | "DYNAMIC" | "KINEMATIC";

export interface PhysicsBase {
    type: BodyType;
}

export interface PhysicsPlane extends PhysicsBase {
    shape: "PLANE";
    rotation: PackagedQuaternion;
}

export interface PhysicsBox extends PhysicsBase {
    shape: "CUBE";
    sizeInMeter: number;
    massInKG: number;
}

export interface PhysicsSphere extends PhysicsBase {
    shape: "SPHERE";
    radiusInMeter: number;
    massInKG: number;
}

export type PhysicsArgs = {
    type: "PHYSIC",
    arg:
        | PhysicsPlane
        | PhysicsBox
        | PhysicsSphere
}


이제 장면 개체를 빌드하는 방법에 대한 일반적인 정의가 있습니다. (여기에서 전체 코드 참조: https://gitlab.noukakis.ch/voidbattlesengine/voidbattlesengineweb/-/tree/chapter-2/src/engine/systems/_meta )

여기서 쿼터니언에 대해 참고할 사항은 다음과 같습니다.
JS의 수치적 정밀도와 JSON에서 내보낸/빨간색 때문에 다음과 같이 오일러 각도로 내보내는 것이 더 좋습니다.

export type PackagedQuaternion = {
    yaw: number,
    pitch: number,
    roll: number
}


파서 및 형식화된 JSON



이제 장면이 포함된 JSON을 구문 분석하고 이 JSON이 예상대로 유효한지 확인해야 합니다. 이를 위해 JSON Schema과 함께 ts-json-schema-generator을 사용하여 해당 스키마를 자동으로 생성하고 ajv을 사용하여 생성된 스키마에 대해 JSON의 유효성을 검사합니다.

다음 명령을 사용하여 스키마를 가져올 수 있습니다.

./node_modules/.bin/ts-json-schema-generator --path 'src/**/*.ts' --type 'SceneType'


지금은 스키마를 ts 파일에 저장하지만 나중에 스키마 생성 및 번들링을 자동화할 것입니다. 스키마는 다음과 같습니다.

{
    "$ref": "#/definitions/SceneType",
    "$schema": "http://json-schema.org/draft-07/schema#",
    "definitions": {
        "BodyType": {
            "enum": [
                "STATIC",
                "DYNAMIC",
                "KINEMATIC"
            ],
            "type": "string"
            ...


이제 "파서"사용 사례를 작성할 준비가 되었습니다. 이 파서는 장면 JSON을 다운로드하고, 스키마에 대해 유효성을 검사하고, 장면 JSON에 설명된 엔티티로 기존 시스템을 채울 책임이 있습니다.

여기에서 사용 사례, 테스트 및 어댑터를 참조하십시오. https://gitlab.noukakis.ch/voidbattlesengine/voidbattlesengineweb/-/tree/chapter-2/src/parser

이제 시스템에 JSON 파일을 제공하고 여기에서 장면을 가져올 수 있습니다.

이제 이것은:

{
  "cube": [
    {
      "type": "TRANSFORM",
      "position": {
        "x": 10,
        "y": 10,
        "z": 10
      },
      "rotation": {
        "yaw": 0,
        "pitch": 0,
        "roll": 0
      }
    },
    {
      "type": "RENDER",
      "geometry": {
        "type": "BoxGeometry",
        "width": 5,
        "height": 5,
        "depth": 5
      },
      "material": {
        "type": "MeshBasicMaterial",
        "color": 15131077
      }
    },
    {
      "type": "PHYSIC",
      "arg": {
        "type": "DYNAMIC",
        "shape": "CUBE",
        "sizeInMeter": 5,
        "massInKG": 1
      }
    }
  ],
  "floor": [
    {
      "type": "TRANSFORM",
      "position": {
        "x": 0,
        "y": -5,
        "z": 0
      },
      "rotation": {
        "yaw": 0,
        "pitch": 0,
        "roll": 0
      }
    },
    {
      "type": "RENDER",
      "geometry": {
        "type": "BoxGeometry",
        "width": 200,
        "height": 1,
        "depth": 200
      },
      "material": {
        "type": "MeshBasicMaterial",
        "color": 12375026
      },
      "corrections": [
        {
          "yaw": 0,
          "pitch": 0,
          "roll": -90
        }
      ]
    },
    {
      "type": "PHYSIC",
      "arg": {
        "type": "STATIC",
        "shape": "PLANE",
        "rotation": {
          "yaw": 0,
          "pitch": 0,
          "roll": -60
        }
      }
    }
  ]
}


결과는 다음과 같습니다.



Cypress와 스크린샷으로 렌더러 테스트



이제 깔끔한 부분(또한 우리가 모든 리팩터링을 만든 이유)은 cypress과 같은 테스트 자동화 도구를 사용하여 자동화된 방식으로 이 앱을 테스트할 수 있다는 것입니다.

이를 사용하여 "X축에서 45도 회전된 큐브 표시"와 같은 시나리오를 정의하고 screenshots to check for regression을 사용할 수 있습니다.



그리고 그것은 렌더러를 테스트합니다. 이제 데이터 기반 애니메이션, 셰이더 등과 같은 고급 주제를 다룰 준비가 된 것 같습니다.

다음 시간까지!

좋은 웹페이지 즐겨찾기