유니티 타일맵


유니티에서 제공하는 타일맵으로 맵을 쉽게 제작이 가능하다.




Grid

Grid : UI의 canvas같은 존재

Cell Size : 타일 하나의 크기


Tilemap Renderer

Sprite Renderer과 같은 기능


Tile Palette

타일을 모아놓은 콜렉션 에셋




타일을 클릭하고 붓을 칠하듯 찍으면 된다.

삭제 : Shit버튼을 누른상태에서 칠하거나 지우개툴을 선택

회전 : ‘[‘ , ‘]’ 버튼 이용




타일맵 충돌처리

타일맵전용 Collider가 따로 있음


콜라이더 합치기

이 파란색 영역역은 캐릭터가 닿지 않는 부분으로 Collider 각각 들어가 있는것은 비효율적이다


Composite Collider2D를 추가하고 TileMap Collider -> use by Composite를 체크해주면

콜라이더가 한개로 합쳐진다.

Rigidbody 속성을 바꿔줘야 맵이 떨어지지 않는다.


강의 참고 : https://youtu.be/dtpthaIYa8g?list=PL412Ym60h6uunDV_SaHfBXzfMhBjOFTnj


스프라이트 아틀라스


유니티에서 아틀라스는 텍스처를 한곳에 모은 한장의 큰 텍스처라고 할수있다.

텍스처 하나씩 따로따로 사용하고 관리하는것은 효율적이지 못하다.


아틀라스를 사용하면 드로우콜을 줄일 수 가 있다.


드로우콜?:

CPU가 GPU에게 어떠한 그림을 그려 달라고 요청하는것인데 많을 수록 게임이 무거워지고 프레임 저하가 올 수 도 있다.


프로젝트 셋팅하기

Unity 에디터 설정(Edit > Project Settings > Editor)에서 변경

빌드에 대해 활성화(Enabled for Builds): 플레이 모드에서가 아니라 빌드에 대해서만 패킹을 사용하고 싶을 때 

항상 활성화(Always Enabled): 패킹된 스프라이트가 플레이 모드 동안 스프라이트 아틀라스에서 텍스처를 확인하게 만들고, 편집 모드에서 원본 텍스처의 텍스처를 확인하게 만들고 싶을 때 사용




아틀라스 생성

Assets -> Create -> Sprite Atlas


이렇게 보라색 이미지가 생성된 것을 볼 수 있다.




이미지 패킹(묶기)!

그냥 드래그앤 드롭으로 끌어다 놓으면 된다.(폴더를 옮겨도 됨)

Type : Variant 다른 생성된 아틀라스를 포함시킬 수 있다.




스크립트 작성

using UnityEngine.U2D;

using UnityEditor.Experimental.U2D;


public class test : MonoBehaviour

{

public SpriteAtlas m_atlas;

public Image m_img1;


void Start()

{

m_img1.sprite = m_atlas.GetSprite("blue_boxCheckmark");

~~~생략

}

}




비교하기

<일반적인 Sprite를 넣었을때>


<아틀라스를 사용했을때>


Batches가 줄어든것을 볼 수 있다. 



룰타일 & 타일맵 레이어

    



룰타일

룰타일은 추가적인 다운로드가 필요하다.


https://github.com/Unity-Technologies/2d-extras

위 그림처럼 타일을 칠 할때 이미 설정해 놓은 룰에 맞게 칠 할 수 있다.




생성

Create - > Rule Tile

x : 해당 방향으로 타일이 칠 할수 없다

화살표 : 해당 방향으로 타일을 칠 할 수 있다.


생성한 룰타일은 TilePalets에 옮기면 생성이된다.


칠하기

룰의 맞게 색칠해 진다.



타일맵 레이어

타일맵에서도 레이어를 나누어 주변지물, 또는 배경을 표현할 수 있다.

(ex) 캐릭터 뒤에있는 나무 캐릭터 앞에있는 나무 통과는 되지만 캐릭터가 화면에 안보이는)


레이어 추가

Tile map 하나를 더 생성해주고

Tilemap Renderer -> Sorting Layer를 Background로 바꿔준다.

(칠하려는 타일은 색칠할 렌더러 속성을 받지 않고 칠하는 팔레트에 속성을 받기 때문에, 팔레트에서 Active Tilemap을 background를 수정해줘야한다.)


씬화면에서 Foucs On설정으로 타일맵만 볼수 있다.

유니티코리아  유튜브 타일맵 튜토리얼을 보고 작성



나무 -> Background

폭포 -> foreground


https://youtu.be/EZgvwvPcimQ

버텍스 및 프래그먼트 셰이더

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
59
Shader "Unlit/ToonVertx"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
 
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog
 
            #include "UnityCG.cginc"
 
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
 
            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };
 
            sampler2D _MainTex;
            float4 _MainTex_ST;
 
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }
 
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}
 
cs


프로퍼티


머티리얼의 일부로 저장되고 머티리얼 인스펙터에 나타나게 될 셰이더 변수(텍스처, 컬러 등)를 포함하고 있다.

언릿 셰이더 템플릿에서는 하나의 텍스처 프로퍼티가 선언되어있따.



SubShader



하나의 셰이더는 하나 이상의 서브셰이더를 포함할 수 있고 서브 셰이더는 일차적으로 다른 GPU성능에 맞는 셰이더를 구현하기 위해 사용된다.



Pass

각 서브셰이더는 여러 개의 패스로 구성되며 각 패스는 해당 셰이더의 머티리얼과 함께 렌더링될 동일 오브젝트용 버텍스 및 프래그먼트 코드의 실행을 나타낸다.

단순한 쉐이더는 하나의 패스만 사용하는 경우가 많지만 조명과 상호작용하는 셰이더의 경우 더 많은 패스를 필요로 한다.


CGPROGRAM .. ENDCG

버텍스 및 프래그먼트 셰이더 내의 HLSL코드 부분을 이 곳 안에 작성한다.



버텍스 셰이더


3D모델의 각 버텍스에서 실행되는 프로그램이다.

여기서 버텍스 위치를 오브젝트 공간에서 이른바 "클립공간"으로 변환한다.

GPU가 오브젝트를 화면에 래스터화하기 위해 클립 공간을 사용한다.

프래그먼트 셰이더에서 텍스처를 샘플링하기 위해 필요로한다.


래스터화 :  백터 그래픽 이미지를 비디오 디스플레이나 프린터 등의 래스터 디바이스에 출력하기 위해 래스터 이미지로 변환하는 것



프래그먼트 셰이더



오브젝트가 화면에 차지하고 있는 모든 픽셀마다 실행되는 프로그램이며 보통 각 픽셀의 컬러를 계산하고 출력하기 위해 사용된다.

화면에 보통 수백만 개의 픽셀이 있으며 프래그먼트 셰이더는 이 모든 픽셀에 대해 실행된다.


일부 변수 또는 함수 정의 뒤에는 기호가 붙는다. 이 기호에 대해서도 정리 해야겠다.

(예: POSITION 또는 :SV_Target)이 시맨틱은 이 변수의 "의미"를 GPU에 알려준다.


<셰이더를 적용>



모델을 한 가지 색으로 만드는 셰이더 예제


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
 Properties
    {
        // Color property for material inspector, default to white
        _Color ("Main Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            // vertex shader
            // this time instead of using "appdata" struct, just spell inputs manually,
            // and instead of returning v2f struct, also just return a single output
            // float4 clip position
            float4 vert (float4 vertex : POSITION) : SV_POSITION
            {
                return mul(UNITY_MATRIX_MVP, vertex);
            }
            
            // color from the material
            fixed4 _Color;
 
            // pixel shader, no inputs needed
            fixed4 frag () : SV_Target
            {
                return _Color; // just return it
            }
            ENDCG
        }
    }
}
cs

이 구조는 입력(appdata)및 출력(v2f)용으로 구조체를 사용하지 않고 셰이더 함수는 입력을 수동으로 판독하는 구조이다.


#pragma vertex vert : 프로젝션, 컬러, 텍스쳐 나 다른 fragment쉐이더를 coordinate하기 위해 사용 

#pragma fragment frag : 렌더링 창에서 이미지 속성을 수정하기 위한 쉐이더 프로그램, 각 화소마다 실행


POSITION : 변형 된 정점 위치

SV_POSITION : 픽셀 위치를 설명

UnityObjectToClipPos : 오브젝트 공간의 한 점을 동질적인 좌표에 있는 카메라의 클립공간으로 변환(World, View, Projection 행렬 연산을 쉽게 해주는 유니티 도우미 함수)


SV_Target : 결과를 어떤 렌더에게 보낼지 설정


<적용 후>

(얼굴은 적용하지 않아서 색이 남아있음)


학습참고

1) https://docs.unity3d.com/kr/current/Manual/SL-VertexFragmentShaderExamples.html

2) https://haewoneee.tistory.com/27


Humanoid Avatars

사람의 형태를 가지고 있다면 휴머노이드 아바타를 이용할 수 있다.


Avatar Definition

  1. Create From This Model : 이미 구성된 다른 아바타에서 아바타정의를 복사한다.


Optimize Game Object : 유니티는 애니메이션을 만들 때 빈 오브젝트를 만들어서 모델의 모든 뼈와 관절을 표현한다.  그러면 모델의 각각 뼈대마다 트랜스폼할 수 있게지만 효율성이 떨어지기 때문에 체크하여 빈 오브젝트 생성을 막는다.


하지만 디테일한 설정을 원한다면 Transform을 드러내야 한다.  ‘+’버튼을 눌러보면 모델의 어떤 부위를 나타낼 껀지 선택할 수 있다.


예를들면 커피를 들고 있는 손을 표현하고 싶다면 오른쪽 손을 찾고.



Configure..버튼을 누른다.




.




모델계층 정의 창

하이라키창에는 모델을 제외하고는 다른 오브젝트는 나오지 않는다.


Mapping / Muscles

Mapping

Mapping : 임포트한 모델 계층의 맵핑으로, 휴머노이드가 어떻게 배치 되어야 하는지를 정의한 것

Muscles

첫 번째는 그룹은 미리보기로 아바타의 움직임을 전체적으로 볼 수 있다.

두 번째 그룹은 신체 부위별로 그룹화 되어있어 근육의 운동 범위를 설정 할 수 있다.

세 번째

Twist 설정은 이웃한 연결 관절에  트위스트가 얼마나 영향을 주는지 결정한다.

Strech 설정은 다른 모델로 이 아바타를 리타겟팅했을 때 뼈대를 얼마나 늘일 수 있는지 결정한다.

Feet Spacing 발사이즈의 기본거리로 걷기 애니메이션등 영향을 줄 수 있다.

출처 : https://www.youtube.com/watch?time_continue=1&v=pbaOGZzth6g



 마우스로 캐릭터 방향 바꾸기



스크립트 작성

 Ray cameraRay = Camera.main.ScreenPointToRay(Input.mousePosition);

        Plane GroupPlane = new Plane(Vector3.up, Vector3.zero);

float rayLength;

        if(GroupPlane.Raycast(camearRay, out rayLength))

        {

            Vector3 pointTolook = camerRay.GetPoint(rayLength);

            transform.LookAt(new Vector3(pointTolook.x, transform.position.y, pointTolook.z));

        }


코드분석

Ray cameraRay = Camera.main.ScreenPointToRay(Input.mousePosition); : 마우스의 위치를 ScreenPointToRay를 이용해 카메라로 부터의 스크린의 점을 통해 레이를 반환한다.

Plane  GroupPlane = new Plane(Vector3.up, Vector.zero); : 월드 좌표로 하늘방향에 크기가 1인 단위 백터와 원점을 갖는다.

if(GroupPlane.Raycast(cameraRay, out rayLength) ; : 레이가 평면과 교차했는지 여부를 체크한다.

Vector3 pointTolook = camerRay.GetPoint(rayLenghth); : rayLenghth거리에 위치값을 반환한다.

transform.LookAt ~~: 위에서 구한 pointTolook 위치값을 캐릭터가 바라 보도록 한다.

Plane GroupPlane  = new Plane(Vector3.up, Vector.zero); 그냥 쓸수 없고 이렇게 하는 이유는??

유니티에서 Plane을 만드는 생성자는 3가지로 오버로딩 되어있다.(= 평면을 만들 수 있는 조건)

평면을 결정하는 최소 조건!

  1. 한 직선 위에 있지 않은 세 점
  2. 한 직선과 그 직선 위에 있지 않은 한 점
  3. 한 점에서 만나는 두 직선
  4. 서로 평행한 두 직선

첫 번째 사용

normal은 평면의 수직 방향을 얘기한다. 벡터는 방향과 크기를 가지고있지만 특정 위치를 나타내지는 않는다.
그래서 위치를 표현 할 수 있는 점이나, 거리를 인자로 받아야한다. 
1) public Plane(Vector3 inNormalVector3 inPoint);
노멀위치부터 한점의 위치,
2) public Plane(Vector3 inNormal, float d);
노멀위치로 부터 길이(d : 원점으로 부터의 거리)
3) public Plane(Vector3 a, Vector3 b, Vector3 c);
평면을 만드는 조건 :  한직선위에 있지 않는 세점

학습참고 평면조건 https://docs.unity3d.com/ScriptReference/Plane-ctor.html

2D Light 




어셋 다운로드 Light2D


레이어 설정

3가지 레이어를 추가해 준다.


2D Light System 설정 (GameObject -> Light 2D -> LightSystem )

추가한 레이어를 맞게 설정해주고 create 한다.

그러면 메인카메라 밑에 라이트카메라가 생성된다.


Main Camera 설정

Clear Flag -> Solid Color 변경

Projection -> Orthographic 변경(원근감과 공간감이 없는)

culling Mask -> 게임화면에 오브젝트가 안보이면 확인해준다.

(생성된 라이트카메라의 background Color값을 바꾸면 전역광 효과를 낼 수 있다.)



Light Source 생성

라이트를 표현 해 줄 라이트를 생성해준다.

Sprite. Color : 라이트소스의 컬러의 알파값이 빛의 세기가 됨

Shape : 포인트를 라인으로 바꾸면 선형 빛의 모양으로 바뀜


오브젝트 레이어 설정

이제 각 오브젝트마다 표현해 주려는 요소의 맞게 설정해 준다.

AmbientLight : 장면 전체에 존재하는 빛

LightObstacles : 장애물 빛을 받지 않는다.

LightSoucre : 빛을 표현해줌


      






Fade(In,Out)


(fadeIn 효과)


위 그림처럼 게임 시작 시 알파값을 조절해 fadeIn 효과를 연출할 수 있다.


1) Image 생성

Canvas -> UI -> Image를 생성한 뒤 Color

까맣게 만들어 준다.

2) 스크립트 생성

public class FadeIn : MonoBehaviour

{

    public float FadeTime = 2f; // Fade효과 재생시간

    Image fadeImg;

    float start;

    float end;

    float time = 0f;

    bool isPlaying = false;


    void Awake()

    {

        fadeImg = GetComponent<Image>();

        InStartFadeAnim();

    }

    public void OutStartFadeAnim()

    {

        if(isPlaying == true) //중복재생방지

        {

            return;

        }

start = 1f;

         end = 0f;

        StartCoroutine("fadeoutplay");    //코루틴 실행

    }

public void InStartFadeAnim()

