반응형

 

2022.0118 - [Unity] - Unity3D - 유니티3D with AdMob 광고의 내용을 진행하다 보면 예상치 못한 에러를 만날 수 있다. 몇 가지 에러를 해결해 보자.

 

Resolving Android Dependencies 과정 중 발생하는 에러

환경 변수에 JAVA_HOME이 등록되지 않아 발생하는 에러다.

 

유니티 설치 폴더에 있는 OpenJDK 경로를 JAVA_HOME 변수로 등록한다.

 

Google.IOSResolver.dll을 로드할 수 없어 발생하는 에러

안드로이드 앱을 개발하기 위해 iOS Build Support를 설치하지 않아 발생하는 에러다.

 

Unity Hub를 실행한다.

 

 

설치되어 있는 Unity 아이콘 오른쪽 점(...)을 클릭하고 Add Modules를 선택한다.

 

iOS Build Support를 선택하고 DONE을 클릭해 설치한다.

 

반응형
Posted by J-sean
:
반응형

 

2021.12.30 - [Unity] - Unity3D - 유니티 3D with OpenCV 1 에서 만든 라이브러리(OpenCVDll.dll)와 

2021.12.29 - [Unity] - Unity3D - 유니티 3D WebCamTexture 라이브 비디오 텍스쳐 의 WebCamTexture를 이용해 라이브 비디오 데이터를 프로세싱하고 오브젝트의 텍스쳐로 사용해 보자.

 

이미지 프로세싱에 사용할 OpenCVDll 라이브러리를 세팅하고 Cube를 생성한다.

 

Cube에 스크립트를 추가한다.

 

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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
using System.Runtime.InteropServices;
 
public class UnityWithOpenCV : MonoBehaviour
{
    [DllImport("OpenCVDll")]
    private static extern void FlipImage(ref Color32[] rawImage, int width, int height);
 
    Renderer renderer;
    WebCamTexture webCamTexture;
    Color32[] image;
 
    // Start is called before the first frame update
    void Start()
    {
        renderer = GetComponent<Renderer>();
        webCamTexture = new WebCamTexture(64048060);
        webCamTexture.Play();
 
        image = new Color32[webCamTexture.width * webCamTexture.height];
    }
 
    // Update is called once per frame
    void Update()
    {
        webCamTexture.GetPixels32(image);
 
        // Image processing
        FlipImage(ref image, webCamTexture.widthwebCamTexture.height);
 
        Texture2D texture2D = new Texture2D(webCamTexture.width, webCamTexture.height);
        texture2D.SetPixels32(image);
        renderer.material.mainTexture = texture2D as Texture;
        (renderer.material.mainTexture as Texture2D).Apply();
    }
}
 

 

Cube에 추가한 스크립트에 코드를 작성하고 저장한다. 모든 프레임에 텍스쳐가 이미지 프로세서(FlipImage())에 의해 상하좌우 반전된다.

 

유니티로 돌아오면 스크립트가 컴파일 된다. 실행 버튼을 클릭하면 매 프레임마다 Cube의 텍스쳐가 상하좌우 반전 처리된다.

 

 

스크립트의 FlipImage()를 주석처리하고 실행하면 원래 이미지의 텍스쳐가 표시된다.

 

프로그램을 실행하는 동안 처리된 이미지가 Inpector창의 Material Preview에도 실시간 적용된다.

 

반응형
Posted by J-sean
:
반응형

유니티에서 OpenCV를 사용할 수 있도록 라이브러리(DLL)를 만들어 보자.

 

Visual Studio에서 C++ - Windows - Empty Project를 선택한다.

 

적당한 이름과 폴더를 지정한다.

 

프로젝트가 생성되었으면 C++ File을 추가한다. (Add New Item)

 

Solution Platforms는 x64로 변경한다.

 

 

프로젝트 Property Pages - General - Configuration Type - Dynamic Library (.dll)로 변경한다.

 

Advanced - Target File Extension - .dll로 변경한다.

 

C/C++ - General - Additional Include Directories에 OpenCV Include 폴더를 추가한다.

 

Linker - General - Additional Library Directories에 OpenCV Library 폴더를 추가한다.

 

 

