Perspective

  • 원근감 표현
  • 3D라 생각함

Orthographic

  • 원근감 표현하지 않음
  • 2D라 생각함

'Words' 카테고리의 다른 글

Function and Method  (0) 2021.03.30
ASO(App Store Optimization)  (0) 2021.03.30
Instance  (0) 2021.03.15
[Unity] Abstract, Virtual  (0) 2021.03.05
Fade In / Out  (0) 2021.03.05
블로그 이미지

RIsN

,

DOTween 관련 정리

Unity 2021. 3. 6. 12:54

메모

  • Scene을 전환할 때마다 DOTween을 전부 제거(KillAll)할 것
    • 제거하지 않으면 Warning이 뜰 때가 있음

'Unity' 카테고리의 다른 글

Input.multiTouchEnabled  (0) 2021.05.04
Application.targetFrameRate  (0) 2021.05.04
[My Style] Dictator 구조  (0) 2021.03.05
[My Style] 유니티 씬 구조  (0) 2021.03.05
[My Style] 유니티 프로젝트 구조  (0) 2021.03.05
블로그 이미지

RIsN

,

[Unity] Abstract, Virtual

Words 2021. 3. 5. 20:42

Abstract

  • 클래스에 있는 이 메소드는 꼭 구현해야 함
  • 클래스도 Abstract여야 함

Virtual

  • 클래스에 있는 이 메소드는 꼭 구현하지 않아도 됨

'Words' 카테고리의 다른 글

Function and Method  (0) 2021.03.30
ASO(App Store Optimization)  (0) 2021.03.30
Instance  (0) 2021.03.15
[UNITY] Perspective, Orthographic  (0) 2021.03.06
Fade In / Out  (0) 2021.03.05
블로그 이미지

RIsN

,

Fade In / Out

Words 2021. 3. 5. 20:22

페이드 인(Fade In)

  • (검은 / 하얀) 화면이 없어지는 것
  • 씬이 시작할 때

페이드 아웃(Fade Out)

  • (검은 / 하얀) 화면으로 바뀌는 것
  • 씬이 끝날 때

'Words' 카테고리의 다른 글

Function and Method  (0) 2021.03.30
ASO(App Store Optimization)  (0) 2021.03.30
Instance  (0) 2021.03.15
[UNITY] Perspective, Orthographic  (0) 2021.03.06
[Unity] Abstract, Virtual  (0) 2021.03.05
블로그 이미지

RIsN

,

[My Style] Dictator 구조

Unity 2021. 3. 5. 18:38

중복되는 구조

  • 어플리케이션 실행 도중 하나만 존재하고, 사라지지 않는 독재자(Dictator) 스크립트
  • 씬을 로드하는 것은 이 독재자의 행동
  • 각 씬의 Ruler를 로드하는 것도 이 독재자의 행동
  • 각 씬 간의 이동에서 사용하는 스테이터스 변수도 가지고 있을 때가 있음
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class Dictator : MonoBehaviour
{
    // : 0 Awake
    private void Awake()
    {
        // :: Check Overlap
        int hashThis = this.GetHashCode();
        Dictator dictatorFound = GameObject.FindObjectOfType<Dictator>();
        int hashFound = dictatorFound.GetHashCode();
        if (hashThis != hashFound)
            Object.Destroy(dictatorFound.gameObject);

        // ::
        DontDestroyOnLoad(this.gameObject);

        // :: Init
        this.Init();
    }

    // : Status

    // : Init
    private void Init()
    {
        // :: Debug
        Debug_AppName();
        Debug_AppVersion();

        // :: Init Complete
        Debug_Init(this.ToString());

        // :: Load Scene
        LoadScene(Enum.eScene.INTRO);
    }

    // : Load
    public static void LoadScene(Enum.eScene eScene)
    {
        int sceneID = (int)eScene;
        AsyncOperation sync = SceneManager.LoadSceneAsync(sceneID);
        sync.completed += scene =>
        {
            if (scene.isDone)
                switch(eScene)
                {
                    case Enum.eScene.INTRO:
                        InitScene<Intro_Ruler>();
                        break;
                }
        };
    }

    // : Init
    public static void InitScene<T>() where T : Ruler
    {
        var Ruler = GameObject.FindObjectOfType<T>();
        Ruler.Init();
    }

    // : Debug
    public static void Debug_AppName()
    {
        string log = string.Format(":: App Name : {0}", Application.productName);
        Debug.Log(log);
    }
    public static void Debug_AppVersion()
    {
        string log = string.Format(":: App Version : {0}", Application.version);
        Debug.Log(log);
    }
    public static void Debug_CheckDictator()
    {
        Dictator dictator = GameObject.FindObjectOfType<Dictator>();
        if (dictator == null)
            SceneManager.LoadScene((int)Enum.eScene.DICTATOR);
    }
    public static void Debug_Init(string scriptName)
    {
        string log = string.Format(":: {0} Init Complete", scriptName);
        Debug.Log(log);
    }
}

 