    {

        if (isPlaying == true) //중복재생방지

        {

            return;

        }

        StartCoroutine("fadeIntanim");

    }

    IEnumerator fadeoutplay()

    {

        isPlaying = true;


        Color fadecolor = fadeImg.color;

        time = 0f;

        color.a = Mathf.Lerp(start, end, time);


            while (fadecolor .a > 0f)

            {

                time += Time.deltaTime / FadeTime;

                fadecolor .a = Mathf.Lerp(start, end, time);

                fadeImg.color = fadecolor ;

                yield return null;

            }

            isPlaying = false;

    }

코드분석

while(color.a > 0f) {    } :  이미지 색상의 알파값이 0으로 가까워 질 수록 투명해짐(이미지 색상이 약해짐?)

time += Time.deltaTime / FadeTime; : 지정한 시간만큼 효과를 주기 위해 1초를 나눠줌

fadecolor.a = Math.Lerp(start, end, time); : start와 end 중간값을 리턴

yield return null; :  다음 프레임까지 대기


Math.Lerp(start, end, time);

선형 보간을 할때 사용.

시작과 끝을 알고 정해진 시간동안 진행되야될 경우에 사용 
0일경우 시작값을, 1일 경우 끝 값을 반환하는데,

0.4일경우 시작값 40%,끝 값 60%값을 반환해서 자연스러운 연출이 가능하다.

(매 프레임 마다 증가하는 time값과 감소하는 알파값을 Debug.log를 찍어봤다)

Math.Lerp를 사용하면 time값에 따라 0에서 1로 갈수록 점점 큰 약한 알파값을 반환함으로서 자연스럽게 연출이 가능해진다.

FadeOut

같은 코드로 start값과 end값 그리고 while조건문 값만 수정하면 연출이 가능하다.



이 블로그에 더 자세히 나와있음 : http://ronniej.sfuh.tk/%EC%BD%94%EB%A3%A8%ED%8B%B4coroutine%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-fade-%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98-%EB%A7%8C%EB%93%A4%EA%B8%B0/

파티클 시스템(Particle System)








파티클 시스템은

씬 안에 다수의 작은 2D 이미지를 생성하고 애니메이션하여 액체, 구름 및 불 꽃 같은 유체 엔티티를 시물레이션 한다

3D게임에서 대부분의 캐릭터, 소품 및 풍경 요소는 Meshes로 표현되고, 2D는 Sprite를 이러한 용도로 사용한다.


형태가 표현이 가능한 Meshe와 Sprite


<3D> 


<2D>


Mesh와 Sprite는 형태가 뚜렷한 "솔리드"오브젝트를 그리는 가장 이상적인 방법이다.

하지만 Mesh와 Sprite로 표현하기 어려운 것들이 있다.


예를들어 모닥불에 피어 오르는 연기, 총구에 나오는 화염, 리얼하게 표현 되어야 할 마법스킬등 

정해진 물체에 그리는것처럼 형태가 없기 때문에 표현하기는 불가능한 것들이다.


이런것들은 파티클이라는 그래픽스방식을 이용해야 한다.

(그래픽스? : 실시간으로 이루어지는 유동적인 효과를 나타낼때 사용)


파티클 단어 자체가 작은 입자라는 뜻인데 이런 작은 입자(작은 메시와,sprite)가 모여 

큰 모양을 만들어 표현하기 어려운 것들을 보다 자연스럽게 표현한다고 생각하면 된다.



파티클 컴포넌트

Duration : 파티클이 실행되는 시간

Looping : 반복 여부

Start Delay : 파티클시스템이 기다리는 시간, 값이 높을 수록 늦게 시작

Start LifeTime : 파티클의 수명




방사 모듈<Emission Module>

시간의 경과에 따라 방사되는 파티클의 양이나 거리

Bursts : 파티클의 용출 횟수를 조절한다.


<기본>

<1개 추가함>




형태 모듈<Shape Module>

Shape : 파티클 형태를 선택할 수 있다.

<Edge형태>




충돌 모듈<Collision Forces>

파티클 충돌처리

World / Planes : World와 Planes 선택가능


<Planes>형태 

게임의 바닥을 Planes으로 지정했을 경우 cube를 무시하고 파티클이 재생된다.

<world>형태

cube등 주위 오브젝트를 감지하여 불꽃이 빚겨나간다.


Send Collision Messages : 충돌처리에 대한 메세지를 받을 수 있다.

   void OnParticleCollision(GameObject other)

    { 

        Debug.Log("파티클 충돌");

    }

파티클을 체크하고 충돌 메세지를 받을 오브젝트에 스크립트를 추가한다.



추가적인 내용은 유니티 도큐먼트를 참고

https://docs.unity3d.com/kr/2017.4/Manual/PartSysMainModule.html

https://docs.unity3d.com/kr/2017.4/Manual/class-ParticleSystem.html

FloatingText 


데미지값 출력해주기!




1) 프리팹제작

Canvas안에 UI -> Text로 만들어준다.


2) FloatingText 스크립트(프리팹에 추가할 스크립트)

    public void print(string Text)

    {

        FloatTextPrint.text =  string.Format(" {0}",Text);


}

private void Start()

{

moveSpeed = 5f; //위로 움직이는 속도값

destroyTime = 3f; //몇초 후 삭제 될건지

}

void Update()

        {

vector.Set(FloatTextPrint.transform.position.x, FloatTextPrint.transform.position.y 

             + (moveSpeed + Time.deltaTime), FloatTextPrint.transform.position.z);


FloatTextPrint.transform.position = vector;


destroyTime -= Time.deltaTime;


if(destroyTime <=0)

{  

Destroy(this.gameObject);

}


}

 vector.Set(FloatTextPrint.transform.position.x, FloatTextPrint.transform.position.y 

             + (moveSpeed + Time.deltaTime), FloatTextPrint.transform.position.z);

생성된 FloatTextPrint 위치를 Y값으로 증가 시킨다.



3) UI.Mgr 스크립트

public void SetFloating(GameObject vr, string bulletpower) //몬스터의 위치, 총알의 공격력을 받아옴

{

  

PrefabFloatingTXT = Resources.Load<GameObject>("prefab/FloatingTxt");

if(PrefabFloatingTXT == null)

{

Debug.Log("FloatingTxt Null");

}

                GameObject TXT = Instantiate(PrefabFloatingTXT);


Vector3 uiPosition = Camera.main.WorldToScreenPoint(vr.transform.position);


    TXT.transform.localPosition = uiPosition;

            TXT.transform.SetParent(canvas.gameObject.transform);

            Ftxt = TXT.gameObject.transform.GetComponent<FloatingText>();

    Ftxt.print(bulletpower);


}

Vector3 uiPosition = Camera.main.WorldToScreenPoint(vr.transform.position);

UI 오브젝트 이기 때문에 몬스터의 position값을 넣어도 화면에 보이지 않는다. 

Camer.main.WorldToScreenPoint를 월드좌표에서 스크린좌표로 값을 변환해준다.


Animator Sub-state Machine hierarchies



이렇게 복잡한 상태를 하위계층 구조로 나누어 관리할 수가 있다.


생성하기

마우스 오른쪽 클릭 하면 Sub-State Machine를 생성할 수 있다.

일반 상태와 다르게 팔각형 모양인것을 알 수 있다.


하위구조 만들기

이렇게 하위로 넣고 싶은 상태들을 선택 후 집어 넣어주면 된다.


상태 전환 지정하기


참고: https://www.youtube.com/watch?time_continue=1&v=lpekqN4_4xg

UI 체력바 만들기(GUI)



UI이미지 만들기

이미지 2개와 텍스트UI 1개를 생성한다. 


