견고한 아이템 시스템 만들기


12

저의 목표는 다음과 같은 것을 처리 할 수있는 가능한 아이템 시스템을 가능한 한 모듈 식으로 만드는 것입니다

  • 업그레이드 가능한 아이템 (+6 Katana)
  • 능력치 수정 제 (+15 민첩)
  • 아이템 수정 자 (% X 확률로 Y 데미지, 동결 확률)
  • 충전식 (30 인용 매직 스태프)
  • 항목 설정 (Y 기능을 활성화하기 위해 X 세트 4 장 세트)
  • 희귀 성 (일반, 고유, 전설)
  • 마력 추출 가능 (일부 제작 재료에 들어감)
  • 제작 가능 (특정 재료로 제작 가능)
  • 소모품 (5 분 % X 공격력, 치유 +15 HP)

* 다음 설정에서 굵게 표시된 기능을 해결할 수있었습니다.

이제 내가 생각한 것을 반영하기 위해 많은 옵션을 추가하려고했습니다. 필요한 모든 기능을 추가 할 계획은 없지만 적합하다고 생각되는대로 구현할 수 있기를 원합니다. 또한 인벤토리 시스템 및 데이터 직렬화와 호환되어야합니다.

상속전혀 사용하지 않고 엔티티 구성 요소 / 데이터 기반 접근 방식을 계획하고 있습니다. 처음에는 다음과 같은 시스템을 생각했습니다.

  • BaseStat : 이동 중에 통계를 보유하는 일반 클래스 (항목 및 캐릭터 통계에도 사용할 수 있음)
  • Item : 목록, 이름, itemtype 및 ui, actionName, description 등과 관련된 것들과 같은 데이터를 보유하는 클래스입니다.
  • 무기 : 무기 인터페이스. 모든 무기에는 IWeapon이 구현 된 자체 클래스가 있습니다. 여기에는 Attack 및 캐릭터 통계에 대한 참조가 있습니다. 무기가 장착되면 데이터 (아이템 클래스의 스탯)가 캐릭터 스탯에 주입됩니다. (베이스 스테이트가 무엇이든, 스탯 보너스로 캐릭터 클래스에 추가됩니다.) 예를 들어, 칼로 캐릭터 스탯에 5 번의 공격을가 합니다. 따라서 BaseStat 는 ( "Attack", 5) (enum도 사용할 수 있습니다)입니다. 이 스탯은 장착시 캐릭터의 "공격"스탯에 보너스 스탯 (다른 클래스)으로 추가됩니다. 따라서 Sword 라는 클래스는 IWeapon 을 구현할 때 생성됩니다.품목 클래스 가 생성됩니다. 따라서 우리는 이 검에 캐릭터 스탯 을 주입 할 수 있고, 공격 할 때 캐릭터 스탯 에서 총 어택 스탯을 가져 와서 공격 방법에 피해를 입힐 수 있습니다.
  • BonusStat : BaseStat를 건드리지 않고 통계를 보너스로 추가하는 방법입니다.
  • 소모품 : IWeapon과 동일한 논리. 다이렉트 스탯을 추가하는 것은 상당히 쉽지만 (+15 hp)이 설정으로 임시 무기를 추가할지는 확실하지 않습니다 (5 분 동안 공격 할 % x).
  • 업그레이드 가능 :이 설정으로 구현할 수 있습니다. UpgradeLevel 을 기본 스탯으로 생각 하며 무기를 업그레이드하면 증가합니다. 업그레이드 할 때 무기의 BaseStat 를 업그레이드 수준에 맞게 다시 계산할 수 있습니다 .

이 시점까지 시스템이 상당히 좋다는 것을 알 수 있습니다. 그러나 다른 기능을 위해, 나는 예를 들어 내 나는이에 Craftable 기능을 구현 할 수 없기 때문에 우리가 뭔가를해야한다고 생각 BaseStat가 이 기능을 처리 할 수 없을 것 내가 붙어있어 곳입니다. 모든 재료를 스탯으로 추가 할 수 있지만 의미가 없습니다.

이를 쉽게 제공 할 수 있도록 다음과 같이 도움이 될만한 몇 가지 질문이 있습니다.

  • 다른 기능을 구현하려면이 설정을 계속해야합니까? 상속없이 가능할까요?
  • 상속없이 이러한 모든 기능을 구현하기 위해 생각할 수있는 방법이 있습니까?
  • 아이템 수정 자에 대해서는 어떻게 달성 할 수 있습니까? 그것은 본질적으로 매우 일반적이기 때문에.
  • 이런 종류의 아키텍처를 구축하는 과정, 권장 사항을 쉽게하기 위해 무엇을 할 수 있습니까?
  • 이 문제와 관련하여 발굴 할 수있는 출처가 있습니까?
  • 나는 상속을 피하려고 노력하지만 상속을 유지하면서 쉽게 유지하면서 쉽게 해결 / 달성 할 것이라고 생각합니까?

질문을 매우 넓게 유지하면서 여러 가지 측면 / 사람들로부터 지식을 얻을 수 있으므로 단 하나의 질문에 자유롭게 대답하십시오.


