5주차 숙제 게임개발

 

 

Run & Jump - 프로토타입 설명

1.마우스를 누르면 캐릭터 머리 위에 파워 게이지가 표시됨

2.마우스를 놓으면 놓은 방향으로 캐릭터가 빙글빙글 돌아가며 발판들을 넘어감

3.이미 한번 착지한 발판은 점수 x , 발판을 처음 밟을때만 점수 증가

 

오늘 구현 한것

1. 플레이어 캐릭터가 카메라 경계선으로 이동시 카메라 Zoom In/Out 기능 추가

2. 블럭 스폰 위치 규격화 하기

2-1. 4방향에서 랜덤하게 나오게 구현

 

구현 목표

1.블럭 사이에 있을때 점프로 가시 통과할 수 있게 구현

이런 상황이 되었을때 점프를 해서 지나 갈 수 있게 해야함

1-2. 블럭 끼리 겹치지 않게 구현 - BlockSpanwer에서 생성 로직 변경 필요

현재 블럭이 2개가 겹쳐서 생성됨

2.블럭에 빠지는 함정 만들기(함정 특징: 땅바닥인척 하지만 갑자기 내려감)

3.LV 수치에 따른 스테이지 변화 다르게 하기

4.옆으로 흐르는 백그라운드 만들기

5.Title Scene 만들기(마스크 트랜지션 사용해서 화면 옮겨보기)

 


1.카메라 Zoom 기능 추가

이유 : 게임을 플레이 해보는 도중 경계선에 있으면 캐릭터가 화면 밖에 있을때 조작이 잘 되고있는지 못하는 불편함을 느껴 추가하기로 하였다.

 

하지만 카메라가 오브젝트를 추적 할 수있는 메서드를 몰라 구글링 및 ChatGpt의 도움을 받아 추천 받았다.

카메라 안에 있는 특정 오브젝트를 찾는 방법은 2가지를 알았다.

 

1-1.첫번째는 Camera.WorldToViewportPoint' 메서드를 사용

해당 메서드는 월드 좌표를 뷰 포트 좌표로 변환해주는 역할을 한다.

ViewPort란 좌측하단(0,0) 에서 우측상단(1,1)을 가르키는 좌표계이다. 최소(-1,-1) 에서 최대 (2,2)까지 값을 가진다.

 

즉,카메라가 보는 영역인 ViewPort 좌표계에 Player 월드좌표를 변환 시켜서 사용 하는것이다.

    public float originSize = 6.87f;
    public float targetSize = 15f;  // 목표로 하는 Orthographic Size
    public float zoomSpeed = 5f;   // 줌 인/아웃 속도

    void ObjINCamera() //해당 메서드를 Update()에 포함 시킴
    {
        // 오브젝트의 월드 좌표를 뷰포트 좌표로 변환
        Vector3 viewportPos = Camera.main.WorldToViewportPoint(transform.position);

        // 뷰포트 좌표가 카메라의 시야에 있는지 확인
        if (!(viewportPos.x >= 0 && viewportPos.x <= 1 && viewportPos.y >= 0 && viewportPos.y <= 1 && viewportPos.z > 0))
        {
        	Debug.Log("오브젝트는 카메라의 뷰 밖에 있습니다.");
            Camera.main.orthographicSize = Mathf.Lerp(Camera.main.orthographicSize, targetSize, Time.deltaTime * zoomSpeed);

        }
        else
        {
        	Debug.Log("오브젝트는 카메라의 뷰 안에 있습니다.");
            Camera.main.orthographicSize = Mathf.Lerp(Camera.main.orthographicSize, originSize, Time.deltaTime * zoomSpeed);
        }
    }

이런식으로 하면 Lerp 함수를 통하여 자연스럽게 화면이 Zoom기능이 구현된다.

 

1-2.GeometryUtility.TestPlanesAABB 메서드를 사용한 방법

GeometryUtility.CalculateFrustumPlanesGeometryUtility.TestPlanesAABB 두 메서드를 사용하는 방법이다.

카메라의 View frustum(절두체)에 오브젝트의 바운딩 박스가 포함되는지 확인 하는 방법이다.

 

절두체는 카메라가 실질적으로 비추는 3D 공간이라 생각하면된다.

즉, 절두체에 오브젝트가 존재하는지 체크하여 Zoom 기능이 동작하는 코드이다. 

using UnityEngine;

public class ObjectInCameraView : MonoBehaviour
{
    public Camera mainCamera; // 사용할 카메라

    void Update()
    {
        // 카메라의 뷰 프러스텀을 계산
        Plane[] planes = GeometryUtility.CalculateFrustumPlanes(mainCamera);

        // 오브젝트의 바운딩 박스를 얻음
        Bounds objectBounds = GetComponent<Renderer>().bounds;

        // 오브젝트의 바운딩 박스가 카메라의 뷰 프러스텀 안에 있는지 확인
        if (GeometryUtility.TestPlanesAABB(planes, objectBounds))
        {
            Debug.Log("오브젝트는 카메라의 뷰 안에 있습니다.");
        }
        else
        {
            Debug.Log("오브젝트는 카메라의 뷰 밖에 있습니다.");
        }
    }
}

 

절두체는 컬링기법(그래픽 렌더링을 줄여 최적화하는 기법중 하나)을 사용할떄 주로 나타나는 이론이다.

https://01149.tistory.com/category/%EC%BB%B4%ED%93%A8%ED%84%B0%20%EA%B7%B8%EB%9E%98%ED%94%BD%EC%8A%A4

 

'컴퓨터 그래픽스' 카테고리의 글 목록

개발 공부하면서 자주 사용하던것, 알아야하는것을 올리는 곳입니다.

01149.tistory.com

잘 모르면 해당 글을 참고하자 

 

우선, 나는 첫번째 방법으로 구현을 하였으나 문제가 있었다.

플레이어가 뷰포트 경계에 서있는경우 카메라 Zoom 기능이 계속 동작하여 불쾌함을 주었다.

 

해당 코드를 조건문을 View 포트 안/밖 여부만 확인하고 바로 동작하게 되는 로직을 짜서 해당 현상이 일어났던 것이다.

 

