Uinty实现让物体自定义轨迹,以及按轨迹运动

这个问题被困扰了好久,终于在CSDN找到了答案,下面为内容(原作者在最下方)
当你需要相机镜头根据特定轨迹运动。或者一些AI的特定轨迹运动的时候。就可以用到下面的脚本了
一下方法来自官方案例 直接代码喽。你需要做的就是,复制到你的项目中。拖在脚本上,你就知道他怎么用了。
一共两个脚本,一个是自定义轨迹的,另一个是使对象根据轨迹运动的。

脚本WaypointCircuit 。自定义轨迹。把脚本拖在一个父级上面。然后在子级添加 轨迹对象作为轨迹 顶点。脚本上的按键Assign using all child objects按下之后。就会把子级对象 作为 曲线的每个顶点。然后生成曲线轨迹运动
using System;
using System.Collections;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;

#endif

namespace UnityStandardAssets.Utility
{
public class WaypointCircuit : MonoBehaviour
{
public WaypointList waypointList = new WaypointList();
[SerializeField] private bool smoothRoute = true;
private int numPoints;
private Vector3[] points;
private float[] distances;

public float editorVisualisationSubsteps = 100;
public float Length { get; private set; }

public Transform[] Waypoints
{
    get { return waypointList.items; }
}

//this being here will save GC allocs
private int p0n;
private int p1n;
private int p2n;
private int p3n;

private float i;
private Vector3 P0;
private Vector3 P1;
private Vector3 P2;
private Vector3 P3;

// Use this for initialization
private void Awake()
{
    if (Waypoints.Length > 1)
    {
        CachePositionsAndDistances();
    }
    numPoints = Waypoints.Length;
}


public RoutePoint GetRoutePoint(float dist)
{
    // position and direction
    Vector3 p1 = GetRoutePosition(dist);
    Vector3 p2 = GetRoutePosition(dist + 0.1f);
    Vector3 delta = p2 - p1;
    return new RoutePoint(p1, delta.normalized);
}


public Vector3 GetRoutePosition(float dist)
{
    int point = 0;

    if (Length == 0)
    {
        Length = distances[distances.Length - 1];
    }

    dist = Mathf.Repeat(dist, Length);

    while (distances[point] < dist)
    {
        ++point;
    }


    // get nearest two points, ensuring points wrap-around start & end of circuit
    p1n = ((point - 1) + numPoints)%numPoints;
    p2n = point;

    // found point numbers, now find interpolation value between the two middle points

    i = Mathf.InverseLerp(distances[p1n], distances[p2n], dist);

    if (smoothRoute)
    {
        // smooth catmull-rom calculation between the two relevant points


        // get indices for the surrounding 2 points, because
        // four points are required by the catmull-rom function
        p0n = ((point - 2) + numPoints)%numPoints;
        p3n = (point + 1)%numPoints;

        // 2nd point may have been the 'last' point - a dupe of the first,
        // (to give a value of max track distance instead of zero)
        // but now it must be wrapped back to zero if that was the case.
        p2n = p2n%numPoints;

        P0 = points[p0n];
        P1 = points[p1n];
        P2 = points[p2n];
        P3 = points[p3n];

        return CatmullRom(P0, P1, P2, P3, i);
    }
    else
    {
        // simple linear lerp between the two points:

        p1n = ((point - 1) + numPoints)%numPoints;
        p2n = point;

        return Vector3.Lerp(points[p1n], points[p2n], i);
    }
}


private Vector3 CatmullRom(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float i)
{
    // comments are no use here... it's the catmull-rom equation.
    // Un-magic this, lord vector!
    return 0.5f*
           ((2*p1) + (-p0 + p2)*i + (2*p0 - 5*p1 + 4*p2 - p3)*i*i +
            (-p0 + 3*p1 - 3*p2 + p3)*i*i*i);
}


private void CachePositionsAndDistances()
{
    // transfer the position of each point and distances between points to arrays for
    // speed of lookup at runtime
    points = new Vector3[Waypoints.Length + 1];
    distances = new float[Waypoints.Length + 1];

    float accumulateDistance = 0;
    for (int i = 0; i < points.Length; ++i)
    {
        var t1 = Waypoints[(i)%Waypoints.Length];
        var t2 = Waypoints[(i + 1)%Waypoints.Length];
        if (t1 != null && t2 != null)
        {
            Vector3 p1 = t1.position;
            Vector3 p2 = t2.position;
            points[i] = Waypoints[i%Waypoints.Length].position;
            distances[i] = accumulateDistance;
            accumulateDistance += (p1 - p2).magnitude;
        }
    }
}


private void OnDrawGizmos()
{
    DrawGizmos(false);
}


private void OnDrawGizmosSelected()
{
    DrawGizmos(true);
}


private void DrawGizmos(bool selected)
{
    waypointList.circuit = this;
    if (Waypoints.Length > 1)
    {
        numPoints = Waypoints.Length;

        CachePositionsAndDistances();
        Length = distances[distances.Length - 1];

        Gizmos.color = selected ? Color.yellow : new Color(1, 1, 0, 0.5f);
        Vector3 prev = Waypoints[0].position;
        if (smoothRoute)
        {
            for (float dist = 0; dist < Length; dist += Length/editorVisualisationSubsteps)
            {
                Vector3 next = GetRoutePosition(dist + 1);
                Gizmos.DrawLine(prev, next);
                prev = next;
            }
            Gizmos.DrawLine(prev, Waypoints[0].position);
        }
        else
        {
            for (int n = 0; n < Waypoints.Length; ++n)
            {
                Vector3 next = Waypoints[(n + 1)%Waypoints.Length].position;
                Gizmos.DrawLine(prev, next);
                prev = next;
            }
        }
    }
}


[Serializable]
public class WaypointList
{
    public WaypointCircuit circuit;
    public Transform[] items = new Transform[0];
}

public struct RoutePoint
{
    public Vector3 position;
    public Vector3 direction;


    public RoutePoint(Vector3 position, Vector3 direction)
    {
        this.position = position;
        this.direction = direction;
    }
}

}
}

namespace UnityStandardAssets.Utility.Inspector
{
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof (WaypointCircuit.WaypointList))]
public class WaypointListDrawer : PropertyDrawer
{
private float lineHeight = 18;
private float spacing = 4;

public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
    EditorGUI.BeginProperty(position, label, property);

    float x = position.x;
    float y = position.y;
    float inspectorWidth = position.width;

    // Draw label


    // Don't make child fields be indented
    var indent = EditorGUI.indentLevel;
    EditorGUI.indentLevel = 0;

    var items = property.FindPropertyRelative("items");
    var titles = new string[] {"Transform", "", "", ""};
    var props = new string[] {"transform", "^", "v", "-"};
    var widths = new float[] {.7f, .1f, .1f, .1f};
    float lineHeight = 18;
    bool changedLength = false;
    if (items.arraySize > 0)
    {
        for (int i = -1; i < items.arraySize; ++i)
        {
            var item = items.GetArrayElementAtIndex(i);

            float rowX = x;
            for (int n = 0; n < props.Length; ++n)
            {
                float w = widths[n]*inspectorWidth;

                // Calculate rects
                Rect rect = new Rect(rowX, y, w, lineHeight);
                rowX += w;

                if (i == -1)
                {
                    EditorGUI.LabelField(rect, titles[n]);
                }
                else
                {
                    if (n == 0)
                    {
                        EditorGUI.ObjectField(rect, item.objectReferenceValue, typeof (Transform), true);
                    }
                    else
                    {
                        if (GUI.Button(rect, props[n]))
                        {
                            switch (props[n])
                            {
                                case "-":
                                    items.DeleteArrayElementAtIndex(i);
                                    items.DeleteArrayElementAtIndex(i);
                                    changedLength = true;
                                    break;
                                case "v":
                                    if (i > 0)
                                    {
                                        items.MoveArrayElement(i, i + 1);
                                    }
                                    break;
                                case "^":
                                    if (i < items.arraySize - 1)
                                    {
                                        items.MoveArrayElement(i, i - 1);
                                    }
                                    break;
                            }
                        }
                    }
                }
            }

            y += lineHeight + spacing;
            if (changedLength)
            {
                break;
            }
        }
    }
    else
    {
        // add button
        var addButtonRect = new Rect((x + position.width) - widths[widths.Length - 1]*inspectorWidth, y,
                                     widths[widths.Length - 1]*inspectorWidth, lineHeight);
        if (GUI.Button(addButtonRect, "+"))
        {
            items.InsertArrayElementAtIndex(items.arraySize);
        }

        y += lineHeight + spacing;
    }