편집하다


@ jjimenezg93의 답변에 따라 테스트를 위해 C #에서 매우 기본적인 시스템을 만들었습니다. 추가 할 수 있는지 확인하십시오.

public interface IItem
{
    List<IAttribute> Components { get; set; }

    void ReceiveMessage<T>(T message);
}

public interface IAttribute
{
    IItem source { get; set; }
    void ReceiveMessage<T>(T message);
}

지금까지 IItem 및 IAttribute는 기본 인터페이스입니다. 메시지에 대한 기본 인터페이스 / 속성을 가질 필요가 없었으므로 테스트 메시지 클래스를 직접 만들 것입니다. 이제 테스트 수업 :


public class TestItem : IItem
{
    private List<IAttribute> _components = new List<IAttribute>();
    public List<IAttribute> Components
    {
        get
        {
            return _components;
        }

        set
        {
            _components = value;
        }
    }

    public void ReceiveMessage<T>(T message)
    {
        foreach (IAttribute attribute in Components)
        {
            attribute.ReceiveMessage(message);
        }
    }
}

public class TestAttribute : IAttribute
{
    string _infoRequiredFromMessage;

    public TestAttribute(IItem source)
    {
        _source = source;
    }

    private IItem _source;
    public IItem source
    {
        get
        {
            return _source;
        }

        set
        {
            _source = value;
        }
    }

    public void ReceiveMessage<T>(T message)
    {
        TestMessage convertedMessage = message as TestMessage;
        if (convertedMessage != null)
        {
            convertedMessage.Execute();
            _infoRequiredFromMessage = convertedMessage._particularInformationThatNeedsToBePassed;
            Debug.Log("Message passed : " + _infoRequiredFromMessage);

        }
    }
} 

public class TestMessage
{
    private string _messageString;
    private int _messageInt;
    public string _particularInformationThatNeedsToBePassed;
    public TestMessage(string messageString, int messageInt, string particularInformationThatNeedsToBePassed)
    {
        _messageString = messageString;
        _messageInt = messageInt;
        _particularInformationThatNeedsToBePassed = particularInformationThatNeedsToBePassed;
    }
    //messages should not have methods, so this is here for fun and testing.
    public void Execute()
    {
        Debug.Log("Desired Execution Method: \nThis is test message : " + _messageString + "\nThis is test int : " + _messageInt);
    }
} 

필요한 설정입니다. 이제 우리는 시스템을 사용할 수 있습니다 (다음은 Unity 용입니다).

public class TestManager : MonoBehaviour
{

    // Use this for initialization
    void Start()
    {
        TestItem testItem = new TestItem();
        TestAttribute testAttribute = new TestAttribute(testItem);
        testItem.Components.Add(testAttribute);
        TestMessage testMessage = new TestMessage("my test message", 1, "VERYIMPORTANTINFO");
        testItem.ReceiveMessage(testMessage);
    }

}

이 TestManager 스크립트를 장면의 컴포넌트에 첨부하면 디버그에서 메시지가 성공적으로 전달되었음을 알 수 있습니다.


설명하기 위해 : 게임의 모든 항목은 IItem 인터페이스를 구현하고 모든 속성 (이름은 당신을 혼동해서는 안됩니다. 항목 기능 / 시스템을 의미합니다. 그런 다음 메시지를 처리하는 방법이 있습니다 (메시지가 필요한 이유는 추가 예에서 설명 함). 따라서 문맥에 따라 항목에 속성을 첨부하고 나머지는 자동으로 수행 할 수 있습니다. 속성을 쉽게 추가 / 제거 할 수 있기 때문에 매우 유연합니다. 따라서 의사 예제는 마력을 잃을 수 있습니다. Disenchantable (IAttribute)라는 클래스가 있으며 Disenchant 메서드에서 다음을 요청합니다.

  • 재료 나열 (아이템이 마력을 잃었을 때, 플레이어에게 어떤 아이템을 주어야 하는가) 참고 : IItem은 ItemType, ItemID 등을 갖도록 확장되어야합니다.
  • int resultModifier (만약 당신이 마력 제거 기능을 강화하면, 마력을 잃었을 때받은 재료를 증가시키기 위해 int를 전달할 수 있습니다)
  • int failureChance (마법 해제 프로세스에 실패 확률이있는 경우)

기타

이 정보는 DisenchantManager라는 클래스에 의해 제공되며, 아이템을 받고 아이템 (마법 해제시 아이템의 성분)과 플레이어 진행 (resultModifier 및 failureChance)에 따라이 메시지를 형성합니다. 이 메시지를 전달하기 위해이 메시지의 본문 역할을하는 DisenchantMessage 클래스를 만듭니다. 따라서 DisenchantManager는 DisenchantMessage를 채우고 항목으로 보냅니다. 아이템은 메시지를 받아서 첨부 된 모든 속성에 전달합니다. Disenchantable 클래스의 ReceiveMessage 메서드는 DisenchantMessage를 찾기 때문에 Disenchant 특성 만이 메시지를 수신하고 그에 따라 작동합니다. 이것이 나를 위해했던 것처럼 많은 일을 해결하기를 바랍니다 :).



