- Input.GetMouseButtonDown : 마우스 버튼을 누른 순간


뒤에 소괄호 안에 숫자 0, 1, 2에 따라 마우스 왼쪽버튼, 오른쪽버튼, 휠버튼으로 나뉜다.


누른 순간 true로 변환되며, 아무것도 안 했을 때 기본적으로 false로 구분됨. 


using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour { void Update() { if (Input.GetMouseButtonDown(0)) Debug.Log("Pressed left click."); if (Input.GetMouseButtonDown(1)) Debug.Log("Pressed right click."); if (Input.GetMouseButtonDown(2)) Debug.Log("Pressed middle click."); } }

OnMouseDown() 보다는 Input.GetMouseButtonDown()을 추천한다.


그 이유는 



    void OnMouseDown()
    {
        print("OnMouseDown~~");
    }

    void Update () {
        if (Input.GetMouseButtonDown(0))
        {
        print("Input.GetMouseButtonDown!!");
        }
    }


이렇게 하고 실행을 해보니, 이유는 모르겠으나 


Input.GetMouseButtonDown!!만 게속 출력되고, OnMouseDown은 실행이 되지 않는다.


또한,


OnMouseDown() 함수는 모바일에서 적용되지 않는다.

 

Posted by sungho88
,

유니티에서 쿨타임(쿨다운?) 기능을 사용하려고 한다.


RPG 게임에서 어떤 스킬을 사용했을 때, 


사용 후 일정 시간동안 못 쓰게 되고 일정 시간이 지난 후 다시 사용할 수 있게 된다.


[사용 방법]


하나 예제를 만들어보자..


Canvas - UI - Button으로 버튼을 생성한다.


Button을 클릭해보면, Image와 Button 컴포넌트가 기본적으로 만들어진것을 볼 수 있다.


[image 컴포넌트]


Source Image를 통해 버튼의 이미지 모양을 원하는대로 바꿀 수 있다. 

(Sprite 타입 이미지만 사용 가능)


버튼 이미지를 설정 후 Image Type을 Filled로 바꾸면 하위 항목이 다음과 같이 나온다.


여기서 Fill Amount 슬라이더가 생기는데, 


이 슬라이더 값을 0에서 1로 조절하면 버튼 이미지가 이에 따라 채워지는 영역이 변한다. 


이 값을 업데이트 해주면 쿨타임 버튼이 되는 것이다.


참고로,  


Fill Method를 통해 쿨타임의 형태를 바꿀 수 있고,

Fill Origin을 통해 쿨타임의 시작점(원점)을 바꿀 수 있다.


<Fill Method>

  1. Horizontal : 수평으로 쿨타임 적용. 
  2. Vertical : 수직으로 쿨타임 적용.
  3. Radial 90 : 90도로 쿨타임 적용.
  4. Radial 180 : 180도로 쿨타임 적용.
  5. Radial 360 : 360도로 쿨타임 적용.
Fill Origin은 어디서 쿨타임이 시작될 것인지 지정하는 것인데, 위 5가지에 따라 달라진다.


Fill Amount 슬라이더 값을 움직여보면 버튼이 사라지는 것을 볼 수 있다.


이것은 배경은 자동으로 만들어지지 않기 떄문에 사라지는 것처럼 보인다.


즉, 버튼 뒤에 동일한 위치에 배경을 깔아줘야한다.


그리고, Color를 원하는 색상으로 설정해보자. 다음과 같이 된다.




참고로, 빨간 배경을 버튼 위에 놓아야한다.


왜냐하면 Canvas 안에서는 순서대로 겹쳐서 쌓이는 계층구조이므로


빨간 배경이 버튼보다 아래 위치하면 빨간색이 보여지게 된다.


이제 게임 도중에 값을 변화하도록 스크립트를 작성해보자.


Create > C# Script로 스크립트를 하나 생성한다. 이름은 CoolTimeScript.cs로 생성했다.


[CoolTimeScript.cs]


public class CoolTimeScript : MonoBehaviour {
    public Image image;
    public Button button;
    public float coolTime = 10.0f;
    public bool isClicked = false;
    float leftTime = 10.0f;
    float speed = 5.0f;
    
