https://github.com/Unity-Technologies/ml-agents/blob/develop/docs/Installation.md

 

ml-agents/docs/Installation.md at develop · Unity-Technologies/ml-agents

The Unity Machine Learning Agents Toolkit (ML-Agents) is an open-source project that enables games and simulations to serve as environments for training intelligent agents using deep reinforcement ...

github.com

 

https://github.com/adang1345/PythonWindows/blob/master/3.10.12/python-3.10.12-amd64-full.exe

 

PythonWindows/3.10.12/python-3.10.12-amd64-full.exe at master · adang1345/PythonWindows

Unofficial Python installers for Windows. Contribute to adang1345/PythonWindows development by creating an account on GitHub.

github.com

 

폴더를 만들어주고 결로를 지정해준다.

cmd를 켠 후 검색하면 찾을 수 있다.

pip3 install torch~=2.2.1 --index-url https://download.pytorch.org/whl/cu121

 

https://download.pytorch.org/whl/cu121

 

download.pytorch.org

pip install numpy==1.23.3

 

 

하지만 여기서 뒤에 더 추가해야 한다.

python -m pip install mlagents==1.0.0 --no-deps

 

git clone --branch release_21 https://github.com/Unity-Technologies/ml-agents.git

 

GitHub - Unity-Technologies/ml-agents: The Unity Machine Learning Agents Toolkit (ML-Agents) is an open-source project that enab

The Unity Machine Learning Agents Toolkit (ML-Agents) is an open-source project that enables games and simulations to serve as environments for training intelligent agents using deep reinforcement ...

github.com

 

 

소스트리를 활용하여 클론

 

유니티 허브에서 add를 한다.

 

패키지매니저를 열고 TextMeshPro를 설치하면 에러 2줄이 해결된다.

 

 

 

 

 

 

'산대특' 카테고리의 다른 글

2D 3Matchpuzzle - 블록 위치 찾기 테스트  (0) 2024.05.16
2D 3Matchpuzzle - 빈공간 찾기  (0) 2024.05.14
Learn Firebase  (0) 2024.05.02
2D Vertical Shooting Game 모작 최종  (0) 2024.04.08
팬텀로즈스칼렛 모작 최종  (0) 2024.04.08

https://console.firebase.google.com/?hl=ko

 

로그인 - Google 계정

이메일 또는 휴대전화

accounts.google.com

 

 

다운로드 하면 이렇게 제이슨 파일이 만들어지고 이것을 프로젝트에 넣어주면 된다.

 

Firebase SDK도 설치

압축을 풀면 이렇게 생긴다.

여기서 필요한 것을 가져다가 쓰면 된다.

현재는 FirebaseDatabase를 사용할 것이다.

가장 가까운 싱가포르 선택

 

IOS Build 선택 후 다운로드

설치 후 다시 프로젝트로 들어간다.

 

창이 나오면 Yes만 클릭하면 된다.

 

이름으로 추가 를 선택 후

com.unity.nuget.newtonsoft-json를 넣는다.

 

스크립트 를 만든후 빈 오브젝트에 할당

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

public class DBManager : MonoBehaviour
{
    [SerializeField] private string databaseUrl;

    private void Start()
    {
       FirebaseApp.DefaultInstance.Options.DatabaseUrl
            = new System.Uri(databaseUrl);
    }

}

게임정보

유저 아이디

점수

GameInfo

userId

score

이 주소를 복사 한후

여기에 넣어주면 된다.

 

새 스크립트 생성 및 수정

using Newtonsoft.Json;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameMain : MonoBehaviour
{
    [SerializeField] private string userId;
    [SerializeField] private int score;

    void Start()
    {
        SaveGame();
    }

    public void SaveGame()
    {
        Debug.Log("SaveGame");

        GameInfo gameInfo = new GameInfo(userId, score);    //저장 객체 만들기 
        string json = JsonConvert.SerializeObject(gameInfo);  //직렬화 

        Debug.Log($"{json}");

        //저장 
        DBManager.instance.SaveData(json);
    }
}

 

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

public class GameInfo
{
    public string userId;
    public int score;

    //생성자 
    public GameInfo(string userId, int score) {
        this.userId = userId;
        this.score = score;
    }
}

 

using Firebase;
using Firebase.Database;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DBManager : MonoBehaviour
{
    [SerializeField] private string databaseUrl;

    public static DBManager instance;

    private void Awake()
    {
        instance = this;    //싱글톤 
    }

    void Start()
    {
        FirebaseApp.DefaultInstance.Options.DatabaseUrl
            = new System.Uri(databaseUrl);
    }

    public async void SaveData(string json)
    {
        Debug.Log("SaveData");

        //DB의 최상단 디렉토리를 참조 
        DatabaseReference dbRef = FirebaseDatabase.DefaultInstance.RootReference;

        Debug.Log($"dbRef: {dbRef}");

        //비동기 메서드때문에 완료 될때까지 대기
        await dbRef.SetRawJsonValueAsync(json);

        Debug.Log("데이터 저장 완료");
    }
}

 

