
#20221129 :: UI 드래그 앤 드롭
#20221130 :: 위치변경 동기화(parent~child, Linq), 아이디어 메모, 라인렌더러
#20221129 :: UI 드래그 앤 드롭
문제
- 씬이동 일어날 때 등록해 놓은 노드가 null 됨(메모리 해제)
- 연결 끊김(연결노드 전부 null)
- 위치정보를 null로 구분지어서 등록하는데 무용지물 됨
⇒ 해결완료
그냥 터미널 데이터에는 인덱스 정보(int)를 넣는 걸로 해결함. 객체가 아니라서 안 없어짐. (레벨데이터처럼)
생각해 보니 Node로 저장하면 DB에서 어떻게 불러오지 ㅋㅋ
UI 드래그 앤 드롭
(C#) UNITY_비전공자가 게임 만들기_이동하기, 드래그(drag&drop), 클릭한곳으로 이동
오늘은 Unity에서 가장 많이 사용되는 오브젝트 이동과 관련된 내용을 정리해 보겠다. Unity에서 물체를 이동시키는 방법은 여러가지 있겠지만 크게 3가지 종류로 나뉠수 있을 것 같다. 키보드 방
justdoitman.tistory.com
그냥 이거 보면 해결됨.
- 마우스 월드좌표 변환. (이거 자주 쓰니 따로 클래스로 만들어)
- offSet 설정하는 것 중요. (이거 안 하면 첫 클릭한 대로 순간이동함)
문제상황
background를 이동시키는 건 좋은데 범위를 제한시키지 않으면 저 멀리까지 떠나버리게 된다.
background가 camera 밖으로 안 나가려면(camera 안쪽으로만 범위를 제한하려면) 어떻게 해야 할까?
해결과정
- Camera.main.WorldToViewportPoint 사용
참고 : https://sunghojang.tistory.com/11
장점 : 짧은 코드로 손쉽게 적용할 수 있다.
단점 : background의 중앙위치를 기준으로 camera 밖으로 넘어갔는지를 체크하는 거라서 원래취지와 맞지 않는다. - 범위 직접 설정하기 => 채택
요약 : 직접 구하기. 근데 꽤 난관이 있더라. 😇
정리하자면 background의 범위는
-t < x < t
-k < y < k
이때
t = (background.width - camera.width)/2
k = (background.height- camera.height)/2
그럼 이제 각각의 width와 height만 알아내면 구현할 수 있다.
camera에 대해서
화면 좌표에 대하여-Unity
우리가 TV를 볼때 TV화면을 Camera를 통해서 보여주는 것이다. Unity에서도 이처럼 게임 화면을 구성...
blog.naver.com
camera의 크기 정보
[Unity] Camera를 이용한 게임 화면 크기 구하기
Camera는 플레이어가 보는 화면을 구성하는 장치이다. xScreenHalfSize 스크린의 x좌표 가운데의 크기를 저장할 변수이다. yScreenHalfSize 스크린의 y좌표 가운데의 크기를 저장할 변수이다. orthograorthograph
hangjastar.tistory.com
그런데 결과적으로 이 코드는 사용하지 않았다.
왜냐하면 UI크기를 1600X900 고정 픽셀로 해놓은걸 나중에 발견했기 때문이다. 🤮
(어쩐지 Camera로 직접 구하니까 안 맞더라)
따라서 camera의 width는 1600, height는 900이다.
background의 width 랑 height를 구하는 방법은 두 가지이다.
- boxCollider의 bounds()
이거 하나 구하겠다고 boxCollider컴포넌트 추가하는 건 정말 말도 안 되므로 폐기. - RectTransform.rect.width, height
RectTransform를 Get 해서 보면 안에 width랑 height가 있더라. 채택!
이제 각각의 width와 height를 알았으므로 범위 제한코드만 구현하면 된다.
그런데 이때의 width랑 height 정보는 RectTransform의 anchoredPosition에 대응된다.
(당시엔 몰라서 그냥 transform.position으로 코드를 구현하다가 머리가 깨졌다.)
[Unity3D] UI 비법서 (4) - UI 개발자라면 제발 Rect Transform 애용합시다!
UI 비법서 (4) - UI 개발자라면 제발 Rect Transform 애용합시다! 작성 기준 버전 :: 2019.1-2019.2 [이 포스트의 내용은 유튜브 영상으로도 시청하실 수 있습니다] 유니티 개발을 처음으로 공부하는 개발자
wergia.tistory.com
rectTransform.position은 그냥 transform.position(마우스 갖다 대면 알 수 있다) 이랑 똑같다.
무조건 anchoredPosition을 사용하자.
rectTransform.rect.position 이것도 사용하면 안 된다. 이상한 거 가리키고 있다.
(정확히는 내가 원하는 정보를 가르키고 있지 않더라, 이걸로도 시도해 보다가 머리가 깨졌다.)
RectTransform 이해하기: rect, scale, anchoredPosition vs localPosition
RectTransform WorldSpace 좌표계로 가정한다. RectTransform canvas 값 panel 값 .position (WorldSpace) ㆍWorldSpace 상의 피벗 위치 (3, 2) ㆍWorldSpace 상의 피벗 위치 (5, 4) .position (ScreenSpace) ㆍScreenSpace 상의 피벗 위치 cf
planek.tistory.com
여기서 rectTransform의 위치정보들이 어떤 걸 가리키는지 확인해 보자.
구현
if(rectTransform.anchoredPosition.x < -lx){
rectTransform.anchoredPosition = new Vector2(-lx, rectTransform.anchoredPosition.y);
Debug.Log("넘어감");
}
if (rectTransform.anchoredPosition.x > lx)
rectTransform.anchoredPosition = new Vector2(lx, rectTransform.anchoredPosition.y);
if (rectTransform.anchoredPosition.y < -ly)
rectTransform.anchoredPosition = new Vector2(rectTransform.anchoredPosition.x, -ly);
if (rectTransform.anchoredPosition.y > ly)
rectTransform.anchoredPosition = new Vector2(rectTransform.anchoredPosition.x, ly);
TerminalManager.Instance.terminalStatus[world][nodeIndex].nodePos = this.transform.position;
마지막줄의 터미널데이터에는 anchoredPosition이 아니라 transform.position을 저장하고 있는데 괜찮다.
rectTransform.anchoredPosition 에 맞춰서 transform.position 도 변하기 때문이다. (반대의 경우도 마찬가지다.)
해결 후 느낀 점
- 카메라 크기랑 ui 크기는 별개로 생각하자. 지금 ui를 고정픽셀로 해놨기 때문에 둘에는 차이가 있다.
- RectTransform 💩
#20221130 :: 위치변경 동기화(parent~child, Linq), 아이디어 메모, 라인렌더러
문제상황
BackGround의 위치가 바뀔 때마다 Child Node의 위치도 바뀌므로(부모 자식관계라서)
BackGround 위치 변경에 따른 Child Node 위치 데이터(nodePos) 업데이트가 필요.
해결과정
- BackGround의 위치가 바뀜에 따라 flag를 설정. flag가 true면 Child Node들의 데이터 업데이트. (각자의 스크립트에서)
- 근데 false는 언제 해..? ⇒ Child Node 데이터 업데이트가 전부 끝나면! ⇒ 그게 언젠데??.? ⇒ …
- Child Node의 데이터는 게임진행상황에 따라 유동적으로 늘어나고 줄어드므로 데이터 업데이트가 언제 끝나는지는 명시할 수 없음.(= 가능하긴 한데 굳이 구현을 growable 하게 만들고 싶지 않음)
- 따라서 폐기.
- 걍 BackGround 위치가 변경되면 즉시 Child Node를 전부 배열로 가져와서 한꺼번에 데이터 업데이트하자
⇒ 채택
Node[] childrens = gameObject.GetComponentsInChildren<Node>()
.Where(n => n.GetType().Name != "NodeInput" && n.GetType().Name != "NodeOutput").ToArray();
// Linq 로 필터링해서 자식노드들 반환
foreach(var i in childrens)
TerminalManager.Instance.terminalStatus[i.world][i.nodeIndex].nodePos = i.transform.position;
작동 잘함!
- Node 스크립트를 찾을 때 클래스 캐스팅이 잘된다. Node(BackGround, StageNode, NodeInput, NodeOutput…)
전부 찾아진다. (자기 자신도 찾는다.) - Linq로 필터링하기(중요@@@@)
부모 클래스로 찾으면 캐스팅돼서 자식 클래스가 전부 찾아지므로 취사선택을 해야 한다.
참고
velog
velog.io
유니티 LINQ 활용하기
유니티 C# 프로그래밍에서 List를 다룰 때 List의 요소들을 다루는 일은 꽤나 코딩이 길어 질 수 있다. 하지만 LINQ를 사용한다면 꽤나 코드를 간결화 가능하다. #. 첫번째 : List 에서 현재 위치와 2f
mentum.tistory.com
레벨 아이디어
이거처럼 어떤 레벨엔 필요한 오브젝트만 두고 나머지는 글자로 처리하는 것도 괜찮을 듯.
이런 느낌으로?
라인렌더러
Unity 유니티 라인그리기, LineRenderer
라인 렌더러 Line Renderer는 3D 공간에서 두 개 이상의 점의 배열을 가지고 각각의 사이에 직선을 그립니다. 따라서, 하나의 라인 렌더러 컴포넌트를 사용하여 1개의 직선에서 복잡한 나선형까지 그
itskeleton.tistory.com
레이캐스트
Unity RayCast - 클릭시 정보받아오기 및 처리하기
클릭하여 오브젝트의 정보를 받아올일이 생길때 사용합니다. 꼭 지켜야할것 !클릭하는 오브젝트에 Collider를 추가할것 Collider가 없으면 반응 하지않습니다 레이캐스트를 사용하여 큐브의 색깔을
dhy948.tistory.com
1. 아웃풋에서 라인 그리기
- 연결 없는 상태에서 새롭게 라인 그리기
- 라인 연결 실패
- 이미 연결된 상태에서 끊었다가 다시 같은 노드로 연결하기
- 이미 연결된 상태에서 끊었다가 다른 노드랑 연결하기
/** 연결 / 다른연결로 **/
///////////////////////////////////////////////////
public override void OnBeginDrag(PointerEventData eventData)
{
start = this.transform.position;
}
public override void OnDrag(PointerEventData eventData)
{
lineRenderer.SetPosition(0, start);
Vector3 mouseWorldPosition = MouseWorldPosition.GetMouseWorldPostion();
target = new Vector3(mouseWorldPosition.x, mouseWorldPosition.y, this.transform.position.z);
lineRenderer.SetPosition(1, target); // 마우스 따라가기
}
public override void OnEndDrag(PointerEventData eventData)
{
////
// 일단 연결해제 하고 다시 연결파악하기 **중요
if (input != null){
TerminalManager.Instance.terminalStatus[origin.world][origin.nodeIndex].Remove(input.origin.nodeIndex);
TerminalManager.Instance.terminalStatus[origin.world][input.origin.nodeIndex].Remove(origin.nodeIndex);
input.output = null; // 이거 안하면 연결이 없는데도 input에서 라인을 만들어냄 ㄷㄷ *중요
input = null;
}
////
////
// 연결 파악
Vector3 mouseWorldPosition = MouseWorldPosition.GetMouseWorldPostion();
target = new Vector3(mouseWorldPosition.x, mouseWorldPosition.y, this.transform.position.z);
Debug.DrawRay(target, Vector3.back, Color.red, 100f);
RaycastHit2D hit = Physics2D.Raycast(target, Vector3.back); // 레이캐스트 충돌판별(input 콜라이더 필요)
if(hit == true && hit.collider.name == "Input"){ // input과 충돌에 성공하면 연결하기
Debug.Log("닿았지롱");
target = hit.collider.transform.position;
input = hit.transform.GetComponent<NodeInput>(); // 이 if 문에 들어오는 순간 이 문구가 실패할 일은 없음.
input.output = this;
TerminalManager.Instance.terminalStatus[origin.world][origin.nodeIndex].Link(input.origin.nodeIndex);
TerminalManager.Instance.terminalStatus[origin.world][input.origin.nodeIndex].Link(origin.nodeIndex);
lineRenderer.SetPosition(1, target);
}else{ // 연결 실패
Debug.Log("안닿았지롱");
lineRenderer.SetPosition(1, start);
}
////
}
///////////////////////////////////////////////////
2. 인풋에서 라인 해제하기
- 연결 없는 상태
- 라인 연결 실패(해제)
- 이미 연결된 상태에서 끊었다가 다시 같은노드로 연결하기
- 이미 연결된 상태에서 끊었다가 다른 노드랑 연결하기(기존건 연결해제)
/** 연결해제 -> 재연결 / 다른연결로 **/
///////////////////////////////////////////////////
// 연결이 되어 있을 때만 실행하기 (output != null)
public override void OnBeginDrag(PointerEventData eventData)
{
if(output != null){
this.lineRenderer = output.lineRenderer;
start = output.transform.position;
}else{
Debug.Log("연결된 선이 없어엉");
}
}
public override void OnDrag(PointerEventData eventData)
{
if (output != null){
lineRenderer.SetPosition(0, start);
Vector3 mouseWorldPosition = MouseWorldPosition.GetMouseWorldPostion();
target = new Vector3(mouseWorldPosition.x, mouseWorldPosition.y, this.transform.position.z);
lineRenderer.SetPosition(1, target); // 마우스 따라가기
}
}
public override void OnEndDrag(PointerEventData eventData)
{
if(output != null){
////
// 일단 연결해제 하고 다시 연결파악하기 **중요
// (output.input == this)
TerminalManager.Instance.terminalStatus[origin.world][output.origin.nodeIndex].Remove(origin.nodeIndex);
TerminalManager.Instance.terminalStatus[origin.world][origin.nodeIndex].Remove(output.origin.nodeIndex);
output.input = null;
////
////
// 연결 파악
Vector3 mouseWorldPosition = MouseWorldPosition.GetMouseWorldPostion();
target = new Vector3(mouseWorldPosition.x, mouseWorldPosition.y, this.transform.position.z);
Debug.DrawRay(target, Vector3.back, Color.red, 100f);
RaycastHit2D hit = Physics2D.Raycast(target, Vector3.back); // 레이캐스트 충돌판별(input 콜라이더 필요)
if (hit == true && hit.collider.name == "Input"){ // input과 충돌에 성공하면 연결하기
Debug.Log("인풋에서 땡겨서 닿았지롱");
target = hit.collider.transform.position;
output.input = hit.transform.GetComponent<NodeInput>(); // 이 if 문에 들어오는 순간 이 문구가 실패할 일은 없음.
// output.input 은 나 일수도, 아닐수도
if(output.input == this){ // 다시 나랑 연결 (this.output은 그대로)
Debug.Log("다시 나랑 연결");
TerminalManager.Instance.terminalStatus[origin.world][output.origin.nodeIndex].Link(origin.nodeIndex);
TerminalManager.Instance.terminalStatus[origin.world][origin.nodeIndex].Link(output.origin.nodeIndex);
}else{ // 다른애랑 연결, 나랑은 정말로 연결해제 (this.output -> null)
Debug.Log("다른 애랑 연결");
output.input.output = output;
TerminalManager.Instance.terminalStatus[output.origin.world][output.origin.nodeIndex].Link(output.input.origin.nodeIndex);
TerminalManager.Instance.terminalStatus[output.origin.world][output.input.origin.nodeIndex].Link(output.origin.nodeIndex);
this.output = null;
}
lineRenderer.SetPosition(1, target);
}
else{ // 연결 실패
// output.input == this
Debug.Log("인풋에서 땡겨서 안닿았지롱");
lineRenderer.SetPosition(1, start);
this.output = null; // 나랑 연결해제
}
////
}
lineRenderer = null; // 라인렌더러 해제하기
}
///////////////////////////////////////////////////
3. 업데이트에서 연결되어 있는지 체크, 라인 위치 변화
→ 내일 할 거임
'개인 프로젝트 > Node Princess' 카테고리의 다른 글
Node Princess :: 개발일지 #6 - 캐릭터 컨셉 (0) | 2024.02.16 |
---|---|
Node Princess :: 개발일지 #5 - 터미널 구현 (2) (1) | 2024.02.16 |
Node Princess :: 개발일지 #3 - 터미널(월드 선택) 컨셉 (0) | 2024.02.15 |
Node Princess :: 개발일지 #2 - 타이틀 만들기 (1) | 2024.02.15 |
Node Princess :: 개발일지 #1 - 초기 컨셉 (0) | 2024.02.15 |
틀린 부분은 언제든지 말씀해주세요!!! 감사합니다!