    // Update is called once per frame
    void Update () {

        if(isClicked)
            if (leftTime > 0)
            {
                leftTime -= Time.deltaTime * speed;
                if(leftTime < 0) {
                    leftTime = 0;
                    if(button)
                        button.enabled = true;
                    isClicked = true;   
                }

                float ratio = 1.0f - (leftTime / coolTime);
                if(image)
                    image.fillAmount = ratio;
            }
    }

    public void StartCoolTime() {
        leftTime = coolTime;
        isClicked = true;
        if(button)
        button.enabled = false// 버튼 기능을 해지함.
    }
}



다음과 같이 작성한다.


유니티 On Click에서 버튼을 눌렀을 때 StartCoolTime()을 실행시키도록 하고,


Image와 Button은 버튼에 동일한 위치에 존재하는 컴포넌트를 집어넣어 주면 된다.

speed를 통해 쿨타임 시간을 조절할 수 있다.





Posted by sungho88
,

코드를 짤때, 필드를 public으로 해야할지, private로 해야할지 고민할 때가 있다.


public으로 했다가 잘못된 값이 입력되어 프로그램이 원치않는 동작이 일어날 수도 있다.


그래서 private로 하려고 하니... 해당 클래스에서만 사용이 가능해서 다른 클래스에서 사용하기 어렵다.


은닉화되어있는 private 변수를 사용하는 방법은 겟/셋 함수를 사용하는 것이다.


자바에서도 


private String name;


public String GetName() {

    return name;

}

public void SetName(String name) {

    this.name = name;

}


이렇게 이름을 갖고,


getName() 사용할 경우 name의 값을 얻을 수 있고,


수정하려면 setName() 메소드를 통해 변경을 할 수 있다.


유니티 C#에서는 더욱 간단한 방법을 제공한다.


    class People

    {

        private string name; 

 

        public string Name

        {

            get

            {

                return name;

            }

            set

            {

                if (value.Length > 3)

                    Console.WriteLine("이름은 3자 이상 넘을 수 없습니다.");

                else

                    name = value;

            }

        }


 

이렇게 하면 get은 GetName() 메소드를 대체한다. 즉, 속성 값 name을 반환하는데 사용된다.


 set 접근자는 SetName(String name)을 대체한다.


반드시 다 써야할 필요는 없고 둘 중 한가지만 사용해도 문제가 되지 않는다.


당연히, get접근자 하나만 사용하면 읽어오는 것밖에 할 수가 없고.. set접근자만 사용하면 쓸 수밖에 없다.


 



Posted by sungho88
,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class Drag : MonoBehaviour, IDragHandler
{

float distance = 10.0f;
public void OnDrag(PointerEventData eventData)
{
Vector3 mousePosition = new Vector3(Input.mousePosition.x,
Input.mousePosition.y, distance);
transform.position = mousePosition;
}

}


1. 드래그를 하기 위해서는 OnMouseDrag() 함수도 있지만, OnDrag() 함수를 사용할 수도 있다.

2. 먼저, OnDrag()를 사용하기 위해서는 


using UnityEngine.EventSystems; 


을 적어줘서 이벤트 시스템을 사용하겠다는 것을 표기한다.


3. MonoBehaviour뒤에 콤마(,)를 쓴 뒤 IDragHandler를 작성한다. 참고로 IDragHandler 는 인터페이스이다.


4. 3번 IDragHandler를 적으면 에러난다. 즉, 인터페이스에 선언된 메소드를 정의해줘야한다.

형태는 다음과 같다. public void OnDrag(PointerEventData eventData)


5. OnDrag()안에 드래그 했을 때, 일어날 동작들을 적으면 된다. 

이 블로그에서는 마우스 드래그하는데로 오브젝트가 딸려오는 것을 구현하려고 한다.


 


마우스는 평면으로 움직이므로  (x,y)축만 필요하다.


z축은 위와 같이 10으로 지정해줘도 되고, 그냥 오브젝트의 z축을 그냥 기입해도 된다.


그 다음에 transform.position에 mousePosition을 넣음으로써,


드래그할때 mousePosition이 게임오브젝트 위치에 대입되어 마우스 드래그할때 오브젝트가 딸려 움직이게 된다.


이상으로 마우스 드래그를 이용하여 게임오브젝트를 움직이는 방법을 알아보았다.


Posted by sungho88
,

스크린 좌표와 월드 좌표의 혼란때문에 머리가 복잡하다.


월드좌표로 하고 있는데 갑자기 스크린 좌표로 코드가 설정되었는지 이상해지고 있다.


예를 들어, 어떤 플레이어 캐릭터가 센터(0,0)에 있다고 가정하자.


그리고 적 캐릭터가 화면에서 플레이어를 향해 돌진한다.


이렇게 할 경우 플레이어의 위치로 이동하도록 0,0) 했는데