------------------------- 오류가 계속 나서 다시 시작 ------------------------

이 세개를 빼고 library, obj 같이 생성시 생기는 파일들을 모두 삭제하였다.

Firebase 폴더가 삭제되지 않으면 오픈 윈도우로 들어가서 삭제해보고

그래도 관리자 권한때문에 삭제되지 않으면 재부팅하면 된다.

 

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

public class User
{
    public string username;
    public string email;

    public User()
    {
    }

    public User(string username, string email)
    {
        this.username = username;
        this.email = email;
    }
}

 

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

public class GameInfo
{
    public string userId;
    public int score;

    //생성자 
    public GameInfo(string userId, int score)
    {
        this.userId = userId;
        this.score = score;
    }
}

 

using Firebase.Database;
using Firebase.Extensions;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class GameMain : MonoBehaviour
{
    DatabaseReference mDatabaseRef;
    void Start()
    {
        Debug.Log($"Firebase.FirebaseApp.DefaultInstance: {Firebase.FirebaseApp.DefaultInstance}");
        // Get the root reference location of the database.

        mDatabaseRef = FirebaseDatabase.DefaultInstance.RootReference;

        Debug.Log($"reference : {mDatabaseRef}");

        writeNewUser("hong", "홍길동", "hong@gmail.com");


    }

    private void writeNewUser(string userId, string name, string email)
    {
        User user = new User(name, email);
        string json = JsonUtility.ToJson(user);

        mDatabaseRef.Child("Users").SetRawJsonValueAsync(json);
        CheckAndFixDependenciesAsync();
    }

    private void CheckAndFixDependenciesAsync()
    {
        Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => {
            var dependencyStatus = task.Result;
            if (dependencyStatus == Firebase.DependencyStatus.Available)
            {
                // Create and hold a reference to your FirebaseApp,
                // where app is a Firebase.FirebaseApp property of your application class.
                var app = Firebase.FirebaseApp.DefaultInstance;
                Debug.Log($"app : {app}");


                // Set a flag here to indicate whether Firebase is ready to use by your app.
            }
            else
            {
                UnityEngine.Debug.LogError(System.String.Format(
                  "Could not resolve all Firebase dependencies: {0}", dependencyStatus));
                // Firebase Unity SDK is not safe to use here.
            }
        });
    }

}

 

using Firebase;
using Firebase.Database;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DBManager : MonoBehaviour
{
    [SerializeField] private string databaseUrl;

    public static DBManager instance;

    private void Awake()
    {
        instance = this;    //싱글톤 
    }


    public void Init()
    {
        FirebaseApp.DefaultInstance.Options.DatabaseUrl
            = new System.Uri(databaseUrl);

        FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => {

            FirebaseApp app = FirebaseApp.DefaultInstance;

            if (app != null)
            {
                Debug.Log("DefaultInstance를 찾을수 없습니다.");
            }
            else
            {
                Debug.Log("초기화가 정상적으로 되었습니다.");
            }
        });
    }

    public async void SaveData(string json)
    {
        Debug.Log("SaveData");

        //DB의 최상단 디렉토리를 참조 
        DatabaseReference dbRef = FirebaseDatabase.DefaultInstance.RootReference;

        Debug.Log($"dbRef: {dbRef}");

        //비동기 메서드때문에 완료 될때까지 대기
        await dbRef.SetRawJsonValueAsync(json);

        Debug.Log("데이터 저장 완료");
    }
}

 

 

 

지정된 파이어베이스 주소로 데이터가 잘 들어옴을 확인할 수 있다.

 

 

 

 

'산대특' 카테고리의 다른 글

2D 3Matchpuzzle - 빈공간 찾기  (0) 2024.05.14
Download Python  (0) 2024.05.08
2D Vertical Shooting Game 모작 최종  (0) 2024.04.08
팬텀로즈스칼렛 모작 최종  (0) 2024.04.08
2D Airplane - 원근감있는 무한 배경 만들  (0) 2024.03.17

한손만 복사본을 저장했다면 이버튼을 눌러 반대손도 적용 할 수 있다.

 

 

 

 


그런데 공을 다시 잡으면 손이 저장된 위치로 가기 때문에 공이 회전한다.

이 공의 회전을 고정하기 위한 작업을 실행했다.

모든 작업들은 오른손을 했으면 왼손도 적용해주면 된다.

 

 

이제 공을 다시 잡아도 돌지 않는다.

 

 

 

이번에는 책에 적용해 보았다.

아까 설명한 오른손으로 이미지를 잡고 왼손을 복사하여 적용된 모습이고

 

생성된 Hand Grab Pose에 Box Grab Surface 컴포너트를 추가한 후

박스를 정하는데

이는 손목을 기준점으로 잡힌다.

 

그후

 

 

여기 Follow Surface를 설정하면 테스트도 해볼 수 있다.

 

 

 