비동기로 Zoom 기능이 동작을 해야만 자연스럽게 움직일것같아 코드를 수정하였다.

    public float originSize = 6.87f;
    public float targetSize = 15f;  // 목표로 하는 Orthographic Size
    public float zoomSpeed = 5f;   // 줌 인/아웃 속도
    bool isZooming = false;

    void ObjINCamera() //Update()함수안에 포함
    {
        // 오브젝트의 월드 좌표를 뷰포트 좌표로 변환
        Vector3 viewportPos = Camera.main.WorldToViewportPoint(transform.position);

        // 뷰포트 좌표가 카메라의 시야에 있는지 확인
        if (!(viewportPos.x >= 0 && viewportPos.x <= 1 && viewportPos.y >= 0 && viewportPos.y <= 1 && viewportPos.z > 0))
        {
            //Camera.main.orthographicSize = Mathf.Lerp(Camera.main.orthographicSize, targetSize, Time.deltaTime * zoomSpeed);
            if (!isZooming)
            {
                StartCoroutine(ZoomCamera(targetSize));
            }
        }
        else
        {
            //Camera.main.orthographicSize = Mathf.Lerp(Camera.main.orthographicSize, originSize, Time.deltaTime * zoomSpeed);
            if (!isZooming)
            {
                StartCoroutine(ZoomCamera(originSize));
            }
        }
    }

    // 코루틴을 사용하여 카메라의 줌을 부드럽게 변경
    IEnumerator ZoomCamera(float targetZoom)
    {
        isZooming = true;
        while (Mathf.Abs(Camera.main.orthographicSize - targetZoom) > 0.01f)
        {
            Camera.main.orthographicSize = Mathf.Lerp(Camera.main.orthographicSize, targetZoom, Time.deltaTime * zoomSpeed);
            yield return null;
        }
        Camera.main.orthographicSize = targetZoom;
        isZooming = false;
    }

isZooming 이라는 bool 함수를 넣어서 Zoom 기능 사용중인지 체크 여부를 했다.

코루틴에서 isZooming을 true로 바꿔 ObjINCamera()의 if문이 참이여도 동작을 안하게 막아놓았다.

코루틴은 비동기로 해당 Camera Size 만큼 Zoom 동작을 하고 코루틴을 탈출하게 설계를 하였다. 

 

해당 코드로 수정하니 카메라 Zoom 기능이 잘 동작 하였다.

 

2. 블럭 스폰 위치 규격화 하기

블럭 위치를 4방향에서 랜덤하게 나오게 구현하였다.

 

BlockSpanwer.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum defineNum
{
    Blokc_End_posX = -17, //해당 x좌표에 닿으면 pivot이 닿으면 사라지는 좌표 
}

public class BlockSpawner : MonoBehaviour
{
    [SerializeField] List<Block> block_list;
    [SerializeField] float blockSummonCoolTime = 2.0f;

    [SerializeField] float startPosX = 17.0f; //생성 시작되는 위치 x좌표
    [SerializeField] float startPosY_Min = -5.0f;
    [SerializeField] float startPosY_Max = 2.0f;

    Coroutine coroutineBlockSpanw;
    [SerializeField] List<float> blockSpawnPosY;

    int isSpawnBlockIndex = -1;

    public void BlockSpawnerON()
    {
       
        if (coroutineBlockSpanw == null)
        {
            coroutineBlockSpanw = StartCoroutine(BlockSpanw());
        }
        else
        {
            StopCoroutine(coroutineBlockSpanw);
            coroutineBlockSpanw = StartCoroutine(BlockSpanw());
        }
    }

    public void BlockSpawnerOFF()
    {
        if (coroutineBlockSpanw != null)
        {
            StopCoroutine(coroutineBlockSpanw);
        }
    }

    IEnumerator BlockSpanw()
    {
        while(true)
        {
            MakeBlock();

            yield return  new WaitForSeconds(blockSummonCoolTime);
        }
    }

   private void MakeBlock()
    {
        int type = Random.Range(0, block_list.Count);

        Block block = Instantiate(block_list[type]);
        block.gameObject.SetActive(false);

        float x = startPosX;
        //y 범위 : -4.5 ,-1.5,1.5, 4.5

        //한번 생겼던 위치에 다시 생기지 않는 함수
        int y;
        do
        {
            y = Random.Range(0, blockSpawnPosY.Count);
        }
        while (isSpawnBlockIndex == y);
        isSpawnBlockIndex = y;

        block.block_pos = new Vector3(x, blockSpawnPosY[y], 0);




        block.gameObject.SetActive(true);
    }
}

블럭 생성하는것을 코루틴으로 작성하여 비동기로 동작하게 하였으며 스테이지 LV 변화에 따라 생성속도를 빠르게 하기위해 blockSummonCoolTime 변수로 조절할 수 있게 했다.

 

또한 한번 생성한 자리에는 다시 생성하지 않게 로직을 만들어 변칙성을 주었다.

 

SerializeField(직렬화)를 통하여 Inspector에서 제어 할 수 있게 했다.

 

'내일배움캠프_Unity_6기 > TIL(Today I Learend)' 카테고리의 다른 글

TIL : 2024-08-29(목)  (0) 2024.08.29
TIL : 2024-08-28(수)  (0) 2024.08.28
TIL : 2024-08-26(월)  (0) 2024.08.26
TIL : 2024-08-22(금)  (0) 2024.08.23
TIL :: 2024-08-22(목)  (0) 2024.08.22

유니티퀘스트 8.야구게임

using Microsoft.VisualBasic;

Random rand = new Random(); //랜덤클래스 초기화
int Number = rand.Next(100, 1000); //100이상 1000미만 랜덤 숫자 초기화

Console.WriteLine("랜덤 정답 : " + Number);

char[] targetNumber = new char[3];
bool guessedCorrectly = false;
int attempts = 0;
for(int i = 0; i < 3 ; i++)
{
    targetNumber[i] = Number.ToString()[i];
}

while(!guessedCorrectly)
{
    attempts++;

    Console.Write("Enter your guess (3 digits) : ");
    string userGuess = Console.ReadLine();

    //예외처리
    //1.유저가 숫자를 입력 안한경우
    //총합 입력값이 (100 <= input <= 999) 이면 검사 시작



    int strikes = 0;
    int balls = 0;

    for (int i = 0; i < 3; i++) // userGuess
    {
        for(int j = 0; j < 3; j++) // targetNumber
        {
            if(targetNumber[j]  == userGuess[i]) //숫자가 같은지 비교
            {

                switch(i == j) //같은 자리인지 비교
                {
                    case true: //같은자리 (스트라이크)
                        strikes++;
                        break;

                    case false: //다른자리 (볼)
                        balls++;
                        break;
                }

            }
        }
    }
    Console.WriteLine(strikes + "Strike(s), " + balls + "Ball(s)");

    if (strikes >= 3) guessedCorrectly = true;
}

Console.WriteLine("Congratulations! You've guessed the number in" + attempts + " attempts.");

 

5주차 숙제 게임개발

 

Run & Jump - 프로토타입 설명

1.마우스를 누르면 캐릭터 머리 위에 파워 게이지가 표시됨

2.마우스를 놓으면 놓은 방향으로 캐릭터가 빙글빙글 돌아가며 발판들을 넘어감