갑자기 왼쪽 하단 구석으로 적들이 몰려가서 바글바글거린다.


찾아보니 좌표의 문제였다.


참고 블로그 : http://hyunity3d.tistory.com/m/368



1. 월드 좌표계


- 오브젝트의 위치를 나타내는 좌표계로, 화면의 중심을 원점으로(0,0,0)으로 하는 3차원 상대좌표계이다.

- 게임 화면을 투영하는 카메라의 위치와 회전 상태에 따라 달리지므로 화면의 중심이 원점이 100%라고 할 수 없다.


- 스크린 좌표계는 단말기의 화면 좌표계로, 화면의 왼쪽 아래를 원점으로 하는 평면 절대좌표계입니다.

- 마우스 클릭이나 터치는 스크린 좌표계를 이용하여 처리한다. 이 좌표계는 카메라의 위치나 각도와 상관없이 일정하다.


- 뷰 포트 좌표계는 화면에 글자나 2D 이미지를 표시하기 위한 것. 화면 왼쪽


Posted by sungho88
,

유니티에서 사용하는 모든 UI 요소들은 기본적으로 Canvas안에 위치해야 한다.

캔버스(Canvas)는 Canvas 컴포넌트가 존재하는 게임 오브젝트의 일종이다.


캔버스를 먼저 생성하지 않아도, GameObject - UI 메뉴에서 자신이 원하는 UI (버튼, 이미지, 스크롤뷰 등등...)를


선택하면, 자동으로 Canvas가 생성이 되며, 그 안에 선택한 UI가 자식으로 생성이 된다.


또한, 캔버스(Canvas) 기능을 돕기 위해 EventSystem 오브젝트가 사용되기 때문에 동시에 생성된다.


캔버스(Canvas) 영역은 씬(Scene) 뷰에서 사각형으로 나타나므로 UI요소를 배치하기 용이하다.


캔버스(Canvas)에 있는 UI 요소는 계층 구조에 나타나는 것과 동일한 순서이다.


즉, 첫 번째 자식이 처음... 두 번째 자식이 그 다음으로 그려진다.


다시말하면, 첫 번째 자식이 두 번째 자식에 가려져 안 보이게 된다.


변경을 하고 싶다면 드래그하여 계층 구조에서 순서를 변경하면 된다.


또는 메소드를 사용하여 스크립트로 제어할 수도 있다.


1. Transform.SetAsLastSibling

가장 마지막으로 순서 변경

Move the transform to the end of the local transfrom list.



2. Transform.SetAsFirstSibling

가장 처음으로 순서 변경

Move the transform to the start of the local transfrom list.



3. Transform.SetSiblingIndex

순서 설정(index 값)

Sets the sibling index.



4. Transform.GetSiblingIndex

현재 순서 반환(index 값)

Gets the sibling index.

 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

렌더 모드(Render Mode)

캔버스에는 스크린 공간 또는 월드 공간에 렌더링하도록 하기 위해 사용되는 렌더 모드(Render Mode) 설정이 존재함.


1) 스크린 공간(Screen Space - Overlay)


이 렌더 모드에서는 UI 요소가 화면에서 씬 위에 렌더링된다.

스크린의 크기가 조절되거나 해상도가 변경되면 캔버스는 여기에 맞춰 자동으로 크기를 변경한다.



2) 스크린 공간(Screen Space - Camera)


이 모드는 Screen Space - Overlay 와 유사하지만 
이 렌더 모드에서는 캔버스가 특정 Camera 에서 주어진 거리만큼 앞쪽에 위치한다.
UI 요소는 이 카메라에 의해 렌더링된다. 즉 카메라 설정이 UI의 모습에 영향을 준다. 
카메라가 Perspective 으로 설정되어 있으면 UI 요소는 원근감이 있게 렌더링되며,
원근 왜곡의 정도는 카메라 Field of View 에 의해 조절될 수 있다. 
스크린의 크기가 조절되거나 해상도가 변경되거나 카메라 절두체가 변경되면 캔버스 역시 여기에 맞춰 
자동으로 크기를 변경한다.

