【Unity】常用API和功能

前言

持续更新中..  
没有更新必要了,原本为了记录温故知新,现在直接问ChatGPT(2023.11.27)
ChatGPT经常人工智障,还是继续更新好了(2024.06.28)

目标阅读者:Unity新手 项目程序员
简单的前置:编程习惯

Unity常用API

旧版按键Input
void Update()
    {
        if (Input.GetKey("up"))
        {
            print("up arrow key is held");
        }
        if (Input.GetKeyDown("down"))
        {
            print("down arrow key is held down");
        }       
        if (Input.GetMouseButton(0))
        {
            print("鼠标左键点击");
        } 
    }

OnGUI
void OnGUI()
    {
        if (GUI.Button(new Rect(10, 10, 150, 100), "I am a button"))
        {            
        }
    }

Debug(Log)
/*正常Log*/
Debug.Log("<color=#9400D3> You clicked the button! </color>");
/*跳转到GameObject*/
Debug.Log(Obj,"跳转");

/*颜色*/
//green 绿色	#008000ff    red 红色	#ff0000ff
//white 白色	#ffffffff	 yellow 黄色	#ffff00ff
         
/*转义符*/
\’   单引号符 
\”  单引号符 
\\  反斜线符"\" 
\n  换行
\r  回车 
text在运行后\会自动变成了\\,所以无效解决方法:
msgText.text = msg.Replace("\\n", "\n");

发布pc日志路径
Windows   %localappdata%\..\LocalLow\CompanyName\ProductName\Player.log
或者
Windows    %localappdata%\..\LocalLow\CompanyName\ProductName\output_log.txt

发布的时候勾选development build的unity游戏和常规build有两方面不同:development build在游戏运行时可以连接profiler,并且包含了调试用的文件。
取消发布log消耗

Draw
  • Debug.DrawLine
    public float radius = 10;
    public float angle = 360;
    public int smooth = 20;
    public Vector3 dir = Vector3.right;
    public bool drawSide = true;

    void Update()
    {
        Vector3 firstPos;
        Vector3 lastPos = Vector3.zero;
        for (int i = 0; i <= smooth; i++)
        {
            firstPos =  Quaternion.Euler(0, 0, (angle  / smooth) * i) * dir * radius;
            lastPos = Quaternion.Euler(0, 0, (angle / smooth) * (i + 1)) * dir * radius;
            Debug.DrawLine(firstPos, lastPos, Color.red);
        }
        if (drawSide)
        {
            Vector3 start = Quaternion.Euler(0, 0, -angle) * dir * radius;
            Debug.DrawLine(transform.position, transform.position + start, Color.red);
            Debug.DrawLine(transform.position, lastPos, Color.red);
        }
    }
  • Gizmos and Handles

Unity - Manual: Gizmos and Handles

  • LineRender
private LineRenderer line;//画线
line = this.gameObject.AddComponent<LineRenderer>();
//只有设置了材质 setColor才有作用
line.material = new Material(Shader.Find("Particles/Additive"));
line.SetVertexCount(2);//设置两点
line.SetColors(Color.yellow, Color.red); //设置直线颜色
line.SetWidth(0.1f, 0.1f);//设置直线宽度

//设置指示线的起点和终点
line.SetPosition(0, start.position);
line.SetPosition(1, end.position);

public float R;//半径
public int N;//不要超过45

line.SetVertexCount(N+1);//这里要加1,达成闭合曲线
for (int i = 0; i < N + 1; i++)
{
  float x = R * Mathf.Cos((360 / N * i) * Mathf.Deg2Rad) + transform.position.x; //确定x坐标
  float z = R * Mathf.Sin((360 / N * i) * Mathf.Deg2Rad) + transform.position.z; //确定z坐标
  line.SetPosition(i, new Vector3(x, transform.position.y, z));         
}

更多画线工具插件: Physics Debug Extension


移动与旋转
//-------------旋转------------ //
//设置角度     (超过90°或负数时,会设置-1结果是359这样的问题,可以使用下面旋转的方式)
transform.rotate = new Quaternion(0,0,0,0);//Quaternion四元数
transform.localEulerAngles = new Vector3(0,0,0);//EulerAngles欧拉角
Vector3.Angle(form, to)//夹角

