확장 가능한 자산 로딩 시스템을 어떻게 구성해야합니까?


19

Java의 취미 게임 엔진의 경우 단순하지만 유연한 자산 / 자원 관리자를 코딩하고 싶습니다. 자산은 사운드, 이미지, 애니메이션, 모델, 텍스처 등입니다. 몇 시간 동안 탐색하고 일부 코드 실험을 한 후에도이 디자인 방법을 잘 모르겠습니다.

특히 특정 자산 유형이로드되는 방법과 자산이로드되는 위치를 추상화하는 방식으로 관리자를 설계하는 방법을 찾고 있습니다. 나머지 프로그램에 대해 알 필요없이 파일 시스템과 RDBMS 스토리지를 모두 지원하고 싶습니다. 마찬가지로 XML 인 애니메이션 설명 애셋 (FPS, 렌더링 할 프레임, 스프라이트 이미지 참조 등)을 추가하고 싶습니다. XML 파일을 찾아서 읽고 AnimationAsset해당 정보가 있는 클래스를 만들고 반환하는 기능을 사용하여 클래스를 작성할 수 있어야합니다 . 데이터 기반 디자인을 찾고 있습니다.

나는에 대한 정보를 많이 찾을 수 있습니다 무엇을 하지만에, 자산 관리자가해야 할을 하는 방법 을 수행하는. 관련된 제네릭은 어떤 형태의 클래스 계단식 또는 어떤 형태의 도우미 클래스를 초래하는 것으로 보입니다. 그러나 나는 개인적인 해킹이나 합의점처럼 보이지 않는 명확한 예를 보지 못했습니다.

답변:


23

나는 자산 관리자 에 대해 생각하지 않고 시작합니다 . "관리자"와 같이 느슨하게 정의 된 용어로 아키텍처를 생각하면 양탄자 아래에서 많은 세부 사항을 정신적으로 쓸어 버리게되어 결과적으로 솔루션을 결정하기가 더 어려워집니다.

기본 요구 사항 저장소를 추상화하고 지원되는 유형 집합을 확장 할 수있는 리소스로드 메커니즘을 만드는 것과 관련이있는 것처럼 보이는 특정 요구에 집중하십시오. 예를 들어 이미로드 된 리소스의 캐싱과 관련하여 실제로는 아무것도 없습니다. 단일 책임 원칙에 따라 자산 캐시를 별도의 엔터티로 구축하고 다른 두 인터페이스를 집계해야 하기 때문에 좋습니다. , 적절한.

특정 관심사를 해결하려면 로더가 자산 자체를로드하지 않고 특정 유형의 자산로드에 맞게 조정 된 인터페이스에 책임을 위임하도록 로더를 설계해야합니다. 예를 들면 다음과 같습니다.

interface ITypeLoader {
  object Load (Stream assetStream);
}

스트림에서 특정 유형의 데이터를로드하도록 각 새 클래스를 조정하여이 인터페이스를 구현하는 새 클래스를 작성할 수 있습니다. 스트림을 사용하면 형식 로더를 일반적인 스토리지에 구애받지 않는 인터페이스에 대해 쓸 수 있으며 디스크 나 데이터베이스에서로드하기 위해 하드 코딩 할 필요가 없습니다. 이를 통해 네트워크 스트림에서 에셋을로드 할 수도 있습니다 (게임이 콘솔에서 실행되고 편집 도구가 네트워크에 연결된 PC에서 실행될 때 에셋의 핫 리로드를 구현하는 데 매우 유용 할 수 있음).

기본 자산 로더는 다음 유형별 로더를 등록하고 추적 할 수 있어야합니다.

class AssetLoader {
  public void RegisterType (string key, ITypeLoader loader) {
    loaders[key] = loader;
  }

  Dictionary<string, ITypeLoader> loaders = new Dictionary<string, ITypeLoader>();
}

여기에 사용 된 "키"는 원하는 것이 될 수 있으며 문자열 일 필요는 없지만 시작하기 쉽습니다. 이 키는 사용자가 특정 자산을 식별하는 방법을 고려하여 적절한 로더를 찾는 데 사용됩니다. 구현시 파일 시스템이나 데이터베이스를 사용하고 있다는 사실을 숨기고 싶기 때문에 파일 시스템 경로 또는 이와 유사한 방식으로 자산을 참조하는 사용자를 가질 수 없습니다.

사용자는 최소한의 정보가있는 자산을 참조해야합니다. 어떤 경우에는 파일 이름만으로 충분하지만 유형 / 이름 쌍을 사용하는 것이 바람직하므로 모든 것이 매우 명확합니다. 따라서 사용자는 애니메이션 XML 파일 중 하나의 명명 된 인스턴스를로 참조 할 수 있습니다 "AnimationXml","PlayerWalkCycle".

