Generics 



일반적으로 클래스를 생성할 경우 각 데이터마다 타입을 지정해준다. 다른 데이터형을 사용하게 될 경우가 있는데, 강제 형변환은 좋지않다고 한다. 그래서~ 제네릭을 사용한다.


제네릭은 박싱/언박싱(형변환)이 일어나지 않도록 타입을 미리 지정하는 방식이다.

클래스,인터페이스, 메서드등에서도 구현 가능하다.


참조 타입 --> 힙 (Heap)

값 타입    --> 스택(Stack)

두 메모리 영역을 왔다갔다는 좋지않음


많이 사용했던 컬렉션들에서 이미 제네릭을 사용하고 있었다.


<출처 :  https://m.blog.naver.com/rocabilly/140133375098 >



Generics에서는 int, float, double같은 데이터 요소 타입을 확정하지 않고 이 데이터 타입 자체를 파라미터로 받아들이도록 클래스를 정의한다. 


형태

class 클래명 <T>

{

T 변수명;

}


public 메소드명 (T 변수명)

{

T 내부 변수명;

}

}


테스트 예제 코드

<결과>

이렇게 다른타입도 제네릭을 통해 원하는 결과를 도출 할 수 있다.




Generics 제약 Where


타입 파라미터가 값형식인지 참조 타입인지, 사용자가 정의한 베이스 클래스로부터 파생된 타입인지, 

어떤 인터페이스를 구현한 타입인지 등등을 지정 할 수 있다. 

제약을 걸어 사용자가 원하지 않는 형식의 매개변수를 넘기는 상황을 방지 할 수 있다.


형태

class 클래스명<T> where T : 제약형식

class 메소드명<T> where T : 제약형식

value(값) 타입

class MyClass<T> where T : struc


Reference(참조) 타입

class MyClass<T> where T  : class


T가 파생 클래스 일때

class MyClass <T> where T : MyBase


인터페이스

class MyClass <T> where T : Myinterface

등등..


2개 이상 제약이 가능함

class 클래스명<T,V> where T : 제약형식1, 제약형식2 where V : 제약형식


where <NAME> : <TYPE> 조건에 대한 제약사항

1) 구조체 및 정적 클래스는 사용할 수 없다.

2) 특수형식(object, Array, Delegate등)은 사용 불가

3) class와 struct조건은 같이 사용 불가




학습한 자료 사이트

http://www.csharpstudy.com/

https://m.blog.naver.com/rocabilly/140133303502

https://slaner.tistory.com/126

감사합니다.

반응형

'프로그래밍언어 > C#' 카테고리의 다른 글

C#) Lamda #2  (0) 2019.02.07
C#) Lamda 람다 식  (0) 2019.02.06
C#) 비트연산자  (0) 2019.01.30
C#) abstract, virtual, override  (0) 2019.01.26
C#) Delegate 콜백메서드,Delegate 체인 #3  (0) 2019.01.25

파란색 : 풀면서 어려웠던 부분 or 고려해야 될 부분

빨간색 : 해결방법

초록색 : 느낀 점

문제

문자 열중의 문자 한개를 빼서 자릿수를 하나 줄여 가장 큰 수를 출력하는 문제이다. 

풀이 : 내가 작성한 코드

문자열중 가장 작은 수를 빼면 가장 큰 수가 나 올 줄 알았다.  그런데 222250은  0이 가장 작은데 22225가 된다. 하지만 답은 22250이 되어야 하고 그래서 이건 아니고..., 앞 자릿수보다 뒷자리수가 크면 앞 자릿수를 빼면 된다. 인덱스 비교로 현재 값보다 다음 자릿수의 값이 크면 break로 빠져나와 그 숫자를 제거한다.

풀이 : 다른 사람이 작성한 코드

string s로 처음 받아온 숫자를 문자열로 바꾼 뒤 그 문자열에 서 해당하는 문자만 빼고 int형으로 리턴했다. 나는 형 변환도 많고, 굳이 안 써도 되는 집합 자료구조는 등 많이 사용하는 게 습관이 됐는데 최대한 쓰지 않는 방법으로 문제를 풀도록 해야겠다. 

문제 : https://codesignal.com/

반응형

'알고리즘자료구조 > 알고리즘문제' 카테고리의 다른 글

03.28_CodeSignal  (0) 2019.03.28
03.27_CodeSignal  (0) 2019.03.27
03.26_CodeSignal  (0) 2019.03.26
03.07_CodeSignal(배열의 요소중 가장 큰수일 경우)  (0) 2019.03.07
02.05_CodeSignal(체스 말판)  (0) 2019.02.05

 

점프구현하기

<현재>

 

(수정 전)

(수정 후)

 

 

위 그림처럼 캐릭터가 타겟을 노란색 테두리를 착지 지점으로 계산하여 점프하는 구현하였다.

캐릭터가 중앙지점을 가장 높은 위치를 지난 후 밑으로 떨어질 때 속도가 빨라져야 더 자연스러운 점프모션이 될 것같다. 

 

 

점프기능은 여러가지 방법이 있겠지만 DoTween을 사용했다.

스크립트를 살펴보자

Transform stpos; //플레이어 위치

Transform endpos; // 스킬발동 위치

Rigidbody Prb; //플레이어 Rigidbody

public void Jumpping()

{

Vector3 hpos = stpos.position + ((endpos.position - stpos.position) / 2);

 

Vector3[] Jumppath ={new Vector3(stpos.position.x,stpos.position.y,stpos.position.z),

new Vector3(hpos.x,hpos.y+3f,hpos.z),

new Vector3(endpos.position.x,endpos.position.y,endpos.position.z) };

Prb.DOPath(Jumppath, 2, PathType.CatmullRom, PathMode.Full3D); //점프구간

 

}

중간백터

Vector3 hpos = stpos.position + ((endpos.position - stpos.position) / 2);

hpos는 캐릭터에 움직일 path경로중 시작백터와 종료백터의 중간 백터가 된다.

 

path경로 설정