1) 체력이 깍일 때 밑에 보이는? 이미지

2) 실제 HP를 표시 할 이미지

3) 캐릭터의 HP를 텍스트로 표시할 UI오브젝트

(UI오브젝트들은  'Canvas'안에 넣어야 게임뷰에 보이기 때문에 canvas안에 생성해준다)


1) HP_glass는 별다른 설정없지만

2) HP_Image는 HP수치에 따라 왼쪽에서 오른쪽으로 길이가 줄어드는 효과를 내야한다.


HP 이미지 컴포넌트 설정

Source Image : UI이미지에 적용할 sprite이미지

Image Type -> Filled

Fill Metod -> Horizontal 수평으로 증감 효과 

Fill Origin -> Left 이미지가 채워지는 시작장소(체력이 달면 오른쪽에서 왼쪽으로 줄어듦)

Fill Amount : 1이면 이미지가 모두 채워지고 0으로 갈수록 줄어듦


소스코드

 private void Update()

 {

PlayerHPbar();

 }


public void PlayerHPbar( ) 

{

float HP = CharMgr.Instance._PlayerHp; //캐릭터 hp를 받아옴

hpbar.fillAmount = HP / 100f;

HpText.text = string.Format("HP {0}/100", HP);

}


hpbar.fillAmount HP/ 100f; : 현재 체력을 100으로 나누어 이미지 넓이를 표시한다.

HpText.text = stirng.Format("HP {0}/100",HP); : 텍스트로도 표시 하기 위한 UI text 


Blend Trees


사람이 뛸때 왼쪽방향으로 돌면서 뛸때(왼쪽으로 조금 숙인다 던지)와 오른쪽 방향으로 돌면서 뛸때 다르다.

유니티에서도 . 디테일한 애니메이션을 주기 위해 사용한다.



애니메이터 창에서 Create State -> From New Blend Tree를 생성한다.

이렇게 생성하면 모션으로서 단일한 애니메이션이 아니라 Blend Tree를 가지게 된다.


생성한 Blend Tree를 클릭해서 들어간다.

인스펙터 창에 Blend Type이 1D라는것은 일차원으로 파라미터(현재는 speed)로 각각의 모션을 얼만큼 재생할지 제어하는 것이다.


모션추가

Add Motition Field를 추가해서 Run 애니메이션을 추가한다.



Threshold : 온전히 이  애니메이션만을 사용하는 블랜드를 나타내는 파라미터 값이다.

ex) speed 값이 걷기가 5이고 달리기가 10이다. speed가 10이 되면 달리기 모션을 취하고 7.5가 되면 걷기와 달리기가  50:50으로 혼합되어 실행 된다.

Time Scale(시계모양) : 애니메이션의 속도를 정한다.

사람모양 : 애니메이션의 반전 여부를 지정한다.



Automate Thresholds를 체크하면 각 애니메이션 임계값 변경이 안된다.

Adjust Time Scale : 각 애니메이션의 속도를 균일하게 만들 수 있다.(루트 모션의 속도와 같아진다)


Blend Type

2D Simple Directional : 방향 파라미터에 기초하여 모션을 블렌딩 각 방향에 애니메이션이 하나 일때 사용한다. ex) 앞으로 걷기, 뒤로 걷기, 왼쪽으로 걷기 등

2D Freeform Directional : 같은 방향에 대해 두 개 이상의 애니메이션을 가질 수 있다.

ex) 앞으로 걷기와 앞으로 달리기 등

2D Freeform Cartesian : 파라미터가 방향과 무관할 때 사용  ex) 속도, 각속


x축을 나타낼 애니메이터 파라미터

y축을 나타낼 애니메이터 파라미터를 각각 생성해서 할당해준다.




빨간점으로 임계값을 주고 밑에 미리보기를 통해 어떻게 반응하는지 알 수 있다.

(빨간점을 마우스로 끌어서 움직이면 방향에 맞는 에니메이션을 취한다)


출처 : https://www.youtube.com/watch?v=HeHvlEYpRbM



코루틴 Coroutine 


유니티 Update( )함수는 게임의 지속적인 변화를 주거나 게임의 진행되는 매순간 체크(사용자의 입력 등)해야할 경우 사용하게 된다.

하지만 특정행동을 5초 동안 취해야 할 경우,  초당60~80(프레임)번 호출 되는 Update함수 안에서는 제어 하기가 힘들다. 

그리고 플레이하는 디바이스마다 각각 다른 프레임이 나오기 때문에 구현 자체가 더더욱 힘들다.


그래서 C#과 유니티에서는 코루틴이라는 것을 제공한다.

유니티에서 사용할 것이기 때문에 유니티 코루틴을 알아보도록 하자


사용법 

StartCoroutine

void 함수명( ) //Update안에 있는

{

     StartCoroutine( 코루틴 함수 이름( ) );  

}

IEnumerator

    public IEnumerator 코루틴 함수 이름( ) //코루틴 사용

    {

            Debug.log(코루틴 대기);   

 yield return new WaitForFixedUpdate();

            Debug.log(코루틴 완료);

    }

 코드분석

우선 StartCoroutine( ) 괄호안에 코루틴으로 IEnumerator 개채명, 지정한 함수이름을 써준다.

yield라는 것은 반복자의 IEnumerator객체에 값을 전달하거나, 반복의 종료를 알리기 위해 사용한다.

WaitForFixedUpdate( );  다음 FixedUpdate 물리 프레임 전까지 대기

(코루틴 예외 :  함수안에서 만 사용, 알수 없는 이름의 함수(람다)에서는 에러)

코루틴용 데이터엔진이 수행하는 기능
yield return null다음 프레임까지 대기
yield return new WaitForSeconds(float)지정된 초 만큼 대기
yield return new WaitForFixedUpdate()다음 물리 프레임까지 대기
yield return new WaitForEndOfFrame()모든 렌더링작업이 끝날 때까지 대기
yield return StartCoRoutine(string)다른 코루틴이 끝날 때까지 대기
yield return new WWW(string)웹 통신 작업이 끝날 때까지 대기
yield return new AsyncOperation

비동기 작업이 끝날 때까지 대기




참고 : https://docs.unity3d.com/kr/530/Manual/Coroutines.html





TrailRenderer





Description

trail 렌더러는 오브젝트 뒤에서 씬에서 오브젝트가 움직일 때, 해당 오브젝트 뒤를 따라 움직이도록 할 때에 사용됩니다.


TrailRenderer은 유니티에서 제공하는 기본컴포넌트이다.


TrailRenderer 컴포넌트 추가하기

우선 발사체(오브젝트)에 TrailRenderer컴포넌트를 추가한다.


테스트1

총알을 발사해보면 발사체가 이동했던 경로를 그린다.



Material생성

총알처럼 효과를 주기 위해


material을 생성하고 이미지를 Albedo에 넣어준다.

Shader모드를 변경한다. 그래야 더 발사체 효과같기 때문에 변경해준다.


생성한 Material 추가

그런 다음 TrailRenderer컴포넌트에 효과이미지를 추가한 메테리얼을 넣어준다.

Materials 트레일을 렌더링하는 데 사용하는 Material의 배열

size: 배열의 요소 수

Time : 트레일의 길이(단위 초)


Width

곡선의 기울기에 따라 트레일을 넓이를 조절 할 수있다.

Width가 굵을 수록 실제 보여지는 발사체효과도 굵어진다고 보면 된다.

 

그래프에서 왼쪽부분이 발사체 종료부분(=끝나는 부분)

그래프에서 오른쪽부분이 발사체 시작부분(=발사부분에 가까운 부분)


참고: https://docs.unity3d.com/kr/530/Manual/class-TrailRenderer.html

Vector3.Distance