3.이미 한번 착지한 발판은 점수 x , 발판을 처음 밟을때만 점수 증가

 

오늘 구현 한것

1.각종 플레이어의 플레이를 방해할 장애물 및 아이템 추가

    ㄴ점수를 주는 아이템인 동전 추가 및 플레이어를 방해하는 가시 추가 

2.날라오는 블럭들이 랜덤 범위, 규격화 필요

     ㄴ위치는 규격화 하지 못헀지만, 블럭들 4종류로 고정시켜 동전과 가시를 배치시킴

3. 게임 오버시 팝업창 및 제작

     ㄴ 플레이어가 떨어지거나 가시에 찔리면 팝업차이 뜨며 현재 점수 및 최고점수가 표시되게 설정 Retry버튼 누르맨

          Scene로드하기

 

구현 해야될것

1.날라오는 블럭 위치 규격화하기 

 


 

1.Scene 재로드 하는경우 Find()로 오브젝트를 못 찾는 이유 

이유 : GameObject가 비활성화 상태

비활성화 된 객체를 활성화시키려면, 활성화된 부모를 찾아서 자식을 찾는 형식으로 접근해야 합니다. 

빈 오브젝트를 하나 만들고 자식으로 넣으면 해결

게임이 끝나면 뜨는 팝업창에 빈 오브젝트를 부모오브젝트로 설정

GameManager.cs

 private void OnEndPanel()
 {
     Transform EndObj = endPanel.transform.GetChild(0);
     EndObj.gameObject.SetActive(true);
 }

 private void DisEndPanel()
 {
     Transform EndObj = endPanel.transform.GetChild(0);
     EndObj.gameObject.SetActive(false);
 }

게임 매니저에 해당 코드를 추가하여 EndPanel 오브젝트를 껏다 켰다 할수 있게 설계

 

2.자식오브젝트의 Scale 값을 부모오브젝트와 Scale 값을 덜 영향받게 하기

이유 : 자식으로 오브젝트를 넣으며 해당 오브젝트는 계층 구조를 가지기 떄문에
위치,크기,각도 값이 같이 변경 된다.

위의 두 Block 오브젝트는 Scale 값이 다른걸 볼 수있다. 이걸 바로 자식 오브젝트로 넣으면 Coin의 Scale을 조절 해야됨으로 나중에 보수적인 면에서도 좋지 않다. 부모(Block) - 자식1(빈오브젝트) - 자식2(Coin)으로 설계를 하면 

빈 오브젝트를 조절하여 Coin 은 제어하지 않고도 조절이 가능하다. 

'내일배움캠프_Unity_6기 > TIL(Today I Learend)' 카테고리의 다른 글

TIL : 2024-08-28(수)  (0) 2024.08.28
TIL : 2024-08-27(화)  (0) 2024.08.27
TIL : 2024-08-22(금)  (0) 2024.08.23
TIL :: 2024-08-22(목)  (0) 2024.08.22
TIL : 2024-08-21(수)  (0) 2024.08.21

5주차 숙제 게임개발 

 

Run & Jump - 프로토타입 설명

1.마우스를 누르면 캐릭터 머리 위에 파워 게이지가 표시됨

2.마우스를 놓으면 놓은 방향으로 캐릭터가 빙글빙글 돌아가며 발판들을 넘어감

3.이미 한번 착지한 발판은 점수 x , 발판을 처음 밟을때만 점수 증가

 

구현 해야될것

1.각종 플레이어의 플레이를 방해할 장애물 추가 필요(화면을 날라다니는 오브젝트,발판 위의 가시 등등)

2.날라오는 블럭들이 지금 완전 랜덤 범위라서, 규격화가 필요함 ( 블럭이 겹쳐서 나오면 캐릭터가 피할 곳이 없음)

 

오늘 구현한것

1.게임에서 Player들이 획득하는 아이템(Coin)추가

2.Player의 게임플레이를 방해할 Spikes(가시) 추가 , 가시에 닿게되면 캐릭터가 죽음(Dead) 처리 됨

3.밑에 버그 수정


 

버그 수정

1. 자기 발 밑에 바닥이 있는 상태로 아래를 향해 발사하면 캐릭터
착지하지 못한 판정이 되서 조작이 불가함

 

해당 블럭 위에서 아래로 캐릭터를 이동시키면 제자리에서 계속 구르는 버그가 있었다.

 

Player.cs

  private void PlayerStop()
  {

      isJump = false;
      isJumping = false;
      rigid.velocity = Vector2.zero;
  }

  private void OnCollisionEnter2D(Collision2D collision)
  {
      if (collision.gameObject.CompareTag("Block"))
      {
          PlayerStop();
          anim.SetBool("isJump", false);
      }

      if (collision.gameObject.CompareTag("Spike"))
      {
          Debug.Log("Player 가시 닿음");
          isdead = true;
          anim.SetBool("isDead", true);
          PlayerStop();

      }
  }

Player.cs 있는 CollisonEnter2D 메서드 내의 "Block"태그 의 PlayerStop() 메서드가 동작해야되는데

Block 위에서만 발사 하면  CollisonEnter2D가 동작을 하지 않는것이다. 그렇다고 Stay함수를 쓰면 로직이 꼬이기 시작하였다. 

 

Block 위에서 아랫방향으로 굴러도 버그 발생 x

Player.cs

 void Update()
 {
     if (!isdead)
     {
         anim.SetBool("isRun", false);

         if (!isJump && !isJumping)
         {
             if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))
             {
                 transform.position += Vector3.right * speed * Time.deltaTime;
                 renderer.flipX = true;
                 anim.SetBool("isRun", true);
             }

             if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
             {
                 transform.position += Vector3.left * speed * Time.deltaTime;
                 renderer.flipX = false;
                 anim.SetBool("isRun", true);
             }

             if (Input.GetMouseButton(0) || Input.GetKeyDown(KeyCode.Space))
             {
                 isJump = true;
                 JumpArrow.JumpArrowPrint(transform.position);
                 JumpPower.JumpPowerPrint(transform.position);
                 anim.SetBool("isRun", false);

             }

         }

         if (isJump && !isJumping)
         {
             if (Input.GetMouseButtonUp(0) || Input.GetKeyUp(KeyCode.Space))
             {
                 isJumping = true;

                 anim.SetBool("isJump", true);
                 Vector2 PlayerPos = transform.position;
                 Vector2 dir = JumpArrow.GetJumpingDir() - PlayerPos;
                 dir.Normalize();

                 float power = JumpPower.GetJumpingPower();

                 Debug.Log("발사 방향" + dir);
                 Debug.Log("발사 파워" + power);

                 rigid.AddForce(dir * power, ForceMode2D.Impulse);
                 JumpArrow.JumpArrowOff();
                 JumpPower.JumpPowerOff();

                 //제자리 구르기 or 밑에 방향으로 발사 시에
                 //블럭이 있으면 멈추지 않는 현상을
                 //콜리더를 껏다가 바로 켜는형식으로 변경
                 collider.enabled = false;
                 Invoke("ColliderON", 0.05f);
             }
         }
     }
 }

Player 오브젝트 객체가 발사하여 날라갈때 , Player의 Collider를 껏다 켜는걸로 해결하였다.

발사해서 공중에 날라갈떄 Collider를 껏다가 0.05f 후에 Collider를 다시 켜서 충돌처리를 하게 하였다.

Collider를 껏다 다시 켜니 위에 있던 CollisonEnter2D가 동작이 되어서 정상적으로 처리 되었다.

 

 

2.재시작(Scene 재로드)를 하면 GameManager의 오브젝트들이 Mssing되어버림

GameManager.cs

   [SerializeField] GameObject block;
   [SerializeField] Text scoreTxt;
   [SerializeField] Text levelTxt;
   [SerializeField] float blockSummonCoolTime = 2.0f;
   [SerializeField] bool isstop = false;

   int totalscore = 0;
   int curLevel = 0;
   
   
    private void UI_Load()
 {
     // 씬 전환 시 매번 호출하여 최신 오브젝트 참조를 설정합니다.
     if (scoreTxt == null)
     {
         GameObject scoreObj = GameObject.Find("UI_Score");
         if (scoreObj != null)
         {
             scoreTxt = scoreObj.GetComponent<Text>();
         }
     }

     if (levelTxt == null)
     {
         GameObject levelObj = GameObject.Find("UI_Level");
         if (levelObj != null)
         {
             levelTxt = levelObj.GetComponent<Text>();
         }
     }

 }
 private void OnEnable()
 {
     UnityEngine.SceneManagement.SceneManager.sceneLoaded += OnSceneLoaded;
 }

 private void OnDisable()
 {
     UnityEngine.SceneManagement.SceneManager.sceneLoaded -= OnSceneLoaded;
 }

 private void OnSceneLoaded(UnityEngine.SceneManagement.Scene scene, UnityEngine.SceneManagement.LoadSceneMode mode)
 {
     // 씬이 로드된 후 UI 로드
     UI_Load();
 }

GameManager를 싱글톤으로 구성하고  DontDestroyOnLoad()를 이용하여 Scene 파괴를 막았는데 오브젝트가 Missing이 되었다. 

Scene이 Load 될때마다 Text 오브젝트들이 Missing 되었다.

원인 :
1.Scene은 Load씨에 하이어라키에 있는 오브젝트들은 전부 제거된다.

2.DontDestroyOnLoad() 실행 시,  GameManager는 파괴되지 않더라도 다른 오브젝트들이 터졌다가 다시 생성되는것이기에 Missing이 됨

3.Inspector에서 참조한 경우 자동으로 참조하지 못하기에 직접 스크립트에서 참조 해줄수 있게 해줘야함 

 private void UI_Load()
 {
     // 씬 전환 시 매번 호출하여 최신 오브젝트 참조를 설정합니다.
     if (scoreTxt == null)
     {
         GameObject scoreObj = GameObject.Find("UI_Score");
         if (scoreObj != null)
         {
             scoreTxt = scoreObj.GetComponent<Text>();
         }
     }

     if (levelTxt == null)
     {
         GameObject levelObj = GameObject.Find("UI_Level");
         if (levelObj != null)
         {
             levelTxt = levelObj.GetComponent<Text>();
         }
     }

 }

해당 코드를 통해서 null 인경우 새 GameObject를 만들고 하이어라키에 존재하는 오브젝트를 찾아 초기화 해준뒤

Text 컴포넌트를 추가해주었다.

 

하지만 위에 코드만으로는 Scene 로드 할떄마다 오브젝트를 원하는 타이밍에 넣어 줄 수가 없었다.

 

** Awake vs OnEnable vs Start **


게임 오브젝트가 활성화된 상태로 씬에 처음 로드되면:

  1. Awake called: 오브젝트가 씬에 로드되면서 호출됨.
  2. OnEnable called: 오브젝트가 활성화되면서 호출됨.
  3. Start called: Awake가 끝나고 오브젝트가 활성화된 상태이므로 Start가 호출됨.

만약 오브젝트를 비활성화했다가 다시 활성화하면:

  1. OnDisable called: 오브젝트가 비활성화될 때 호출됨.
  2. OnEnable called: 오브젝트가 다시 활성화될 때 호출됨.

따라서 Awake는 오브젝트의 초기 설정을 위한 함수로, 활성화 여부에 관계없이 한 번만 호출되는 반면, OnEnable은 활성화될 때마다 호출됩니다.

 

새로운 씬이 로드되어도 이 오브젝트는 파괴되지 않으므로, AwakeStart 함수가 다시 호출되지 않습니다.


이 내용대로 동작 하는 경우면 GameManager는 계속 살아있기에 Awake()와 Start()는 호출이 되지 않기에 

OnEnable(),OnDisable() 함수를 활용해야한다. 

 

OnEnable(),OnDisable()을 활용하여 게임매니저가 활성화/비활성화 될때마다

Scene을 Load할때 동작하는 Event 함수를 만들어서 동작하였다.

Event를 추가하기 위해서 자료형 Scene,LoadSceneMode를 매개변수로 필요로 함
매개변수를 사용하진 않더라도 해당 메서드를 작성

 

 

유니티 퀘스트 달리기반 7번 행맨

string secretWord = "hangMan".ToUpper();
//C#에서는 string이 읽기 속성이기에 char[] 로 문자열을 관리해야된다.
//C#에서의 char[]과 string에 관련하여 TIL 작성하기
char[] guessWord = Enumerable.Repeat('_',secretWord.Length).ToArray(); // char[]을 '_'로 정답 글자수만큼 초기화하는 코드 
int attepts = secretWord.Length;
bool wordGuessed = false;

string copySecretWord = secretWord;

