블로그 이미지
Every unexpected event is a path to learning for you. blueasa

카테고리

분류 전체보기 (2809)
Unity3D (865)
Programming (479)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (234)
협업 (61)
3DS Max (3)
Game (12)
Utility (140)
Etc (98)
Link (32)
Portfolio (19)
Subject (90)
iOS,OSX (55)
Android (16)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (3)
Memories (20)
Interest (38)
Thinking (38)
한글 (30)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (18)
Exercise (3)
나만의 맛집 (3)
냥이 (10)
육아 (16)
Total
Today
Yesterday

[Link] https://github.com/Yortw/Yort.Ntp

반응형
Posted by blueasa
, |


[링크]

https://themangs.tistory.com/entry/%EC%9C%A0%EB%8B%88%ED%8B%B0%EB%9E%91-nodejs-%EA%B0%84%EB%8B%A8-%EC%97%B0%EB%8F%99-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%95%B4%EB%B3%B4%EA%B8%B0


[링크2]

https://github.com/NetEase/UnitySocketIO

반응형

'Unity3D > Plugins' 카테고리의 다른 글

[링크] FancyScrollView (CoverFlow)  (0) 2019.04.24
[링크] Simple Unity browser  (0) 2019.03.28
[펌] Socket.io-Client for Unity3D 소개  (0) 2019.03.08
[링크] iOSPlugin Sample  (0) 2019.01.29
[펌] Unity Excel Importer  (0) 2019.01.03
Posted by blueasa
, |


image.png

들어가면서

socket.io

웹 환경에서는 클라이언트인 브라우저와 서버 간에 푸쉬나 실시간 데이터를 처리하는데 여러 가지 방법을 활용했습니다. 그중에 Ajax를 이용한 polling, Long polling과 Streaming의 특징은 다음 그림과 같이 비교할 수 있습니다.

image.png
출처 : https://blogs.oracle.com/theaquarium/entry/slideshow_example_using_comet_dojo

위와 같은 방식을 활용하다가 나온 표준이 Websocket입니다. Websocket은 기존에 게임 개발자들이 활용하고 있던 TCP socket과 비슷하게 데이터를 주고 받을 수 있으며 Polling보다 자유롭게 데이터를 주고 받을 수 있게 되었습니다. Webocket에 대해 좀더 자세한 내용을 원하시면 여기 링크(https://ko.wikipedia.org/wiki/웹소켓) 를 참조 부탁드립니다. 하지만 이 좋은 Websocket의 단점은 지원하는 브라우저가 없을 수 있다는 점이 입니다. 이런 단점을 보완 하고자 브라우저와 상관 없이 웹에서 실시간으로 데이터를 처리 할수 있게 도와주는 것이 바로 socket.io입니다. socket.io(http://socket.io)는 Guillermo Rauch에의해 LearnBoost라는 회사가 개발했으며 MIT 라이선스의 오픈소스입니다.

socket.io는 WebSocket, FlashSocket, JSONP과 Long Polling을 하나로 묶어 브라우저에서 Websocket을 지원하지 않더라도 실시간 데이터처리를 돕습니다. 다양한 브라우저에서 데이터를 처리하기 위한 방법 뿐만 아니라 데이터를 주고 받기 위한 메세지 규약, 연결 유지를 위한 핑퐁 등과 같이 socket 연결을 위해 기본으로 구현해야할 부분까지 처리가 되어있기 때문에 서비스 개발자는 실시간 데이터 처리만 집중할 수 있습니다. socket.io의 프로토콜 정의는 링크 https://github.com/socketio/socket.io-protocol와 https://github.com/socketio/engine.io-protocol 참조 하시면 됩니다.

socket.io-Client

요 근래 게임에서도 실시간으로 데이터를 처리 하기 위해 Websocket을 많이 활용하는 추세 입니다. 왜냐하면 TCP socket보다 단순하게 구현할 수 있기 때문입니다. 실제로 Unity3d에서 WebGL로 빌드 할 때에도 데이터를 주고 받는 방법으로 Websocket을 이용하고 있습니다. socket보다 구현하기 쉬운 Websocket도 사실 데이터처리가 용의할 뿐 연결을 체결하거나 유지하는 등의 네트워크 레이어는 개발자가 직접 구현해야 합니다. 하지만 socket.io를 이용하면 연결과 기본적인 프로토콜이 구현되어 있기 때문에 개발자는 컨텐츠 데이터를 주고 받는데만 집중할 수 있습니다.

socket.io는 다양한 언어를 지원합니다. Javascript는 물론이고, Java, Swift와 CPP을 지원합니다. 지원하는 언어는 아래와 같습니다.

개발 하기까지

우리 회사에는 풋볼데이와 야구9단과 같은 웹 게임을 개발하면서 데이터를 push해주기 위해 socket.io를 이용했습니다. 기존의 socket.io구현체는 Node.js를 활용한 구현체 였지만 우리회사는 JAVA를 활용하여 대용량 트래픽 처리와 보안 인증을 추가한 플랫폼으로 구현했습니다. 해당 플랫폼을 사내 공용 플랫폼화 하여 웹 게임 뿐만 아니라 다른 서비스에도 적용하였고, 웹과 네이티브 앱간의 데이터 통신하는데 활용하고 있습니다.

기존의 socket.io 클라이언트 들은 다양한 네이티브 언어를 지원하고 있으나 게임개발에 많이 활용되고 있는 unity3d는 지원하지 않습니다. 우리는 우리의 플랫폼을 게임에도 활용하여 게임 개발자들에게 데이터 처리를 위해 네트워크를 개발하는 수고를 덜어주고 싶었습니다. 서버는 우리 회사의 플랫폼도 있고 오픈소스인 socket.io node.js용 서버도 있으니 클라이언트만 있다면 게임 개발자들도 쉽게 개발 할수 있을 것 같았습니다. 하지만 기존의 unity3d용 라이브러리들은 0.9버전이거나 유지 보수가 되지 않아서 우리가 직접 socket.io-Client Unity3D 라이브러리를 개발하게 되었습니다.

socket.io 사용법

실제로 Node.js + socket.io-Client Unity3D를 이용하여 구현된 샘플 코드를 살펴보면서 socket.io 사용법을 소개하겠습니다. 서버/클라이언트 코드로 구성되며 서버 코드는 socket.io 공식 홈페이지에서 발췌/변경하였습니다. socket.io 공식 홈페이지는 아래 링크를 확인해주세요.
socket.io 공식 홈페이지

추가로 Node.js로 직접 서버 코드를 실행해보고 싶으신 분들은 아래 링크를 참조하여서 서버 개발 환경 셋팅을 할 수 있습니다.
Node.js and Visual Studio Code End to End

예제를 구현하기 위한 소스는 아래 링크에서 찾아 보실 수 있습니다.

Connect to Server

첫번째 해야할 것은 역시나 컨넥션을 맺는 것입니다. 서버는 우선 HTTP 서버를 오픈해야합니다. 이는 socket.io 클라이언트가 최초엔 Polling 모드로 접속을 시도하도록 구현되어 있기 때문입니다.

서버 코드 [https://github.com/nhnent/socket.io-client-unity3d/blob/master/Assets/__Sample/Server~/connection.js]
var app = require('http').createServer(handler)
var fs = require('fs');

app.listen(4444);

function handler (req, res) {
  fs.readFile(__dirname + '/index.html',
  function (err, data) {
      if (err) {
          res.writeHead(500);
          return res.end('Error loading index.html');
      }

      res.writeHead(200);
      res.end(data);
  });
}

// socket.io 스타트
var io = require('socket.io')(app);

// 클라이언트 컨넥션 이벤트 처리
io.on('connection', function (socket) {
  console.log('Hello, socket.io~');
});

Socket.io가 시작되는 것은 'socket.io' 모듈을 가져올 때, 원샷에 처리가 됩니다. 이 때, 인자값으로 HTTP 서버의 인스턴스 (app 변수)를 추가해주면 HTTP 서버의 주소로 Socket.io가 Bind되면서 자동으로 Listen을 시작합니다. 이 때, 리턴하는 io 변수는 일종의 매니저 객체로써 이를 통해서 실제 세션에 해당하는 socket 객체를 획득할 수 있습니다. io.on() 메소드에서 'connection'이라는 이벤트를 핸들링하도록 구현했고, 클라이언트에서 컨넥션이 완료되었을 때, 콜백 함수를 통해서 socket을 획득할 수 있습니다. socket은 일종의 세션 객체라고 생각해도 무방합니다. socket을 통해서 앞으로 각종 패킷을 Send/Receive하게 됩니다.

클라이언트 코드 [https://github.com/nhnent/socket.io-client-unity3d/blob/master/Assets/__Sample/Client/Src/Connection.cs]
using UnityEngine;
using socket.io;

namespace Sample {

    public class Connection : MonoBehaviour {

        void Start() {
            // 접속 Url
            var serverUrl = "http://localhost:4444";

            // 서버로 접속 시도~
            var socket = Socket.Connect(serverUrl);

            // 접속 완료 이벤트 처리
            socket.On("connect", () => {
                Debug.Log("Hello, socket.io~");
            });
        }

    }

}

클라이언트는 socket.io.Socket 클래스의 Connect() 메소드를 호출합니다. 이 때 인자값으로 서버의 Url을 입력합니다. 접속이 성공하면, socket 변수를 리턴하고 서버와 동일하게 각종 패킷을 Send/Receive하게 됩니다. 예제 코드에서는 'connect' 이벤트를 캐치하여 웰컴 메세지를 출력하도록 구현해보았습니다.

Event 핸들링

socket.io는 Event 기반으로써 모든 통신의 처리는 'Event 등록 + Callback 함수 구현'의 형식으로 구현됩니다. Event를 Send하는 메소드는 Emit(), 그리고 Event를 Receive하는 메소드는 On()입니다. 아래 예제를 보면서 좀 더 자세히 살펴보겠습니다.

서버 코드 [https://github.com/nhnent/socket.io-client-unity3d/blob/master/Assets/__Sample/Server~/events.js]
// HTTP 서버 코드는 생략 (...)

// socket.io 스타트
var io = require('socket.io')(app);

// 클라이언트 컨넥션 이벤트 처리
io.on('connection', function (socket) {

    // 'news' 이벤트 send
    socket.emit('news', { hello: 'world' });

    // 'my other event' 이벤트 receive
    socket.on('my other event', function(data) {
        console.log(data);
    });

});
클라이언트 코드 [https://github.com/nhnent/socket.io-client-unity3d/blob/master/Assets/__Sample/Client/Src/Events.cs]
using UnityEngine;
using socket.io;

namespace Sample {

    public class Events : MonoBehaviour {

        void Start() {
            var serverUrl = "http://localhost:4444";
            var socket = Socket.Connect(serverUrl);

            // "news" 이벤트 처리 Receive
            socket.On("news", (string data) => {
                Debug.Log(data);

                // "my other event" 이벤트 Send
                socket.Emit(
                    "my other event",       // 이벤트명
                    "{ \"my\": \"data\" }"  // 데이터 (Json 텍스트)
                    );
            });
        }

    }

}

2개의 이벤트를 정의하여 서버와 클라이언트 간의 패킷 송수신을 구현했습니다. 서버는 'news' 이벤트를 Send하고 'my other event'를 Receive합니다. 클라이언트는 반대입니다.

실행 결과
  • 서버
    image.png
  • 클라이언트
    image.png

Emit() 메소드

Send를 담당하는 Emit() 메소드는 다음과 같은 형식을 같습니다.

public void Emit(string evtName, string data);
  • Emit() 파라메터
evtNamedata
이벤트 이름이벤트 데이터 (주로 Json 객체)

On() 메소드

Receive를 담당하는 On() 메소드의 정의는 다음과 같습니다.

public void On(string evtName, Action<string> callback);
  • On() 파라메터
evtNamecallback
이벤트 이름이벤트 핸들러 함수

System Event

Emit()과 On() 메소드에서 선언한 이벤트 이름 중 일부 문자열은 시스템 예약어이므로 사용을 금지합니다. 서버 용 시스템 이벤트는 아래 링크에서 확인이 가능합니다.

[https://socket\.io/docs/server\-api/\]\(socket\.io Server API)

여기서는 클라이언트 용 시스템 이벤트만 정리해보겠습니다.

이벤트 이름콜백 파라메터비고
connect없음접속 완료
connectTimeOut없음시간 초과로 인한 접속 실패
reconnectAttempt없음재접속 시도
reconnectFailed없음재접속 실패
reconnectint (재접속 횟수)재접속 완료
reconnectingint (재접속 횟수)재접속 중
connectErrorException접속 실패
reconnectErrorException재접속 실패

콜백 파라메터를 기준으로 On() 메소드 호출 형식이 바뀌게 됩니다. 예를 들어 'reconnect' 이벤트를 핸들링하고 싶다면 다음과 같이 코드를 작성해야 합니다.

socket.On("reconnect", (int attempt) => {
    Debug.LogFormat("Reconnected after {0} trials, attempt);
});

Event Ack

이번엔 이벤트를 Send하고 이에 대한 응답을 Receive하는 방법을 알아보겠습니다.

서버 코드 [https://github.com/nhnent/socket.io-client-unity3d/blob/master/Assets/__Sample/Server~/acks.js]
// HTTP 서버 코드는 생략 (...)

// socket.io 스타트
var io = require('socket.io')(app);

// 클라이언트 컨넥션 이벤트 처리
io.on('connection', function (socket) { 

   socket.on('ferret', function (name, fn) {
        // 'woot' 문자열을 Ack 메세지로 보냄
        fn('woot');
    }); 
});
클라이언트 코드 [https://github.com/nhnent/socket.io-client-unity3d/blob/master/Assets/__Sample/Client/Src/Acks.cs]
using UnityEngine;
using socket.io;

namespace Sample {

    public class Acks : MonoBehaviour {

        void Start() {
            var serverUrl = "http://localhost:4444";
            var socket = Socket.Connect(serverUrl);

            socket.On("connect", () => {

                // "ferret" 이벤트 Send
                socket.Emit(
                    "ferret", "\"toby\"", 
                    (string ackData) => { Debug.Log(ackData); } // 3번째 인자로 콜백을 셋팅하면 Ack 모드로 동작하며 Ack 시에 콜백이 호출됨
                    );
            });
        }

    }

}

앞서 소개한 Event 핸들링 예제와 달라진 점은 클라이언트 코드에서 Emit() 메소드에 3번째 인자로 콜백을 추가한 것입니다. 이렇게 호출하게 되면 Ack 모드로 Emit() 메소드가 동작하게 됩니다. 위의 예제에서는 서버 코드에서 Ack 메시지로 'woot' 문자열을 리턴하였고, 클라이언트는 Ack 콜백에서 'woot' 문자열을 인자값으로 받게 됩니다.

Namespace

하나의 소켓을 논리적인 처리 단위로 구분할 수 있습니다. Multiplexing이라고도 불리는 이러한 패턴을 통해서 하나의 커넥션(소켓 인스턴스)에서 여러 개의 독립적인 코드를 개발할 수 있습니다.

서버 코드 [https://github.com/nhnent/socket.io-client-unity3d/blob/master/Assets/__Sample/Server~/namespace.js]
// HTTP 서버 코드는 생략 (...)

// socket.io 스타트
var io = require('socket.io')(app);

// chat 네임스페이스
var chat = io
  .of('/chat')
  .on('connection', function (socket) {
    chat.emit('a message', { everyone: 'in', '/chat': 'will get' });
    socket.emit('a message', { that: 'only', '/chat': 'will get' });
  });

// news 네임스페이스
var news = io
  .of('/news')
  .on('connection', function (socket) {
    socket.emit('item', { news: 'item' });
  });
클라이언트 코드 [https://github.com/nhnent/socket.io-client-unity3d/blob/master/Assets/__Sample/Client/Src/Namespace.cs]
using UnityEngine;
using socket.io;

namespace Sample {

    public class Namespace : MonoBehaviour {

        void Start() {
            var serverUrl = "http://localhost:4444";

            // news 네임스페이스
            var news = Socket.Connect(serverUrl + "/news");
            news.On("connect", () => {
                news.Emit("woot");
            });
            news.On("a message", (string data) => {
                Debug.Log("news => " + data);
            });
            news.On("item", (string data) => {
                Debug.Log(data);
            });

            // chat 네임스페이스
            var chat = Socket.Connect(serverUrl + "/chat");
            chat.On("connect", () => {
                chat.Emit("hi~");
            });
            chat.On("a message", (string data) => {
                Debug.Log("chat => " + data);
            });
        }

    }

}

서버는 of() 메소드를 통해서 네임스페이스를 선언합니다. 클라이언트는 단순히 Url에 주소를 추가하는 것으로 선언이 가능합니다. 위 예제에선 "http://localhost:4444/chat"과 "http://localhost:4444/news"와 같이 chat과 news 네임스페이스를 선언하였습니다. 그 외에 나머지는 일반적인 이벤트 핸들링과 동일합니다.

실행 결과

image.png

위 예제의 포인트는 클라이언트 코드에서 chat과 news이 모두 'a message' 이벤트를 등록하였지만 오직 chat 소켓만이 'a message' 이벤트를 핸들링하게 되는 점입니다. 네임스페이스로 구분하여 이벤트명을 중복하여 사용하는 것이 가능하다라는 것을 시사하며 이를 통해서 채널 혹은 컨텐츠 개발 스코프마다 각각 네임스페이스르 할당해서 작업한다면 하나의 소켓 컨넥션 안에서도 이벤트 이름의 충돌없이 코드 작성이 가능하게 됩니다.

결론

오랫동안 게임 개발에서는 Socket을 이용한 네트워크를 구현해 왔습니다. 하지만 socket을 이용하여 데이터를 주고 받기 위해서는 네트워크의 연결 유지와 데이터 전송을 위해 너무 많은 노력이 필요합니다. 게임 개발에 있어서 어려운 네트워크 구현을 빼고 컨텐츠 개발에 힘을 쓴다면 더 좋은 게임이 나올 것이라 생각됩니다. 더욱이 웹과 같은 다양한 플랫폼을 지원 하는 게임을 개발하는데 있어 네트워크 레이어마다 개발을 해야한다면 좀더 좋은 게임을 개발할 수 있는시간을 빼앗기게 됩니다. 그래서 우리는 게임 개발자들이 데이터 통신에 많은 시간을 투자하지 않고 게임을 개발 할수 있도록 socket.io를 위한 Unity3d를 MIT라이선스로 공개하기로 했습니다. socket.io-client-unity3d를 활용하여 좀더 컨텐츠 개발에 시간을 투자하고, 하나의 서버 플랫폼을 이용해 다양한 클라이언트 플랫폼을 지원 하는데 도움이 되었으면 좋겠습니다.

해당 라이브러리에 관심이 있으신분들은 https://github.com/nhnent/socket.io-client-unity3d에서 확인 하실 수 있습니다. 또한 참여하시고 싶으신 분들은 https://github.com/nhnent/socket.io-client-unity3d/blob/master/CONTRIBUTING.md를 참조하셔서 참여하여 주시면 감사하겠습니다.


[출처]

https://meetup.toast.com/posts/112



[추가]

socket.io v2.x에서 수정해야 될 내용

https://github.com/nhnent/socket.io-client-unity3d/commit/48b1fb6e743ff2228942a6e08f2eb9a73e1ff2a0


[추가2]

socket.io https 대응 (출처: shmhlove)

https://github.com/shmhlove/Asgardium_Client/tree/develop/Assets/Plugins/socket.io

반응형

'Unity3D > Plugins' 카테고리의 다른 글

[링크] Simple Unity browser  (0) 2019.03.28
[링크] 유니티랑 nodejs 간단 연동 테스트 해보기  (0) 2019.03.08
[링크] iOSPlugin Sample  (0) 2019.01.29
[펌] Unity Excel Importer  (0) 2019.01.03
[링크] Emoji_Extension  (0) 2018.06.07
Posted by blueasa
, |
윈도우10 설정 ms-settings 명령어 활용하기

Windows10에서는 기존의 제어판 외에도 새로운 설정 응용 프로그램을 제공하고 있습니다. 윈도우 10의 설정 응용 프로그램은 많은 페이지로 구성되어 있으며, 이전의 클래식 제어판의 설정 기능을 그대로 활용 가능합니다.

이 설정 프로그램은 거의 모든 설정 항목에 바로 접근할 수 있는 자신만의 URL이 존재합니다. 설정 프로그램에서 특정 기능을 바로 활용하고 싶은데, 접근 루트가 복잡하거나 어느 메뉴에 있는지 모른다면 이 URL에 유용할 수 있습니다.

이번 포스팅에서는 Windows10의 설정 응용프로그램의 기능들을 바로 활용할 수 있는 URL 주소를 소개해 드리도록 하겠습니다. 이 명령어들은 윈도우 10 버전 1704 크리에이터 업데이트에서 활용 가능한 명령어 목록입니다.











윈도우10 설정 ms-settings 명령어 활용하기
윈도우10 설정 ms-settings 명령어 활용하기

예시로 색상 설정을 사용하고 싶다면, 실행창 또는 파일탐색기에 아래의 명령어를 실행해 줍니다.

ms-settings:colors

위의 명령어를 실행시키면 예시처럼 바로 색상 설정을 활용 가능합니다.

아래부터는 Windows 10 Creators Update의 ms-settings 명령 목록입니다.

[홈]
설정 메인 : ms-setting:

[시스템]
디스플레이 : ms-settings:display
알림 및 작업 : ms-settings:notifications
전원 및 절전 : ms-settings:powersleep
배터리 : ms-settings:batterysaver
앱별 배터리 사용량 : ms-settings:batterysaver-usagedetails
저장 공간 : ms-settings:storagesense
태블릿 모드 : ms-settings:tabletmode
멀티태스킹 : ms-settings:multitasking
이 PC에 대한 표시 : ms-settings:project
디바이스 간 환경 : ms-settings:crossdevice
정보 : ms-settings:about

[장치]
블루투스 및 기타 디바이스 : ms-settings:bluetooth
프린터 및 스캐너 : ms-settings:printers
마우스 : ms-settings:mousetouchpad
터치 패드 : ms-settings:devices-touchpad
펜 및 윈도우 잉크 : ms-settings:pen
자동 실행 : ms-settings:autoplay
USB : ms-settings:usb

[네트워크 및 인터넷]
상태 : ms-settings:network-status
와이파이 : ms-settings:network-wifi
이더넷 : ms-settings:network-ethernet
전화 접속 : ms-settings:network-dialup
VPN : ms-settings:network-vpn
비행기 모드 : ms-settings:network-airplanemode
모바일 핫스팟 : ms-settings:network-mobilehotspot
데이터 사용량 : ms-settings:datausage
프록시 : ms-settings:network-proxy

[개인 설정]
배경 : ms-settings:personalization-background
색 : ms-settings:colors
잠금 화면 : ms-settings:lockscreen
테마 : ms-settings:themes
시작 : ms-settings:personalization-start
작업 표시줄 : ms-settings:taskbar

[앱]
앱 및 기능 : ms-settings:appsfeatures
기본 앱 : ms-settings:defaultapps
오프라인 지도 : ms-settings:maps
웹 사이트용 앱 : ms-settings:appsforwebsites

[계정]
사용자 정보 : ms-settings:yourinfo
메일 및 앱 계정 : ms-settings:emailandaccounts
로그인 옵션 : ms-settings:signinoptions
회사 또는 학교 액세스 : ms-settings:workplace
다른 사용자 : ms-settings:otherusers
설정 동기화 : ms-settings:sync

[시간 및 언어]
날짜 및 시간 : ms-settings:dateandtime
지역 및 언어 : ms-settings:regionlanguage
음성 : ms-settings:speech

[게임]
Game bar : ms-settings:gaming-gamebar
게임 DVR : ms-settings:gaming-gamedvr
브로드캐스팅 : ms-settings:gaming-broadcasting
게임 모드 : ms-settings:gaming-gamemode

[접근성]
내레이터 : ms-settings:easeofaccess-narrator
돋보기 : ms-settings:easeofaccess-magnifier
고대비 : ms-settings:easeofaccess-highcontrast
선택 캡션 : ms-settings:easeofaccess-closedcaptioning
키보드 : ms-settings:easeofaccess-keyboard
마우스 : ms-settings:easeofaccess-mouse
기타 옵션 : ms-settings:easeofaccess-otheroptions

[개인정보]
일반 : ms-settings:privacy
위치 : ms-settings:privacy-location
카메라 : ms-settings:privacy-webcam
마이크 : ms-settings:privacy-microphone
알림 :  ms-settings:privacy-notifications
음성, 수동 입력 및 입력 : ms-settings:privacy-speechtyping
계정 정보 : ms-settings:privacy-accountinfo
연락처 : ms-settings:privacy-contacts
일정 : ms-settings:privacy-calendar
통화 기록 : ms-settings:privacy-callhistory
메일 : ms-settings:privacy-email
작업 : ms-settings:privacy-tasks
메시지 : ms-settings:privacy-messaging
라디오 : ms-settings:privacy-radios
기타 장치 : ms-settings:privacy-customdevices
피드백 및 진단 : ms-settings:privacy-feedback
백그라운드 앱 : ms-settings:privacy-backgroundapps
앱 진단 : ms-settings:privacy-appdiagnostics

[업데이트 및 복구]
Windows 업데이트 : ms-settings:windowsupdate
업데이트 확인 : ms-settings:windowsupdate-action
업데이트 기록 : ms-settings:windowsupdate-history
다시 시작 옵션 : ms-settings:windowsupdate-restartoptions
고급 옵션 : ms-settings:windowsupdate-options
Windows Defender : ms-settings:windowsdefender
백업 : ms-settings:backup
문제해결 : ms-settings:troubleshoot
복구 : ms-settings:recovery
정품 인증 : ms-settings:activation
내 장치 찾기 : ms-settings:findmydevice
개발자용 : ms-settings:developers
Windows 내부자 프로그램 : ms-settings:windowsinsider

[혼합 현실]
혼합 현실 : ms-settings:holographic
오디오 및 음성 : ms-settings:holographic-audio


반응형
Posted by blueasa
, |

Using coroutines in Unity is often a great way to solve certain problems, however it comes with certain drawbacks as well:

  1. Coroutines can’t return values. This encourages programmers to create huge monolithic coroutine methods instead of composing them out of many smaller methods. Some workarounds exist, such as passing a callback parameter of type Action<> to the coroutine, or casting the final untyped value that is yielded from the coroutine after it completes, but these approaches are awkward to use and error prone.
  2. Coroutines make error handling difficult. You cannot put a yield inside a try-catch, so it is not possible to handle exceptions. Also, when exceptions do occur the stack trace only tells you the coroutine where the exception was thrown, so you have to guess which other coroutines it might have been called from.

With the release of Unity 2017, it is now possible to use a new C# feature called async-await for our asynchronous methods instead. This comes with a lot of nice features compared to coroutines.

To enable this feature, all you need to do is open your player settings (Edit -> Project Settings -> Player) and change “Scripting Runtime Version” to “Experimental (.NET 4.6 Equivalent).

Let’s look at a simple example. Given the following coroutine:

1
2
3
4
5
6
7
8
9
public class AsyncExample : MonoBehaviour
{
    IEnumerator Start()
    {
        Debug.Log("Waiting 1 second...");
        yield return new WaitForSeconds(1.0f);
        Debug.Log("Done!");
    }
}

The equivalent way to do this using async-await would be the following:

1
2
3
4
5
6
7
8
9
public class AsyncExample : MonoBehaviour
{
    async void Start()
    {
        Debug.Log("Waiting 1 second...");
        await Task.Delay(TimeSpan.FromSeconds(1));
        Debug.Log("Done!");
    }
}

It’s helpful to be somewhat aware of what’s happening under-the-hood in both these cases.

In short, Unity coroutines are implemented using C#’s built-in support for iterator blocks. The IEnumerator iterator object that you provide to the StartCoroutine method is saved by Unity and each frame this iterator object is advanced forward to get new values that are yielded by your coroutine. The different values that you ‘yield return’ are then read by Unity to trigger special case behaviour, like executing a nested coroutine (when returning another IEnumerator), delaying by some number of seconds (when returning an instance of type WaitForSeconds), or just waiting until the next frame (when returning null).

Unfortunately, due to the fact that async-await is quite new within Unity, this built-in support for coroutines as explained above does not exist in a similar fashion for async-await. Which means that we have to add a lot of this support ourselves.

Unity does provide one important piece for us however. As you can see in the above example, our async methods will be run on the main unity thread by default. In non-unity C# applications, async methods are often automatically run on separate threads, which would be a big problem in Unity since we would not always be able to interact with the Unity API in these cases. Without this support from the Unity engine, our calls to Unity methods/objects inside our async methods would sometimes fail because they would be executed on a separate thread. Under the hood it works this way because Unity has provided a default SynchronizationContext called UnitySynchronizationContext which automatically collects any async code that is queued each frame and continues running them on the main unity thread.

As it turns out, however, this is enough to get us started with using async-await! We just need a bit of helper code to allow us to do some more interesting things than just simple time delays.

Custom Awaiters

Currently, there’s not a lot of interesting async code we can write. We can call other async methods, and we can use Task.Delay, like in the example above, but not much else.

As a simple example, let’s add the ability to directly ‘await’ on a TimeSpan instead of always having to call Task.Delay every time like the example above. Like this:

1
2
3
4
5
6
7
public class AsyncExample : MonoBehaviour
{
    async void Start()
    {
        await TimeSpan.FromSeconds(1);
    }
}

All we need to do to support this is to simply add a custom GetAwaiter extension method to the TimeSpan class:

1
2
3
4
5
6
7
    public static class AwaitExtensions
{
    public static TaskAwaiter GetAwaiter(this TimeSpan timeSpan)
    {
        return Task.Delay(timeSpan).GetAwaiter();
    }
}

This works because in order to support ‘awaiting’ a given object in newer versions of C#, all that’s needed is that the object has a method named GetAwaiter that returns an Awaiter object. This is great because it allows us to await anything we want, by using an extension method like above, without needing to change the actual TimeSpan class.

We can use this same approach to support awaiting other types of objects too, including all of the classes that Unity uses for coroutine instructions! We can make WaitForSeconds, WaitForFixedUpdate, WWW, etc all awaitable in the same way that they are yieldable within coroutines. We can also add a GetAwaiter method to IEnumerator to support awaiting coroutines to allow interchanging async code with old IEnumerator code.

The code to make all this happen can be downloaded from either asset store or the releases section of the github repo. This allows you to do things like the following:

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
public class AsyncExample : MonoBehaviour
{
    public async void Start()
    {
        // Wait one second
        await new WaitForSeconds(1.0f);
 
        // Wait for IEnumerator to complete
        await CustomCoroutineAsync();
 
        await LoadModelAsync();
 
        // You can also get the final yielded value from the coroutine
        var value = (string)(await CustomCoroutineWithReturnValue());
        // value is equal to "asdf" here
 
        // Open notepad and wait for the user to exit
        var returnCode = await Process.Start("notepad.exe");
 
        // Load another scene and wait for it to finish loading
        await SceneManager.LoadSceneAsync("scene2");
    }
 
    async Task LoadModelAsync()
    {
        var assetBundle = await GetAssetBundle("www.my-server.com/myfile");
        var prefab = await assetBundle.LoadAssetAsync<GameObject>("myasset");
        GameObject.Instantiate(prefab);
        assetBundle.Unload(false);
    }
 
    async Task<AssetBundle> GetAssetBundle(string url)
    {
        return (await new WWW(url)).assetBundle
    }
 
    IEnumerator CustomCoroutineAsync()
    {
        yield return new WaitForSeconds(1.0f);
    }
 
    IEnumerator CustomCoroutineWithReturnValue()
    {
        yield return new WaitForSeconds(1.0f);
        yield return "asdf";
    }
}

As you can see, using async await like this can be very powerful, especially when you start composing multiple async methods together like in the LoadModelAsync method above.

Note that for async methods that return values, we use the generic version of Task and pass our return type as the generic argument like with the GetAssetBundle above.

Note also that using WaitForSeconds above is actually preferable to our TimeSpan extension method in most cases because WaitForSeconds will use the Unity game time whereas our TimeSpan extension method will always use real time (so it would not be affected by changes to Time.timeScale)

Triggering Async Code and Exception Handling

One thing you might have noticed with our code above is that some methods are defined ‘async void’ and some are defined ‘async Task’. So when should you use one over the other?

The main difference here is that methods that are defined ‘async void’ cannot be waited on by other async methods. This would suggest that we should always prefer to define our async methods with return type Task so that we can ‘await’ on them.

The only exception to this rule is when you want to call an async method from non-async code. Take the following example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class AsyncExample : MonoBehaviour
{
    public void OnGUI()
    {
        if (GUI.Button(new Rect(100, 100, 100, 100), "Start Task"))
        {
            RunTaskAsync();
        }
    }
 
    async Task RunTaskAsync()
    {
        Debug.Log("Started task...");
        await new WaitForSeconds(1.0f);
        throw new Exception();
    }
}

In this example, when the user clicks the button, we want to start our async method. This code will compile and run, however there is a major issue with it. If any exceptions occur within the RunTaskAsync method, they will happen silently. The exception will not be logged to the unity console.

This is because when exceptions occur in async methods returning Task, they are captured by the returned Task object instead of being thrown and handled by Unity. This behaviour exists for a good reason: To allow async code to work properly with try-catch blocks. Take the following code for example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
async Task DoSomethingAsync()
{
    var task = DoSomethingElseAsync();
 
    try
    {
        await task;
    }
    catch (Exception e)
    {
        // do something
    }
}
 
async Task DoSomethingElseAsync()
{
    throw new Exception();
}

Here, the exception is captured by the Task returned by the DoSomethingElseAsync method and is only re-thrown when it is ‘awaited’. As you can see, invoking async methods is distinct from awaiting on them, which is why it’s necessary to have the Task object capture the exceptions.

So in our OnGUI example above, when the exception is thrown inside the RunTaskAsync method, it is captured by the returned Task object, and since nothing awaits on this Task, the exception does not get bubbled up to Unity and therefore is never logged to the console.

But that leaves us with the question of what to do in these cases where we want to call async methods from non-async code. In our example above, we want to start the RunTaskAsync async method from inside the OnGUI method and we don’t care about waiting for it to complete, so we don’t want to have to add an await just so that exceptions can be logged.

The rule of thumb to remember here is:

Never call `async Task` methods without also awaiting on the returned Task. If you don’t want to wait for the async behaviour to complete, you should call an `async void` method instead.

So our example becomes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class AsyncExample : MonoBehaviour
{
    public void OnGUI()
    {
        if (GUI.Button(new Rect(100, 100, 100, 100), "Start Task"))
        {
            RunTask();
        }
    }
 
    async void RunTask()
    {
        await RunTaskAsync();
    }
 
    async Task RunTaskAsync()
    {
        Debug.Log("Started task...");
        await new WaitForSeconds(1.0f);
        throw new Exception();
    }
}

If you run this code again, you should now see that the exception is logged. This is because when the exception gets thrown during the await in the RunTask method, it bubbles up to Unity and gets logged to the console, because in that case there is no Task object to capture it instead.

Methods that are marked as `async void` represent the root level ‘entry point’ for some async behaviour. A good way to think about them is that they are ‘fire and forget’ tasks that go off and execute some number of things in the background while any calling code immediately continues on.

By the way, this is also a good reason to follow the convention of always using the suffix ‘Async’ on async methods that return Task. This is standard practice in most code bases that use async-await. It is helpful in conveying the fact that the method should always be preceded by an ‘await’, but also allows you to create an `async void` counterpart for the method that does not include the suffix.

Also worth mentioning is that if you are compiling your code in visual studio, then you should receive warnings when you attempt to call an `async Task` method without an associated await, which is a great way to avoid this mistake.

As an alternative to creating your own ‘async void’ method, you can also use a helper method (included with the source code associated with this article) that will perform the await for you. In this case our example would become:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class AsyncExample : MonoBehaviour
{
    public void OnGUI()
    {
        if (GUI.Button(new Rect(100, 100, 100, 100), "Start Task"))
        {
            RunTaskAsync().WrapErrors();
        }
    }
 
    async Task RunTaskAsync()
    {
        Debug.Log("Started task...");
        await new WaitForSeconds(1.0f);
        throw new Exception();
    }
}

The WrapErrors() method is simply a generic way to ensure that the Task gets awaited on, so that Unity will always receive any exceptions that are thrown. It simply does an await and that’s it:

1
2
3
4
public static async void WrapErrors(this Task task)
{
    await task;
}

Calling async from coroutines

For some code bases, migrating away from coroutines to use async-await might seem like a daunting task. We can make this process simpler by allowing async-await to be adopted incrementally. In order to do this however, we not only need the ability to call IEnumerator code from async code but we also need to be able to call async code from IEnumerator code. Thankfully, we can add this very easily with yet another extension method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static class TaskExtensions
{
    public static IEnumerator AsIEnumerator(this Task task)
    {
        while (!task.IsCompleted)
        {
            yield return null;
        }
 
        if (task.IsFaulted)
        {
            throw task.Exception;
        }
    }
}

Now we can call async methods from coroutines like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class AsyncExample : MonoBehaviour
{
    public void Start()
    {
        StartCoroutine(RunTask());
    }
 
    IEnumerator RunTask()
    {
        yield return RunTaskAsync().AsIEnumerator();
    }
 
    async Task RunTaskAsync()
    {
        // run async code
    }
}

Multiple Threads

We can also use async-await to execute multiple threads. You can do this in two ways. The first way is to use the ConfigureAwait method like this:

1
2
3
4
5
6
7
8
9
10
11
12
public class AsyncExample : MonoBehaviour
{
    async void Start()
    {
        // Here we are on the unity thread
 
        await Task.Delay(TimeSpan.FromSeconds(1.0f)).ConfigureAwait(false);
 
        // Here we may or may not be on the unity thread depending on how the task that we
        // execute before the ConfigureAwait is implemented
    }
}

As mentioned above, Unity provides something called a default SynchronizationContext, which will execute asynchronous code on the main Unity thread by default. The ConfigureAwait method allows us to override this behaviour, and so the result will be that the code below the await will no longer be guaranteed to run on the main Unity thread and will instead inherit the context from the task that we are executing, which in some cases might be what we want.

If you want to explicitly execute code on a background thread, you can also do this:

1
2
3
4
5
6
7
8
9
10
11
12
public class AsyncExample : MonoBehaviour
{
    async void Start()
    {
        // We are on the unity thread here
 
        await new WaitForBackgroundThread();
 
        // We are now on a background thread
        // NOTE: Do not call any unity objects here or anything in the unity api!
    }
}

WaitForBackgroundThread is a class included in the source code for this post, and will do the work of starting a new thread and also ensuring that Unity’s default SynchronizationContext behaviour is overridden.

What about returning to the Unity thread?

You can do this simply by awaiting any of the Unity specific objects that we created above. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class AsyncExample : MonoBehaviour
{
    async void Start()
    {
        // Unity thread
 
        await new WaitForBackgroundThread();
 
        // Background thread
 
        await new WaitForSeconds(1.0f);
 
        // Unity thread again
    }
}

The included source code also provides a class WaitForUpdate() that you can use if you just want to return to the unity thread without any delay:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class AsyncExample : MonoBehaviour
{
    async void Start()
    {
        // Unity thread
 
        await new WaitForBackgroundThread();
 
        // Background thread
 
        await new WaitForUpdate();
 
        // Unity thread again
    }
}

Of course, if you do use background threads, you need to be very careful to avoid concurrency issues. However it can be worth it in a lot of cases to improve performance.

Gotchas and Best Practices

  • Avoid async void in favour of async Task, except in ‘fire and forget’ cases where you want to start async code from non-async code
  • Attach the suffix ‘Async’ to all async methods which return Task. This is helpful in conveying the fact that it should always be preceded by an ‘await’ and allows an async void counterpart to be added easily without conflict
  • Debugging async methods using breakpoints in visual studio doesn’t work yet. However the “VS tools for Unity” team says that they are working on it, as indicated here

UniRx

Yet another way to do asynchronous logic is to use reactive programming with a library like UniRx. Personally I am a huge fan of this type of coding and use it extensively in many projects that I’m involved with. And thankfully, it is very easy to use alongside async-await with just another custom awaiter. For example:

1
2
3
4
5
6
7
8
9
10
public class AsyncExample : MonoBehaviour
{
    public Button TestButton;
 
    async void Start()
    {
        await TestButton.OnClickAsObservable();
        Debug.Log("Clicked Button!");
    }
}

I find that UniRx observables serve a different purpose from long-running async methods/coroutines, so they naturally fit alongside a workflow using async-await like in the examples above. I won’t go into detail here, because UniRx and reactive programming is a separate topic in itself, but I will say that once you get comfortable thinking about data flow in your application in terms of UniRx “streams”, there is no going back.

Source Code

You can download the source code that includes async-await support from either asset store or the releases section of the github repo.

Further Reading




[출처] http://www.stevevermeulen.com/index.php/2017/09/using-async-await-in-unity3d-2017/

반응형
Posted by blueasa
, |

 안드로이드 앱을 개발하다 보면 부득이하게 액티비티 화면을 가로, 혹은 세로로 고정해야 할 때가 있습니다. 예를 들자면 게임의 경우는 대부분 가로모드로 고정이 되어 실행되죠. 물론 액티비티 화면을 세로 모드로 고정해야 할 때도 있습니다. 화면의 레이아웃이 가로, 혹은 세로 모드에 최적화되어 있는 경우에는 부득이하게 화면의 회전을 강제로 막아야만 합니다.

 가로 모드는 Landscape, 세로 모드는 Portrait가 정식 명칭입니다. 안드로이드 액티비티의 화면을 Landscape 혹은 Portrait로 고정하고 싶은 경우, 두 가지 방법이 있습니다. 첫번째는 Manifest 파일에서 Activity에 screenOrientation 속성을 주는 방법이고, 두번째는 안드로이드 액티비티의 onCreate() 메소드 내에서 코드로 화면 오리엔테이션을 지정해주는 방법입니다.



 먼저 첫 번째로, Android Manifest 파일에 속성을 지정하는 방법입니다. 먼저 원하는 안드로이드 프로젝트의 AndroidManifest.xml 파일을 엽니다. 그리고 화면을 고정하기 원하는 액티비티의 이름을 찾아 아래 코드의 3번째(혹은 4번째) 열에 해당하는 코드를 추가합니다.


<activity android:name="com.example.appname.ActivityName"
            android:theme="@style/AppTheme"
            android:screenOrientation="portrait" //화면을 portrait(세로) 화면으로 고정하고 싶은 경우
            android:screenOrientation="landscape"> //화면을 landscape(가로) 화면으로 고정하고 싶은 경우
        </activity>



 두 번째로, 액티비티 실행 시에 onCreate() 메소드 내부에서 지정해주는 방법입니다. 원하는 안드로이드 프로젝트의 해당 액티비티를 열어서 아래 코드의 6열(8열)에 해당하는 코드를 추가해 줍니다.

@Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
         
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        // 화면을 portrait(세로) 화면으로 고정하고 싶은 경우
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        // 화면을 landscape(가로) 화면으로 고정하고 싶은 경우
         
        setContentView(R.layout.main);
        // setContentView()가 호출되기 전에 setRequestedOrientation()이 호출되어야 함
    }



이제 해당 프로젝트를 실행하면 지정한 액티비티의 화면이 회전하지 않는 것을 볼 수 있습니다.

출처: https://proletariat.tistory.com/86 [프롤레타리아, IT에 범접하다.]

반응형
Posted by blueasa
, |


[링크] https://xtasy.tistory.com/665

반응형
Posted by blueasa
, |


[링크] https://eunplay.tistory.com/54

반응형
Posted by blueasa
, |

이클립스/Eclipse JAR 파일 만들기



Java 프로젝트를 진행하다 보면, 보안상이나 라이브러리 구축을 이유로 JAR 파일을 만들게 되는 데요. 이클립스에서 JAR 파일을 만드는 방법에 대해 알아 보겠습니다.


(1) JAR 파일을 만들 해당 프로젝트에서 마우스 오른쪽 버튼을 클릭 합니다.


(2) 마우스 오른쪽 버튼을 클릭하면 나오는 메뉴중에 [Export] 라는 메뉴를 클릭해 줍니다. 그러면 아래의 Export 메뉴가 나오게 되는데 검색바에서 JAR 라고 입력해 주면, JAR 파일 Export 메뉴가 나오게 됩니다.




(3) [JAR file] 메뉴를 선택해 주시고, JAR 파일을 만들 프로젝트를 선택해 줍니다. 프로젝트를 클릭하면, JAR 파일을 만들 때 디테일한 부분 까지 선택하여 만들 수 있습니다. [Export generated class files and resources] 를 선택해 주시고 [Finish] 버튼을 클릭하시면 Class 형식으로 JAR 파일을 만들 수 있습니다. API 처럼 참조 할 수 있게 말이죠.




(4) 다음은 완성된 Jar 파일의 모습 입니다.




출처: https://arabiannight.tistory.com/entry/이클립스Eclipse-JAR-파일-만들기 [아라비안나이트]

반응형
Posted by blueasa
, |


안드로이드(Android) library projects cannot be launched 에러발생시 처치

 

개발환경 : window 7 64bit, Eclipse Mars, Android 4.2.2

 

이클립스를 또 하나 띄워서 다른workspce 

실행했는데 Android library projects cannot be launched

에러가 나면서 debung 가 되지 않았습니다.

 

안드로이드(Android) library projects cannot be launched 에러발생시 처치

 

이것은 현재 프로젝트가 라이브러리로 인식이 되어서

그렇습니다. Main 이 있는 실행 프로그램이 아니고

참조만 되는 라이브러리가 실행이 될리가 없겠죠.

이것은 나도 모르게 어떤 옵션을 체크해서 그런것인데

프로젝트를 선택하고 properties 옵션으로 갑니다.

화면 왼쪽에서 Android 를 선택하고 오른쪽으로 가서

Library 란에 Is Library 체크를 풉니다.

안드로이드(Android) library projects cannot be launched 에러발생시 처치



출처: https://mainia.tistory.com/1163 [녹두장군 - 상상을 현실로]

반응형
Posted by blueasa
, |