'Unity' 카테고리의 다른 글

Application.targetFrameRate  (0) 2021.05.04
DOTween 관련 정리  (0) 2021.03.06
[My Style] 유니티 씬 구조  (0) 2021.03.05
[My Style] 유니티 프로젝트 구조  (0) 2021.03.05
[Unity] 로컬 서버 연결 테스트 케이스  (0) 2021.02.21
블로그 이미지

RIsN

,

중복되는 구조

  • Ruler : Ruler 상속
    • 모든 것을 관리하는 관리자
    • 실제 기능을 가지고 있지는 않음
    • Dictator에 의해 Init 됨
  • UIChief : Chief 상속
    • UI를 관리하는 관리자
    • 실제 UI의 기능이 정의되어 있음
    • Ruler에 의해 Init 됨
  • GOChief : Chief 상속
    • 게임 오브젝트(2D 혹은 3D)를 관리하는 관리자
    • 실제 게임 오브젝트의 기능이 정의되어 있음
    • Ruler에 의해 Init 됨

'Unity' 카테고리의 다른 글

DOTween 관련 정리  (0) 2021.03.06
[My Style] Dictator 구조  (0) 2021.03.05
[My Style] 유니티 프로젝트 구조  (0) 2021.03.05
[Unity] 로컬 서버 연결 테스트 케이스  (0) 2021.02.21
0 : UNITY=SPINEの記憶するべきもの  (0) 2020.12.12
블로그 이미지

RIsN

,

중복되는 구조

  • Dictator
    • 독재자
    • Don't Destroy로 게임 실행시 사라지지 않도록 함
  • Intro
    • 인트로
블로그 이미지

RIsN

,

<환경 설정>

// : Express 사용
const express = require('express');
const router = express.Router();

// : 데이터베이스 커넥트
const connectPool = require('../connectPool');

router.post('/', async (require, response) => {

    // :: 받아옴
    const { userName } = require.body;

    // :: 쿼리 생성
    const query = "select * from users where userName = ?";

    // :: 쿼리 적용
    try {
        let[rows, fields] = await connectPool.promise()
        .query(query, [userName]); // :: 적용

		// :: 결과 테스트
        const name = rows[0].userName;
        const email = rows[0].userEmail;
        const time = rows[0].userConnectedTime;
        console.log(`이름 : ${name}, 이메일 : ${email}, 시간 : ${time}`);

        response.json({userData: rows});
    } catch(error) {
        
        response.sendStatus(500);
    }
});

module.exports = router;

