IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 测试Unity中常用代码的运行所用时间:三维向量和变换位移篇 #性能测试 #Vector3 #tranform.position -> 正文阅读

[游戏开发]测试Unity中常用代码的运行所用时间:三维向量和变换位移篇 #性能测试 #Vector3 #tranform.position

〇、前言

系统自带计时器 -System.Diagnostics.StopWatch- 的使用

System.Diagnostics.StopWatch 的基本使用方法

//引用命名空间
using System.Diagnostics;

//创建一个计时器
Stopwatch timer = new Stopwatch();

//计时开始
timer.Start();

//计时结束
timer.Stop();

//转换成秒形式
decimal second = timer.Elapsed.Ticks * 0.000_000_1m;

//如果要转换成微秒形式:
decimal microSecond =  timer.Elapsed.Ticks * 0.1m;

//在输出面板输出代码所用时间, ":F4"是保留4位小数的意思
Debug.Log($"Takes {second:F4} second");

一、三维向量测试

1. 三维向量的函数方法和属性

首先说说 Vector3 的所有函数方法和属性,如下:

  • 重要属性:
----属性名称----
--------------------------属性作用------------------------
Vector3.right返回 new Vector3(1,0,0); ( static )
Vector3.left返回 new Vector3(-1,0,0); ( static )
Vector3.up返回 new Vector3(0,1,0); ( static )
Vector3.down返回 new Vector3(0,-1,0); ( static )
Vector3.forward返回 new Vector3(0,0,1); ( static )
Vector3.back返回 new Vector3(0,0,-1); ( static )
Vector3.one返回 new Vector3(1,1,1); ( static )
Vector3.zero返回 new Vector3(0,0,0); ( static )
.normalized返回该向量的单位向量:
? e ? = n ? ∣ n ? ∣ \ \vec{e} = \frac {\vec{n}} {\vert \vec{n}\vert} ?e =n n ?
.magnitude返回该向量的模:
m a g → = ∣ n ? ∣ \overrightarrow{mag}= \vert \vec{n}\vert mag ?=n
.sqrMagnitude返回该向量的平方模:
? s q r M a g = n ? ? n ? = ∣ n ? ∣ 2 \ sqrMag = \def\nor{\vec{n}} \nor \cdot \nor = \vert \nor\vert^2 ?sqrMag=n ?n =n 2;
  • 常用静态方法:
----属性名称----
返回值类型
-------------------------返回值-----------------------
Angle(Vector3 a, Vector3 b)float返回两个向量的夹角(度数):
? < a ? , b ? > = arccos ? ( a ? ? b ? ∣ a ? ∣ ? ∣ b ? ∣ ) \ \def\fromVec{\mathsf {\vec a}} \def\toVec{\mathsf {\vec b}} <\vec a,\vec b>= \arccos\left( \frac {\fromVec \cdot \toVec} {\vert\fromVec\vert \cdot \vert \toVec\vert} \right) ?<a ,b >=arccos(a ?b a ?b ?)
ClampMagnitude(Vector3 a, float maxLength)Vector3返回方向与 a 相同,长度最大为 maxLength 的向量:
v e c → = min ? { a ? . m a g n i t u d e , m a x L e n g t h } ? a ? . n o r m a l i z e \overrightarrow{vec}= \min\{\vec \mathsf a.magnitude,\mathsf {maxLength}\}\cdot \vec \mathsf a.normalize vec =min{a .magnitude,maxLength}?a .normalize
Cross(Vector3 a, Vector3 b)Vector3返回两个向量的叉积:
? v e c → = a ? × b ? = ∣ i ? j ? k ? a ? . x a ? . y a ? . z b ? . x b ? . y b ? . z ∣ \ \def\A{\vec\mathsf a} \def\B{\vec\mathsf b} \overrightarrow{vec} = \A \times \B = \begin{vmatrix} \vec i & \vec j & \vec k \\ \A .x & \A.y & \A.z \\ \B.x & \B.y & \B.z\end{vmatrix} ?vec =a ×b =?i a .xb .x?j ?a .yb .y?k a .zb .z??
Dot(Vector3 a, Vector3 b)float返回两个向量的点积:
? r e s u l t = a ? ? b ? = a ? . x ? b ? . x + a ? . y ? b ? . y + a ? . z ? b ? . z \ \def\A{\vec\mathsf a} \def\B{\vec\mathsf b} result= \A \cdot \B = \A.x*\B.x+\A.y*\B.y+\A.z*\B.z ?result=a ?b =a .x?b .x+a .y?b .y+a .z?b .z
Scale(Vector3 a, Vector3 b)Vector3返回两个向量各部分对应相乘的结果:
v e c → = n e w ? V e c t o r 3 ( a ? . x ? b ? . x , a ? . y ? b ? . y , a ? . z ? b ? . z ) ; \def\A{\vec\mathsf a}\def\B{\vec\mathsf b} \overrightarrow\mathsf {vec} = \mathsf {new \ Vector3}( \A.x*\B.x, \A.y*\B.y, \A.z*\B.z); vec =new?Vector3(a .x?b .x,a .y?b .y,a .z?b .z);
Distance(Vector3 a, Vector3 b)float返回ab的距离:
? d i s = ∣ b ? ? a ? ∣ \ dis = \vert \vec\mathsf b - \vec\mathsf a\vert ?dis=b ?a
Lerp(Vector3 a, Vector3 b, float t)Vector3返回ab的插值( 其中t 被限制在0~1之间 ):
v e c → = a ? + ( b ? ? a ? ) ? t \overrightarrow{vec}= \vec\mathsf a + (\vec\mathsf b - \vec\mathsf a)*\mathsf t vec =a +(b ?a )?t
LerpUnclamp(Vector3 a, Vector3 b, float t)Vector3返回ab的插值( t 不会被限制在0~1之间 ):
v e c → = a ? + ( b ? ? a ? ) ? t \overrightarrow{vec}= \vec\mathsf a + (\vec\mathsf b - \vec\mathsf a)*\mathsf t vec =a +(b ?a )?t
Magnitude(Vector3 vector)float返回 vector 的模:
? m a g = ∣ v e c t o r → ∣ \ mag = \vert\overrightarrow\mathsf {vector} \vert ?mag=vector
Normalize(Vector3 vector)Vector3返回 vector 的单位向量:
? e ? = v e c t o r → ∣ v e c t o r → ∣ \ \vec e = \frac {\overrightarrow\mathsf {vector}} {\vert\overrightarrow\mathsf {vector} \vert} ?e =vector vector ?
SqrMagnitude(Vector3 vector)float返回 vector 的平方模:
s q r M a g = v e c t o r → ? v e c t o r → \def\Vec{\overrightarrow\mathsf {vector}} sqrMag = \Vec \cdot \Vec sqrMag=vector ?vector ;
Max(Vector3 a, Vector3 b)Vector3返回由两个向量最大值部分组成的向量:
? v e c → = n e w ? V e c t o r 3 ( max ? { a ? . x , b ? . x } , max ? { a ? . y , b ? . y } , max ? { a ? . z , b ? . z } ) ; \ \def\A{\vec\mathsf a} \def\B{\vec\mathsf b} \overrightarrow{vec}= \mathsf {new \ Vector3}(\max\{\A.x,\B.x\},\max\{\A.y,\B.y\},\max\{\A.z,\B.z\}); ?vec =new?Vector3(max{a .x,b .x},max{a .y,b .y},max{a .z,b .z});
Min(Vector3 a, Vector3 b)Vector3返回由两个向量最小值部分组成的向量:
? v e c → = n e w ? V e c t o r 3 ( min ? { a ? . x , b ? . x } , min ? { a ? . y , b ? . y } , min ? { a ? . z , b ? . z } ) ; \ \def\A{\vec\mathsf a} \def\B{\vec\mathsf b} \overrightarrow {vec}= \mathsf {new \ Vector3}(\min\{\A.x,\B.x\},\min\{\A.y,\B.y\},\min\{\A.z,\B.z\}); ?vec =new?Vector3(min{a .x,b .x},min{a .y,b .y},min{a .z,b .z});
MoveTowards(Vector3 current, Vector3 target, float maxDistanceDelta)Vector3返回 current 沿 “ current 指向 target ” 方向移动 maxDistanceDelta 距离后的向量(令 direction = (target - current).Normalized();):
v e c → = c u r r e n t → + d i r e c t i o n → ? m a x D i s t a n c e D e l t a \overrightarrow{vec}= \overrightarrow\mathsf {current}+ \overrightarrow\mathsf {direction}*\mathsf {maxDistanceDelta} vec =current +direction ?maxDistanceDelta
Project(Vector3 vector, Vector3 onNormal)Vector3返回 vectoronNormal 上的投影(令 normal = onNormal.Normalized();):
v e c → = ( v e c t o r → ? n o r m a l → ) ? n o r m a l → \overrightarrow{vec} = \def\Nor{\overrightarrow\mathsf {normal}} \left(\overrightarrow\mathsf {vector}\cdot\Nor \right)\cdot \Nor vec =(vector ?normal )?normal
ProjectOnPlane(Vector3 vector, Vector3 planeNormal)Vector3返回 vector 在面上的投影(令 normal = planeNormal.Normalized();):
v e c → = v e c t o r → ? ( v e c t o r → ? n o r m a l → ) ? n o r m a l → \overrightarrow{vec} = \def\Vec{\overrightarrow\mathsf {vector}} \def\Nor{\overrightarrow\mathsf {normal}} \Vec - \left(\Vec \cdot\Nor\right)\cdot \Nor vec =vector ?(vector ?normal )?normal
Reflect(Vector3 inDirection, Vector3 inNormal)Vector3返回 inDirectioninNormal 上的反射(令 normal = inNormal.Normalized(); dir = inDirection;):
v e c → = d i r → ? 2 ? ( d i r → ? n o r m a l → ) ? n o r m a l → \overrightarrow\mathsf {vec} = \def\Dir{\overrightarrow\mathsf{dir}} \def\Nor{\overrightarrow\mathsf{normal}} \Dir - 2 *\left(\Dir \cdot \Nor\right) \cdot \Nor vec =dir ?2?(dir ?normal )?normal
SignedAngle(Vector3 from, Vector3 to, Vector3 axis)float返回两个向量在法向量为 axis 的面上的投影之间的夹角:
∠ a n g l e = A n g l e ( P r o j e c t O n P l a n e ( f r o m ? , a x i s ? ) , P r o j e c t O n P l a n e ( t o ? , a x i s ? ) ) ; \angle angle = \mathsf{Angle(ProjectOnPlane(\vec {\bold {from}},\vec {\bold {axis}}),ProjectOnPlane(\vec {\bold {to}},\vec {\bold {axis}}))}; angle=Angle(ProjectOnPlane(from ,axis ),ProjectOnPlane(to ,axis ));
  • 常用方法:
----属性名称----
返回值类型
-------------------------返回值-----------------------
.Scale(Vector3 scale)void该向量为与 scale 各部分对应相乘的结果:
v e c → = n e w ? V e c t o r 3 ( x ? s a c l e → . x , y ? s a c l e → . y , z ? s a c l e → . z ) ; \def\B{\overrightarrow\mathsf {sacle}} \overrightarrow\mathsf {vec} = \mathsf{new \ Vector3}( x*\B.x, y*\B.y, z*\B.z); vec =new?Vector3(x?sacle .x,y?sacle .y,z?sacle .z);
.Set(float newX, float newY, float newZ)void不开新的内存空间的改变该向量值的方法:
v e c → = n e w ? V e c t o r 3 ( n e w X , n e w Y , n e w Z ) ; \overrightarrow\mathsf {vec} = \mathsf{new \ Vector3}(\mathsf{newX}, \mathsf{newY}, \mathsf{newZ}); vec =new?Vector3(newX,newY,newZ);
.Normalize()void单位化该向量:
e ? = n ? n ? . m a g n i t u d e \vec e = \frac{\vec n}{\vec n.\mathsf magnitude} e =n .magnituden ?

2. 三维向量测试

介绍完三维向量的基本常用函数方法,接下来将进行一系列测试来说明各方法的最佳使用环境。
接下来所有测试都以以下代码为标准代码块(跟详细的见测试代码运行所用时间(一)),并在测试不同内容时进行不同修改:

...
// ==!==(修改区 0) 修改 count 大小
private int count = 100_000;
...

private void OnClicked()
{
    // ==!==(修改区 1) 最初初始化代码块
    ...

    for (int testIndex = -1; testIndex < (n/*测试实验个数(预热和对比区除外)*/ + 1) * 3; ++testIndex)
    {
    	// ==!==(修改区 2) 每次初始化代码块
    	...
    
    	
		int index = Mathf.FloorToInt(testIndex * 0.334f);
		
        Stopwatch timer = new Stopwatch();
        timer.Start();
        switch (index)//每个测试 3 次
        {
            //传统"预热"
            case -1:
                break;

            // case 0 一般都为普通循环(对比区)
            case 0:
                for (int i = 0; i < count; ++i) { }
                break;

            // ==!==(修改区 3) 其他测试代码块
			case 1:
				...
				break;
				
			...
        }

        timer.Stop();
        UnityEngine.Debug.Log($"{index}: Takes -{timer.Elapsed.Ticks}- ticks");

        // ==!==(修改区 4) 结束后进行其余操作的代码块
        ...
    }
}

(1). 初始化、创建和赋值

//(修改区 2)
Vector3 dir = Vector3.zero;

其中 测试实验个数 n = 4 
//(修改区 3)
case 1:
    for (int i = 0; i < count; ++i)
    {
    	Vector3 tmpDir = Vector3.up;
    }
    break;
case 2:
    for (int i = 0; i < count; ++i)
    {
        dir = Vector3.up;
    }
    break;
case 3:
    for (int i = 0; i < count; ++i)
    {
        dir = new Vector3(0, 1, 0);
    }
case 4:
    for (int i = 0; i < count; ++i)
    {
        dir.Set(0, 1, 0);
    }
    break;

在Unity中多次测试后取稳定值,记录实验数据

次数\实验编号01234
1608912182455115477
2609912182354885488
3612913182454685511

(2). Distance、magnitude 和 sqrMagnitude

//(修改区 1)
Vector3 targetPos = Random.onUnitSphere * Random.Range(0f, 42f);
Vector3 startPos = Vector3.zero;

//(修改区 2)
float len = 0;

其中 测试实验个数 n = 5 
//(修改区 3)
case 1:
    for (int i = 0; i < count; ++i)
    {
    	len = Vector3.Distance(startPos,targetPos);
    }
    break;
case 2:
    for (int i = 0; i < count; ++i)
    {
        len = targetPos.magnitude;
    }
    break;
case 3:
    for (int i = 0; i < count; ++i)
    {
        len = Vector3.Magnitude(targetPos);
    }
case 4:
    for (int i = 0; i < count; ++i)
    {
        len = targetPos.sqrMagnitude;
    }
    break;
case 5:
    for (int i = 0; i < count; ++i)
    {
        len = Vector3.SqrMagnitude(targetPos);
    }
    break;

在Unity中多次测试后取稳定值,记录实验数据

次数\实验编号012345
162966122123161176275188677
25556579723677204181360911996
350365150220431576970847656
4752638841201513409117128624
563062636123971627471567714

由上述实验数据可得:

在进行向量之间距离的比较时,最好用 sqrMagnitude
如果需要用到向量的模就还是用 magnitude