Vector3[] Jumppath ={new Vector3(stpos.position.x,stpos.position.y,stpos.position.z),

 new Vector3(hpos.x,hpos.y+3f,hpos.z),

 new Vector3(endpos.position.x,endpos.position.y,endpos.position.z) };

캐릭터가 지나갈 경로를 배열로 받는다. 시작백터그리고, 중간백터, 끝백터를 차례로 넣어준다. 

여기서 중간백터의 y값은 캐릭터가 얼마만큼 점프할 것인가를 +해주면 된다.

 

점프동작부분

Prb.DOPath(Jumppath, 2, PathType.CatmullRom, PathMode.Full3D);

마지막으로 실제 점프가 이루어지는 DOpath인데 Dotween은 캐릭터의 리지드바디로 움직일 캐릭터를 설정해주고, 계산한 점프경로 백터 배열, path이동 타입을 설정해준다. 

 

여기서 추가 업그레이드 해야 할 부분은 거리에 따른 점프시간(공중에 떠 있는 시간) 플레이어의 위치에서 점프하려는 거리에 맞게 점프시간을 설정하도록 할 것이다.  

 

 

 

 

반응형

스테미너 효과 UI 



내가 제작하고 있는 게임은 공격액션을 한번 취할때마다 한칸의 스테미너를 소모한다 

위 그림에서 알수 있듯이 연속적으로 3번 사용가능하고 시간이 지나면 다시 공격을 할 수 있도록 스테미너가 채워진다.

삼 등분으로 나누어 회색게이지가 한 칸찰 때마다 주황색 게이지가 채워지는 형태로 스테미너 HudBar를 구현했다.


필요한 이미지

이미지는 총4개 

1) 아무것도 채워지지 않은 상태를 표시하기 위한 배경

2) 점점채워지는 회색게이지바

3) 한칸씩 채워질 주황색 게이지바

4) 명확한 표시를 위한 3칸 나눔선


회색 게이지바는 시간초로 계산하여 매초마다 계속해서 증가시켜주고 

주황색게이지바는 2초의 한 1칸씩 6초후에 3칸이 된다. (maxguage는 6초)

이미지 위치를 다르게 놓고 보면 좀 더 명확하게 볼 수 있다.

반응형

스테미너 부족 시 떨림 효과




현재 제작하고 있는 게임은 스테미너가 없으면 공격을 못하는 방식이다. 그런 방식때문에 스테미너 부족상태를 명확하게 전달 할 필요가 있다즉 공격불가라는 표현을 텍스트대신 스테미너Bar가  좌우로 흔들리고 빨간색으로 변하도록 표현했다.


좌우 떨림효과

여러가지 표현방법이 있게지만 나는 DOTween을 이용했다.

DOTween을 이용하여 스테미너Bar가 전체를 좌우로 흔들리 도록 해주었다.


빨간색 알파 효과 

기존이미지의 색상을 빨간색으로 바꾸는것보단 미리 빨간색 이미지를 같은 사이즈로 만들어 놓는다.

알파 값을 0으로 놓고 평사시에는 안보이도록 숨겨놓는다. 


그리고 스테미너 부족상황이 오면 이 이미지의 알파값을 DOFade로 조절한다. 

반응형

비트연산자



비트 단위로 논리 연산하고 비트 이동 연산을 수행하는 연산자이다.

실행속도와 표현의 편리성때문에 많이 사용된다고 한다.


& 비트단위 AND

| 비트단위 OR

^ 비트단위 XOR

<< 왼쪽으로 1비트 이동

>> 오른쪽으로 1비트 이동


<결과>




반응형

'프로그래밍언어 > C#' 카테고리의 다른 글

C#) Lamda 람다 식  (0) 2019.02.06
C#) Generics 제네릭 클래스  (0) 2019.02.04
C#) abstract, virtual, override  (0) 2019.01.26
C#) Delegate 콜백메서드,Delegate 체인 #3  (0) 2019.01.25
C#) 델리게이트 #2  (0) 2019.01.24

 

형식이 같은 두 개체를 비교할때 자주 사용하는  Comparsion 델리게이트이다.

public class m_test : MonoBehaviour 
{
	public delegate int Comparsion<in T>(T x, T y);
	static int SortWithPrice(Product a, Product b)
	{
		return a.price.CompareTo(b.price);
	}
	class Product
	{
		public string Name { get; set; }
		public int price { get; set; }
	}
	void Start()
	{
		List<Product> products = new List<Product>()
		{
			new Product() { Name = "감자", price = 500 },
			new Product() { Name = "사과", price = 700 },
			new Product() { Name = "고구마", price = 400 },
			new Product() { Name = "배추", price = 600 },
			new Product() { Name = "상추", price = 300 }
		};

		products.Sort(SortWithPrice);
		foreach(var val in products)
		{
			Debug.Log(string.Format("품목:{0}, 가격:{1}",val.Name, val.price));
		}
	}

}

public delegate int Comparison<in T>(T x, T y);

T : 비교할 개체의 형식,

x : 비교할 첫 번째 개체

y : 비교할 두 번째 개체

public class m_test : MonoBehaviour 
{
	//public delegate int Comparsion<in T>(T x, T y);
	//static int SortWithPrice(Product a, Product b)
	//{
	//	return a.price.CompareTo(b.price);
	//}
	class Product
	{
		public string Name { get; set; }
		public int price { get; set; }
	}
	void Start()
	{
		List<Product> products = new List<Product>()
		{
			new Product() { Name = "감자", price = 500 },
			new Product() { Name = "사과", price = 700 },
			new Product() { Name = "고구마", price = 400 },
			new Product() { Name = "배추", price = 600 },
			new Product() { Name = "상추", price = 300 }
		};

		products.Sort(delegate (Product a, Product b)
		{
			return a.price.CompareTo(b.price);
		});
		//products.Sort(SortWithPrice);
		foreach(var val in products)
		{
			Debug.Log(string.Format("품목:{0}, 가격:{1}",val.Name, val.price));
		}
	}

}