<테스트>

  1. 빈 유니티 3D 프로젝트 생성
    • 이름 : LocalTest00
  2. Asset 추가
    • JSON .NET For Unity : JSON 쉽게 쓰기 위한 것
  3. Scene 이름 변경
    • SampleScene ⇒ App
  4. App 오브젝트 및 스크립트 제작
    • 빈 오브젝트 생성 및 이름 변경 : App
    • Scripts 폴더를 만들고 그 안에 App 스크립트 제작
    • App 스크립트를 App 오브젝트에 부착
    • using System.Collections;
      using UnityEngine;
      using UnityEngine.UI;
      using UnityEngine.Networking;
      using Protocols;
      using Newtonsoft.Json;
      using System.Text;
      
      public class App : MonoBehaviour
      {
          // : Objects
          public Button BUTTON_Send;
          public InputField IFIELD_Name;
          public Text TEXT_Output;
      
          // : Status
          const string SERVER_HOST = "http://localhost:";
          const int SERVER_PORT = 3000;
      
          // Start is called before the first frame update
          void Start()
          {
              this.BUTTON_Send.onClick.AddListener(() =>
              {
                  // :: 객체 생성
                  string userName = IFIELD_Name.text;
                  Packets.find_req findRequest = new Packets.find_req(userName);
      
                  // :: 요청
                  this.StartCoroutine(this.RequestFind(findRequest));
              });
          }
      
          // : Request
          // :: IEnumerator 사용 이유 : SendWebRequest를 비동기 전송으로 하려고
          private IEnumerator RequestFind(Packets.find_req packet)
          {
              // :: 직렬화
              string json = JsonConvert.SerializeObject(packet);
      
              // :: 요청객체 생성
              string url = string.Format("{0}{1}", SERVER_HOST + SERVER_PORT, "/find");
              UnityWebRequest webRequest = new UnityWebRequest(url, "POST");
      
              // :: 바디
              var bodyRaw = Encoding.UTF8.GetBytes(json); // :: 한글 처리를 위해 UTF8 설정
      
              // :: 이벤트
              webRequest.uploadHandler = new UploadHandlerRaw(bodyRaw); // :: 이 부분 좀 더 이해 필요
              webRequest.downloadHandler = new DownloadHandlerBuffer(); // :: Response 받아오는 애 : 이해 필요
      
              // :: 헤더 설정
              webRequest.SetRequestHeader("Content-Type", "application/json"); // :: JSON이라고 알려줌
      
              // :: 비동기 전송
              yield return webRequest.SendWebRequest();
      
              // :: 응답 : 에러 처리
              if(webRequest.isNetworkError || webRequest.isHttpError)
              {
                  Debug.Log("www error");
              } else // :: 응답 : 결과 처리
              {
                  Debug.LogFormat("responseCode: {0}", webRequest.responseCode);
      
                  // :: 데이터 받기
                  byte[] resultRaw = webRequest.downloadHandler.data; // :: 왜인지 바이트로 전달함
                  string resultJSON = Encoding.UTF8.GetString(resultRaw); // :: 바이트를 문자열로 변경
                  
                  Debug.Log(resultJSON);
      
                  // :: 역직렬화 ⇒ find_req
                  Packets.find_res result = JsonConvert.DeserializeObject<Packets.find_res>(resultJSON);
      
                  // :: UI 출력
                  string userOutput = "";
                  foreach(Packets.info_userData data in result.userData)
                  {
                      userOutput += string.Format("이름 : {0}\n", data.userName);
                      userOutput += string.Format("이메일 : {0}\n", data.userEmail);
                      userOutput += string.Format("접속 시간 : {0}\n", data.userConnectedTime);
                  }
                  this.SetText_Output(userOutput);
              } 
          }
      
          // : Set
          private void SetText_Output(string text)
          {
              this.TEXT_Output.text = text;
          }
      }
      
  5. Protocols 스크립트 제작
    • Scripts 폴더에 Protocols 스크립트 제작
    • using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;
      
      namespace Protocols
      {
          public class Packets
          {
              // : 직렬화
              // : 클라이언트 ⇒ 서버
              public class find_req
              {
                  public string userName;
                  public find_req(string userName)
                  {
                      this.userName = userName;
                  }
              }
      
              // : 역직렬화
              // : 서버 ⇒ 클라이언트
              public class find_res
              {
                  public List<info_userData> userData;
              }
      
              // : 역직렬화 대상
              public class info_userData
              {
                  public string userID;
                  public string userName;
                  public string userEmail;
                  public string userPassword;
                  public string userConnectedTime;
              }
          }
      }
  6. 전송 Button 생성
    1. 이름 : Button_Send
  7. 이름을 전송할 Input Field 생성
    1. 이름 : InputField_Name
  8. 정보가 나올 Text 생성
    1. 이름 : Text_Output
  9. Button, Input Field, Text를 App에 연결
  10. 테스트 결과 확인
  • 추가 테스트 : 좀 더 UI
블로그 이미지

RIsN

,

<데이터베이스 테이블 작성>

  • 스키마 이름 : user_db
  • 테이블 이름 : users
  • utf-8 설정
  • 데이터
    • userID : varchar(45) / pk, nn
    • userName : varchar(45) / nn
    • userEmail : varchar(45) / nn, uq
    • userPassword : varchar(255) / nn
    • userConnectedTime : datetime / Default : CURRENT_TIMESTAMP

<프로토콜 문서 작성>

<환경 설정>

  1. 테스트 폴더를 작성한다
    • 이름 : test-express
  2. 터미널을 열어서 해당 폴더에서 필요한 것들을 설치 및 설정한다
    1. npm init -y : npm을 사용하기 전의 초기화
    2. npm i express : express 설치
    3. npm i mysql2 : my-sql를 편하게 사용할 수 있게 해주는 거인듯?
    4. npm i crypto-js : 암호화
    5. npm i uuid : 유니크 ID
    6. npm i nodemon : 딱히 필요 없지만, 스크립트가 갱신될 때마다 서버를 알아서 다시 돌려주는 녀석

<테스트 케이스>

  1. 폴더에 메인이 될 app.js를 만든다
    • const { response } = require('express');
      const express = require('express'); // :: Express 사용
      const app = express();
      
      app.use(express.json());
      
      app.get('/', (require, response) => {
          response.sendStatus(200); // :: OK 돌려보내준다.
      });
      const portNumber = 3000;
      app.listen(portNumber, () => {
          console.log(`server is running at ${portNumber} port`);
      });
  2. nodemon app으로 서버를 켠다
    • postman 설치 생략
    • postman으로 get으로 해놓고 <localhost:3000/>에 전송해보면 문제 없으면 OK가 돌아온다
  3. My SQL 데이터베이스와 연결을 담당할 connectPool.js를 만든다
    • const mysql = require('mysql2');
      
      const connectPool = mysql.createPool({
          host: 'localhost',
          user: 'root', // :: 데이터베이스 유저 id
          database: 'user_db',
          password: '<패스워드 넣어주세요>', // :: 데이터베이스 유저 패스워드
          waitForConnections: true,
          connectionLimit: 10,
          queueLimit: 0
      });
      
      module.exports = connectPool;
  4. 작성한 프로토콜을 기준으로 유저 라우터 router/uesrs.js를 만든다
    • // : Express 사용
      const express = require('express');
      const router = express.Router();
      
      // : 암호화
      const CrpytoJS = require('crypto-js');
      const encodeKey = 'saint place'; // :: 암호화 키
      
      // : 유니크 아이디
      const {v4: uuid4} = require('uuid');
      
      // : 데이터베이스 커넥트
      const connectPool = require('../connectPool');
      
      router.post('/', async (require, response) => {
      
          // :: 받아옴
          const { userName, userEmail, userPassword } = require.body;
      
          // :: 유니크 아이디 생성
          const userID = uuid4();
      
          // :: 패스워드 암호화
          const password = CrpytoJS.AES.encrypt(userPassword, encodeKey).toString();
          
          // :: 암호화 확인
          const encodePass = password;
          console.log("encode Password : " + encodePass);
      
          // :: 복호화 확인
          const decodePass = CrpytoJS.AES.decrypt(encodePass, encodeKey).toString(CrpytoJS.enc.Utf8);
          console.log("decode Password : " + decodePass);
      
          // :: 쿼리 생성
          const query = "insert into users(userID, userName, userEmail, userPassword) values (?, ?, ?, ?);";
      
          // :: 쿼리 적용
          try {
              let[rows, fields] = await connectPool.promise()
              .query(query, [userID, userName, userEmail, password]); // :: 적용
      
              response.sendStatus(200);
          } catch(error) {
              
              response.sendStatus(500);
          }
      });
      
      
      router.delete('/', async (require, response) => {
          console.log('회원목록 가져오기 테스트');
      
          try {
              const query = "select * from users";
              let[rows, fields] = await connectPool.promise().query(query);
      
              rows.forEach(row => {
                  const name = row.userName;
                  const email = row.userEmail;
                  const password = CrpytoJS.AES.decrypt(row.userPassword, encodeKey).toString(CrpytoJS.enc.Utf8);
                  const time = row.userConnectedTime;
                  console.log(`이름 : ${name}, 이메일 : ${email}, 패스워드 : ${password}, 시간 : ${time}`);
              });
          } catch (error) {
              response.sendStatus(500);
          }
      
          response.sendStatus(200);
      });
      
      module.exports = router;
  5. 회원가입 전송 테스트
    • {
          "userName" : "아이유",
          "userEmail" : "mylifefor@IU",
          "userPassword" : "IU4EVER"
      }
  6. 성공
  7. 추가 테스트(회원 목록 받아오기)
