Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

안녕하세요 InfiniteScroll 시스템 중 Dynamic Item Size에 대한 궁금증입니다. #165

Closed
1 of 8 tasks
lumiereent opened this issue Nov 22, 2021 · 25 comments · Fixed by #177
Closed
1 of 8 tasks
Assignees
Labels
bug Issue type : Bug report question Issue type : Question UI Service : UI

Comments

@lumiereent
Copy link

lumiereent commented Nov 22, 2021

Service

  • Adapter
  • AssetManagement
  • Communicator
  • DLST
  • LogViewer
  • Manager
  • UI
  • WebView

Version

2.0.7

Summary

가변길이의 텍스트를 스크롤로 출력하려고 하는데 잘 구현되지 않아 질문 드립니다.

Screenshots

InfiniteScrollItem

Additional context

제가 구현하고자 하는 시스템은 가변길이의 텍스트를 스크롤로 출력 기능입니다.

해당 텍스트는 스크린샷과 같이 텍스트 갯수에 따라 가변적으로 오브젝트 사이즈가 늘어나도록
UGUI에서 자동으로 정렬해주는 컴포넌트인
Contents Size Filter 및 Vertical Layout Group 두가지를 부착하였습니다.

ex1) 오브젝트1의 텍스트는 3라인의 길이 : 높이가 150size
ex2) 오브젝트2의 텍스트는 2라인의 길이 : 높이가 100size

즉 이러한 오브젝트 10개를 텍스트 길이에 따라 가변적으로 InfiniteScroll을 구현하려고 했는데
에러로 인해 잘 구현이 되지 않아서
현재 제가 생각하고있는 기능이 InfiniteScroll을 통해 구현이 가능한 것인지
혹은 다른 방법을 통해 구현할 수 있을지 도움을 얻고자 문의 드립니다.

(에러는 InsertData호출 시 itemShowDataIndex변수가 null이라는 에러였습니다.)

@lumiereent lumiereent added the question Issue type : Question label Nov 22, 2021
@SangYun-nhn SangYun-nhn added the UI Service : UI label Nov 22, 2021
@smflt-nhn
Copy link
Contributor

smflt-nhn commented Nov 22, 2021

ugui에서 Vertical Layout Group와 Contents Size Filter를 중첩으로 사용할 경우 순서에 의해 정상 동작을 하지 않습니다.

일반적으로 Layout Group이 부모 오브젝트이며 Contents Size Filter가 자식 오브젝트이기 때문에

  1. Layout Group 호출(정렬)
  2. Contents Size Fitter(사이즈 변환)

순으로 진행 되어 사이즈에 따른 정렬이 되지 않습니다.

이럴 경우 GpmUI에 포함된 LayoutUpdater 컴포넌트를 사용해 갱신 할 수 있습니다.
LayoutUpdater는 현재 부착된 오브젝트의 ui가 변경할 때 부모 ui 오브젝트가 반영하도록 하는 컴포넌트입니다.
LayoutUpdater를 Contents Size Fitter 오브젝트에 부착할 경우

  1. Layout Group 호출(정렬)
  2. Contents Size Fitter(사이즈 변환)
  3. LayoutUpdater(부모에 이벤트 전달)
  4. Layout Group 호출(정렬)

순으로 사이즈 변환 후 정렬이 되게 됩니다.

ContentSizeSetterSample 셈플 참고 부탁 드립니다.
https://github.com/nhn/gpm.unity/blob/main/docs/UI/ContentSizeSetter/README.md

@lumiereent
Copy link
Author

안녕하세요 답변 감사합니다.

캡처0
캡처

말씀을 듣고 위 스크린샷 처럼

부모 오브젝트에 Vertical Layout Group + TextElementController(InfiniteScroolItem 상속 클래스)
자식 오브젝트에 TextUI + ContentsSizeFilter + Layout Updater

이렇게 배치해 보았는데 LayoutUpdater가 잘 동작하지 않는것 같습니다.
혹시나 해서 자식 오브젝트에 Contents Size Setter 까지 포함시켜 타겟을 부모 오브젝트로 설정해 보았는데

오류

위와 같은 에러가 발생합니다.

@smflt-nhn
Copy link
Contributor

에러는 다른 문제로 보이는데 확인해 보겠습니다.

@smflt-nhn
Copy link
Contributor

InfiniteScroll은 ScrollItem의 길이를 이용해서 작동하고 있습니다.
ScrollItem이 Contents Size Fitter를 사용했을 때 길이가 0으로 나왔을 때 InfiniteScroll의 버그가 확인되었습니다.

