游戏蛮牛学习群(纯技术交流,不闲聊):159852603
游戏蛮牛 手机端
开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

开发者专栏

关注:2335

当前位置:游戏蛮牛 技术专区 开发者专栏

__________________________________________________________________________________
开发者干货区版块规则:

  1、文章必须是图文形式。(至少2幅图)
      2、文章字数必须保持在1500字节以上。(编辑器右下角有字数检查)
      3、本版块只支持在游戏蛮牛原创首发,不支持转载。
      4、本版块回复不得无意义,如:顶、呵呵、不错......【真的会扣分的哦】
      5、......
__________________________________________________________________________________
查看: 4106|回复: 62

[RaymondChan] Unity中Lerp与SmoothDamp函数使用误区浅析

[复制链接]  [移动端链接]
3偶尔光临
273/300
排名
11945
昨日变化

4

主题

8

帖子

273

积分

Rank: 3Rank: 3Rank: 3

UID
254628
好友
2
蛮牛币
308
威望
0
注册时间
2017-11-16
在线时间
43 小时
最后登录
2018-10-19

专栏作家

发表于 2017-12-18 20:55:07 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
本帖最后由 RaymondChan 于 2017-12-18 20:59 编辑

写在前面
Unity的Lerp和SmoothDamp这两个函数我们在处理物体移动、一些渐进变化或者摄像机的跟随等场景会较常用到,虽然Mathf、Vector2、Vector3等都有这两个函数,但用法上是基本一样的,用起来也比较简单。不过在实际的运用中,往往一不小心却会掉进坑里,下面我们一起来分析一下。

Lerp函数
Lerp函数使用的最常见的误区是把线性移动用成了弹性移动(关键是自己还不知道,还奇怪,咦,不是说好的线性移动吗?怎么有点弹性的赶脚……),如下图所示:
01.gif