=> SortWithPrice 메서드를 선언하지 않고도 가능하다.

		products.Sort((a, b) => 
		{
			return a.price.CompareTo(b.price);
		});

=> 람다 사용

 

콜백메서드 구현하기

		class Monster
		{
			public string Name { get; set; }
			public double Pow { get; set; }

			public Monster(string name, double pow)
			{
				Name = name;
				Pow = pow;
			}

			public override string ToString()
			{
				return this.Name+" : " +this.Pow;
			}
		}

Monster.class 몬스터이름과 파워수치를 받는 클래스

		class Monsters
		{
			private List<Monster> listOfMonster = new List<Monster>();
			public delegate void PrintProcess(Monster list);

			public void Add(Monster monster)
			{
				listOfMonster.Add(monster);
			}

			public void Print()
			{
				Print((monseter) =>
				{
					Console.WriteLine(monseter);
				});
			}
			public void Print(PrintProcess porocess)
			{
				//콜백메서드를 사용
				foreach(var item in listOfMonster)
				{
					porocess(item); //콜백 메서드에 매개변수를 전달해 호출
				}
			}

		}

Mosters.class 

Add메서드를 통해 Monster클래스를 가지고있는 객체들을 리스트로 넣어주고,

델리게이트를 통해 몬스터 리스트들의 정보를 출력해준다.

monsters.Print(); : monster.class ToString을 통해서 출력

monsters.Print((monster) =>
{
      Console.WriteLine();
      Console.WriteLine("이름: " + monster.Name);
      Console.WriteLine("파워: " + monster.Pow);
});

: 델리게이트를 통해 출력형식을 지정한 출력

풀 코드

...더보기

 

using System;
using System.Collections.Generic;

namespace ConsoleApp23
{
    class Program
    {
        class Monster
        {
            public string Name { get; set; }
            public double Pow { get; set; }

            public Monster(string name, double pow)
            {
                Name = name;
                Pow = pow;
            }

            public override string ToString()
            {
                return this.Name + " : " + this.Pow;
            }
        }

        class Monsters
        {
            private List<Monster> listOfMonster = new List<Monster>();
            public delegate void PrintProcess(Monster list);

            public void Add(Monster monster)
            {
                listOfMonster.Add(monster);
            }

            public void Print()
            {
                Print((monseter) =>
                {
                    Console.WriteLine(monseter);
                });
            }
            public void Print(PrintProcess porocess)
            {
                foreach (var item in listOfMonster)
                {
                    porocess(item);
                }
            }

        }
        static void Main(string[] args)
        {
            Monsters monsters = new Monsters();
            monsters.Add(new Monster("고블린", 100));
            monsters.Add(new Monster("슬라임", 10));

            monsters.Print();
            monsters.Print((monster) =>
            {
                Console.WriteLine();
                Console.WriteLine("이름: " + monster.Name);
                Console.WriteLine("파워: " + monster.Pow);
            });


        }
    }
}
   

 

반응형

abstract (추상)


먼저 간단한 예제를 테스트해봤다.

1) 메서드에 abstract을 사용하려면 해당 클래스도 abstract로 선언되어야 한다.

2) abstract 키워드로 표시한 속성이나 메서드는 본문을 정의할 수 없고, 

파생된 자식클래스에서 이 메서드를 정의 할 수 있다.(반드시 정의 해야함)

3) 접근 한정자 private은 사용이 불가하다.

4) 새로운 개체 생성이 불가능하다. (파생 클래스로 생성가능)


.


Virtual (가상)

Virtual도 간단한 예제를 테스트 해봤다.

1) 메서드에 virtual 키워드를 붙여야한다.

2) 접근 한정자 private은 사용이 불가하다

3) abstract과 다르게 본문을 정의 할 수 있다. (괄호만 치고 비워 놓아도 됨)

4) 파생클래스에 base. 키워드를 쓰면 부모 클래스에있는 메소드에 있는 내용 실행할 수 있음

(Parents클래스의 test에 접근한다는 것임) 

<실행결과>


override

override키워드는 위에서 사용한 것 들을 보면 부모클래스에서 선언,정의한 내용을 상속을 받은 자식클래스에서 매서드를 재 정의 할때 사용 한다는 것을 알 수 있다. opp지향 언어의 다형성을 잘 보 여 주는 키워드이다.





반응형

'프로그래밍언어 > C#' 카테고리의 다른 글

C#) Generics 제네릭 클래스  (0) 2019.02.04
C#) 비트연산자  (0) 2019.01.30
C#) Delegate 콜백메서드,Delegate 체인 #3  (0) 2019.01.25
C#) 델리게이트 #2  (0) 2019.01.24
C#) 델리게이트(delegate) #1  (0) 2019.01.23

Delegate 콜백 메서드


콜백이란?_

A라는 메서드를 호출할 때, B라는 메서드를 매개변수로 넘겨주고 A메서드에서 B메서드를 호출하게 되는것이다.


Delegate 체인

델리게이트는 하나의 메소드만 참조하는 것이 아닌 여러개 메서드를 참조 할 수 있다.

추가 연산(+=)을 통해 메서드를 추가 할 수 있다. 

-=을 통해 빼기도 가능하다.



델리게이트는 해당 클래스 내부 뿐만 아니라 다른 클래스에서도 이 델리게이트를 호출 할 수 있는 장점이자 단점인 위험이 존재한다. 추가 연산(+=)이 아닌, 할당 연산(=)을 하게 되면 기록된 메서드를 모두 지워지니 조심해야한다.



반응형

'프로그래밍언어 > C#' 카테고리의 다른 글

C#) 비트연산자  (0) 2019.01.30
C#) abstract, virtual, override  (0) 2019.01.26
C#) 델리게이트 #2  (0) 2019.01.24
C#) 델리게이트(delegate) #1  (0) 2019.01.23
c#) 스태틱 Static 변수,메서드,클래스  (4) 2019.01.16

델리게이트 #2


