思路:
1.分为领导者,追随者,追随点。
2.先创建领导者。根据剩下人数的多少,再在左右创建追随者和追随点,成v字排列。追随点是领导者的子物体
3.领导者按照路径点移动
4.追随者追的追随点移动,如果距离过远,要提高速度

创建队形脚本

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

public class SanJiao : MonoBehaviour {

public GameObject m_tmp;
public List<Transform> m_listPos;
public Transform m_pointPar;
public int m_tankCnt;
public Dictionary<int, List<Vector3>> m_dicPos = new Dictionary<int, List<Vector3>>();
public List<Transform> m_listTank;
// Use this for initialization
void Start () {
m_dicPos[0] = new List<Vector3>();
for (int i = 0; i < m_pointPar.childCount; i++)
{
m_listPos.Add(m_pointPar.GetChild(i));


}

for (int i = 0; i < m_listPos.Count; i++)
{
m_dicPos[0].Add(m_listPos[i].transform.position);


GameObject objPos = Instantiate(m_tmp);
objPos.transform.position = m_listPos[i].transform.position;

if (i < m_listPos.Count - 1)
{

Vector3 dir = m_listPos[i + 1].transform.position - m_listPos[i].transform.position;
objPos.transform.rotation = Quaternion.LookRotation(dir.normalized);
}

if (i == m_listPos.Count - 1)
{
Vector3 dir = m_listPos[i].transform.position - m_listPos[i-1].transform.position;
objPos.transform.rotation = Quaternion.LookRotation(dir.normalized);
}

if (m_tankCnt > 1)
{
Sector(objPos.transform, objPos.transform.position, 60, 5, m_tankCnt-1);
}
}

CreateTank();
}

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

}

void CreateTank()
{
Transform par = null;
Quaternion qua = Quaternion.identity;
for (int i = 0; i < m_tankCnt; i++)
{
GameObject tank;
//m_listTank.Add(tank.transform);

if (i == 0)
{
tank = Instantiate(m_tmp);
tank.transform.position = m_dicPos[i][0];
tank.transform.GetComponent<FollowPath>().SetLeader(m_dicPos[i]);

Vector3 dir = m_dicPos[i][1] - m_dicPos[i][0];
tank.transform.rotation = Quaternion.LookRotation(dir.normalized); // 一开始就要确定leader 的方向
qua = tank.transform.rotation;
par = tank.transform;
}
else {
GameObject follow = new GameObject();
follow.transform.position = m_dicPos[i][0];
follow.transform.SetParent(par, true);

tank = Instantiate(m_tmp);
tank.transform.position = m_dicPos[i][0];
tank.transform.GetComponent<FollowPath>().SetFollow(follow.transform);
tank.transform.rotation = qua;
//tank.transform.SetParent(par, false);
}
}
}


public void Sector(Transform t, Vector3 center, float angle, float radius,int cnt)
{
Vector3 forward = -t.forward;

int sign = -1;
for (int i = 0; i < cnt; i++)
{

if (i % 2 == 0)
{
sign = -1;
}
else
{
sign = 1;
}

Vector3 pos = Quaternion.Euler(0f, sign * angle / 2, 0f) * forward * radius * (1 + i / 2) + center;

//GameObject obj = Instantiate(m_tmp);
//obj.transform.position = pos;

if (m_dicPos.ContainsKey(i+1) == false)
{
m_dicPos[1 + i] = new List<Vector3>();
}
m_dicPos[1 + i].Add(pos);
}



}


void OnDrawGizmos()
{
Gizmos.color = Color.white;

for (int i = 0; i < m_pointPar.transform.childCount; i++)
{
Vector3 position = m_pointPar.transform.GetChild(i).position;
if (i > 0)
{
Vector3 previous = m_pointPar.transform.GetChild(i-1).position;
Gizmos.DrawLine(previous, position);
Gizmos.DrawWireSphere(position, 0.3f);
}
}

}

}

追随脚本

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

public class FollowPath : MonoBehaviour {

public int CurrentWayPointID = 0;
public float Speed;//移动速度
public float reachDistance = 0.01f;//里路径点的最大范围

public List<Vector3> m_listPos = new List<Vector3>();
public bool m_isCanFollow = false;

public bool m_isLeader = false;

public Transform m_followTrans;
public Transform m_leaderTrans;
void Start()
{

}

public void SetLeader(List<Vector3> listPos)
{
m_isLeader = true;
m_listPos = listPos;
m_isCanFollow = true;
CurrentWayPointID = 0;

}

public void SetFollow(Transform trans)
{
m_isLeader = false;
m_followTrans = trans;

m_isCanFollow = true;
CurrentWayPointID = 0;

}

void Update()
{
if (!m_isCanFollow)
return;

if (m_isLeader == true)
{
if (CurrentWayPointID >= m_listPos.Count)
{
return;
}
float distance = Vector3.Distance(m_listPos[CurrentWayPointID], transform.position);
transform.position = Vector3.MoveTowards(transform.position, m_listPos[CurrentWayPointID], Time.deltaTime * Speed);

Vector3 LookDirection = m_listPos[CurrentWayPointID] - transform.position;
Quaternion targetlook = Quaternion.LookRotation(LookDirection);

if (distance <= reachDistance)
{
CurrentWayPointID++;
if (CurrentWayPointID < m_listPos.Count)
{
playerRotate(m_listPos[CurrentWayPointID], gameObject/*transform.Find("Tank").gameObject*/);
}

}
}

else if (m_isLeader == false)
{
float distance = Vector3.Distance(m_followTrans.position, transform.position);

if (distance > 3)
{
transform.position = Vector3.MoveTowards(transform.position, m_followTrans.position, Time.deltaTime * Speed * 1.25f);
}
else {
transform.position = Vector3.MoveTowards(transform.position, m_followTrans.position, Time.deltaTime * Speed );
}

//Vector3 LookDirection = m_listPos[CurrentWayPointID] - transform.position;
//Quaternion targetlook = Quaternion.LookRotation(LookDirection);
//playerRotate(m_followTrans.position, gameObject/*transform.Find("Tank").gameObject*/);
transform.LookAt(m_followTrans);
}
}


void playerRotate(Vector3 target, GameObject rot)
{
//向量的叉乘
if (Vector3.Cross(rot.transform.forward, target - this.transform.position).y > 0)
{
//计算角度
rot.transform.Rotate(Vector3.up * Vector3.Angle(rot.transform.forward, target - this.transform.position));
}
else
{
rot.transform.Rotate(Vector3.down * Vector3.Angle(rot.transform.forward, target - this.transform.position));
}
}

}

效果

unity3d:保持V字型队形,按路径点移动_V字型


unity3d:保持V字型队形,按路径点移动_V字型_02


unity3d:保持V字型队形,按路径点移动_i++_03