关于 Distance 和 magnitude 的区别,本博主认为是两个向量差引起的,所以再次进行实验:

//(修改区 1)
Vector3 targetPos = Random.onUnitSphere * Random.Range(0f, 42f);
Vector3 startPos = Vector3.zero;
Vector3 dir = targetPos - startPos;

//(修改区 2)
float len = 0;

其中 测试实验个数 n = 4 
//(修改区 3)
case 1:
    for (int i = 0; i < count; ++i)
    {
    	len = Vector3.Distance(startPos,targetPos);
    }
    break;
case 2:
    for (int i = 0; i < count; ++i)
    {
        len = (targetPos - startPos).magnitude;
    }
    break;
case 3:
    for (int i = 0; i < count; ++i)
    {
        len = Vector3.Magnitude(targetPos - startPos);
    }
case 4:
    for (int i = 0; i < count; ++i)
    {
        len = dir.magnitude;
    }
    break;

再在Unity中进行多次测试后取稳定值,记录实验数据

次数\实验编号01234
183738458404704624710289
210442966140310443439605
38042868639578446139601

由此可发现:

distancemagnitude 中:
如果要计算两点之间的距离,用 distance 是最好的选择;
如果要计算一个向量的模,用 magnitude 是最好的选择;

(3). 由 normalize 引出的各个数字运算耗费时间

一般情况下,我们都会认为 单位化向量 的性能消耗比较大,以为其中有 1 n ? ? n ? \frac {1} {\sqrt{\vec n \cdot \vec n}} n ?n ?1?,既有除的操作,又有根号的操作。
于是下面先来测试一下各个运算符的性能消耗:

//(修改区 1)
float creator = 1.0001f;
float sqrtNum = 114514.114514f;
double outsideNum = 0;

//(修改区 2)
double num = 1314.520f;

其中 测试实验个数 n = 8 
//(修改区 3)
case 1:
    for (int i = 0; i < count; ++i)
    {
    	num += creator;
    }
    break;
case 2:
    for (int i = 0; i < count; ++i)
    {
        num -= creator;
    }
    break;
case 3:
    for (int i = 0; i < count; ++i)
    {
        num *= creator;
    }
case 4:
    for (int i = 0; i < count; ++i)
    {
        num /= creator;
    }
    break;
case 5:
    for (int i = 0; i < count; ++i)
    {
    	num %= creator;
    }
    break;
case 6:
    for (int i = 0; i < count; ++i)
    {
        num = sqrtNum;
    }
    break;
case 7:
    for (int i = 0; i < count; ++i)
    {
        num = Mathf.Sqrt(sqrtNum);
    }
case 8:
    for (int i = 0; i < count; ++i)
    {
        num = System.Math.Sqrt(sqrtNum);
    }
    break;

//(修改区 4)
outsideNum = num;//为了让 num 有被使用到,防止被优化没了

在Unity中进行多次测试后取稳定值,记录实验数据

次数\实验编号012345678
18373827377637618161148683175289222418
28793760376137608492147772877295222517
38383950378937608168148903742298612673

本博主为了方便观察,画出直观图

:05 :10 :15 :20 :25 :30 3760~3873 3759~3903 3873~4258 3760~3958 3760~3778 3782~5465 3758~3829 3760~3848 3759~3773 8160~8166 8158~8697 8151~8341 14766~15025 14774~14949 14856~15055 2744~3577 2868~4044 3369~4163 28363~29649 29500~29701 29555~31073 2409~2420 2511~2526 2520~4093 求余 (double)float Unity Sqrt System Sqrt 数字运算性能测试直观图

但是 System.Math.Sqrt 的超短时间计算确实出乎意料。本博主测试了每次测试 count *= 10,结果发现 System.Math.Sqrt 与 count 成线性相关 还是 1:1 那种。而且每次计算结果和 Unity.Mathf.Sqrt 的计算结果一样。

这次意料之外的测试在以后再进行探究。

