I'm not sure what's going on but one of my coroutines is completely crashing unity. This is the coroutine in question.
IEnumerator attack()
{
currentState = state.attack;
pathFinder.enabled = false;
Vector3 origin = transform.position;
Vector3 attackPos = target.position;
float percent = 0;
float attackSpeed = 3;
while(percent <= 1)
{
percent += Time.deltaTime * attackSpeed;
float interpolation = (-Mathf.Pow(percent, 2) + percent) * 4;
transform.position = Vector3.Lerp(origin, attackPos, interpolation);
yield return null;
}
currentState = state.chase;
pathFinder.enabled = true;
}
A lot of code here might seem bad. I'm following a tutorial, probably heard that one a lot here, and on their end, it seems to be working fine but right when I press play it freezes unity forcing me to close it. Here's the entire script if that would give a better understanding of what could be going on
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(NavMeshAgent))]
public class Enemy : LivingEntity
{
public enum state { idle, chase, attack};
private state currentState;
private NavMeshAgent pathFinder;
private Transform target;
private float attackThreshhold = .5f;
private float timeBetweenAttacks = 1;
private float attackTimer;
private float myCollisionRadius;
private float targetCollisionRadius;
// Start is called before the first frame update
protected override void Start()
{
base.Start();
currentState = state.chase;
pathFinder = GetComponent<NavMeshAgent>();
target = GameObject.FindGameObjectWithTag("Player").transform;
myCollisionRadius = GetComponent<CapsuleCollider>().radius;
targetCollisionRadius = GetComponent<CapsuleCollider>().radius;
StartCoroutine(updatePath());
}
// Update is called once per frame
void Update()
{
if(Time.time > attackTimer)
{
float sqrDistanceToTarget = (target.position - transform.position).sqrMagnitude;
if (sqrDistanceToTarget < Mathf.Pow(attackThreshhold + myCollisionRadius + targetCollisionRadius, 2)) {
attackTimer = Time.time + timeBetweenAttacks;
StartCoroutine(attack());
}
}
}
IEnumerator attack()
{
currentState = state.attack;
pathFinder.enabled = false;
Vector3 origin = transform.position;
Vector3 attackPos = target.position;
float percent = 0;
float attackSpeed = 3;
while(percent <= 1)
{
percent += Time.deltaTime * attackSpeed;
float interpolation = (-Mathf.Pow(percent, 2) + percent) * 4;
transform.position = Vector3.Lerp(origin, attackPos, interpolation);
yield return null;
}
currentState = state.chase;
pathFinder.enabled = true;
}
IEnumerator updatePath()
{
float refreshRate = .25f;
while(target != null)
{
if(currentState == state.chase)
{
Vector3 dirToTarget = (target.position - transform.position).normalized;
Vector3 targetPosition = target.position - dirToTarget * (myCollisionRadius + targetCollisionRadius/2);
if (!dead) pathFinder.SetDestination(targetPosition);
yield return new WaitForSeconds(refreshRate);
}
}
}
}
Your problem is in the other routine in updatePath.
while(target != null)
{
if(currentState == state.chase)
{
Vector3 dirToTarget = (target.position - transform.position).normalized;
Vector3 targetPosition = target.position - dirToTarget * (myCollisionRadius + targetCollisionRadius/2);
if (!dead) pathFinder.SetDestination(targetPosition);
yield return new WaitForSeconds(refreshRate);
}
}
Problem: In the case your are not in the state state.chase you never yield => you have an infinite while loop!
It should probably rather be
while(target != null)
{
if(currentState == state.chase)
{
var dirToTarget = (target.position - transform.position).normalized;
var targetPosition = target.position - dirToTarget * (myCollisionRadius + targetCollisionRadius / 2);
if (!dead) pathFinder.SetDestination(targetPosition);
yield return new WaitForSeconds(refreshRate);
}
// In other cases wait one frame
else
{
yield return null;
}
}
Because at some point percent goes negative. It also never goes above 1. I would say your math is incorrect. Try removing the negative infront of Mathf.Pow
Math function has nothing to do with crash. Unity crashes because, #SOPMOD is running while loop infinetly. That causing a overflow. Your code tells it should break while loop when percent value is smaller or equal to 1 but you are yielding it null everytime inside the while loop. yield return null tells to compiler, coroutine will run in next frame. So, coroutine start again and percent value is set to 0 again. While loop run again and coroutine again will yield null. Infinite coroutine loop is crashing your app. Move
float percent = 0;
decleration to outside of function. Move it to class level and you will be fine. Because if it is in the class level, it will hold previous value and it will able to reach >1.
Here is How Coroutine yields work
Related
I am working on a flocking system in Unity and am new to c#. I am working with 2 scripts - 1 that manages the overall flock (FlockTest) and the other that manages particle behaviour (FlockParticleBehaviour). I have followed a tutorial which has public boolean values that control seeking behaviour in FlockParticleBehaviour through FlockTest. In play mode, I can toggle these booleans to change the goal seeking behaviour. However, I want to automate this toggling based on time (To add it to an AR session). I have added an if statement to void Update() in the FlockTest and when I hit play, the seekGoal and obedient boolean boxes switch on and off but nothing happens to the particles. I have tried using an invoke method which didn't work(no errors but boxes dont switch on and off) and thought about trying a coRoutine but I am not sure this will work since I don't want to stop and start my script. I am at a loss as to how to get the particles obeying the boolean in update. Am I meant to be referencing in my particle behaviour script's flock function? Very new so would love some help if anyone knows a better way forward!
FlockTest script (contains if statement)
using System.Collections.Generic;
using UnityEngine;
public class FlockTest : MonoBehaviour
{
public GameObject[] particles;
public GameObject particlePrefab;
public int particleCount = 10;
public Vector3 range = new Vector3(5,5,5);
public Vector3 innerLimit = new Vector3(1,1,1);
public bool seekGoal = true;
public bool obedient = true;
public bool willful = false;
[Range(0, 200)]
public int neighbourDistance =50;
[Range(0,2)]
public float maxForce = 0.5f;
[Range(0,5)]
public float maxvelocity = 2.0f;
// Start is called before the first frame update
void Start()
{
int time = (int)Time.time;
particles = new GameObject[particleCount];
for(int i = 0; i < particleCount; i++)
{
Vector3 particlePos = new Vector3(Random.Range(-range.x, range.x), Random.Range(-range.y, range.y), Random.Range(-range.z, range.z));
particles[i] = Instantiate(particlePrefab, this.transform.position + particlePos, Quaternion.identity) as GameObject;
particles[i].GetComponent<FlockParticleBehaviour>().manager = this.gameObject;
}
}
void Update()
// the toggles in the inspector are changing but nothing is happening with the particles.
{
int time = (int)Time.time;
if(time == 3f) {
seekGoal = false;
obedient = false;
willful = true;
}
if(time == 6f)
{
seekGoal = true;
obedient = true;
willful = false;
}
}
}
FlockParticleBehaviour script
using System.Collections.Generic;
using UnityEngine;
public class FlockParticleBehaviour : MonoBehaviour
{
public GameObject manager;
public Vector3 location = Vector3.zero;
public Vector3 velocity;
Vector3 goalPos = Vector3.zero;
Vector3 currentForce; //this is a current force position. pushes particle around by adding all the other forces
// Start is called before the first frame update
void Start()
{
velocity = new Vector3(Random.Range(0.01f, 0.1f), Random.Range(0.01f, 0.1f), Random.Range(0.01f, 0.1f));
location = new Vector3(this.gameObject.transform.position.x, this.gameObject.transform.position.y, this.gameObject.transform.position.z);
}
Vector3 seek(Vector3 target)
{
return(target - location);
}
void applyForce(Vector3 f)
{
Vector3 force = new Vector3(f.x, f.y, f.z);
if(force.magnitude > manager.GetComponent<FlockTest>().maxForce)
{
force = force.normalized;
force *= manager.GetComponent<FlockTest>().maxForce;
}
this.GetComponent<Rigidbody>().AddForce(force);
if(this.GetComponent<Rigidbody>().velocity.magnitude > manager.GetComponent<FlockTest>().maxvelocity)
{
this.GetComponent<Rigidbody>().velocity = this.GetComponent<Rigidbody>().velocity.normalized;
this.GetComponent<Rigidbody>().velocity *= manager.GetComponent<FlockTest>().maxvelocity;
}
Debug.DrawRay(this.transform.position, force, Color.white);
}
Vector3 align()
{
float neighbourdist = manager.GetComponent<FlockTest>().neighbourDistance;
Vector3 sum = Vector3.zero;
int count = 0;
foreach (GameObject other in manager.GetComponent<FlockTest>().particles)
{
if(other == this.gameObject) continue;
float d = Vector3.Distance(location, other.GetComponent<FlockParticleBehaviour>().location);
if (d < neighbourdist) {
sum += other.GetComponent<FlockParticleBehaviour>().velocity;
count++;
}
}
if (count >0)
{
sum /= count;
Vector3 steer = sum - velocity;
return steer;
}
return Vector3.zero;
}
Vector3 cohesion()
{
float neighbourdist = manager.GetComponent<FlockTest>().neighbourDistance;
Vector3 sum = Vector3.zero;
int count = 0;
foreach (GameObject other in manager.GetComponent<FlockTest>().particles)
{
if(other == this.gameObject) continue;
float d = Vector3.Distance(location, other.GetComponent<FlockParticleBehaviour>().location);
if(d < neighbourdist)
{
sum += other.GetComponent<FlockParticleBehaviour>().location;
count++;
}
}
if (count > 0)
{
sum /= count;
return seek(sum);
}
return Vector3.zero;
}
void flock()
{
location = this.transform.position;
velocity = this.GetComponent<Rigidbody>().velocity;
if(manager.GetComponent<FlockTest>().obedient && Random.Range(0,50) <=1)
{
Vector3 ali = align();
Vector3 coh = cohesion();
Vector3 gl;
if(manager.GetComponent<FlockTest>().seekGoal)
{
gl = seek(goalPos);
currentForce = gl + ali +coh;
}
else
currentForce = ali + coh;
currentForce = currentForce.normalized;
}
if(manager.GetComponent<FlockTest>().willful && Random.Range(0,50)<=1)
{
if(Random.Range(0,50)<1) //change direction
currentForce = new Vector3(Random.Range(0.01f, 0.1f), Random.Range(0.01f, 0.1f),Random.Range(0.01f, 0.1f));
}
applyForce(currentForce);
}
// Update is called once per frame
void Update()
{
flock();
goalPos = manager.transform.position;
}
}
Several points:
it is much easier and cleaner to set your flock manager directly as FlockTest, not GameObject to avoid GetComponent calls.
I cannot understand what you want to achieve by calling (int)Time.time and comparing it later with 3 and 6. Time.time returns the number of seconds that passed from the start of the application. So your code in Update method of FlockTest script will not have any chance to be called after the seventh second of your game passed. So obedient will always be true and willful will always be false after the seventh second.
Your Random.Range(0, 50) <= 1 is quite a low chance. It will return an int value from 0 to 49, so it is only a 2% chance that your changes in FlockTest will apply to FlockParticleBehaviour instance. Is it what you wanted to get? You can try to remove this random from the if statement to make this chance 100% and check if this is an issue.
Right now it seems like the chance of changing something is too low to see it in several seconds of the game. As I've said above, after the seventh second your bool values will never change.
I am trying to make a grappling hook more fluent but as of right now it is very choppy and does not have the right feel. It currently makes a line and pulls the player there. I have not tried anything yet because I am not even sure we're to start on fixing this. Here is all the grappling code below. `using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(SFPSC_PlayerMovement))] // PlayerMovement also requires Rigidbody
public class SFPSC_GrapplingHook : MonoBehaviour
{
public bool IsGrappling
{
get { return isGrappling; }
}
private SFPSC_PlayerMovement pm;
private Rigidbody rb;
private int segments;
private void Start()
{
segments = rope.segments;
pm = this.GetComponent<SFPSC_PlayerMovement>();
rb = this.GetComponent<Rigidbody>();
}
private bool isGrappling = false;
private void Update()
{
if (crossHairSpinningPart != null)
{
// we need 2 raycasts bc w/ 1 you can grapple through colliders which isn't good
if (Physics.Raycast(SFPSC_FPSCamera.cam.transform.position, SFPSC_FPSCamera.cam.transform.forward, out hitInfo, maxGrappleDistance, layerMask))
{
hitName = hitInfo.collider.name;
if (Physics.Raycast(SFPSC_FPSCamera.cam.transform.position, SFPSC_FPSCamera.cam.transform.forward, out hitInfo, maxGrappleDistance))
{
if (hitName != hitInfo.collider.name)
goto _else;
crossHairSpinningPart.gameObject.SetActive(true);
crossHairSpinningPart.Rotate(Vector3.forward * crossHairSpinSpeed * Time.deltaTime);
goto _out;
}
}
_else:
crossHairSpinningPart.gameObject.SetActive(false);
}
_out:
if (!isGrappling)
{
if (Input.GetKeyDown(SFPSC_KeyManager.Grapple))
Grapple();
return;
}
else
{
if (!Input.GetKey(SFPSC_KeyManager.Grapple))
UnGrapple();
GrappleUpdate();
return;
}
}
[Header("Properties")]
public float maxGrappleDistance = 100.0f;
public SFPSC_Rope rope;
public float maximumSpeed = 100.0f;
public float deceleration = 2500.0f; // This is how much the player is going to decelerate after stopped grappling
public float deceleratingTime = 1.4f; // This is the time the decelerating is going to act on the player after stopped grappling
public RectTransform crossHairSpinningPart;
public float crossHairSpinSpeed = 200.0f;
public float distanceToStop = 2.0f;
public LayerMask layerMask;
public float grappleCooldown = 1.0f;
private bool isBlocked = false;
private Transform location; // the grappled location
private RaycastHit hitInfo;
private string hitName;
public void Grapple()
{
if (isBlocked)
return;
// we need 2 raycasts bc w/ 1 you can grapple through colliders which isn't good
if (Physics.Raycast(SFPSC_FPSCamera.cam.transform.position, SFPSC_FPSCamera.cam.transform.forward, out hitInfo, maxGrappleDistance, layerMask))
{
hitName = hitInfo.collider.name;
if (Physics.Raycast(SFPSC_FPSCamera.cam.transform.position, SFPSC_FPSCamera.cam.transform.forward, out hitInfo, maxGrappleDistance))
{
if (hitName != hitInfo.collider.name)
return;
// We create a GameObject and we parent it to the grappled object.
// If we don't parent it to the object and the object moves the player is stuck only on one location instead of the moving object.
location = new GameObject().transform;//Instantiate(new GameObject(), hitInfo.point, Quaternion.identity).transform;
location.position = hitInfo.point;
location.parent = hitInfo.collider.transform;
if (decelerateTimer != 0.0f)
StopCoroutine(Decelerate());
pm.DisableMovement();
// Rope attaching
rope.segments = (int)((hitInfo.distance / maxGrappleDistance) * segments);
rope.Grapple(transform.position, hitInfo.point);
rb.useGravity = false;
isGrappling = true;
}
}
}
private Vector3 grappleForce;
public void UnGrapple()
{
if (!isGrappling)
return;
if (location != null)
Destroy(location.gameObject);
if (decelerateTimer == 0.0f)
StartCoroutine(Decelerate());
else
decelerateTimer = 0.0f;
pm.EnableMovement();
// Rope detaching
rope.UnGrapple();
Invoke("UnblockGrapple", grappleCooldown);
rb.useGravity = true;
isGrappling = false;
}
private void UnblockGrapple()
{
isBlocked = false;
}
private float decelerateTimer = 0.0f, max;
private IEnumerator Decelerate()
{
WaitForEndOfFrame wfeof = new WaitForEndOfFrame();
max = deceleratingTime * Mathf.Clamp01(targetDistance / 10.0f) * Mathf.Clamp01(rb.velocity.magnitude / 30.0f);
for (; decelerateTimer < max; decelerateTimer += Time.deltaTime)
{
rb.AddForce(-rb.velocity.normalized * deceleration * (1.0f - decelerateTimer / max) * Mathf.Clamp01(rb.velocity.sqrMagnitude / 400.0f) * Time.deltaTime, ForceMode.Acceleration);
yield return wfeof;
}
decelerateTimer = 0.0f;
}
private Vector3 dir;
private float speed = 0.0f, targetDistance;
private void GrappleUpdate()
{
if (location == null)
return;
targetDistance = Vector3.Distance(transform.position, location.position);
rope.segments = (int)((targetDistance / maxGrappleDistance) * segments);
dir = (location.position - transform.position).normalized;
rb.velocity = Vector3.Lerp(rb.velocity, dir * maximumSpeed * Mathf.Clamp01(targetDistance / (4.0f * distanceToStop)), Time.deltaTime);
// Rope updating
rope.UpdateStart(transform.position);
rope.UpdateGrapple();
}
private Vector3 ClampMag(Vector3 vec, float maxMag)
{
if (vec.sqrMagnitude > maxMag * maxMag)
vec = vec.normalized * maxMag;
return vec;
}
}
`
Try using FixedUpdate instead of Update for physics based work (basically all of your code in Update right now). Update is dependent on your computer's clock speed and refresh rate (more or less), and gets called at fairly irregular intervals, because the next update is called in the next frame, after the present frame has finished processing. FixedUpdate makes it frame-rate independent.
Also, you can cap your framerate using Application.targetFrameRate and cap it to a decent FPS.
You could also multiply your movement with Time.deltaTime for smoother movement, although this is a standard practice and yet debatable for use as a smoothing value.
The main goal is to move the object between the positions with a delay. This is working fine but other things are not working.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class Waypoints : MonoBehaviour
{
public GameObject objectPrefab;
public LineRenderer lineRenderer;
public List<Transform> pointsToMove;
public float speed;
public bool go = false;
private List<GameObject> objectsToMove;
private List<Vector3> positions;
void Start()
{
objectsToMove = new List<GameObject>();
//Spawn objects and start movement
}
private void Update()
{
if (go && lineRenderer.positionCount > 0 && CurvedLineRenderer.linesSet)
{
positions = GetLinePointsInWorldSpace();
foreach(Transform objToMove in pointsToMove)
{
positions.Add(objToMove.position);
}
for (int i = 0; i < 3; i++)
{
objectsToMove.Add(Instantiate(objectPrefab, transform.parent));
StartCoroutine(Move(i));
}
go = false;
}
}
private IEnumerator Move(int i)
{
//Wait interval
yield return new WaitForSeconds(i * 10);
bool stillTraveling = true;
int pointIndex = 0;
float threshold = 0.1f;
while (stillTraveling)
{
//Check if close to point
if (Vector3.Distance(objectsToMove[i].transform.position, positions[pointIndex]) < threshold)
{
//Check if can move next point
if (pointIndex + 1 > positions.Count - 1)
{
stillTraveling = false;
}
//If not stop moving
else
{
pointIndex++;
}
}
//Move towards point
objectsToMove[i].transform.position = Vector3.MoveTowards(objectsToMove[i].transform.position, positions[pointIndex], speed * Time.deltaTime);
yield return null;
}
}
List<Vector3> GetLinePointsInWorldSpace()
{
var pointsToMove = new Vector3[lineRenderer.positionCount];
//Get the positions which are shown in the inspector
lineRenderer.GetPositions(pointsToMove);
//the points returned are in world space
return pointsToMove.ToList();
}
}
I'm updating once the positions in the Update.
I'm using StartCoroutine once in the Update to send the objects to move between the waypoints with a delay.
private void Update()
{
if (go && lineRenderer.positionCount > 0 && CurvedLineRenderer.linesSet)
{
positions = GetLinePointsInWorldSpace();
foreach(Transform objToMove in pointsToMove)
{
positions.Add(objToMove.position);
}
for (int i = 0; i < 3; i++)
{
objectsToMove.Add(Instantiate(objectPrefab, transform.parent));
StartCoroutine(Move(i));
}
go = false;
}
}
It's working by sending the objects to move with delay between the waypoints, but now I have some other problems.
Because I'm using StartCoroutine for the delay and because I'm setting the go flag to false then the speed value have no affect on the moving objects at run time and I'm not sure how to solve it so the speed will have a factor affecting the moving objects.
Another problem or not a problem but not sure how to do it is how to Update in run time the positions List ? Here in the Update I'm updating the List once :
positions = GetLinePointsInWorldSpace();
foreach(Transform objToMove in pointsToMove)
{
positions.Add(objToMove.position);
}
but I want that if the amount of positions in the function GetLinePointsInWorldSpace or/and the amount of pointsToMove have changed to update the positions List at rune time. I could just call this part in the update nonstop but I'm not sure of it will be too expensive ?
Last problem or how to do is how to make the object to move to move between the waypoints non stop over again from the start or moving backward when getting the last waypoint ? Again because using StartCoroutine for the delay everything is happening once.
If you want to move an object from one point to another, you should use Mathf.Sin(). The is using the sine function, and you put in the amount of cycles times Mathf.PI times 2. Here is what you would write:
float period = 2f;
Vector3 a; //the first point the object goes to
Vector3 b; //the second point that the object goes to
void Update()
{
float cycles = period * Time.time * Mathf.PI;
float amplitude = Mathf.Sin(cycles);
Vector3 location = Vector3.Lerp(a, b, amplitude);
Transform.position = location;
}
I then use Vector3.Lerp to interpolate between point a and point b. This isn’t what you were doing, but it is a much simpler way you can do it.
Vector3 a; //the first point the object goes to
Vector3 b; //the second point that the object goes to
void Update()
{
float cycles = period * Time.time;
Vector3 location = Vector3.Lerp(a, b, cycles);
Transform.position = location;
}
This one is for bringing the object just strait to point b and not back.
If you want there to be a delay between when it moves, here is the script:
float period = 2f;
float waitTime;
bool AtoB = true;
Vector3 a; //the first point the object goes to
Vector3 b; //the second point that the object goes to
void Start()
{
StartCoroutine(“Oscillator”);
}
IEnumerator Oscillator
{
float amplitude;
if (AtoB)
{
amplitude += Time.deltaTime * period;
}
else
{
amplitude -= Time.deltaTime * period;
}
amplitude = Mathf.Clamp(amplitude, 0, 1);
Vector3 location = Vector3.Lerp(a, b, amplitude);
Transform.position = location;
if (amplitude == 1)
{
AtoB = false;
yield return new WaitForSeconds(waitTime);
}
if else (amplitude == 0)
{
AtoB = true;
yield return new WaitForSeconds(waitTime);
}
else
{
yield return false;
}
}
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngine.AI;
public class Waypoints : UnityEngine.MonoBehaviour
{
public List<Transform> waypoints = new List<Transform>();
public float movementSpeed = 5.0f;
public float slowdownSpeed = 1f;
public float rotationSpeed = 2.0f;
public float waypointDistance = 0.1f;
public float slowdownDistance = 7f;
public bool moveBackward = false;
public bool moveLoop = false;
public bool includeTransformPosition = false;
private Transform targetWaypoint;
private int targetWaypointIndex = 0;
private int lastWaypointIndex;
private bool includeTransform = true;
private GameObject go;
// Use this for initialization
void Start()
{
go = new GameObject();
go.transform.position = transform.position;
if (moveBackward && waypoints.Count > 2)
{
lastWaypointIndex = 0;
targetWaypoint = waypoints[waypoints.Count - 1];
}
else
{
lastWaypointIndex = waypoints.Count - 1;
targetWaypoint = waypoints[targetWaypointIndex]; //Set the first target waypoint at the start so the enemy starts moving towards a waypoint
}
}
// Update is called once per frame
void Update()
{
if (includeTransformPosition && includeTransform)
{
waypoints.Insert(0,go.transform);
includeTransform = false;
}
else
{
if (includeTransformPosition == false)
{
waypoints.Remove(go.transform);
includeTransform = true;
}
}
float movementStep = movementSpeed * Time.deltaTime;
float rotationStep = rotationSpeed * Time.deltaTime;
Vector3 directionToTarget = targetWaypoint.position - transform.position;
Quaternion rotationToTarget = Quaternion.LookRotation(directionToTarget);
transform.rotation = Quaternion.Slerp(transform.rotation, rotationToTarget, rotationStep);
float distance = Vector3.Distance(transform.position, targetWaypoint.position);
CheckDistanceToWaypoint(distance);
if(slowdownDistance < 7f)
{
movementSpeed -= movementSpeed * Time.deltaTime;
}
transform.position = Vector3.MoveTowards(transform.position, targetWaypoint.position, movementStep);
}
void CheckDistanceToWaypoint(float currentDistance)
{
if (currentDistance <= waypointDistance)
{
targetWaypointIndex++;
UpdateTargetWaypoint();
}
}
void UpdateTargetWaypoint()
{
if (targetWaypointIndex > lastWaypointIndex)
{
targetWaypointIndex = 0;
}
targetWaypoint = waypoints[targetWaypointIndex];
}
}
At this part I'm trying to slowdown the movement speed but it's not changing the speed at all :
if(slowdownDistance < 7f)
{
movementSpeed -= movementSpeed * Time.deltaTime;
}
What I'm trying to do when the transform start to move increase the speed slowly to some constant speed and then when the transform is getting closer to the waypoint then if the distance is less then 7 decrease the speed down to 0 so the object will stop at the waypoint then after X seconds move back the transform to the transform original position(go.transform) with the same increasing decreasing speed movement.
but I can't even make the first simple slowdown.
you set slowdownDistance to 7, i dont see you ever reducing it below 7 but you have if statement that executes only it it is under 7 do you reduce it elsewhere?
You probably planned to compare slowdownDistance to some distance rather than comparing it to its own value.
If you want to slow down the whole scene you could use a:
while(distance < range)
{
Time.timeScale = 1 - (distance / value);
}
with this you can change the value and so when the player come close time will be slown down mostly.
With the same idea you can do:
while(distance < range)
{
speed = speedInitial - (distance / value);
}
If you want to change just the speed of the player
In one of my games, a snake like, I have a bug where the parts are supposed to be 0.125f apart, but instead, the the distance between is the same as the distance between the first and second part.
What I believe to be the relevant code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class SnakeHead : MonoBehaviour
{
public float growTime = 5;
private float moveSpeed;
private Transform origin, prevPart = null;
private List<GameObject> snakeBodyPartsList;
public Mesh mesh;
public Material material;
private GameObject body, sphere;
private RaycastHit hit;
void Start()
{
sphere = GameObject.Find("Sphere");
origin = new GameObject("SnakeHead Origin").transform;
origin.parent = sphere.transform;
transform.parent = origin;
snakeBodyPartsList = new List<GameObject>();
snakeBodyPartsList.Add(gameObject);
StartCoroutine("addBodyPart");
}
void Update()
{
if (Input.GetKey(KeyCode.Escape))
{
Application.Quit();
}
foreach (GameObject part in snakeBodyPartsList)
{
moveSpeed = 30 + (snakeBodyPartsList.Count * 2.5f);
if (part == gameObject)
{
if (Input.GetKey("a") || Input.GetKey("d"))
{
var turnRate = Input.GetKey("a") ? -120 : 120;
part.transform.parent.Rotate(0, 0, turnRate * Time.deltaTime);
}
part.transform.rotation = Quaternion.LookRotation(transform.forward, part.transform.position - sphere.transform.position);
part.transform.parent.rotation = Quaternion.AngleAxis(moveSpeed * Time.deltaTime, Vector3.Cross(part.transform.parent.position - part.transform.position, part.transform.forward)) * part.transform.parent.rotation;
}
else
{
part.transform.rotation = Quaternion.LookRotation(prevPart.transform.position, part.transform.position - sphere.transform.position);
if (prevPart != null && Vector3.Distance(part.transform.position, prevPart.position) > 0.125f)
{
// moveSpeed = moveSpeed + Vector3.Distance(part.transform.position, prevPart.position)* 100;
part.transform.parent.rotation = Quaternion.AngleAxis(moveSpeed * Time.deltaTime, Vector3.Cross(part.transform.parent.position - part.transform.position, -(prevPart.position - part.transform.position).normalized * 0.125f)) * part.transform.parent.rotation;
}
}
returnToSurface(part);
prevPart = part.transform;
}
}
private IEnumerator addBodyPart()
{
yield return new WaitForSeconds(1);
body = createNewGameObject(body, "Body " + snakeBodyPartsList.Count, null, mesh, material, snakeBodyPartsList[snakeBodyPartsList.Count - 1].transform.position, Vector3.one / 10, true, true);
snakeBodyPartsList.Add(body);
yield return new WaitForSeconds(Mathf.Abs(growTime - 1));
StartCoroutine("addBodyPart");
}
public GameObject createNewGameObject(GameObject uGO, string Name, Transform Parent, Mesh Mesh, Material Material, Vector3 Position, Vector3 localScale, bool needsOrigin, bool needscollider)
{
uGO = new GameObject(Name);
if (needsOrigin)
{
origin = new GameObject("BodyPart Origin " + snakeBodyPartsList.Count).transform;
origin.parent = sphere.transform;
uGO.transform.parent = origin;
}
else
{
uGO.transform.parent = Parent;
}
uGO.gameObject.AddComponent<MeshFilter>().mesh = Mesh;
uGO.AddComponent<MeshRenderer>().material = Material;
uGO.transform.position = Position;
uGO.transform.localScale = localScale;
if (needscollider)
{
uGO.AddComponent<BoxCollider>().size = Vector3.one;
uGO.GetComponent<BoxCollider>().isTrigger = true;
}
uGO.transform.forward = transform.forward;
uGO.transform.rotation = transform.rotation;
return uGO;
}
void returnToSurface(GameObject a)
{
if (Vector3.Distance(a.transform.position, sphere.transform.position) > 1.05)
{
while (Vector3.Distance(a.transform.position, sphere.transform.position) >= 1.045)
{
a.transform.position = new Vector3(a.transform.position.x, a.transform.position.y, a.transform.position.z - 0.001f);
}
}
else if (Vector3.Distance(a.transform.position, sphere.transform.position) < 1.04)
{
while (Vector3.Distance(a.transform.position, sphere.transform.position) <= 1.045)
{
a.transform.position = new Vector3(a.transform.position.x, a.transform.position.y, a.transform.position.z + 0.001f);
}
}
}
public List<GameObject> getPartsList()
{
return snakeBodyPartsList;
}
}
The movement of the block is probably badly coded because I am fairly new, any general tips and cleanup is generally welcome also. The blocks are moving around a sphere hence the perhaps odd seeming movement code.
I might be wrong about this, but I don't have enough rep to comment, so I'll post it as a reply for you to try:
Since your issue is only between the head and first body part, I'm imagining your issue is because there's no prevPart set on the first run (the gameObject body part isn't necessarily the first one to be checked in the foreach). You might want to try handling the first if-statement before the foreach, so that everything is guaranteed to be set up properly for the first run. You can keep the gameObject body part out of the list or handle the case with an if (like in this example).
Something like this:
void Update()
{
moveSpeed = 30 + (snakeBodyPartsList.Count * 2.5f);
// First update the head (gameObject)
if (Input.GetKey("a") || Input.GetKey("d"))
{
var turnRate = Input.GetKey("a") ? -120 : 120;
transform.parent.Rotate(0, 0, turnRate * Time.deltaTime);
}
transform.rotation = Quaternion.LookRotation(transform.forward, transform.position - sphere.transform.position);
transform.parent.rotation = Quaternion.AngleAxis(moveSpeed * Time.deltaTime, Vector3.Cross(transform.parent.position - transform.position, transform.forward)) * transform.parent.rotation;
returnToSurface(part);
prevPart = part.transform;
// Then update the body (snakeBodyPartsList)
foreach (GameObject part in snakeBodyPartsList)
{
if (part != gameObject)
{
if (prevPart != null) // prevPart is already used here, so you might as well check it here instead of after the next line
{
part.transform.rotation = Quaternion.LookRotation(prevPart.transform.position, part.transform.position - sphere.transform.position);
if (Vector3.Distance(part.transform.position, prevPart.position) > 0.125f)
{
part.transform.parent.rotation = Quaternion.AngleAxis(moveSpeed * Time.deltaTime, Vector3.Cross(part.transform.parent.position - part.transform.position, -(prevPart.position - part.transform.position).normalized * 0.125f)) * part.transform.parent.rotation;
}
}
else
{
Debug.Log('No prevPart in the snakeBodyPartsList foreach'); // EDIT: Might as well add something here just in case... :)
}
returnToSurface(part);
prevPart = part.transform;
}
}
}
Does that help?
PS: The movement code looks fine to me, but you might want to give your magic numbers variable names for clarity.
So, this was solved by halving the move speed of the head during turning, havnt figure out why but the head moves faster than the body when turning causing it to pull away from the body parts. Thanks #Bruins from solent uni.