//旋转
transform.rotation = Quaternion.AngleAxis(30, Vector3.up);//绕轴旋转Quaternion正方向30
transform.Rotate(new Vector3(20 * Time.dealtime, 0, 0));//旋转XYZ
transform.RotateAround(Vector3.zero, Vector3.up, 50 * Time.dealtime);//绕点旋转

//四元数与欧拉角转换
Quaternion quaternoin= Quaternion.Euler(vector3);
Vector3 rotation = Quaternion.EulerAngles(quaternoin);

//旋转向量
targetVector = quaternion * origVector;
var destDir = Quaternion.Euler(0, angle, 0) * Transform.forward;

//获取角度  不能获取正负,暂没找到方法
Vector3 rotation = Obj.transform.localEulerAngles;
//轴LookAt
        var targetDir = lookAtTgt.position - transform.position;
        //指定哪根轴朝向目标,自行修改Vector3的方向
        var directionAxis = lookAtAxis == Axis.X ? Vector3.right :
            lookAtAxis == Axis.Y ? Vector3.up : Vector3.forward;

        var fromDir = transform.rotation * directionAxis;
        //计算垂直于当前方向和目标方向的轴
        var axis = Vector3.Cross(fromDir, targetDir).normalized;
        //计算当前方向和目标方向的夹角
        var angle = Vector3.Angle(fromDir, targetDir);
        transform.rotation = Quaternion.AngleAxis(angle, axis) * transform.rotation;
        transform.rotation *= Quaternion.Euler(angleOffsetX,angleOffsetY, angleOffsetZ);

//-----------3D位移---------- //
//方向
transform.Translate(Vector3.back * FallingSpeed);
transform.position = Vector3.MoveTowards(transform.position,tempUpdatePos, 
                      Time.deltaTime * DisShakeThreshold);
//固定时间
transform.Translate(Vector3.normalize(tarPos-selfPos) *
         (Vector3.Distance(selfPos,tarPos)/(setTime * Time.deltime)));


//-----------2D方向移动---------- //
moveDirection = mTarget.transform.position - mSelf.transform.position;
moveDirection.Normalize();
float target = Mathf.Atan2(moveDirection.y, moveDirection.x) * Mathf.Rad2Deg - 90f;
mSelf.transform.rotation = 
Quaternion.Slerp(mSelf.transform.rotation,  
Quaternion.Euler(0, 0, target), turnSpeed * Time.deltaTime);
_controller.SetForce(speedX,speedY));

DOTween

显示模式分为:
Center状态:按照计算GameObject的中心
Pivot状态:GameObject的时机位置

Animator/Animation

Animator结构
Animator(组件)→runtimeAnimator(*.controller)→Layer→State→Animation Clips(*.anim)
Unity 从Animator组件中获得AnimationClip(editor工具不用new)
animator.update()可以保证更新一致(lockstep用)

Animation组件
如果使用Animation组件,clip的设置需要先打卡锁旁边的设置为debug,然后勾选legacy
animation不能播放ui.image的序列帧动画
baseonrenderes在屏幕外结束的动画,再次播放会被裁剪..
Animator可以被继承!
Animator可以添加脚本处理状态!

RootMontion
可以使用RootMotion达到美术想要的移动,如果要逻辑表现分离,那么可以导出数据

AnimationEvent
在Clip上可以直接打事件,事件会获取所有Component的所有Public方法
也可以使用AnimationEvent作为参数
在第0帧和末尾真可能会不触发 ,
据测试,BlendTree是但凡有一点都会触发,完全没有则不会触发。

获取某个动画名字的时间长度

void GetLengthByName(string name)
{
    AnimationClip[] clips = animator.runtimeAnimatorController.animationClips;
    foreach(AnimationClip clip in clips)    
    {
        if(clip.name.Equals(name))        
        {
            float length = clip.length;            
            break;
        }
    } 
}

重置动画(播放动画第一帧)

