2019-12-19 23:41:04 zhunju0089 阅读数 23
  • Unity3D协程-基础篇

    本课程为Unity3D协程技术精讲中的基础部分,协程是UnityEngine技术里面为常用且设计为精髓的技术;基础部分主要讲解协成的前世今生,其中包括设计模式、实现模式、验证模式的递进式推进过程让同学们掌握协程原理与程序设计。

    1956 人正在学习 去看看 曾家海

协程脚本:

using System.Collections;
using UnityEngine;

public class MyTest : MonoBehaviour
{
    // 声明一个协程
    Coroutine coroutine;
    void Start()
    {
        // 定义协程
        coroutine = StartCoroutine(MyCoroutine());
        // 停止协程
        StopCoroutine(coroutine);
    }
    // 运行的协程程序
    IEnumerator MyCoroutine()
    {
        yield return new WaitForSeconds(0.1f);
        for (int i = 0; i < 100; i++)
        {
            Debug.Log(i);
        }
    }
}

 

 

2016-08-08 17:09:09 u010832643 阅读数 941
  • Unity3D协程-基础篇

    本课程为Unity3D协程技术精讲中的基础部分,协程是UnityEngine技术里面为常用且设计为精髓的技术;基础部分主要讲解协成的前世今生,其中包括设计模式、实现模式、验证模式的递进式推进过程让同学们掌握协程原理与程序设计。

    1956 人正在学习 去看看 曾家海

本文转自:Unity3D协程介绍以及使用

作者ChevyRay 2013928日,snaker7译  原文地址:http://unitypatterns.com/introduction-to-coroutines/

Unity中,协程(Coroutines)的形式是我最喜欢的功能之一,几乎在所有的项目中,我都会使用它来控制运动,序列,以及对象的行为。在这个教程中,我将会说明协程是如何工作的,并且会附上一些例子来介绍它的用法。

协程介绍


Unity的协程系统是基于C#的一个简单而强大的接口 ,IEnumerator,它允许你为自己的集合类型编写枚举器。这一点你不必关注太多,我们直接进入一个简单的例子来看看协程到底能干什么。首先,我们来看一下这段简单的代码...

倒计时器

这是一个简单的脚本组件,只做了倒计时,并且在到达0的时候log一个信息。

[csharp]view plaincopy
using Unity Engine;  
using System.Collections;  
publicclass Countdown : MonoBehaviour  
{  
publicfloat timer = 3;  
void Update()  
    {  
        timer -= Time.deltaTime;  
if(timer <= 0)  
            Debug.Log("Timer has finished!");  
    }  
}  


还不错,代码简短实用,但问题是,如果我们需要复杂的脚本组件(像一个角色或者敌人的类),拥有多个计时器呢?刚开始的时候,我们的代码也许会是这样的:

[csharp]view plaincopy
using UnityEngine;  
using System.Collections;  
publicclass MultiTimer : MonoBehaviour  
{  
publicfloat firstTimer = 3;  
publicfloat secondTimer = 2;  
publicfloat thirdTimer = 1;  
void Update()  
    {  
        firstTimer -= Time.deltaTime;  
if(firstTimer <= 0)  
            Debug.Log("First timer has finished!");  
        secondTimer -= Time.deltaTime;  
if(secondTimer <= 0)  
            Debug.Log("Second timer has finished!");  
        thirdTimer -= Time.deltaTime;  
if(thirdTimer <= 0)  
            Debug.Log("Third timer has finished!");  
    }  
}  

尽管不是太糟糕,但是我个人不是很喜欢自己的代码中充斥着这些计时器变量,它们看上去很乱,而且当我需要重新开始计时的时候还得记得去重置它们(这活我经常忘记做)。

如果我只用一个for循环来做这些,看上去是否会好很多?

[csharp]view plaincopy
for(float timer = 3; timer >= 0; timer -= Time.deltaTime)  
{  
//Just do nothing...
}  
Debug.Log("This happens after 5 seconds!");  

现在每一个计时器变量都成为for循环的一部分了,这看上去好多了,而且我不需要去单独设置每一个跌倒变量。

好的,你可能现在明白我的意思:协程可以做的正是这一点!

码入你的协程!

现在,这里提供了上面例子运用协程的版本!我建议你从这里开始跟着我来写一个简单的脚本组件,这样你可以在你自己的程序中看到它是如何工作的。

[csharp]view plaincopy
using UnityEngine;  
using System.Collections;  
publicclass CoroutineCountdown : MonoBehaviour  
{  
void Start()  
    {  
        StartCoroutine(Countdown());  
    }  
    IEnumerator Countdown()  
    {  
for(floattimer = 3; timer >= 0; timer -= Time.deltaTime)  
            Yield return 0;  
        Debug.Log("This message appears after 3 seconds!");  
    }  
}  

这看上去有点不一样,没关系,接下来我会解释这里到底发生了什么。

  1. StartCoroutine(Countdown());  

这一行用来开始我们的Countdown程序,注意,我并没有给它传入参数,但是这个方法调用了它自己(这是通过传递Countdown的return返回值来实现的)。

Yield

在Countdown方法中其他的都很好理解,除了两个部分:

l IEnumerator 的返回值

l For循环中的yield return

为了能在连续的多帧中(在这个例子中,3秒钟等同于很多帧)调用该方法,Unity必须通过某种方式来存储这个方法的状态,这是通过IEnumerator 中使用yield return语句得到的返回值,当你“yield”一个方法时,你相当于说了,“现在停止这个方法,然后在下一帧中从这里重新开始!”。

注意:用0或者nullyield的意思是告诉协程等待下一帧,直到继续执行为止。当然,同样的你可以继续yield其他协程,我会在下一个教程中讲到这些。

一些例子

协程在刚开始接触的时候是非常难以理解的,无论是新手还是经验丰富的程序员我都见过他们对于协程语句一筹莫展的时候。因此我认为通过例子来理解它是最好的方法,这里有一些简单的协程例子:

多次输出“Hello

记住,yield return是“停止执行方法,并且在下一帧从这里重新开始”,这意味着你可以这样做:

[csharp]view plaincopy
//This will say hello 5 times, once each frame for 5 frames
IEnumerator SayHelloFiveTimes()  
{  
    Yield return 0;  
    Debug.Log("Hello");  
    Yield return 0;  
    Debug.Log("Hello");  
    Yield return 0;  
    Debug.Log("Hello");  
    Yield return 0;  
    Debug.Log("Hello");  
    Yield return 0;  
    Debug.Log("Hello");  
}  
//This will do the exact same thing as the above function!
IEnumerator SayHello5Times()  
{  
for(inti = 0; i < 5; i++)  
    {  
        Debug.Log("Hello");  
        Yield return 0;  
    }  
}  


每一帧输出“Hello”,无限循环。。。

通过在一个while循环中使用yield,你可以得到一个无限循环的协程,这几乎就跟一个Update()循环等同。。。

//Once started, this will run until manually stopped or the object is destroyed
IEnumerator SayHelloEveryFrame()  
{  
while(true)  
    {  
//1. Say hello
        Debug.Log("Hello");  
//2. Wait until next frame
        Yield return 0;  
    }//3. This is a forever-loop, goto 1
}

计时

...不过跟Update()不一样的是,你可以在协程中做一些更有趣的事:

[csharp]view plaincopy
IEnumerator CountSeconds()  
{  
int seconds = 0;  
while(true)  
    {  
for(float timer = 0; timer < 1; timer += Time.deltaTime)  
            Yield return 0;  
        seconds++;  
        Debug.Log(seconds +" seconds have passed since the Coroutine started.");  
    }  
}  

这个方法突出了协程一个非常酷的地方:方法的状态被存储了,这使得方法中定义的这些变量都会保存它们的值,即使是在不同的帧中。还记得这个教程开始时那些烦人的计时器变量吗?通过协程,我们再也不需要担心它们了,只需要把变量直接放到方法里面!

开始和终止协程

之前,我们已经学过了通过 StartCoroutine()方法来开始一个协程,就像这样:

[csharp]view plaincopy
StartCoroutine(Countdown());  

如果我们想要终止所有的协程,可以通过StopAllCoroutines()方法来实现,它的所要做的就跟它的名字所表达的一样。注意,这只会终止在调用该方法的对象中(应该是指调用这个方法的类吧)开始的协程,对于其他的MonoBehavior类中运行的协程不起作用。

如果我们有以下这样两条协程语句:

[csharp]view plaincopy
StartCoroutine(Countdown());  

。。。那我们怎么终止其中的一个协程呢?在这个例子里,这是不可能的,如果你想要终止某一个特定的协程,那么你必须得在开始协程的时候将它的方法名作为字符串,就像这样:

[csharp]view plaincopy
//If you start a Coroutine by name...
StartCoroutine("FirstTimer");  
StartCoroutine("SecondTimer");  
//You can stop it anytime by name!
StopCoroutine("FirstTimer");  


更多关于协程的学习

即将为你带来:“Scripting with Coroutines”,一个更深入的介绍,关于如何使用协程以及如何通过协程编写对象行为。


扩展链接

Coroutines – Unity Script Reference

如果你知道其他很棒的关于协程的Unity教程,或者相关的主题,请在回复中分享链接!当然,如果在教程有什么问题,比如链接无效或者其他一些问题,欢迎给我发邮件


作者ChevyRay ,2013年9月28日,snaker7译 原文地址:http://unitypatterns.com/scripting-with-coroutines/

请注意:这个关于协程的教程共有两部分,这是第二部分,如果您未曾看过第一部分——协程介绍,那么在阅读这部分内容之前建议您先了解一下。

计时器例子

第一个教程中,我们已经了解了协程如何让一个方法“暂停”下来,并且让它yield直到某些值到达我们给定的数值;并且利用它,我们还创建了一个很棒的计时器系统。协程一个很重要的内容是,它可以让普通的程序(比方说一个计时器)很容易地被抽象化并且被复用。

协程的参数

抽象化一个协程的第一个方法是给它传递参数,协程作为一个函数方法来说,它自然能够传递参数。这里有一个协程的例子,它在特定的地方输出了特定的信息。

[csharp] view plaincopy
Using UnityEngine;  
Using System.Collections;  
   
Public class TimerExample : MonoBehaviour  
{  
    Void Start()  
    {  
        //Log "Hello!" 5 times with 1 second between each log  
        StartCoroutine(RepeatMessage(5, 1.0f,"Hello!"));  
    }  
   
    IEnumerator RepeatMessage(int count,float frequency,string message)  
    {  
        for(int i = 0; i < count; i++)  
        {  
            Debug.Log(message);  
            for(float timer = 0; timer < frequency; timer += Time.deltaTime)  
                Yield return 0;  
               
        }  
    }  
}  

嵌套的协程

在此之前,我们yield的时候总是用0(或者null),仅仅告诉程序在继续执行前等待下一帧。协程最强大的一个功能就是它们可以通过使用yield语句来相互嵌套。

眼见为实,我们先来创建一个简单的Wait()程序,不需要它做任何事,只需要在运行的时候等待一段时间就结束。

[csharp] view plaincopy
IEnumerator Wait(float duration)  
{  
    for(float timer = 0; timer < duration; timer += Time.deltaTime)  
        Yield return 0;  
}  

接下来我们要编写另一个协程,如下:

[csharp] view plaincopy
Using UnityEngine;  
Using System.Collections;  
   
Public class TimerExample : MonoBehaviour  
{  
    voidStart()  
    {  
        StartCoroutine(SaySomeThings());  
    }  
   
    //Say some messages separated by time  
    IEnumerator SaySomeThings()  
    {  
        Debug.Log("The routine has started");  
        Yield return StartCoroutine(Wait(1.0f));  
        Debug.Log("1 second has passed since the last message");  
        Yield return StartCoroutine(Wait(2.5f));  
        Debug.Log("2.5 seconds have passed since the last message");  
    }  
   
    //Our wait function  
    IEnumerator Wait(float duration)  
    {  
        for(float timer = 0; timer < duration; timer += Time.deltaTime)  
            Yield return 0;  
    }  
}  

第二个方法用了yield,但它并没有用0或者null,而是用了Wait()来yield,这相当于是说,“不再继续执行本程序,直到Wait程序结束”。

现在,协程在程序设计方面的能力要开始展现了。

控制对象行为的例子

在最后一个例子中,我们就来看看协程如何像创建方便的计时器一样来控制对象行为。协程不仅仅可以使用可计数的时间来yield,它还能很巧妙地利用任何条件。将它与嵌套结合使用,你会得到控制游戏对象状态的最强大工具。

运动到某一位置

对于下面这个简单脚本组件,我们可以在Inspector面板中给targetPosition和moveSpeed变量赋值,程序运行的时候,该对象就会在协程的作用下,以我们给定的速度运动到给定的位置。

[csharp] view plaincopy
usingUnityEngine;  
Using System.Collections;  
   