빠른 해결을 위해서 InfiniteScroll.cs의 GetNeedItemNumber함수에서

if (itemSize == 0)
{
   itemSize = 1;
}

를 추가 부탁드립니다 위치는 아래와 같습니다.

private int GetNeedItemNumber()
{
    int needItemNumber = 0;

    float itemSize = 0.0f;
    if (dynamicItemSize == true)
    {
        itemSize = minItemSize;
    }
    else
    {
        itemSize = defaultItemSize;
    }
    // 추가
    if (itemSize == 0)
    {
        itemSize = 1;
    }

    if (isVertical == true)
    {
        needItemNumber = (int)(viewport.rect.height / itemSize) + 2;
    }
    else
    {
        needItemNumber = (int)(viewport.rect.width / itemSize) + 2;
    }

    return needItemNumber;
}

그리고 원하는 기능을 위해서 라면 InfiniteScrollItem에도 Size변경이 적용되어야 합니다.
인피니티 스크롤에서 사이즈를 맞춰주는 함수는 OnUpdateItemSize() 입니다.

아래 그림은
InfiniteScrollItem을 상속받은 LayoutGroupScrollItem 스크롤 아이템입니다.
LayoutGrpup과 Contents Size Fitter를 같이 적용했다면 InfiniteScroll 에서도 기본 사이즈가 0입니다.
image

하위 객체들이 LayoutUpdater를 적용했다면 해당 스크롤 아이템도 사이즈 변경이 계산됩니다.
ugui 함수 내에서는 OnRectTransformDimensionsChange함수를 추가하면 사이즈 변경 시 이벤트를 가집니다.
때문에 InfiniteScrollItem을 상속받은 함수 내에 OnRectTransformDimensionsChange를 정의합니다.

public class LayoutGroupScrollItem : InfiniteScrollItem
{
    public override void UpdateData(InfiniteScrollData scrollData)
    {
        base.UpdateData(scrollData);
        
        // 구현
    }

   // 추가
    protected void OnRectTransformDimensionsChange()
    {
        OnUpdateItemSize();
    }
}

이 문제없이 작동할 것입니다. 확인 부탁드립니다.

@lumiereent
Copy link
Author

lumiereent commented Nov 22, 2021

말씀하신 대로 따라하니 정상작동하였습니다.

감사합니다.

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

(추가수정) 스크롤 중 에러사항이 있어 공유드립니다.

캡처

@lumiereent
Copy link
Author

배치는 정상적으로 되는데 스크롤 시

InfiniteScrollItem 상속 프리팹(TextElementController 부착 오브젝트)를

인스펙터로 활성화 시키고 테스트하면 프리팹이 너무 많이 생성되고
인스펙터로 비활성화 시키고 테스트하면 스크롤 시, 위와 같은 오류가 생성됩니다.

@smflt-nhn
Copy link
Contributor

구현이 어떻게 되었는지 셈플이 필요합니다.

예상으로는 InfiniteScrollItem의 UpdateData함수는 한번 호출되는 것이 아닌 스크롤 시 지속적으로 호출됩니다.
InfiniteScroll은 InfiniteScrollItem을 보이는 구간만큼 재사용하여 갱신하기 때문입니다.

LayoutGrpup로 관리하는 것으로 보아 자식 오브젝트를 여러개 생성하는 것으로 보이는데 리스트로 관리해
자식 오브젝트를 비활성화 후 UpdateData시 설정을 하여 관리하거나 삭제 후 재 생성이 관리가 필요합니다.

좀 더 확인해보겠습니다.

@lumiereent
Copy link
Author

lumiereent commented Nov 22, 2021

안녕하세요 스크린샷 및 InfiniteScroll 구현 코드를 첨부합니다.

자식 컴포넌트 때문에 문제 해결에 혼동이 올 것 같아 Layout Group은 제외하고
프리팹 오브젝트는 단일 TextMeshPro 텍스트로 줄였습니다.

즉 제가 구현하고자 하는 기능은 어떠한 텍스트 파일을 읽어와 줄바꿈 기준으로 배열로 분할 후
InfiniteScroll시스템으로 장문의 텍스트를 렉 없이 스크롤 안에 배치하는 목적입니다.

public class TextScrollData : InfiniteScrollData
{
    public readonly string text;

    public TextScrollData(string text)
    {
        this.text = text;
    }
}

public class TextElementController : InfiniteScrollItem
{
    [SerializeField] TextMeshProUGUI _tmpText = null;

