Foreach loop stuck. Trying to get smooth movement.
I'm stuck trying to get this foreach loop to work with smooth movement. I've used Vector3.lerp and can't really figure this out. Any help is greatly appropriated and if you could explain to me how you came to solving this issue. It would really be a big help. Here is the cold below.. Thank you!
public class EnemyMovement : MonoBehaviour {
public float transition = .1f;
// Use this for initialization
void Start ()
{
PathFinder pathfinder = FindObjectOfType<PathFinder>();
var path = pathfinder.GetPath();
StartCoroutine(FollowPath(path));
}
IEnumerator FollowPath(List<Waypoint> path)
{
print("Starting patrol...");
foreach (Waypoint waypoint in path)
{
Vector3 top = new Vector3(0, 5, 0);
transform.position = Vector3.Lerp(waypoint.transform.position, waypoint.transform.position, transition * Time.deltaTime);
yield return new WaitForSeconds(transition);
}
print("Ending patrol");
}
// Update is called once per frame
void Update () {
}
}
public class Enemy$$anonymous$$ovement : $$anonymous$$onoBehaviour {
//float transition = 1.5f;
bool hasTarget = true;
float moveStep = 1f;
// Use this for initialization
void Start ()
{
PathFinder pathfinder = FindObjectOfType<PathFinder>();
var path = pathfinder.GetPath();
StartCoroutine(FollowPath(path));
}
// Update is called once per frame
void Update()
{
if (!hasTarget)
{ return; }
// transform.position = Vector3.Lerp(startPosition, targetPosition, moveStep);
moveStep += Time.time;
}
IEnumerator FollowPath(List<Waypoint> path)
{
print("Starting patrol...");
foreach (Waypoint waypoint in path)
{
// Vector3 top = new Vector3(0, 5, 0);
//transform.position = Vector3.Lerp(waypoint.transform.position, transition * Time.deltaTime);
//var startRotation = transform.rotation;
/* moveStep is used to control the interpolation */
//var targetRotation = Quaternion.LookRotation((waypoint.transform.position - transform.position).normalized, Vector3.up);
//transform.rotation = Quaternion.Slerp(startRotation, targetRotation, moveStep * 2);
var startPosition = transform.position;
var targetPosition = waypoint.transform.position;
print(waypoint.transform.position);
moveStep = 0.0f;
// transform.position = Vector3.$$anonymous$$oveTowards(transform.position, waypoint.transform.position, transition * Time.deltaTime);
//transform.position = waypoint.transform.position + top;
yield return new WaitForSeconds(1f);
}
print("Ending patrol");
}
}
working on it right now. Little sloppy... kinda bouncing ideas....
Answer by JVene · Sep 19, 2018 at 03:54 AM
The following is pseudo code, intended as a suggestion. It is not tested, not compiled, and is only intended to illustrate a plan for the answer to your question with our commentary exchanges in mind. It is not a complete class. Interpret for your circumstance and naming conventions.
public class EnemyMovement : MonoBehaviour
{
Path path; // the path container returned by PathFinder.GetPath()
bool isFollowingPath = false; // bool indicating path is still in progress
int curPathPoint = 0; // the current position in the waypoint list ( path )
Waypoint curWp; // the current Waypoint in path
Waypoint nextWp; // the next Waypoint in path (after current)
float pathTween = 0; // the 'tween' value for lerp
float pathSpeed = 0.1f; // the 'speed' of the tween, or motion between waypoints
void Start()
{
PathFinder p = FindObjectOfType<PathFinder>();
// note, path is now a member
path = p.GetPath();
StartFollowingPath();
}
void StartFollowingPath()
{
// if there's a path, initialize path finding values
if ( path )
{
curPathPoint = 0;
SetPathPosition( 0 );
isFollowingPath = true;
}
}
void SetPathPosition( int n )
{
curWp = path[ curPathPoint ];
nextWp = path[ curPathPoint + 1 ];
}
bool IncPathPosition()
{
// increment to the next waypoint, return false
// if the end has been reached
++curPathPoint;
if ( curPathPoint > ( waypoint.Count - 2 ) )
{
return false;
}
SetPathPosition( curPathPoint );
return true;
}
void Update()
{
// no longer a coroutine, step through the
// 'tween' animation between Waypoints
if ( isFollowingPath ) StepFollowPath();
}
void StepFollowPath()
{
// pathTween moves from 0.0f to 1.0f, such that when
// 1.0f is reached, the current waypoint should be
// incremented
// increment the pathTween according to pathSpeed
// for current time slice
pathTween += ( pathSpeed * Time.deltaTime );
if ( pathTween >= 1.0f )
{
// if tween >= 1.0f, the waypoint has been passed,
// move to the next waypoint pair
if ( IncPathPosition() == false )
{
// when increment returns false, the endpoint was reached
// stop following path
isFollowingPath = false;
return;
}
// at this point, the pathTween is > 1.0f, and the remainder
// is retained, because it is that portion of the next
// waypoint pair time has already passed by
pathTween -= 1.0f;
}
// pathTween has been updated, even if that means moving to the
// next waypoint pair
// calculate lerp between current waypoint and next waypoint
// according to the pathTween ratio
transform.position = Vector3.lerp( curWp.transform.position,
nextWp.transform.position,
pathTween );
}
}
The idea begins by eliminating the coroutine. It is getting in the way. Instead, the "loop" is emulated by curPathPoint; it sequences through the path list (I assume that's a List). Start, here, is your example start but where path is now a member of the class.
Focus first on Update(). Note that is checks if the path is being followed, and calls for a step to be followed through the path. When the path end is reached, isFollowingPath will be set false, so update will stop calling for path animation.
Next, focus on StepFollowPath. It increments one step through the path, with the key feature being that the steps you must animate are not from one waypoint to the next, but lerps between the current waypoint and the next waypoint. This is how jerkiness is eliminated, by animating the transition between two points. pathTween is the ratio between the current and next waypoint, calculated according to pathSpeed for each slice of time through the animation.
When pathTween reaches (or exceeds) 1.0f, the lerp has reached the next waypoint, so then and only then is it appropriate to 'loop' to the next waypoint (IncPathPosition). Until pathTween reaches 1.0f , it is somewhere, according to time slice and speed, between 0.0f and 1.0f, indicating that another tiny step between the current waypoint and the next must be incrementally advanced (that's the purpose of lerp in the first place).
Note that the lerp is calculated as a 'tween' between the current waypoint and the next waypoint. That is the animation of small slices of time through the list of waypoints, smoothly.
This represents a plan to implement what I think you've described. Let me know if I misunderstood. I doubt this code is ready to paste and use, but read it and interpret it to merge into your next code experiment.
Answer by tjtony1323 · Sep 13, 2018 at 06:32 PM
Err i see where i started wrong... I'm using start instead of update... I can hopefully take it from here.. lol sorry for the post.
@tjtony1323, There's more to it, but the lerp is using the same value for start and end (or from and to), which can't do anything. However, lerp's require a value to transit between 0 and 1, a ratio (a percentage divided by 100). I see nothing here to do that. The third issue is how the loop knows to wait until the lerp's ratio reaches it's goal (100%, or 1.0) before moving on to the next waypoint. I had started on an answer to all of this, but I'll wait to see if you actually need it before proceeding. What I see is that this is an example of when a coroutine is probably not the best fit, and may be confusing the use case.
I had to work late today but I worked on it a little on my break. I got it moving and used a variable to store the transition time. I attached it to the waitafewseconds too. I ran out of time but it started moving.... A bit jittery but it was a start. I also saw that it spawned on the wrong area lol. I'll post again tomorrow when I actually have more than 30 $$anonymous$$s to work on it. Thank you again.
I expected it to be jittery. You actually needed an interior loop for time between waypoints upon which to animate the accumulation of time, which then concludes to allow the outer loop to increment to the next waypoint. I would otherwise do this without a coroutine, but that merely reflects my preference.
So I've been trying to get this right but no matter what i get jitter... I'm pretty prideful and been banging my head against the keyboard for days now... I think i might need a point in the right direction or just a solution with an explanation. I really appropriate the help. I think this would be easier to do without the Coroutine but hey i already set it up this way...
I've been at this for decades, and I understand your two points, about knowing your stuff and having self confidence but then hitting a wall on some problem. It is quite common, so don't think it's just you or this one thing. If you have some update to your code, post that. I'm just about to have some time to look on this and one other problem on this board, perhaps an hour or so.
public class Enemy$$anonymous$$ovement : $$anonymous$$onoBehaviour {
//float transition = 1.5f;
//float moveStep = 1f;
// private Vector3 velocity = Vector3.zero;
private Vector3 startPosition;
private Vector3 targetPosition;
private float distance = 10f;
private float lerpTime = 5f;
private float currentLerpTime;
bool hasTarget = true;
// Use this for initialization
void Start ()
{
// transform.position = Vector3.Lerp(startPosition, targetPosition, moveStep);
PathFinder pathfinder = FindObjectOfType<PathFinder>();
var path = pathfinder.GetPath();
StartCoroutine(FollowPath(path));
}
// Update is called once per frame
void Update()
{
if (!hasTarget)
{ return; }
}
IEnumerator FollowPath(List<Waypoint> path)
{
print("Starting patrol...");
foreach (Waypoint waypoint in path)
{
// Vector3 top = new Vector3(0, 5, 0);
//transform.position = Vector3.Lerp(waypoint.transform.position, transition * Time.deltaTime);
//var startRotation = transform.rotation;
/* moveStep is used to control the interpolation */
//var targetRotation = Quaternion.LookRotation((waypoint.transform.position - transform.position).normalized, Vector3.up);
//transform.rotation = Quaternion.Slerp(startRotation, targetRotation, moveStep * 2);
Vector3 startPosition = transform.position;
Vector3 targetPosition = waypoint.transform.position * distance;
print(waypoint.transform.position);
//float firstPoint = 0.1f;
currentLerpTime += Time.deltaTime;
if(currentLerpTime >= lerpTime)
{
currentLerpTime = lerpTime;
}
float perc = currentLerpTime / lerpTime;
transform.position = Vector3.Lerp(startPosition, targetPosition, perc);
//moveStep = 0.0f;
// transform.position = Vector3.Lerp(startPosition, targetPosition, $$anonymous$$athf.Lerp($$anonymous$$imum, maximum, firstPoint * Time.deltaTime));
//firstPoint += 10f * Time.deltaTime;
//transform.position = waypoint.transform.position + top;
yield return new WaitForSeconds(currentLerpTime);
}
print("Ending patrol");
}
Ignore all the blocked out comments... i've been at it.
public class Enemy$$anonymous$$ovement : $$anonymous$$onoBehaviour {
private float transition = 1f; [SerializeField] float speed = 3f; //float moveStep = 1f; // private Vector3 velocity = Vector3.zero;
// Use this for initialization
void Start ()
{
// transform.position = Vector3.Lerp(startPosition, targetPosition, moveStep);
PathFinder pathfinder = FindObjectOfType<PathFinder>();
var path = pathfinder.GetPath();
StartCoroutine(FollowPath(path));
}
// Update is called once per frame
void Update()
{
}
IEnumerator FollowPath(List<Waypoint> path)
{
print("Starting patrol...");
foreach (Waypoint waypoint in path)
{
// Vector3 top = new Vector3(0, 5, 0);
//transform.position = Vector3.Lerp(waypoint.transform.position, transition * Time.deltaTime);
//var startRotation = transform.rotation;
/* moveStep is used to control the interpolation */
//var targetRotation = Quaternion.LookRotation((waypoint.transform.position - transform.position).normalized, Vector3.up);
//transform.rotation = Quaternion.Slerp(startRotation, targetRotation, moveStep * 2);
while(Vector3.Distance(transform.position, waypoint.transform.position) > .02f)
{
transform.position = Vector3.Lerp(transform.position, waypoint.transform.position, transition * Time.deltaTime * speed);
yield return null;
}
//moveStep = 0.0f;
// transform.position = Vector3.Lerp(startPosition, targetPosition, $$anonymous$$athf.Lerp($$anonymous$$imum, maximum, firstPoint * Time.deltaTime));
//firstPoint += 10f * Time.deltaTime;
//transform.position = waypoint.transform.position + top;
yield return new WaitForSeconds(1f);
}
print("Ending patrol");
}
}
Got it... Ignore comments on there... It needs a little fixing and it might not be the best way but i'm okay with this till next project. You might get 3 messages at the same time since it seems they have to look over the post before it being posted. But yes... this part is done and damn did it take a long time. Thank you for the motivation
If I've been instrumental in the result, would you $$anonymous$$d accepting the answer below?
Your answer
Follow this Question
Related Questions
Implementing a "flyer" game in with the old CardBoard Google sdk 2 Answers
Grid Movement with Smooth Movement 0 Answers
InvalidOperationException: Collection was modified; enumeration operation may not execute 1 Answer
How do I change my NavMesh Agent from one NavMesh to another? 0 Answers
How can I smooth out 2D object movement? 0 Answers