4주차 4️⃣

[복습을 위한 질문]

  • Thread는 무엇인가?
    -> 음.. 겉으로 보기에 CPU같은 존재..? 쉽게 얘기하자면 일을 대신 수행해주는 그런 일종의 작업자? 같은 존재이다.

  • 싱글쓰레드 vs 멀티 스레드
    -> 말그대로 하나의 쓰레드냐 아니면 여러개의 쓰레드냐.. 컴퓨터에도 듀얼 쓰레드 쿼드 쓰레드가 있듯이 다다익선..?(반드시 그런건 아니라고 들음) 느낌

  • 동기 / 비동기 그리고 직렬 / 동시 가 무엇인가?
    -> 아마도 Serial Thread와 Concurrent Thread를 얘기하는것같은데.. Serial Thread는 자기 작업이 끝날때까지 기다리는 친구고, Concurrent Thread는 작업이 끝나던지 말던지 상관없이 다음 작업을 시작하는 친구다.

과제

    1. Thread를 이용한 게임 프로그래밍

과정

이번주에도 역시 무슨 프로그램을 만들까 굉장히 고민이 많았다.. 😩 아무래도 게임 프로그래밍이다보니 너무 복잡하면 안될것같고.. 무엇보다 SpriteKit을 사용하면 안된다그래서 정말로 지금까지 배운거 + Thread를 이용한걸로만 만들어야했다..

사실 저번주에 수업하면서 막판에 과제설명을 들었을때 머릿속에 스쳐지나간게 "카드 짝맞추기 게임"이였다..

그래서 혼자 속으로 '와.. 난 무조건 이거해야겠다..' 라고 생각했는데, 갑자기 예전 수료생들이 만든 예시 게임으로 카드 짝맞추기를 틀어주시길래.. 아 이건 글렀다..라고 생각하고 다른걸 해야겠다고 생각했다.

근데 정말로 마땅한게 생각이 안났다.. 조금 움직이는 모션과 애니메이션을 넣으려고하니 spriteKit이 거의 필수로 들어가고, 그렇다고 아무 재미도 없는 게임을 만들고 싶지는 않아서 기존에 내가 생각했던 카드 짝맞추기 게임을 만들기로 했다.

우선 이번주에 내가 만든 App 화면이다

UI에 많은 신경을 쓰기보다는 기능에 좀 더 집중했다.

사용한 주요 컴포넌트

  • Stack View
  • Label / Button / Image View / Text Field

이번주차에는 복잡한 컴포턴트들은 들어가지 않았지만, 대신 로직이 굉장히 복잡해졌고 코드의 길이가 굉장히 길다..

구현한 기능

  • UIView.transition 사용해서 Flip 애니메이션 구현
    -> 원래 이걸 알기전에는 그냥 Button을 스택뷰로 쌓아서 카드를 구성하려고 했다. 근데 너무 느낌이 밋밋하고 재미가없어 애니메이션 느낌을 줘야겠다라고 생각하고, 이것저것 구글링하다가 찾았다. 하지만 여기서 굉장히 골치아픈 이슈가 있었음..

  • 게임에 들어나고가서 3초동안 아무런 동작도 할 수 없는 Lock
    -> 이 부분을 구현하려고 GCD를 더욱 자세히 공부했고, 이부분에서 배운게 좀 많았던것같다. DispatchQueue.Main은 전역으로 사용이 가능한 Serial Thread라는 것을 알았고, 가령 DispatchQueue.Main로 어떠한 함수 혹은 코드가 실행된다해도 기존에 있던 Main Thread 끝에 추가됨을 알았다.
    -> ViewDidLoad에 아무 생각없이 다짜고짜 DispatchQueue.Main 혹은 글로벌로 Thread를 새로 생성해버리면 뷰가 완전히 로드되는데에 시간이 걸린다.. 그래서 serial DispatchQueue를 하나 선언하고, 순서를 나눠서 Thread를 뿌려주는식으로 진행했음. (숫자 카운트를 다 마치면 그때 GestureRecognizer를 각 ImageView에 추가 <-여기에서도 이슈있었음..)

  • background에서 count되는 시간과 Flip 횟수
    -> 위 기능과 같은 연장선에 있는 기능인데, 위에 3초동안 걸렸던 Lock이 풀림과 동시에 플레이 시간은 0.01초 단위로 타이머가 업데이트되며 실행된다

DispatchQueue.main.async {
	self.countLabel.text = ""
	self.timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.timerCounter), userInfo: nil, repeats: true)
            }
  • Pair가 맞으면 GestureRecognizer가 다시 비활성화
    -> 여기서도 좀 이슈가 있긴했는데.. 처음에는 초반부 구성을 세팅해주는 함수쪽에서 GestureRecognizer들을 다 선언했었는데, 초반부 구성이 거의 통채로 다 DispatchQueue에 들어가 있었어서, 다른 함수부분에서 GestureRecognizer를 불러올수가 없었음.. 그래서 결국 나중에 빈 GestureRecognizer를 전역변수로 미리 선언하고 배열을 만들어줬음

  • 짝을 다 맞추면 뜨는 alert기능 & Best Time, Best Flip 저장 기능
    -> 현재 뒤집혀있는 카드들을 다 뒤집게되면 타이머가 중지되고, 게임을 다시 하거나 홈호면으로 나갈 수 있는 alert창이 뜬다.
    -> 이때 Best Time, Best Flip을 UserDefalut에 저장해서 새로고침하더라도 최고의 값만 남길 수 있도록 함수를 설정했다.

  • 난이도 설정 가능
    -> 원래 카드의 개수나 좀 더 복잡한 패터의 카드를 만들어서 추가하려고 했는데 시간 관계상 카드의 개수는 동일하나 backgorund 등의 컬러와 카드를 바꿔서 구현했음