    public override void UpdateData(InfiniteScrollData scrollData)
    {
        base.UpdateData(scrollData);

        _tmpText.text = (scrollData as TextScrollData).text;
    }

    protected void OnRectTransformDimensionsChange()
    {
        OnUpdateItemSize();
    }
}

스크롤 구현 스크린샷

캡처

지금 결과는 의도치 않게 많은 오브젝트가 활성화되어 있거나
혹은 스크롤이 위와 같은 오류가 납니다.

스크롤 구현 스크린샷

@smflt-nhn
Copy link
Contributor

원인을 찾았습니다.

InfiniteScroll은 InfiniteScrollItem을 보이는 구간에 재사용합니다.
스코롤 길이는 이전에 저장된 index의 길이를 이용하는데 재사용 시 이전 index의 길이로 ui를 세팅할 때
이번에 추가한 OnRectTransformDimensionsChange가 호출되면서 중간에 계산이 잘못되어 문제가 발생하였습니다.

로직 중간에는 다시 사이즈변경 이벤트가 호출되지 않도록 개선하여 수정하였습니다.
수정한 InfiniteScroll.cs 전달드리니 적용 후 테스트 부탁드립니다.
InfiniteScroll.zip

감사합니다.

@lumiereent
Copy link
Author

테스트 해보니 이젠 문제없이 작동합니다.
빠른 대응 감사합니다

@SangYun-nhn SangYun-nhn added the bug Issue type : Bug report label Nov 24, 2021
@lumiereent
Copy link
Author

안녕하세요

지난번에 유니티 에디터에서는 잘 되는 줄 알았는데 (실제로 에디터에서는 잘 동작합니다.)
해당 기능이 모바일 빌드에서는 또 잘 작동되지 않습니다.

스크롤을 하면서 지난번처럼 에러가 발생하진 않는데
텍스트가 막 겹쳐서 나오는 현상이 있습니다.

캡처1

캡처2

그리고 스크롤을 내리면 겹쳐진 부분이 갑자기 사라지는 등의 현상이 있습니다.
(캡쳐1 -> 캡쳐2 는 스크롤을 살짝 내렸을 때 변경된는 사항입니다.)

@smflt-nhn
Copy link
Contributor

스크롤 재사용하면서 각 행의 길이가 제대로 적용되지 않은 문제로 보이는데 좀 더 자세히 살펴보겠습니다.

@lumiereent
Copy link
Author

몇가지 테스트 후 나타나는 현상을 공유하고자 합니다.

스크린샷1과 2는 InfiniteScroolItem과 Contents Size Filter가
포함된 오브젝트입니다.

[스크린샷1] 인스펙터에서 활성화 상태로 프리팹을 생성한 경우
활성화

유니티 에디터 : 무수히 많은 오브젝트가 생성되지만 출력 자체는 정상 (오브젝트가 많이 활성화되서 렉이 걸립니다.)
모바일 : 무수히 많은 오브젝트가 생성되고 아이템 사이즈가 0으로 할당되는 듯한 결과물

-스크린샷1의 모바일 결과-
결과1

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

[스크린샷2] 인스펙터에서 비활성화 상태로 프리팹을 생성한 경우
비활성화

유니티 에디터 : 정상적으로 출력
모바일 : DynamicSize가 적용되지 않는 현상으로 보입니다. (텍스트 길이에 상관없이 균일한 사이즈)

-스크린샷2의 모바일 결과
캡처1

팝업 내부에 사용된 텍스트파일 리소스 및 로딩 방식도 같이 첨부합니다.
terms_of_service.txt

var texts = Resources.Load("terms_of_service").text.Split("\n".ToCharArray(), StringSplitOptions.None);

@SangYun-nhn SangYun-nhn reopened this Dec 7, 2021
@smflt-nhn
Copy link
Contributor

안녕하세요. 수정이 늦었습니다.
동적 스크롤 로직 개선된 gpm ui 패키지 전달 드립니다.
문제 발생 시 피드백 부탁 드립니다.

gpm_ui_v2.0.8.zip

@r31ack
Copy link

r31ack commented Dec 27, 2021

아직 동일한 현상이 계속되는것 같습니다.
(에디터에서는 정상, 모바일에서는 동적 기능이 작동 안하는 현상)

추가적으로 새로운 패키지에서는 에디터 다음과 같은 에러도 나오는데
스크롤 기능에 직접적인 영향을 일으키는 에러라는 생각은 들지 않습니다.
Tmp 스크롤 에러