두 위치 사이의 거리를 반환한다.


일정거리 안에 플레이어가 접근하면 반응하도록 하기위해 Vector3.Distance를 이용한다.


Vector3.Distance() 사용법

float distance = Vector3.Distance(코인의 위치, 플레이어의 위치);


적용하기

Coin 스크립트

public void CoinMove()

{

dir= (playerpos.position - transform.position).normalized;

acceleration= 0.2f;

velocity = (velocity + acceleration* Time.deltaTime);

float distance = Vector3.Distance(playerpos.position, transform.position);

if (distance <= 3.0f)

{

transform.position = new Vector3(transform.position.x + (dir.x * velocity),

   transform.position.y,

transform.position.z+(dir.z * velocity));

}

else

{

velocity = 0.0f;

}

}

코드해석

direction = (playerpos.position - transform.position).normalized; : 코인위치와 플레이어의 위치를 뺀 값을 단위 백터화 한다.

acceleration= 0.2f;  : 가속도

velocity = (velocity + acceleration* Time.deltaTime); :  한 프레임으로 가속도 계산


간단하게 구현하기에는 Distance를 사용하면 좋긴하지만, 좀 더 최적화된 퍼포먼스를 위해서는 밑에 있는 것들을 이용하는것을 추천한다.ㅎㅎ

1) Vector3.Distance

2) Vector3.magnitude

3) Vector3.sqrMagnitude


참고 : https://docs.unity3d.com/kr/530/ScriptReference/Vector3.Distance.html


Animator Controller Layers

애니메이터 컨트롤러의 상태 머신들은 레이어로 나눌 수 있다.

예를들어 다른 신체 부위의 움직임을 나타낼때 사용 된다.


IK Pass :  Inverse Kinematics 를 체크 하게 되면 그 레이어에 맞는 인덱스와 함께 OnAnimatorIk 함수가 호출된다.

Base Layer는 0이고 그다음에 추가되는 레이어들은 1부터 증가하는 자연수 인덱스를 가지게된다.


추가 되는 레이어들은 이전 레이어에 반응하는 방식과 관련된 추가 옵션이 있다.

Weight : 레이어가 가지는 가중치 값이 ‘0’이면 애니메이션의 전혀 영향을 주지 않는다.

(애니메이션 우선순위)

Mask : 애니메이션에서 신체의 어떤 부분만 제어할 것인지 AvatorMask 를 등록해 정할 수 있다.

Blending :1) Override : 가중치 낮게 지정된 레이어의 애니메이션을 대체한다.

       2) Additive : 기존 애니메이션의 혼합되거나 더해지는 연산을 취한다.



ex) 캐릭터가 숨을 가쁘게 몰아쉬면서 점점 피곤해지는 레이어를 생각해보자

이 예에서, 숨을 가쁘게 쉬는 애니메이션이 더 낮은 레이어에 할당된 애니메이션에 추가된다. 레이어의 가중치를 높이면 피곤한 애니메이션이 더 뚜렷하게 보일 것이다.


Sync : 동기화 할 레이어를 선택할 수 있다. 그러면 선택한 레이어의 애니메이션과 동일한 상태 머신이 생성되지만 움직임이 없다. 이것은 부상당한 행동 같은 것을 표현 할 때

캐릭터가 건강한 상태와 체력을 잃어가는 과정에 이들을 혼합할 수 있다.


Timing : 동기화된 레이어에 있는 각 애니메이션의 지속 시간을 가중치를 기준으로 결정할 수 있다.


기본있는 BaseLayer에는 Walk 애니메이션을 추가한다.


Walk

(팔과 다리를 움직이게 했다.)


handWave 레이어


wavehand


추가 한 handWave 레이어 설정 값

Weight값은 0.5로 주고

Bleding은 Override로 설정했다.


파라미터 hand를 실행할때 손을 흔드는 모습을 볼 수 있다.

그런데 좌우로만 움직이는게 아니라 앞뒤로도 움직인다.

앞뒤로 움직이는 Base Layer와 좌우로 움직이는 Wavehand 레이어가 섞인 모습을 볼 수 있다.



다음은 Avatar를 통해 걷는 애니메이션과 인사하는 애니메이션 상,하체 따로 진행시키는 방법을 알아보자

The Animation View






하이라키창에서 움직일 오브젝트를 선택하고



애니메이션 창에서  AddProperty를 필요한 속성을 추가한다.



왼쪽에는 속성과 값을 설정할 수있고 오른쪽에는 원하는 시간대에 설정값을 넣어 애니메이션 만들 수 있다.



첫 번째는 원하는 시간대로 이동

두 번째는 프레임 설정(숫자가 낮으면 느려짐)



  

녹화 버튼을 누르면 빨갛게 변한다. 추가 하고싶은 요소를 추가한다.

현재는 위치와 회전값만 프로퍼티로 추가 했기 때문에 Position과 Rotation만 색이 변한 걸 볼 수 있다.

(녹화 버튼을 누른 상태에 값을 주어야함)


게임신에서 움직여도 되지만 정확한 값을 넣기 위해 인스펙창을 통해 설정한다.






The Animator Component




Controlle : 유니티가 생성한 에셋으로 하나 이상의 상태 머신이 포함되어있다.

상태머신? 씬이 실행될 때 어느 애니메이션이 재생될 것인지 결정한다.

Avater : 3D휴머노이드를 임포트할 때 유니티가 생성하는 에셋이다.

모델과 애니메이터를 이어주는 접착제? 같은 역할이라고 생각하면 된다.


Apply RootMotion : 애니메이션이 게임 오브젝트의 트랜스폼에 영향을 줄 수 있는지 여부를 결정한다.

UnCheck하게 되면 애니메이션은 작동하지만 이동하지는 않는다.



Culling Mode : 렌더링 상태가 아닐때 애니메이션이 재생될 것인지 아닌지를 정할 수 있다.

(비용절약) 화면에 보이지 않으면 애니메이션은 작동하지 않는다.



The Animator Controller


Parameters옆의 + 아이콘을 클릭하면

Float, Int, Bool,Trigger

애니메이션의 퀄리티나 조건을 나타낸다.


Animator Window 살펴보기

애니메이터 창에서 애니메이터컨트롤러 상태를 설정할 수가 있다.


처음 생성된 상태는 기본상태(주황색)로 된다.

(기본을 변경하고 싶으면 원하는 상태에서 마우스 오른쪽 -> Set As Default)


상태 편집은 클릭하면 인스펙터창에서 할 수 있다.

맨위 는 이름과 태그로 코드로 참조 가능하며 어떤 상태에 있는 동안 원하는 동작을 수행하게 할 수 있다.


speed  : 일반 애니메이션 속도의 비해 얼마나 빠르게 재생되는지를 참조

Mirror : 애니메이션의 좌우가 반전

Foot IK : 체크하면 애니메이션이 발 미끄러짐이 감소하거나 제거 된다.

Transitions : 다른 상태로 트랜지션할 수있는 상태를 표시,설정


트랜지션 상태 (흰 화살표)

마우스 오른쪽 -> Make Transitions으로 설정



style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;">트랜지션 상태 설정하기

디버그용

Mute : 현재 상태에 연결된 트랜지션을 비활성화 한다.

Solo : 같은 상태에 연결된 모든 트랜지션을 고려하여 그 중에서 Solo가 표기 되지 않은 모든 트랜지션을 비활성화한다.

Conditions : 설정한 파라미터(bool,Int,flat)를 통해 다른 상태로 이동하는 조건을 설정 할 수 있다.


예를 들어 일정 속도값이 생기면 걷는 동작을 취하도록 하려면

void Update()

{

float h = Input.GetAxis("Horizontal");

anim.SetFloat("speed", v);

}