중간중간에 있었던 issue

  • 메인 이슈

개강..

아무래도 UIView.transition와 Thread에 관련된 이슈가 가장 크지 않았을까 싶다..

처음에 생각했던 것은 그냥 버튼을 만들어서 구현하려했던건데, 좀 더 게임같은 요소를 구현하고싶어서 결국 ImageView로 카드를 구현하게 됐고, 카드를 Flip하는 애니메이션을 UIView.transition 을 통해 구현했다.

UIView.transition은 기본적으로 이렇게 생겼다

UIView.transition(with: self.arrImageView[pickedCardNum], duration: 0.5, options: .transitionFlipFromLeft, animations: nil, completion: nil)

전환해주고싶은 이미지와, 전환하는데 걸리는 시간, 그리고 전환 모양 등등을 설정할 수 있다.

내가 생각했었던것은 duration: 0.5 요놈 때문에, Serial로 Thread를 설정했을 때 저 duration이 다 끝날때까지 기다리는줄 알았다.. 근데 유감스럽게 아니였다..

처음에는 뭐가 잘못된지 모르고 엄청 삽질을 했지만,, Thread는 그냥 저 함수를 실행시키면 자기 할일을 다한것이기 때문에, 저 duration이 몇초인지는 관심이없다.

즉, UIView.transition의 durationd은 Thread에서 usleep() 혹은 Thread.sleep() 처럼 기다려주지 않는다..

그래서, 만약 pair가 맞지 않았을 때 일단 두번째 카드가 앞으로 뒤집힌 다음에 첫번째 카드와 함께 다시 뒤로 뒤집혀야하는데, 아무리 Thread를 나눠서 걸어줘도 자꾸 두번째 카드가 앞으로 뒤집히기도 전에 다시 뒤로 뒤집힌다..

Why? Thread는 일단 UIView.transition()라는 함수를 실행시켰고, pair가 맞지 않았기 때문에 또 다시 UIView.transition()라는 함수를 실행시키기 때문에...

그래서 결국엔

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5)

를 이용했다.

지연함수라는것인데, 저걸 해놓으면 저 함수를 실행하는 시점에서 n초 후에 실행된다.. duration값이랑 맞춰놓으니 딱 맞아떨어졌음..

이건 참.. 구글링에도 안나와와서 맨땅에 헤딩하면서 배웠다..

  • GestureRecognizer는 View당 한개..

심각한 이슈였다기보다는 매우 귀찮은 이슈였다.. 위 화면에 저 16개의 ImageView들이 보이는가..?

처음에는 GestureRecognizer를 하나 만들고 16개의 ImageView에 추가만 해주려했는데.. 불가능했다.

View 혹은 ImageView는 무조건 1대1 개념으로 GestureRecognizer를 만들어줘야한다.. ㅠ 그래서 노가다좀 했음..

  • About DispatchQueue 그룹..

처음에 쓰다가 조금 복잡해져서 다시 안쓰기는 했는데.. DispatchQueue Group을 만들어서 사용하면, 그 Group들의 작업이 언제 끝나는지 알 수 있음..!

아 근데 만약 그 그룹안에서 다른 Thread를 생성할 경우 따로 처리를 또 해줘야함

아쉬웠던 점

  • 카드를 광클하면 고장남..
    -> 위에서 말했던 UIView.transition() 관련 이슈이다.. duration을 어떻게 통제해야하는지 잘 모르겠어서 발생한 이슈..
    -> 해결은 못했지만, 아무래도 함수 하나하나에 다 Thread를 만들어줘서 더 세분화하게 쪼개주면 되지 않을까..

  • 중국산 양산형 게임보다 못한 UI.. 😅

  • 좀 더 복잡한 기능(더 세분화된 난이도, 최고기록자 이름, 사운드 등) 구현 못함..

  • escaping closure
    -> 주초에 공부를 하긴 했는데, 정작 프로그램에 쓸 곳이 없다 생각해서 안썼음..
    -> 이걸 쓰는 목적은 확실히 알았다 그래도

이번주 과제를 하면서 느낀점

  1. 개강을 했으니.. 좀 더 부지런하자..

  2. 제발 개발일지.. 아니 메모라도 중간중간에 해놓자..

  3. 주석 많이 달아놓기

  4. Thread 제발 까먹지 말자

참고: https://seons-dev.tistory.com/entry/Swift-DispatchQueue%EB%9E%80-GCD-Grand-Central-Dispatch

전체코드: https://github.com/shintaewon/RC_Week_4_2

profile
일단 배우는거만 정리해보자 차근차근,,

0개의 댓글