2017-05-11 21:27:39 BIGMAD 阅读数 4597
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

Unity3D跟随路径移动

在做项目的时候,突然想到要做一个可以跟随路径走的AI,而且路径可视化。
原视频地址:https://www.youtube.com/watch?v=1aBjTa3xQzE

分为两部分代码,第一个是编辑路径的代码,另一个为跟随路径的代码。


代码如下

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class EditorPathScript : MonoBehaviour {

    public Color rayColor = Color.white;
    public List<Transform> path_objs = new List<Transform>();
    Transform[] theArray;

    void OnDrawGizmos(){
        Gizmos.color = rayColor;
        theArray = GetComponentsInChildren<Transform>();
        path_objs.Clear();
        foreach(Transform path_obj in theArray){
            if(path_obj != this.transform){
                path_objs.Add(path_obj);
            }
        }
        for(int i = 0;i<path_objs.Count;i++){
            Vector3 position = path_objs[i].position;
            if(i>0){
                Vector3 previous = path_objs[i-1].position;
                Gizmos.DrawLine(previous,position);
                Gizmos.DrawWireSphere(position, 0.3f);
            }
        }

    }

    void Start () {

    }

    // Update is called once per frame
    void Update () {

    }
}

挂到一个游戏物体上,然后在该游戏物体下创建子游戏物体,在scene视图可以看到。
scene视图
Scene视图
至少要有两个物体,这样才会连成线。

hierarchy视图
编辑游戏物体

inspector视图
游戏对象脚本

第二部分代码如下

using UnityEngine;
using System.Collections;

public class FollowPath : MonoBehaviour {

    public bool StartFollow = false;
    public EditorPathScript PathToFollow;
    public int CurrentWayPointID = 0;
    public float Speed;//移动速度
    public float reachDistance = 0f;//里路径点的最大范围
    public string PathName;//跟随路径的名字
    private string LastName;
    private bool ChangePath = true;



    void Start () {

    }

    void Update () {
        if (!StartFollow)
            return;
        if (ChangePath)
        {
            PathToFollow = GameObject.Find(PathName).GetComponent<EditorPathScript>();
            ChangePath = false;
        }
        if (LastName != PathName)
        {
            ChangePath = true;
        }
        LastName = PathName;




        float distance = Vector3.Distance(PathToFollow.path_objs[CurrentWayPointID].position, transform.position);
        //transform.Translate(PathToFollow.path_objs[CurrentWayPointID].position * Time.deltaTime * Speed, Space.Self);
        transform.position = Vector3.MoveTowards(transform.position, PathToFollow.path_objs[CurrentWayPointID].position, Time.deltaTime * Speed);
        if (distance <= reachDistance)
        {
            CurrentWayPointID++;
        }
        if (CurrentWayPointID >= PathToFollow.path_objs.Count)
        {
            CurrentWayPointID = 0;
        }
    }
}

脚本挂到要移动的游戏物体上,PathName是路径的名字。

和视频里面的脚本有一点不同,我加了开关,然后能动态检测名字。。。当名字改变后仍然能够继续运行。

在EditorPath下创建两个子物体,然后挂上编辑脚本,然后再创建路径
这里写图片描述
这里写图片描述
这里写图片描述

这里写图片描述
指定Path1为跟随路径。

挂到游戏物体上,打开StartFollow,可以看到他自动往路径点跑,然后一个一个接着跑,最后会循环。

2014-03-05 22:08:10 bean244 阅读数 19185
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

 在TORQUE引擎中,我们只要设置几个点,然后物体会按照这几个点顺序移动,Unity3d也可以的,现在介绍一个很简单的按照路径移动的方法。


目标是让蓝色方块沿着紫色方块组成的路径移动,设计思想就是让蓝色方块移动第一个,然后继续移动到第二个,然后.....,我们使用碰撞检测判定蓝色方块是否已经到位置来改变方向。

首先编写2个简单的脚本:

/----------------------------- move.cs 给蓝色方块使用------------------------------/
using UnityEngine;
using System.Collections;

public class move : MonoBehaviour
{
    //用来保存目标的数组
    public Transform [] obj;
    //用来改变数组的值
    private static int i = 0;
 
    void Start()
    {
    }
  
    void Update()
    {
        // 让我们的物体朝目标移动
        transform.LookAt(obj[i % obj.Length]);
        transform.Translate(Vector3.forward*Time.deltaTime*5);
    }

    //改变目标物体
    public static void Add()
    {
        i++;
    }
}
/----------------------------- move.cs end --------------------------/

/----------------------------- point.cs 给紫色方块使用 ----------------/
using UnityEngine;
using System.Collections;

public class point : MonoBehaviour {

// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}

    //当用户接触到目标物体改变参数到下一个目标物体
    void OnTriggerEnter(Collider cos)
    {
        move.Add();
    }
}
/----------------------------- point.cs end --------------------------/

蓝色方块的参数设置,注意要将box collider 换成Character Controller,这样才能跟紫色方块产生碰撞触发。

紫色方块的参数设置,注意要将Is Trigger 参数打钩,否则也不会产生碰撞检测。


运行工程,你就会发现蓝色方块会沿着紫色方块路径移动,不停的循环......

2018-07-26 21:10:23 wwlcsdn000 阅读数 1833
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

Unity场景物体路径复制插件


在unity开发中,经常需要去获取某个物体,这时我们需要用到Find方法,参数是一个路径。每次总是傻乎乎的复制黏贴,今天看到群里大佬说直接快捷键生成,自己也找资料搞了一个,代码基本没怎么改,整个代码的基本思路也很简单就不赘述了。增加了快捷键功能和Hierarchy面板右键功能。
原文地址:https://blog.csdn.net/yangjie6898862/article/details/51897335
下边有一部分设计Unity编辑器扩展,这里找了一个写的比较全面的文章
https://blog.csdn.net/wpapa/article/details/51066397

效果展示

这里写图片描述

这里写图片描述

使用方法

直接选中场景中的某个物体,然后使用CopyPath功能。
这里有三种方式,
一 菜单栏-GameObject-Copy Path
二 右键 Copy Path
三 默认快捷键 Ctrl + 1 (可以自己在代码里修改)
复制完后,可以直接去其他地方进行复制粘贴了。
这里写图片描述

完整代码

直接放在工程中即可,推荐还是放在Editor下边。

using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.IO;



public class FindPath
{
    public static List<string> filelist = new List<string>();
    public static List<string> deallist = new List<string>();
    public static string filepath;

    // Use this for initialization
    [MenuItem("GameObject/Copy Path %1", priority = 0)]
    public static void GetPath()
    {
        clearmemory();
        recursiveFind(Selection.activeGameObject.gameObject);

        printinScreen();
        clearmemory();
    }

    public static void recursiveFind(GameObject go)
    {
        if (go != null)
        {
            filelist.Add(go.name);
            if (go.transform.parent != null)
            {
                recursiveFind(go.transform.parent.gameObject);
            }
        }
    }


    public static void clearmemory()
    {
        filelist.Clear();
        deallist.Clear();
    }

    public static void printinScreen()
    {

        for (int i = filelist.Count - 1; i >= 0; i--)
        {
            string str = filelist[i];
            if (i != 0)
            {
                str = str + "/";
            }
            deallist.Add(str);
        }
        string showstr = "";
        foreach (var list in deallist)
        {
            showstr += list;

        }
        Debug.Log(showstr);

        //text copy to shear plate
        TextEditor te = new TextEditor();
        te.content = new GUIContent(showstr);
        te.SelectAll();
        te.Copy();


    }

}
2012-11-28 13:22:54 xggllc 阅读数 5337
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

    这几天在写一个数字漫游项目,有一个要求是在高空鸟瞰,沿着固定的路径移动.........

    部分代码如下:C#代码,CameraMove.cs

using UnityEngine;
using System.Collections;
public class CameraMove : MonoBehaviour 
{
     int SpeedUp = 10;
     bool Automatic = true;
     Vector3 Goal = new Vector3(200,10,100);
     void Update ()
    {
       if(Automatic)
		{
			Speed = true;
			transform.position=Vector3.MoveTowards(transform.position,Goal,Time.deltaTime * SpeedUp);
		}
    }
}

主要是 这个 Vector3.MoveTowards当前的地点移向目标。

API 上没有具体介绍,

说是 “这个函数基本上和Vector3.Lerp相同,而是该函数将确保我们的速度不会超过maxDistanceDelta。maxDistanceDelta的负值从目标推开向量,就是说maxDistanceDelta是正值,当前地点移向目标,如果是负值当前地点将远离目标。”

第一个参数是 开始位置,第二个参数是 目标位置; 第三个参数 我理解为控制 移动速度与方向的;

上面代码中 SpeedUp 是为了控制 移动的速度的.......

上面代码拖动到摄像机上 ,就可以实现摄像机移动了......

貌似通过这个还可以设置按固定曲线轨迹移动,个人觉得可以通过一些逻辑判断来实现(PS:目前没要求曲线我就没有做测试)。

嘿嘿........小弟新手 ,希望大家多多指教~

                                                                                                                                                                                                                                                         破破

                                                                                                                                                                                                                                           2012年11月28日 13:02:44
2018-07-04 17:48:31 IT_yanghui 阅读数 2249
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

近日,项目需求:前提:三维空间

1.动态添加(删除)路径点,通过两个以上的路径点来绘制曲线,删除点之后不影响其他点绘制曲线;

2.每个路径点都可以被拖拽发生位移,可以通过锁定某个轴,使该轴不发生位移;

3.每个路径点处有两个可调节点(首尾只有一个调节点),可通过调节点来调节曲线切线(速度方向线)斜率,以达到平滑曲线;

4.使该曲线形成路径,隐藏曲线,Player沿曲线完成自动寻路。

先看效果,再贴代码。(使用贝塞尔曲线公式与LineRenderer绘制3D可调节曲线


代码如下:两个脚本。

1. DMDrawCurve.cs 挂载到任意对象即可

该脚本实现绘制曲线,动态添加(删除)点,发生位移时更新绘制曲线,以及隐藏该曲线。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DM.Editor.View
{
    [RequireComponent(typeof(LineRenderer))]
    public class DMDrawCurve : MonoBehaviour
    {
        public List<Transform> m_allPoints;
        private GameObject m_anchorPoint;
        private GameObject m_controlPoint;
        private GameObject m_pointParent;
        private LineRenderer m_lineRenderer;
        
        private int m_curveCount = 0;
        private int SEGMENT_COUNT = 60;//曲线取点个数(取点越多这个长度越趋向于精确)

        private static DMDrawCurve m_instance;
        public static DMDrawCurve Instance
        {
            get {
                if (null == m_instance)
                    m_instance = new DMDrawCurve();
                return m_instance;
            }
        }
        void Awake()
        {
            if (null == m_instance)
                m_instance = this;
            SetLine();
            if (null == m_anchorPoint)
                m_anchorPoint = Resources.Load("Prefabs/AnchorPoint") as GameObject;
            if (null == m_controlPoint)
                m_controlPoint = Resources.Load("Prefabs/ControlPoint") as GameObject;
        }
        void SetLine()
        {
            if (null == m_lineRenderer)
                m_lineRenderer = GetComponent<LineRenderer>();
            m_lineRenderer.material = Resources.Load("Materials/Line") as Material;
            m_lineRenderer.startColor = Color.red;
            m_lineRenderer.endColor = Color.green;
            m_lineRenderer.widthMultiplier = 0.2f;
        }

        public void Init(GameObject player)
        {//初始化一个基准点(Player)
            if (player == null) return;
            GameObject anchorPoint = LoadPoint(m_anchorPoint, player.transform.position);
            m_allPoints.Add(anchorPoint.transform);
        }      
        public void AddPoint(Vector3 anchorPointPos)
        {
            //初始化时m_allPoints添加了一个player
            if (m_allPoints.Count == 0) return;
            Transform lastPoint = m_allPoints[m_allPoints.Count - 1];
            GameObject controlPoint2 = LoadPoint(m_controlPoint, lastPoint.position+new Vector3(0,0,-1));   
            GameObject controlPoint = LoadPoint(m_controlPoint, anchorPointPos + new Vector3(0, 0, 1));
            GameObject anchorPoint = LoadPoint(m_anchorPoint, anchorPointPos);

            anchorPoint.GetComponent<CurvePointControl>().m_controlObject = controlPoint;
            lastPoint.GetComponent<CurvePointControl>().m_controlObject2 = controlPoint2;

            m_allPoints.Add(controlPoint2.transform);
            m_allPoints.Add(controlPoint.transform);
            m_allPoints.Add(anchorPoint.transform);

            DrawCurve();
        }
        public void DeletePoint(GameObject anchorPoint)
        {
            if (anchorPoint == null) return;
            CurvePointControl curvePoint = anchorPoint.GetComponent<CurvePointControl>();
            if (curvePoint && anchorPoint.tag.Equals("AnchorPoint"))
            {
                if (curvePoint.m_controlObject)
                {
                    m_allPoints.Remove(curvePoint.m_controlObject.transform);
                    Destroy(curvePoint.m_controlObject);
                } 
                if (curvePoint.m_controlObject2)
                {
                    m_allPoints.Remove(curvePoint.m_controlObject2.transform);
                    Destroy(curvePoint.m_controlObject2);
                }
                if (m_allPoints.IndexOf(curvePoint.transform) == (m_allPoints.Count - 1))
                {//先判断删除的是最后一个元素再移除
                    m_allPoints.Remove(curvePoint.transform);
                    Transform lastPoint = m_allPoints[m_allPoints.Count - 2];
                    GameObject lastPointCtrObject = lastPoint.GetComponent<CurvePointControl>().m_controlObject2;
                    if (lastPointCtrObject)
                    {
                        m_allPoints.Remove(lastPointCtrObject.transform);
                        Destroy(lastPointCtrObject);
                        lastPoint.GetComponent<CurvePointControl>().m_controlObject2 = null;
                    }
                }
                else
                {
                    m_allPoints.Remove(curvePoint.transform);
                }
                Destroy(anchorPoint);
                if(m_allPoints.Count == 1)
                {
                    m_lineRenderer.positionCount = 0;
                }
            }

            DrawCurve();
        }
        public void UpdateLine(GameObject anchorPoint, Vector3 offsetPos1, Vector3 offsetPos2)
        {
            if (anchorPoint == null) return;
            if (anchorPoint.tag.Equals("AnchorPoint"))
            {
                CurvePointControl curvePoint = anchorPoint.GetComponent<CurvePointControl>();
                if (curvePoint)
                {
                    if (curvePoint.m_controlObject)
                        curvePoint.m_controlObject.transform.position = anchorPoint.transform.position + offsetPos1;
                    if (curvePoint.m_controlObject2)
                        curvePoint.m_controlObject2.transform.position = anchorPoint.transform.position + offsetPos2;
                }
            }
            DrawCurve();
        }
        public List<Vector3> HiddenLine(bool isHidden=false)
        {
            m_pointParent.SetActive(isHidden);
            m_lineRenderer.enabled = isHidden;
            List<Vector3> pathPoints = new List<Vector3>();
            if(!isHidden)
            {
                for(int i = 0; i < m_lineRenderer.positionCount; i++)
                {
                    pathPoints.Add(m_lineRenderer.GetPosition(i));
                }
            }
            return pathPoints;
        }

        private void DrawCurve()//画曲线
        {
            if (m_allPoints.Count < 4) return;
            m_curveCount = (int)m_allPoints.Count / 3;
            for (int j = 0; j < m_curveCount; j++)
            {
                for (int i = 1; i <= SEGMENT_COUNT; i++)
                {
                    float t = (float)i / (float)SEGMENT_COUNT;
                    int nodeIndex = j * 3;
                    Vector3 pixel = CalculateCubicBezierPoint(t, m_allPoints[nodeIndex].position, m_allPoints[nodeIndex + 1].position, m_allPoints[nodeIndex + 2].position, m_allPoints[nodeIndex + 3].position);
                    m_lineRenderer.positionCount = j * SEGMENT_COUNT + i;
                    m_lineRenderer.SetPosition((j * SEGMENT_COUNT) + (i - 1), pixel);
                }
            }
        }
        private GameObject LoadPoint(GameObject pointPrefab,Vector3 pos)
        {
            if (pointPrefab == null)
            {
                Debug.LogError("The Prefab is Null!");
                return null;
            }
            if (null == m_pointParent)
                m_pointParent = new GameObject("AllPoints");
            GameObject pointClone = Instantiate(pointPrefab);
            pointClone.name = pointClone.name.Replace("(Clone)", "");
            pointClone.transform.SetParent(m_pointParent.transform);
            pointClone.transform.position = pos;

            return pointClone;
        }

        //贝塞尔曲线公式:B(t)=P0*(1-t)^3 + 3*P1*t(1-t)^2 + 3*P2*t^2*(1-t) + P3*t^3 ,t属于[0,1].
        Vector3 CalculateCubicBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
        {
            float u = 1 - t;
            float tt = t * t;
            float uu = u * u;
            float uuu = uu * u;
            float ttt = tt * t;

            Vector3 p = uuu * p0;
            p += 3 * uu * t * p1;
            p += 3 * u * tt * p2;
            p += ttt * p3;

            return p;
        }
    }
}

2. CurvePointControl.cs (挂载到路径点与调节点上(两个预制体))

该脚本实现每个路径点对应的两个调节点,以及绘制调节点与路径点之间的切线。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


namespace DM.Editor.View
{
    public class CurvePointControl : MonoBehaviour
    {
        [Header("锁定X轴")]
        public bool m_isLockX = false;
        [Header("锁定Y轴")]
        public bool m_isLockY = true;
        [Header("锁定Z轴")]
        public bool m_isLockZ = false;
       
        [HideInInspector]
        public GameObject m_controlObject;
        [HideInInspector]
        public GameObject m_controlObject2;


        private Vector3 offsetPos1 = Vector3.zero;
        private Vector3 offsetPos2 = Vector3.zero;
        private LineRenderer lineRenderer;
        void Start()
        {
            if (gameObject.tag.Equals("AnchorPoint") && !lineRenderer)
                lineRenderer = gameObject.AddComponent<LineRenderer>();
            if (lineRenderer)
            {
                lineRenderer.sortingOrder = 1;
                lineRenderer.material = new Material(Shader.Find("Particles/Alpha Blended"));
                lineRenderer.startColor = lineRenderer.endColor = Color.yellow;
                lineRenderer.widthMultiplier = 0.03f;
                lineRenderer.positionCount = 0;
            }
        }
        void OnMouseDown()
        {
            if (!gameObject.tag.Equals("AnchorPoint")) return;
            OffsetPos();
        }
        public List<Vector3> OffsetPos()
        {
            List<Vector3> offsetPosList = new List<Vector3>();
            if (m_controlObject)
                offsetPos1 = m_controlObject.transform.position - transform.position;
            if (m_controlObject2)
                offsetPos2 = m_controlObject2.transform.position - transform.position;
            offsetPosList.Add(offsetPos1);
            offsetPosList.Add(offsetPos2);


            return offsetPosList;
        }
        void OnMouseDrag()
        {
            //if (gameObject.tag.Equals("AnchorPoint")) return;
            Vector3 pos0 = Camera.main.WorldToScreenPoint(transform.position);
            Vector3 mousePos = new Vector3(Input.mousePosition.x, Input.mousePosition.y, pos0.z);
            Vector3 mousePosInWorld= Camera.main.ScreenToWorldPoint(mousePos);
            Vector3 thisPos = mousePosInWorld;
            if (m_isLockX)
                thisPos.x = transform.position.x;
            if (m_isLockY)
                thisPos.y = transform.position.y;
            if (m_isLockZ)
                thisPos.z = transform.position.z;
            transform.position = thisPos;
            DMDrawCurve.Instance.UpdateLine(gameObject, offsetPos1, offsetPos2);   
        }      
        private void DrawControlLine()
        {
            if (!gameObject.tag.Equals("AnchorPoint") || (!m_controlObject && !m_controlObject2)) return;
            if (lineRenderer)
            {
                lineRenderer.positionCount = (m_controlObject && m_controlObject2) ? 3 : 2;
                if (m_controlObject && !m_controlObject2)
                {
                    lineRenderer.SetPosition(0, m_controlObject.transform.position);
                    lineRenderer.SetPosition(1, transform.position);
                }
                if (m_controlObject2 && !m_controlObject)
                {
                    lineRenderer.SetPosition(0, transform.position);
                    lineRenderer.SetPosition(1, m_controlObject2.transform.position);
                }
                if (m_controlObject && m_controlObject2)
                {
                    lineRenderer.SetPosition(0, m_controlObject.transform.position);
                    lineRenderer.SetPosition(1, transform.position);
                    lineRenderer.SetPosition(2, m_controlObject2.transform.position);
                }
            }
        }
        void Update()
        {
            DrawControlLine();
        }
    }
}

对应Inspector,如图


3. Test.cs (任意挂载)

该脚本实现Player自动寻路。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DM.Editor.View
{
    public class Test : MonoBehaviour
    {
        public GameObject m_player;
        public List<Vector3> m_pathPoints;
        void Start()
        {
            DMDrawCurve.Instance.Init(m_player);
        }

        IEnumerator Move()
        {
            if (m_pathPoints.Count == 0) yield break;
            int item = 1;
            while (true)
            {
                m_player.transform.LookAt(m_pathPoints[item]);
                m_player.transform.position = Vector3.Lerp(m_pathPoints[item - 1], m_pathPoints[item], 1f);
                item++;
                if (item >= m_pathPoints.Count)
                {
                    item = 1;
                    yield break;
                }
                yield return new WaitForEndOfFrame();
            }
        }
        void Update()
        {
            if (Input.GetKey(KeyCode.LeftControl) && (Input.GetMouseButtonUp(0) || Input.GetMouseButtonUp(1)))
            {
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                RaycastHit hit;
                if (Physics.Raycast(ray, out hit))
                {
                    if (Input.GetMouseButtonUp(0) && hit.collider.tag.Equals("Terrain"))
                    {
                        Vector3 pointPos = new Vector3(hit.point.x, m_player.transform.position.y, hit.point.z);
                        DMDrawCurve.Instance.AddPoint(pointPos);
                    }
                    else if (Input.GetMouseButtonUp(1) && hit.collider.tag.Equals("AnchorPoint"))
                    {
                        DMDrawCurve.Instance.DeletePoint(hit.collider.gameObject);
                    }
                }
            }
            if (Input.GetKeyUp(KeyCode.A))
                m_pathPoints = DMDrawCurve.Instance.HiddenLine(false);
            else if (Input.GetKeyUp(KeyCode.Escape))
            {
                DMDrawCurve.Instance.HiddenLine(true);
                m_pathPoints.Clear();
            }
            if (Input.GetKeyUp(KeyCode.B))
            {
                StartCoroutine(Move());
            }
        }
    }
}

注意:需要添加两个Tag值(Terrain,AnchorPoint),也可动态添加,一个给场景地面,一个给路径点(AnchorPoint),调节点不需要Tag。

开发测试Demo以及.unitypackage的网盘链接如下,需要的自行下载:

链接:https://pan.baidu.com/s/1dwIOxcMB-Lhq_Tlxenb4fQ 密码:7g8y

以上就是近日开发结果,如有不足,请批评指正。如有疑问,请留言,看到自然回复。

如转载,请注明出处:IT_yanghui的博客

没有更多推荐了,返回首页