스크립트 작성후 컴포넌트 해주고


애니메이터윈도우에

파라미터 speed를 생성하고


Idle -> Walk

(스크립트로 제어할것이기 때문에 Has Exit Time 언체크!)

speed값이 0.1이 넘으면 걷기 애니메이션이 작동하도록 설정해준다.  



Walk -> Idle

speed값이 0이면 다시 Idle 상태로 돌아 가기 위해 Less는 ‘0’으로 설정한다.



니메이터 윈도우에 Base Layer를 보면 현재 어떤 상태인지 볼 수있다.

Parameters에서는 키보드 위 방향을 누르면 speed 값이 올라가는 걸 볼 수 있다.

gPlayerPrefs(플레이어프렙스)



키-벨류 방식으로 유니티안에서 간단하게 저장하고 데이터를 불러올수 있다.

getter을 통해 저장하고 setter을 통해 불러온다. 저장가능 용량은 1MB이다.

간단하게 저장할 때 사용한다.(보안x)


사용법 먼저 알아보자


저장하기

PlayerPrefs.SetInt("score", getcoin);

매개 변수안에 key,value를 넣면 된다. 

Setfloat,Setint,Setstring 3가지가있다. 저장하려는 형식의 맞게 사용하면 된다

"score"라는 키값으로  벨류인 getcoin의 값을 저장하고 있다.

(아이템을 먹으면 1씩 증가하는 getcoin)



불러오기 

score = PlayerPrefs.GetInt("score");

불러 올때는 Getter을 사용하는데 score라는 키값으로 getcoin을 불러오는것이다.



적용하기

void Start() {

score = PlayerPrefs.GetInt("score");

getCoin();

}


void OnApplicationQuit() //종료 콜백함수

{

PlayerPrefs.SetInt("score", getcoin);

}

이런식으로 시작할 때 저장한 값을 불러오고 종료할때 값을 저장했다.


삭제하기

유니티를 꺼도 삭제되지 않는다 그렇기 때문에 키워드를 통해 지워줘야한다.

PlayerPrefs.DeleteAll

PlayerPrefs.DeleteKey("키이름")


https://docs.unity3d.com/kr/530/ScriptReference/PlayerPrefs.html




인터페이스 interface


 클래스를 만들 때 사용하는 규약?



기본구조

interface [인터페이스 이름]

{

}



생성위치

인터페이스는 클래스와 동급의 카테고리이다. 그렇기 때문에 클래스를 생성하는 위치라면 어디든지 만들 수 있다.



예제를 보면서 이해하자



1) 인터페이스 생성

interface IBasic

{

int TestInstanceMethod( );

int TestProperty{  get; set ;}

}

메서드에 내부 구현을 입력할 수 없다.

속성에도 마찬가지로 내부 구현을 입력할 수 없다.


2) 인터페이스 상속

class TestClass : IBasic

{


}

static void Main(string[] args)

{

}


3) 인터페이스 구현

'Ctrl' + '.' 누르면 자동완성 

class TestClass : IBasic

{

public int TestInstanceMethod( )

{

throw new NotImplementedException( );

}

public int TestProperty

{

get

{

throw new NotImplementedException( );

}

set

{

throw new NotImplementedException( );

}

}

}

클래스에서 이후에 구현해준다.



4) 인터페이스 다중 상속

class Program

{


class Parent{ }

class Child : Parent, IDisposable, IComparable

{

public void Dispose( )

{

throw new NotImplementedException( );

}


public int CompareTo(object obj)

{

throw new NotImplementedException( );

}

}

//한 개의 클래스와 두 개의 인터페이스를 상속받는다.


이렇게 상속되면 다음과 같이 세종류로 자료형 변환이 가능해진다.

Child child = new Child( );

Parent ChildAsParent = new Child( );

IDisposable childAsDisposable = new Child( );

IComparable childAsComparable = new Child( );






Camera.ScreenPointToRay


카메라로 부터의 스크린의 점을 통해 레이를 반환합니다.

스크린공간은 픽셀로 정의되며.

픽셀단위

1번 : 왼쪽 하단의 화면이 (0,0)

2번: 오른쪽 상단이 (pixelWidth,pixelHeight)


if (Input.GetButtonDown("0"))

{


Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

RaycastHit hit;

if (Physics.Raycast(ray, out hit, Mathf.Infinity))

{

print("raycast hit!");

Debug.DrawRay(ray.origin, ray.direction * 20, Color.red, 5f);

Debug.Log(hit.point);

}

}




Debug.Ray를 통해 어느부분을 클릭했는지 씬뷰에서 확인이 가능하다.

hit.point를 통해 스크린상 좌표값을 얻어 올 수 있다.

Raycast


Physics.Raycast

어떤 콜라이더와 레이가 충돌했으면 true, 아니면 false.


Debug.DrawRay 체크 : 거리와 위치를 디버깅 할 수 있다.(유니티 씬장면에서만 나옴)

Debug.DrawRay(transform.position, transform.forward * 8, Color.red);


(위 scene, 아래 game)


충돌한 오브젝트 반환하기

       RaycastHit hit;

       if (Physics.Raycast(transform.position, transform.forward, out hit, 8))

       { //광선을 쏴서 충돌한 게임 오브젝트를 레이캐스트 변수에 저장처리한다.

           Debug.Log(hit.collider.gameObject.name);

       } //광선이 충동한 게임 오브젝트의 이름을 콘솔창에 출력한다.

콘솔창을 보면 ray에 닿은 오브젝트를 확인 할 수 있다.



중첩되는 오브젝트일경우 Physics.RaycastAll

RaycastHit[] hits; //Raycast데이터 저장용 배열 설정

       hits = Physics.RaycastAll(transform.position, transform.forward, 8.0f);


       for(int i=0; i<hits.Length; i++)

       {

           RaycastHit hit = hits[i];

           Debug.Log(hit.collider.gameObject.name);

       }


Raycast데이터 저장용 배열을 사용한다.

정해진 길이만큼 통과한 오브젝트들의 이름을 반환한다.

Random.Range 


컴퓨터 프로그래밍 언어에서는 난수라고도 한다.

사실 컴퓨터는 랜덤이란게 있을 수 없기 때문에 일정 범위에 숫자값을 집어넣고

그중에 윈도우나 유니티상에서 시간값을 가지고 숫자를 뽑아내는 것을 난수라고 한다.


유니티에서는 Random.Range가 존재한다.


사용법

Random.Range(min,max ) //min [inclusive]과 max [inclusive]사이의 랜덤 float 수를 반환


주의할점을 값이 int일 경우 max값은[exclusive] 나오지 않는다. 


public class ItemDrop : MonoBehaviour {


    public GameObject Item;


void Start () {

        InvokeRepeating("Spawnitem", 1, 1); //1초에 1번씩 Spawnitem()를 호출한다.

    }


    void Spawnitem()

