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

카테고리

분류 전체보기 (2737)
Unity3D (817)
Script (91)
Extensions (14)
Effect (3)
NGUI (77)
UGUI (8)
Physics (2)
Shader (36)
Math (1)
Design Pattern (2)
Xml (1)
Tips (200)
Link (22)
World (1)
AssetBundle (25)
Mecanim (2)
Plugins (70)
Trouble Shooting (68)
Encrypt (7)
LightMap (4)
Shadow (4)
Editor (8)
Crash Report (3)
Utility (9)
UnityVS (2)
Facebook SDK (2)
iTween (3)
Font (11)
Ad (14)
Photon (2)
IAP (1)
Google (8)
Android (45)
iOS (41)
Programming (474)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (228)
협업 (58)
3DS Max (3)
Game (12)
Utility (136)
Etc (96)
Link (32)
Portfolio (19)
Subject (90)
iOS,OSX (53)
Android (14)
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
04-24 00:01

It forces to sample current state of animations. "Sample animations" means: put character in the position defined by animation states. Sampling always happens between Update and LateUpdate automatically, but in some cases you might want to sample animations yourself. My most common case: I want to put character in pose of first frame some specific animation on Start, so I would do something like this:

 

void Start() 
{ 
	AnimationState state = animation["Aim"]; 
	state.enabled = true; 
	state.weight = 1; 
	state.normalizedTime = 0;

	animation.Sample();
}

 

[출처] https://answers.unity.com/questions/46869/animationsample-usage.html

반응형
Posted by blueasa
, |

NIST 타임서버에서 시간가져오기 (NST)

아래와 같은 코드로 가져올 수 있다.
Unity 에서 사용할 수 있다.

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

public class TimeServer : MonoBehaviour {

    [SerializeField]
    private string _comment = "만료시킬 날짜를 적으세요 (한국시각 기준)";

    public int _yyyy, _mm, _dd;
    private DateTime _expireDateTime, _nowServerDateTime, _nowLocalDateTime;
    private TimeSpan _duration;

 // Use this for initialization
 void Start () {

        // 한국 시각
        _duration = System.TimeSpan.FromHours(9);
        _expireDateTime = new DateTime(Mathf.Clamp(_yyyy, 1900, 3000), Mathf.Clamp(_mm, 1,12), Mathf.Clamp(_dd, 1, 31));

        _nowLocalDateTime = DateTime.Now;
        _nowServerDateTime = GetNISTDate().Add(_duration);

        if (Debug.isDebugBuild)
        {
            Debug.LogWarning("만료지정일 : " + _expireDateTime);
            Debug.LogWarning("현재 로컬 시각 :" + _nowLocalDateTime);
            Debug.LogWarning("현재 서버 시각 :" + _nowServerDateTime);
        }

        if (_nowLocalDateTime < _expireDateTime)
        {
            if (_nowServerDateTime < _expireDateTime)
            {
                // Debug.Log("실행");
            }
            else
            {
                // Debug.Log("서버 체크 결과 만료 됨");
            }
        }
        else
        {
            // Debug.Log("로컬 체크 결과 만료 됨");
        }


    }

#region NTPTIME

    //NTP time 을 NIST 에서 가져오기
    // 4초 이내에 한번 이상 요청 하면, ip가 차단됩니다.

    public static DateTime GetDummyDate()
    {
        return new DateTime(2017, 12, 24); //to check if we have an online date or not.
    }

    public static DateTime GetNISTDate()
    {
        System.Random ran = new System.Random(DateTime.Now.Millisecond);
        DateTime date = GetDummyDate();
        string serverResponse = string.Empty;

        // NIST 서버 목록
        string[] servers = new string[] {
        "time.bora.net",
        //"time.nuri.net",
        //"ntp.kornet.net",
        //"time.kriss.re.kr",
        //"time.nist.gov",
        //"maths.kaist.ac.kr",
        "nist1-ny.ustiming.org",
        "time-a.nist.gov",
        "nist1-chi.ustiming.org",
        "time.nist.gov",
        "ntp-nist.ldsbc.edu",
        "nist1-la.ustiming.org"
    };

        // 너무 많은 요청으로 인한 차단을 피하기 위해 한 서버씩 순환한다. 5번만 시도한다.
        for (int i = 0; i < 5; i++)
        {
            try
            {
                // StreamReader(무작위 서버)
                StreamReader reader = new StreamReader(new System.Net.Sockets.TcpClient(servers[ran.Next(0, servers.Length)], 13).GetStream());
                serverResponse = reader.ReadToEnd();
                reader.Close();

                // 서버 리스폰스를 표시한다. (디버그 확인용)
                if (Debug.isDebugBuild)
                    Debug.Log(serverResponse);

                // 시그니처가 있는지 확인한다.
                if (serverResponse.Length > 47 && serverResponse.Substring(38, 9).Equals("UTC(NIST)"))
                {
                    // 날짜 파싱
                    int jd = int.Parse(serverResponse.Substring(1, 5));
                    int yr = int.Parse(serverResponse.Substring(7, 2));
                    int mo = int.Parse(serverResponse.Substring(10, 2));
                    int dy = int.Parse(serverResponse.Substring(13, 2));
                    int hr = int.Parse(serverResponse.Substring(16, 2));
                    int mm = int.Parse(serverResponse.Substring(19, 2));
                    int sc = int.Parse(serverResponse.Substring(22, 2));

                    if (jd > 51544)
                        yr += 2000;
                    else
                        yr += 1999;

                    date = new DateTime(yr, mo, dy, hr, mm, sc);

                    // Exit the loop
                    break;
                }
            }
            catch (Exception e)
            {
                /* 아무것도 하지 않고 다음 서버를 시도한다. */
            }
        }
        return date;
    }
    #endregion
}

아래 코드 원본에서 주석 변경 및 내가 사용하기 위해 수정함.
경로 : https://stackoverflow.com/questions/516788/getting-current-gmt-time

 

[출처] http://blog.kpaper.com/2017/10/unity-nst-nist.html

반응형
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
, |
웹 페이지를 여는 방법을 찾고 있었는데....조금 다른 것이 들어왔네요 ㅎ
웹 상에 노출되어 있는 이미지 url를 입력하면 유니티 내에서 볼 수 있게 해 주는 코드입니다.
구글 클라우드에 있는 것들을 가져오면 앨범도 만들어 볼 수 있겠습니다.

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

public class example : MonoBehaviour 
{
public string url = "https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/6180.mali_2D00_shield_2D00_2.png";

IEnumerator Start() 
{
WWW www = new WWW(url);
yield return www;
Renderer renderer = GetComponent<Renderer>();
renderer.material.mainTexture = www.texture;
}
}


반응형
Posted by blueasa
, |

[출처http://theeye.pe.kr/archives/2725#disqus_thread

유니티에서 사용되는 코루틴(Coroutine)은 왜 필요한가?

유니티에서 화면의 변화를 일으키기 위해서는 Update() 함수 내에서 작업을 하게 됩니다. 이 Update() 함수는 매 프레임을 그릴때마다 호출되며 60fps의경우라면 초당 60번의 Update() 함수 호출이 발생하게 됩니다. 하나의 프레임 안에서 어떤 작업을 한다면 이 Update() 함수에 코드를 작성하면 될 것입니다.

하지만 다수의 프레임을 오가며 어떤 작업을 수행해야 한다면 어떻게 해야 할까요? 혹은 특정 시간, 가령 3초 동안 특정 작업을 수행해야 한다면 어떻게 해야 할까요? 3초니깐 3 x 60 = 180 프레임동안 작업을 수행하도록 하면 될까요?

안타깝게도 기기의 성능이나 상황에 따라 프레임 드랍(Frame drop)이라는 상황이 발생하게 됩니다. 60fps의 게임일지라 하더라도 디바이스의 성능에 따라 그 이하로 떨어질 수 있다는 의미가 됩니다. 이렇게 되면 더더욱 3초 동안 작업을 수행한다는게 쉽지 않은 일이 됩니다.

예로 다음의 코드를 준비하였습니다. 특정 오브젝트를 페이드 아웃(Fade out) 시키는 예제 코드입니다. 이 코드를 수행하면 스프라이트의 알파값이 점점 작아져서 결국 화면에서 사라지게 됩니다.






위의 코드를 보면 매 프레임마다 스프라이트 렌더러의 알파값을 0.1씩 감소시키고 있습니다. Update() 함수가 10번 호출되면 사라지게 되겠네요. 이는 1/6 초만에 사라지게 된다는것을 의미합니다. 이것도 1/6초만에 사라질지 보장받기가 어렵습니다.

그럼 혹시, 1초에 걸쳐 (60fps가 정상적으로 보장될 경우 60 프레임에 걸쳐) 사라지게 하려면 어떻게 하면 될까요? 대충 알파값을 0.017씩 감소시키면 될까요? 프레임이 아닌 시간 단위로 특정 작업을 수행할 수 있을까요? 여기서 생각할 수 있는 수단은 Time.deltaTime 이 있습니다.

하지만 우리가 여기서 알아보고자 하는것은 델타 타임이 아닌 코루틴이므로 코루틴에 대해서 알아보도록 하겠습니다. 코루틴은 프레임과 상관없이 별도의 서브 루틴에서 원하는 작업을 원하는 시간만큼 수행하는 것이 가능합니다.

다음은 코루틴을 사용하여 1초동안 페이드 아웃을 진행하는 예제 코드입니다.








이전 코드에서는 Update() 에서 모든 작업을 처리하던것을 Start() 에서 RunFadeOut() 코루틴을 실행하는것으로 변경된 것을 볼 수 있습니다. 여기서 주목해야 하는 부분은 yield return new WaitForSeconds(0.1f); 부분입니다.

이 복잡해 보이는 코드는 0.1초 동안 잠시 멈추라는 의미를 가진 코드입니다. 이제 위의 코드를 통해서 페이드 아웃 효과는 0.1초씩 10번을 수행하며 1초동안 사라지는 모습을 보여주게 됩니다. 이 코루틴은 Update() 함수에 종속적이지 않으며 마치 별도의 쓰레드와 같이 동작을 하게 됩니다. 이와 같은 코드로 프레임율에 영향을 받지 않는 시간 기반의 서브루틴을 구동할 수 있게 되었습니다.

IEnumerator와 yield는 무엇이며 어떤 관계가 있는가?

그렇다면 여기서 궁금증을 유발하는 부분이 몇가지 있는데요 RunFadeOut의 리턴 타입은 IEnumerator(열거자) 입니다. 또한 while 문 내부에 보면 yield(양보)라는 구문이 보이는군요. 그 뒤로 return이 따라나오는 것도 일반적인 언어에서 보기 힘든 문법입니다. 이것들이 어떤 관계를 가지고 있는지 알아보겠습니다.

우선 다음의 일반적인 C# 코드를 한번 살펴보도록 하겠습니다.





위의 Main() 함수를 실행하게 되면 다음과 같은 결과물이 출력됩니다.



조금 헷갈리지만 알고보면 어렵지 않은 코드입니다. 이 코드는 다음과 같은 순서로 동작하게 됩니다.

  1. SomeNumbers() 함수를 실행한 결과를 IEnumerator 열거자로 받습니다. 정확히는 실행된 결과가 아닙니다. enumerator 에 함수의 실행결과가 할당 되었다고 생각될만한 코드지만 여기서는 SomeNumbers() 함수는 한줄도 실행되지 않은 상태입니다. 함수의 포인터를 받았다고 생각하시는게 이해하시기 편할 것 같습니다.
  2. while 문을 만나면서 처음으로 enumerator의 MoveNext()가 호출됩니다. 여기서 SomeNumbers()가 실행이 되며 딱 yield 문을 만날때까지 실행이 됩니다.
  3. 첫번째 yield 문인 yield return 3; 을 만납니다. 여기서는 표현식 그대로 return 3에게 양보한다는 느낌으로 코드를 읽으시면 될 것 같습니다. 우선 여기까지 오면 3을 리턴하는것에 양보가 일어납니다. 이때에 리턴되는 값이 존재하므로 MoveNext()의 결과값으로 true가 반환됩니다.
  4. 이제 enumerator의 Current를 통해서 현재 반환된 값을 꺼내올 수 있습니다. MoveNext()를 통해서 yield return 되는 값이 있는지를 알 수 있고 반환된 값이 있다면 Current에 담겨 있게 됩니다.
  5. Debug.Log를 사용하여 Current를 출력해보면 처음으로 양보 반환된 3이 출력되게 됩니다.
  6. 다시한번 while문이 실행되며 MoveNext()가 호출되면 정말 재미있게도 가장 마지막에 yield return이 일어났던 위치의 다음줄부터 재실행이 되게 됩니다. 다시한번 yield 문을 만날때까지 진행이 됩니다.
  7. 이번에는 두번째 yield문인 yield return 5를 만나게 됩니다. 결과적으로 MoveNext() 의 결과값은 true가 되게 됩니다.
  8. 현재 Current에 할당된 값은 MoveNext()의 양보 반환된 값인 5가 될 것입니다.
  9. Debug.Log를 통해 값을 출력해보면 5가 출력됩니다.
  10. 다시한번 while문의 MoveNext()를 호출하면 yield return 5; 다음줄부터 재시작이 되게 되면 yield return 8;까지 진행이 되게 됩니다.
  11. 8이 양보 반환되었으므로 MoveNext()의 값은 true가 되며 Current에는 8이 들어가있게 됩니다.
  12. Debug.Log로 8이 출력됩니다.
  13. 다시한번 MoveNext() 가 호출되며 yield return 8; 이후의 코드부터 실행이 되지만 함수의 끝을 만나게 되므로 더이상 yield가 일어나지 않습니다.
  14. MoveNext() 의 결과 값으로 false가 반환되며 while 문이 종료됩니다.

조금 특이하지만 함수의 반환값이 IEnumerable, IEnumerable<T>, IEnumerator, IEnumerator<T> 인 경우에는 위와 같은 동작을 하게 됩니다. 함수의 동작이 비동기적으로 동작하게 되므로 파라미터에 ref나 out을 사용할 수 없다는 제약 사항이 있습니다. 위의 코드 동작 예시는 코루틴이 어떻게 동작하는지 알기위한 기본적인 코드라고 생각됩니다. 이제 다시 코루틴으로 돌아가 보겠습니다.







StartCoroutine을 직접 구현해 본다면 위와 같은 형태의 코드가 될 것 같습니다. 먼저 코루틴 함수의 포인터 역할을 하는 열거자를 받은 다음에 MoveNext()를 통해 첫번째 yield를 만날때까지 수행하고 그 결과값을 받습니다. 그리고 그 결과값에 맞는 작업을 수행해줍니다. 그리고 이것을 함수가 완료될 때까지 반복합니다.

위의 코드에서는 4번의 MoveNext()가 호출될 것이며 3번의 yield문을 만날 것입니다. 마지막 MoveNext()에서는 false가 반환될 것이므로 코루틴이 종료됩니다. 만약 함수의 실행이 완료되기 이전에 임의로 코루틴을 종료시키고 싶다면 yield break를 호출하면 됩니다. 즉시 MoveNext()에서 false가 반환되어 종료됩니다.

결론적으로 StartCoroutine은 IEnumerator를 반환하는 함수를 인자로 받으며 이 함수는 특이하게도 실행된 결과를 의미하는것이 아니라 함수 포인터와 같은 개념으로 사용이 됩니다.




이 코드를 한번 봐보겠습니다. 일반적인 함수의 개념으로 보자면 StartCoroutine에는 RunCoroutine() 함수의 결과값이 파라미터로 넘겨지게 되어있습니다. 하지만 여기서 RunCoroutine()은 단 한줄도 실행이 되지 않습니다. 함수의 포인터 역할을 하는 IEnumerator가 넘겨지게 되고 MoveNext()를 호출할 때마다 yield 문을 만날때까지 수행됩니다. 만나게 되면 MoveNext()가 true를 반환하고 함수가 끝나거나 yield break; 를 만나게 되면 false를 반환하게 됩니다. true를 반환할 경우 Current를 통해 반환된 값을 꺼내볼 수 있습니다.

StartCoroutine을 수행할 때 사용할 수 있는 두가지 방법

public Coroutine StartCoroutine(IEnumerator routine);

일반적으로 사용할 수 있는 방법입니다. 수행하고자 하는 코루틴의 IEnumerator 열거자를 넘겨서 실행되도록 합니다. 다음과 같은 방법으로 사용이 가능합니다.




위와 같은 방법은 일반적인 방법으로 waitTime 파라미터 값을 넘길 수 있으며 코루틴이 실행되는데에 추가적인 오버헤드가 전혀 없는 방법입니다. 뿐만 아니라 Start() 함수의 반환값을 IEnumerator로 변경하여 아예 코루틴이 실행 완료될때까지 기다리도록 의존적인 방법으로 실행하는 것도 가능합니다.




위의 코드는 WaitAndPrint(waitTime) 코루틴이 실행 완료된 이후에야 Done이 출력되는 과정을 보여줍니다.

public Coroutine StartCoroutine(string methodName, object value = null);

대부분의 경우는 StartCoroutine을 사용하기 위해 전자의 방법을 사용합니다. 하지만 StartCoroutine을 문자열 형태의 코루틴 함수 이름으로도 호출하는 것이 가능합니다. 이렇게 호출하면 StopCoroutine 역시 함수 이름만으로 호출하는것이 가능해 집니다.





위의 코드는 DoSomething(someParameter) 코루틴 함수를 함수 이름과 넘겨질 파라미터를 통해 호출하는 과정을 보여주고 있습니다. 그리고 1초 기다린 뒤에 실행했었던 DoSomething 코루틴을 종료시킵니다. 이러한 함수 이름을 문자열로 넘겨 실행하는 방법은 StartCoroutine을 수행하는데에 오버헤드가 크고 파라미터를 한개밖에 넘길 수 없다는 제약사항이 있습니다. 물론 배열을 넘기는것 역시 가능합니다.

yield return에서 사용할 수 있는 것들

위에서 본 예시에는 WaitForSeconds 클래스를 양보 반환함으로써 원하는 시간(초)만큼 기다리는 것이 가능하다는것을 알 수 있었습니다. 추가로 더 알아 보도록 하겠습니다.

yield return new WaitForSecondsRealtime (float time);

WaitForSeconds와 하는 역할은 동일하지만 결정적으로 다른것이 있습니다. 유니티상의 시간은 임의로 느리게 하거나 빠르게 하는 것이 가능합니다. 이를 Time.timeScale을 통해서 조정을 할 수 있습니다. 매트릭스에서 보던 총알이 느리게 날아오면서 그것을 피하는 모션을 구현해 본다면 이 값을 1보다 낮추게 되면 현재 시간의 진행보다 느려지게 되며 1보다 빠르게 변경하면 현재의 시간의 진행보다 빨라지게 됩니다. 하지만 WaitForSecondsRealtime는 이러한 Scaled Time의 영향을 받지 않고 현실 시간 기준으로만 동작을 하게 됩니다.

yield return new WaitForFixedUpdate ();

다음 FixedUpdate() 가 실행될때까지 기다리게 됩니다. 이 FixedUpdate()는 Update()와 달리 일정한 시간 단위로 호출되는 Update() 함수라고 생각하시면 됩니다.

yield return new WaitForEndOfFrame ();

하나의 프레임워 완전히 종료될 때 호출이 됩니다. Update(), LateUpdate() 이벤트가 모두 실행되고 화면에 렌더링이 끝난 이후에 호출이 됩니다. 특수한 경우에 사용하면 될 것 같습니다만 잘 모르겠군요.

yield return null;

WaitForEndOfFrame를 이야기 했다면 이것을 꼭 이야기 해야 할 것 같습니다. yield return null; 을 하게 되면 다음 Update() 가 실행될때까지 기다린다는 의미를 갖게 됩니다. 좀 더 정확하게는 Update()가 먼저 실행되고 null을 양보 반환했던 코루틴이 이어서 진행 됩니다. 그 다음에 LateUpdate()가 호출됩니다.

yield return new WaitUntil (System.Func<Bool> predicate);

이번엔 특정 조건식이 성공할때까지 기다리는 방법입니다. WaitUntil에 실행하고자 하는 식을 정의해 두면 매번 Update() 와 LateUpdate() 이벤트 사이에 호출해 보고 결과값이 true면 이후로 재진행을 하게 됩니다. 다음의 예제 코드를 보겠습니다.








이 코드는 Update() 함수를 통해 매 프레임마다 frame 멤버 변수값을 1씩 최대 10까지 증가시키게 됩니다. 실행중인 코루틴은 frame값이 10또는 10보다 커질때까지 기다리다가 이 식이 충족되게 되면 다음으로 진행을 하게 됩니다. 여기서 사용되는 식은 람다 표기법이 사용됩니다. 다음과 같은 느낌이라고 생각하시면 될 것 같습니다.

yield return new WaitWhile(System.Func<Bool> predicate);

WaitWhile은 WaitUntil과 동일한 목적을 가지고 있지만 한가지만 다릅니다. WaitUntil은 람다식 실행 결과값이 true가 될때까지 기다린다면 WaitWhile은 false가 될때까지 기다립니다. 즉 WaitWhile은 결과가 true인 동안 계속 기다리게 됩니다.








위의 코드는 첫프레임부터 람다식의 결과가 true이게 됩니다. 10프레임에 도달하면 false가 되어서 이후 진행이 되겠네요.

yield return StartCoroutine (IEnumerator coroutine);

이번에는 심지어 코루틴 내부에서 또다른 코루틴을 호출할 수 있습니다. 물론 그 코루틴이 완료될 때까지 기다리게 됩니다. 의존성 있는 여러작업을 수행하는데에 유리하게 사용 될 수 있습니다.







위와 같은 코드를 실행해 본다면 결과는 다음과 같이 출력됩니다.

Coroutine 중단하기

public void StopCoroutine(IEnumerator routine);

이 방법은 기존에 StartCoroutine을 실행할 때 넘겨주었던 코루틴 함수의 열거자를 파라미터로 사용하여 그것을 중단시키는 방법입니다. 다음과 같은 사용이 가능합니다.








Start() 함수에서 WaitAndPrint(waitTime) 코루틴 함수의 열거자를 획득하여 클래스의 멤버 변수로 설정해 두고 이 코루틴을 실행합니다. 이 코루틴은 1초에 한번씩 WaitAndPrint 를 출력하게 되며 유저가 스페이스키를 누르게 되면 멤버 변수에 담겨 있는 기존 코루틴의 열거자를 이용하여 실행중인 코루틴을 중단시킵니다.

public void StopCoroutine(string methodName);

이 방법은 이전 방식보다 오버헤드는 크지만 간편하게 사용할 수 있는 방법입니다. 다음과 같이 멤버 변수 없이도 간편하게 사용할 수 있습니다.







이때에 주의할 점으로는 StopCoroutine을 문자열로 종료시키려면 StartCoroutine 역시 문자열로 실행했었어야 한다는 점입니다. StartCoroutine(IEnumerator routine) 으로 실행한 다음에 StopCoroutine(string methodName) 으로 종료시킬 수 없습니다.

public void StopAllCoroutines();

마지막으로 현재 Behaviour (클래스라고 이해하면 될 것 같습니다)에서 실행한 모든 코루틴을 한번에 종료시키는 함수입니다. 이와 같은 방법으로 현재 클래스에서 실행한 모든 코루틴을 한번에 중단시키게 됩니다.





어디선가 Example()을 실행하게 되면 DoSomething 코루틴이 실행되게 되면 곧바로 StopAllCoroutines() 이 호출되어 모든 코루틴이 종료됩니다.

참고 :
http://docs.unity3d.com/kr/current/Manual/Coroutines.html
http://docs.unity3d.com/ScriptReference/MonoBehaviour.StartCoroutine.html


반응형
Posted by blueasa
, |

앞서 정리한 스탑코루틴의 활용 1의 내용으로 개념을 잡고 넘어가자.

 

 

코루틴 호출은 script.StartCoroutine() 으로 호출하게 되어있고,

앞부분을 생략하고 StartCoroutine()으로 호출하면

자동적으로 콜링한 스크립트에서 제어하는 코루틴으로 호출되도록 되어있다.

 

즉 StartCoroutine( ~~~ ) 를 포함하여 코루틴을 호출하면 이 코루틴은 자신을 콜링한 스크립트가 달려있는 게임오브젝트에 할당되는 것이다.

 

이것이 적용되어 해당 게임오브젝트를 disable시키면,

그 게임오브젝트에 할당되어 돌아가던 모든 코루틴은 정지된다. (StopAllCoroutine의 효과)

 

 

 

 

 

<예시1>

 

 

위의 그림을 참고하자.

 

위 그림은 이런 상황을 거친 결과다

Scr Main===

 start()

{

StartCoroutine(A);

}

 

Scr Manager====

{

GameObj1.GetComponent<Scr Main>().StartCoroutine(B);

GameObj2.GetComponent<Scr Main>().StartCoroutine(B);

 

GameObj1.GetComponent<Scr Main>().StartCoroutine(C);

GameObj2.GetComponent<Scr Sub>().StartCoroutine(C);

}

 

1. Main 스크립트는 생성되면서 동시에 코루틴A를 호출한다. 그래서 Main 스크립트가 붙어있는 게임오브젝트1,2에서 각각 시작되었다.

 

2 Manager에서 게임오브젝트1의 Main에 B와 C를 호출했다. (타 오브젝트, 타 스크립트에서 호출한 경우 )

이 때는 Manager의 게임오브젝트에 할당되지않고 호출타겟을 찾아간다.

마찬가지로 게임오브젝트 2 에는 Main에 코루틴B를, Sub에 코루틴 C를 호출하였다.

 

그 결과 위의 그림처럼 코루틴이 붙어 있게 된 모양.

 

 

 

Q. 여기서 게임오브젝트1의 Main에서 올스탑 코루틴을 콜하거나, 게임오브젝트 1번을 disable하면 어떻게 될까?

A. 게임오브젝트1의 코루틴 A,B,C가 종료된다.

 

Q. 게임오브젝트2를 disable 한다면?

A. 게임오브젝트의 Main의 A,B와 Sub의 C가 모두 종료된다.

 

Q. 임오브젝트2의 Main에서 StopAllCoroutine()을 호출한다면?

A. 게임오브젝트2의 Main의 A,B만 종료되고 Sub의 C는 살아있다.

 

 

위 상황만으로도 어느정도 코루틴들이 어떤 오브젝트의 어느스크립트에 포함되어 돌아가는가 유추할수 있다.

어려운 부분이 있다면 비슷한 내용으로 테스트를 해보길 바란다.

 

 

 

 

이러한 내용을 설명한 이유는 

코루틴에는 정립해야할 내용이 하나 더 있기 때문이다.

 

코루틴이 어떤 스크립트에 매칭되어 있는가 머리속에 그려진다면 스크립트에서 다른오브젝트의 스크립트의 코루틴을 호출하여 가지고 있을수 있다는 것을 알수 있다.

 

 

예를 들면

Scr A에서 선언한 변수 co가 있다고 해보자. 

 

coroutine co = GameObj1.GetComponent<Scr Main>().StartCoroutine(B);

 

위와같이 변수에 다른오브젝트 코루틴을 담아두었다.

 

Q.이것을 멈추기 위해서 어떻게 하겠는가?

 

StopCoroutine( co );

라고 생각하기 쉽지만, 이렇게 멈추게 되면 (코루틴이 멈추긴 한다)  

 

Coroutine continue failure  라는 에러메세지 가 발생한다.

 

정확하게 멈추기 위해서는

 

GameObj1.GetComponent<Scr Main>().StopCoroutine( co );

 

과 같이 코루틴이 할당된 스크립트에서 스탑을 시켜줘야한다.

 

 

위의 에러메세지가 나타나는 이유는 다른스크립트에서 코루틴을 스탑시키면 코루틴은 멈추지만,

기존에 코루틴을 돌리고 있던 스크립트에서 다른동작을 하고 다시 코루틴을 찾아올때 (코루틴은 IEnumerator 로 돌아간다)

이미 코루틴이 사라져서 찾을수 없게 되기 때문인것 같다.

(정확한 이유를 알기 위해선 유니티 엔진 내부를 보아야 할것이다) 



출처: http://qits.tistory.com/entry/StopCoroutine-의-활용-2-Coroutine-continue-failure [Quiet, In The Storm...]

반응형
Posted by blueasa
, |




[출처] C공식 게이ㅁ코디 방 - 폴리모프님


[참조] https://docs.unity3d.com/kr/current/Manual/class-ScriptExecution.html

반응형
Posted by blueasa
, |

[펌] Unity BigInteger

Unity3D/Script / 2017. 4. 26. 19:51

[File]

BigInteger.zip




BigInteger


現時点、unityでサポートされている.Netのバージョンは3.5相当となっている(2015/01/15現在)
この事から.Net4.0からサポートされたクラスは利用できない状態となっている


C#コードを利用した幾何解析などの演算では、扱う数字の桁数に制限がない BigInteger クラスが必要になる場合がある
そこで、検索を利用して探してみるとMITライセンスの BigInteger クラスが公開されていたので、これを利用する事にした
unityが将来.Net4.0をサポートした時の事を考え、このMIT版BigInteger に少し改編を施す。利用側のコード改編が必要ない形にしておく


資料:


<雛形とするBigIntegerクラス>


<unity用に名前空間を書き換えたBigIntegerクラス>
Scott Garland氏が作成されたBigIntegerクラスです。ライセンスはMITライセンスとして指定されています。これを使えばunityでBigIntegerの機能が利用できます
コード内の名前空間に「ScottGarland」が使われているのでこの部分を「System.Numerics」に書き換えunityの将来のバージョンアップに備えます
(少し行儀の悪い方法ですが一時的な利用になると予想しています)



ダウンロードした「BigInteger.cs」ファイルをunityのAssetsフォルダ内にコピーし「System.Numerics」名前空間を経由して他のコードから利用する事にします
当サイト内でBigIntegerをサンプルコード内で利用する場合、unityが.Net4.0に対応するまでは、このクラスを利用します



[출처] http://unitylab.wiki.fc2.com/wiki/BigInteger

반응형
Posted by blueasa
, |
  1. var fileStream : FileStream;
  2. //creating a folder for the Files
  3. var pathToAudioData : String = "ThePathWithFolderName";
  4. if (!Directory.Exists (pathToAudioData)){
  5. Directory.CreateDirectory (pathToAudioData);
  6. }
  7. var url: String = "http://www.TheURL.ch";
  8. var request : WWW = new WWW (url);
  9. yield request;
  10. File.WriteAllBytes(pathToAudioData+"/"+"xy.ogg", request.bytes);




[출처] http://answers.unity3d.com/questions/703879/download-and-save-ogg-files.html

반응형

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

Unity3D MonoBehaviour Lifecycle(흐름도)  (0) 2017.12.20
[펌] Unity BigInteger  (0) 2017.04.26
[펌] StreamingAssets 폴더 지정하기  (0) 2016.11.10
Invert ParticleEffect Velocity  (0) 2016.11.09
[펌] Parallax Scrolling  (0) 2016.10.27
Posted by blueasa
, |

StreamingAssets 폴더는 Platform마다 다르다.

게다가 더 기가막힌게, Application.streamingAssetsPath를 그대로 믿고 사용할 수 없다는 것이다.

게다가 Unity 문서는 지나간 내용을 설명하고 있으며, 잘못된 내용을 설명하고 있다.

http://docs.unity3d.com/kr/current/Manual/StreamingAssets.html

이런~~~~


정답은 아래와 같다.

#if UNITY_EDITOR || UNITY_STANDALONE_WIN

public static readonly string StreamingPath = "file://" + Application.streamingAssetsPath + "/";

#elif UNITY_ANDROID

public static readonly string StreamingPath = Application.streamingAssetsPath + "/";

#elif UNITY_IOS

public static readonly string StreamingPath = "file://" + Application.streamingAssetsPath + "/";

#endif




[출처] http://baramlife.tistory.com/7

반응형
Posted by blueasa
, |