firstAnimName = GetComponent<Animator>().GetCurrentAnimatorClipInfo(0)[0].clip.name;
GetComponent<Animator>().Play (firstAnimName,-1,0); 
资源加载和创建删除

//作为重要功能 深入研究会发现坑很多→详细解析

/************创建****************/
/*********Resource文件夹*********/
/*同步加载*/
GameObject prefab = Resources.Load("资源路径/名字") as GameObject;
/*异步加载*/
GameObject prefab2 = Resources.LoadAsync<Object>(path);
/*实例化*/
GameObject go = Instantiate(prefab) as GameObject;

/*************非Resourece**************/
//路径为 Assets/XX/XX.prefab 这样的格式
UnityEditor.AssetDatabase.LoadAssetAtPath<T>(path);

/**************AssetBundle**************/
AssetBundle bundle = AssetBundle.LoadFromFile(path);
Object obj = bundle.LoadAsset<GameObject>("XXXX");


/******************删除****************/
//非AB
GameObject.Destroy(go);//下一帧删除
GameObject.DestroyImmediate(go);//马上删除

//AB
Destroy UnloadAB(true) 
如果没清除依赖UnloadUnUseAsset然后GC

Destory的坑:会before删除

射线射碰撞体与层
//(检测是否碰撞)
//parameter1:origin起始位置
//parameter2:世界方向
//parameter3:长度
//parameter4:检测的层
if (Physics.Raycast(origin, Vector3.down, 1f,1 << LayerMask.NameToLayer("Ground")))
{
    //层的说明
    //int 1-31是未转化的层index 而用于射线LayerMask 是1<<int 运算后的 是一个很大的数
    // 1 << 10 打开第10的层。 等价于 1 << LayerMask.NameToLayer("Ground");
    //~(1 << 10) 打开除了第10之外的层。
    //~(1 << 0) 打开所有的层。
    //(1 << 10) | (1 << 8) 打开第10和第8的层。等价于 LayerMask.GetMask(("Ground", "Wall");
}
//(输出信息的)
Ray ray = new Ray(transform.position, Vector3.down);
RaycastHit hitInfo = new RaycastHit();
if (Physics.Raycast(ray, out hitInfo, 30f, layerMask))
    Debug.Log(hitInfo.point);

//物体
gameObject.layer = (1-31层 或 LayerMask.NameToLayer("Ground"));
1 << layer = LayerMask
//发射碰撞体
Physics.OverXXX
//一些问题
//射线无法检查到起点边缘的物体

在Unity中每个GameObject都有Layer属性,默认的Layer都是Default。在Unity中可编辑的Layer共有24个(8—31层),官方已使用的是0—7层,默认不可编辑!

LayerMask实际上是一个位码操作,在Unity3D中一共有32个Layer层,并且不可增加。

位运算符
按位运算符:~、|、&、^。位运算符主要用来对二进制位进行操作。

逻辑运算符:&&、||、!。逻辑运算符把语句连接成更复杂的复杂语句。

按位运算符:左移运算符<<,左移表示乘以2,左移多少位表示乘以2的几次幂。

 物理

Edit-Project Settings-Physics
打开物理管理器(Physics Manager)里面可以设置层级相互间的碰撞
碰撞条件是都带Collider 其中一个带rigidbody

//触发器
MonoBehaviour.OnTriggerEnter(Collider collider)当进入触发器
MonoBehaviour.OnTriggerExit(Collider collider)当退出触发器
MonoBehaviour.OnTriggerStay(Collider collider)当逗留触发器
 
//碰撞信息检测:
MonoBehaviour.OnCollisionEnter(Collision collision) 当进入碰撞器
MonoBehaviour.OnCollisionExit(Collision collision) 当退出碰撞器
MonoBehaviour.OnCollisionStay(Collision collision)  当逗留碰撞器
刚体

Mass:质量(kg)
Drag:空气阻力 越大越难推动
Angular Drag:旋转阻力  越大越不会旋转 0无阻力 10以后基本无惯性
Use Gravity:是否使用重力
Is Kinematic:运动学

Constraints
Freeze Position:锁定位置
Freeze Rotation:锁定旋转

协程IEnumerator
void Start(){
    StartCoroutine(Test());
}
IEnumerator Test()
{
    yield return null; // 下一帧再执行后续代码
    yield return 0; //下一帧再执行后续代码
    yield return 6;//(任意数字) 下一帧再执行后续代码
    yield break; //直接结束该协程的后续操作
    yield return asyncOperation;//等异步操作结束后再执行后续代码
    yield return StartCoroution(/*某个协程*/);//等待某个协程执行完毕后再执行后续代码
    yield return WWW();//等待WWW操作完成后再执行后续代码
    yield return new WaitForEndOfFrame();//等待帧结束,等待直到所有的摄像机和GUI被渲染完成后,在该帧显示在屏幕之前执行
    yield return new WaitForSeconds(0.3f);//等待0.3秒,一段指定的时间延迟之后继续执行,在所有的Update函数完成调用的那一帧之后(这里的时间会受到Time.timeScale的影响);
    yield return new WaitForSecondsRealtime(0.3f);//等待0.3秒,一段指定的时间延迟之后继续执行,在所有的Update函数完成调用的那一帧之后(这里的时间不受到Time.timeScale的影响);
    yield return WaitForFixedUpdate();//等待下一次FixedUpdate开始时再执行后续代码
    yield return new WaitUntil()//将协同执行直到 当输入的参数(或者委托)为true的时候....如:yield return new WaitUntil(() => frame >= 10);
    yield return new WaitWhile()//将协同执行直到 当输入的参数(或者委托)为false的时候.... 如:yield return new WaitWhile(() => frame < 10);
}


想使用协程如果没有继承mono 那可以使用一个继承mono的去做
当代码不依赖mono而又想使用协程 那么可以使用这个工具
每当这个工具被调用是 创建一个新的GameObject去启动任务IEnumerate的Func 
因为当需要停止的时候 虽然mono有stop的方法 但是对于同名的函数就没有办法了,所以每次调用都会创建go

    public class CoroutineComponent : Component
    {
        public static CoroutineComponent Instance { get; private set; }
        public delegate System.Collections.IEnumerator CoroutineFunc();
        private GameObject root;
        private Dictionary<CoroutineFunc, GameObject> dicFunc2Object;

        public void Awake()
        {
            Instance = this;
            root = new GameObject("CoroutineRoot");
            root.transform.SetParent(GameObject.Find("Global/").transform);
            dicFunc2Object = new Dictionary<CoroutineFunc, GameObject>();
        }
        public void Destroy()
        {
            UnityEngine.Object.Destroy(root);
        }
        public void StartCoroutine(CoroutineFunc func)
        {
            if (dicFunc2Object.ContainsKey(func))
            {
                HotfixMonoCoroutine cor =         dicFunc2Object[func].GetComponent<HotfixMonoCoroutine>();
                cor.StartCoroutine(func());
            }
            else
            {
                GameObject obj = new GameObject();                obj.transform.SetParent(GameObject.Find("Global/CoroutineRoot/").transform);
                HotfixMonoCoroutine cor = obj.AddComponent<HotfixMonoCoroutine>();
                dicFunc2Object[func] = obj;
                cor.StartCoroutine(func());
            }
        }
        public void StopCoroutine(CoroutineFunc func)
        {
            GameObject obj = null;
            if (dicFunc2Object.TryGetValue(func, out obj))
            {
                UnityEngine.Object.Destroy(dicFunc2Object[func]);
                dicFunc2Object.Remove(func);
            }
        }
平台预处理
#if UNITY_EDITOR  
    platform = "hi,大家好,我是在unity编辑模式下";  
#elif UNITY_SERVER
#elif UNITY_IPHONE  
    platform="hi,大家好,我是IPHONE平台";  
#elif UNITY_ANDROID  
    platform="hi,大家好,我是ANDROID平台";  
#elif UNITY_STANDALONE_OSX  
#elif UNITY_STANDALONE_WIN  
    platform="hi,大家好,我是Windows平台";  
#endif  
    Debug.Log("Current Platform:" + platform);  
二进制文件读写  

DoFile 读取一个Txt

BinaryWriter和BinaryReader(二进制文件的读写)_起个名字好难啊-CSDN博客

string localConfig = Application.dataPath + "/Config/123.txt";
if (File.Exists(localConfig))
{
    string[] strs = File.ReadAllLines(localConfig);
}
UGUI

进度条 -- 通过改变UI的FillAmount值 UnityEngine.UI.Image healthBar.fillAmount = 0.0f;

艺术字体 -- 对于Text 使用材质和Outline可以制作 但会引起drawcall增加

Mask -- 可以遮挡 对于制作一些动画比较好 但会引起drawcall增加

自动排列用 Layout Group 如果要适配改变大小 都要加Content Size Fitter 要改变的设置为preferred size;
 

渲染

设置窗口(Window->Lighting->Settings)是主要控制unity全局光照(GlobalIllumination GI)的地方。尽管GI的默认设置已经有了很好的效果,lighting设置面板的一些属性可以调节GI处理的很多方面,从而可以定制场景或者优化场景的质量、速度和存储空间。窗口还包括环境光、光晕、雾效、烘焙的设置。

可使用Class.UnityEngine.RenderSettings代码改变这些

Sorting Group

用法1:最上层添加,下层会按照Hierarchy排序
用法2:同级都加上然后设置Layer和Oder

LOD Group

LOD 组 (LOD Group) 组件管理游戏对象的细节级别 (LOD)。
可以在Quality内设置影响LOD Group最终效果

Lod Bias设置细节级别 (LOD) 偏差。
根据对象在屏幕上的大小选择 LOD 级别。当大小在两个 LOD 级别之间时,可以偏向于两个可用模型中细节级别更低或更高者。此属性设置为 0 到 +无穷大之间的值。设置为 0 到 1 之间时,表示倾向于更少细节。超过 1 的设置表示倾向于更多细节。例如,将 LOD Bias 设置为 2 并使其在 50% 距离处变化,LOD 实际上仅基于 25% 变化。
Maximum LOD Level设置游戏使用的最高 LOD。请参阅最大 LOD 级别以了解更多信息。

判断当前LOD是否生效

if (GUILayout.Button("Log Current LOD"))
{
    LODGroup lodGroup = script.GetComponent<LODGroup>();
    LOD[] lods = lodGroup.GetLODs();

    //输出当前活跃LOD
    for (int i = 0; i < lods.Length; i++)
    {
        foreach (Renderer renderer in lods[i].renderers)
        {
            Debug.Log($"当前LOD{i} isVisible: {renderer.isVisible}");
        }
    }

            // 获取LOD数组
            LOD[] lods = lodGroup.GetLODs();
            script.origLod0Renderers = lods[0].renderers;
            script.origLod1Renderers = lods[1].renderers;
            foreach (var r in lods[0].renderers)
                r.gameObject.SetVisible(false);
            foreach (var r in lods[1].renderers)
                r.gameObject.SetVisible(false);

            // 将LOD0和LOD1的渲染器设为LOD2的渲染器
            lods[0].renderers = lods[2].renderers;
            lods[1].renderers = lods[2].renderers;

            // 重新设置LOD数组
            lodGroup.SetLODs(lods);
}
路径

各路径的定义:
Resources路径
Resources文件夹是Unity里自动识别的一种文件夹,可在Unity编辑器的Project窗口里创建,并将资源放置在里面。
Resources文件夹下的资源不管是否有用,全部会打包进.apk或者.ipa,并且打包时会将里面的资源压缩处理。
Application.streamingAssetsPath路径
这个文件夹中的资源在打包时会原封不动的打包进去,不会压缩,一般放置一些资源数据。在PC/MAC中可实现对文件的“增删改查”等操作,但在移动端是一个只读路径。
Application.persistentDataPath路径
这个路径可读、可写,但是只能在程序运行时才能读写操作,不能提前将数据放入这个路径。在IOS上可以被iCloud自动备份;

Editor
Application.dataPath :                     项目路径/Assets
Application.streamingAssetsPath:  项目路径/Assets/StreamingAssets
Application.temporaryCachePath: C:/Users\username/AppData/Local/Temp/company name/product name
Application.persistentDataPath:   C:/Users\username/AppData/LocalLow/company name/product name

PC
Application.dataPath :                     exe文件夹/项目名_Data
Application.streamingAssetsPath:   项目路径/项目名_Data/StreamingAssets
Application.temporaryCachePath:  C:/Users\username/AppData/Local/Temp/company name/product name
Application.persistentDataPath:    C:/Users\username/AppData/LocalLow/company name/product name

安卓
Application.dataPath :                                   /data/app/xxx.xxx.xxx.apk
Application.streamingAssetsPath :                jar:file:///data/app/xxx.xxx.xxx.apk/!/assets(只读不可写)
Application.persistentDataPath :                   /data/data/xxx.xxx.xxx/files(运行可读写)
Application.temporaryCachePath :                /data/data/xxx.xxx.xxx/cache

IOS平台
Application.dataPath :                    Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/xxx.app/Data
Application.streamingAssetsPath : Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/xxx.app/Data/Raw
Application.persistentDataPath :    Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Documents
Application.temporaryCachePath : Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Library/Caches

创建自定义Package

创建自定义包 - Unity 手册
文件夹:UrPackage,RunTime,Editor
文件:Jason,asmdef,ReadMe(可选)
package manager里面添加disk package(直接修改json会在项目中搜不到)
上传:Package目录, manifest.json , packages-lock.json

Unity特性

1.unity具有特殊意义的文件夹

link

2.Editor的support文件夹

打包支持在Editor/Data/PlaybackEngines

3.unity时间函数执行顺序

数学相关

向量与夹角与三角函数

//U3D  Vector3.forward 实际是(0,0,1) 向前的意思 也就是右上角Z轴方向
//forward是蓝色箭头方向 back是反方向  right是红色箭头方向 left反之 up是绿色箭头方向 down反之

Vector3 vector = targetPos - originPos;//任意两点向量 = V3目标位置 - V3起点位置
float angle = Vector3.Angle(from, to); //向量夹角 v3.angle(向量A,向量B)
Vector3.Normalize;//单位向量

//Deg2Rad 度转弧度 ; Rad2Deg 弧度转度
//求角度的三角函数值
Mathf.Sin(Mathf.Deg2Rad * angle);
//求变长求角度
Mathf.Atan2(y,x) * Mathf.Rad2Deg;

其他

  1. Windows工具:
    svn项目同步
    AutoHotKey作为快捷键操作打开
    Clover作为打开文件夹的工具
    SETUNA作为截图工具
  2. 音频
    插件:wwise(教程看101课程)
    控制全局音量
    通过 AudioListener.volume来控制场景场景音量

  3. Mac鸡工具 

    连接:可以使用vnc viewer,anydesk
    项目管理:Cornerstone

  4. 调试
    调试安卓:直接在调试面板Console/Profiler输入ip链接(unity在2020的新版中甚至支持无需编译改代码)
    苹果:链接XCode调试

  • 15
    点赞
  • 110
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Unity3D中常用API包括Component、Transform、GameObject、Object和Time等。\[1\]这些API可以用于处理游戏对象的组件、变换、实例化和销毁等操作。例如,可以使用Component来获取和管理游戏对象的组件,使用Transform来控制游戏对象的位置、旋转和缩放,使用GameObject来创建、查找和销毁游戏对象,使用Object来处理资源的加载和释放,使用Time来获取游戏的时间信息。此外,还有一些常见的API函数,如Instantiate函数用于实例化预制件,Start函数和Update函数用于处理游戏对象的初始化和更新等。\[2\]\[3\]这些API和函数是Unity开发中经常使用的工具,可以帮助开发者实现各种功能和效果。 #### 引用[.reference_title] - *1* [Unity3D数字孪生笔记——Unity常用API篇](https://blog.csdn.net/Lcl_huolitianji/article/details/120875486)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Unity3D常用API](https://blog.csdn.net/u011360242/article/details/77046732)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [【Unity 3D】常见API的讲解以及在C#脚本中的执行(附源码)](https://blog.csdn.net/jiebaoshayebuhui/article/details/128510864)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值