/// <summary> /// 현재 존재하는 오브젝트들만 toLayer 의 상태에 맞춰 트랜지션. (자동재생 아님) /// 다음으로 넘기기 위해 Swipe하는 도중에 화면에 보여지는 상태. /// </summary> /// <param name="toLayer"></param> /// <param name="ratio">트랜지션 비율. 0 : 현재 상태 그대로, 1 : 완전히 toLayer 상태로</param> /// <param name="backward">진행 반대 방향으로 swipe를 한 경우에는 false</param> public void OldElementOnlyTransition(FSNSnapshot.Layer toLayer, float ratio, bool backward) { UpdateTargetLayerDiff(toLayer); // 비교 업데이트 // *** 유지되는 오브젝트들 IterateMatchingUIDs((int uId) => { m_objectDict[uId].TransitionWith(toLayer.GetElement(uId) as ElmT, ratio); }); // *** 다음에 사라지는 오브젝트들 IterateOnlyInThisUIDs((int uId) => { var currentElem = toLayer.GetRemovedElementOrNull(uId) // (정방향) 다음 레이어에 지워지는 해당 오브젝트에 관한 정보가 있다면 얻어오고 ?? m_curLayerRef.GetElement(uId); // 아니면 현재 레이어의 해당 오브젝트를 얻는다 var finalElem = (!backward)? currentElem.GenericFinalState // 정방향일 경우 마지막 스테이트로 움직인 뒤 소멸, : currentElem.GenericInitialState; // 역방향일 경우 최초 스테이트로 움직인 뒤 소멸해야한다. m_objectDict[uId].TransitionWith(finalElem as ElmT, ratio); }); }
/// <summary> /// 오브젝트 초기 세팅 /// </summary> /// <param name="segment"></param> /// <param name="layer"></param> void SetElementInitial(SegT segment, FSNSnapshot.Layer layer) { int uid; if (!FindUIDFromLookupDict(segment.objectName, out uid, layer)) // 이름으로 uid 찾기 { Debug.LogError("cannot find SnapshotElem named " + segment.objectName); } var elem = layer.GetElement(uid) as ElemT; SetElemBySegProperties(elem.InitialState as ElemT, segment); // 설정값들 세팅 OnSetElementInitial(segment, layer, elem); // 추가동작 }
/// <summary> /// 오브젝트 파괴 세팅 /// </summary> /// <param name="segment"></param> /// <param name="layer"></param> void SetElementFinal(SegT segment, FSNSnapshot.Layer layer) { int uid; if (!FindUIDFromLookupDict(segment.objectName, out uid, layer)) // 이름으로 uid 찾기 { Debug.LogError("cannot find SnapshotElem named " + segment.objectName); } var elem = layer.GetElement(uid) as ElemT; AutoSetFinalState(elem); // Final State가 하나도 세팅되지 않은 경우, 기본값부터 세팅 SetElemBySegProperties(elem.FinalState as ElemT, segment); // 마지막 설정값들 세팅 OnSetElementFinal(segment, layer, elem); // 추가 동작 }
/// <summary> /// 텍스트들을 일정 방향으로 모두 밀어내며 삭제한다. /// </summary> /// <param name="layer"></param> /// <param name="direction"></param> private static void ClearTextsToDirection(FSNSnapshot.Layer layer, FSNInGameSetting.FlowDirection direction) { Vector3 screenHalf = FSNEngine.Instance.ScreenDimension * 0.5f; // 화면 크기 절반 Vector3 dirVec = FSNInGameSetting.GetUnitVectorFromFlowDir(direction); // 흐름 방향 벡터 var uidList = layer.UniqueIDList; int count = uidList.Count; int[] removeIDList = new int[uidList.Count]; uidList.CopyTo(removeIDList, 0); for (int i = 0; i < count; i++) { var textElem = layer.GetElement(removeIDList[i]) as SnapshotElems.Text; textElem.FinalState.Position = textElem.Position + Vector3.Scale(screenHalf, dirVec); // 화면 절반 거리만큼 해당 방향으로 이동 layer.RemoveElement(removeIDList[i]); } }
/// <summary> /// 오브젝트 세팅 커맨드 /// </summary> /// <param name="segment"></param> /// <param name="layer"></param> void SetElement(SegT segment, FSNSnapshot.Layer layer) { int uid; if (!FindUIDFromLookupDict(segment.objectName, out uid, layer)) // 이름으로 uid 찾기 { Debug.LogError("cannot find SnapshotElem named " + segment.objectName); } var elem = layer.GetElement(uid) as ElemT; SetElemBySegProperties(elem, segment); // 설정값들 세팅 elem.motionState = SnapshotElems.ObjectBase <ElemT> .State.MotionKey; AutoSetFinalState(elem, false); // Final State가 세팅되지 않은 객체에 한해서만 Final State를 임시로 계속 만들어주기 CalculateStates(elem); // 지금까지 좌표값들 보간 OnSetElement(segment, layer, elem); // 추가 동작 }
/// <summary> /// 오브젝트 삭제 커맨드 처리 /// </summary> /// <param name="segment"></param> /// <param name="layer"></param> void RemoveElement(SegT segment, FSNSnapshot.Layer layer) { int uid; if (!FindUIDFromLookupDict(segment.objectName, out uid, layer)) // 이름으로 uid 찾기 { Debug.LogError("cannot find SnapshotElem named " + segment.objectName); } var elem = layer.GetElement(uid) as ElemT; AutoSetFinalState(elem); // Final State가 하나도 세팅되지 않은 경우, 기본값부터 세팅 SetElemBySegProperties(elem, segment); // 마지막 설정값들 세팅 elem.motionState = SnapshotElems.ObjectBase <ElemT> .State.MotionKey; OnRemoveElement(segment, layer, elem); // 추가 동작 실행 CalculateStates(elem); // 지금까지 좌표값들 보간 RemoveFromLookupDict(segment.objectName, layer); // 제거 layer.RemoveElement(uid); }
/// <summary> /// 트랜지션 애니메이션 시작. /// </summary> /// <param name="toLayer"></param> /// <param name="startRatioForOlds">기존 오브젝트들은 해당 비율부터 애니메이션 시작</param> /// <param name="backward">진행 반대 방향으로 swipe를 한 경우에는 false</param> /// <returns>트랜지션이 모두 끝나는데 걸리는 시간</returns> public float StartTransition(FSNSnapshot.Layer toLayer, IInGameSetting nextSetting, float startRatioForOlds, bool backward) { UpdateTargetLayerDiff(toLayer); // 비교 업데이트 float longestDuration = 0f; // 트랜지션 중 제일 오래걸리는 것의 시간 // *** 유지되는 오브젝트들 IterateMatchingUIDs((int uId) => { var elem = toLayer.GetElement(uId) as ElmT; float trTime = elem.TransitionTime / nextSetting.TransitionSpeedRatio; // 전환속도 비율 적용 m_objectDict[uId].DoTransition(elem, startRatioForOlds, trTime, false); if (longestDuration < trTime) { longestDuration = trTime; // 제일 긴 트랜지션 시간 추적하기 } }); // *** 다음에 사라지는 오브젝트들 IterateOnlyInThisUIDs((int uId) => { var currentElem = m_curLayerRef.GetElement(uId); // 다음 레이어에 없어질 현재 레이어 객체 var refelem = toLayer.GetRemovedElementOrNull(uId) // (정방향) 다음 레이어에 지워지는 해당 오브젝트에 관한 정보가 있다면 이것을 사용 ?? currentElem; // 아니면 현재 레이어의 해당 오브젝트를 사용하여 finalState를 구한다 var finalElem = (!backward)? refelem.GenericFinalState // 정방향일 경우 마지막 스테이트로 움직인 뒤 소멸, : refelem.GenericInitialState; // 역방향일 경우 최초 스테이트로 움직인 뒤 소멸해야한다. float trTime = finalElem.TransitionTime / nextSetting.TransitionSpeedRatio; // 전환속도 비율 적용 m_objectDict[uId].DoTransition(finalElem as ElmT, startRatioForOlds, trTime, true); if (longestDuration < trTime) { longestDuration = trTime; // 제일 긴 트랜지션 시간 추적하기 } }); // *** 다음에 처음 등장하는 오브젝트들 IterateOnlyInOtherUIDs((int uId) => { var currentElem = toLayer.GetElement(uId); // 다음 레이어의 해당 객체 var refelem = m_curLayerRef.GetRemovedElementOrNull(uId) // (역방향) 현재 레이어에 지워진 오브젝트의 정보가 있다면 그것을 사용, ?? currentElem; // 아니면 다음 레이어의 오브젝트를 참조해서 InitialState를 구한다 var initialElem = backward? refelem.GenericFinalState : refelem.GenericInitialState; // 역방향이면 finalState, 정방향이면 InitialState 로 초기세팅한다 float trTime = initialElem.TransitionTime / nextSetting.TransitionSpeedRatio; // 현재 상태로 transition하지만 시간값은 최초 상태값에 지정된 걸 사용한다. var newobj = AddNewLayerObject(initialElem as ElmT, nextSetting); newobj.DoTransition(currentElem as ElmT, 0, trTime, false); if (longestDuration < trTime) { longestDuration = trTime; // 제일 긴 트랜지션 시간 추적하기 } }); // NOTE : 트랜지션이 완전히 끝난 뒤에 레이어를 교체해야할 수도 있다. 이슈가 생기면 그때 바꾸자... m_curLayerRef = toLayer; // 현재 레이어를 트랜지션 타겟 레이어로 교체. OnLayerTransitionStart(toLayer); // 이벤트 호출 return(m_useTransitionDelay? longestDuration : 0); // 트랜지션 딜레이를 사용하지 않는다면 딜레이 시간은 0으로 }
/// <summary> /// Layer 안에 들어있는 텍스트들을 특정 방향으로 모두 밀어낸다. 알파값도 변경. 수명이 다 된 것은 제거 처리. /// </summary> /// <param name="layer">변경할 레이어 (이미 복제된 상태여야함)</param> /// <param name="direction"></param> /// <param name="newTextSize"></param> private static void PushTextsToDirection(FSNSnapshot.Layer layer, FSNInGameSetting.FlowDirection direction, Vector2 newTextSize, float paraSpacing = 0) { Vector2 dirVec = FSNInGameSetting.GetUnitVectorFromFlowDir(direction); List <int> UIDtoRemove = new List <int>(); // 삭제 리스트 foreach (var uId in layer.UniqueIDList) { var textElem = layer.GetElement(uId) as SnapshotElems.Text; int elemAge = textElem.ChainedParentCount; // 텍스트의 종류에 따라서 다른 룰을 적용한다. if (textElem.type == SnapshotElems.Text.Type.Normal) // ** 일반 텍스트 { Vector3 transVec = Vector2.Scale(newTextSize, dirVec); // 이동할 벡터 양 if (elemAge > 0) { transVec += (Vector3)(dirVec * paraSpacing); // 최초에 등장한 이후엔 문단 간격도 적용 } if (elemAge < c_textLife) // 텍스트가 아직 살아있어야하는 경우 { textElem.Alpha = (float)(c_textLife - elemAge) / (float)c_textLife; textElem.Position = textElem.Position + transVec; CopyCurrentToFinal(textElem); // 임시 FinalState } else { // 텍스트가 죽어야하는 경우 textElem.FinalState.Position = textElem.Position + transVec; UIDtoRemove.Add(uId); } } else { // ** 기타 (선택지 관련 텍스트) int killAge = textElem.type == SnapshotElems.Text.Type.OptionTexts? 1 : 2; //(선택한 텍스트는 1턴 더 살아있어야 하므로) if (elemAge == killAge) // 없어지는 타이밍 { // NOTE : 현재 구조상의 문제로 인해 분기점 이후 바로 없어지는 오브젝트의 FinalState를 여러개 둘 수 없음. // 따라서 분기점 이후에도 한번은 오브젝트를 살려놓은 뒤 안보이게만 하고 다음번에 없애는 식으로. Vector2 halfScreen = FSNEngine.Instance.ScreenDimension / 2f; Vector3 transVec = Vector2.Scale(dirVec, halfScreen); textElem.Position = textElem.Position + transVec; textElem.Alpha = 0f; // TODO : Alpha를 0으로 하는 것 이외에 실제로 visible을 끌 수 있는 방법이 있다면 사용하도록 한다. 지금도 딱히 문제는 없긴 한데... } else if (elemAge == killAge + 1) // 원래 없어져야했던 타이밍이 지나고 나서 실제로 없앤다. { UIDtoRemove.Add(uId); } } } int rmvCount = UIDtoRemove.Count; for (int i = 0; i < rmvCount; i++) // 삭제 리스트 처리 { layer.RemoveElement(UIDtoRemove[i]); } }