중력을 키면 책이 책상에 떨어진다.

프로젝트에 있는 HandVelocityCalculator 추가 한후

인트렉터에 넣어준다.

 

주의 !

책의 y좌표보다 낮아야지 리스폰이 된다.

즉, 기존의 좌표보다 낮을 경우 리스폰이 된다.

 

 

'산대특 > VRAR' 카테고리의 다른 글

[VR] Transformer Interaction  (0) 2024.04.23
[VR] Locomotion(Teleport)  (0) 2024.04.23
[VR]총 잡기 - HandGun Grab  (1) 2024.04.18
Create Ghost Reticles  (0) 2024.04.18
Oculus Settings and Grab  (0) 2024.04.17

 

 

 

실행 후 1, 2번을 한후 나와서 3번 4번과정

이렇게 에셋이 생성된다.

Max의 옵션을 Locked에서 Constrained로 변경한후 관절을 변경 가능

 

 

 

손의 잔상을 없애기 위해 Hand Grab Interactable 을 비활성화 or 삭제한다.

 

이렇게 되니 손가락이 움직이면 복사가 되었다 알고보니 이 옵션을 꺼주어야 한다.

 

원하는 관절을 고정및 변경 시킬 수 있다.

 

이번엔 컨트롤러에도 설정 할 것이다.

 

 

이제 콘솔에 총을 잡으면 출력이 되도록 할것이다.

 

using Oculus.Interaction;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HandGun : MonoBehaviour
{

    [SerializeField] private InteractableUnityEventWrapper wrapper;
    private bool isFire = false;

    void Start()
    {
        wrapper.WhenSelect.AddListener(() =>
        {
            Debug.Log("총을 잡았습니다.");
            isFire = true;
        });
        wrapper.WhenUnselect.AddListener(() =>
        {
            Debug.Log("총을 놓았습니다.");
            isFire = false;
        });

    }

    void Update()
    {
        var indexTriggerVal = OVRInput.Get(OVRInput.Axis1D.SecondaryIndexTrigger);

        //인덱스 트리거는 0~1 즉 강도조절
        if (indexTriggerVal > 0.5f)
        {
            isFire = true;
            Debug.Log("발사");
        }
        else if (indexTriggerVal == 0)
        {
            if (isFire)
            {
                //초기화
                isFire = false;

            }
        }

    }
}

 

인덱스트리거(검지를) 누르면 발사 또 한 출력된다.

 

'산대특 > VRAR' 카테고리의 다른 글

[VR] Locomotion(Teleport)  (0) 2024.04.23
[VR]반대손 복사와 회전 및 던지기  (0) 2024.04.19
Create Ghost Reticles  (0) 2024.04.18
Oculus Settings and Grab  (0) 2024.04.17
Reticle  (0) 2024.04.16

https://developer.oculus.com/documentation/unity/unity-isdk-create-ghost-reticles/

Option A에 관한 설명을 읽으면 된다.

 

Create Ghost Reticles | Oculus Developers

 

developer.oculus.com

 

 

 

큐브의 색이 흰색이면 레티클이 흰색 이미지이므로 안보일 수 있다.

임의로 메터리얼을 생성하여 큐브의 색을 변화시키면

레티클을 보다 가시적으로 확인할 수 있다.

 

 


 

 

https://developer.oculus.com/documentation/unity/unity-isdk-creating-handgrab-poses/

 

Create a Hand Grab Pose (PC) | Oculus Developers

 

developer.oculus.com

 

 

 

왼손이나 오른손으로 지정후

플레이를 한다.

1. 구체에 손을 얹고 Record HandGrabPose를 누르면 흰색으로 스캔이 된다.

2. 그 후 아래에 있는 Save To Collection을 누르면 에셋에 저장이 된다.

 

 

이렇게 에셋에 새로운 폴더와 함께 저장이된다.

새로 만들어진 컬렉션을 열어보면 이러한 프로퍼티를 볼 수 있다.

하이어라이키에는 이런식으로 생긴다.

 

Sphere안에있는 인터렉터를 지우고

HandGrabInteractable를 누른채로 실행하면 

근처에서 잡으면 저장된 손모양이 적용된다.

 

 

 

 

 

 

 

 

 

세이브한 모양이 마음에 들지 않으면 관절을 직접 조절해서 모양을 맞춰줄 수 있다.

'산대특 > VRAR' 카테고리의 다른 글

[VR]반대손 복사와 회전 및 던지기  (0) 2024.04.19
[VR]총 잡기 - HandGun Grab  (1) 2024.04.18
Oculus Settings and Grab  (0) 2024.04.17
Reticle  (0) 2024.04.16
VR로 360도 동영상 다운로드 후 적용하기 + 오큘러스 적용  (0) 2024.04.16

Ctrl + K 혹은

 

 

 

 


 

 

 

 

 

 

'Study > ' 카테고리의 다른 글