3)월드 공간(World Space)


이 렌더 모드에서는 캔버스는 씬에 있는 다른 게임오브젝트처럼 동작한다.

캔버스의 크기는 사각 트랜스폼(Rect Transform)을 사용하여 수동으로 설정할 수 있다. (위 두 개는 수동 설정 불가)

또한 UI 요소는 3D 배치에 기반하여 씬의 다른 오브젝트의 앞 또는 뒤에 렌더링된다.

이 방식은 월드의 일부를 이루도록 의도된 UI에 유용하며 이 방식을 서사적 인터페이스라 부르기도 한다.

Posted by sungho88
,

스프라이트 랜더러(Sprite Renderer)의 속성 중의 Sorting Layer의 경우


Sprite의 렌더링 순서를 정의해준다.


물론 Z의 크기값에 따라 조정할 수 있지만, 이것을 사용하면 수치가 아니라 좀 더 간편하게 할 수 있게 된다.


Sprite가 여러 개 있을 때, 


가장 뒤에 있어야 할 Sprite...예를 들면 배경등은 Sorting Layer 가장 상단에 작성하고,


가장 앞에 보여야 할 Sprite...예를 들면 Player등은 Sorting Layer 가장 하단에 작성한다.


이렇게되면 같은 Sprite라고 해도 위에 나오게 된다.


물론 Default가 가장 뒷쪽으로 가게 된다.



예를 들어 1 2 3이 있다고 하자.


1에는 Sorting Layer가 적용되지 않은 Default


2에는 Sorting Layer 첫 번째 Num_01


3에는 Sorting Layer 두 번째 Num_01


즉, 태그는 다음과 같이 생성되어있다.



이렇게 한 뒤, 실행해보자.


2 --> 1로 드래그해보면 1 위에 2가 올라가는 것을 볼 수 있다.



3은 1과 2 모두보다 앞으로 나온다. 실제로 드래그해보면 3밑으로 1과 2가 밑으로 나오는 것을 볼 수 있다.



결론. 1이 가장 밑 < 2가 중간 < 3이 가장 상위에 있는 것을 볼 수 있다.


이처럼 스프라이트(Sprite) 이미지의 레이어 층을 쉽게 만들어주는 것이 바로 Sorting Layer이다.




Posted by sungho88
,

게임 오브젝트를 움직이는 작업을 하다보면, 


화면을 벗어나 우주 공간처럼 끝없이 가게 되는 것을 볼 수 있다.


이때, 화면안에 있도록 제한을 해줘야 하는데 가장 간단하게 구현할 수 있는 것이 바로



Camera.main.WorldToViewportPoint 를 사용하는 것이다.




Transforms position from world space into viewport space.


Parameters:


    position:

Vector3 Camera.WorldToViewportPoint(Vector3 position)



바로 카메라(Camera).WorldToViewportPoint(pos)로 써도 되지만, Camera.main을 쓰는 이유는


MainCamera라는 태그(Tag)가 붙은 첫번째 활성화된 카메라를 불러온다는 뜻이다.


즉, 기본적으로 유니티 프로젝트 생성시 만들어지는 main카메라를 사용하겠다는 의미이다.


WorldToViewportPoint(현재 위치)의 경우 position을 월드 공간에서 뷰포트 공간으로 변경시킨다.


카메라의 좌측 하단은 (0,0 , 0.0)이며, 우측 상단은 (1.0 , 1.0)이다. 


또한, z position은 카메라로부터의 거리를 월드 단위로 환산한 값이다.


따라서,


       Vector3 pos = Camera.main.WorldToViewportPoint(transform.position);


            if (pos.x < 0f) pos.x = 0f;

            if (pos.x > 1f) pos.x = 1f;

            if (pos.y < 0f) pos.y = 0f;

            if (pos.y > 1f) pos.y = 1f;


        transform.position = Camera.main.ViewportToWorldPoint(pos);



렇게 하면, 현재 위치(transform.position)를 pos 변수에 대입하고,


pos.x < 0f 라면 왼쪽 화면 밖으로 나갔다는 의미이므로 아무리 왼쪽으로 이동해도 0.0f

 

pos.x > 1f 라면 오른쪽 화면 밖으로 나갔다는 의미이므로 아무리 오른쪽으로 이동해도 1.0f 