这样子的代码一般是这么写的:
[C#] 纯文本查看 复制代码
private Vector3 target = new Vector3(0, 0, 5);

void Update()
    {
            transform.position = Vector3.Lerp(transform.position, target, 0.1f);
    }



这个错误主要是没有理解好Lerp的第三个参数t的作用,这里我们为了便于理解,我们拿Mathf.Lerp函数来分析,思路是一样的。我们先来看一下Mathf.Lerp函数的具体实现:
[C#] 纯文本查看 复制代码
        /// <summary>
        ///   <para>Clamps value between 0 and 1 and returns value.</para>
        /// </summary>
        /// <param name="value"></param>
        public static float Clamp01(float value)
        {
            float result;
            if (value < 0f)
            {
                result = 0f;
            }
            else if (value > 1f)
            {
                result = 1f;
            }
            else
            {
                result = value;
            }
            return result;
        }

        /// <summary>
        ///   <para>Linearly interpolates between a and b by t.</para>
        /// </summary>
        /// <param name="a">The start value.</param>
        /// <param name="b">The end value.</param>
        /// <param name="t">The interpolation value between the two floats.</param>
        /// <returns>
        ///   <para>The interpolated float result between the two float values.</para>
        /// </returns>
        public static float Lerp(float a, float b, float t)
        {
            return a + (b - a) * Mathf.Clamp01(t);
        } 



估计大家一看函数的实现就明白了关键点,想要线性移动,应该只控制t的变化,t的值在0-1,它就类似一个百分比的值,代表着a点到b点之间的位置,比如想要到a和b之间的3/10的位置,t就应该是0.3,想要一步到位t的值就应该为1。在上述错误的用法中,实际的效果就是第一次到达1/10位置,第二次到达1/10+9/10*1/10的位置……理论上讲永远到不了b点,每一次都是a与b点之间的1/10的位置,所以移动的幅度越来越小,有一种弹性感觉。正确的使用方法如下:
[C#] 纯文本查看 复制代码
    private Vector3 target = new Vector3(0, 0, 5);
    private Vector3 startPos;
    private float t1;

    void Start()
    {
        startPos = transform.position;
    }

    void Update()
    {           
            t1 += 1f * Time.deltaTime;
            transform.position = Vector3.Lerp(startPos, target, t1);
    }


效果如下:

02.gif


SmoothDamp函数
SmoothDamp函数在使用过程中比较容易出现的一个问题就是容易在代码较复杂的情形下将currentVelocity这个参数需要的变量定义为局部变量,如下:
[C#] 纯文本查看 复制代码
   private Vector3 target = new Vector3(0, 0, 5);
    public float smoothTime = 0.3F;

    void Update()
    {
            Vector3 velocity = Vector3.zero;
            transform.position = Vector3.SmoothDamp(transform.position, target, ref velocity, smoothTime);
        }
    }


使用局部变量的效果如下图所示,越来越慢,定义的平滑时间明明是0.3f,怎么用了10几秒的时间都还在缓慢移动?
03.gif


为了便于理解,我们来看一下Mathf.SmoothDamp的具体实现:
[C#] 纯文本查看 复制代码
public static float SmoothDamp(float current, float target, ref float currentVelocity, float smoothTime, [DefaultValue("Mathf.Infinity")] float maxSpeed, [DefaultValue("Time.deltaTime")] float deltaTime)
        {
            smoothTime = Mathf.Max(0.0001f, smoothTime);
            float num = 2f / smoothTime;
            float num2 = num * deltaTime;
            float num3 = 1f / (1f + num2 + 0.48f * num2 * num2 + 0.235f * num2 * num2 * num2);
            float num4 = current - target;
            float num5 = target;
            float num6 = maxSpeed * smoothTime;
            num4 = Mathf.Clamp(num4, -num6, num6);
            target = current - num4;
            float num7 = (currentVelocity + num * num4) * deltaTime;
            currentVelocity = (currentVelocity - num * num7) * num3;
            float num8 = target + (num4 + num7) * num3;
            if (num5 - current > 0f == num8 > num5)
            {
                num8 = num5;
                currentVelocity = (num8 - num5) / deltaTime;
            }
            return num8;
        }


通过具体的实现我们就可以很清楚的发现,currentVelocity这个变量是先使用,然后再赋值,通过ref传递就是为了获取到上一次计算得到的速度值,如果我们使用局部变量,每一次速度都是零,相当于刚开始进行平滑移动,平滑的距离(transform.position到target)不断在缩短,然而平滑时间却没有变化,为了保证大约在平滑的时间内完成平滑移动,这个起步速度肯定是越来越慢的,所以就导致了上图中的问题。
我们把currentVelocity改为全局变量,就可以看到正常效果了

[C#] 纯文本查看 复制代码
private Vector3 target = new Vector3(0, 0, 5);
    public float smoothTime = 0.3F;
    private Vector3 velocity = Vector3.zero;
    void Update()
    {
            transform.position = Vector3.SmoothDamp(transform.position, target, ref velocity, smoothTime);
    }


效果如下:
04.gif


写在最后
好了,以上就是这一次想要和大家一起讨论的内容了,希望对大家有那么一丢丢的帮助。

本文工程文件(Unity2017.2.0f3):

链接:https://pan.baidu.com/s/1kV7rd4b  密码:vft2





点评

小妞牛和RaymondChan(楼主)的相关点评,已在楼下置顶大家可以查看.  发表于 2017-12-20 09:44

评分

参与人数 19鲜花 +58 收起 理由
Tlrbs + 1 赞一个!
yilianxin + 5 淡定
没动00 + 5 赞一个!
RyeCat + 2 赞一个!
美ぁ无与伦比 + 5 很给力!
YT4444 + 5 很给力!
步行骑士 + 5 很给力!
煮粥侠 + 5 很给力!
X-eJack + 5 赞一个!
lilexy + 5 不哭不哭
jordanseed + 1 淡定
xifg2007 + 2 很给力!
仅为年时 + 20 淡定
盖世花满楼 + 1 不错,讲得通俗易懂
陌上君无邪 + 2 很给力!
Mill + 2 赞一个!
小妞牛 -20 所谓误区十分可笑!请看官方文档,明确线性.
perfecthacker + 2 很给力!
Vincher + 5 赞一个!

查看全部评分


回复

使用道具 举报

排名
10248
昨日变化

13

主题

163

帖子

476

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
70381
好友
8
蛮牛币
553
威望
0
注册时间
2015-1-22
在线时间
188 小时
最后登录
2018-9-19
发表于 2017-12-19 13:36:31 | 显示全部楼层
本帖最后由 小妞牛 于 2017-12-20 13:16 编辑

这个帖子是水的吧,居然还顶置了
首先这个所谓的误区根本就是楼主自己过去的使用习惯有问题,请发帖前先看看官方文档好伐,不然很可笑啊,
   t1 += 1f * Time.deltaTime;这个1f有何意义
而且官方文档明确案例说明了,线性lerp要使用的t是time.time
本身人家设置这个api的时候用的就是时间增量,你用deltaTime这个衡量自加不是搞笑得嘛

我看这个帖子尴尬病都快犯了……
===

有些人回复我,觉得我说的有问题,我统一在这里补充一下—
(下面是我回复别人的,我直接粘过来了)

我没说他这点说的有问题

我是说上一步自加deltatime的这个步骤完全多余,我说的官方文档明确说明的就是指time。time这个累计值的问题,并不需要衡量的deltatime来自加,这个浅析误区的帖子,弄的好像unity设定的api有逻辑漏洞一样,所以我才那么说的。
====

刚才没仔细看,你理解错了,我不是说t就是time.time,我是说官方文档上使用的t参数这个位置上用的值是时间的增量time.time,因为只有这样,lerp才是线性的,原理和帖子里说的一样。他的原理没问题,但是本身unity api就是线性的,就是没有问题的,我指的是这个!将api的time改成deltatime形成了非线性移动,是程序员自身的问题,就算是出帖子也要标上官方给的正确优化方法才比较hao。

也是我多事。。。


-------------------------------------------


好吧,我已经觉得我自己多事了,其实都无所谓,我也是闲的……请楼主不要介意。
说出去的话泼出去的水,之前我还觉得终点值有问题,刚刚有测试了一下,发现是通的,但我就是觉得在update里写deltatime自加冗余,可能是我自己的习惯问题。回复我就不改了,就当有两个方法。


微信截图_20171220101405.png


刚刚看了楼主发的最新api,这个是我没考虑到中途使用lerp的情况。嗯,最初对这个帖子的评价是我的错。原理和解决方式都没有问题。如果lerp不是在运行一开始就用,需要在update里自加,如果是在一开始就用,还是推荐老版本里的方法。就是这样。





回复 支持 2 反对 2

使用道具 举报

3偶尔光临
273/300
排名
11945
昨日变化

4

主题

8

帖子

273

积分

Rank: 3Rank: 3Rank: 3

UID
254628
好友
2
蛮牛币
308
威望
0
注册时间
2017-11-16
在线时间
43 小时
最后登录
2018-10-19

专栏作家

 楼主| 发表于 2017-12-19 21:15:47 | 显示全部楼层
小妞牛 发表于 2017-12-19 13:36
这个帖子是水的吧,居然还顶置了
首先这个所谓的误区根本就是楼主自己过去的使用习惯有问题,请发帖前先看 ...

首先,这个误区的确我自己也犯过,但正是因为我自己犯过这个错,然后发现身边不少开发者也犯了这个错,所以才写出来给大家,希望大家能避免错误。

第二,请看Mathf.Lerp官方文档示例,如下。怎么没有使用Time.deltaTime呢,示例中使用了0.5f,是要在1/0.5 也就是2秒内走完,而我使用1f就是要在1s内走完这个线性流程。而Vector3.Lerp的示例中虽然没有使用Time.deltaTime,但是使用Time.time-startTime同样是为了得到时间的累计值。 请问我哪里错了呢?你这边直接给我减了20分,感觉有点气啊。

[C#] 纯文本查看 复制代码
    void Update()
    {
        // animate the position of the game object...
        transform.position = new Vector3(Mathf.Lerp(minimum, maximum, t), 0, 0);

        // .. and increate the t interpolater
        t += 0.5f * Time.deltaTime;

        // now check if the interpolator has reached 1.0
        // and swap maximum and minimum so game object moves
        // in the opposite direction.
        if (t > 1.0f)
        {
            float temp = maximum;
            maximum = minimum;
            minimum = temp;
            t = 0.0f;
        }
    }
[发帖际遇]: RaymondChan 乐于助人,奖励 3 蛮牛币. 幸运榜 / 衰神榜

回复 支持 反对

使用道具 举报

排名
451
昨日变化

258

主题

1338

帖子

7036

积分

Rank: 16

UID
73452
好友
98
蛮牛币
5649
威望
0
注册时间
2015-2-6
在线时间
2118 小时
最后登录
2018-10-22

专栏作家蛮牛译员社区QQ达人活力之星七夕浪漫情人原创精华达人蛮牛哥认证开发者

QQ
发表于 2017-12-20 10:17:22 | 显示全部楼层
RaymondChan 发表于 2017-12-19 21:15
首先,这个误区的确我自己也犯过,但正是因为我自己犯过这个错,然后发现身边不少开发者也犯了这个错,所 ...

这篇文章从细节解释了lerp的作用,通俗易懂,对于不是特别理解lerp的童鞋们其实很有帮助,对于deltatime的使用其实很多人也都是这样用的,包括我,下面回复说的我觉得也没什么错,程序员嘛,应该追求用最合适的方式,最简洁的代码来完成工作,论坛就是用来谈论的,无可厚非,我给楼主加20分,都别生气,OK啦
[发帖际遇]: 仅为年时 在论坛发帖时没有注意,被小偷偷去了 1 蛮牛币. 幸运榜 / 衰神榜

回复 支持 1 反对 0

使用道具 举报

3偶尔光临
286/300
排名
8044
昨日变化

0

主题

82

帖子

286

积分

Rank: 3Rank: 3Rank: 3

UID
26826
好友
0
蛮牛币
155
威望
0
注册时间
2014-5-26
在线时间
60 小时
最后登录
2018-9-21
发表于 2017-12-20 11:26:29 | 显示全部楼层
小妞牛 发表于 2017-12-19 13:36
这个帖子是水的吧,居然还顶置了
首先这个所谓的误区根本就是楼主自己过去的使用习惯有问题,请发帖前先看 ...

对于你的评论,别的我不敢质疑,我也是新手,不过你说的“而且官方文档明确案例说明了,线性lerp要使用的t是time.time”这句话肯定有问题,我之前就是认为t是Time.time,所以一直没弄懂这个函数的用法。楼主说的没错啊,这个t就是0到1之间的一个值,官方文档不就是这么说的吗“Interpolates between the vectors a and b by the interpolant t. The parameter t is clamped to the range [0, 1]. This is most commonly used to find a point some fraction of the way along a line between two endpoints (e.g. to move an object gradually between those points).”
官方案例只是用了Time.time作为参数而已。

回复 支持 反对

使用道具 举报

排名
10248
昨日变化

13

主题

163

帖子

476

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
70381
好友
8
蛮牛币
553
威望
0
注册时间
2015-1-22
在线时间
188 小时
最后登录
2018-9-19
发表于 2017-12-20 11:42:49 | 显示全部楼层
本帖最后由 小妞牛 于 2017-12-20 12:49 编辑
xifg2007 发表于 2017-12-20 11:26
对于你的评论,别的我不敢质疑,我也是新手,不过你说的“而且官方文档明确案例说明了,线性lerp要使用的 ...

我没说他这点说的有问题

我是说上一步自加deltatime的这个步骤完全多余,我说的官方文档明确说明的就是指time。time这个累计值的问题,并不需要衡量的deltatime来自加,这个浅析误区的帖子,弄的好像unity设定的api有逻辑漏洞一样,所以我才那么说的。
====

刚才没仔细看,你理解错了,我不是说t就是time.time,我是说官方文档上使用的t参数这个位置上用的值是时间的增量time.time,因为只有这样,lerp才是线性的,原理和帖子里说的一样。他的原理没问题,但是本身unity api就是线性的,就是没有问题的,我指的是这个!将api的time改成deltatime形成了非线性移动,是程序员自身的问题,就算是出帖子也要标上官方给的正确优化方法才比较。
也是我多事。。。


回复 支持 反对

使用道具 举报

3偶尔光临
273/300
排名
11945
昨日变化

4

主题

8

帖子

273

积分

Rank: 3Rank: 3Rank: 3

UID
254628
好友
2
蛮牛币
308
威望
0
注册时间
2017-11-16
在线时间
43 小时
最后登录
2018-10-19

专栏作家

 楼主| 发表于 2017-12-20 12:46:41 | 显示全部楼层
本帖最后由 RaymondChan 于 2017-12-20 12:56 编辑
小妞牛 发表于 2017-12-20 10:43
亲,我们说的是一个官方文档么

Time.time - startTime和Time.deltaTime累加的这两种方法都是为了应对不是从程序一运行开始就执行线性移动的这种情况。不过我这里确实不够严谨,因为我并没有用在这种情形下,所以如果是一运行就直接的移动的确实只需使用Time.time就可以了。不过Unity的官方文档确实都是这么用的,因为一运行就执行的情况下相对比较少。以下是官方文档的截图和链接
https://docs.unity3d.com/ScriptReference/Mathf.Lerp.html
https://docs.unity3d.com/ScriptReference/Vector3.Lerp.html






回复 支持 反对

使用道具 举报

3偶尔光临
273/300
排名
11945
昨日变化

4

主题

8

帖子

273

积分

Rank: 3Rank: 3Rank: 3

UID
254628
好友
2
蛮牛币
308
威望
0
注册时间
2017-11-16
在线时间
43 小时
最后登录
2018-10-19

专栏作家

 楼主| 发表于 2017-12-20 12:59:39 | 显示全部楼层
小妞牛 发表于 2017-12-20 12:33
用deltatime自加是多余的,为什么不直接用time.time

如果从程序运行中途突然要使用Lerp,就不能直接使用Time.time啦

回复 支持 反对

使用道具 举报

排名
10248
昨日变化

13

主题

163

帖子

476

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
70381
好友
8
蛮牛币
553
威望
0
注册时间
2015-1-22
在线时间
188 小时
最后登录
2018-9-19
发表于 2017-12-20 13:10:33 | 显示全部楼层
RaymondChan 发表于 2017-12-20 12:59
如果从程序运行中途突然要使用Lerp,就不能直接使用Time.time啦

你说得对,这个我确实没考虑到

回复 支持 反对

使用道具 举报

排名
21255
昨日变化

0

主题

21

帖子

52

积分

Rank: 2Rank: 2

UID
257856
好友
0
蛮牛币
7
威望
0
注册时间
2017-12-4
在线时间
9 小时
最后登录
2018-8-8
发表于 2017-12-18 22:51:58 | 显示全部楼层
haodongxia dajiaweiguan
[发帖际遇]: unity真de难学 乐于助人,奖励 3 蛮牛币. 幸运榜 / 衰神榜

回复 支持 反对

使用道具 举报

7日久生情
1634/5000
排名
3066
昨日变化

3

主题

867

帖子

1634

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
246489
好友
1
蛮牛币
2713
威望
0
注册时间
2017-9-28
在线时间
312 小时
最后登录
2018-10-22

活力之星

发表于 2017-12-19 08:33:52 | 显示全部楼层
谢谢分享

回复

使用道具 举报

7日久生情
1749/5000
排名
2489
昨日变化

13

主题

332

帖子

1749

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
217870
好友
1
蛮牛币
631
威望
0
注册时间
2017-4-15
在线时间
862 小时
最后登录
2018-10-23
发表于 2017-12-19 08:35:08 | 显示全部楼层
不错不错

回复

使用道具 举报

4四处流浪
447/500
排名
12922
昨日变化

3

主题

149

帖子

447

积分

Rank: 4

UID
216830
好友
2
蛮牛币
331
威望
0
注册时间
2017-4-9
在线时间
233 小时
最后登录
2018-10-22
发表于 2017-12-19 08:59:42 | 显示全部楼层
谢谢分享

回复

使用道具 举报

6蛮牛粉丝
1062/1500
排名
5832
昨日变化

1

主题

283

帖子

1062

积分

Rank: 6Rank: 6Rank: 6

UID
159858
好友
1
蛮牛币
596
威望
0
注册时间
2016-8-1
在线时间
556 小时
最后登录
2018-8-31
发表于 2017-12-19 09:06:25 | 显示全部楼层
通俗易懂

回复

使用道具 举报

6蛮牛粉丝
1041/1500
排名
4625
昨日变化

2

主题

336

帖子

1041

积分

Rank: 6Rank: 6Rank: 6

UID
238701
好友
1
蛮牛币
897
威望
0
注册时间
2017-8-21
在线时间
407 小时
最后登录
2018-10-19
发表于 2017-12-19 09:07:40 | 显示全部楼层
很好

回复

使用道具 举报

7日久生情
1798/5000
排名
2271
昨日变化

5

主题

719

帖子

1798

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
239879
好友
1
蛮牛币
3879
威望
0
注册时间
2017-8-26
在线时间
490 小时
最后登录
2018-10-22
发表于 2017-12-19 09:10:45 | 显示全部楼层
看看学习一下

回复

使用道具 举报

7日久生情
2333/5000
排名
2815
昨日变化

11

主题

1119

帖子

2333

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
168159
好友
4
蛮牛币
5352
威望
0
注册时间
2016-9-12
在线时间
707 小时
最后登录
2018-10-22
发表于 2017-12-19 09:18:27 | 显示全部楼层
[发帖际遇]: ceshi12580 发帖时在路边捡到 2 蛮牛币,偷偷放进了口袋. 幸运榜 / 衰神榜

回复

使用道具 举报

5熟悉之中
857/1000
排名
4694
昨日变化

2

主题

373

帖子

857

积分

Rank: 5Rank: 5

UID
213486
好友
1
蛮牛币
1916
威望
0
注册时间
2017-3-21
在线时间
192 小时
最后登录
2018-10-10
发表于 2017-12-19 09:20:00 | 显示全部楼层
学习了,谢谢分享
[发帖际遇]: 燕子飞飞飞 乐于助人,奖励 3 蛮牛币. 幸运榜 / 衰神榜

回复

使用道具 举报

5熟悉之中
592/1000
排名
4694
昨日变化

1

主题

55

帖子

592

积分

Rank: 5Rank: 5

UID
201386
好友
0
蛮牛币
368
威望
0
注册时间
2017-1-12
在线时间
246 小时
最后登录
2018-10-21
发表于 2017-12-19 09:33:02 | 显示全部楼层
很不错

回复

使用道具 举报

7日久生情
1990/5000
排名
1955
昨日变化

8

主题

784

帖子

1990

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
83438
好友
2
蛮牛币
2095
威望
0
注册时间
2015-3-23
在线时间
537 小时
最后登录
2018-10-22
发表于 2017-12-19 09:38:27 | 显示全部楼层
好东西 真细致

回复

使用道具 举报

6蛮牛粉丝
1051/1500
排名
2843
昨日变化

0

主题

286

帖子

1051

积分

Rank: 6Rank: 6Rank: 6

UID
228538
好友
9
蛮牛币
2208
威望
0
注册时间
2017-6-24
在线时间
283 小时
最后登录
2018-10-22
发表于 2017-12-19 09:44:09 | 显示全部楼层
感谢分享

回复

使用道具 举报

6蛮牛粉丝
1016/1500
排名
3412
昨日变化

1

主题

371

帖子

1016

积分

Rank: 6Rank: 6Rank: 6

UID
122160
好友
1
蛮牛币
1669
威望
0
注册时间
2015-9-10
在线时间
236 小时
最后登录
2018-8-1
发表于 2017-12-19 09:44:50 | 显示全部楼层
感谢分享,很赞
[发帖际遇]: Vincher 发帖时在路边捡到 1 蛮牛币,偷偷放进了口袋. 幸运榜 / 衰神榜

回复

使用道具 举报

7日久生情
3637/5000
排名
138
昨日变化
1

1

主题

174

帖子

3637

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
19245
好友
2
蛮牛币
7407
威望
0
注册时间
2014-3-27
在线时间
1212 小时
最后登录
2018-10-22
发表于 2017-12-19 09:47:58 | 显示全部楼层
谢谢分享

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

快速回复 返回顶部 返回列表