How to rotate object through waypoints on a curved path on sphere
Hello, first, escuse me for my poor english. I'm a beginner for 6 months on unity, and I'm trying to build a waypoint system for a spherical board game seems like a planet-shaped map. So, a vehicle moves along a path by choosing the nodes having the lowest costs to arrive at a destination of my choice. everything works great, until I try to have a curve between each node crossed by my vehicle. Of course, the curves must follow closely to the shape of the planet, and having searched for 3 weeks, it is in total despair that I come to ask for a solution.
I chose to create my pathinding, so that the next node is added to the vehicle's destination when it collides with the collider of a node until it finishes the path. Everything happens on a sphere that can sometimes roll on itself or that the player can drag on an axis with a mouse.
I had then chosen to answer my curve problem with rotatearound because after searching, it was the simplest solution and I did not know how to use slerp properly. On a test on a sphere, the function works well, but when I assemble it to other scripts, nothing works. I first thought it was a problem in my hierarchy but it does not seem to come from there. So, now I'm out of inspiration and I turn to the community
I give you the different scripts, the waypointScript is placed on a node, the pathScript on a empty regrouping the nodes, the objectToPathScript on the vehicle, the pathController in a empty containing the path and the vehicle. Everything is placed in a sphere representing the planet where we travel, itself in a empty with a controller to manipulate the planets system.
The first script is the WaypointScript
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WaypointScript : MonoBehaviour
{
[Space(5)]
[Header("To Slide")]
[Space(5)]
public List<GameObject> nodesVoisins;
[Space(10)]
[Header("Options")]
[Space(5)]
public bool explored = false; /// if true, node is operational
public bool visibleNode = false;
[Space(10)]
[Header("Information")]
[Space(5)]
public bool startNode = false;
public bool endNode = false;
[Space(5)]
public float value;
public Vector3 coordonné;
public Vector3 vecSuivant;
private Color couleur;
void Update()
{
coordonné = gameObject.transform.localPosition;
if (visibleNode == false)
{
explored = false;
}
if (explored == true)
{
if (startNode == true)
{
GetComponentInParent<PathScript>().PositionInitial = gameObject;
}
if (endNode == true)
{
GetComponentInParent<PathScript>().destination = gameObject;
GetComponentInParent<PathScript>().endSelected = true;
vecSuivant = coordonné;
value = 0;
}
if (endNode == false)
{
if (GetComponentInParent<PathScript>().endSelected == true)
{
float distTotal = Vector3.Distance(GetComponentInParent<PathScript>().PositionInitial.transform.localPosition, GetComponentInParent<PathScript>().destination.transform.localPosition);
float distwaypointToEnd = Vector3.Distance(GetComponentInParent<PathScript>().destination.transform.localPosition, coordonné);
value = Mathf.Abs((distTotal * distwaypointToEnd) / 2);
if (nodesVoisins.Count == 1)
{
foreach (var voisin in nodesVoisins)
{
vecSuivant = voisin.GetComponent<WaypointScript>().coordonné;
}
}
if (nodesVoisins.Count == 2)
{
foreach (var voisin in nodesVoisins)
{
if (nodesVoisins[0].GetComponent<WaypointScript>().value == Mathf.Min(nodesVoisins[0].GetComponent<WaypointScript>().value, nodesVoisins[1].GetComponent<WaypointScript>().value))
{
vecSuivant = nodesVoisins[0].GetComponent<WaypointScript>().coordonné;
}
if (nodesVoisins[1].GetComponent<WaypointScript>().value == Mathf.Min(nodesVoisins[0].GetComponent<WaypointScript>().value, nodesVoisins[1].GetComponent<WaypointScript>().value))
{
vecSuivant = nodesVoisins[1].GetComponent<WaypointScript>().coordonné;
}
}
}
if (nodesVoisins.Count == 3)
{
foreach (var voisin in nodesVoisins)
{
if (nodesVoisins[0].GetComponent<WaypointScript>().value == Mathf.Min(nodesVoisins[0].GetComponent<WaypointScript>().value, nodesVoisins[1].GetComponent<WaypointScript>().value, nodesVoisins[2].GetComponent<WaypointScript>().value))
{
vecSuivant = nodesVoisins[0].GetComponent<WaypointScript>().coordonné;
}
if (nodesVoisins[1].GetComponent<WaypointScript>().value == Mathf.Min(nodesVoisins[0].GetComponent<WaypointScript>().value, nodesVoisins[1].GetComponent<WaypointScript>().value, nodesVoisins[2].GetComponent<WaypointScript>().value))
{
vecSuivant = nodesVoisins[1].GetComponent<WaypointScript>().coordonné;
}
if (nodesVoisins[2].GetComponent<WaypointScript>().value == Mathf.Min(nodesVoisins[0].GetComponent<WaypointScript>().value, nodesVoisins[1].GetComponent<WaypointScript>().value, nodesVoisins[2].GetComponent<WaypointScript>().value))
{
vecSuivant = nodesVoisins[2].GetComponent<WaypointScript>().coordonné;
}
}
}
if (nodesVoisins.Count == 4)
{
foreach (var voisin in nodesVoisins)
{
if (nodesVoisins[0].GetComponent<WaypointScript>().value == Mathf.Min(nodesVoisins[0].GetComponent<WaypointScript>().value, nodesVoisins[1].GetComponent<WaypointScript>().value, nodesVoisins[2].GetComponent<WaypointScript>().value, nodesVoisins[3].GetComponent<WaypointScript>().value))
{
vecSuivant = nodesVoisins[0].GetComponent<WaypointScript>().coordonné;
}
if (nodesVoisins[1].GetComponent<WaypointScript>().value == Mathf.Min(nodesVoisins[0].GetComponent<WaypointScript>().value, nodesVoisins[1].GetComponent<WaypointScript>().value, nodesVoisins[2].GetComponent<WaypointScript>().value, nodesVoisins[3].GetComponent<WaypointScript>().value))
{
vecSuivant = nodesVoisins[1].GetComponent<WaypointScript>().coordonné;
}
if (nodesVoisins[2].GetComponent<WaypointScript>().value == Mathf.Min(nodesVoisins[0].GetComponent<WaypointScript>().value, nodesVoisins[1].GetComponent<WaypointScript>().value, nodesVoisins[2].GetComponent<WaypointScript>().value, nodesVoisins[3].GetComponent<WaypointScript>().value))
{
vecSuivant = nodesVoisins[2].GetComponent<WaypointScript>().coordonné;
}
if (nodesVoisins[3].GetComponent<WaypointScript>().value == Mathf.Min(nodesVoisins[0].GetComponent<WaypointScript>().value, nodesVoisins[1].GetComponent<WaypointScript>().value, nodesVoisins[2].GetComponent<WaypointScript>().value, nodesVoisins[3].GetComponent<WaypointScript>().value))
{
vecSuivant = nodesVoisins[3].GetComponent<WaypointScript>().coordonné;
}
}
}
}
}
}
if (explored == false)
{
value = float.MaxValue;
}
}
private void OnDrawGizmos()
{
if (nodesVoisins == null)
{
return;
}
Gizmos.color = couleur;
couleur = Color.blue;
Gizmos.DrawWireCube(transform.position, new Vector3(1, 1, 1));
foreach (var nodeConnect in nodesVoisins)
{
if (nodeConnect != null)
Gizmos.DrawLine(transform.position, nodeConnect.transform.position);
}
if (startNode == true)
{
couleur = Color.green;
}
if (endNode == true)
{
couleur = Color.red;
}
}
}
The second is the PathScript
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PathScript : MonoBehaviour
{
[Space(5)]
[Header("To Slide")]
[Space(5)]
public GameObject objectToMove;
public List<GameObject> nodes; // place the nodes here
[Space(10)]
[Header("Information")]
[Space(5)]
public bool endSelected = false;
[Space(5)]
public GameObject PositionInitial;
public GameObject destination;
private void Start()
{
if (PositionInitial == null)
{
PositionInitial = nodes[0];
nodes[0].GetComponent<WaypointScript>().startNode = true;
}
foreach (var node in nodes)
{
if (node.GetComponent<WaypointScript>().coordonné == objectToMove.transform.localPosition)
{
objectToMove.GetComponent<ObjectToPathScript>().onPath = true;
}
}
}
}
the third one is the ObjectToPathScript It's here that my problem arises, I think.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectToPathScript : MonoBehaviour
{
[Space(5)]
[Header("Move")]
[Space(5)]
public bool moveToPath = false; // move function
[Space(10)]
[Header("Options")]
[Space(5)]
public float speed;
public float speedFacingDirection;
public float alphaLevel = .5f;
[Space(10)]
[Header("To Slide")]
[Space(5)]
public GameObject objectToMove;
public GameObject pathToFollow;
public GameObject planete;
[Space(10)]
[Header("Information")]
[Space(5)]
public bool onPath = false;
[Space(5)]
public Vector3 targetVector;
public Vector3 vehiculePosition;
[Space(5)]
public bool inverseAlpha = false;
public bool decreaseAlpha = false;
private int randomNode;
public float angleToMoveAroundSphere; // variables and objects to turnAround
public Transform planetoide; //
public Transform currentPosition; //
public Transform nextPosition; //
public GameObject nextNode; //
void Update()
{
if (planete != null) //
{
////////////////////////////////// here, just to be able to manipulate my positions effectively
foreach (var node in pathToFollow.GetComponent<PathScript>().nodes)
{
if (targetVector == node.GetComponent<WaypointScript>().coordonné)
{
nextNode = node;
}
}
planetoide = planete.transform;
nextPosition = nextNode.transform;
currentPosition = transform;
////////////////////////////////////////////////////////////////////
}
if (inverseAlpha == true)
{
if (alphaLevel <= 1f)
{
alphaLevel += .05f;
}
}
if (decreaseAlpha == true)
{
if (alphaLevel >= 0)
{
alphaLevel -= .05f;
}
}
objectToMove.GetComponent<Renderer>().material.color = new Color(objectToMove.GetComponent<Renderer>().material.color.r, objectToMove.GetComponent<Renderer>().material.color.g, objectToMove.GetComponent<Renderer>().material.color.b, alphaLevel);
if (onPath != true)
{
vehiculePosition = this.transform.localPosition;
targetVector = pathToFollow.GetComponent<PathScript>().PositionInitial.GetComponent<WaypointScript>().coordonné;
inverseAlpha = false;
decreaseAlpha = true;
}
if (onPath != true && pathToFollow.GetComponent<PathScript>().endSelected == true && moveToPath == true)
{
inverseAlpha = true;
decreaseAlpha = false;
if (planete != null)
{
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
}
transform.localPosition = Vector3.MoveTowards(transform.localPosition, targetVector, speed * Time.deltaTime);
Vector3 vector = targetVector - transform.localPosition;
transform.rotation = Quaternion.LookRotation(Vector3.RotateTowards(transform.forward, vector, speedFacingDirection * Time.deltaTime, 0.0f));
}
if (transform.localPosition == targetVector)
{
onPath = true;
}
if (transform.localPosition != targetVector && moveToPath == true && onPath == true)
{
StartCoroutine(MicroTimerBeforeMove());
}
}
void OnTriggerStay(Collider other)
{
if (!other.tag.Equals("moonTarget") == true) // sphere target tag is call moontarget
{
if (planete != null)
{
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
}
if (transform.localPosition == other.transform.localPosition)
{
targetVector = other.GetComponent<WaypointScript>().vecSuivant;
if (other.GetComponent<WaypointScript>().endNode == true && moveToPath == true)
{
if (gameObject.GetComponentInParent<PathController>().pathMode == true && gameObject.GetComponentInParent<PathController>().loopMode == false && gameObject.GetComponentInParent<PathController>().randomLoop == false)
{
moveToPath = false;
inverseAlpha = false;
decreaseAlpha = true;
foreach (var node in pathToFollow.GetComponent<PathScript>().nodes)
{
node.GetComponent<WaypointScript>().startNode = false;
node.GetComponent<WaypointScript>().endNode = false;
other.GetComponent<WaypointScript>().startNode = true;
}
pathToFollow.GetComponent<PathScript>().endSelected = false;
}
if (gameObject.GetComponentInParent<PathController>().loopMode == true && gameObject.GetComponentInParent<PathController>().pathMode == false && gameObject.GetComponentInParent<PathController>().randomLoop == false)
{
foreach (var node in pathToFollow.GetComponent<PathScript>().nodes)
{
if (node.GetComponent<WaypointScript>().startNode == true)
{
node.GetComponent<WaypointScript>().startNode = false;
node.GetComponent<WaypointScript>().endNode = true;
}
other.GetComponent<WaypointScript>().endNode = false;
other.GetComponent<WaypointScript>().startNode = true;
}
}
if (gameObject.GetComponentInParent<PathController>().randomLoop == true && gameObject.GetComponentInParent<PathController>().loopMode == false && gameObject.GetComponentInParent<PathController>().pathMode == false)
{
foreach (var node in pathToFollow.GetComponent<PathScript>().nodes)
{
node.GetComponent<WaypointScript>().startNode = false;
node.GetComponent<WaypointScript>().endNode = false;
}
other.GetComponent<WaypointScript>().startNode = true;
randomNode = Random.Range(0, pathToFollow.GetComponent<PathScript>().nodes.Count);
pathToFollow.GetComponent<PathScript>().nodes[randomNode].GetComponent<WaypointScript>().endNode = true;
}
}
}
}
}
IEnumerator MicroTimerBeforeMove()
{
inverseAlpha = true;
decreaseAlpha = false;
yield return new WaitForFixedUpdate();
//////////////////////////////////////////////// my big trouble begin here
if (planete != null)
{
//foreach(var node in pathToFollow.GetComponent<PathScript>().nodes)
//{
// if (targetVector == node.GetComponent<WaypointScript>().coordonné)
//// {
// nextNode = node;
// }
//}
// planetoide = planete.transform;
//nextPosition = nextNode.transform.localPosition;
//currentPosition = transform.localPosition;
// here, it's the heart of the probleme
Vector3 targetDir = (nextPosition.localPosition + planetoide.position) - currentPosition.localPosition;
angleToMoveAroundSphere = Vector3.SignedAngle(currentPosition.localPosition, nextPosition.localPosition, planetoide.up);
transform.RotateAround(targetDir, planetoide.up, (angleToMoveAroundSphere * speed) * Time.deltaTime);
//Vector3 targetDir = (targetVector + planetoide.position) - vehiculePosition;
//angleToMoveAroundSphere = Vector3.SignedAngle(vehiculePosition, targetVector, planetoide.up);
//transform.RotateAround(targetDir, planetoide.up, (angleToMoveAroundSphere * speed) * Time.deltaTime);
Debug.Log("....angle..." + angleToMoveAroundSphere + "..direction...." + targetDir + "....current position..." + currentPosition.position + "...current local..." + currentPosition.localPosition + "....next position..." + nextPosition.position + "...next local..." + nextPosition.localPosition);
}
////////////////////////////////////////////////////////////////////
if (planete == null)
{
transform.localPosition = Vector3.MoveTowards(transform.localPosition, targetVector, speed * Time.deltaTime);
Vector3 vector = targetVector - transform.localPosition;
transform.rotation = Quaternion.LookRotation(Vector3.RotateTowards(transform.forward, vector, speedFacingDirection * Time.deltaTime, 0.0f));
}
}
}
thanks for help, do not hesitate to explain to me how to optimize my code, but I look for simple and effective solutions to learn and understand. I did not study math or computer, so be indulgent and don't tell me to use itween, I want to do everything from a to z. I would be happy if beginners like me use my scripts. My wayoints system works well without the curves options, and I have suffered enough in tutorials to know how it feels to try to understand how a dijkstra or an A* works. Here, i think it's a simple alternative to complex pathfinding. I hope I can find the same thing for the curves
Have a good day.