Delegate를 메서드 파라미터로 전달

델리게이트는 메서드 타입(동일한 리턴타입&파라미터)이어야 사용할 수 있다.




MyDelegate 델리게이트는 리턴타입과 파라미터가 string으로 선언 되어 있고,

델리게이트로 사용 할 메서드들도 타입들도 string으로 정의 되어있다.

27번줄 : del = new MyDelegate(StringtestTwo); 을 줄여서 쓸수 있다.

<출력 결과>



 참고 : http://www.csharpstudy.com/CSharp/CSharp-delegate.aspx

반응형

델리게이트(delegate) #1



delegate 뜻은 대리인,(집단의 의사를 대표하는) 대표(자)라는 뜻이 있다 무엇인가를'대신'해주는 느낌이다.


델리게이트는 메서드를 다른 메서드로 전달할 수 있도록 하기 위해 만들어 졌다.


정의

delegate string MyDelegate(string s);

델리게이트 선언의 중요점은 입력 파라미터와 리턴 타입이다.

사용하려는 메서드는 입력 파라미터타입및 갯수, 리턴타입이 같아야 한다.


객체 생성

MyDelegate del = new MyDelegate(Stringtest);

클래스 객체를 생성하는 것과 비슷한 방식으로 new를 통해 생성한다. 괄호 안에는 사용할 메서드명을 넣어 준다.


델리게이트 전달

del = new new MyDelegate(Stringtest);

Testdel(del);


void Testdel(MyDelegate m)

{

s1s2 = m("gogo");

}

델리게이트 객체를 메서드 호출 파라미터에 넣으면 된다.



 

참조 : http://www.csharpstudy.com/CSharp/CSharp-delegate-concept.aspx

반응형

2D Physics


유니티에는 3D와 마찬가지로 2D에도 물리 시뮬레이션 엔진이있다. 

Rigidbody2DCollider 2D가 대표적인 컴포넌트이다.


일단 가장 기본적으로 2D 물리에서는 position Z값은 의미가 없다.


간단히 테스트를 해보면


박스는 position Z값 15 

막대는 position Z값 0


에디터 3D모드로 보면 막대와 박스가 떨어져 있지만 박스는 막대에 물리가 적용되어 지나가지 않는다.

2D는 Z값을 변경이 가능하지만 2D Physic는 물리상호작용은 Z가 0에서 일어난다 





Hinge Joint 2D

2D에도 Hinge Joint가있다.



Anchor

어떻게 회전할 것 인지를 선택할 수 있다.


Connected Anchor

씬 어디서 배치될것 인가를 설정하는 곳이다.
이 값은 연결된 본체의 Local Space에있다.






https://www.youtube.com/watch?time_continue=5&v=l6awvCT29yU

참고 : https://www.youtube.com/watch?v=xEVXE7_YILk&feature=youtu.be

반응형

Physics 2D Manager






물리가 자세하게 필요한 게임이라면 여기서 설정을 통해 원하는 방식의 게임으로 제작이 가능할 것같다.

유니티 도큐에서 보면 질량의무게, 속도 임계값, 선형보간등을 설정이 가능하다.


이번에 테스트 한 것은 맨 밑에 많은 체크박스로 이루어진

Layer Collision Matrix이다. 


Layer Collision Matrix 레이어에 따른 충돌여부를 설정할 수 있다.

간단하게 테스트로 해보았다. 



1번 막대에는 레이어를 Default로 설정하고

2번 막대에는 ground로 설정했다. 

그리고 1,2번막대에는 모두 충돌체크를 할 수있도록 Collider컴포넌트를 추가했다.

그런다음 player와 Default 레이어에 해당하는 체크박스는 해제한다.


이제 player를 떨어트려보면.


예상으로는 막대에 콜라이더가 있기 때문에 1번막대에서 서있을것이다.




하지만 1번 막대를 통과하고 2번막대는 통과하지 못한다.


Layer Collision Matrix에서 설정한대로 player는 ground만 충돌 체크를 하게된다. 


활용도는 점프가 가능한 지형(통과 가능한)과 점프가 불가능한(통과하지 못하는)지형으로


구분하여 게임의 재미요소를 추가가 가능할 것같다.




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

반응형

오버로딩,오버라이딩


오버로딩, 오버라이딩 두개 이름이 비슷하지만 다른 재정의 기법이다.


오버로딩(Overloading)

하나의 클래스 안에 같은 이름으로 메서드를 재정의한다.

이름은 같게 하되 매개변수의 개수 및 타입정보는 다르하면 된다.

(2번째 메서드만 에러가 나는데 첫 번째 메서드의 매개변수의 정보가 같아서 이다.)

오버 로딩은 언제 쓰나?

유사하거나 동일한 결과를 출력하는 메서드가 전달 받는 매개변수에 따라 다른 연산을 해야 하는 경우에 사용된다.

메서드마다 다른 이름을 정의 하는것 보다는 직관적이고 편리해진다. 



오버라이딩(Overriding)

오버라이딩은 상위 클래스에서 선언된 함수를 하위 클래스에서 재정의하는 것

즉 이미 정의된 메서드를 재 사용 하는 것이다.

부모클래스의 메서드는 virtual키워드를 자식클래스의 메서드는 override를 꼭 선언해야한다.

오버라이딩은 다형성을 잘 나타내는 기법이다. 

<결과>

예를 들어 Animal이라는 부모 클래스에서 "동물을 짖는다"라는 메서드를 정의했다. 

그리고 자식 클래스로 Dog,Cat클래스를 만들고 Animal클래스의 "동물은 짖는다"라는 메서드를 override하여 "멍멍","냐옹냐옹"을 추가하여 재정의 하였다. 

고양이와 강아지는 공통적으로 짖는다 라는 메서드를 사용하지만 또 각각 짖는 방법이 다르기 때문이다.

게임속에서 생각해보면 공격이라는 메서드를 갖고있는 오브젝트들이 있다고 하면 그 오브젝트들은 다른 공격 스타일을 갖고 있을 것이다.



