Action과 Func (람다식)


Action

Action은 파라미터가를 받아 들이는데, 리턴 값이 없는 함수에 사용되는 Delegate이다.


Action은 쓰려면 네임스페이스에 선언해줘야한다.

using System;

무명메델리게이트와 다른 Action 델리게이트

delegate void MyDelegate<T1, T2>(T1 a, T2 b); 와 다르게 네임스페이스에 선언한 System에 Action은 유니티시스템에 이미 정의되어있다.



Func 


Func는 Action과 비슷한데, 리턴값이 있을 경우 사용한다.

3번째 는 반환값 타입을 입력해줘야한다.


실제사용 코드 

테이블종류를 넣고 데이터를 리스트에 추가하는 코드이다, 여기서도 Action<bool>을 사용하였다. 



학습참고 : https://www.youtube.com/watch?v=7H3MHXfFkhI&index=19&list=PLUZ5gNInsv_O7XRpaNQIC9D5uhMZmTYAf

http://www.csharpstudy.com/Tip/Tip-Func.aspx



암시적 변환


C#은 컴파일 타임에서 정적으로 데이터 형이 지정된다. 변수의 형을 변환 하지 않으면 다시 선언하거나 다른 형태의 값에 넣는 것이 당연히 불가능하다,예외가 있는데 바로 암시적 변환 될 경우다.


형식이 안전하고 데이터가 손실되지 않으므로 특수 구문이 필요하지 않는다. 


1) 파생클래스 -> 기본클래스로 변환



2) 동일 데이터형의 큰타입으로 변환






명시적 변환(캐스트)


명시적 변환은 캐스트가 필요하다. 변환 시 정보가 손실 될 수도 있고, 암시적으로 변환된 작은 타입에서 큰타입으로 변환되는데 값의 손실이 없었지만 반대(큰타입->작은타입)는 값 손실이 있다.


그냥 변환하는것이 불가하고 캐스팅 키워드를 이용해야 한다.

앞에 '( 타입 )' 써준다.


1) 기본클래스 -> 파생클래스


2) (같은 타입)작은자료형 -> 큰자료형




학습참고 : https://guslabview.tistory.com/68

https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/types/casting-and-type-conversions

ScriptableObject


스크립팅 가능한 오브젝트

메소드를 실행하는 스크립트가아닌 데이터를 사용하기 위한 스크립트이다.




생성방법

[CreateAssetMenu(fileName = "New HeroData", menuName = "Sword HeroData", order = 2)]

public class test : ScriptableObject

{


}

유니티에서 스크립트를 생성하면 Monobehaviour를 자동으로 상속 받게 되는데 지우고

ScriptableObject를 상속받아온다.


[CreateAssetMenu] : 없어도 되지만 있으면 편함

fileName : 새로 제작하게되면 임시로 생성된다. 

menuName : 유니티 메뉴에 표기되는 이름

order : 메뉴에서 보일 순서

Assets -> Create 보면 방금 생성한 HeroData가 보이게 된다.


SerializeField의 원본 데이터들은 수정되면 안되기 때문에 

데이터 값을 받을 게터를 public으로 추가해준다.

public class test : ScriptableObject

{

    [SerializeField]

    string heroName;

    [SerializeField]

    string description;

    [SerializeField]

    int goldCost;


    public string sName => heroName;

    public string sDesc => description;

    public int    nCoin => goldCost;

}


사용법

데이터 값 입력

ScriptableObject를 받을 코드 작성

public class Scene : MonoBehaviour

{

    public Text m_txtUI;

    public test m_Data;

    void Start()

    {

        m_txtUI.text = string.Format(m_Data.sName + " // " + m_Data.sDesc);

    }

}

Data 매칭 인스펙터 창에서 끌어다 놓는다

실행


데이터를 한 오브젝트나 클래스에서 사용하기에는 괜찮은 것 같다. 데이터가 왔다갔다 하거나 값이 변하는 데이터는 쓰기에 어려울 것 같다.


학습 참고 : https://docs.unity3d.com/kr/current/Manual/class-ScriptableObject.html

https://m.blog.naver.com/yoohee2018/220725032540

C#) 네임스페이스



가끔 "네임스페이스에 형식또는 네임스페이스 이름이 없습니다."라는 문구를 볼때가 있다.

네임스페이스를 알아보기 전에 작업 하다가 생긴 오류를 살펴보자


Define클래스

인터페이스나 enum같은 경우는 Defie클래스를 만들어서 한 곳에 정의하였다.


인터페이스를 사용하려는 클래스

아까 정의한 인터페이스를 사용하려는데? 

오류가 났다. 바로 'Global_Define' namespace를 선언해 주지 않았기 때문이다.


예시

콘솔창에서 텍스트를 출력하는 Console이 오류가 났다. 

이유는 System이라는 namespace가 없기 때문이다. 


Console클래스 정의로 이동해봤다.

Console이라는 클래스에는 WirteLine( )이라는 메서드가 있었다. 


Console이라는 클래스는 System 네임스페이스의 정의 되어있다.

System은 -> 네임스페이스

Console은 -> System의 정의되어있는 클래스

WirteLine은 -> Console클래스 에 정의된 메소드


네임스페이스 정의방법

첫 번째 

맨 윗단에 해당 using을 사용하여 C#파일에서 사용하고자 할때

두 번째

클래스명 앞에 네임스페이스 전부를 적는 경우 이 경우는 네임스페이스 안의 메소드, 

변수를 사용하려면 앞에 매번 적어줘야함 



네임스페이스는 즉 

소속이라는 정의하는 역할을 하는 것이다. 

네임스페이스를 귀찮게 나눠서 쓰는 이유.

이름이 같은 변수,메소드등등 이지만 실행되는 내용들은 다를 수 있기 떄문이다. 

A프로그램과 B프로그램이 있는데 같은 이름을 사용하지만 그 내용들이 조금 다르면?

프로젝트가 커지고 스크립트 수가 증가할수록 스크립트 클래스 이름이 충돌할 가능성이 커집니다. 이것은 게임의 다른 파트를 ​​따로 작업하고 궁극적으로 같은 프로젝트에 통합하는 경우에 적용됩니다. 예를 들어 한 명의 프로그래머가 메인 플레이어의 제어 코드를 만들고, 또 한사람이 적 캐릭터의 제어 코드를 작성했다고합니다. 둘 다 기본 스크립트 클래스를 Controller 라고 명명하고 프로젝트를 통합할 때 이름이 충돌합니다.

<유니티도큐>


유니티에 생성되는 namespace

유니티에서 스크립트를 생성하면 자동으로 추가 되는 것들이다. 클래스에서 Start나 Update메소드가 자동으로 생성되지만 있는 존재만으로도 비용이 들기 때문에 사용하지 않으면 삭제한다. namespace도 마찬가지이다. 사용하지 않으면 지워주자



학습참고 : 

1) https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/namespaces/

2) https://thinkpro.tistory.com/22

3) http://www.csharpstudy.com/csharp/CSharp-namespace.aspx


객체지향 언어와 절차지향 언어 차이점



절차지향 언어

프로그램의 프로세스 흐름을 표현하는데 비중


객체지향 언어

데이터나 정보의 표현에 비중

데이터와 함수를 하나의 덩어리로 묶어서 생각하는 방법


절차지향 언어는 대표적으로 C언어가 있다. 컴퓨터의 작업처리 방식과 유사하기 때문에 객체지향 언어를 사용하는 것보다 속도가 빨랐다. 

하지만 지금은 기술발전으로 소프트웨어가 발달로 절차지향과 객체지향언어는 속도차이가 거의 나지 않음 뿐더러

객체지향 언어는같은 기능을 중복으로 사용하는 경우를 없애고, 모듈을 재활용하기 때문에 하드웨어가 감당하는 처리량을 많이 줄 뿐만 아니라  새로운 기능을 수정,추가 하는 유지보수, 캡슐화로주변에 미치는 영향을 최소화 해준다. 


객체지향 프로그래밍은 실행절차에 영향을 받지 않는 프로그램 기법인 것처럼 생각할 수 있지만, 구조와 모듈을 먼저 설계한 다음에 실행순서와 흐름을 짜는 방식일뿐, 객체지향방식으로 개발한 프로그램도 절차에 따라 실행된다. 


객체지향언어의 장점이 더 많지만 코드의 사이즈를 최소화 하고 빠른 동작이 필요한 프로그램이면 절차지향언어로 개발하는게 맞는것 처럼, 프로그램의 목적의 맞게 선택해서 개발해야한다.