for (int i = 0; i < attepts; i++)
{
    //char[] -> string 변환 코드
    string str = string.Concat(guessWord);
    //'_'가 포함되어있으면 false , 포함되지 않으면 true
    wordGuessed = !str.Contains('_');

    //게임 클리어
    if (wordGuessed)
    {
        Console.WriteLine("");
        Console.WriteLine("축하합니다!! 단어 " + copySecretWord + " 를(을) 완성하셨습니다!! ");
        Console.WriteLine("도전 횟수 " + i + "회에 통과하셨습니다!");
        break;
    }

    //게임 안내
    Console.WriteLine("도전 횟수 : " + (i+1) + "/" + attepts);
    Console.WriteLine("[ " + str + " ]");
    Console.Write("추측할 단어를 입력해 주세요 : ");

    //글자 입력
    string input = Console.ReadLine();

    //글자 입력 체크 - 한개의 글자 체크 
    if(input.Length >= 2)
    {
        Console.WriteLine("");
        Console.WriteLine("두 글자 이상이 입력되었습니다. 다시 입력해 주십시오.");
        i--;
        continue;
    }
    //글자 입력 체크 - 글자 영문 체크
    else if ( !((input[0] >= 'A' && input[0] <= 'Z') || (input[0] >= 'a' && input[0] <= 'z')))
    {
        Console.WriteLine("");
        Console.WriteLine("영어가 아닙니다. 다시 입력해 주십시오.");
        i--;
        continue;
    }

    //글자 소문자 고정
    input = input.ToUpper();
    //입력된 글자가 어디의 자리인지 체크
    int result = secretWord.IndexOf(input);

    do
    {
        if (result == -1)//없는 경우
        {
            Console.WriteLine("");
            Console.WriteLine("해당 문자는 없습니다.");
            break;
        }
        else
        {
            guessWord[result] = char.Parse(input);
            char[] word = secretWord.ToCharArray(); //문자열을 수정 할 수 있게 char[] 로 변경

            word[result] = '_'; //맞춘 글자를 '_' 처리함
            secretWord  = string.Concat(word); //char[]를 문자열로 다시 수정함 

        }

        result = secretWord.IndexOf(input);
    }
    while (result != -1);

 
    
}

if (!wordGuessed)
{
    Console.WriteLine("");
    Console.WriteLine("실패하셨습니다!! 정답은 " + copySecretWord + " 입니다!! 다음기회에...");
}

 

5주차 숙제 게임개발 

Run & Jump - 프로토타입 설명

1.마우스를 누르면 캐릭터 머리 위에 파워 게이지가 표시됨

2.마우스를 놓으면 놓은 방향으로 캐릭터가 빙글빙글 돌아가며 발판들을 넘어감

3.이미 한번 착지한 발판은 점수 x , 발판을 처음 밟을때만 점수 증가

 

구현 해야될것

1.각종 플레이어의 플레이를 방해할 장애물 추가 필요(화면을 날라다니는 오브젝트,발판 위의 가시 등등)