참고 : http://www.mkexdev.net/Article/Content.aspx?parentCategoryID=1&categoryID=5&ID=675

https://itmining.tistory.com/36

반응형

스태틱Static 변수


스태틱 변수는 객체를 선언만 해도 메모리가 할당되며 일반적인 변수들이 객체가 새로 생성될 때 메모리가 초기화 되는 것과 다르게 해당 객체를 계속 반복적으로 생성해도 메모리가 유지 된다는 특징이 있다. 

테스트를 해봤다

        public class Myclass

        { 

        private static int stNum= 0;

        private int num = 0;


        public Myclass()

        {

                stNum++;

                num++;

        }

     

        public void Showtest()

        {

            Console.WriteLine("stNum: {0},  num : {1}", stNum, num);

        }


    }

        static void Main(string[] args)

        {

            for (int i = 0; i < 5; i++)

            {

                Myclass test = new Myclass();

                test.Showtest();

            }

        }

    }


일반 변수인 num의 값은 변하지 않지만 Static으로 선언한 stNum은 메모리를 초기화 하지 않고 계속 더해 지고 있다. 

=> static 필드는 프로그램 실행 후 해당 클래스가 처음으로 사용될 때 한번 초기화되어

 계속 동일한 메모리를 사용하게 된다. 






스태틱Static 메소드



Static메소드는 객체가 메모리를 할당 받기 전에 호출이 되기 때문에 객체 생성 후 메모리 할당을 받는 일반 변수에 접근이 불가능하다. 인스턴스 메소드와 다르게 클래스로 객체를 따로 생성하지 않고 [클래스명.메서드명]이런식으로 호출하게 된다.

호출 시 일반 인스턴스 메서드와 달리 클래스로 부터 객체를 생성하지 않고 직접 호출한다. 



일반 인스턴스 메서드는 클래스로부터 객체를 생성하고 메서드를 호출해야하지만

static메서드는 [클래스명.메서드명]으로 메서드를 호출한다.


static메소드 내부에서 객체 멤버를 호출 할 수없다.

인스턴스 메서드는 스태틱메서드 멤버를 참조가 가능하지만 스태틱메서드는 다른 멤버 참조가 불가능하다.






스태틱Static 클래스



Static 클래스는 모든 클래스 멤버가 static 멤버로 되어 있으며, 클래스명 앞에 static이 붙는다.

다른 일반 클래스와 다르게 static생성자를 갖는다. 이 생성자는 보통 static필드를 초기화 할 때 사용한다.


테스트를 해보았다.



static 멤버 호출


객체 생성 불가

static는 인스턴스화 하지 않는 클래스이기 때문에 객체생성이 불가능하다.


 

스태틱객체는 프로그램이 종료되기 전까지 메모리를 해제하지 않고 객체를 생성하지 않고도 멤버에 접근이 가능하다는 장점 (특징)을 잘 활용해야 할 것같다. 하지만 단점으로 여러군데서 스태틱 멤버를 동시에 참조하게 될 경우와 객체 지향에 벗어난 개념이 되어 안좋다는 시선도 있다고 한다. 다른 장단점을 알아 볼 필요가 있겠다.



 참조 : http://www.csharpstudy.com/csharp/CSharp-static.aspx 사이트

http://ronniej.sfuh.tk/ , http://mygumi.tistory.com/253 ,http://blog.acronym.co.kr/347 블로그 





반응형

'프로그래밍언어 > C#' 카테고리의 다른 글

C#) 델리게이트 #2  (0) 2019.01.24
C#) 델리게이트(delegate) #1  (0) 2019.01.23
C#) 스택(Stack), 힙(Heap) 메모리구조  (0) 2019.01.15
C#)기본 개념 interface 인터페이스  (0) 2018.12.20
C#) continue , break 키워드  (0) 2018.11.14

스택(Stack), 힙(Heap) 메모리구조


우리가 어떤 동작을 할때(프로그램을 키거나) 운영체제는 그동작을 할 수 있게 메모리공간을 할당해준다.

그 할당해주는 메모리구조중 스택과 힙에 대해서 알아보자


스택 Stack 

프로그램은 함수의 호출로써 작동되는데 함수는 다른 함수를 호출하고 그 함수는 또 다른 함수를 호출하게된다. 함수들은 자신을 호출한 함수에게 결과를 반환한다.  함수 호출을 스택구조로 메모리에 차곡차곡  쌓고 처리가 끝나면 메모리에서 해제된다.

스택 메모리는 영역은 값 형식이다.

int a = 100;

int b = a;

b = 200;

a = 100;

a를 100으로 초기화하고 b는 a값을 넣어준다.

그리고 다시 b에는 200을 넣어주고 a는 100을 넣어준다.

결국은 a와 b는 마지막에 넣은100,200이 값이 되는것을 알 수있다.

스택메모리는 변수가 할당되었던 위치에 값만 변경되는것이다.

그리고 ,스택은 함수 호출 시 생성되는 지역 변수와 매개 변수가 저장되는 영역이며 함수 호출이 완료되면 해제한다.


힙 Heap

스택과 다르게 관리가 가능한(=사용자가 직접) 데이터 외에 다른 형태의 데이터를 관리하기 위한 빈 공간이라 생각하자

이 빈 공간은 동적생성된 변수를 관리 하기 위한 공간이다. 

힙 메모리 영역은 참조 형식이다.

Program p1 = new Program();

p1.value =200;

Program p2 = p1;

p2.value  = 100;

p를 동적 생성하고 값을 100을 넣어준다.

그리고 p2에 p1객체를 넣는다. 이 과정은 Stack에서의 복사가 이루어 진것이다.

p1이 참조하고있는 주소가 1000이라고 하면 p2 주소도 1000을 참조하게 된 것이다.

당연히 주소1000은 200을 참조하고 있으니 p2도 200을 참조하게 된다.

스택메모리에는 주소1000이 저장되고 힙메모리에는 값 200을 갖게된다.