학습참고

1) http://blog.wishket.com/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-vs-%EC%A0%88%EC%B0%A8%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/

2) https://sesok808.tistory.com/32


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

실수비교/ Epsilon 엡실  (0) 2019.06.09
오버로딩,오버라이딩 Overloading / Overriding  (0) 2019.01.19

스태틱 오브젝트


게임이 실행되는 동안 움직이지 않는 오브젝트를 스태틱으로 설정해 놓으면 유니티 에디터 상에서 미리 계산해놓아 실시간으로 계산하지 않도록 하여 비용을 줄이는 최적화 방법이다.



  • Lightmapping: 씬을 위한 고급 라이팅;
  • Occluder and Occludee: 특정 카메라 위치로부터 오브젝트의 가시성에 기반한 렌더링 최적화;
  • Batching: 여러 오브젝트를 더 큰 하나의 오브젝트로 결합하는 렌더링 최적화;
  • Navigation: 씬 안에서 캐릭터가 장애물을 지나게끔 만들어주는 시스템.
  • Off-mesh Links: Navigation 시스템에 의해 만들어지는 씬에서의 불연속적인 영역 간의 연결.
  • Reflection Probe: 모든 방향에 대한 주위 환경을 둥근 뷰의 형태로 캡처합니다.

유니티는 베이크된 GI(전역 조명)을 간접광에 대한 내용을 미리 계산해서 데이터화를 미리 가능하다. 




Occlusion Culling오클루젼 컬링


땅을 스태틱오브젝트로 설정하고 카메라가 비추는 곳만 그리게 된다.

오클루젼 컬링 (Occlusion Culling)은 오브젝트가 다른 오브젝트에 의해 가려져서 카메라에 보이지 않을 때 해당 오브젝트의 랜더링을 비활성화 하는 기능이다.

+ 이거는 절두체컬링이네.. 

정적오브젝트는 리소스가 사전준비가 되어있어야 하며 추가/변경 될때는 베이크를 해주어야 한다.  



참고자료 : https://docs.unity3d.com/kr/530/Manual/StaticObjects.html

boxing/unboxing


박싱/언박싱?


내가 알고 있는 박싱/언박싱은 자료의 형변환이 일어날때

값 형식을 참조형식으로 박싱

참조형식을 값형식으로 언박싱이다 라는정도인데 메모리영역에서 어떻게 일어나는지 알아보았다.


박싱

값 형식을 참조 형식으로 변환되는 것을 말한다. 

static void Main(string[] args)