[Tip] Object.FindObjectOfType  (0) 2024.06.11
[Tip] RenderSettings  (0) 2024.06.05
[팁] Slider.onValueChanged  (0) 2024.06.05
[Tip] 3D 프로젝트에서 SpriteAtals 패킹하는 법  (0) 2024.05.23
[Tip] CameraShaker  (0) 2024.03.29

Panel을 UI로 만든후 시점이 맞았을때

패널을 보이게 하고 그렇지 않을 경우 보이지 않게 한다.

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem.LowLevel;

public class Car : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IGvrPointerHoverHandler
{
    private Coroutine coroutine;
    public GameObject menuUI;
    public void OnGvrPointerHover(PointerEventData eventData)
    {
        //Debug.Log("Hover");
    }


    public void OnPointerEnter(PointerEventData eventData)
    {
        coroutine = StartCoroutine(CoClick());
        Debug.Log("Enter");
        menuUI.SetActive(true);

    }


    private IEnumerator CoClick()
    {
        float delta = 0f;
        while (true)
        {
            delta += Time.deltaTime;
            if(delta >= 3)
            {
                Debug.LogFormat("Clicked , {0}", delta);
                delta = 0f;
            }
            yield return null;
        }
    }
    public void OnPointerExit(PointerEventData eventData)
    {
        Debug.Log("Exit");
        menuUI.SetActive(false);
        StopCoroutine(coroutine);
    }
}

 

 

플레이어의 위치에 따라서 패널의 방향이 변경되는데

이는 캔버스안에 넣어주어야 한다.

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

public class Billboard : MonoBehaviour
{
    

    // Update is called once per frame
    void Update()
    {
        this.transform.LookAt(Camera.main.transform.position);
    }
}

 

 

 

 

IPointerEnterHandler, IPointerExitHandler는 유니티엔진에 있는 메소드이지만,

IGvrPointerHoverHandler는 직접 만들어준 메소드이다.

 


 

커스텀 래티클 만들기

 

이번엔 기존에 사용했던 빨간색 래티클을 없애고 새로운 래티클을 만들어 주려 한다.

 

Main Camera의 GvrRecticlePointer와 GvrPointerPhysicsRaycast를 비활성화  한다.

 

Main Camera자식으로 Canvas를 생성하고  다음과  같이  속성을  변경  한다

 

 

그리고 적절한 이미지를 붙여준 후 Fill효과를 넣어줄 것이므로

복사해서 앞과 뒤를 만든다.

Back은 알파 값을 조금 줄여주고

Front는 filled, Radial 360, Top으로 변경하였다.

 

우선 래티클이 변경되었으니 인지를 하게 만드는 것이 우선인데

차의 레이어는 7 메뉴의 레이어는 6으로 만들어서

인지할수 있도록 하였다.

 

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem.LowLevel;

public class Car : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IGvrPointerHoverHandler
{
    private static Car instance;
    private Coroutine coroutine;
    public GameObject menuUI;