    {

        

        float randomX = Random.Range(-23f, 19f); 

        if(true)

        {

            Debug.Log("생성");

            GameObject item = (GameObject)Instantiate(Item, new Vector3(randomX, 1.1f,0f), Quaternion.identity);

        }

    }

}


InvokeRepeating : time 초에 /methodName/메서드를 호출한 후, 매 /repeatRate/초 마다 반복적으로 호출

Instantiate(Item, new Vector3(randomX, 1.1f,0f), Quaternion.identity); Item이라는 프리팹을 생성한다.

Quaternion.identity : 회전없음



생성될 아이템 프리팹을 설정한다.



아이템이 랜덤으로 생성되는것을 볼 수 있다.

위 코드는 X위치만 랜덤 생성해 주었지만 Z값도 랜덤 설정하면 사방에서 스폰 될 것이다.



카메라 시점 바꾸기



Left Shift 버튼을 눌렀을 때 시점을 바꿔준다.

시점이 3개이기 때문에 카메라도 3개가 필요하다

메인카메라 프로젝트 생성시 존재하는 카메라


시점카메라(1인칭)

캐릭터가 움직일때 같이 따라 와야하기 때문에 카메라는 캐릭터 오브젝트 자식으로 넣어둔다.




캐릭터 정면에서 바라보는 카메라


메인카메라를 제외하고 모두 비활성화 해준다.

PlayerController스크립트


public class PlayerController : MonoBehaviour

{

public Camera[] arrCam; //카메라 요소들을 추가한다.

int nCamCount = 3;

int nNowCam = 0;

// 필드값 세팅

Camera[] arrCam : 카메라 활성화/비활성화를 해주기 위한 배열

nNowCam : 현재 활성화 된 카메라의 번호

nCamCount : 카메라 갯수


void Update ()

{

if( Input.GetButtonUp("Fire3"))

{

++nNowCam;


if (nNowCam >= nCamCount)

{

nNowCam = 0;

}


for ( int i=0; i<arrCam.Length; ++i )

{

arrCam[i].enabled = (i == nNowCam);

}

}

}

if( Input.GetButtonUp("Fire3"))...

버튼이 눌리면 nNowCam값을 1증가 시킨다.

if (nNowCam >= nCamCount) ...

세 번째 카메라까지 순서대로 보여주고 다시 첫 번째 카메라로 돌아가게 해주는 코드이다.

for ( int i=0; i<arrCam.Length; ++i )...

NowCam(0번카메라)이면 enabled=true 해주고 나머지 1,2번은 false해준다.

또 NowCam(1번카메라)값이 1이면 카메라 컴포넌트를 enabled=true 해주고 나머지 0,2번은 false해준다.

2번 카메라 일때도 마찬가지 나머지 요소는 enabled를 false시킴


*Fire3가 무엇이냐? InputManager



오브젝트 찾기



이름으로 찾기



이름으로 오브젝트를 찾는법

GameObject.Find("찾고자 하는 오브젝 이름");


그런데 오브젝트가 비활성화 되어있다면 이방법이 불가능합니다.

부모오브젝트를 찾은 다음에 자식오브젝트를 찾아야합니다.


비활성화 된 오브젝트 찾는법


GameObject.Find("부모 오브젝트 이름").transform.Find("자식 오브젝트이름").gameObject;



인덱서로 찾는법(첫 번째가 0) 

GameObject.Find("Panel").transform.GetChild(0).gameObject;



태그로 찾기


GameObject.FindWithTag("태그명");



위 방법들은 유니티 하이라이키상에 있는 오브젝트를 찾을 수 있다.

사실 게임로직에서는 위에 있는 Find로 특정 오브젝트를 찾는 경우는 드물다. 특히 실시간으로 계속해서 찾는 경우는 더더욱 쓰지않는다. 하이라키상에서 나와있는 모든 오브젝트를 모두 검사해야하기 때문에 그만큼 시간도 오래걸리고 비용도 들기 때문이다. 

AddListener 이용해서 함수불러오기


버튼 프리팹 동적 생성하기(http://funfunhanblog.tistory.com/15)

동적으로 생성한 버튼을 위 그림 처럼 클릭 시 각각 다른 함수를 실행할 수 있도록 하자





유니티에는 UI버튼 기능중에는 OnClick( )이 있다. 일반적으로는 버튼을 생성하면 인스펙터 창을 통해 적용하면 되는데

동적으로 생성된 버튼에는 당연히 인스펙터창에서 실행할 함수를 적용할 수 가 없다.


그러면 스크립트에서 버튼이 생성됨과 동시에 각 버튼마다 OnClick( ) 기능을 주어야한다.



1) 실행 시킬 메서드 생성(스크립트 생성)

먼저 버튼이 눌리면 실행 할 함수들을 작성해준다.

public class BtnAnimal : MonoBehaviour {


        Debug.Log(" 로그 로그 ");

    }

    public void OnClickRed()

    {

        Debug.Log("레드 클릭");

    }

    public void OnClickDog()

    {

        Debug.Log("강아지 클릭");

    }

    public void OnClickCow()

    {

        Debug.Log("소 클릭");

    }

    public void OnClickOct()

    {

        Debug.Log("문어 클릭");

    }

}

 

2) 함수불러오기 AddListener

저번 포스팅에서 만들어 줬던 동적생성 메소드에서 작성해준다.

public void CreateBtns()

    {

////생략 

      for (int nn = 0; nn < 4; nn++)

        {

if (mbtnPrefab == null)

{

Debug.Log("널값");

mbtnPrefab = Resources.Load<GameObject>("BtnAnimal");

}

GameObject button = (GameObject)Instantiate(mbtnPrefab);

                            mmgr.Add(button); //리스트에 추가


////생략


            Button btnMthod = button.GetComponent<Button>();

            BtnAnimal clickbtn = btnMthod.GetComponent<BtnAnimal>();

            if (nn == 0)

{

btnMthod.onClick.AddListener(()=> { clickbtn.OnClickRed(); }); 

}

else if(nn==1)

{

btnMthod.onClick.AddListener(() => { clickbtn.OnClickDog(); }); 

}

else if (nn == 2)

{

btnMthod.onClick.AddListener(() => { clickbtn.OnClickCow(); }); 

}

else if (nn == 3)

{

btnMthod.onClick.AddListener(() => { clickbtn.OnClickOct(); }); 

}

(명시적으로 표현하기 쉬운 if else 사용)

AddListener을 이용하여 BtnAnimal에 있는 함수를 불러오고 있다.


*AddListener : 함수를 불러온다. 콜백






3D 물리 Physics #1






Adding Physics Forces



마우스를 클릭하면 forward방향으로 물리적 힘을 가해지도록 해보겠습니다.


private void OnMouseDown()

{

Debug.Log("클릭");

Rigidbody Rigidoor = DOOR.GetComponent<Rigidbody>();

Rigidoor.AddForce(transform.forward * 1500);

}


Rigidoor.AddForce(transform.forward * 1500.ForceMode.);

ForceMode. 뒤에 설정값을 다르게 줄수도 있습니다.

ForceMode.Force : 질량의 영향값을 받아 작동, 연속적인 힘을 가하는 경우

ForceMode.Accelration 질량의 영향값 받지 않고 작동, 가속력

ForceMode.Impulse : 질량의 의해서 순간적으로 변함

ForceMode.VelocityChange : 질량의 영향받지 않고 짧은 순간에 적용

예상했던 장면이 나오지 않는다면 ForceMode를 바꿔보는것도 좋을것 같습니다.



private void OnTriggerStay(Collider other)

{

Rigidbody trigger = other.GetComponent<Rigidbody>();

trigger.AddForce(Vector3.up * 20f,ForceMode.Acceleration);

}


OnTriggerStay를 통해 이런 장면도 연출이 가능하겠죠?? 




Adding Physics Troque

이번에는 축을 중심으로 회전을 주겠습니다.

private void OnMouseDown()

{


Rigidbody RigObj = obj.GetComponent<Rigidbody>();

RigObj.AddTorque(transform.up * 1500);

RigObj.AddTorque(transform.right *2000);

}


RigObj.AddTorque(0, 10, 0); 이렇게도 사용이 가능합니다.

Forces와 마찬가지로 ForceMode 설정이 가능합니다.




#2에서는 픽스조인트,스프링조인트,피직스메티리얼에 대해서 알아보겠습니다








Character Controller


캐릭터 컨트롤러(컴포넌트)

캐릭터 컨트롤러(CharacterController)는 리지드바디(rigidbody)를 다루지 않고,

충돌에 의한 움직임을 다루기 쉽도록 해준다.


캐릭터 컨트롤러는 힘(관성, 중력 등)에 의해 영향을 받지 않고, Move함수가 호출되었을 때만 움직이게 됩니다.

충돌에 의해 힘이 가해졌을 때에만 움직임을 수행한다.


Slope Limit : 경사의 한계를 도의 단위로 나타낸다.

stepOffset : 캐릭터 컨트롤러의 단위의 스텝 오프셋값을 나타낸다(설정 값보다 낮은 높이의 계단만 오를 수 있다.)

Skin Width:  미터단위의 오프셋 설정값보다 낮은 계단만 오를 수 있다.

Center : 플레이의 무게중심

Radius : 캡슐의 반지름

Height : 캐릭터의 캡슐의 높이




CharacterController가 사용할 수 있는 기능과 설정



void Start ()

{

m_Ctrl = GetComponent<CharacterController>(); //컴포넌트에 접근하기 위해 m_Ctrl 필드명을 설정해준다.


}

void Update()

{

if( m_Ctrl.isGrounded == true ) //캐릭터가 땅에 위치하면

{

float fRot = fRotSpeed * Time.deltaTime;

float hor = Input.GetAxis("Horizontal");

float ver = Input.GetAxis("Vertical");

transform.Rotate(Vector3.up * hor * fRot); //Rotate(회전할 기준 좌표 축 * 변위값 * 회전속도)


moveDir = new Vector3(0, 0, ver * fMoveSpeed); //왼쪽과 오른쪽 키보드입력은 이동이 아닌 방향을 바꿈

//캐릭터는 앞으로만 움직이기 때문에 Vertical만 Vector값을 설정한다.

moveDir = transform.TransformDirection(moveDir); //로컬 좌표계 -> 월드 좌표계

if( Input.GetButton("Jump") == true )

{

moveDir.y = fJumpSpeed; //점프 버튼(스페이스바) 입력시 y축으로 힘을 준다.

}

}

}




버튼(프리펩,Prefab) 동적생성


   

BtnB를 클릭하면 그림 처럼 각각 다른 button 4개가 생성되게 만들겠습니다.


1)Panel 세팅



먼저 UI Panel 아래에 BtnGroupWindow를 만들어 줍니다.

button 4개 위치를  BtnGroupWindow 버튼 관리와 위치를 잡아주기 위해서입니다.


2)프리팹(견본생성)


