이전 버전에 비해 더 심플해진 상점입니다.
상점이라는 명칭도 업그레이드로 바꿀 예정입니다. 지금은 적용하지 않았지만 구입한 업그레이드 범위 내에서 조정 가능하게 만들 생각이기에 상점이라는 이름은 그리 맞지 않는거 같아서 그렇습니다.
친구가 목숨이 늘어나니까 오히려 재미없다고 해서...
아무튼 이 씬도 메인 화면과 동일하게 UI로만 이루어져 있습니다.
그리고 겉보기는 이전과 크게 다르지 않아 보이지만 꽤 많이 바뀌었습니다.
이런 식으로 지금은 별로 의미는 없지만 스크롤이 되고 클릭하면 하이라이트가 표시됩니다.
그리고 예전엔 클릭해야만 가격이 나타났고 얼마나 업그레이드가 진행된 상태인지 알 수 없었지만 이젠 알 수 있습니다.
아무것도 클릭하지 않으면 선택해달라는 문구가 뜨고
클릭한 상태에서 구입 버튼을 누르면 해당 품목의 아이콘 이미지가 같이 뜨면서 물어봅니다.
여기서 돈이 있으면 구입이 되고 없으면 위처럼 뜹니다.
만약 전부 구입한 상태라면 해당 상품의 가격은 0원으로 되고 구매할지 묻지 않고 바로 이미 최고치라고 답합니다.
그리고 상점에도 메인 화면처럼 안드로이드 뒤로가기 버튼이 먹힙니다. 바로 메인 화면으로 돌아가.. 는데 생각해보니 구입 과정 중에 뒤로가기를 누르면 그 창이 꺼져야 되는데 그래도 메인화면 가버리겠군요. 고쳐야겠네요;;
눈에 보이는 것은 대충 이게 전부입니다. 사실 저한테는 저 스크롤이나 하이라이트나 처음 해보는 것들이라 신선한 느낌으로 작업했는데 일반적으로 많이 보던 방식이라 다른 분들이 보시기엔 별 느낌 없겠네요.
코드쪽을 좀 보면 상점은 이전 버전 방식에서 완전히 다시 만들었습니다. 원래는 다시 만드는김에 데이터베이스나 다른 저장 방식을 좀 찾아서 동시에 적용시킨 후에 1.03버전을 올리려고 했는데 생각보다 오래걸릴거 같고 블로그 잠수를 본의 아니게 너무 오래 타고 있는거 같아서 미뤘습니다.
스크롤 기능을 만든게 추후 추가되었을 때를 고려해서인데 마찬가지로 스크립트도 고민을 많이 했습니다.
이전 버전은 그냥 한 스크립트에 몰아 넣고 버튼의 OnClick으로 함수를 빼오는 식으로 했는데 나중에 상품이 두 자리가 된다고 하면 관리가 끔찍할거 같아서 스크립트를 나누긴 해야겠는데...
하나의 스크립트를 주고 public으로 빼는 방식으로 갈 지 상품마다 스크립트를 따로 만드는 방식으로 할지 고민하다 그냥 후자로 택했습니다.
대신 이번에도 안 써본 방법을 사용해서 만들었습니다.
원래라면 자주 썼어야 정상일지도 모르는 상속입니다.
상품의 종류가 많다 하더라도 기본적인 형태는 다 동일하고 구입 이후의 효과만 다르기 때문에 기본 베이스를 하나 만들고 상속 받는 형식으로 했습니다.
코드보기 접기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
using System .Collections;
using System .Collections.Generic;
using System .Text;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class ItemInfo : MonoBehaviour, IPointerClickHandler
{
public int [] price; //가격. 배열 사이즈만큼 업그레이드 가능하다. 마지막은 무조건 0을 넣는다.
[SerializeField] //임시 확인용
protected int nowCount; //현재 구입 단계.
public Text levelText; //(0/0) 텍스트
public Text priceText; //가격이 표시되는 텍스트
protected Shop manager; //상점의 manager 스크립트
protected Image faceImage; //아이콘 이미지
protected StringBuilder sb; //ToString의 가비지를 줄이기 위한 스트링빌더
protected virtual void Awake() //protected virtual선언을 하면 자식이 베이스의 Awake를 호출 할 수 있다.
{
sb = new StringBuilder ();
TextSetting(); //텍스트 초기화
manager = GameObject.Find("ShopManager" ).GetComponent< Shop> (); //public으로 직접 연결해주어도 상관없음
faceImage = transform.Find("imgFace" ).GetComponent< Image> (); //아이콘 이미지를 가져온다. 구입 과정에서 필요하다.
}
public virtual void OnPointerClick(PointerEventData data) //해당 아이템을 클릭했을 시
{
manager.highlight.transform.SetParent(transform, false ); //하이라이트의 부모를 자기자신으로 바꾼다.
//SetParent의 두번째 인자는 자식의 로컬 좌표를 보정할 것인지를 묻는 것인데
//여기선 부모의 zero 좌표에 붙어있기만 하면 되므로 보정이 필요없다.
if (! manager.resultOn) //아무거나 처음 클릭하면 진입
{
manager.highlight.SetActive(true ); //하이라이트가 처음엔 꺼져있으므로 켜준다.
manager.buy.onClick = new Button.ButtonClickedEvent(); //영구적 onClick 값을 초기화 시켜준다.
manager.resultOn = true ;
}
manager.buttons.resultImage.sprite = faceImage.sprite; //구입창에 아이콘을 클릭한 상품의 아이콘으로 변경한다.
}
protected void TextSetting()
{
sb.Length = 0 ;
sb.Append("(" );
sb.Append(nowCount);
sb.Append("/" );
sb.Append(price.Length);
sb.Append(")" );
levelText.text = sb.ToString();
priceText.text = price[nowCount - 1 ].ToString();
}
}
cs
접기
베이스 코드입니다.
위 요소들은 공통적으로 사용하는 부분입니다.
저기서 중요한게 manager.buy.onClick = new Button.ButtonClickedEvent(); 부분입니다.
저도 이거 하면서 안건데 UI Button 을 쓰면 흔히 쓰는
이 OnClick()이랑 스크립트에서 다루는 onClick이랑은 다른거라고 봐야 하더군요.
스크립트에 onClick에 담긴 실행 함수들을 초기화 시켜주는 onClick.RemoveAllListeners();이라는 함수가 존재합니다만 이걸 써도 위 이미지의 OnClick() 에 변화가 생기지 않습니다.
그리고 스크립트에서 onClick에 새로 함수를 등록하려면 onClick.AddListener(() => 의 형태로 집어넣게 됩니다. 근데 이렇게 넣은 후에 인스펙터 창을 봐도 추가된게 없습니다.
찾아보니 이미지에서 설정한 OnClick()은 영구적 onClick이라고 불리는 모양입니다.
스크립트를 통해 함수를 붙여주거나 삭제하면 그건 이미지의 OnClick() 과 별개의 공간에서 이루어져서 저 영구적 onClick에는 접근할 수 없다는 말인거 같습니다.
근데 함수 추가야 굳이 영구적 onClick에 넣을 필요는 없으니 상관 없지만 기존에 설정해둔 OnClick()은 삭제할 수 있어야 추후 이용에 문제가 없지 않겠습니까?
그래서 위와 같은 방법을 씁니다.
아예 새로 할당해서 초기화 시켜버리는 겁니다.
그러면 영구적 OnClick()에 설정된 값은 사라지게 되어 차후 문제를 일으키지 않습니다.
OnClick()만 새로 할당한 것이기 때문에 그 상위인 버튼엔 아무런 영향을 주지 않습니다.
아무튼 이런 식으로 베이스 코드를 짠 다음
자식을 다음과 같은 틀에서 짭니다.
코드보기 접기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using System .Collections;
using System .Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class Item_Heart : ItemInfo
{
protected override void Awake()
{
nowCount = SingletoneValue.Instance.MaxHeart; //현재 수치엔 아이템마다 다른 값을 불러와야 하므로 자식에서 부른다.
base .Awake(); //그 후에 베이스의 Awake를 호출한다. 베이스에서 nowCount를 사용하기 때문
}
public override void OnPointerClick(PointerEventData data)
{
base .OnPointerClick(data); //베이스로부터 이어받는다.
if (nowCount = = price.Length) //최대 수치 도달
{
//최대치임을 알리는 글을 세팅
}
else
{
//정상 구입 절차. 아래의 함수를 세팅
}
}
void Buy()
{
switch (nowCount) //현재 업그레이드 상태를 반영
{
case 1 :
if (SingletoneValue.Instance.Coin > price[0 ])
{
//소지량이 가격보다 많으면 구입 후 스탯 적용
}
else
{
//아니면 코인 부족 출력
}
break ;
case 2 :
if (SingletoneValue.Instance.Coin > price[1 ])
{
//
}
else
{
//
}
break ;
default : //정상적이라면 이곳에 진입하면 안 된다.
Debug.Log("구입 오류" );
break ;
}
//구입이 이루어졌다면 업그레이드 수치가 오르고 가격도 오르고 소지량은 줄어든다. 바로 반영한다.
TextSetting();
}
}
cs
접기
내부에 주석만 있는 부분은 주석 내용에 맞게 세팅합니다.
이런식으로 상속을 활용해서 간결하고 활용도 높은 스크립트들이 되었습니다.
현업에서 상속을 이런식으로 활용하고 쓰는지는 알 수 없지만 상속을 유니티에서 활용한건 처음인 저에게는 꽤나 만족스럽게 활용한거 같습니다.
아마 상점은 이 틀을 거의 끝까지 가지고 갈 거 같습니다.
저장 정도만 PlayerPrefs가 아닌 다른 방식으로 바뀔 거 같네요.