    private void Start()
    {
        //menuUI.SetActive(false);
    }

    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
        }
        else
        {
            Debug.LogWarning("Duplicate instance of Car found!");
            Destroy(gameObject); // 중복된 인스턴스 제거
        }
    }

    public static Car GetInstance()
    {
        return instance;
    }



    public void OnGvrPointerHover(PointerEventData eventData)
    {
        //Debug.Log("Hover");
    }


    public void OnPointerEnter(PointerEventData eventData)
    {
        coroutine = StartCoroutine(CoClick());
        Debug.Log("Enter");
        menuUI.SetActive(true);
        Debug.Log("이벤트 대상: " + eventData.pointerEnter.name);
    }


    private IEnumerator CoClick()
    {
        float delta = 0f;
        while (true)
        {
            delta += Time.deltaTime;
            if(delta >= 3)
            {
                Debug.LogFormat("Clicked , {0}", delta);
                delta = 0f;
            }
            yield return null;
        }
    }
    public void OnPointerExit(PointerEventData eventData)
    {
        Debug.Log("Exit");
        menuUI.SetActive(false);
        StopCoroutine(coroutine);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class EyeCast : MonoBehaviour
{
    private Transform trans;
    private Ray ray;
    private RaycastHit hit;
    public float dist = 10f;
    public Image gauge;
    bool isOpenedUIMenu = false;
    private Car carInstance; // 전역 변수로 선언

    void Start()
    {
        this.trans = this.GetComponent<Transform>();
        carInstance = Car.GetInstance(); // 전역 변수에 할당
        carInstance.menuUI.SetActive(false);
    }

    void Update()
    {
        ray = new Ray(this.trans.position, this.trans.forward);
        Debug.DrawRay(ray.origin, ray.direction * this.dist, Color.green);

        var mask = 1 << 6 | 1 << 7; // 레이어 마스크 설정
        if (Physics.Raycast(ray, out hit, dist, mask))
        {
            this.GazeButton();
        }
        else
        {
            ReleaseButton();
            // 여기에서 메뉴 닫는 동작을 추가할 수 있습니다.
            // 예: carInstance.menuUI.SetActive(false);
        }
    }

    private void GazeButton()
    {
        if (this.hit.collider.gameObject.layer == 7 && carInstance != null)
        {
            carInstance.menuUI.SetActive(true);
        }
    }

    private void ReleaseButton()
    {
        if (this.hit.collider == null || this.hit.collider.gameObject == null)
        {
            carInstance.menuUI.SetActive(false);
        }
    }
}

 

보기와 같이 코드에서 차뿐만이 아닌 버튼에도 레이어를 설정해두었기 때문에

버튼쪽에 래티클을 갖다 두어도 창이 사라지지 않는다.

 

레이어를 인식해서 게이지를 채우고

나갔을 때 초기화

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class EyeCast : MonoBehaviour
{
    private Transform trans;
    private Ray ray;
    private RaycastHit hit;
    public float dist = 10f;
    public Image gauge;
    bool isOpenedUIMenu = false;
    private Car carInstance; // 전역 변수로 선언
    private float delta;

    void Start()
    {
        this.trans = this.GetComponent<Transform>();
        carInstance = Car.GetInstance(); // 전역 변수에 할당
        carInstance.menuUI.SetActive(false);
    }

    void Update()
    {
        ray = new Ray(this.trans.position, this.trans.forward);
        Debug.DrawRay(ray.origin, ray.direction * this.dist, Color.green);

        var mask = 1 << 6 | 1 << 7; // 레이어 마스크 설정
        if (Physics.Raycast(ray, out hit, dist, mask))
        { 
            //gauge.fillAmount = 0;
            StartCoroutine(GazeButton());
        }
        else
        {
            delta = 0;
            gauge.fillAmount = 0;

            StartCoroutine(ReleaseButton());
           

            // 여기에서 메뉴 닫는 동작을 추가할 수 있습니다.
            // 예: carInstance.menuUI.SetActive(false);
        }
    }
    IEnumerator GazeButton()
    {
        
        if (this.hit.collider.gameObject.layer == 7 && carInstance != null)
        {
            carInstance.menuUI.SetActive(true);

            this.delta += Time.deltaTime;
            gauge.fillAmount = delta / 3;
        }
        else
        {

        }
        yield return null;
    }

    IEnumerator ReleaseButton()
    {
        if (this.hit.collider == null || this.hit.collider.gameObject == null)
        {

            carInstance.menuUI.SetActive(false);


        }
        yield return null;
    }
}

 

 

게이지를 채워서 예를 누르면 예가 찍히고

아니요를 누르면 창을 종료 시켰다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class EyeCast : MonoBehaviour
{
    private Transform trans;
    private Ray ray;
    private RaycastHit hit;
    public float dist = 10f;
    public Image gauge;
    private Car carInstance; // 전역 변수로 선언
    private float delta;
    public Button clickYes;
    public Button clickNo;
    private bool isYesClicked = false;
    private Coroutine coroutine;

    void Start()
    {
        this.trans = this.GetComponent<Transform>();
        carInstance = Car.GetInstance(); // 전역 변수에 할당
        carInstance.menuUI.SetActive(false);
    }

    void Update()
    {
        

        ray = new Ray(this.trans.position, this.trans.forward);
        Debug.DrawRay(ray.origin, ray.direction * this.dist, Color.green);

        var mask = 1 << 7; // 레이어 마스크 설정
        if (Physics.Raycast(ray, out hit, dist, mask))
        {
            //gauge.fillAmount = 0;
            
              if(coroutine != null)
            {
                StopCoroutine(coroutine);

            }

            StartCoroutine(GazeButton());

        }
       

        else if (Physics.Raycast(ray, out hit, dist, 1 << 6))
        {
            //gauge.fillAmount = 0;
            //StartCoroutine(GazeButton());
       
         
              
                StartCoroutine(GazeButton());
                StartCoroutine(ClickButton());

          
           
        }

        else
        {
            if (isYesClicked == true)
            {
                isYesClicked = false;
            }
            delta = 0;
            gauge.fillAmount = 0;

            StartCoroutine(ReleaseButton());


            // 여기에서 메뉴 닫는 동작을 추가할 수 있습니다.
            // 예: carInstance.menuUI.SetActive(false);
        }
    }
    IEnumerator GazeButton()
    {

        if (this.hit.collider.gameObject.layer == 7 || this.hit.collider.gameObject.layer == 6 && carInstance != null)
        {
            carInstance.menuUI.SetActive(true);

            this.delta += Time.deltaTime;
            gauge.fillAmount = delta / 2;
        }
        else
        {
            delta = 0;
            gauge.fillAmount = 0;
        }
        yield return null;
    }

    IEnumerator ClickButton()
    {
        if (this.hit.collider.gameObject.layer == 6)
        {
            this.delta += Time.deltaTime;
            if (gauge.fillAmount >= 1)
            {
                if (hit.collider.gameObject.name == "ButtonYes" && isYesClicked == false)
                {
                    isYesClicked = true;
                    Debug.Log("예를 입력하였습니다");
                }
                if (hit.collider.gameObject.name == "ButtonNo")
                {
                    carInstance.menuUI.SetActive(false);
                }
            }
        }
        yield return null;
    }


    IEnumerator ReleaseButton()
    {
        if (this.hit.collider == null || this.hit.collider.gameObject == null)
        {
            carInstance.menuUI.SetActive(false);
        }
        yield return null;
    }
}

 

 

 

마지막으로 레이어 이동시 게이지를 초기화 시키도록 구현하기 위해 수정을 하였다.

using System.Collections;
using UnityEngine;
using UnityEngine.UI;

public class EyeCast : MonoBehaviour
{
    private Transform trans;
    private RaycastHit hit;
    public float dist = 10f;
    public Image gauge;
    public Car car;
    public Button clickYes;
    public Button clickNo;

    private int lastLayer = 0;
    private string lastButton = "";

    float carCount = 0;
    float yesButtonCount = 0;
    float noButtonCount = 0;


    void Start()
    {
        this.trans = this.GetComponent<Transform>();
        car.menuUI.SetActive(false); // 처음엔 menu가 닫힌채로 시작

        StartCoroutine(ReleaseButton());
        StartCoroutine(GazeButton());
        StartCoroutine(ClickButton());
    }


    IEnumerator GazeButton()
    {
        while (true)
        {
            Ray ray = new Ray(this.trans.position, this.trans.forward);
            if (Physics.Raycast(ray, out hit, dist, 1 << 7)) // Layer 7 = Car
            {
                if (lastLayer != 7 && carCount < 2)
                {
                    carCount = 0;
                    gauge.fillAmount = 0;
                }
                else if (lastLayer != 7 && carCount >= 2)
                {
                    carCount = 2;
                    gauge.fillAmount = 1;
                }
                else if (lastLayer !=7 && carCount >= 2 && !car.menuUI.gameObject.activeSelf) // No 버튼으로 menu가 닫혔을때, 다시 초기화
                {
                    carCount = 0;
                    gauge.fillAmount = 0;
                }
                lastLayer = 7;
                lastButton = "";

                //car.menuUI.SetActive(true);  -> Car를 응시 했을 때, 바로 메뉴 오픈

                if (carCount < 2)
                {
                    carCount += Time.deltaTime;
                    gauge.fillAmount = carCount / 2;

                    if (gauge.fillAmount >= 1)
                    {
                        car.menuUI.SetActive(true); // -> Car를 응시 했을 때, 2초 후 메뉴 오픈
                    }
                }
            }
            yield return null;
        }
    }



    IEnumerator ClickButton()  // ButtonYes와 ButtonNo 콜라이더 사이에 공백이 있으면 ResetCountAndGauge 메서드(메뉴 닫힘 및 초기화)가 실행 될 수 있으니 유의
    {
        while (true)
        {
            Ray ray = new Ray(this.trans.position, this.trans.forward);
            if (Physics.Raycast(ray, out hit, dist, 1 << 6)) //Layer 6 = Button
            {
                if (hit.collider.gameObject.name == "ButtonYes")
                {
                    lastLayer = 6;

                    if (lastButton != "ButtonYes" && yesButtonCount < 2)
                    {
                        lastButton = "ButtonYes";
                        yesButtonCount = 0;
                        gauge.fillAmount = 0;
                    }
                    else if (lastButton != "ButtonYes" && yesButtonCount >= 2)
                    {
                        lastButton = "ButtonYes";
                        yesButtonCount = 2;
                        gauge.fillAmount = 1;
                    }
                    if (yesButtonCount < 2)
                    {
                        yesButtonCount += Time.deltaTime;
                        gauge.fillAmount = yesButtonCount / 2;

                        if (gauge.fillAmount >= 1)
                        {
                            Debug.Log("예를 입력하였습니다");
                            car.uiText.text = "차량 정보 확인"; // UI Text 변경
                        }
                    }
                }
                else if (hit.collider.gameObject.name == "ButtonNo")
                {
                    lastLayer = 6;

                    if (lastButton != "ButtonNo")
                    {
                        lastButton = "ButtonNo";
                        noButtonCount = 0;
                        gauge.fillAmount = 0;
                    }
                    if (noButtonCount < 2)
                    {
                        noButtonCount += Time.deltaTime;
                        gauge.fillAmount = noButtonCount / 2;

                        if (gauge.fillAmount >= 1)
                        {
                            car.menuUI.SetActive(false);

                            // Count 및 Text 초기화
                            car.uiText.text = "차량의 정보를 보시겠습니까?"; 
                            carCount = 0;
                            yesButtonCount = 0;
                            noButtonCount = 0;
                            Debug.Log("아니오를 입력하였습니다. 메뉴를 종료합니다.");
                        }
                    }
                }
            }
            yield return null;
        }
    }


    IEnumerator ReleaseButton()
    {
        while (true)
        {
            Ray ray = new Ray(this.trans.position, this.trans.forward);
            if (!Physics.Raycast(ray, out hit, dist))
            {
                //car.menuUI.SetActive(false); -> Car, 버튼 어느것도 응시하지 않으면 menu 닫음
                gauge.fillAmount = 1;
                lastLayer = 0;
                lastButton = "";
            }
            yield return null;
        }
    }





}

 

 

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.UI;
using TMPro;

public class Car : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IGvrPointerHoverHandler
{

    private Coroutine coroutine;
    public GameObject menuUI;
    public Text uiText; // menuUI Text

    private void Start()
    {
        //menuUI.SetActive(false);
    }



    public void OnGvrPointerHover(PointerEventData eventData)
    {
        //Debug.Log("Hover");
    }


    public void OnPointerEnter(PointerEventData eventData)
    {
        //coroutine = StartCoroutine(CoClick());
        //Debug.Log("Enter");
        ////menuUI.SetActive(true);
        //Debug.Log("이벤트 대상: " + eventData.pointerEnter.name);
    }


    private IEnumerator CoClick()
    {
        float delta = 0f;
        while (true)
        {
            delta += Time.deltaTime;
            if(delta >= 3)
            {
                Debug.LogFormat("Clicked , {0}", delta);
                delta = 0f;
            }
            yield return null;
        }
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        //Debug.Log("Exit");
        //menuUI.SetActive(false);
        //StopCoroutine(coroutine);
    }
}

rhttps://www.youtube.com/watch?v=1BtSgFkrkY8

 

 

360도 회전가능하게 만들어진 영상을 다운로드

www. 다음에 앞에 ss를 붙이면 다운로드 사이트로 갈 수 있다.

 

 

https://gist.github.com/unity3dcollege/d059b376f7461d908107e97534ed9e04

 

InvertedSphere.cs

GitHub Gist: instantly share code, notes, and snippets.

gist.github.com

이 사이트의 스크립트를 복사 한후 스크립트를 만들어준다.

 

 

 

InvertedSphere 스크립트는  Editor폴더에  넣어주어야 한다.

 

그 후 메뉴에서 GameObject/Create Other/Inverted Sphere 를 선택해 구체를 생성한다.

 

구체에 Video Player컴포넌트를  부착 하고 Source에 영상을 넣자

GvrEditorEmulator와  비슷한
Editor에서만 동작하는 마우스를 움직여 카메라를 조작하는 스크립트 작성

 

그 후 메인카메라의 좌표를 0, 0 , 0으로 맞춰주어야 한다.

 

새로운 메터리얼을 만들고 Shader를  Unlit/Texture로  변경한다

 

Sphere안에 카메라를 넣어주고 해당 메타리얼을 넣어준다.

 

 

1. 앞서 만들었던 HMDEmulator를  Main Camera에  컴포넌트로 추가

 

2. Main Camera에  Tracked Pose Driver컴포넌트를  부착

 

 

 

 

만들어진 프로그램은 간이식 장치(안드로이드)로 apk로 추출후 테스해볼 수 있다.

 

 

 

'산대특 > VRAR' 카테고리의 다른 글

Oculus Settings and Grab  (0) 2024.04.17
Reticle  (0) 2024.04.16
캐릭터 이동지점 변경시 VR시점 변경  (0) 2024.04.12
VR Setting 문제  (0) 2024.04.12
VR Setting 문제  (0) 2024.04.12

 

시점을 변경하고 자동차에 콜라이더를 넣어서

점의 레이캐스트가 닿을 시 인지할 수 있도록 하였다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem.LowLevel;

public class Car : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IGvrPointerHoverHandler
{
    private Coroutine coroutine;

    public void OnGvrPointerHover(PointerEventData eventData)
    {
        //Debug.Log("Hover");
    }


    public void OnPointerEnter(PointerEventData eventData)
    {
        coroutine = StartCoroutine(CoClick());
        Debug.Log("Enter");
    }


    private IEnumerator CoClick()
    {
        float delta = 0f;
        while (true)
        {
            delta += Time.deltaTime;
            if(delta >= 3)
            {
                Debug.LogFormat("Clicked , {0}", delta);
                delta = 0f;
            }
            yield return null;
        }
    }
    public void OnPointerExit(PointerEventData eventData)
    {
        Debug.Log("Exit");
        StopCoroutine(coroutine);
    }
}

 

 

플레이어가 지정한 지정한 위치에 도달하면 방향을 전환하여

다음 목적지를 향해 이동하고 그에 맞춰 VR의 시선이 달라진다.

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

public class Player : MonoBehaviour
{
    public Transform[] wayPoints;
    private int nextIdx = 0;
    private float speed = 2f;
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        MoveWayPoint();
    }

    public void MoveWayPoint()
    {
        Vector3 dir = wayPoints[nextIdx].position - this.transform.position;
        // 회전 각도
        Quaternion rot = Quaternion.LookRotation(dir);

        this.transform.Translate(Vector3.forward * speed * Time.deltaTime);

        this.transform.rotation = Quaternion.Slerp(this.transform.rotation, rot, Time.deltaTime * speed);

    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.CompareTag("WayPoint"))
        {
            nextIdx++;
            if(nextIdx == 4)
            {
                nextIdx = 0;
            }
        }
    }



}

 

'산대특 > VRAR' 카테고리의 다른 글

Oculus Settings and Grab  (0) 2024.04.17
Reticle  (0) 2024.04.16
VR로 360도 동영상 다운로드 후 적용하기 + 오큘러스 적용  (0) 2024.04.16
VR Setting 문제  (0) 2024.04.12
VR Setting 문제  (0) 2024.04.12

VR 셋팅시 오류가 발생

 

 

 

Project Settings - Player - Other Settings

 

 

Publishing Settings

 

그 후 스크립트를 수정한다

 

 

 

//-----------------------------------------------------------------------
// <copyright file="CardboardStartup.cs" company="Google LLC">
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------

using Google.XR.Cardboard;
using UnityEngine;

/// <summary>
/// Initializes Cardboard XR Plugin.
/// </summary>
public class CardboardStartup : MonoBehaviour
{
    /// <summary>
    /// Start is called before the first frame update.
    /// </summary>
    public void Start()
    {
        // Configures the app to not shut down the screen and sets the brightness to maximum.
        // Brightness control is expected to work only in iOS, see:
        // https://docs.unity3d.com/ScriptReference/Screen-brightness.html.
        Screen.sleepTimeout = SleepTimeout.NeverSleep;
        Screen.brightness = 1.0f;

        // Checks if the device parameters are stored and scans them if not.
        if (!Api.HasDeviceParams())
        {
            Api.ScanDeviceParams();
        }
    }

    /// <summary>
    /// Update is called once per frame.
    /// </summary>
    public void Update()
    {
        if (Api.IsGearButtonPressed)
        {
            Api.ScanDeviceParams();
        }

        if (Api.IsCloseButtonPressed)
        {
            Application.Quit();
        }

        if (Api.IsTriggerHeldPressed)
        {
            Api.Recenter();
        }

        if (Api.HasNewDeviceParams())
        {
            Api.ReloadDeviceParams();
        }

#if !UNITY_EDITOR
        Api.UpdateScreenParams();
#endif
    }
}

CardboardStartup.cs

 

apply plugin: 'com.android.library'
**APPLY_PLUGINS**

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation 'androidx.appcompat:appcompat:1.0.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'com.google.android.gms:play-services-vision:15.0.2'
    implementation 'com.google.android.material:material:1.0.0'
    implementation 'com.google.protobuf:protobuf-javalite:3.8.0'
    


**DEPS**}

android {
    ndkPath "**NDKPATH**"

    compileSdkVersion **APIVERSION**
    buildToolsVersion '**BUILDTOOLS**'

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_11
        targetCompatibility JavaVersion.VERSION_11
    }

    defaultConfig {
        minSdkVersion **MINSDKVERSION**
        targetSdkVersion **TARGETSDKVERSION**
        ndk {
            abiFilters **ABIFILTERS**
        }
        versionCode **VERSIONCODE**
        versionName '**VERSIONNAME**'
        consumerProguardFiles 'proguard-unity.txt'**USER_PROGUARD**
    }

    lintOptions {
        abortOnError false
    }

    aaptOptions {
        noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ')
        ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~"
    }**PACKAGING_OPTIONS**
}
**IL_CPP_BUILD_SETUP**
**SOURCE_BUILD_SETUP**
**EXTERNAL_SOURCES**

mainTemplate

 

org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M
org.gradle.parallel=true
unityStreamingAssets=**STREAMING_ASSETS**
**ADDITIONAL_PROPERTIES**
android.enableJetifier=true
android.useAndroidX=true

gradleTemplate

 

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.unity3d.player"
    xmlns:tools="http://schemas.android.com/tools">
    <application android:requestLegacyExternalStorage="true">
        <activity android:name="com.unity3d.player.UnityPlayerActivity"
                  android:theme="@style/UnityThemeSelector">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
        </activity>
    </application>
</manifest>
​

AndroidManifest

 

 

그 후 수정이 다 되었으면

씬을 올리고 (단, 씬이 많으면 빌드되야 할 부분이 맨 위로 올라가야 함)플랫폼 확인하고 build를 한 후저장 위치를 정하면 apk가 만들어진다.

 

'산대특 > VRAR' 카테고리의 다른 글

Oculus Settings and Grab  (0) 2024.04.17
Reticle  (0) 2024.04.16
VR로 360도 동영상 다운로드 후 적용하기 + 오큘러스 적용  (0) 2024.04.16
캐릭터 이동지점 변경시 VR시점 변경  (0) 2024.04.12
VR Setting 문제  (0) 2024.04.12

+ Recent posts