네 번째줄  코드에서는 다시 100을 넣는다. 

당연히 같은 주소를 갖고있으니 p1,p2는 둘다 100을 값을 갖게 된다.

추가적인 특징으로 스택에는 메모리가 해제되는 시점이 있지만 힙에는 딱히 정해진 시점이 없다.

가비지콜렉터가 알아서 할당을 해제하기 때문이다. 

new로 동적 생성한 객체는 메모리는 힙에 만들어 지는 것과 동시에 가비지 콜렉터가 관리 되어 크게 신경쓸 필요는 없지만

가비지 콜렉터가 있으면 성능상으로 좋지 않다고 한다.


ref를 이용한 참조타입과 값타입 차이




반응형

오브젝트 풀링 Object Pooling

스페이스바를 누르면 총알을 생성되고, forward방향으로 이동하며 3초후에 사라진다.



1) 처음에 10개를 만들어 놓는다.

2) 플레이어가 스페이스바를 누르면 하나씩 SetActive(true)로 활성화 시켜 마치 새로 생성된 것처럼 보여준다.

3) 정해둔 시간이 되면 총알은 SetActiv(false)되어 삭제된 것처럼 보여준다.

4) 만약 씬안에 10개가 이상 총알이 생성돼야 한다면 추가적으로 10개씩 생성해준다.


위 gif처럼 천천히 총알을 쏘면 10개로 활/비활성화로 돌려 쓸수 있다.

하지만 빨리 쏘게되면 추가적으로 10개가 되는것을 볼 수 있다.


오브젝트를 생성과 삭제를 하지 않고 활/비활성화로 구현한다.

( Instantiate과 Destroy을 쓰지 않는 기법이다.)


오브젝트를 복제 생성하고 삭제하는 Instantiate과 Destroy을 사용하게 되면 가비지 콜렉터를 호출하게 된다.

가비지 콜렉터란?

C#에서는 메모리 관리를 프로그래머가 따로 관리 하지 않는다.

가비지 컬렉터는 메모리에 있는 참조가 끝난 객체를 쓰레기 치우는 것처럼 소멸 시키는 역할을 한다.

=> 가비지 컬렉터 호출이 많이 발생 할 수록 성능이 낮아 진다.



pool 컬렉터 셋팅

LinkedList<Bullet> m_lipool = new LinkedList<Bullet>(); //풀에 담을

LinkedList<Bullet> m_liActive = new LinkedList<Bullet>(); //(생성)활성화된 총알을 담을


    const int nMAKE_BULLET_COUNT = 10;


    public void Init(Transform a_objRoot, GameObject a_objBulletOrigin) 

    {

        m_objRoot      = a_objRoot;

        m_BulletOrigin = a_objBulletOrigin;


        MakeBullet(nMAKE_BULLET_COUNT);

    }


~~생략

풀에 담을 오브젝트를 담을 컬렉터와 게임에서 보여질 오브젝트를 담을 컬렉터를 만들어준다.



pool에 오브젝트(값) 삽입
private void MakeBullet(int a_nCount) { for (int i=0; i<=a_nCount; ++i) { var b = Instantiate(m_BulletOrigin).GetComponent<Bullet>(); b.transform.parent = m_objRoot.transform; b.gameObject.SetActive(false); //비활성화 m_lipool.AddLast(b); } }

씬에 생성을 하고 비활성화로 해주고 LinkedList풀에 Add해준다.


오브젝트 생성(생성될 위치, 데이터)

public void AddShot(Vector3 a_vcPos, ref BulletData a_vcData)

    {

        if(m_lipool.Count == 0) 

        {

            MakeBullet(nMAKE_BULLET_COUNT); //10개 

}

            var bullet = m_lipool.First.Value; 

            m_lipool.RemoveFirst() //삭제

            bullet.Init(ref a_vcData); 

            bullet.transform.localPosition = a_vcPos;

            bullet.gameObject.SetActive(true);

            m_liActive.AddLast(bullet); 

    }

오브젝트풀 카운트가 0이되면 추가적으로 다시 10개를 생성한다.

첫번째로 담았던 오브젝트를 위치와 데이터를 정보를 입력해주고 활성화 해준다.

m_lipool 풀에서는 지워준다.

m_liActive 에 추가 해준다.


pool반환

    List<Bullet> m_listemp = new List<Bullet>(); //리스트 생성

    private void Update()

    {

        foreach(var b in m_liActive) //활성화된 풀중에서

        {

            b.DoUpdate(); //DoUpdate(총알 위치 업데이트)

            if(b.IsDie == true) // 사라질 시간이 됐다면

            {

                m_listemp.Add(b); //사라진 총알은 리스트에 추가

            }

        }

        foreach(var b in m_listemp) //리스트에 저장된 사라진 총알들

        {

            b.gameObject.SetActive(false); //비활성화

            m_liActive.Remove(b); //활성화된 풀삭제

            m_lipool.AddLast(b); //다시 풀에  넣는다 (뒤에)

        }

        m_listemp.Clear(); //넣어준 리스트 클리어

    }

풀에 넣을 리스트를 생성해준다 중간 바구니라고 생각하면 쉽다. 

1) 사라질 시간이 될 총알들은 비활성화 해주고  

2) m_liActive에 지운다.

3) 그 총알들을 리스트에 담는다.

4) 바구니에 담은 총알들을 다시 m_lipool 생성풀에 넣어준다.


반응형

C#) 가비지 컬렉터 / 유니티) 가비지 컬렉터


가비지컬렉션?

메모리 관리 기법중 하나, 프로그램이 (동적)할당했던 메모리에서 필요없게 된 영역을 해제하는 과정이다.


명시적 해제가 필요없다

 C++에서는 개발자가 직접 메모리할당을 해제를 해야하는데 유니티에서 사용하는 C#프로그래밍에서는 가비지 컬렉터가 존재하기  명시적 메모리해제를 하지 않아도 된다. 또,유니티 Mono엔진 런타임 시스템 덕분에 메모리 노출 가능성도 크게 준다고 한다.


