Git : 버전 관리 도구
Github : 온라인 협력 도구의 프로그램 이름
git init -> git으로 관리 시작
git clone /*주소*/ -> 해당 주소의 저장소 복제
git branch /*이름*/-> 브랜치 생성
//이름 빼고 입력시 브랜치 목록이 나옴
git switch /*이름*/, git checkout /*이름*/ -> 해당 브랜치로 이동
git add /*파일명*/, git add . -> 관리 내역에 파일을 추가, 현재 디렉토리 추가
git commit -m /*"메세지"*/ -> 저장소의 체크포인트를 남긴다
git pull /*주소*/-> 해당 주소의 저장소를 병합하여 복제
git push /*저장소명*/ /*브랜치명*/ -> 온라인으로 체크포인트를 보낸다
🎉 한 장 요약
void MakeMatchText(string text)
{
string name = SelectName(text); //이름 불러오기, 값형은 아직 미정
GameObject firstCardText =
Instantiate(MatchText, firstCard.transform.position, Quaternion.identity);
GameObject secondCardText =
Instantiate(MatchText, secondCard.transform.position, Quaternion.identity);
// 자식으로 넣으면 스스로 파괴하기 전에 부모가 파괴되면서 같이 사라짐
// 그래서 부모의 포지션 값만 받아서 따로 생성함
firstCardText.transform.Find("Text").gameObject.GetComponent<Text>().text = name;
secondCardText.transform.Find("Text").gameObject.GetComponent<Text>().text = name;
// name으로 쓸 변수가 아직 정해지지 않아 따로 함수로 틀만 만들어 둠
}
string SelectName(string text) // 값을 받아서 name을 분류할 예정
{
string name;
switch (text)
{
case "ㄱㄱㄱ":
name = "ㄱㄱㄱ";
break;
case "ㅈㅈㅈ":
name = "ㅈㅈㅈ";
break;
case "실패":
name = "실패";
break;
default: // 방어 코딩도 잊지 말 것
name = "입력 실패";
break;
}
return name;
}
public class MatchText : MonoBehaviour
{
[SerializeField]
float MoveSpeed; // 글자 올라가는 속도
[SerializeField]
float MoveMaxPosY; // 올라가는 Y 한계값
[SerializeField]
float PosY; // 선택한 카드 기준 추가될 Y값
float CheckMovePos = 0; // MoveMaxPosY와 비교할 값
Text Text; // 투명도 조절에 쓰일 예정
void Awake()
{
Text = transform.Find("Text").GetComponent<Text>(); // Text 값 초기화
transform.position += Vector3.up * PosY;
// 만들어진 위치에서 PosY만큼 추가
// 카드의 중앙보다 위에서 나타나기 위해 추가함
}
void Update() // 위치, 투명도, 오브젝트 파괴 조건
{
transform.position += Vector3.up * MoveSpeed * Time.deltaTime;
// 위로 움직이는 코드
CheckMovePos += Vector3.up.y * MoveSpeed * Time.deltaTime;
// 움직이는 vector3.y를 비교할 수 있게 float로 저장
Text.color =
new Color(Text.color.r, Text.color.g, Text.color.b, 1.0f - (CheckMovePos / MoveMaxPosY ));
// 색깔 변경을 쉽게 하기 위해 기존에 사용하는 Color값을 불러옴
// 알파값(a)는 255 = 1.0f(불투명)이다 그러므로 1 - (CheckMovePos / MoveMaxPosY ) 해주면
// MoveMaxPosY에 도달할수록 알파값이 0(투명)에 가까워진다.
if (CheckMovePos > MoveMaxPosY) // CheckMovePos가 한계값인 MoveMaxPosY 도달하면
{
Destroy(gameObject); // 오브젝트를 파괴한다.
}
}
}
✅ SOLID에 SO가 잘 지켜졌다라고 '생각'한다.
✅ 대부분의 값을 변수로 사용하였으므로 Sprite 패치 후에 유지 보수가 편하지 않을까?
⛔ PosY값을 상수로 사용하였는데 카드 크기가 변경되면 고쳐야한다.
🔧 다음엔 Scale
, Sprite의 PixelPerUnit
, Sprite의 Height
을 이용하여 상단 끝부분을 구해보자
⛔ 방어 코딩을 잘 하였는가?
🔧 ..
⛔ Text.color = new Color(Text.color.r, Text.color.g, Text.color.b, 1.0f - (CheckMovePos / MoveMaxPosY ))
가 최선의 코드였을까?
🎉 한 장 요약
bool ShowHint = false;
void ShowMeTheHint() // 시점 함수 Update에서 사용하는 함수
{
if (ShowHint == false)
{
ShowHint = true; // false 값으로 변경하기 전까지 다시 실행 안됨
GameObject cards = GameObject.Find("cards"); // cards는 card의 부모이기때문에 불러옴
int RandomCard = UnityEngine.Random.Range(0, cards.transform.childCount);
// cards.transform.childCount -> cards의 자식 갯수(남은 카드 갯수)
// 남은 카드 중에서 힌트를 줄 카드 랜덤 선택
for (int num = 0; num < cards.transform.childCount; num++) // 카드들을 비교하기위해 사용
{
if (cards.transform.GetChild(RandomCard).Find("front").GetComponent<SpriteRenderer>().sprite.name
== cards.transform.GetChild(num).Find("front").GetComponent<SpriteRenderer>().sprite.name
&& RandomCard != num)
// RandomCard의 스프라이트 이름과 for문으로 차례대로 카드 스프라이트 이름을 비교
// RandomCard와 for문의 카드 번호가 같으면 안됨
{
cards.transform.GetChild(RandomCard).GetComponent<Animator>().SetTrigger("IsHint");
// 애니메이션 트리거 작동
cards.transform.GetChild(num).GetComponent<Animator>().SetTrigger("IsHint");
// 트리거 = 1회 작동
break; // 짝을 찾으면 바로 중단해서 퍼포먼스 상향
}
}
}
}
✅ RandomCard != num
예외 처리 바로 생각나서 뿌듯함.
⛔ Update에서 사용 안하는 방법은 없었을까?
🔧 현재 상태에선 Update
에서 타이머와 함께 사용하는 게 최선.
⛔ Sprite.name
으로 비교하는 방법말고 다른 방법은 무엇인가?
🔧 1. tag
를 사용할 수도 있지만 태그까지 사용하기에는 배꼽이 커지는 느낌.
🔧 2. card
의 스크립트에 string name
을 선언하여 변별력을 추가해주고 비교.
🎉 한 장 요약
IEnumerator AppearCard(List<GameObject> cardsArr)
{
IsStartAniOff = false; // 악당들도 지키는 변신 매너
for (int num = 0; num < cardsArr.Count; num++) // 카드 수에 따라 수정 필요 없이 실행됨
{
yield return new WaitForSeconds(0.15f); //0.15초 마다 하나씩
cardsArr[num].SetActive(true); //카드를 활성화
cardsArr[num].GetComponent<Animator>().SetTrigger("IsAppear"); // 애니메이션도 실행
if (num == cardsArr.Count - 1) // 마지막 카드에서
{
yield return new WaitForSeconds(0.3f); // 0.3초(애니메이션 끝나는 시간)
IsStartAniOff = true; // 기다렸다가 변신 완료, 게임 시작
}
}
}
✅ Coroutine
을 처음으로 이해하고 사용해 봄.
✅ start
, OnEnable
함수에 애니메이션 트리거를 넣는 방법도 있다.
🔧 둘 다 오브젝트가 활성화되면 실행되는 이벤트 함수(시점 함수)이지만 '활성화 = 등장 애니메이션'은 아니라고 생각하였다.
✅ 난이도에 따라 등장하는 카드 수가 증가하므로 확장이 편한 List
를 사용하였다.
⛔ Invoke
와 다르게 매우 마음에 드는 기능인 Coroutine
에 대하여 겉만 할짝했다.
🔧 주간 일지에서 대서 특필 한번 하자
🎉 한 장 요약
public class PauseUI : MonoBehaviour, IPointerClickHandler //(IPointer 인터페이스)
{
bool isTimeStop = false; // 정지 유무를 확인할 변수
public bool IsTimeStop { get { return isTimeStop; } } // 외부로 리턴'만' 해줄 get 함수
[SerializeField]
Sprite StartImage; // 정지 후 바뀔 sprite, 인스펙터에서 넣어주시면 됩니다.
Sprite PauseImage; // 처음 설정된 sprite이고 자동으로 할당됩니다.
//위에 주석 두 줄은 협업을 위해 작성하였음
void Awake()
{
PauseImage = GetComponent<Image>().sprite;
// PauseImage에 처음 설정된 sprite를 할당
}
public void OnPointerClick(PointerEventData eventData) // 클릭 시 이벤트
{
if (isTimeStop == false && gameManager.I.IsGameing == true) // 게임이 실행 중이면
{
GetComponent<Image>().sprite = StartImage;
isTimeStop = true;
Time.timeScale = 0;
DestroyClickEffect(); // 시간이 정지되면서
gameManager.I.endPanel.SetActive(true);
}
else if (isTimeStop == true && gameManager.I.IsGameing == true)
{ //else if문을 사용한 이유는 무조건 둘 중 하나만 작동 하기 때문에!!
GetComponent<Image>().sprite = PauseImage;
isTimeStop = false;
Time.timeScale = 1.0f;
gameManager.I.endPanel.SetActive(false);
}
}
void DestroyClickEffect() // 마우스를 따라다니는 이펙트를 파괴한다.
{ // 시간을 멈추면 이펙트의 애니메이션도 멈춰서 파괴해야했다.
Transform[] ClickEffectChildArr =
gameManager.I.ClickEffects.GetComponentsInChildren<Transform>();
for (int num = 1; num < ClickEffectChildArr.Length; num++)
{
Destroy(ClickEffectChildArr[num].gameObject);
}
}
}
✅ 그래픽 담당 팀원과 협업을 위해 주석을 활용하였고 별도의 연락없이 잘 처리됨
✅ 팀원과 협업을 위해 주석을 활용하였다.
⛔ Time.timeScale
특정 오브젝트만 적용 혹은 예외 처리하는 방법은 없을까?
🔧 당연하게도 Time.timeScale
에 영향을 받지 않는 독립적인 시간Time.unscaledDeltaTime
가 있다.
Time.timeScale = 0;
float Time1 = 0;
float Time2 = 0;
Time1 += Time.deltatime; // 작동하지 않는다.
Time2 += Time.unscaledDeltaTime; // 작동한다.
애니메이션은 Animator
컴포넌트를 통하여 설정할 수있다.
🎉 한 장 요약
1. 팀원들과 함께 만드는 프로젝트
우리가 생각하는 협업과 개발자로써의 협업은 비슷하면서도 더 깊이 서로 엮이는 것 같았다.
단순히 코드 하나 추가하는 것으로도 팀원의 기능에 버그가 발생하기도 하였다.
그러므로 팀원과의 소통에 좀 더 책임감을 가지는 게 중요하다고 생각한다.
쓰라린 기억은 잠시 잊고 함께 술잔 기울이면 친구가 되었던 시절로 돌아가보는 것도 좋을 것 같다. (틀니를 끼우며)
2. 올해 봄, 코딩을 시작하고 오늘까지의 실력
프로젝트를 하며 한 치의 망설임 없이 기능들을 만들어갔다. 지금 당장 퇴소하고 게임 뚝딱뚝딱 만들고 출시하면 캘리포니아에 별장이라도 살 수 있을 것 같다.
문제는 이 느낌이 더닝 크루거 효과라는 게 학계의 정설이다. 😭
다음 주의 나에게로 보내는 편지는 c# 전공 서적을 기초도 못 끝내고 포기한 허~접🧡 쓰레기~🧡
가 되겠다.
하지만! 개발자로써는 작은 성장이지만, 코드를 그리던 나라는 개발자로써는 위대한 성장인 건 누구도 부정할 수 없다.
📌 기억해야 할 이번 주 키워드
if (EatDinner == true) // 이렇게 쓰지말고
{
if (WashFace == true)
{
if (SayGoodNight == true)
{
/*아도겐*/ Sleep();
}
}
}
-----------------------------------------
if (EatDinner == false) // 이런 식으로 처리하는 게 보기 좋다
{
return;
}
if (WashFace == false || SayGoodNight == false)
{
return;
}
Sleep();
IEnumerator Func()
{
Time.timeScale = 0f;
yield return new WaitForSecondsRealtime(1.0f); // 현실 시간으로 1초 후
Debug.Log("1초 후..") // output : 1초 후..
yield return new WaitForSeconds(1.0f); // 게임 시간으로 1초 후
Debug.Log("1초 후..") // 게임 시간을 멈췄기에 나오지 않는다.
}