여기에 AnimationXml등록한 키 AnimationXmlLoader가 구현 IAssetLoader됩니다. 분명히 PlayerWalkCycle특정 자산을 식별합니다. 유형 이름과 리소스 이름이 주어지면 자산 로더는 해당 자산의 원시 바이트에 대한 영구 저장소를 쿼리 할 수 ​​있습니다. 여기에서 최대한의 일반성을 얻으려고 할 때 로더에 스토리지 액세스 수단을 작성할 때이를 전달하여 나중에 스트림을 제공 할 수있는 것으로 대체 할 수 있습니다.

interface IAssetStreamProvider {
  Stream GetStream (string type, string name);
}

class AssetLoader {
  public AssetLoader (IAssetStreamProvider streamProvider) {
    provider = streamProvider;
  }

  object LoadAsset (string type, string name) {
    var loader = loaders[type];
    var stream = provider.GetStream(type, name);

    return loader.Load(stream);
  }

  public void RegisterType (string type, ITypeLoader loader) {
    loaders[type] = loader;
  }

  IAssetStreamProvider provider;
  Dictionary<string, ITypeLoader> loaders = new Dictionary<string, ITypeLoader>();
}

매우 간단한 스트림 제공자는 지정된 자산 루트 디렉토리에서 이름이 지정된 서브 디렉토리 를 찾고 스트림으로 type이름 지정된 파일의 원시 바이트를로드하여 name리턴합니다.

간단히 말해서, 여기에있는 것은 다음과 같은 시스템입니다.

  • 어떤 종류의 백엔드 스토리지 (디스크, 데이터베이스, 네트워크 스트림 등)에서 원시 바이트를 읽는 방법을 알고있는 클래스가 있습니다.
  • 원시 바이트 스트림을 특정 종류의 리소스로 변환하여 반환하는 방법을 알고있는 클래스가 있습니다.
  • 실제 "자산 로더"에는 위의 모음이 있으며 스트림 제공자의 출력을 유형별 로더로 파이프하여 콘크리트 자산을 생성하는 방법을 알고 있습니다. 스트림 제공자 및 유형별 로더를 구성하는 방법을 노출함으로써 실제 자산 로더 코드를 수정하지 않고도 클라이언트 (또는 사용자)가 확장 할 수있는 시스템이 있습니다.

몇 가지주의 사항 및 최종 참고 사항 :

  • 위의 코드는 기본적으로 C #이지만 최소한의 노력으로 거의 모든 언어로 번역해야합니다. 이를 용이하게하기 위해 오류 확인이나 올바르게 사용하는 IDisposable것과 다른 언어로 직접 적용되지 않는 다른 관용구를 많이 생략했습니다 . 그것들은 독자에게 숙제로 남아 있습니다.

  • 마찬가지로 object위와 같이 콘크리트 자산을 반환 하지만 원하는 경우 제네릭이나 템플릿 또는 더 구체적인 객체 유형을 생성하는 데 사용할 수 있습니다 (작업해야합니다).

  • 위와 같이, 나는 여기서 캐싱을 다루지 않습니다. 그러나 동일한 종류의 일반 성과 구성 가능성으로 캐싱을 쉽게 추가 할 수 있습니다. 사용해보십시오!

  • 이 작업을 수행하는 방법은 많고 많으며, 방법이 없거나 합의가 없기 때문에 찾을 수 없었습니다. 이 답변을 고통스럽게 긴 코드로 변환하지 않고 특정 지점을 가로 질러 충분한 코드를 제공하려고했습니다. 이미 너무 오래되었습니다. 궁금한 점이 있으면 언제든지 의견을 말하거나 채팅 에서 나를 찾으십시오 .


1
좋은 질문과 좋은 대답은 솔루션을 데이터 중심 디자인뿐만 아니라 데이터 중심 방식으로 사고
Patrick Hughes

매우 훌륭하고 심층적 인 답변. 나는 당신이 내 질문을 어떻게 해석했는지를 좋아하고 내가 그것을 잘못 저질렀을 때 알아야 할 것을 정확히 말해주었습니다. 감사! 우연히 스트림에 대한 리소스를 알려 주시겠습니까?
user8363

"스트림"은 바이트 또는 데이터의 시퀀스 (잠재적으로 끝을 결정할 수 없음)입니다. 나는 C #의 Stream에 대해 특별히 생각하고 있었지만 Java의 스트림 클래스 에 더 관심이있을 것입니다.하지만 Java를 너무 많이 알지 못해 사용하기에 이상적인 클래스가 아닐 수도 있습니다.

스트림은 일반적으로 주어진 스트림 객체가 일반적으로 스트림 내에서 현재 읽기 또는 쓰기 위치를 가지고 있고 그에서 수행하는 모든 IO가 해당 위치에서 발생한다는 점에서 일반적으로 상태가 양호합니다. 그들은 본질적으로 "원시 데이터가 있고 읽을 곳, 읽을 곳, 할 일이 있습니다"라고 말하고 있기 때문입니다.

이 접근 방식은 SOLIDOOP 의 핵심 원칙 중 일부를 존중 합니다. 브라보.
Adam Naylor
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.