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 hub 自带教程学习笔记(ruby、lemonJohn) -> 正文阅读

[游戏开发]unity hub 自带教程学习笔记(ruby、lemonJohn)

https://learn.unity.com/?_gl=1*1c5ya13*_ga*MTAzODc3MzI4LjE1OTc5MTQ3NzI.*_ga_1S78EFL1W5*MTYyNjM0NTY2MC4xNy4xLjE2MjYzNTM5ODAuNQ..&_ga=2.119106061.528537968.1626154667-103877328.1597914772

1. void OnTriggerEnter2D(Collider2D other){}? ? ? //2d游戏与触发器开始重叠,other内包含与当前触发器碰撞的对象的信息

RubyController controller = other.GetComponent<RubyController>();? //获得触发器碰撞对象中的组件.

添加尖括号的原因是 GetComponent 方法为泛型。泛型方法具有两组不同的参数:普通参数和类型参数。尖括号之间列出的参数是类型参数。

2. 脚本中的public的变量将在unity脚本中显示出来,可进行重载;

3. public int health { get { return currentHealth; } } //让外部获取变量但不可修改的只读函数示例。

4.

Rigidbody2D rigidbody2d;
rigidbody2d = GetComponent<Rigidbody2D>();? ? //获得刚体变量
?

horizontal = Input.GetAxis("Horizontal");
vertical = Input.GetAxis("Vertical");? ?//获得坐标

刚体运动

Vector2 position = rigidbody2d.position;
position.x = position.x + speed * horizontal * Time.deltaTime;
position.y = position.y + speed * vertical * Time.deltaTime;

动作表现

animator.SetFloat("Move X",?0); animator.SetFloat("Move Y", direction);

在此部分中,如果机器人垂直移动,则会将?0?发送到 horizontal 参数,而 direction 定义机器人是向上还是向下移动。

5.

rigidbody2d.MovePosition(position);? //刚体移动

6.

currentHealth = Mathf.Clamp(currentHealth + amount, 0, maxHealth);
//使结果不能超过前后两个数值。

7.

void FixedUpdate()? ? //非每帧更新,每隔一个固定时间更新一次

8.

void?OnCollisionEnter2D(Collision2D other)? //刚体与某个对象碰撞时调用的函数

9.

Mathf.Approximately(move.x, 0.0f)? ?//类似==,因为计算机存储浮点数的方式意味着精度会有很小的损失。所以,不应测试完美的相等性,因为最终结果应该是?0.0f?的运算可能会得出类似?0.0000000001f?的结果。Approximately?会考虑这种不精确性,如果除去这种不精确性可以认为数值相等,将返回 true。

.Normalize()? //使长度等于1,用在vector2里作为方向储存

.magnitude? ?//计算向量长度

10.

GameObject projectileObject = Instantiate(projectilePrefab, rigidbody2d.position + Vector2.up * 0.5f, Quaternion.identity);
//Instantiate?是?Unity 的一个函数,Instantiate?的第一个参数是一个对象,在第二个参数的位置处创建一个副本,第三个参数是旋转。

四元数 (Quaternion)?是一种可以表达旋转的数学运算符,但这里只需要记住的是,Quaternion.identity?表示“无旋转”。

11.

if(transform.position.magmitude >1000.0f)? //position?可以看作是从世界中心到对象所在位置的向量,magnitude?是该向量的长度。因此,position 的 magnitude 就是到中心的距离。
?

可以获取角色和齿轮之间的距离(使用?Vector3.Distance (a,b)?函数计算?position a?与?position b?之间的距离)。

12.

smokeEffect.Stop();? //

  • 注释掉?smokeEffect.Stop()?并在下一行添加?Destroy(smokeEffect.gameObject)。

  • 运行场景,并修复机器人。

烟雾会立即消失。这是因为当粒子系统被销毁时,也会销毁当前正在处理的所有粒子,即使是刚刚创建的粒子。