※지금은 시일이 좀 지난 관계상 이전 문제는
텍스트 길이를 강제로 잘라 동적 시스템을 쓰고있지 않은 고정 크기의 스크롤 시스템으로
라이브 서비스 중인 상태라
패키지의 개선 변화에 대해 세부적으로 테스트하기가 힘든 상황입니다.

혹시나 동적 스크롤 로직 개선에 대한 유니티 테스트용 씬을 공유해주신다면
다른 샘플 프로젝트에서 테스트후 피드백을 드리겠습니다.

@smflt-nhn
Copy link
Contributor

셈플 전달 드립니다.
이전에 전달 드린 패키지에 셈플 적용 하시고 테스트 하시면 될 것 같습니다.
ContentsSizeFitterSample.zip

이전 버전에 동적 시스템에서 길이 계산, 스크롤 오브젝트 생성 문제를 확인해 수정했습니다
현재 버전 모바일 환경에서 제대로 작동 되어서 원인 파악이 쉽지 않습니다.
피드백 주실 때 유니티 버전, textMeshPro 패키지 버전, android, ios, 여부도 공유도 부탁 드립니다.

@r31ack
Copy link

r31ack commented Dec 27, 2021

테스트 씬을 통해 테스트 및 안드로이드 빌드해 보니
아래 에러가 지속적으로 나오는 것 이외에 기능은 정상적으로 작동합니다.

Unity 2019.4.16f1 LTS / TmpPro 2.1.6

Trying to remove TextElementController(Clone) (TMPro.TextMeshProUGUI) from rebuild list while we are already inside a rebuild loop. This is not supported.
UnityEngine.UI.CanvasUpdateRegistry:UnRegisterCanvasElementForRebuild

@smflt-nhn
Copy link
Contributor

네 확인 감사합니다.
해당 에러는 OnRectTransformDimensionsChange 내부에서 OnUpdateItemSize를 통해
인피니티 스크롤을 처리 했을 때 보이지 않는 스크롤을 숨김처리하면서 발생하는 현상입니다.

Contents Size Fitter제거 후 아래와 같이 사이즈를 설정하여
OnRectTransformDimensionsChange 에서 동작하지 않게 하면 나오지 않습니다.
preferredHeight 로 TextMeshProUGUI의 높이를 미리 계산할 수 있습니다.

LayoutGroupScrollItem.cs

LayoutGroupScrollItemData itemData = (LayoutGroupScrollItemData)scrollData;
text.text = itemData.description;

RectTransform rectTransform = (RectTransform)transform;
SetSize(new Vector2(rectTransform.sizeDelta.x, text.preferredHeight));

@smflt-nhn
Copy link
Contributor

네 확인 감사합니다. 해당 에러는 OnRectTransformDimensionsChange 내부에서 OnUpdateItemSize를 통해 인피니티 스크롤을 처리 했을 때 보이지 않는 스크롤을 숨김처리하면서 발생하는 현상입니다.

Contents Size Fitter제거 후 아래와 같이 사이즈를 설정하여 OnRectTransformDimensionsChange 에서 동작하지 않게 하면 나오지 않습니다. preferredHeight 로 TextMeshProUGUI의 높이를 미리 계산할 수 있습니다.

LayoutGroupScrollItem.cs

LayoutGroupScrollItemData itemData = (LayoutGroupScrollItemData)scrollData;
text.text = itemData.description;

RectTransform rectTransform = (RectTransform)transform;
SetSize(new Vector2(rectTransform.sizeDelta.x, text.preferredHeight));

셈플 포함하여 전달드립니다
sample_dynamic_tmp.zip

@r31ack
Copy link

r31ack commented Dec 27, 2021

감사합니다.
위 에러가 나지않고 잘 작동합니다.

@r31ack
Copy link

r31ack commented Dec 28, 2021

다만 위 업데이트 이후 LogViewer쪽에 영향이 있는지 에러코드가 발생합니다.

@smflt-nhn
Copy link
Contributor

적용 후 확인해보겠습니다.

@smflt-nhn
Copy link
Contributor

적용 후 셈플로 테스트 해봤을 때 영향이 보이지 않는데 좀 더 자세히 알 수 있을까요? 좀 더 확인해 보겠습니다.

@r31ack
Copy link

r31ack commented Dec 29, 2021

재현 과정은 infiniteScroll 내부의 아이템이 0개인 경우라고 추측했지만
아이템이 있는 경우에도 에러가 발생해 정확한 원인은 모르겠습니다.

게임 패키지 매니저에 있는 v2.0.7의 코드로 복원시키면 정상작동합니다.
v2.0.8에서 발생하는 에러 로그를 3가지 첨부합니다.