Public class MoveExample : MonoBehaviour  
{  
    ublic Vector3 targetPosition;  
    ublic float moveSpeed;  
   
    Void Start()  
    {  
        StartCoroutine(MoveToPosition(targetPosition));  
    }  
   
    IEnumerator MoveToPosition(Vector3 target)  
    {  
        while(transform.position != target)  
        {  
            transform.position = Vector3.MoveTowards(transform.position, target, moveSpeed * Time.deltaTime);  
            Yield return 0;  
        }  
    }  
}  

这样,这个程序并没有通过一个计时器或者无限循环,而是根据对象是否到达指定位置来yield。

按指定路径前进

我们可以让运动到某一位置的程序做更多,不仅仅是一个指定位置,我们还可以通过数组来给它赋值更多的位置,通过MoveToPosition() ,我们可以让它在这些点之间持续运动。

[csharp] view plaincopy
Using UnityEngine;  
Using System.Collections;  
   
Public class MoveExample : MonoBehaviour  
{  
    ublic Vector3[] path;  
    ublic float moveSpeed;  
   
    Void Start()  
    {  
        StartCoroutine(MoveOnPath(true));  
    }  
   
    IEnumerator MoveOnPath(bool loop)  
    {  
        do  
        {  
            foreach(var point in path)  
                Yield return StartCoroutine(MoveToPosition(point));  
        }  
        while(loop);  
    }  
   
    IEnumerator MoveToPosition(Vector3 target)  
    {  
        while(transform.position != target)  
        {  
            transform.position = Vector3.MoveTowards(transform.position, target, moveSpeed * Time.deltaTime);  
            Yield return 0;  
        }  
    }  
}  

我还加了一个布尔变量,你可以控制在对象运动到最后一个点时是否要进行循环。

把Wait()程序加进来,这样就能让我们的对象在某个点就可以选择是否暂停下来,就像一个正在巡逻的AI守卫一样,这真是锦上添花啊!

注意:

如果你刚接触协程,我希望这两个教程能帮助你了解它们是如何工作的,以及如何来使用它们。以下是一些在使用协程时须谨记的其他注意事项:


  • l 在程序中调用StopCoroutine()方法只能终止以字符串形式启动(开始)的协程;
  • l 多个协程可以同时运行,它们会根据各自的启动顺序来更新;
  • l 协程可以嵌套任意多层(在这个例子中我们只嵌套了一层);
  • l 如果你想让多个脚本访问一个协程,那么你可以定义静态的协程;
  • l 协程不是多线程(尽管它们看上去是这样的),它们运行在同一线程中,跟普通的脚本一样;
  • l 如果你的程序需要进行大量的计算,那么可以考虑在一个随时间进行的协程中处理它们;
  • l IEnumerator类型的方法不能带ref或者out型的参数,但可以带被传递的引用;
  • l 目前在Unity中没有简便的方法来检测作用于对象的协程数量以及具体是哪些协程作用在对象上。


如果您发现教程中存在问题和错误的信息,或者有任何建议又或者您想要在这里看到其他需要的教程,可以发邮件或者在评论中留言。


2019-09-06 11:22:01 clark_ya 阅读数 124
  • Unity3D协程-基础篇

    本课程为Unity3D协程技术精讲中的基础部分,协程是UnityEngine技术里面为常用且设计为精髓的技术;基础部分主要讲解协成的前世今生,其中包括设计模式、实现模式、验证模式的递进式推进过程让同学们掌握协程原理与程序设计。

    1956 人正在学习 去看看 曾家海

在Unity3D的协程中,如果发生异常,是无法捕获到异常的,try catch不允许跨yield使用,finally也不能确保代码块在协程异常结束时还能被执行,所以很多时候无法知道一个协程是否正常执行结束,出现错误也不方便查找原因,根据Unity3D协程其本质是一个迭代器的原理,我设计了一个可以在协程执行过程中注入代码块,捕获异常的可拦截迭代器InterceptableEnumerator。使用InterceptableEnumerator对原迭代器进行包装,就可以捕获到协程代码执行异常,并且无论协程是否正常结束,都可在协程退出前插入一个代码块,确保这个代码块一定会在协程结束时执行。在我的Executors中,我就是利用InterceptableEnumerator来确保任务正常结束的,无论协程执行成功或者异常我都能通过注册的Finally语句块来设置AsyncResult的结果,确保AsyncResult.IsDone等于true,不会造成任务卡死。
InterceptableEnumerator支持条件语句块,可以在外部插入一个条件语句块,控制协程逻辑或中止协程。异常语句块,可以捕获到协程异常,Finally语句块,确保协程结束一定会调用这个语句块。下面我们来看看示例。

/// <summary>
/// 这是一个迭代器的包装函数
/// </summary>
protected static InterceptableEnumerator WrapEnumerator(IEnumerator routine, IPromise promise)
{
    InterceptableEnumerator enumerator;
    if(routine is InterceptableEnumerator)
        enumerator = (InterceptableEnumerator)routine;
    else
        enumerator = new InterceptableEnumerator(routine);

    //注册一个条件语句块,如果任务取消,IsCancellationRequested = true,则结束任务
    enumerator.RegisterConditionBlock(() => !(promise.IsCancellationRequested));

    //注册一个异常捕获语句块,如果协程执行错误,则将异常赋值到任务结果,并打印错误
    enumerator.RegisterCatchBlock(e =>
    {
        if (promise != null)
            promise.SetException(e);
    
        if (log.IsErrorEnabled)
            log.Error(e);
    });

    //注册一个Finally语句块,确保任务能够正常结束退出
    enumerator.RegisterFinallyBlock(() =>
    {
        if (promise != null && !promise.IsDone)
        {
            if (promise.GetType().IsSubclassOfGenericTypeDefinition(typeof(IPromise<>)))
                promise.SetException(new Exception("No value given the Result"));
            else
                promise.SetResult();
        }
    });
    return enumerator;
}

这个可拦截迭代器是我的Unity3D MVVM框架 Loxodon.Framework 中的源码,有需要的朋友可以在github下载。
迭代器源码地址:InterceptableEnumerator 请点击下载。

2016-05-17 14:57:58 Gary_888 阅读数 4531
  • Unity3D协程-基础篇

    本课程为Unity3D协程技术精讲中的基础部分,协程是UnityEngine技术里面为常用且设计为精髓的技术;基础部分主要讲解协成的前世今生,其中包括设计模式、实现模式、验证模式的递进式推进过程让同学们掌握协程原理与程序设计。

    1956 人正在学习 去看看 曾家海

Unity3d协程的知识,不了解的同学可以在网上查找一下相关资料或者看一下Unity3D协程介绍 以及 使用

下面介绍Unity3d协程实现倒数计时,实现代码:

public class GameManager : MonoBehaviour
{
    private bool _BoolIsDisplayNumber = false;//是否显示数字
    private bool _BoolIsDisplayGo = false;//是否显示go
    private float _IntDisplayNumber;
    private string _StringDisplayNumber;

    public Texture TextNum3;//要显示的图片3
    public Texture TextNum2;
    public Texture TextNum1;
    public Texture TextGo;

    void Start()
    {
        StartCoroutine(DisplayGameStartCountDown());
    }

    IEnumerator DisplayGameStartCountDown()
    {
        yield return new WaitForEndOfFrame();

        //显示3
        _BoolIsDisplayNumber = true;
        _IntDisplayNumber = 3;
        yield return new WaitForSeconds(1f);
        _BoolIsDisplayNumber = false;
        yield return new WaitForSeconds(0.5f);

        //显示2
        _BoolIsDisplayNumber = true;
        _IntDisplayNumber = 2;
        yield return new WaitForSeconds(1f);
        _BoolIsDisplayNumber = false;
        yield return new WaitForSeconds(0.5f);

        //显示1
        _BoolIsDisplayNumber = true;
        _IntDisplayNumber = 1;
        yield return new WaitForSeconds(1f);
        _BoolIsDisplayNumber = false;
        yield return new WaitForSeconds(0.5f);

        //显示go
        _BoolIsDisplayGo = true;
        _StringDisplayNumber = "go";
        yield return new WaitForSeconds(1f);
        _BoolIsDisplayGo = false;
        yield return new WaitForSeconds(0.5f);
    }

    void OnGUI()
    {
        //使用GUI显示图片
        if (_BoolIsDisplayNumber)
        {
            if (_IntDisplayNumber == 3)   //显示3
            {
                GUI.DrawTexture(new Rect(Screen.width / 2 - TextNum3.width / 2, Screen.height / 2 - TextNum3.height / 2,
                                         TextNum3.width, TextNum3.height), TextNum3);
            }
            else if (_IntDisplayNumber == 2)   //显示2
            {
                GUI.DrawTexture(new Rect(Screen.width / 2 - TextNum2.width / 2, Screen.height / 2 - TextNum2.height / 2,
                                         TextNum2.width, TextNum2.height), TextNum2);
            }
            else if (_IntDisplayNumber == 1)   //显示1
            {
                GUI.DrawTexture(new Rect(Screen.width / 2 - TextNum1.width / 2, Screen.height / 2 - TextNum1.height / 2,
                                         TextNum1.width, TextNum1.height), TextNum1);
            }
        }

        if (_BoolIsDisplayGo)
        {
            if (_StringDisplayNumber == "go")   //显示go
            {
                GUI.DrawTexture(new Rect(Screen.width / 2 - TextGo.width / 2, Screen.height / 2 - TextGo.height / 2,
                                         TextGo.width, TextGo.height), TextGo);
            }
        }
    }
}
具体实现代码如上所示,并不复杂。

在Inspector面板为Texture赋值,直接将图上拖上去就可以了。


运行效果:


这样就完成了协程实现倒数计时。不懂的同学可以私聊哦。

2018-01-08 23:36:45 github_34181815 阅读数 116
  • Unity3D协程-基础篇

    本课程为Unity3D协程技术精讲中的基础部分,协程是UnityEngine技术里面为常用且设计为精髓的技术;基础部分主要讲解协成的前世今生,其中包括设计模式、实现模式、验证模式的递进式推进过程让同学们掌握协程原理与程序设计。

    1956 人正在学习 去看看 曾家海

Unity3D 是单线程的

StartCoroutine 开启协程

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

yield 暂停协程执行

yield return null  //暂停一帧,然后再执行

StopCoroutine 停止一个协程

StopCoroutine只能停止同一个游戏脚本中的方法名和传入string型参数相同的协程,而无法影响别的脚本中开启的协程,StopCoroutine方法只能用来停止那些使用了StartCoroutine的string型参数的重载版本开启的协程

协程延时效果

  • WaitForSeconds 暂停几秒
  • WaitForFixedUpdate 暂停协程直到下一次FixedUpdate时才会继续
  • WaitForEndOfFrame 等到所有摄像机和GUI被渲染完成后再恢复协程执行

迭代器

  • IEnumerable
//非泛型
public interface IEnumerable
{
  IEnumerator  GetEnumerator();
}

//泛型
public interface IEumerable<out T> :IEumberable
{
  IEnumerator<T> GetEnumerator();
  IEnumerator   GetEnumerator();
}
  • IEnumerator
//非泛型
public interface IEumerator
{
  Object Current {get;}     //当前所指向的值
  bool MoveNext();          //指向下一个结点
  void Reset();
}

//泛型
public interface IEnumerator<out T>: IDisposable, IEnumerator
{
  void Dispose();
  Object Current {get;}        
  T Current {get;}
  bool MoveNext();
  void Reset();
}

迭代器块中的局部变量会被分配到堆上

WWW和协程

WWW类是提供一个用来从提供的URL获取内容的工具类,并且返回该实例来下载URL的内容。WWW类的构造函数除了创建一个新的实例之外,还会创建和发送一个GET请求,并且会自动开启一个流来下载从URL获取内容

使用WWW实现GET请求和POST请求

public class HttpWrapper : MonoBehaviour
{
  public void GET(string url, Action<WWW> onSuccess, Action<WWW> onFail = null)
  {
    WWW www = new WWW(url);
    StartCoroutine(WaitForResponse(www, onSuccess, onFail));
  }

  public void POST(string url, Dictionary<string, string> post, Action<WWW> onSuccess, Action<WWW> onFail)
  {
    WWWForm form = new WWWForm();
    foreach(KeyValuePair<string, string> post_arg in post)
    {
      form.AddField(post_arg.Key, post_arg.Value);
    }
    WWW www = new WWW(url, form);

    StartCoroutine(WaitForResponse(www, onSuccess, onFail));
  }

  private IEumerator WaitForResponse(WWW www, Action<WWW> onSuccess, Action<WWW> onFail = null)
  {
    yield return null;
    if(www.error == null)
    {
      onSuccess(www);
    }
    else
    {
      Debug.LogError("WWW Error");
      if(onFail != null)  onFail(www);
    }
  }
}

参考

Unity 3D脚本编程—-使用c#语言开发跨平台

unity 协程 详细说明

阅读数 1427

没有更多推荐了,返回首页