接下来测试一下 NormalizeSqrt 的时间消耗:

//(修改区 1)
Vector3 targetPos = Random.onUnitSphere * Random.Range(0f, 42f);
Vector3 norDir;
double outsideNum;

//(修改区 2)
double num = 1314.520f;

其中 测试实验个数 n = 7 
//(修改区 3)
case 1:
    for (int i = 0; i < count; ++i)
    {
    	norDir = dir.normalized;
    }
    break;
case 2:
    for (int i = 0; i < count; ++i)
    {
         dir.Normalize();
         norDir = dir;
    }
    break;
case 3:
    for (int i = 0; i < count; ++i)
    {
        Vector3.Normalize(dir);
        norDir = dir;
    }
case 4:
    for (int i = 0; i < count; ++i)
    {
        outsideNum = Mathf.Sqrt((float)num);
    }
    break;
case 5:
    for (int i = 0; i < count; ++i)
    {
        float sqrMag = dir.sqrMagnitude;
        norDir = dir / Mathf.Sqrt(sqrMag);
    }
    break;
case 6:
    for (int i = 0; i < count; ++i)
    {
        float sqrMag = dir.x * dir.x + dir.y * dir.y + dir.z * dir.z;
        norDir = dir / Mathf.Sqrt(sqrMag);
    }
    break;
case 7:
    for (int i = 0; i < count; ++i)
    {
        float sqrMag = dir.x * dir.x + dir.y * dir.y + dir.z * dir.z;
        norDir = dir / System.Math.Sqrt(sqrMag);
    }
    break;

//(修改区 4)
norDir = Vector3.zero;

在Unity中多次测试后取稳定值,记录实验数据

次数\实验编号01234567
15041107908933910299136709828137029550343
2602103803846209776632458809546910549984
3486104342845939793634093846986912947001

由实验数据可知 Normalize 的性能与 直接 1/Mathf.Sqrt(.sqrMagnitude) 没有太大的区别。而且使用 1/System.Math.Sqrt(.sqrMagnitude)会比正常1/Mathf.Sqrt(.sqrMagnitude)快一些,速度比大概是10:7
还比 Normalize 快一倍多。

二、变换系统测试

1. transform 的结构

transform 中的所有属性如下:
transform中的所有属性
transform 中的所有方法如下:transform中的所有方法

2. transform.position 赋值和获取

//(修改区 1)
Vector3 startPos = transform.position;

//(修改区 2)
Vector3 assignPos;

其中 测试实验个数 n = 4 
//(修改区 3)
//获取 transform.position
case 1:
    for (int i = 0; i < count; ++i)
    {
    	assignPos = transform.position;
    }
    break;
//正常 transform.position 加赋值
case 2:
    for (int i = 0; i < count; ++i)
    {
        transform.position += Vector3.up;
    }
    break;
//正常 transform.position 赋值
case 3:
    for (int i = 0; i < count; ++i)
    {
        transform.position = Vector3.up;
    }
    break;
//用向量赋值和 tranform.position 赋值比较
case 4:
    for (int i = 0; i < count; ++i)
    {
        assignPos = Vector3.up;
    }
    break;

//(修改区 4)
transform.position = startPos;

再在Unity中进行多次测试后取稳定值,记录实验数据

次数\实验名称对比组获取加赋值赋值向量赋值
12881182921924741175651949
23811587372673441588882264
35151715822852831535512276

由实验记录可知,单单获取 transform.position 的时间消耗和赋值 transform.position 差不多,甚至还是给普通向量赋值的 70 倍。
可以得出结论:

如果要使一个物体移动,先计算好所有向量的合向量,再给 transform.position 赋值,也不能频繁获取 transform.position ,如果常用的话就先储存在 Vector3 中。

新人博主,请大家多多光照~~如果有什么不正确或不足的地方请在评论区积极指出哟,一起学习一起进步~

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-08-17 15:43:53  更:2021-08-17 15:44:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 18:15:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码