언제 발생?

힙을 할당해야하는데 사용가능한 메모리가 충분하지 않다고 판단될때, 그리고 아무도 모르는 시점(플랫폼마다 다르다고 한다.)


알 수 없는 메모리 해제 시점

가비지콜렉터는 메모리를 해제 할때 작업을 수행하는 데 시간이 소요된다. 메모리의 양과 프로그램을 실행 중인 플랫폼에 따라 다르고, 중요한 것은 메모리가 해제되는 시점을 개발자가 알 수 없다는 것이다.  게임과 같은 실시간으로 실행되는 프로그램에서는 큰 문제가 될 수 있다.


가비지컬렉터가 실행되면 동반되는 것들

힙에 할당된 변수가 스코프를 벗어나면, 사용되었던 메모리는 이때 해제 되지 않고, 가비지 컬렉터가 실행되어야 해제 되는 이이 때 일어나는 과정들이 있다. 

가비지 컬렉터가 힙 상의 모든 오브젝트를 검사하는데 오브젝트 참조값을 모두 확인해서 해당 오브젝트가 힙상에서 스코프내에 있는지 확인하여 삭제 여부를 판단하다. 당연히 힙 상의 오브젝트가 더 많을수록 가비지 컬렉터는 할 일이 많아진다. 


힙 파편화

힙에 메모리가 저장될 때 데이터의 크기에 따라 블록 단위로 저장이 되는데 서로 다른 크기의 메모리 블록이 저장되고 해제되면서 남은 공간이 생기게 된다. 그렇기 때문에 실제 개발자가 사용하려했던 프로그램 메모리 사용량이 늘어나고, 메모리를 찾기 위해 가비지 컬렉터가 더 자주 실행하게 된다.

 


참고 : https://docs.unity3d.com/Manual/UnderstandingAutomaticMemoryManagement.html?_ga=1.214538258.405170267.1480455402

http://ronniej.sfuh.tk/optimizing-garbage-collection-in-unity-games-2/

http://ronniej.sfuh.tk/optimizing-garbage-collection-in-unity-games-1/


반응형

버텍스 및 프래그먼트 기초 셰이더 #2


월드 공간에 메시 노멀을 표시하는 셰이더


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
    // no Properties block this time!
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // include file that contains UnityObjectToWorldNormal helper function
            #include "UnityCG.cginc"
 
            struct v2f {
                // we'll output world space normal as one of regular ("texcoord") interpolators
                half3 worldNormal : TEXCOORD0;
                float4 pos : SV_POSITION;
            };
 
            // vertex shader: takes object space normal as input too
            v2f vert (float4 vertex : POSITION, float3 normal : NORMAL)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                // UnityCG.cginc file contains function to transform
                // normal from object to world space, use that
                o.worldNormal = UnityObjectToWorldNormal(normal);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 c = 0;
                // normal is a 3D vector with xyz components; in -1..1
                // range. To display it as color, bring the range into 0..1
                // and put into red, green, blue components
                c.rgb = i.worldNormal*0.5+0.5;
                return c;
            }
            ENDCG
        }
    }
}
cs

노멀은 컬러를 만들어내는 것 외에도 모든 종류의 그래픽스 이펙트(조명,반사,실루엣 등등)에 사용된다.


#include "UnityCG.cgin" : UnityObjectToWorldNormal을 쓰기 위한 include


TEXCOORD0, TEXCOORD1 등은 텍스처 좌표 및 포지션과 같은 임의의 고정밀도 데이터를 나타낼 때 사용


UnityObjectToClipPos 오브젝트 공간의 버텍스를 화면으로 변환하는 유틸리티 함수

UnityObjectToWorldNormal :  객체 공간에서 화면으로 정점을 변형시키는 유틸리티 함수 


정규화된 벡터를 컬러로 시각화(-1.0~ +1.0 범위)

절반을 곱하고 나서 절반을 +


<적용 후>


월드 - 공간 노멀을 사용한 환경 반사


씬에서 반사 소스로 스카이박스가 사용 될 때 이 스카이박스 데이터를 담고 있는 

"디폴트"반사 프로브가 생성된다. (반사 프로브 : 내부적으로는 큐브맵 텍스처이다.)


큐브맵

__Cubemap__은 환경에 대한 반사를 나타내는 여섯 개의 사각형 텍스처 컬렉션입니다. 여섯 개의 사각형은 오브젝트를 둘러싸는 가상 큐브면을 형성합니다. 각각의 면은 월드 축의 방향을 따른 뷰를 나타냅니다 (위, 아래, 좌, 우, 앞, 뒤).

큐브맵은 오브젝트의 반사나 “주변 환경”을 캡처하는 데 사용됩니다. 예를 들어, skyboxes  environment reflections는 주로 큐브맵을 사용합니다.

유니티 도큐 : https://docs.unity3d.com/kr/current/Manual/class-Cubemap.html


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
 SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
 
            struct v2f {
                half3 worldRefl : TEXCOORD0;
                float4 pos : SV_POSITION;
            };
 
            v2f vert (float4 vertex : POSITION, float3 normal : NORMAL)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                // compute world space position of the vertex
                float3 worldPos = mul(_Object2World, vertex).xyz;
                // compute world space view direction
                float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
                // world space normal
                float3 worldNormal = UnityObjectToWorldNormal(normal);
                // world space reflection vector
                o.worldRefl = reflect(-worldViewDir, worldNormal);
                return o;
            }
        
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the default reflection cubemap, using the reflection vector
                half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, i.worldRefl);
                // decode cubemap data into actual color
                half3 skyColor = DecodeHDR (skyData, unity_SpecCube0_HDR);
                // output it!
                fixed4 c = 0;
                c.rgb = skyColor;
                return c;
            }
            ENDCG
        }
    }
}
cs


mul(x,y)

: 두 행렬의 곱을 계산한다.

worldPos = mul(unity_ObjectToWorld, vertex).xyz;