Linker - Input - Additional Dependencies에 OpenCV 라이브러리(opencv_worldXXXd.lib)를 추가한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <opencv2/opencv.hpp>
 
struct Color32
{
    uchar red;
    uchar green;
    uchar blue;
    uchar alpha;
};
 
extern "C" __declspec(dllexportvoid FlipImage(Color32 **rawImage, int width, int height)
{
    using namespace cv;
    Mat image(height, width, CV_8UC4, *rawImage);
    flip(image, image, -1);
}
 

 

프로젝트 세팅이 끝나면 이미지의 상하좌우를 반전하는 소스를 입력하고 빌드한다.

 

라이브러리 파일(OpenCVDll.dll)이 생성된다.

 

유니티3D 프로젝트를 생성한다.

 

 

Assets에 Plugin 폴더를 만들고 OpenCV 라이브러리 파일(opencv_worldXXX.dll)과 위에서 만든 라이브러리 파일(OpenCVDll.dll)을 복사한다.

 

원하는 이미지 파일을 Assets 폴더에 복사한다.

 

아래와 같이 텍스쳐 세팅을 변경한다.

Texture Type - Sprite (2D and UI)

Advanced - Read/Write Enabled - Check

Default - Format - RGBA 32 bit

Apply 클릭

 

Image UI를 생성한다.

 

 

Source Image에 위에서 추가한 이미지를 선택한다.

 

스크립트 컴포넌트를 추가한다.

 

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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
using System.Runtime.InteropServices;
using UnityEngine.UI;
 
public class OpenCVTest : MonoBehaviour
{
    [DllImport("OpenCVDll")]
    private static extern void FlipImage(ref Color32[] rawImage, int width, int height);
 
    // Start is called before the first frame update
    void Start()
    {
        Color32[] image = GetComponent<Image>().sprite.texture.GetPixels32();
        FlipImage(ref image, 608912);
        GetComponent<Image>().sprite.texture.SetPixels32(image);
        GetComponent<Image>().sprite.texture.Apply();
    }
 
    // Update is called once per frame
    void Update()
    {
 
    }
}
 

 

OpenCVDll.dll을 사용하는 스크립트를 작성하고 저장한다.

 

유니티로 돌아오면 스크립트가 컴파일 된다. 실행 버튼을 클릭한다.

 

 

이미지의 상하좌우가 반전된다.

 

3D 오브젝트에 적용해 보자. Image UI를 삭제하고 Cube를 생성한다.

 

위에서 사용했던 이미지는 삭제하고 다시 복사한다. 그리고 아래와 같이 세팅한다.

Advanced - Read/Write Enabled - Check

Default - Format - RGBA 32 bit

Apply 클릭

 

Material을 생성하고 위에서 세팅한 이미지를 적용한다. (Albedo 옆 동그라미 클릭)

 

 

Cube에 새로 생성한 Material을 적용하고 OpenCVTest 스크립트를 추가한다.

 

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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
using System.Runtime.InteropServices;
 
public class OpenCVTest : MonoBehaviour
{
    [DllImport("OpenCVDll")]
    private static extern void FlipImage(ref Color32[] rawImage, int width, int height);
 
    // Start is called before the first frame update
    void Start()
    {
        Color32[] image = (GetComponent<Renderer>().material.mainTexture as Texture2D).GetPixels32();
        FlipImage(ref image, 608912);
        (GetComponent<Renderer>().material.mainTexture as Texture2D).SetPixels32(image);
        (GetComponent<Renderer>().material.mainTexture as Texture2D).Apply();
 
        // Assets - Resources 폴더에 이미지를 저장하고 로드해서 텍스쳐로 활용하는 예
        //Texture2D texture2D = Resources.Load("Barbara") as Texture2D;
        //Color32[] image = texture2D.GetPixels32();
        //FlipImage(ref image, 608, 912);
        //texture2D.SetPixels32(image);
        //texture2D.Apply();
        //GetComponent<Renderer>().material.mainTexture = texture2D;
    }
 
    // Update is called once per frame
    void Update()
    {
 
    }
}
 

 

OpenCVTest 스크립트는 위와 같이 수정하고 저장한다.

 

유니티로 돌아오면 스크립트가 컴파일된다. 실행 버튼을 클릭한다.

 

상하좌우가 반전된다.

 

반응형
Posted by J-sean
:
반응형

카메라 영상을 텍스쳐로 사용해 보자.

 

Cube를 하나 생성한다.

 

스크립트(CamTexture)를 만든다.

 

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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class CamTexture : MonoBehaviour
{
    Renderer renderer;
    WebCamDevice[] webCamDevices;
    WebCamTexture webCamTexture;
 
    // Start is called before the first frame update
    void Start()
    {
        renderer = GetComponent<Renderer>();
 
        // 카메라 조사
        webCamDevices = WebCamTexture.devices;
        for (int i = 0; i < webCamDevices.Length; i++)
        {
            Debug.Log($"{i}) {webCamDevices[i].name}: {(webCamDevices[i].isFrontFacing ? "Front" : "Back")} camera");
        }
 
        // 첫 번째 전면 카메라 선택
        for (int i = 0; i < webCamDevices.Length; i++)
        {
            if (webCamDevices[i].isFrontFacing == true)
            {
                webCamTexture = new WebCamTexture(webCamDevices[i].name);
                break;
            }
        }
 
        // 텍스쳐에 적용
        if (webCamTexture != null)
        {
            webCamTexture.requestedFPS = 60;
            renderer.material.mainTexture = webCamTexture;
            webCamTexture.Play();
        }
 
        // 간단히 하고 싶다면 아래처럼 하면 된다.
        /*
        webCamTexture = new WebCamTexture(640, 480, 60);
        // If no device name is supplied to the constructor or is passed as a null string, the first device found will be used.
        renderer.material.mainTexture = webCamTexture;
        webCamTexture.Play();
        */
    }
 
    // Update is called once per frame
    void Update()
    {
 
    }
 
    private void OnDestroy()
    {
        if (webCamTexture != null)
        {
            webCamTexture.Stop();
            WebCamTexture.Destroy(webCamTexture);
        }
    }
}
 

 

소스를 입력하고 저장한다.

 

실행하면 Cube에 카메라 영상이 재생된다.

 

반응형
Posted by J-sean
:
반응형

3D 모델을 임포트하고 텍스쳐를 적용해 보자.

 

모델과 텍스쳐를 임포트한다.

 

Woman.fbx
2.63MB
WomanSkin.png
0.00MB

 

임포트한 모델을 씬으로 불러와도 텍스쳐가 자동으로 적용되지 않는다. 인스펙터창의 Material은 비활성화 되어 있다.

 

임포트한 모델의 Material에서 Create Material Preset을 클릭한다.

 

모델의 Material과 동일한 Material이 생성된다.

 

 

모델을 선택하고 위에서 생성한 Woman Material을 Drag&Drop한다. 씬에 있는 모델에 직접 Drag&Drop 하거나 인스펙터창 Add Component 버튼 아래에 Drag&Drop한다.

 

Material의 Albedo옆 작은 동그라미를 클릭하고 WomanSkin을 선택한다.

 

텍스쳐가 적용되었다.

 

반응형
Posted by J-sean
:
반응형

모델에 포함된 애니메이션도 인스펙터 창에서 직접 수정 할 수 있다.

 

모델을 임포트한다.

 

Woman.fbx
2.63MB

 

Assets 창에서 모델을 선택하고 인스펙터 창에서 Animation 탭으로 이동한다.

 

Idle 애니메이션을 선택하고 Loop Time을 체크한다. Apply를 클릭한다.

 

Assets창 모델의 애니메이션을 확인하면 Loop Time이 체크 되어있다.

 

 

씬에 모델을 Drag&Drop 한다.

 

애니메이터 컨트롤을 생성한다.

 

생성한 애니메이터 컨트롤을 씬에 있는 모델에 Drag&Drop 한다.

 

애니메이터 창을 확인한다.

 

 

Assets창 모델의 애니메이션(Idle)을 애니메이터창으로 Drag&Drop 한다.

 

플레이하면 Idle상태가 반복된다.

 

다른 애니메이션도 추가해 테스트 해 보자.

 

반응형
Posted by J-sean
:
반응형

캐릭터 모델을 임포트하고 애니메이션(애니메이터)을 사용해 보자.

 

 

Woman.fbx
2.63MB

 

Assets - Model 폴더에 모델을 복사한다.

 

Project 창에서 확인할 수 있다.

 

Animation 폴더를 만든다.

 

 

모델에 포함된 애니메이션을 복사한다(Ctrl+D)

 

Animation 폴더에 넣는다.

 

애니메이션 목록 중 Walking 에는 Loop Time을 체크한다.

 

경고는 신경쓰지 않는다.

 

 

모델을 씬으로 불러온다.

 

애니메이션 목록 중 Idle, Jump, Walking을 모델로 drag&drop 한다.

 

Animator를 확인해 보자.

 

위 그림과 같은 트랜지션을 만든다.

 

 

Walk(Bool), Jump(Trigger) 파라미터를 만든다.

 

Idle -> Walk 트랜지션의 컨디션을 설정한다. (Walk -> true, Has Exit Time 체크 해제)

 

Walk -> Idle 트랜지션의 컨디션을 설정한다. (Walk -> false, Has Exit Time 체크 해제)

 

Any State -> Jump 트랜지션의 컨디션을 설정한다. (Jump, Has Exit Time 체크 해제)

 

 

컨트롤러 스크립트를 만든다.

 

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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Controller : MonoBehaviour
{
    private Animator animator;
 
    // Start is called before the first frame update
    void Start()
    {
        animator = GetComponent<Animator>();
    }
 
    // Update is called once per frame
    void Update()
    {
        float xInput = Input.GetAxis("Horizontal");
        float zInput = Input.GetAxis("Vertical");
 
        if (xInput != 0 || zInput != 0)
        {
            animator.SetBool("Walk"true);
        }
        else
        {
            animator.SetBool("Walk"false);
        }
 
        if (Input.GetKeyDown(KeyCode.Space))
        {
            animator.SetTrigger("Jump");
        }
    }
}
 

스크립트를 작성한다.

 

컨트롤러 스크립트를 모델에 넣는다.

 

이 상태에서 실행해 보면 Idle, Walk는 생각대로 작동하지만 Jump 후 다시 Idle이나 Walk로 돌아가지 못한다.

Jump 후 다른 애니메이션으로 가는 트랜지션이 없기 때문이다.

 

Jump -> Idle 트랜지션을 만들고 Has Exit Time을 체크한다. (컨디션은 설정하지 않는다)

 

 

다시 실행해 보자.

 

반응형
Posted by J-sean
:
반응형

하나의 객체만 존재해야 하는 싱글턴 패턴 게임 매니저 예제.

 

 

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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
using UnityEngine.SceneManagement;
using UnityEngine.UI;
 
public class GameManager : MonoBehaviour
{
    public static GameManager instance;
 
    public bool isGameover = false;
    private Text scoreText; // Awake()에서 대입.
    public Text gameoverText;   // Editor - Inspector에서 대입. 시작할 때 비활성화 된
                                // 오브젝트이기 때문에 GameObject.Find()로 찾을 수 없다.
                                // 자식 오브젝트를 찾는 방식으로만 찾을 수 있다.
    
    public int marbleCount; // 마블 생성기에서 초기화.
 
    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
 
            scoreText = GameObject.Find("ScoreText").GetComponent<Text>();
        }
        else
        {
            Debug.LogWarning("Game manager exists already.");
            Destroy(gameObject);
        }
    }
 
    // Update is called once per frame
    void Update()
    {
        if (!isGameover)
        {
            if (marbleCount <= 0)
            {
                isGameover = true;
                gameoverText.gameObject.SetActive(true);
            }
 
            scoreText.text = marbleCount + " marbles to go!!";            
        }
        else
        {
            if (Input.GetKeyDown(KeyCode.N))
            {
                isGameover = false;
                gameoverText.gameObject.SetActive(false);
                SceneManager.LoadScene(SceneManager.GetActiveScene().name);                
            }
        }
    }
}
cs

 

다른 스크립트에서 GameManager.instance.XXX 와 같은 방식으로 사용한다.

 

반응형
Posted by J-sean
: