언어/UNITY

[C# / UNITY] mummy asset를 이용하여 Rollerball test 진행하기

코린영이 2023. 3. 2. 12:52
728x90
반응형

Rollerball test을  mummy asset을 이용하여 해보기

  • rollerball 폴더 안에 meshes 폴더를 만들고 mummy prefab을 복사해 넣어준다.
    • 파일을 추가할 땐 항상 파일 명을 체크해야 한다.
    • fbx -> meshes 폴더 안에 넣어도된다.
    • FBX -> ctrl도 함께 존재하기 때문에 독립적으로 존재해야 한다.
    • asset store에서 추가해도 된다.
  • 이제 RollerAgent를 바꿔야 한다.
    • mummy가 rolleragent를 대체하도록 위치해준다. Training Area 폴더 안에 넣어준다.
    • 그냥 끌어다 놓으면 게임 오브젝트로 생성되는 것이 아니기 때문에 assets 폴더 안에 prefabs이라는 폴더를 만들어 넣어준다. original prefab을 눌러주면 된다. 그럼 게임 오브젝트화가 됐다.
    • Scene의 이름도 movemummy로 바꿔 준다.
  • mummy에도 rollerballagent처럼 component들을 추가해준다. (작업은 prefabs 안에서 해준다.)
    • Capsule collider 추가

    • RigidBody 추가한다. 아직은 따로 설정해 줄 정보는 없다.
    • 나머지 몸 부위의 mesh 정보들은 mummy의 material에서 불러올 수 있다.
      • Skinned Mesh Renderer의 materials 부분에 material을 넣어주면 된다.
    • 우리가 prefabs에서 정보들을 바꿔줬을 때의 장점은 우리가 세팅했던 그대로 화면에 추가된다. 몇 개를 추가하던지 간에
    • DecisionRequester 추가한다. (아님)
      • DecisionRequester를 추가하니 BehaviorParameter도 함께 추가가 됐다.
    • Rolleragent 였던 script를 mummyagent로 바꿔준다.
      • script를 mummyagent에 추가하려고 하니까 에러가 나면서 창이 닫힌다.
      • **script를 연결하려면 꼭 decisionrequester를 추가하기 전에 연결해야 한다.**
      • script를 수정해준다.
        • 당장은 이름만 바꿔주면 된다.
    • Rolleragent의 component를 script 연결하기 전에 추가해준다. 이 때 절대로 decisionrequester부터 추가하면 안된다. Behaviorparameter부터 추가
      • script를 연결해준다. script를 연결할 땐 꼭 script를 저장한 후에 연결해줘야 한다.
      • 타겟도 넣어준다.
      • 그리고 나서 rolleragent를 지워준다.
      • 이제 그리고 난 후 DecisionRequester를 추가해준다.
  • 이제 play를 해본다.
    • 오류가 나는데 이 전에 mummy 예제에서 힘을 가하지 않고 매 순간 위치를 업데이트 하는 방법으로 오류를 해결했기 때문에 이번에도 그렇게 해준다.
    • OnActionReceived 함수에서 결정해주면 된다.
    • 힘을 가하는 AddForce가 아닌 transform 정보를 가져와서 입력 값에 대해서 -1과 +1로 제한시켜 놓고 Vector3.forward와 Vector3.right으로 설정해 주었다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Sensors;
using Unity.MLAgents.Actuators;

public class MummyAgent : Agent 
{
    Transform tr;
    Rigidbody rb;
    
    void Start()
    {
        tr = GetComponent<Transform>();
        rb = GetComponent<Rigidbody>();
    }

    
    public Transform Target; 
    public override void OnEpisodeBegin(){ 
        if(this.transform.localPosition.y < 0){ 
            this.rb.angularVelocity = Vector3.zero; 
            this.rb.velocity = Vector3.zero; 
            this.transform.localPosition = new Vector3(0, 0.5f, 0); 
        } 

        Target.localPosition = new Vector3(Random.value * 8 - 4, 0.5f, Random.value * 8 - 4);
    }

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

    public override void CollectObservations(VectorSensor sensor) 
    {
        // Target and Agent positions
        sensor.AddObservation(Target.localPosition); // float 3
        sensor.AddObservation(this.transform.localPosition); // float 3

        // Agent velocity
        sensor.AddObservation(rb.velocity.x); // float 1
        sensor.AddObservation(rb.velocity.z); // float 1
    }

    public float forceMultiplier = 10;
    public override void OnActionReceived(ActionBuffers actionBuffers) 
    {
        // Actions, size = 2;
        float move_x = actionBuffers.ContinuousActions[0];
        float move_z = actionBuffers.ContinuousActions[1];

        float delta_x = Mathf.Clamp(move_x, -1.0f, +1.0f);
        float delta_z = Mathf.Clamp(move_z, -1.0f, +1.0f);

        Vector3 dir = (Vector3.forward * delta_z) + (Vector3.right * delta_x);
        // Vector3 dir = new Vector3(h,0,v);
        tr.position += dir * 0.1f;

        // 각속도를 0으로 해줘서 위치만 움직이게 한다.
        rb.velocity = Vector3.zero;
        rb.angularVelocity = Vector3.zero;

        float distanceToTarget = Vector3.Distance(this.transform.localPosition, Target.localPosition);

        if (distanceToTarget < 1.42f)
        {
            SetReward(1.0f); // reward +1
            EndEpisode(); 
        }

        // Fell off platform, 공이 floor 바깥으로 내려갈 때
        else if (this.transform.localPosition.y < 0)
        {
            EndEpisode(); // 에피소드 종료
        }
    }

    public override void Heuristic(in ActionBuffers actionsOut) 
    {
        var continuousActionsOut = actionsOut.ContinuousActions;
        continuousActionsOut[0] = Input.GetAxis("Horizontal"); 
        continuousActionsOut[1] = Input.GetAxis("Vertical"); 
    }
}
  • 근데 나는 왜 안움직일까,,
  • animator를 적용시키기 위해서는 entry가 움직일 때 move라는 것을 동작시켜야 할 것이다.

  • mummy monster가 발 구르면서 걷는 것을 표현하고 싶으므로
    • Controll에 mummy animation을 넣어줘야 한다.
  • 좀 더 자연스럽게 움직이게 하기 위해서는 위치만 업데이트하면 안되고, transform에 있는 rotation도 함께 업데이트 해야한다.
  • 에러 발생 시발
    •  
    • Couldn't connect to trainer on port 5004 using API version 1.5.0. Will perform inference instead. UnityEngine.Debug:Log(object)
    • 오류가 나서 pytorch를 다시 다운 받아줬다.
    • pip3 install torch torchvision torchaudio
    • 그래도 안된다.
    • Package manager 에 있는 모든 것들을 upgrade 해줬는데도 안된다.
    • Editor를 공식 버전으로 다른 버전으로 다운받았다. 그래도 안된다.
    • Heuristic 오류는 해결했다.
    • prefab에서 component를 추가하면 안되는 것 같다.
    • prefab에서는 capsule collider 만 설정해주고 나머지는 scene에서 별도로 지정해줬다.
    • 그리고 절대로 decision requester 먼저 추가하면 안된다. Behavior -> script 그리고 제일 마지막으로 decision을 추가해줘야 한다.
    • 아직까지는 움직이는 것이 부자연스럽지만 나중에 script에 rotation에 관련된 코드도 집어 넣어주면 된다.
  • 어떻게 Rotation 할지 생각하는 과정
    • 지금 mummy는 한 방향만을 바라본 채로 x, z방향으로 위치만 바꾸고 있다. 자신이 가는 방향으로 시선이 가게 끔 만들어줘야 한다.
    • 입력이 x는 1만큼, z는 3만큼일 때 그 사이를 바라보게 삼각함수를 이용하여 업데이트를 할 수도 있고, 아이디어를 생각해서 기능을 구현해보자.
    • 다른 사람의 코드를 참조하는 것도 좋은 방법이다. 펭귄 예제에서 코드를 참조해보자. 펭귄에서는 KeyCode로 입력을 받고 있다. 그리고 discreteaction으로 받아서 transform.rotate를 이용했다.
    • action의 종류가 달라서 그대로 쓰는 것에는 무리가 있지만 transform.rotate만 가져와보자.
    • 자꾸 한 방향으로 도는 문제가 발생한다. 에러 발생
  • 요점
기본적인 롤러볼 예제에서 환경을 설계. 환경에 대한 아이디어는 
여러 개의 오브젝트가 생성되고 특정 몇 개만 획득
중간에 장애물을 만들어서 점프 기능을 추가한 후 원하는 목적지 도달
대상을 밀어서 민 대상을 특정 지역으로 옮기기

 

728x90
반응형