월드공간 관측 방향 계산

worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos)); 

: 지정된 객체 공간 정점 위치에서 카메라 방향으로 월드 공간 방향을 계산하고 정규화 함

worldNormal = UnityObjectToWorldNormal(normal);

: 월드 공간을 노멀로 변환

reflect(-worldViewDir, worldNormal);

: 정반사광의 방향 벡터를 구하는 벡터반사 함수.

첫 번째 인자로 입사광의 방향벡터를 두 번째 인자로 반사면의 법선을 받는다.


UNITY_SAMPLE_TEXCUBE

 : 큐브맵을 샘플링하기 위한 빌트인 매크로

__unity_SpecCube0

: (셰이더 변수)사용중인 반사 프로브의 데이터를 포함하고있다

DecodeHDR 

: 반사 프로브 데이터에서 실제 컬러를 얻기 위함,반사 프로브 큐브맵을 특별하게 인코딩된 방식으로 저장


<적용 후>



노멀 맵을 사용한 환경 반영


노멀맵(Normal Maps)은 오브젝트들에 추가 지오메트리 생성 없이 추가 디테일을 생성하기 위해 종종 사용된다. 

노멀 맵 텍스처를 가지고 환경을 반사하는 셰이더를 만드는 예제


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
 SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
 
            struct v2f {
                half3 worldRefl : TEXCOORD0;
                float4 pos : SV_POSITION;
            };
 
            v2f vert (float4 vertex : POSITION, float3 normal : NORMAL)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                // compute world space position of the vertex
                float3 worldPos = mul(_Object2World, vertex).xyz;
                // compute world space view direction
                float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
                // world space normal
                float3 worldNormal = UnityObjectToWorldNormal(normal);
                // world space reflection vector
                o.worldRefl = reflect(-worldViewDir, worldNormal);
                return o;
            }
        
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the default reflection cubemap, using the reflection vector
                half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, i.worldRefl);
                // decode cubemap data into actual color
                half3 skyColor = DecodeHDR (skyData, unity_SpecCube0_HDR);
                // output it!
                fixed4 c = 0;
                c.rgb = skyColor;
                return c;
            }
            ENDCG
        }
    }
}
cs


노멀맵을 사용하기 시작하면 표면 노멀 자체가 피셀당 기준으로 계산되어야한다. 

즉 환경이 픽셀마다 어떻게 반사되는지도 계산해야한다.

이전 쉐이더 예제코드와 크게 변한 것은 없다. 다른 점은 이 셰이더는 모델의 각 버텍스에 대해서가 아니라 화면의 모든 픽셀 각각 대해 계산을 한다. 


노멀 맵 텍스처는 대부분의 경우 좌표 공간에서 표현되는데 이는 모델의 "표면을 따르는"것으로 생각할 수 있다. 지금 만들려는 셰이더는 탄젠트 공간 기반 벡터를 알아야 하고 텍스처로부터 노멀벡터를 읽어야하고, 이 벡터를 월드 공간으로 변환하고 그 후 위 셰이더에서 모든 수학적 계산을 해야한다. 

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
    Properties{
        // normal map texture on the material,
        // default to dummy "flat surface" normalmap
        _BumpMap("Normal Map", 2D) = "bump" {}
    }
        SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
 
            struct v2f {
                float3 worldPos : TEXCOORD0;
                // these three vectors will hold a 3x3 rotation matrix
                // that transforms from tangent to world space
                half3 tspace0 : TEXCOORD1; // tangent.x, bitangent.x, normal.x
                half3 tspace1 : TEXCOORD2; // tangent.y, bitangent.y, normal.y
                half3 tspace2 : TEXCOORD3; // tangent.z, bitangent.z, normal.z
                // texture coordinate for the normal map
                float2 uv : TEXCOORD4;
                float4 pos : SV_POSITION;
            };
 
    // vertex shader now also needs a per-vertex tangent vector.
    // in Unity tangents are 4D vectors, with the .w component used to
    // indicate direction of the bitangent vector.
    // we also need the texture coordinate.
    v2f vert(float4 vertex : POSITION, float3 normal : NORMAL, float4 tangent : TANGENT, float2 uv : TEXCOORD0)
    {
        v2f o;
        o.pos = UnityObjectToClipPos(vertex);
        o.worldPos = mul(_Object2World, vertex).xyz;
        half3 wNormal = UnityObjectToWorldNormal(normal);
        half3 wTangent = UnityObjectToWorldDir(tangent.xyz);
        // compute bitangent from cross product of normal and tangent
        half tangentSign = tangent.w * unity_WorldTransformParams.w;
        half3 wBitangent = cross(wNormal, wTangent) * tangentSign;
        // output the tangent space matrix
        o.tspace0 = half3(wTangent.x, wBitangent.x, wNormal.x);
        o.tspace1 = half3(wTangent.y, wBitangent.y, wNormal.y);
        o.tspace2 = half3(wTangent.z, wBitangent.z, wNormal.z);
        o.uv = uv;
        return o;
    }
 
    // normal map texture from shader properties
    sampler2D _BumpMap;
 
    fixed4 frag(v2f i) : SV_Target
    {
        // sample the normal map, and decode from the Unity encoding
        half3 tnormal = UnpackNormal(tex2D(_BumpMap, i.uv));
        // transform normal from tangent to world space
        half3 worldNormal;
        worldNormal.x = dot(i.tspace0, tnormal);
        worldNormal.y = dot(i.tspace1, tnormal);
        worldNormal.z = dot(i.tspace2, tnormal);
 
        // rest the same as in previous shader
        half3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
        half3 worldRefl = reflect(-worldViewDir, worldNormal);
        half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, worldRefl);
        half3 skyColor = DecodeHDR(skyData, unity_SpecCube0_HDR);
        fixed4 c = 0;
        c.rgb = skyColor;
        return c;
    }
    ENDCG
}
    }
}
cs


학습참고

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

반응형

유니티 타일맵


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




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

반응형

+ Recent posts