블로그 이미지

RIsN

,

ML-Agents란?

Machine Learning Agents로 말 그대로 머신러닝을 시키는 API라고 이해하고 있음
Python을 사용, Unity에 접목 가능

진행과정

<시작>

  1. 새로운 Unity3D 프로젝트(RollerBall00)를 만든다.
  2. 유니티 패키지를 열어서 ML-Agents를 추가한다.
    1. Window -> Package Manager -> ML Agents(당시 실험 버전 1.0.6) Install

<오브젝트 환경 설정>

  1. SampleScene의 이름을 RollerBallA로 변경한다.
    1. Scene의 이름이 중요한가 확인 중
  2. 새 GameObject : TrainingArea 추가한다.
  3. TrainingArea 오브젝트 밑에 바닥을 추가한다.
    1. 3D Object > Plane
    2. 이름은 Floor
    3. 한 번 Reset
  4. TrainingArea 오브젝트 밑에 타겟이 될 Cube를 추가한다.
    1. 3D Object > Cube
    2. 이름은 Target
    3. 한 번 Reset하고 Position을 변경 : (3, 0.5, 3)
  5. TrainingArea 오브젝트 밑에 장애물이 될 Cube를 추가한다.
    1. 3D Object > Cube
    2. 이름은 Hurdle
    3. 한 번 Reset하고 Position을 변경 : (1.5, 0.5, 1.5)
    4. 장애물 테스트 중
  6. TrainingArea 오브젝트 밑에 이제 지가 알아서 학습할 녀석인 Sphere를 추가한다.
    1. 3D Object > Sphere
    2. 이름은 RollerAgent
    3. 한번 Reset하고 Position을 변경 : (0, 0.5, 0)
    4. Component 추가 : Rigidbody

<스크립트 설정>

  1. Scripts 폴더를 만들고 RollerAgent 스크립트를 그 속에 만든다.
  2. 이것을 구인 RollerAgent에 부착
  3. RollerAgent 편집
    •  

      using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;
      
      // : MLAgent 사용
      using Unity.MLAgents;
      using Unity.MLAgents.Sensors;
      
      public class RollerAgent : Agent
      {
          // : 1 Start
          Rigidbody rBody;
          void Start()
          {
              rBody = this.GetComponent<Rigidbody>();
          }
      
          // : Episode가 시작할 때마다 상황 변경
          public Transform target;
          public Transform hurdle;
          public override void OnEpisodeBegin()
          {
              // : 떨어졌을 때 속도 및 위치 초기화
              if(this.transform.localPosition.y < 0)
              {
                  this.rBody.angularVelocity = Vector3.zero;
                  this.rBody.velocity = Vector3.zero;
                  this.transform.localPosition = new Vector3(0, 0.5f, 0);
              }
      
              // : 타깃 위치 변경
              target.localPosition = this.GetRandomPosition(this.transform.localPosition);
              // : 장애물 위치 변경
              hurdle.localPosition = this.GetRandomPosition(this.transform.localPosition);
          }
      
          // : 관찰 스테이터스 결정
          public override void CollectObservations(VectorSensor sensor)
          {
              // :: 포지션
              sensor.AddObservation(this.transform.localPosition);
              sensor.AddObservation(this.target.localPosition);
              sensor.AddObservation(this.hurdle.localPosition);
      
              // :: 속도
              sensor.AddObservation(rBody.velocity.x);
              sensor.AddObservation(rBody.velocity.z);
          }
      
          // : 행동 에피소드 결정
          public float forceMultiplier = 10;
          public override void OnActionReceived(float[] vectorAction)
          {
              // :: 행동할 액션은 2가지 x 이동과 z 이동
              Vector3 controlSignal = Vector3.zero;
              controlSignal.x = vectorAction[0];
              controlSignal.z = vectorAction[1];
              rBody.AddForce(controlSignal * this.forceMultiplier);
      
              // :: 거리 측정
              float distanceToTarget = Vector3.Distance(this.transform.localPosition, this.target.localPosition);
              float distanceToHurdle = Vector3.Distance(this.transform.localPosition, this.hurdle.localPosition);
      
              // : 보상
              if(distanceToTarget < 1.42f)
              {
                  Debug.Log("+1");
                  this.SetReward(1.0f);
                  this.EndEpisode();
              }
      
              // : 벌
              else if(distanceToHurdle < 1.42f)
              {
                  Debug.Log("-1");
                  this.SetReward(-0.5f);
                  this.EndEpisode();
              }
      
              // : 떨어졌을 때
              else if(this.transform.localPosition.y < 0)
              {
                  this.EndEpisode();
              }
          }
      
          // : 키보드 조작
          public override void Heuristic(float[] actionsOut)
          {
              actionsOut[0] = Input.GetAxis("Horizontal");
              actionsOut[1] = Input.GetAxis("Vertical");
          }
      
          // : Get
          private Vector3 GetRandomPosition(Vector3 exceptPosition)
          {
              // :: Set
              Vector3 position = new Vector3(Random.value * 8 - 4, 0.5f, Random.value * 8 - 4);
      
              // :: 중복이 발생하지 않도록
              float distance = Vector3.Distance(exceptPosition, position);
              while(distance <= 1f)
              {
                  position = new Vector3(Random.value * 8 - 4, 0.5f, Random.value * 8 - 4);
                  distance = Vector3.Distance(exceptPosition, position);
              }
      
              return position;
          }
      }
  4. RollerAgent 스크립트에 Target과 Hurdle 오브젝트를 연결 
  5. RollerAgent 오브젝트에 Add Component로 Decision Requester 추가
    1. Decision Period는 10으로 설정
  6. RollerAgent 오브젝트에 Add Component로 Behavior Parameter 추가
    1. Behavior Name은 RollerBall
    2. Vector Observation > Space Size 는 11로 변경
    3. Vector Action > Space Type을 Continuous로 변경
    4. Vector Action > Space Size는 2로 변경

<트레이닝 설정>

  1. Python, pip3, MLAgents 등의 설치 : 생략(나중에 기회되면 추가)
  2. MLAgents 클로닝(github.com/Unity-Technologies/ml-agents)
  3. MLAgents 의 config 폴더 밑에 rollerball_config.yaml 생성
    • UTF-8 with BOM으로 되어 있으면 UTF-8로 변경
    • behaviors:
        RollerBall:
          trainer_type: ppo
          hyperparameters:
            batch_size: 10
            buffer_size: 100
            learning_rate: 3.0e-4
            beta: 5.0e-4
            epsilon: 0.2
            lambd: 0.99
            num_epoch: 3
            learning_rate_schedule: linear
          network_settings:
            normalize: false
            hidden_units: 128
            num_layers: 2
          reward_signals:
            extrinsic:
              gamma: 0.99
              strength: 1.0
          max_steps: 500000
          time_horizon: 64
          summary_freq: 10000
  4. 터미널로 해당 config 폴더로 가서 MLAgents 실행
    1. 명령어 : mlagents-learn rollerabll_config.yaml --run-id=firstRollerBall00
    2. 2번째 테스트부터는 ID 변경하면 됨
  5. 배우는 것을 본다
    1. https://youtu.be/bdBM4BFAd0E
    2. ML-Agents 롤러볼 머신러닝 중
  6. 결과물(Result 폴더에 있는 것)을 유니티에 넣는다.
    1. RollerBall.onmx
  7. RollerAgent 오브젝트의 Behavior Parameters의 Model에 해당 RollerBall.onmx를 추가
  8. 결과 확인(30만번 돌렸을 때)
    1. youtu.be/TL0kVnCFEnk

 

블로그 이미지

RIsN

,