2.날라오는 블럭들이 지금 완전 랜덤 범위라서, 규격화가 필요함 ( 블럭이 겹쳐서 나오면 캐릭터가 피할 곳이 없음

 

버그

1.자기 발 밑에 바닥이 있는 상태로 아래를 향해 발사하면 캐릭터 착지하지 못한 판정이 되서 조작이 불가함

2.재시작(Scene 재로드)를 하면 GameManager에 있는 오브젝트들이 Mssing되어버림

싱글 톤 상태에서 Don'tDestroty를 했는데 Text 파일들이 Missing 됨

 

*Collider2D의 한 면만 충돌판정을 받게 하고 싶을때

//Blcok Class

private void OnCollisionEnter2D(Collision2D collision)
 {
 	 //플레이어한테 점수 부여가 안되있고 , 콜리더가 Player 태그일때
     if (!isScore && collision.gameObject.CompareTag("Player"))
     {
     	 
         foreach (ContactPoint2D contact in collision.contacts)
         {
             if (contact.point.y > transform.position.y + 0.3f)
             {
                 GameManager.Instance.AddScore(score);
                 isScore = true;
             }
         }
     }
  
 }

 

  • ContactPoint2D: 충돌 지점에 대한 정보를 나타내는 구조체로, 충돌한 두 물체의 접점 좌표, 충돌 방향(normal), 충돌 지점의 상대 속도 등을 포함합니다.
  • collision.contacts: 충돌이 발생했을 때, 해당 충돌에 관련된 모든 접점을 포함하는 배열입니다. 이 배열은 충돌이 발생한 각 접점에 대해 ContactPoint2D 객체를 담고 있습니다.
  • 이 조건은 충돌 지점(Block의 윗면 = transform.positon.y +0.3f)의 y 좌표가 현재 객체(Player)y 위치보다 0.3 단위 이상 위에 있을 때만 참이 됩니다. 이 코드는 플레이어가 적을 위에서 밟았을 때 점수를 주는 식으로 사용할 수 있다.

 

5주차 하이퍼 캐주얼 게임 제작

 

 

1.2D 에서  기준 벡터(pivot_dir)와 각도를 알고 있을때  날라가는 벡터값 구하기

Vector2 pivot_dir = new Vector2(1, 0);
var quaternion = Quaternion.Euler(0, 0, tr.localEulerAngles.z);
Vector3 dir = quaternion * pivot_dir;
return dir;

pivot_dir로 오른쪽 진행이 기준 , 유니티는 각도값을 쿼터니언을 사용하기에 오일러(degree)를 쿼터니언으로 변경

최종적으로 각도 * 방향 벡터 = 원하는 방향 벡터를 구하여서 사용 하였음 

게임 개발이 처음이어도 쉽게 배우는 모바일 게임 개발 4주차

 

1.C# Linq 라이브러리의 OrderBy()의 정렬 알고리즘

 

OrderBy병합정렬 기반의 알고리즘을 사용하였으며,

.NET에서 OrderBy는 내부적으로 TimSort알고리즘을 사용중이다. 

 

OrderBy는 매개변수 데이터 기준으로 오름차순 정렬을 한다.

내림 차순을 원하면  OrderByDescending을 호출해야한다.

 

p.s 키워드

더보기

*병합정렬(MergeSort)  : 

1.안전정렬에 속하며 분할 정복 알고리즘의 하나 이다.

//따로 병합정렬 게시글을 만들어 정리 할것

 

*.NET :

.NET은 다양한 유형의 애플리케이션을 빌드하기 위한 무료 크로스 플랫폼 오픈 소스 개발자 플랫폼입니다. 이는 여러 언어로 작성된 프로그램을 실행할 수 있으며, 그 중 가장 인기 있는 언어는 C#입니다. 이는 많은 대규모 앱 프로덕션에서 사용되는 고성능 런타임에 의존합니다. - MicroSoft 메뉴얼 참조

 

.NET  다양한 언어(C#,F#,VB NET등등)을 이용해 프로그램을 개발하는 프레임 워크로 이 프레임 워크를 사용하여 다양한 플랫폼(윈도우,리눅스,macOS,모바일 등등)에서 실행 가능케해주는 플랫폼과 언어 간의 호환성을 제공하는 프레임 워크라고 생각하면 된다.

 

간단한 동작 과정

 

 

 

 

1.프로그램 코드가 C#, VB.NET 같은 고급 프로그래밍 언어로 작성.

2.위에 작성한 코드를 컴파일

3.NET 컴파일러가 이 코드를 CIL( Common Intermediate Language )이라는 중간언어로 변환합니다.

4.그리고 이 CIL 코드는 각 플랫폼에 맞는 .NET 런타임에 의해 실행됩니다. 

(사진출처 - 위키피디아 백과사전)

 

 

 

 

*TimSort 알고리즘 : 

2002년 소프트웨어 엔지니어 Tim Peters에 의하여 Tim Sort 알고리즘이 등장했다.

이 알고리즘은 삽입 정렬과 병합정렬을 결합하여 만든 정렬이다.

현재 2.3 이후 버전의 Python, Java SE 7, Android, Google chrome (V8), 그리고 swift까지 많은 프로그래밍 언어에서 표준 정렬 알고리즘으로 채택되어 사용

//따로 정렬 게시글을 만들어 정리 할것

2.랜덤으로 원소를 섞기 알고리즘 ( Fisher-Yates shuffle)

해당 강의에서 카드를 섞어서 배열해야되기 때문에 OrderBy()메서드에 x=>Random.Range() 를 매개변수로 넣어서 간단한 셔플 기능을 넣었었다. 하지만 위의 방법은 간단하게 셔플기능을 만드는것에 특화되 있지 효율적으로 볼수가 없다.

 

이 기능은 Random.Range로 숫자 난수를 뽑아오기에 O(n) 시간이 걸리고

이제 정렬을 위해 OrderBy가 동작하면 O(n log n)이기에 시간면에서 비효율적이다.

 

하지만 Fisher-Yates shuffle 알고리즘의 시간복잡도는 O(n)이기 최적화면에서 매우 훌륭하다.

다음 프로젝트때 셔플기능을 사용할때 이 알고리즘을 사용해보자

 

3. m x n 배열에 오브젝트를 배치하는 알고리즘

int[] arr = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7 };
arr = arr.OrderBy(x => Random.Range(0f, 7f)).ToArray();

//4 * 4 행렬에 오브젝트 배치할때

//내가 자주 쓰는 방법
 int[,] arr1 = new int[4, 4];

 for(int y= 0; y < 4; y++)
 {
     for(int x = 0; x <4; x++)
     {
         GameObject go = Instantiate(card, this.transform);

         go.transform.position = new Vector2(x * 1.0f - 1.5f, y * 1.0f - 4.5f);
         go.GetComponent<Card>().Setting(arr[(x * 4) + y]);
     }
 }
 
 //강의
 for (int i = 0; i < 16; i++)
{
    GameObject go = Instantiate(card, this.transform);

    float x = (i % 4) * 1.0f - 1.5f; //나머지
    float y = (i / 4) * 1.0f - 4.5f; // 몫

    go.transform.position = new Vector2(x,y);
    go.GetComponent<Card>().Setting(arr[i]);
}

x의 좌표 =  (index % 4) * 벌어지는 간격 * 초기 x 좌표값 , y의 좌표 =  (index / 4) * 벌어지는 간격 * 초기 y 좌표값

으로 반복문을 한번만 써서 만들수도 있다. 

 

4.PixelPerUnit (PPU)

Unity에서 1유닛(Unit)에 포함되는 픽셀(Pixel)의 수를 의미한다.

우리가 아는 FPS에도 Frame per Second로 1초에 몇장의 Frame이 출력되냐 와 비슷한 의미이다.

 

일반적으로는 PPU는 실제 이미지 사이즈와 똑같이 설정을 많이 한다.

이미지 500 x 500의 원본사이즈 , PPU도 500으로 설정된 화면이다.

 

PixerPerUnity을 1000으로 설정하면 이미지가 작아진다.

PPU가 500 일때 , 1유닛의 원본 사이즈 (500) : PPU (500) 으로 1x1 크기로 렌더링 된다.

하지만 PPU가 1000이 되면 , 1유닛의 원본사이즈(500) : PPU(1000)하나의 유닛을 표현하는데 1000픽셀에 해당

됨으로 1000(PPU) x (렌더링 크기) = 500(원본사이즈) , 렌더링 사이즈 = 0.5로 0.5 x 0.5 크기로 렌더링 되는것이다.

 

 

게임 개발이 처음이어도 쉽게 배우는 모바일 게임 개발 5주차

 

1.안드로이드 빌드 환경 세팅하기

Key 생성을 하면 원하는 위치에 .keystore 파일이 생성된다.

생선된 keystore 형식의 파일

 

2.Unity Ads를 통해 광고 붙이기 (테스트)

 

 

*메뉴얼 적용해서 Script 및 유니티 내부 변경

AsdInitalier Scripts에 https://docs.unity.com/ads/en-us/manual/InitializingTheUnitySDK 해당 코드 기입

RewardedAdsButton 에 https://docs.unity.com/ads/en-us/manual/ImplementingRewardedAdsUnity 해당 코드 기입

한국어 설정있으니 변경해서 참고하면 도움 많이 됨 , 만든 프로그램에 적용시키려면 코드 변경 필요함

 

AsdInitalizer Inspector에 숫자로 등록된 GameID를 입력을 똑바로 해줘야함 (중요)
게임 시작시 광고 SDK 초기화 및 광고를 불러오기 준비 완료 알람

1.게임 시작시 AdsInitalizer의 OnInitializationComplete() 통해 초기화 완료 알림

2.초기화 완료함에 따라 LoadAd()를 통해 광고를 미리 불러오기 알림

3.LoadAded()를 통해 광고가 불러져왔다는것을 알림

 

즉, 게임이 재시작 될때마다 광고 SDK 초기화 -> 광고 미리 불러오기 -> 광고 완료 -> 게임 완료 -> 끝(버튼)을 누르면

미리 불러온 광고를 재생 -> 광고의 close 버튼을 누르면 Scene 재로드

 

 

라는 로직으로 변경하였다.

 

변경한 AsdInitlaizer Scripts

더보기
using UnityEngine;
using UnityEngine.Advertisements;

public class AdsInitializer : MonoBehaviour, IUnityAdsLoadListener, IUnityAdsInitializationListener
{
    [SerializeField] string _androidGameId;
    [SerializeField] string _iOSGameId;
    [SerializeField] bool _testMode = true;
    private string _gameId;

    [SerializeField] string _androidAdUnitId = "Rewarded_Android";
    [SerializeField] string _iOSAdUnitId = "Rewarded_iOS";
    string _adUnitId = null; // This will remain null for unsupported platforms

    void Awake()
    {
        InitializeAds();
    }

    public void InitializeAds()
    {
#if UNITY_IOS
        _gameId = _iOSGameId;
        _adUnitId = _iOSAdUnitId;
#elif UNITY_ANDROID
        _gameId = _androidGameId;
        _adUnitId = _androidAdUnitId;
#elif UNITY_EDITOR
        _gameId = _androidGameId; //Only for testing the functionality in the Editor
        _adUnitId = _androidAdUnitId;
#endif
        if (!Advertisement.isInitialized && Advertisement.isSupported)
        {
            Advertisement.Initialize(_gameId, _testMode, this);
        }
    }

    public void LoadAd()
    {
        // IMPORTANT! Only load content AFTER initialization (in this example, initialization is handled in a different script).
        Debug.Log("Loading Ad: " + _adUnitId);
        Advertisement.Load(_adUnitId, this);
    }

    public void OnInitializationComplete()
    {
        Debug.Log("Unity Ads initialization complete.");
        LoadAd();
    }

    public void OnInitializationFailed(UnityAdsInitializationError error, string message)
    {
        Debug.Log($"Unity Ads Initialization Failed: {error.ToString()} - {message}");
    }


    public void OnUnityAdsAdLoaded(string placementId)
    {
        //throw new System.NotImplementedException();
        Debug.Log("Ad Loaded: " + placementId);
    }

    public void OnUnityAdsFailedToLoad(string placementId, UnityAdsLoadError error, string message)
    {
        //throw new System.NotImplementedException();
    }
}

변경한 RewardedAdsButton

더보기
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Advertisements;
using UnityEngine.SceneManagement;

public class RewardedAdsButton : MonoBehaviour, IUnityAdsLoadListener, IUnityAdsShowListener
{
    //[SerializeField] Button _showAdButton; //버튼 오브젝트에 적용시킬거면 여기에 넣어 쓰면됨
    //나는 글자를 버튼기능처럼 쓰려고 글자에 button 컴포넌트 넣고 주석처리함
    [SerializeField] string _androidAdUnitId = "Rewarded_Android";
    [SerializeField] string _iOSAdUnitId = "Rewarded_iOS";
    string _adUnitId = null; // This will remain null for unsupported platforms

    void Awake()
    {
        // Get the Ad Unit ID for the current platform:
#if UNITY_IOS
        _adUnitId = _iOSAdUnitId;
#elif UNITY_ANDROID
        _adUnitId = _androidAdUnitId;
#elif UNITY_EDITOR
   		_adUnitId = _androidAdUnitId;
#endif

        // Disable the button until the ad is ready to show:
        //_showAdButton.interactable = false;
    }

    // Call this public method when you want to get an ad ready to show.
    public void LoadAd()
    {
        // IMPORTANT! Only load content AFTER initialization (in this example, initialization is handled in a different script).
        Debug.Log("Loading Ad: " + _adUnitId);
        Advertisement.Load(_adUnitId, this);
    }

    // If the ad successfully loads, add a listener to the button and enable it:
    public void OnUnityAdsAdLoaded(string adUnitId)
    {
        Debug.Log("Ad Loaded: " + adUnitId);

        if (adUnitId.Equals(_adUnitId))
        {
            // Configure the button to call the ShowAd() method when clicked:
            //_showAdButton.onClick.AddListener(ShowAd);
            // Enable the button for users to click:
            //_showAdButton.interactable = true;
        }
    }

    // Implement a method to execute when the user clicks the button:
    public void ShowAd()
    {
        // Disable the button:
        //_showAdButton.interactable = false;
        // Then show the ad:
        Advertisement.Show(_adUnitId, this);
       
    }

    // Implement the Show Listener OnUnityAdsShowComplete callback method to determine if the user gets a reward:
    public void OnUnityAdsShowComplete(string adUnitId, UnityAdsShowCompletionState showCompletionState)
    {
        if (adUnitId.Equals(_adUnitId) && showCompletionState.Equals(UnityAdsShowCompletionState.COMPLETED))
        {
            Debug.Log("Unity Ads Rewarded Ad Completed");
            // Grant a reward.
            
            LoadAd(); // 광고 불러오기
            SceneManager.LoadScene("MainScene"); //광고 다 보면 메인 씬으로 이동하기
        }
    }

    // Implement Load and Show Listener error callbacks:
    public void OnUnityAdsFailedToLoad(string adUnitId, UnityAdsLoadError error, string message)
    {
        Debug.Log($"Error loading Ad Unit {adUnitId}: {error.ToString()} - {message}");
        // Use the error details to determine whether to try to load another ad.
    }

    public void OnUnityAdsShowFailure(string adUnitId, UnityAdsShowError error, string message)
    {
        Debug.Log($"Error showing Ad Unit {adUnitId}: {error.ToString()} - {message}");
        // Use the error details to determine whether to try to load another ad.
    }

    public void OnUnityAdsShowStart(string adUnitId) { }
    public void OnUnityAdsShowClick(string adUnitId) { }

    void OnDestroy()
    {
        // Clean up the button listeners:
        //_showAdButton.onClick.RemoveAllListeners();
    }
}

 

4.광고 추가하고 Build 오류 대처법

 

강의를 보고 광고를 추가 했을때 Build가 되지않아서 계속 터졌었다.

원인은 Build 시에 resolving android dependencies 가 자꾸 멈춰버리는 현상이 있었다.

https://meta-jun.tistory.com/253

 

[Unity] Gradle failed to fetch dependencies, Android Resolve 오류

가끔씩 Android Resolve가 제대로 안되면서 오류가 생기더라구요. 이때 한참 고생했는데 제가 해결한 방법을 알려드리겠습니다. 1. Preference - Exteranl Tools의 밑 JDK, Android SDK, NDK 경로가 제대로 되어있

meta-jun.tistory.com

 

https://stackoverflow.com/questions/50251836/unity-android-resolution-failed

 

Unity Android Resolution Failed

I'm currently struggling weird bug or error in Unity3D. Despite of only using original Google plugins, I get following error try to resolve Android dependencies. Here is error log: Resolution fail...

stackoverflow.com

Assets > Play Services Resolver > Android Resolver > Resolve 

 

해당 두 글을 보고서 Build 문제를 해결 하였다. 

 

Project Setting - Player - Android 환경에서 빨간 박스에 있는곳에 체크하면 

광고 추가 하여 Assets 폴더에 2개아 폴더가 추가되었다.

위에서 체크하여 Android 폴더에 2개의 파일이 추가 된것을 확인 하였고

 

위에 설정들을 체크하고 resolving android dependencies 를 Resolve 하여 정상적으로 동작하는지

확인하고 Build를 하니 정상적으로 해결 되었다.

'내일배움캠프_Unity_6기 > TIL(Today I Learend)' 카테고리의 다른 글

TIL :: 2024-08-22(목)  (0) 2024.08.22
TIL : 2024-08-21(수)  (0) 2024.08.21
TIL : 2024-08-19(월)  (0) 2024.08.19
TIL : 2024-08-16(금)  (0) 2024.08.16
TIL : 2024-08-14(수)  (0) 2024.08.14
게임 개발이 처음이어도 쉽게 배우는 모바일 게임 개발 4주차
1.간단한 셔플(랜덤) 배열 및 리스트를 만드는 방법
Using.UnityEngine.Linq

int[] arr = [0,1,2,3,4];

arr = arr.OrderBy(x => Random.Range(0f,4f)).Toarray();
해당 코드는 C#의 Linq 라이브러리의 OrderBy(오름차순 정렬할 매개변수) 메서드 이다.
OrderBy는 복합 정렬을 기반으로 만들어진 정렬 알고리즘이다.
arr.OrderBy(x => Random.Range(0f, 4f)) 메서드를 사용하면 각 요소에 대해 Random.Range(0f, 4f)가 호출되면서 그 결과값을 기준으로 정렬이 진행됩니다. 이때 Random.Range()가 각 요소마다 다른 값을 반환하여 랜덤으로 정렬 하게 됩니다.
정렬한 arr를 Toarray() 함수를 이용하여 배열로 만들고 초기화 합니다.

구체적으로 설명하자면:

OrderBy는 배열의 각 요소에 대해 람다 식 x => Random.Range(0f, 7f)를 실행하여 반환된 값을 기준으로 정렬합니다.
즉 ,  Random.Range(0f, 7f)는 호출될 때마다 0과 7 사이의 난수를 반환하므로, 배열의 각 요소에 대한 정렬 기준이 모두 다릅니다.
그러나 이 방법은 OrderBy가 Random.Range를 각 요소마다 몇 번 호출할지 제어할 수 없기 때문에,
성능이 중요한 경우 다른 방식의 섞기 알고리즘(Fisher-Yates shuffle)을 사용하는 것이 더 좋다.
나중에 Fisher-Yates shuffle를 공부를 해봐야겠다.
 

 

p.s 람다식이란 이름없는 함수로 간단하게 함수를 변수로 넣을때 사용된다.
하지만 이름이 없기에 재사용 할 수는 없으므로, 재사용 예정이면 따로 함수를 만들어 써야된다.
게임 개발이 처음이어도 쉽게 배우는 모바일 게임 개발 3주차
1.Hierarachy(하이어라키)에 있는 Prefeb 수정시 변경방법

Assset에 있는 Prefeb을 월드에 올려놓고 테스트 및 수정시 변경하는 방법이 Override를 이용해서 Asset에 등록된 Prefeb의 내용을 현재 월드에서 변경한 내용으로 덮어 쓸 수 있다.

 

2.Rigidbody와 Collider

Rigidbody  GameObject 가 물리 제어로 동작하게 합니다. 리지드바디는 힘과 토크를 받아 오브젝트가 사실적으로 움직이도록 해줍니다. 리지드바디가 포함된 모든 게임 오브젝트는 중력의 영향을 받아야 하며 스크립팅을 통해 가해진 힘으로 움직이거나 NVIDIA PhysX 물리 엔진을 통해 다른 오브젝트와 상호 작용해야 합니다.- Unity 메뉴얼 출처

 

콜라이더(Collider) 컴포넌트는 물리 충돌 처리를 위한 오브젝트의 형태를 정의합니다. 콜라이더는 보이지 않는 요소이므로 오브젝트의 메시와 정확히 동일한 모양일 필요는 없으며, 실제로는 게임플레이 시에는 대략적인 근사치로도 크게 구분되지 않으며 더 효율적입니다. - - Unity 메뉴얼 출처

 

정리 : Rigidbody 컴포넌트는 GameObject에 물리를 적용 시킨다.

Collider는 물리충돌 처리의 유무 및 충돌처리의 범위를 제어하는데 사용하는 컴포넌트이다.

*충돌 시키고 싶은 오브젝트는 전부 Rigidbody 가 필요하고 Collider는 충돌시 특정코드를 동작 시키는 곳에만 필요하다.

 

3.Rigidbody 컴포넌트의 물리엔진 사용 X

Kinematic : 충돌시 특정 동작을 하고싶지만 물리현상을 적용시키지 않을때 사용된다. 하지만 옵션을 키면 물리영향을 빋지 않기에 OnTrigger 함수를 사용하여 코드를 작성하여아한다. (Collider의  is Trigger를 체크해야만 Kinematic에서 충돌처리가 작동한다)

 

4.UnityPackage 파일을 Assets - Resources에 Import 시키고 싶을때

디자이너나 Assest Store에서 받은 Package 파일을 드래그 앤 드랍으로 쉽게 Import 할 수 있다.

 

★★★ 5.RectTransform의 Anchor,Pivot (매우 중요 :: UI 위치 및 크기 설정에 필수로 들어가는 내용)

Canvas 오브젝트화면 해상도에 따라 크기가 조정된다.
간단히 정리하면 이미지는 Anchor를 기준으로 Pivot이 결정된다.

 

600 * 800 해상도에 좌측,상단으로부터 100씩 거리가 떨어져있는 이미지를 800*600 해상도로 변경해도 위치나 크기가 변경되지 않는다.

 

 

RectTransform 에서 나타내는 좌표 및 거리값  [앵커와 피봇의 거리를 나타낸다].

Pivot은 이미지의 중심점으로 피봇의 좌표는 0,0(좌측하단) - 1,1(우측상단)로 정해져있다.

 

노란색Anchor의 위치 , 파란색 점Pivot의 위치 , 하얀박스 이미지의 크기 를나타낸다.

 

Alt를 누른채 클릭하면 Rect Transform까지 동시에 변하고(이미지를 해상도에 맞춰 변경),
Shift를 누른채 클릭하면 Pivot이 동시에 변하고,
Shift + Alt를 누른채 클릭하면 Rect Transform과 Pivot이 동시에 변합니다.

 


Strectch를 쓰면 앵커가 분리되어 Rect Transform의 좌표Left top right bottom으로 표기가 변경된다.

(Pivot이 아닌 이미지와 앵커의 거리 를 나타냄)

 

Pivot 위치에 따른 Scale 값 변경시 바뀌는 모습으로 Pivot 설정의 중요성을 보여준다.


6.Slider 오브젝트를 안쓰고 HP 만들기 테크닉

  //Cat 오브젝트에 Food라는 오브젝트가 충돌 될때마다
  //energy가 회복되는 코드
  
  private void OnTriggerEnter2D(Collider2D collision)
  {
      if(collision.gameObject.CompareTag("Food"))
      {
          if(energy < full)
          {
              energy += 1.0f;
              
               //front(이미지 오브젝트)
               //Scale 값을 수정하여 빨간색 게이지가 차오르게 설계
              front.localScale = new Vector3(energy / full, 1.0f, 1.0f);
              Destroy(collision.gameObject);

          }          
      }
  }

+ Recent posts