동적으로 프리팹을 만들기 위해서는 먼저 복사?가 될 프리팹을 1개 만들어 주어야합니다.

GameObject -> UI -> Button

Button을 생성한 다음

Hierarchy창에서 Project창으로 드래그로 하면 프리팹이 만들어집니다.



3)BtnGroupWindow스크립트 작성 (BtnGroupWindow 오브젝트에 추가합니다.)

  

public class BtnGroupWindow : MonoBehaviour {

GameObject mbtnPrefab;

   public Transform panelPos;

mbtnPrefab이란 이름으로 프리팹으로 만들어 주기 위한 세팅입니다.


   string[] mstrImageName = new string[]

   {

        "bonbon_cha0025", "bonbon_cha0021"

                       ,"bonbon_cha0020", "bonbon_cha0014"

   };


버튼 4개가 모두 다른 이미지이기 때문에, 이미지를 바꿔 주기위해 배열을 세팅해줍니다.

이미지이름을 문자열로 넣어줍니다.


   void Start () {


mbtnPrefab = Resources.Load<GameObject> ("BtnAnimal");


if (mbtnPrefab ==null)

       { Debug.Log("mbtnpreprefab==null"); }


   }

mbtnPrefab에 아까 만들어 놓은 BtnAnimal 프리팹을 로드해서 넣어줍니다.


   public void CreateBtns()

   {

       for (int nn = 0; nn < 4; nn++)

       {

           

           GameObject button = Instantiate(mbtnPrefab);

//Instantiate(생성할 물체);

           RectTransform btnpos = button.GetComponent<RectTransform>();

//RectTransform을 잡기 위한 코드

button.transform.position = gameObject.transform.position;

//버튼프리팹의 최초 생성 postion값은 부모인 BtnGroup으로 맞춰줍니다.

//텍스처 바꾸기

Image image = button.GetComponent<Image>();

//생성할 버튼 Image 컴포넌트에 접근

Sprite btnsprite = Resources.Load<Sprite>(mstrImageName[nn]);

//nn이 0부터 3까지 도는 동안 각각에 맞는 이미지 sprite을 바꿔줍니다.

image.sprite = btnsprite;


btnpos.SetParent(gameObject.transform);

//SetParent 부모오브젝트 설정 여기서는 부모의 transform을 받기 위함입니다.

btnpos.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left,(20*nn), 36);

                       //왼쪽 모서리로 부터,얼마만큼 떨어져서 ,버튼의 크기

//버튼의 크기는 만들어 놓은 프리팹의 크기로 맞춰주는게 좋습니다.

       }

}

+원하는 위치에 생성이 안된다면 Panel과 BtnGroupWindow의 Rect Transform의 값을 설정해줍니다.


캐릭터 점프구현


이번에도 역시나 Roll a Ball을 활용한다.


캐릭터 점프는 실제 위치값을 움직이는 Translate보다는 

물리적 힘을 위로 주어서 튀어 오르는듯한 느낌을 주는 AddForce를 이용하는게 좋아보인다.


먼저 소스코드를 살펴보자


void Start( )

{       

        rb = GetComponent<Rigidbody>();

        jumpCount = 1; //점프 가능횟수

        isGround = true; //땅에 있을때

}

jumpCount는 1로 초기화 해준다.

isGround는 true로 땅에 있을때 캐릭터가 점프를 할 수 있는 환경이라고 판단하기 위해 선언 한 것이다.


void FixedUpdate ()

{        

        if (isGround) 

        {

            jumpCount = 1;

            if (Input.GetKeyDown("space")) //점프

           { 

                if (jumpCount==1){             

                    rb.AddForce(0, 300f, 0); //점프

                    isGround = false;

                    jumpCount = 0;

                }

           }

        }

    }

update문에서는 우선 플에이어가 있는 위치가 땅바닥인지 체크를 해준다.

땅바닥이라면 jumpCount값을 1로 만들어준다.

그리고 스페이스바가 눌리면 다시 jumpCount가 1인지 체크를 한다. 

두 if문이 모두 맞다면 플레이어의 rigidbody에 y값에 힘을 주어 캐릭터가 위로 튀어 오르게 만들어준다.

점프를 하고 있는 중에는

플레이어의 위치가 땅바닥이 아니기 때문에 isGround는 false로 설정하고

점프를 한번 했기 때문에 다시 jumCount는 0으로 만들어준다.(다시 점프를 할수 없게)

    private void OnCollisionEnter(Collision col)

    {

        if (col.gameObject.tag == "ground")

        {

            isGround = true;    //Ground에 닿으면 isGround는 true

            jumpCount = 1;          //Ground에 닿으면 점프횟수가 1로 초기화됨

        }

    }

OnCollision물체가 플레이어에게 닿으면 아래 코드들이 활성화된다.

플레이어에게 Collision(여기서는 땅바닥)이 닿게 되면 

jumCount를 1로 만들어준다. 


여기서 알아야 할 것은 

JumCount와 플레이의 위치 isGround를 체크 하지 않고 스페이스바를 여러번 누르게 되면 

계속해서 하늘로 치솟아 무한 점프가 될것이다 

그것을 방지하기 위해 체크한다. (캐릭터가 땅에 도착해야 다시 점프를 할 수 있게)

그리고 OnTrigger이 아닌 OnCllision을 사용했다는 점도 파악해야한다.


+ Recent posts