但?Stop?只会阻止粒子系统创建粒子,已经存在的粒子可以正常结束自己的生命周期。这比所有粒子突然消失要看起来自然得多。

13.

实例化粒子系统

与飞弹一样,从粒子系统制作预制件后,便可以使用?Instantiate 函数创建粒子系统。

因此,对于仅发生一次的效果(例如被击中或拾取生命值可收集对象),你可以将对预制件的引用存储在公共变量中,并在应该产生效果时调用?Instantiate。如果?Particle System?未设置为?Looping?并且?Stop Action?设置为?Destroy,则会播放效果,然后销毁。

14。

originalSize = mask.rectTransform.rect.width;? //获取UI的宽度

mask.rectTransform.SetSizeWithCurrentAnchors(RcctTransform.Axis.Horizontal, originalSize * value);? ?//以当前锚点设置大小

代码行 using?UnityEngine; 让你无需在?UnityEngine?中的所有类(如?GameObject)前面键入?UnityEngine。所以,如果你在?using.UnityEngine; 代码行下面直接添加?using UnityEngine.UI; 代码行,你的代码将会正确编译,因为现在可以在该脚本内直接使用 Image 了。

15.

public?class?UIHealthBar?:?MonoBehaviour?{

public static UIHealthBar instance { get; private set; }

void?Awake()?{ instance =?this; }

  • static UIHealthBar instance 属性表示是静态的公共属性,因此可以在任何脚本中编写?UIHealthBar.instance,都会调用该 get 属性。set 属性是私有属性,因为我们不希望其他人能够从脚本外部进行更改。

  • 然后在你的?Awake 函数中(注意,一旦创建了对象,在我们这种情况下就是指游戏开始后,便会调用此函数),你在静态实例中存储了?this,这是一个特殊的?C# 关键字,表示“当前运行该函数的对象”。

  • 静态成员在脚本的所有实例之间共享,因此,在附加了相应脚本的所有游戏对象中,静态成员的值都是相同的。

16.

if?(Input.GetKeyDown(KeyCode.X))

{ RaycastHit2D hit = Physics2D.Raycast(rigidbody2d.position + Vector2.up *?0.2f, lookDirection,?1.5f, LayerMask.GetMask("NPC"));

首先,你将声明一个类型为?RaycastHit2D?的新变量。此变量存储由?Physics2D.Raycast?提供的?Raycast?的结果。

Physics2D.Raycast?有多种版本,但我们在这里使用的版本有四个参数:

你的示例中的起点是从 Ruby 的位置向上偏移,因为你想从 Ruby 精灵中心进行测试,而不是从她的双脚。

方向,这是 Ruby 注视的方向。

射线的最大距离应设置为?1.5,这样射线投射不会测试相距起点?1.5?个单位的相交点。

一个图层遮罩,允许我们仅测试某些图层。在相交测试期间,所有不属于遮罩的图层都将被忽略。在这里,你将选择?NPC 图层,因为该图层包含你的青蛙。

17.?

名称开头的?m_?是什么意思?这是命名约定的一部分。命名约定用于标识特定对象或对象类。在此项目中,您将使用 Unity 的内部命名约定。所有变量均以小写字母开头,但随后的单词以大写字母开头(这称为?camelCase)

例外情况是非公共成员变量,这种变量以 m_ 前缀开头,所有单词均以大写字母开头(这称为?PascalCase)。成员变量是属于类而不是属于特定方法的变量。非公共成员变量的 m_ 部分起源于它们是“成员 (member)”变量。

18.

m_Movement.Set(horizontal, 0f,?vertical);

3D 空间中的矢量具有三个值 — 此?Set?方法为每一个分配一个值。该方法有三个参数,矢量的每个坐标对应一个参数。

移动矢量由两个数字组成,这两个数字的最大值可以为 1。如果它们两者的值都为 1,则矢量的长度(称为其大小)将大于 1。这便是勾股定理描述的三角形的边之间的关系。

这意味着角色沿对角线移动的速度将比沿单个轴的移动速度更快。为了确保不会发生这种情况,您需要确保移动矢量始终具有相同的大小。为此,可对其进行标准化。对矢量进行标准化意味着保持矢量的方向相同,但是将其大小更改为 1。

m_Movement.Normalize?();

19.

Vector3 desiredForward = Vector3.RotateTowards (transform.forward, m_Movement, turnSpeed * Time.deltaTime,?0f);

  • 此代码创建一个名为?desiredForward?的 Vector3 变量。

  • 该变量设置为名为 RotateTowards 的方法的返回值,这个方法是 Vector3 类中的一个静态方法。RotateTowards 接受四个参数:前两个是 Vector3,分别是旋转时背离和朝向的矢量。

  • 该代码以?transform.forward?开头,目标是?m_Movement?变量。transform.forward 是访问 Transform 组件并获取其前向矢量的快捷方式。

  • 接下来的两个参数是起始矢量和目标矢量之间的变化量:首先是角度变化(以弧度为单位),然后是大小变化。此代码中的角度变化为?turnSpeed * Time.deltaTime,而大小变化为 0。

接下来,使用矢量来获取并存储旋转,以便可以在任何需要的地方使用。存储移动矢量一样存储旋转,因此在该位置声明其变量很合理。

Quaternion m_Rotation = Quaternion.identity;

四元数 (Quaternion)?是存储旋转的一种方式,可用于解决将旋转存储为 3D 矢量时遇到的一些问题。本教程不会详细探讨四元数,现在只需要知道它们是一种存储旋转的方式即可。

为 Quaternion 指定默认值 Quaternion.identity。通常情况下,创建类的实例时,会将属于类的变量(成员变量)而不是属于特定方法的变量设置为默认值。例如,Vector3 的默认值是将 x、y 和 z 都设置为 0。四元数也是如此。但是,虽然零矢量是合理的(因为没有移动),零四元数却不太合理。这里设置的 Quaternion.identity 值就是为其赋予无旋转的值,这是一个更合理的默认值。

创建了旋转变量,现在可以对其进行设置。在创建 desiredForward 变量的代码行下面,添加以下行:

m_Rotation = Quaternion.LookRotation (desiredForward);

该行代码仅调用?LookRotation?方法,并在给定参数的方向上创建旋转。

20.

该角色有一段有趣的 Walk 动画,最好为此使用根运动。但是,该动画中没有任何旋转,如果尝试在 Update 方法中旋转刚体 (Rigidbody),则动画可能会覆盖该刚体(这可能导致角色在应该旋转的时候不旋转)。

实际需要的是动画的一部分根运动,但不是全部的根运动;具体来说,需要应用移动而不是旋转。那么如何更改从 Animator 中应用根运动的方式呢?幸运的是,MonoBehaviour 有一种特殊的方法可用于更改从 Animator 中应用根运动的方式。

在 Update 方法下面,声明一个新方法:

void?OnAnimatorMove?()?{ }

让我们先从移动开始吧。将以下行添加到新的 OnAnimatorMove 方法中:

m_Rigidbody.MovePosition?(m_Rigidbody.position?+?m_Movement?*?m_Animator.deltaPosition.magnitude);

首先,要使用对 Rigidbody 组件的引用来调用其 MovePosition 方法,并传入唯一的参数:其新位置。新位置从刚体的当前位置开始,然后您在此基础上添加一个更改 — 移动矢量乘以 Animator 的 deltaPosition 的大小。但是,这是什么意思?

Animator 的?deltaPosition?是由于可以应用于此帧的根运动而导致的位置变化。将其大小(即长度)乘以我们希望角色移动的实际方向上的移动向量。

21.

以前,查找对组件的引用时,使用的是 GetComponent 方法,但这仅适用于与此脚本在同一游戏对象上的组件。 为 Canvas Group 组件创建一个可以在 Inspector 中分配的公共变量。?在 player 变量声明下面添加以下公共变量声明:

public?CanvasGroup exitBackgroundImageCanvasGroup;

22.

在代码块中添加以下代码行以退出游戏:

Application.Quit();

这里有一些重要的知识需要了解。该方法确实可以退出游戏,但仅适用于完全构建的应用程序。目前,该游戏只是在 Editor 中播放的一个项目,因此尚无任何作用。

23.

使用 Observer 类来检测玩家的角色。在脚本中的花括号之间,添加以下行:

public?Transform player;

if(other.transform == player)

该脚本将检查玩家角色的?Transform,而不是其游戏对象。这样可以更轻松地了解 JohnLemon 的位置并确定是否可以清楚看到他。

24.

有了方向,接下来便可以创建射线 (Ray)。在 direction 变量下面添加以下代码行:

Ray ray =?new?Ray (transform.position, direction);

Unity 中有很多不同的 Raycast 方法,但是所有这些方法都有两个共同点:它们需要定义发生射线投射的射线,以及关于要检测的碰撞体类型的限制。 您将使用的 Raycast 方法会返回一个布尔值,当射线投射击中某个对象时为 true,否则为 false。因为返回的是布尔值,所以将 Raycast 方法放在 if 语句中非常方便。if 语句的代码块仅在射线投射击中某个对象时才执行。 在创建射线 (Ray) 的位置下方,并且仍在之前编写的 if 语句内,添加以下代码:

if(Physics.Raycast(ray)) { }

已经定义了射线,并且没有限制射线投射可以检测到的碰撞体。对碰撞体的限制往往用作过滤器,而不是识别单个碰撞体。需要的是有关射线投射 (Raycast) 准确击中的对象的信息,以便可以检查这是否是玩家角色。 但有一个问题:从方法中获取信息的方式是通过其返回值,而这里的 Raycast 仅返回布尔值。但幸运的是有非常相似的 Raycast 方法,这个方法使用?out 参数。 out 参数的工作方式与普通参数相同,不同之处是参数前面要写关键字“out”。这些参数的值可以由它们的方法更改或设置,以便能够由任何调用方使用。out 参数有一个名为?RaycastHit?的类型,并且 Raycast 方法会将其数据设置为有关射线投射命中的任何对象的信息。 在创建 Ray 的代码行与 Raycast if 语句之间,添加以下代码:

RaycastHit raycastHit;

该行代码定义了 RaycastHit 变量。

接下来,需要更改 Raycast 方法以便使用此变量。将包含 Raycast 方法的 if 语句更改为如下所示:

if(Physics.Raycast(ray,?out?raycastHit)) { }

这是一个不同的 Raycast 方法,其工作方式非常类似,但使用 out 参数返回信息。

现在,脚本可以识别玩家的角色是否在范围内、执行射线投射并知道是否有任何对象被击中。接下来,需要检查击中了什么。

在 Raycast 的 if 语句中,添加以下代码:

if(raycastHit.collider.transform == player) { }

25.

为了给 Nav Mesh Agent 编写任何脚本,需要包含其命名空间。 在脚本顶部与其他 using 指令一起,添加以下代码:

using?UnityEngine.AI;

包含 AI 命名空间将让您能够访问?NavMeshAgent 类。

public?NavMeshAgent navMeshAgent;

这个对组件的公共引用将使您能够在 Inspector 窗口中分配 Nav Mesh Agent 引用。

需要设置幽灵应该巡逻的路径点。Nav Mesh Agent 的目标是 Vector3 矢量,即世界空间中的位置。但是,假如将路径点设置为 Vector3,则必须手动设置所有位置,并希望使用的数字准确无误。 相反,合理的做法是采用一组空游戏对象并将它们的位置用作路径点。但是,可以不引用一组游戏对象,而只是引用这些游戏对象的 Transform 组件。可以直接声明几个公共 Transform 变量,然后在 Inspector 窗口中分别设置每个变量,但在每个幽灵可以拥有的路径点数量方面,这种方案没有太大的灵活性。 实际上,更好的方案是使用数组。数组是一起存在的变量的基本集合。数组是通过方括号定义的。 在 navMeshAgent 变量声明下面添加以下行

public?Transform[] waypoints;

让我们继续向脚本添加:

.当幽灵到达设置为目标的最后一个路径点时,需要继续前进到下一个路径点。跟踪下一个路径点的最简单方法是存储路径点数组的当前索引。 在 waypoints 数组声明下面,添加以下代码:

int?m_CurrentWaypointIndex;

.接下来,让我们前往 Update 方法,以便可以使用这个索引。 在 Update 方法中,需要执行检查:您想知道 Nav Mesh Agent 是否已到达其目标。一种简单的检查方法是查看到目标的剩余距离是否小于先前在 Inspector 窗口中设置的停止距离。 在 Update 方法中添加以下 if 语句:

if(navMeshAgent.remainingDistance?<?navMeshAgent.stoppingDistance) { }

.现在,需要更新当前索引,然后将其用于设置 Nav Mesh Agent 的目标。为此,将使用一个称为求余运算符的新运算符,该运算符由百分比字符?%?表示。 在 if 语句中添加以下代码:

m_CurrentWaypointIndex = (m_CurrentWaypointIndex + 1) % waypoints.Length;

代码表示:“将当前索引加 1,但是如果该增量导致索引等于 waypoints 数组中元素的数量,则将索引设置为零。”在这种情况下,索引将被设置为零,因为任何数字除以本身时,余数均为零。

现在已经增加了索引(并在必要时将其循环回零),接下来需要使用索引。在索引增量设置下面添加以下代码:

navMeshAgent.SetDestination?(waypoints[m_CurrentWaypointIndex].position);

这与?Start 方法中所做的完全一样,唯一不同之处是使用幽灵当前所在的路径点作为索引,而不是使用零作为索引。

26.

if(!m_AudioSource.isPlaying) { }

向 if 语句中添加对 Play 方法的调用:

m_AudioSource.Play?();

要停止播放音频源,请将以下代码添加到 else 语句中:

m_AudioSource.Stop?();

27.

if?(Input.GetKeyDown(KeyCode.R)) { GetComponent<Renderer> ().material.color = Color.red;

28.全屏地图

public static FullscreenMap Instance { get; private set; }

public RawImage MapImageTarget;
public RectTransform Arrow;

public int Resolution;

public MinimapSystem.MinimapSystemSetting MinimapSystemSettings;

public RenderTexture RenderTexture => m_RT;

RenderTexture m_RT;
float m_Ratio;

void Awake()
{
Instance = this;
gameObject.SetActive(false);
m_Ratio = MapImageTarget.rectTransform.rect.height / MapImageTarget.rectTransform.rect.width;
m_RT = new RenderTexture(Resolution, Mathf.FloorToInt(Resolution * m_Ratio), 16, RenderTextureFormat.ARGB32);
MapImageTarget.texture = m_RT;
}

public void UpdateForPlayerTransform(Transform playerTransform)
{
MinimapSystem.Render(m_RT, playerTransform.position, playerTransform.forward, MinimapSystemSettings);

if (MinimapSystemSettings.isFixed)
{
Arrow.rotation = Quaternion.Euler(0, 0, Vector3.SignedAngle(playerTransform.forward, Vector3.forward, Vector3.up));
}
else
{
Arrow.rotation = Quaternion.identity;
}
}

29. 迷你地图

public class MinimapSystem
{
[System.Serializable]
public struct MinimapSystemSetting
{
public float heightStep;
public float halfSize;
public float wallThickness;
public Material material;
public bool isFixed;
}

static int s_WallThicknessId;

static MinimapSystem()
{
s_WallThicknessId = Shader.PropertyToID("_WallThickness");
}


public static void Render(RenderTexture renderTarget, Vector3 origin, Vector3 forward, MinimapSystemSetting settings)
{
settings.material.SetFloat(s_WallThicknessId, settings.wallThickness);

float aspectRatio = renderTarget.width / (float)renderTarget.height;

CommandBuffer buffer = new CommandBuffer();

Matrix4x4 lookAt;

Vector3 camPos = origin + Vector3.up * 3.0f;

if (settings.isFixed)
{
lookAt = Matrix4x4.TRS(camPos, Quaternion.LookRotation(Vector3.down, Vector3.forward), new Vector3(1, 1, -1)).inverse;
}
else
{
lookAt = Matrix4x4.TRS(camPos, Quaternion.LookRotation(Vector3.down, forward), new Vector3(1, 1, -1)).inverse;
}

buffer.SetRenderTarget(renderTarget);
buffer.SetProjectionMatrix(Matrix4x4.Ortho(-settings.halfSize * aspectRatio, settings.halfSize * aspectRatio, -settings.halfSize , settings.halfSize , 0.5f, 1.5f));
buffer.SetViewMatrix(lookAt);

buffer.ClearRenderTarget(true,true, Color.black);
foreach (var r in MinimapElement.Renderers)
{
buffer.DrawRenderer(r, settings.material);
}

Graphics.ExecuteCommandBuffer(buffer);
}
}
?

public class MinimapElement : MonoBehaviour
{
public static List<Renderer> Renderers => s_Renderers;

static List<Renderer> s_Renderers = new List<Renderer>();

Renderer m_Renderer;

void OnEnable()
{
m_Renderer = GetComponent<Renderer>();

if(m_Renderer != null)
s_Renderers.Add(m_Renderer);
}

void OnDisable()
{
if (m_Renderer)
s_Renderers.Remove(m_Renderer);
}
}

public class MinimapUI : MonoBehaviour
{
public static MinimapUI Instance { get; private set; }

public RawImage MapImageTarget;
public RectTransform Arrow;

public int Resolution;

public MinimapSystem.MinimapSystemSetting MinimapSystemSettings;

public RenderTexture RenderTexture => m_RT;

RenderTexture m_RT;
float m_Ratio;
bool m_HeightWasInited;
float m_InitialHeight;

void Awake()
{
Instance = this;
}

void Start()
{
m_HeightWasInited = false;
m_Ratio = MapImageTarget.rectTransform.rect.height / MapImageTarget.rectTransform.rect.width;
m_RT = new RenderTexture(Resolution, Mathf.FloorToInt(Resolution * m_Ratio), 16, RenderTextureFormat.ARGB32);
MapImageTarget.texture = m_RT;
}

public void UpdateForPlayerTransform(Transform playerTransform)
{
if (!m_HeightWasInited)
{
m_HeightWasInited = true;
m_InitialHeight = playerTransform.position.y;
}

Vector3 usedPosition = playerTransform.position;
float heightDifference = m_InitialHeight - usedPosition.y;
float heightSign = Mathf.Sign(heightDifference);
heightDifference = Mathf.FloorToInt(Mathf.Abs(heightDifference/MinimapSystemSettings.heightStep)) * heightSign * heightDifference;

usedPosition.y = m_InitialHeight + heightDifference;

MinimapSystem.Render(m_RT, usedPosition, playerTransform.forward, MinimapSystemSettings);

if (MinimapSystemSettings.isFixed)
{
Arrow.rotation = Quaternion.Euler(0, 0, Vector3.SignedAngle(playerTransform.forward, Vector3.forward, Vector3.up));
}
else
{
Arrow.rotation = Quaternion.identity;
}
}
}

  游戏开发 最新文章
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-03 11:32:18  更:2021-08-03 11:33:23 
 
开发: 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/4 9:50:42-

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