NullReferenceException: Object reference not set to an instance of an object
Gpm.Ui.InfiniteScroll.UpdateShowItem (System.Boolean forceUpdateData) (at Assets/GPM/UI/Scripts/InfiniteScroll.cs:675)
Gpm.Ui.InfiniteScroll.OnValueChanged (UnityEngine.Vector2 value) (at Assets/GPM/UI/Scripts/InfiniteScroll.cs:808)
UnityEngine.Events.InvokableCall`1[T1].Invoke (T1 args0) (at <29ad182faa3f478c9310d6a2e7143c15>:0)
UnityEngine.Events.UnityEvent`1[T0].Invoke (T0 arg0) (at <29ad182faa3f478c9310d6a2e7143c15>:0)
UnityEngine.UI.ScrollRect.LateUpdate () (at C:/Program Files/Unity/Hub/Editor/2019.4.16f1/Editor/Data/Resources/PackageManager/BuiltInPackages/com.unity.ugui/Runtime/UI/Core/ScrollRect.cs:860)
NullReferenceException: Object reference not set to an instance of an object
Gpm.Ui.InfiniteScroll.UpdateShowItem (System.Boolean forceUpdateData) (at Assets/GPM/UI/Scripts/InfiniteScroll.cs:675)
Gpm.Ui.InfiniteScroll.CheckNeedMoreItem () (at Assets/GPM/UI/Scripts/InfiniteScroll.cs:553)
Gpm.Ui.InfiniteScroll.ResizeScrollView () (at Assets/GPM/UI/Scripts/InfiniteScroll.cs:99)
Gpm.LogViewer.Internal.LogList.Resize () (at Assets/GPM/LogViewer/Scripts/Internal/Viewer/ConsoleView/LogList.cs:38)
Gpm.LogViewer.Internal.ConsoleView.SetOrientation (UnityEngine.ScreenOrientation orientation) (at Assets/GPM/LogViewer/Scripts/Internal/Viewer/ConsoleView/ConsoleView.cs:42)
Gpm.LogViewer.Internal.Viewer.UpdateOrientation () (at Assets/GPM/LogViewer/Scripts/Internal/Viewer/Viewer.cs:139)
Gpm.LogViewer.Internal.Viewer.Update () (at Assets/GPM/LogViewer/Scripts/Internal/Viewer/Viewer.cs:99)
NullReferenceException: Object reference not set to an instance of an object
Gpm.Ui.InfiniteScroll.UpdateShowItem (System.Boolean forceUpdateData) (at Assets/GPM/UI/Scripts/InfiniteScroll.cs:675)
Gpm.Ui.InfiniteScroll.InsertData (Gpm.Ui.InfiniteScrollData data) (at Assets/GPM/UI/Scripts/InfiniteScroll.cs:248)
Gpm.LogViewer.Internal.LogList.Insert (Gpm.LogViewer.Internal.Log+LogData data, System.Boolean showPlayTime, System.Boolean showSceneName) (at Assets/GPM/LogViewer/Scripts/Internal/Viewer/ConsoleView/LogList.cs:92)
Gpm.LogViewer.Internal.ConsoleView.UpdateShowLog () (at Assets/GPM/LogViewer/Scripts/Internal/Viewer/ConsoleView/ConsoleView.cs:194)
Gpm.LogViewer.Internal.ConsoleView.Update () (at Assets/GPM/LogViewer/Scripts/Internal/Viewer/ConsoleView/ConsoleView.cs:155)

@smflt-nhn
Copy link
Contributor

Logview에서 item 최초 초기화 하기전에 리사이즈 호출 시 발생하는 문제로 확인 되었습니다. 수정 및 예외 처리 강화 하였습니다. 곧 해당 빌드로 공식 배포하겠습니다. 감사합니다.

gpm_ui_v2.0.8.zip

smflt-nhn added a commit that referenced this issue Jan 13, 2022
### Added
* InfiniteScroll
    * ScrollItem active 함수 추가
    * ScrollItem Item size 설정 함수 추가

### Updated
* InfiniteScroll
    * dynamicItemSize 환경에서 가변길이 ScrollItem 대응 가능하도록 수정 로직 수정[(165)](#165)

### Fixed
* InfiniteScroll
    * dynamicItemSize 환경에서 보이지 않는 ScrollItem 오브젝트 활성화 되는 문제 수정
@smflt-nhn smflt-nhn linked a pull request Jan 13, 2022 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issue type : Bug report question Issue type : Question UI Service : UI
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants