• c#批量抓取免费代理并验证有效性


    之前看到某公司的官网的文章的浏览量刷新一次网页就会增加一次,给人的感觉不太好,一个公司的官网给人如此直白的漏洞,我批量发起请求的时候发现页面打开都报错,100多人的公司的官网文章刷新一次你给我看这个,这公司以前来过我们学校宣传招人+在园子里搜招聘的时候发现居然以前招xamarin,挺好奇的,所以就关注过。好吧不说这些了,只是扯扯蛋而已,回归主题,我想说的是csdn的文章可以通过设置代理ip刷新文章的浏览量,所以首先要做的就是这篇文章的主题“使用c#验证代理ip有效性”。

    当然代理IP来源肯定是免费,所以嘛效率一般,从一些免费的代理ip的网页抓取的代理IP并不一定都是有用的,所以需要我们对我们抓取的代理ip进行验证,代理ip的有效时间也是有限,从10几秒到1个小时不限,大多数时间非常短,所以比如说,我们1分钟需要100个代理ip,那就1分钟获取一次,每次获取100个(这里是理想状态下的,抓取的代理ip都是有效的),原则上来说抓取下来后应该立即马上被使用。

    当然这篇文章比较基础,一直觉得爬虫比较有趣,其实我在爬虫方面也是个小白,只是做一个简单的记录,如果有什么错误的地方,希望能提出建议。针对下面几个问题,我们就可以完成如何验证代理IP有效性的检测了。

    1.从哪些网页上可以抓取免费的代理IP?

    http://www.xicidaili.com

    http://www.ip3366.net

    http://www.66ip.cn

    百度一下“免费代理ip”挺多的。

    2.代理IP稳定吗?有什么作用?

    这种免费的代理ip时效性和有效性都不强,上面这三个免费的代理网站,时效性大概在十几秒到1个小时不等,一般需要自己处理验证后使用,提高命中率。可适用于隐藏网页IP(有些网站还不准使用代理ip,比如豆瓣,其实挺尴尬的,内容这么贵吗),一般常用于空间留言、刷网站流量、网赚任务、批量注册账号等,只要没有其他限制,需要频繁更换ip都可以使用。

    3.ping通IP就是有效的吗?如何验证代理是否有效

    好吧,这有点废话,进行端口测试才是最有效的,能ping通并不代表代理有效,不能平通也不一定代理不可用。可以使用HttpWebRequest,也可以使用Scoket,当然HttpWebRequest比Socket连接代理ip、port要慢。

    4.一次提取多少代理合适?

    代理ip时效性不强、并且有效性也不高,所以只能从一些代理ip的网站上批量定时去获取,有的代理在一分钟内使用是有限制的,所以说限制比较多。

    5.http代理和https代理有什么区别?

    需要访问https的网站就需要使用https代理了,比如百度,需要访问http的代理,可以使用http。这个并不是100%的。

    检测代理ip有效性步骤如下:

    1.使用HttpWebRequest、HttpWebResponse请求代理ip的网页,获取包含代理的网页内容

    2.使用HtmlAgilityPack或者正则表达式对抓取的内容进行截取,保存到代理集合

    3.拿到代理集合,多线程发起http请求,比如访问百度,是否成功,成功则存到Redis里面。

    效果图如下:

    • 使用HttpWebRequest发起请求

    Request.cs如下,主要就是两个方法,一个方法是验证代理ip是否有效,设置HttpWebRequest的Proxy属性,请求百度,看到有些文章大多数会获取响应的内容,如果内容符合请求的网址则证明代理哟有效,实际上根据HttpStatusCode 200就可以判断是否验证有效。

    【注意】建的是控制台程序,使用了异步,所以还是建.net core吧,c#语言的版本7.1。C#如何在控制台程序中使用异步

     1  public class Request
     2     {
     3         /// <summary>
     4         /// 验证代理ip有效性
     5         /// </summary>
     6         /// <param name="proxyIp">代理IP</param>
     7         /// <param name="proxyPort">代理IP 端口</param>
     8         /// <param name="timeout">详情超时</param>
     9         /// <param name="url">请求的地址</param>
    10         /// <param name="success">成功的回调</param>
    11         /// <param name="fail">失败的回调</param>
    12         /// <returns></returns>
    13         public static async System.Threading.Tasks.Task getAsync(string proxyIp,int  proxyPort, int  timeout,string url, Action success, Action<string> fail)
    14         {
    15             System.GC.Collect();
    16             HttpWebRequest request = null;
    17             HttpWebResponse response = null;
    18             try
    19             {
    20                 request = (HttpWebRequest)WebRequest.Create(url);
    21                 //HttpWebRequest request = HttpWebRequest.CreateHttp(url);
    22                 request.Timeout =timeout;
    23                 request.KeepAlive = false;
    24                 request.Proxy = new WebProxy(proxyIp,proxyPort);
    25                 response =  await  request.GetResponseAsync() as HttpWebResponse;
    26                 if (response.StatusCode == HttpStatusCode.OK)
    27                 {
    28                     success();
    29                 }
    30                 else
    31                 {
    32                     fail(response.StatusCode+":"+response.StatusDescription);
    33                 }
    34             }
    35             catch (Exception ex)
    36             {
    37                 fail("请求异常"+ex.Message.ToString());
    38             }
    39             finally
    40             {
    41                 if (request != null)
    42                 {
    43                     request.Abort();
    44                     request = null;
    45                 }
    46                 if (response != null)
    47                 {
    48                     response.Close();
    49                 }
    50             }
    51         }
    52 
    53         /// <summary>
    54         ///  发起http请求
    55         /// </summary>
    56         /// <param name="url"></param>
    57         /// <param name="success">成功的回调</param>
    58         /// <param name="fail">失败的回调</param>
    59         public static void get(string url,Action<string> success,Action<string> fail)
    60         {
    61             StreamReader reader = null;
    62             Stream stream = null;
    63             WebRequest request = null;
    64             HttpWebResponse response = null;
    65             try
    66             {
    67                 request = WebRequest.Create(url);
    68                 request.Timeout = 2000;
    69                 response = (HttpWebResponse)request.GetResponse();
    70                 if (response.StatusCode == HttpStatusCode.OK)
    71                 {
    72                     stream = response.GetResponseStream();
    73                     reader = new StreamReader(stream);
    74                     string result = reader.ReadToEnd();
    75                     success(result);
    76                 }
    77                 else
    78                 {
    79                     fail(response.StatusCode+":"+response.StatusDescription);
    80                 }
    81             }
    82             catch (Exception ex)
    83             {
    84                 fail(ex.ToString());
    85             }
    86             finally
    87             {
    88                 if (reader != null)
    89                     reader.Close();
    90                 if (stream != null)
    91                     stream.Close();
    92                 if(response!=null)
    93                     response.Close();
    94                 if(request!=null)
    95                     request.Abort();
    96             }
    97         }
    98     }
    • 抓取免费代理,并检查是否有效

    ProxyIpHelper.cs 中主要有四个方法,检查ip是否可用CheckProxyIpAsync、抓取xicidaili.com的代理GetXicidailiProxy、抓取ip3366.net的代理GetIp3366Proxy、抓取66ip.cn的代理GetIp3366Proxy。如果想多抓取几个网站可以多写几个。

     public class ProxyIpHelper
        {
            private static string address_xicidaili = "http://www.xicidaili.com/wn/{0}";
            private static string address_66ip = "http://www.66ip.cn/nmtq.php?getnum=20&isp=0&anonymoustype=0&start=&ports=&export=&ipaddress=&area=1&proxytype=1&api=66ip";
            private static string address_ip3366 = "http://www.ip3366.net/?stype=1&page={0}";
            /// <summary>
            /// 检查代理IP是否可用
            /// </summary>
            /// <param name="ipAddress">ip</param>
            /// <param name="success">成功的回调</param>
            /// <param name="fail">失败的回调</param>
            /// <returns></returns>
            public  static async Task CheckProxyIpAsync(string ipAddress, Action success, Action<string> fail)
            {
                int index = ipAddress.IndexOf(":");
                string proxyIp = ipAddress.Substring(0, index);
                int proxyPort = int.Parse(ipAddress.Substring(index + 1));
                await Request.getAsync(proxyIp, proxyPort, 3000, "https://www.baidu.com/", () =>
                {
                    success();
                }, (error) =>
                {
                    fail(error);
                });
            }
            /// <summary>
            /// 从xicidaili.com网页上去获取代理IP,可以分页
            /// </summary>
            /// <param name="page"></param>
            /// <returns></returns>
            public static List<string> GetXicidailiProxy(int page)
            {
                List<string> list = new List<string>();
                for (int p = 1; p <= page; p++)
                {
                    string url = string.Format(address_xicidaili, p);
                   Request.get(url,(docText)=> {
                       if (!string.IsNullOrWhiteSpace(docText))
                       {
                           HtmlDocument doc = new HtmlDocument();
                           doc.LoadHtml(docText);
                           var trNodes = doc.DocumentNode.SelectNodes("//table[@id='ip_list']")[0].SelectNodes("./tr");
                           if (trNodes != null && trNodes.Count > 0)
                           {
                               for (int i = 1; i < trNodes.Count; i++)
                               {
                                   var tds = trNodes[i].SelectNodes("./td");
                                   string ipAddress = tds[1].InnerText + ":" + int.Parse(tds[2].InnerText); ;
                                   list.Add(ipAddress);
                               }
                           }
                       }
                   },(error)=> {
                       Console.WriteLine(error);
                   });
                }
                return list;  
             }
            /// <summary>
            /// 从ip3366.net网页上去获取代理IP,可以分页
            /// </summary>
            /// <param name="page"></param>
            /// <returns></returns>
            public static List<string> GetIp3366Proxy(int page)
            {
                List<string> list = new List<string>();
                for (int p = 1; p <= page; p++)
                {
                    string url = string.Format(address_ip3366, p);
                    Request.get(url, (docText) => {
                        if (!string.IsNullOrWhiteSpace(docText))
                        {
                            HtmlDocument doc = new HtmlDocument();
                            doc.LoadHtml(docText);
                            var trNodes1 = doc.DocumentNode.SelectNodes("//table")[0];
                            var trNodes2 = doc.DocumentNode.SelectNodes("//table")[0].SelectSingleNode("//tbody");
                            var trNodes = doc.DocumentNode.SelectNodes("//table")[0].SelectSingleNode("//tbody").SelectNodes("./tr");
                            if (trNodes != null && trNodes.Count > 0)
                            {
                                for (int i = 1; i < trNodes.Count; i++)
                                {
                                    var tds = trNodes[i].SelectNodes("./td");
                                    if (tds[3].InnerHtml == "HTTPS")
                                    {
                                        string ipAddress = tds[0].InnerText + ":" + int.Parse(tds[1].InnerText); ;
                                        list.Add(ipAddress);
                                    }
                                }
                            }
                        }
                    }, (error) => {
                        Console.WriteLine(error);
                    });
                }
                return list;
             }
            /// <summary>
            /// 从66ip.cn中去获取,不需要分页
            /// </summary>
            /// <returns></returns>
            public static List<string> Get66ipProxy()
            {
                List<string> list = new List<string>();
                Request.get(address_66ip,
                (docText)=> {
                    int count = 0;
                    if (string.IsNullOrWhiteSpace(docText) == false)
                    {
                        string regex = "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}";
                        Match mstr = Regex.Match(docText, regex);
                        while (mstr.Success && count < 20)
                        {
                            string tempIp = mstr.Groups[0].Value;
                            list.Add(tempIp);
                            mstr = mstr.NextMatch();
                            count++;
                        }
                    }
                },
                (error)=> {
                    Console.WriteLine(error);
                });
                return list;
            }
        }
    • 使用Timer定时抓取,并检查,成功则保存到redis

    c#有三种定时器,这里定时器是使用System.Threading命名空间, 这个Timer会开启新的线程,抓取三个网页定义了三个Timer对象。每一次抓取都会保存上一次抓取的集合,检查前,会进行对比,取出新的集合也就是没有重复的那部分。有效性的ip比较低,这里没有做统计,如果代码再优化一下,可以做一下统计,看看程序的主入口吧,最终的实现如下:

      1   class Program
      2     {
      3         static bool timer_ip3366_isCompleted = true;
      4         static bool timer_xicidaili_isCompleted = true;
      5         static bool timer_66ip_isCompleted = true;
      6         static  Timer timer_ip3366, timer_xicidaili, timer_66ip;
      7         private static List<string> lastListip3366,lastList66ip,lastListxicidaili;//保存上一次抓取的代理,与下一次进行对比,取新的集合进行检查筛选
      8         static async Task Main(string[] args)
      9         {
     10             System.Net.ServicePointManager.DefaultConnectionLimit = 2000;
     11             Console.WriteLine("hellow proxyIp");
     12             Console.ReadLine();
     13             lastList66ip = new List<string>();
     14             lastListip3366 = new List<string>();
     15             lastListxicidaili = new List<string>();
     16             timer_ip3366 = new Timer(async (state) =>
     17             {
     18                 await TimerIp3366Async();
     19             }, "processing timer_ip3366 event", 0,1000*30);
     20             timer_xicidaili = new Timer(async (state) =>
     21             {
     22                 await TimerXicidailiAsync();
     23             }, "processing timer_xicidaili event", 0, 1000 * 60);
     24             timer_66ip = new Timer(async (state) =>
     25             {
     26                 await Timer66ipAsync();
     27             }, "processing timer_66ip event", 0, 1000*30);
     28             
     29             Console.ReadLine();
     30         }
     31 
     32 
     33 
     34         private static async Task Timer66ipAsync()
     35         {
     36             if (timer_66ip_isCompleted)
     37             {
     38                 timer_66ip_isCompleted = false;
     39                 List<string> checkList = new List<string>();
     40                 var listProxyIp = ProxyIpHelper.Get66ipProxy();
     41 
     42                 if (listProxyIp.Count > 0)
     43                 {
     44                     Console.ForegroundColor = ConsoleColor.DarkCyan;
     45                     Console.WriteLine("66ip.cn 抓取到" + listProxyIp.Count + "条记录,正在对比.........");
     46                     listProxyIp.ForEach(f =>
     47                     {
     48                         if (!lastList66ip.Contains(f))
     49                         {
     50                             checkList.Add(f);
     51                         }
     52                     });
     53                     lastList66ip = listProxyIp;
     54                     if (checkList.Count > 0)
     55                     {
     56                         Console.ForegroundColor = ConsoleColor.DarkCyan;
     57                         Console.WriteLine("66ip.cn 需要检查" + checkList.Count + "条记录,正在进行检测是否有效..........");
     58                         for (int i = 0; i < checkList.Count; i++)
     59                         {
     60                             string ipAddress = checkList[i];
     61                             await ProxyIpHelper.CheckProxyIpAsync(ipAddress, () =>
     62                             {
     63                                 bool insertSuccess = RedisHelper.InsertSet(ipAddress);
     64                                 Console.ForegroundColor = ConsoleColor.White;
     65                                 Console.WriteLine("66ip.cn");
     66                                 if (insertSuccess)
     67                                 {
     68                                     Console.WriteLine("success" + ipAddress + "任务编号:" + i + "当前任务线程:" + Thread.CurrentThread.ManagedThreadId);
     69                                 }
     70                                 Console.WriteLine("重复插入" + ipAddress + "任务编号:" + i + "当前任务线程:" + Thread.CurrentThread.ManagedThreadId);
     71                             }, (error) =>
     72                             {
     73                                 Console.ForegroundColor = ConsoleColor.Green;
     74                                 Console.WriteLine("66ip.cn");
     75                                 Console.WriteLine("error:" + ipAddress + error + "任务编号:" + i + "当前任务线程:" + Thread.CurrentThread.ManagedThreadId);
     76                             });
     77                         }
     78                         timer_66ip_isCompleted = true;
     79                         Console.ForegroundColor = ConsoleColor.DarkCyan;
     80                         Console.WriteLine("66ip.cn" + checkList.Count + "条记录,已经检测完成,正在进行下一次检查");
     81                     }
     82                     else
     83                     {
     84                         timer_66ip_isCompleted = true;
     85                         Console.ForegroundColor = ConsoleColor.DarkCyan;
     86                         Console.WriteLine("66ip.cn没有需要检查的代理ip");
     87                     }
     88                 }
     89                 else
     90                 {
     91                     timer_66ip_isCompleted = true;
     92                     Console.ForegroundColor = ConsoleColor.DarkCyan;
     93                     Console.WriteLine("66ip.cn没有获取到代理ip");
     94                 }
     95             }
     96         }
     97 
     98         private static async Task TimerXicidailiAsync()
     99         {
    100             if (timer_xicidaili_isCompleted)
    101             {
    102                 //取出需要检查的ip地址,第一次100条则checklist就是100条记录,
    103                 //第二次的100条中只有10是和上一次的不重复,则第二次只需要检查这10条记录
    104                 timer_xicidaili_isCompleted = false;
    105                 List<string> checkList = new List<string>();
    106                 var listProxyIp = ProxyIpHelper.GetXicidailiProxy(1);
    107                 if (listProxyIp.Count > 0)
    108                 {
    109                     Console.WriteLine("xicidaili.com 抓取到" + listProxyIp.Count + "条记录,正在对比............");
    110                     listProxyIp.ForEach(f =>
    111                     {
    112                         if (!lastListxicidaili.Contains(f))
    113                         {
    114                             checkList.Add(f);
    115                         }
    116                     });
    117                     lastListxicidaili = listProxyIp;
    118                     if (checkList.Count > 0)
    119                     {
    120                         Console.ForegroundColor = ConsoleColor.DarkCyan;
    121                         Console.WriteLine("xicidaili.com 需要检查" + checkList.Count + "条记录,正在进行检测是否有效..........");
    122                         for (int i = 0; i < checkList.Count; i++)
    123                         {
    124                             string ipAddress = checkList[i];
    125                             await ProxyIpHelper.CheckProxyIpAsync(ipAddress, () =>
    126                             {
    127                                 bool insertSuccess = RedisHelper.InsertSet(ipAddress);
    128                                 Console.ForegroundColor = ConsoleColor.White;
    129                                 Console.WriteLine("xicidaili.com");
    130                                 if (insertSuccess)
    131                                 {
    132                                     Console.WriteLine("success" + ipAddress + "任务编号:" + i + "当前任务线程:" + Thread.CurrentThread.ManagedThreadId);
    133                                 }
    134                                 else
    135                                     Console.WriteLine("重复插入" + ipAddress + "任务编号:" + i + "当前任务线程:" + Thread.CurrentThread.ManagedThreadId);
    136                             }, (error) =>
    137                             {
    138                                 Console.WriteLine("xicidaili.com");
    139                                 Console.ForegroundColor = ConsoleColor.Red;
    140                                 Console.WriteLine("error:" + ipAddress + error + "任务编号:" + i + "当前任务线程:" + Thread.CurrentThread.ManagedThreadId);
    141                             });
    142                         }
    143                         timer_xicidaili_isCompleted = true;
    144                         Console.ForegroundColor = ConsoleColor.DarkCyan;
    145                         Console.WriteLine("xicidaili.com" + checkList.Count + "条记录,已经检测完成,正在进行下一次检查");
    146                     }
    147                     else
    148                     {
    149                         timer_xicidaili_isCompleted = true;
    150                         Console.ForegroundColor = ConsoleColor.DarkCyan;
    151                         Console.WriteLine("xicidaili.com没有需要检查的代理ip");
    152                     }
    153                 }
    154                 else
    155                 {
    156                     timer_xicidaili_isCompleted = true;
    157                     Console.ForegroundColor = ConsoleColor.DarkCyan;
    158                     Console.WriteLine("xicidaili.com没有获取到代理ip");
    159                 }
    160             }
    161         }
    162         private static async Task TimerIp3366Async()
    163         {
    164             if (timer_ip3366_isCompleted)
    165             {
    166                 timer_ip3366_isCompleted = false;
    167                 List<string> checkList = new List<string>();
    168                 var listProxyIp = ProxyIpHelper.GetIp3366Proxy(4);
    169                 if (listProxyIp.Count > 0)
    170                 {
    171                     Console.ForegroundColor = ConsoleColor.DarkCyan;
    172                     Console.WriteLine("ip3366.net 抓取到" + listProxyIp.Count + "条记录,正在进行检测是否有效..........");
    173                     listProxyIp.ForEach(f =>
    174                     {
    175                         if (!lastListip3366.Contains(f))
    176                         {
    177                             checkList.Add(f);
    178                         }
    179                     });
    180                     lastListip3366 = listProxyIp;
    181                     if (checkList.Count != 0)
    182                     {
    183                         Console.ForegroundColor = ConsoleColor.DarkCyan;
    184                         Console.WriteLine("ip3366.net 需要检查" + checkList.Count + "条记录,正在进行检测是否有效..........");
    185                         for (int i = 0; i < checkList.Count; i++)
    186                         {
    187                             string ipAddress = checkList[i];
    188                             await ProxyIpHelper.CheckProxyIpAsync(ipAddress, () =>
    189                             {
    190                                 bool insertSuccess = RedisHelper.InsertSet(ipAddress);
    191                                 Console.ForegroundColor = ConsoleColor.White;
    192                                 Console.WriteLine("ip3366.net");
    193                                 if (insertSuccess)
    194                                 {
    195                                     Console.WriteLine("success" + ipAddress + "任务编号:" + i + "当前任务线程:" + Thread.CurrentThread.ManagedThreadId);
    196                                 }
    197                                 else
    198                                 {
    199                                     Console.ForegroundColor = ConsoleColor.Red;
    200                                     Console.WriteLine("重复插入" + ipAddress + "任务编号:" + i + "当前任务线程:" + Thread.CurrentThread.ManagedThreadId);
    201                                 }
    202                             }, (error) =>
    203                             {
    204                                 Console.ForegroundColor = ConsoleColor.Yellow;
    205                                 Console.WriteLine("ip3366.net");
    206                                 Console.WriteLine("error " + ipAddress + "任务编号:" + i + "当前任务线程:" + Thread.CurrentThread.ManagedThreadId);
    207                             });
    208                         }
    209                         timer_ip3366_isCompleted = true;
    210                         Console.WriteLine("ip3366.net" + checkList.Count + "条记录,已经检测完成,正在进行下一次检查");
    211                     }
    212                     else
    213                     {
    214                         timer_ip3366_isCompleted = true;
    215                         Console.ForegroundColor = ConsoleColor.DarkCyan;
    216                         Console.WriteLine("ip3366.net没有需要检查的代理ip");
    217                     }
    218                 }
    219                 else
    220                 {
    221                     timer_ip3366_isCompleted = true;
    222                     Console.ForegroundColor = ConsoleColor.DarkCyan;
    223                     Console.WriteLine("ip3366.net没有获取到代理ip");
    224                 }
    225 
    226             }
    227         }
    228     }
    View Code

    Redis第三库使用的stackoverflow的 StackExchange.Redis,代理ip不能重复储存,所以采用的数据结构是Set。存的值非常简单就一个ip加上port,也可以存入更多相关信息,感觉没必要。即使有这些其他的信息,也很难发挥作用。RedisHelper.cs如下

     1  public class RedisHelper
     2     {
     3         private static readonly object Locker = new object();
     4         private static ConnectionMultiplexer _redis;
     5         private const string CONNECTTIONSTRING = "127.0.0.1:6379,DefaultDatabase=3";
     6         public const string REDIS_SET_KET_SUCCESS = "set_success_ip";
     7         private static ConnectionMultiplexer Manager
     8         {
     9             get
    10             {
    11                 if (_redis == null)
    12                 {
    13                     lock (Locker)
    14                     {
    15                         if (_redis != null) return _redis;
    16                         _redis = GetManager();
    17                         return _redis;
    18                     }
    19                 }
    20                 return _redis;
    21             }
    22         }
    23         private static ConnectionMultiplexer GetManager(string connectionString = null)
    24         {
    25             if (string.IsNullOrEmpty(connectionString))
    26             {
    27                 connectionString = CONNECTTIONSTRING;
    28             }
    29             return ConnectionMultiplexer.Connect(connectionString);
    30         }
    31         public static  bool InsertSet(string value)
    32         {
    33             var db = Manager.GetDatabase();
    34             return db.SetAdd(REDIS_SET_KET_SUCCESS,value);
    35         }
    36     }
    View Code

    总结

    明天补上刷新网页浏览量的文章吧,代码还不够好,ip的有效性还不高,对多线程的使用还不是很熟练

    7月6号的补充:完成csdn刷文章的浏览量了:C#使用代理Ip刷新csdn文章浏览量

  • 相关阅读:
    Idea如果添加Maven模块
    idea在debugger模式下无法启动,但是在run模式下可以启动的问题
    Idea搭建SpringMVC框架(初次接触)
    Intellij IDEA 环境 tomcat 启动设置
    Winform .NET 利用NPOI导出大数据量的Excel
    unity3D AudioMixer+Slider实现音量调节,多音效控制(主音量,背景音,特效音等)
    unity3D 点击按钮暂停和继续游戏
    unity3D AsyncOperation异步加载场景&百分比效果
    unity3D 实现2D游戏背景层交错感(视觉差)
    unity3D 重新加载当前场景&加载当前场景的下一个场景
  • 原文地址:https://www.cnblogs.com/zhangmumu/p/9269762.html
Copyright © 2020-2023  润新知