{ int i = 93; object s = i; //박싱 }

int i 는 93이라는 값을 받고, object s는 i의 값을 받고있다.

값 형식인 정수형 자료 int를 object(참조형식)객체로 할당하게 되면 박싱이 일어나게 된다.


=> 스택에 있는 데이터가 힙으로 복사 된 것이다.


Stack에 있는 93이라는 정수형 자료가 Heap영역으로 복사되고, 이영역을 객체 변수 s가 가리키게 된다.



언 박싱

참조 형식을 값 형식으로 변환하는 것,

static void Main(string[] args) { int i = 93; object s = i; int j = (int)s; //언 박싱(명시적 변환) }

s를 int로 값 형식으로 변환한다. 

박싱된 객체인 s를 다시 값 형식으로 변환하면 언박싱이 일어난다.


=> 힙에 있는 데이터가 스택으로 복사된다.



언 박싱이 가능한 객체

값 형식을 박싱하여 생성된 객체이어야만 가능

static void Main(string[] args) { short i = 93; object s = i; int j = (int)s; //언 박싱 불가 }

short타입을 박싱한 객체를 int타입으로 언 박싱은 불가능

해당 타입보다 작은 범위로 변환 불가능



높은 비용의 박싱/언박싱

박싱과 언박싱은 많은 시간이 소모된다고 한다. 되도록 제네릭을 사용해서 박싱과 언박싱이 일어나지 않도록 구성하도록 하는게 좋다.


박싱의 과정 : 

1. 값 타입을 힙에 생성하기 위해 메모리를 힙 영역에 생성

2. 값을 힙 영역에 할당된 메모리로 복사

3. 참조할 변수에 할당된 메모리 주소를 할당


언박싱의 과정 :

1. 박싱값인지 확인

2. 박싱된 값이라면 값 타입 변수에 복사

3. 박싱한 메모리와 언박싱한 메모리 2개 존재 ( 가비지 발생 )



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

 https://grayt.tistory.com/87 [IT's me]

https://hongjinhyeon.tistory.com/90


ref 키워드 (참조자)


C++에서 참조자 & 있다면 C#에는 단순히 참조를 하기위한 ref키워드가 있다. 

값형식이든 참조 형식이든 둘다 스택에 있는 값이 복사된다.


값 형식 : 스택영역메모리에 값이 직접 들어가있어서 그것이 복사된것

참조 형식 : 스택영역메모리에 힙영역의 주소가 들어있어서 그 주소가 복사되면서 같은 객체를 가리 킴

ref키워드를 이용하면 스택영역 메모리의 주소를 사용하면서 Call by reference를 사용하게된다. 


예제코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
        static void Main(string[] args)
        {
            int a = 100;
            int b = 200;
            swapvalue(ref a, ref b);
            Console.WriteLine("변수{0}에 들어있는 수는{1}입니다.""a", a);
            Console.WriteLine("변수{0}에 들어있는 수는{1}입니다.""b", b);
 
        }
        static void swapvalue(ref int a, ref int b)
        {
            int temp = a;
            a = b;
            b = temp;
        }


a값과 b값이 변경되었다.


out 키워드 (참조자)


out키워드도 Call by reference를 가능하게 하는 키워드이며 

'출력용 매개변수'라고도 하여 값을 읽을 수 없는 쓰기용이다.

ref와 다른 특징들이 있다.


1. 현재 메소드가 끝나기 전에 out키워드가 붙은 변수는 값을 할당 받아야한다.

1
2
3
4
5
6
7
        static void swapvalue(ref int a, ref int b, out int c)
        {
            int temp = a;
            a = b;
            b = temp;
  //c 변수는 값을 할당 받아야한다.
        }

cs


2. out 키워드가 쓰인 변수는 함수내에서 값을 읽을 수 없다.

1
2
3
4
5
6
7
8
        static void swapvalue(ref int a, ref int b, out int c)
        {
            int temp = c; //에러
            a = b;
            b = temp;
            c = a;
 
        }
cs


예제코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
        static void Main(string[] args)
        {
            int a = 100;
            int b = 200;
            int swapA ; //값 초기화 의미 X
            int swapB;  //값 초기화 의미 X
            swapvalue(a,b, out swapA, out swapB);
            Console.WriteLine("변수{0}에 들어있는 수는{1}입니다.""a", swapA);
            Console.WriteLine("변수{0}에 들어있는 수는{1}입니다.""b", swapB);
 
        }
        static void swapvalue(int a,int b, out int swapA, out int swapB)
        {
            swapA = b;
            swapB = a;
        
        }
cs


ref와 기능이 같은데 out키워드를 쓰는 이유?

ref키워드를 이용해서 매개 변수를 넘기는 경우에는 메소드가 해당 매개변수에 결과를 저장하지 않더라도 컴파일러는 아무런 경고를 하지 않는다.

하지만 out 키워드는 매개변수를 넘길 때 메서드가 해당 매개변수에 결과를 저 정하지 않으면 컴파일러 에러를 주면서 강제적으로 제한을 준다.

 또 첫 번째처럼 초기화를 하지 않아도 되는 이유는 호출당할 때 값을 할당할 것을 보장하기때문이다. 프로그램이 실행됐을 때가 아닌 코드를 작성하는 중에 컴파일 과정 에러를 확인할 수 있다.



공부참조

https://grayt.tistory.com/90?category=683217

https://runtoyourdream.tistory.com/279

https://grayt.tistory.com/90?category=683217

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

C#) 네임스페이스 / 유니티 네임스페이스  (0) 2019.02.13
C#) 박싱/언박싱 (boxing/unboxing)  (0) 2019.02.10
C#) Lamda #2  (0) 2019.02.07
C#) Lamda 람다 식  (0) 2019.02.06
C#) Generics 제네릭 클래스  (0) 2019.02.04

람다 Lamda  #2





앞서 공부한 

델리게이트를 써서 sum값을 출력해봤다.

//결과 값 15


이번에는 Print함수를 작성하지 않고 

무명메소드로 sum을 출력해봤다.

 //결과 값 15


이번에는 

람다식으로 sum을 출력했다.

// 결과 값 15


마지막으로

형식 매개변수를 이용해서 람다 식을 사용해 봤다.

// 결과 값 15 (유니티 디버그로 출력)

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

C#) 박싱/언박싱 (boxing/unboxing)  (0) 2019.02.10
C#) ref 키워드 (참조자) , out 키워드  (1) 2019.02.09
C#) Lamda 람다 식  (0) 2019.02.06
C#) Generics 제네릭 클래스  (0) 2019.02.04
C#) 비트연산자  (0) 2019.01.30

 Lamda 람다 식




람다 식은 대리자또는 식트리 형식을 만드는 데 사용할 수 있는 익명 함수이다.

람다 식을 사용하여 인수로 전달되거나 함수 호출값으로 반환되는 로컬 함수를 쓸 수 있다.


작성법

' => ' 왼쪽에 입력 매개 변수를 지정(있는 경우) 다른 쪽에 식이나 문 블록을 넣는다.


delegate int del(int i); static void Main(string[] args) { del myDelegate = x => x * x; int j = myDelegate(5); //j = 25 }




람다 식

무명 메소드를 단순한 계산식으로 표현 한 것이다.

메소드는 매개변수, 내부 식, 반환 값으로 구성 되어 있는데 이들을 가지고 메소드를 계산 식으로 표현할 수 있다.(=람다 식)



먼저 무명 메소드를 보면

매개변수 타입, 내부의 { } 내부 식, 반환 값을 입력 해주어야 한다.


무명메소드를 람다 식으로

 매개변수 타입, { }안 내부 식,내부 반환 값이 생략이 가능하다.




https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions

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

C#) ref 키워드 (참조자) , out 키워드  (1) 2019.02.09
C#) Lamda #2  (0) 2019.02.07
C#) Generics 제네릭 클래스  (0) 2019.02.04
C#) 비트연산자  (0) 2019.01.30
C#) abstract, virtual, override  (0) 2019.01.26

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

빨간색 : 해결방법

초록색 : 느낀 점

문제

체스 말판이 위 특정 셀에 위치할 때 움직일 수 있는 방법의 개수를 구하는 문제이다.  특정 위치 이기 때문에  특정 셀 밑이거나 위에 있을 때 제한된다.

풀이 : 내가 작성한 코드

 

메모장에 적으면서

 처음 생각하기에 문자열의 앞cell[0]뒤cell[1] 8방향의 이동하게 문자를 더하는 값으로 확인이 가능할까 생각했지만 안된다고 판단하고, 체스 말이 위 체스 말이 움직 일 수 있는 방법이 최대 8가지이기 때문에 8방향을 모두 체크하고 각각 케이스에 검사하는 방법밖에 생각이 안 났다. 그래서 그냥 case문을..

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

가장 높은 vote를 받은 코드이다. 5개의 길이의 배열을 만드는데 무엇인가 했더니 체스 말판이 움직일 수 있는 방향 개수들이다. 그다음으로 문자 알파벳과 숫자를 체크하는데 'a'나 'h'를 빼는 이유는 체스가 저 두 문자에 위치했을 때 이동 방향 개수가 제한된다. 또, 체스 말판이 a에 가깝나 h에 가깝나를 체크한다. Math.Min(int,int )는 둘 중 작은 수를 반환하는 함수인데, 'a', h'에 가깝지 않아 2를 넘으면 4방향은 이동 가능이 보장이 된다. 반대로  마찬가지로 숫자 부분도 체크하여 리턴된 두 정수를 더한 뒤 배열에서 찾는다.. 

일단 문제 접근에 대한 방식 자체가 다르다ㅜ. 훨씬 더 깔끔하고 나도 문제에서 8가지 방향을 정해져 있다고 판단하고 포문을 8번 돌렸지만, 이 코드는 한 번 더 생각하여 제한된 이동 방향 개수 생각하고 그 값을 배열로 넣어두었다. 그리고 dist라는 말의 칸 이동 수가 정해져 있기 때문에 체스판 테두리에 가까웠을 때 제한되는 방법으로 깔끔하게 구현했다. 역시 접근 방법은 여러 가지로 생각하는 게 좋겠다. 

문제 : https://codesignal.com/


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

+ Recent posts