@DMGregory 이봐! 링크 주셔서 감사합니다. 매우 유익한 것처럼 보이지만 불행히도 나는 개념을 파악하기 위해 실제 대화가 필요합니다. 그리고 불행히도 GDCVault의 회원 전용 콘텐츠 인 1 년 동안 495 $는 미친 이야기입니다. (GDCVault 회원 자격이있는 경우 여기서 대화를 찾을 수 있습니다.-,
Vandarthul

"BaseStat"개념이 정확히 제작 가능한 무기를 어떻게 배제 할 수 있습니까?
Attackfarm

그것은 실제로 배제하지는 않지만 내 마음의 맥락에 실제로 맞지 않습니다. "Static"으로 "Wood", 2 및 "Iron", 5를 제작 레시피에 추가하면 칼을 얻을 수 있습니다. BaseStat의 이름을 BaseAttribute로 변경하면이 컨텍스트에서 더 잘 작동한다고 생각합니다. 그러나 여전히 시스템은 그 목적을 달성 할 수 없습니다. 공격력이 5 분-% 50 인 소모품을 생각해보십시오. BaseStat로 어떻게 전달합니까? "Perc_AttackPower", 50이 값은 "Perc 인 경우 정수를 백분율로 처리"로 해결되고 분 정보가 누락됩니다. 내가 무슨 뜻인지 알기를 바랍니다.
Vandarthul

두 번째 생각에 @Attackfarm은이 "BaseStat"개념을 하나의 int 대신 int 목록으로 확장 할 수 있습니다. 따라서 소모품 버프의 경우 "공격", 50, 5, 1을 제공 할 수 있으며 IConsumable은 3 개의 정수, 1-값, 2.-분, 3을 찾습니다. 백분율인지 여부입니다. 그러나 다른 시스템이 들어 와서 int에서만 자신을 설명하도록 강요함에 따라 느낌이 들지 않습니다.
Vandarthul

답변:


6

기본 상속 및 메시징 시스템이있는 엔터티 구성 요소 시스템을 사용하면 확장 성 및 유지 관리 측면에서 원하는 것을 얻을 수 있다고 생각합니다. 물론이 시스템은 내가 생각할 수있는 가장 모듈 식 / 사용자 지정 가능 / 확장 가능하지만 현재 솔루션보다 성능이 떨어질 수 있습니다.

더 설명하겠습니다 :

우선, 인터페이스 IItem와 인터페이스를 만듭니다 IComponent. 저장하려는 모든 항목은에서 상속 받아야하고 IItem, 항목에 영향을 주려는 구성 요소는에서 상속해야합니다 IComponent.

IItem구성 요소의 배열과 처리 방법이 IMessage있습니다. 이 처리 방법은 수신 된 메시지를 저장된 모든 구성 요소에 보냅니다. 그러면 해당 메시지에 관심이있는 구성 요소가 그에 따라 작동하고 다른 구성 요소는이를 무시합니다.

예를 들어, 한 가지 유형의 메시지는 피해 유형이며 공격자와 공격자에게 정보를 제공하므로 해당 피해를 기준으로 분노 막대를 얼마나 많이 타격하고 충전 할 수 있는지 알 수 있습니다. 또는 적의 AI가 자신에게 타격을 입히고 2HP 미만의 피해를 입으면 달리기로 결정할 수 있습니다. 이것들은 멍청한 예이지만 내가 언급 한 것과 비슷한 시스템을 사용하면 이러한 종류의 역학을 대부분 추가하기 위해 메시지와 적절한 처리를 만드는 것 이상을 수행 할 필요가 없습니다.

여기 메시징으로 ECS를 구현 했지만 항목 대신 엔티티에 사용되며 C ++을 사용합니다. 어쨌든, 난 당신이 살펴 경우가 도움이 될 수 있습니다 생각 component.h, entity.h그리고 messages.h. 개선해야 할 것이 많이 있지만 간단한 대학 일에서 저에게 효과적이었습니다.

도움이 되길 바랍니다.


@ jjimenezg93 님, 답변 해 주셔서 감사합니다. 그래서 당신이 설명 한 간단한 예를 자세히 설명하기 위해 : 우리는 다음과 같은 검을 원합니다.-Disenchantable [Component]-Stat Modifier [Component]-Upgradeable [Component] -DISENCHANT-MODIFY_STAT-UPGRADE 항목이이 메시지를 받고 모든 구성 요소를 통과하여이 메시지를 보낼 때마다 각 구성 요소는 주어진 메시지로 수행 할 작업을 알게됩니다. 이론적으로 이것은 굉장한 것 같습니다! 나는 당신의 모범을 확인하지는 않았지만 많이 감사합니다!
Vandarthul

@ Vanarthul 네, 기본적으로 아이디어입니다. 이 방법으로 아이템은 컴포넌트에 대해 전혀 알지 못하므로 전혀 커플 링이 없으며 동시에 원하는 모든 기능을 갖게되며 다른 유형의 아이템간에 공유 할 수도 있습니다. 그것이 당신의 요구에 맞기를 바랍니다!
jjimenezg93
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.