pos.y < 0f 라면 아랫쪽 화면 밖으로 나갔다는 의미이므로 아무리 아래쪽으로 이동해도 0.0f 


pos.y > 0f 라면 윗쪽 화면 밖으로 나갔다는 의미이므로 아무리 윗쪽으로 이동해도 0.0f 



로 조건을 줘서 화면 안에서 못 벗어나게 한다.


그 다음에 다시 뷰포트 공간에서 월드 공간으로 변경시킨 뒤, 현재 위치에 넣으면 된다.


이렇게 한 뒤, 게임을 실행해보면 아무리 애써도 화면을 벗어나지 않고 가장자리까지만 가는 것을 볼 수 있다.




Posted by sungho88
,

짧아서 간단히 작성할 것이라 생각했는데 약 2시간동안 고생했다.


드래그 코드는 이미 내장된 함수가 존재한다.


OnMouseDrag()


이 함수를 호출하여 원하는 코드를 적으면 된다.


일단, 움직이고자 하는 게임오브젝트들에 Drag.cs 파일을 갖다 붙인뒤 Drag.cs파일에 코드를 입력한다.


[Drag.cs]


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

public class Drag : MonoBehaviour
{

float distance = 10;

void OnMouseDrag()
{
print("Drag!!");
Vector3 mousePosition = new Vector3(Input.mousePosition.x,
Input.mousePosition.y, distance);
Vector3 objPosition = Camera.main.ScreenToWorldPoint(mousePosition);
transform.position = objPosition;
}
}


불필요한 Start() 함수와 Update() 함수를 삭제하고 OnMouseDrag()함수만 작성해 간략하게 만들었다.


그런데!! 아무리 해도 함수가 실행되지 않았다.


그래서 혹시나해서 OnMouse로 시작하는 모든 함수를 작성해보았다.


OnMouseEnter

OnMouseExit

OnMouseUp

OnMouseDown

..


모든 마우스 관련 함수가 호출되지 않았다.


고민끝에 혹시나해서 이미지(Sprite)에 Collider를 추가해봤더니 이런 !! 된다.


마우스 관련 함수를 사용하기 위해서는 반드시 


콜라이더를 써야만 한다는 것을 알게 되었다.


위와 같이 쓴 다음에 실행해보면 게임오브젝트가 마우스로 이동하는 것을 볼 수 있다.

 

Posted by sungho88
,

FPS = Frames Per Second(초당 프레임 횟수)


즉, 1초에 출력, 만들어낼 수 있는 프레임 횟수를 뜻한다.


PC의 성능에 따라 FPS는 다를 수밖에 없다.


성능이 뛰어난 컴퓨터는 1초에 100FPS가 날 수도 있고,


후진 고물 컴퓨터는 1초에 5FPS일 수도 있다.


이럴경우, 총싸움을 하면 어떻게 될까?


예를들어, 1초에 100발이 나가는 사람과 1초에 5발이 나가는 사람이 싸운다면?


말이 안된다. 게다가 게임 성능에도 매우 좋지 않다. 큰 리소스를 잡아먹는다.


따라서, 성능에 무관하게 Time.deltatime으로 두 사용자가 동일한 FPS 결과값을 보장하게 한다.



Time.deltaTime의 의미는 다음과 같다.


이전 프레임에서 현재 프레임까지 걸린 시간이다.


만약,


int speed=1; 


void Update(){ 

      shotBullet += speed; 



정리하면, 


슈퍼 컴퓨터는 1초에 1000프레임이라고 가정하자.


그러면 Time.deltaTime는 1/1000(1000분의 1)이다.


이것을 곱하면  1000 * speed * 1/1000 이므로  = speed만 남는다.

 


게임용 일반 컴퓨터는 1초에 80프레임이라고 가정하자.


그러면 Time.deltaTime는 1/80(80분의 1)이다.


이것을 곱하면  80 * speed * 1/80 이므로  = speed만 남는다.



10년이 지난 구린 컴퓨터는 1초에 20프레임이라고 가정하자.


그러면 Time.deltaTime는 1/20(20분의 1)이다.


이것을 곱하면  20 * speed * 1/20 이므로  = speed만 남는다.



결론 : Time.deltaTime을 이용하면, PC의 성능과는 무관하게 동등한 조건이 되게 되므로 반드시 필요하다. 

Posted by sungho88
,