    // add all button
    var addAllButtonRect = new Rect(x, y, inspectorWidth, lineHeight);
    if (GUI.Button(addAllButtonRect, "Assign using all child objects"))
    {
        var circuit = property.FindPropertyRelative("circuit").objectReferenceValue as WaypointCircuit;
        var children = new Transform[circuit.transform.childCount];
        int n = 0;
        foreach (Transform child in circuit.transform)
        {
            children[n++] = child;
        }
        Array.Sort(children, new TransformNameComparer());
        circuit.waypointList.items = new Transform[children.Length];
        for (n = 0; n < children.Length; ++n)
        {
            circuit.waypointList.items[n] = children[n];
        }
    }
    y += lineHeight + spacing;

    // rename all button
    var renameButtonRect = new Rect(x, y, inspectorWidth, lineHeight);
    if (GUI.Button(renameButtonRect, "Auto Rename numerically from this order"))
    {
        var circuit = property.FindPropertyRelative("circuit").objectReferenceValue as WaypointCircuit;
        int n = 0;
        foreach (Transform child in circuit.waypointList.items)
        {
            child.name = "Waypoint " + (n++).ToString("000");
        }
    }
    y += lineHeight + spacing;

    // Set indent back to what it was
    EditorGUI.indentLevel = indent;
    EditorGUI.EndProperty();
}


public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
    SerializedProperty items = property.FindPropertyRelative("items");
    float lineAndSpace = lineHeight + spacing;
    return 40 + (items.arraySize*lineAndSpace) + lineAndSpace;
}


// comparer for check distances in ray cast hits
public class TransformNameComparer : IComparer
{
    public int Compare(object x, object y)
    {
        return ((Transform) x).name.CompareTo(((Transform) y).name);
    }
}

}

endif

}

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385

脚本WaypointProgressTracker 功能是让 Target 对象根据轨迹进行运动。脚本拖放在一个对象上之后,然后,指定 Circuit 为刚才使用的WaypointCircuit。然后在指定 Target 对象。运行游戏就可以运动了。试试其他参数,来调节一下速度与运动方式。
using System;
using UnityEngine;

namespace UnityStandardAssets.Utility
{
public class WaypointProgressTracker : MonoBehaviour
{
// This script can be used with any object that is supposed to follow a
// route marked out by waypoints.

 // This script manages the amount to look ahead along the route,
 // and keeps track of progress and laps.

 [SerializeField] private WaypointCircuit circuit; // A reference to the waypoint-based route we should follow

 [SerializeField] private float lookAheadForTargetOffset = 5;
 // The offset ahead along the route that the we will aim for

 [SerializeField] private float lookAheadForTargetFactor = .1f;
 // A multiplier adding distance ahead along the route to aim for, based on current speed

 [SerializeField] private float lookAheadForSpeedOffset = 10;
 // The offset ahead only the route for speed adjustments (applied as the rotation of the waypoint target transform)

 [SerializeField] private float lookAheadForSpeedFactor = .2f;
 // A multiplier adding distance ahead along the route for speed adjustments

 [SerializeField] private ProgressStyle progressStyle = ProgressStyle.SmoothAlongRoute;
 // whether to update the position smoothly along the route (good for curved paths) or just when we reach each waypoint.

 [SerializeField] private float pointToPointThreshold = 4;
 // proximity to waypoint which must be reached to switch target to next waypoint : only used in PointToPoint mode.

 public enum ProgressStyle
 {
     SmoothAlongRoute,
     PointToPoint,
 }

 // these are public, readable by other objects - i.e. for an AI to know where to head!
 public WaypointCircuit.RoutePoint targetPoint { get; private set; }
 public WaypointCircuit.RoutePoint speedPoint { get; private set; }
 public WaypointCircuit.RoutePoint progressPoint { get; private set; }

 public Transform target;

 private float progressDistance; // The progress round the route, used in smooth mode.
 private int progressNum; // the current waypoint number, used in point-to-point mode.
 private Vector3 lastPosition; // Used to calculate current speed (since we may not have a rigidbody component)
 private float speed; // current speed of this object (calculated from delta since last frame)

 // setup script properties
 private void Start()
 {
     // we use a transform to represent the point to aim for, and the point which
     // is considered for upcoming changes-of-speed. This allows this component
     // to communicate this information to the AI without requiring further dependencies.

     // You can manually create a transform and assign it to this component *and* the AI,
     // then this component will update it, and the AI can read it.
     if (target == null)
     {
         target = new GameObject(name + " Waypoint Target").transform;
     }

     Reset();
 }


 // reset the object to sensible values
 public void Reset()
 {
     progressDistance = 0;
     progressNum = 0;
     if (progressStyle == ProgressStyle.PointToPoint)
     {
         target.position = circuit.Waypoints[progressNum].position;
         target.rotation = circuit.Waypoints[progressNum].rotation;
     }
 }


 private void Update()
 {
     if (progressStyle == ProgressStyle.SmoothAlongRoute)
     {
         // determine the position we should currently be aiming for
         // (this is different to the current progress position, it is a a certain amount ahead along the route)
         // we use lerp as a simple way of smoothing out the speed over time.
         if (Time.deltaTime > 0)
         {
             speed = Mathf.Lerp(speed, (lastPosition - transform.position).magnitude/Time.deltaTime,
                                Time.deltaTime);
         }
         target.position =
             circuit.GetRoutePoint(progressDistance + lookAheadForTargetOffset + lookAheadForTargetFactor*speed)
                    .position;
         target.rotation =
             Quaternion.LookRotation(
                 circuit.GetRoutePoint(progressDistance + lookAheadForSpeedOffset + lookAheadForSpeedFactor*speed)
                        .direction);


         // get our current progress along the route
         progressPoint = circuit.GetRoutePoint(progressDistance);
         Vector3 progressDelta = progressPoint.position - transform.position;
         if (Vector3.Dot(progressDelta, progressPoint.direction) < 0)
         {
             progressDistance += progressDelta.magnitude*0.5f;
         }

         lastPosition = transform.position;
     }
     else
     {
         // point to point mode. Just increase the waypoint if we're close enough:

         Vector3 targetDelta = target.position - transform.position;
         if (targetDelta.magnitude < pointToPointThreshold)
         {
             progressNum = (progressNum + 1)%circuit.Waypoints.Length;
         }


         target.position = circuit.Waypoints[progressNum].position;
         target.rotation = circuit.Waypoints[progressNum].rotation;

         // get our current progress along the route
         progressPoint = circuit.GetRoutePoint(progressDistance);
         Vector3 progressDelta = progressPoint.position - transform.position;
         if (Vector3.Dot(progressDelta, progressPoint.direction) < 0)
         {
             progressDistance += progressDelta.magnitude;
         }
         lastPosition = transform.position;
     }
 }


 private void OnDrawGizmos()
 {
     if (Application.isPlaying)
     {
         Gizmos.color = Color.green;
         Gizmos.DrawLine(transform.position, target.position);
         Gizmos.DrawWireSphere(circuit.GetRoutePosition(progressDistance), 1);
         Gizmos.color = Color.yellow;
         Gizmos.DrawLine(target.position, target.position + target.forward);
     }
 }

}
}


作者:KitStar
来源:CSDN
原文:https://blog.csdn.net/KiTok/article/details/78131552

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容