RotateTo is call from the Update. I want it to rotate on the Y only.
now it seems like it's rotating on all axis X,Y,Z
private void RotateTo()
{
var distance = Vector3.Distance(capsule.position, curvedLinePoints[rotationIndex].transform.position);
if(distance < 0.1f)
{
rotationIndex++;
}
// Determine which direction to rotate towards
Vector3 targetDirection = curvedLinePoints[rotationIndex].transform.position -capsule.position;
// The step size is equal to speed times frame time.
float singleStep = rotationSpeed * Time.deltaTime;
// Rotate the forward vector towards the target direction by one step
Vector3 newDirection = Vector3.RotateTowards(capsule.forward, targetDirection, singleStep, 0.0f);
// Calculate a rotation a step closer to the target and applies rotation to this object
capsule.rotation = Quaternion.LookRotation(newDirection);
}
This is the full script.
The transform is moving along g waypoints there are more than 4000 waypoints the capsule should only rotate looking each time to the next curvedLinePoint from the List curvedLinePoints.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class WaypointsFollower : MonoBehaviour
{
public float speed;
public Waypoints waypoints;
public Transform capsule;
public bool go;
public bool goForward;
public float rotationSpeed;
private int index = 0;
private int rotationIndex = 0;
private int counter = 0;
private int c = 0;
private List<GameObject> curvedLinePoints = new List<GameObject>();
public int numofposbetweenpoints;
private bool getonce;
private bool getBackwardIndexOnce = true;
private void Start()
{
waypoints = GameObject.Find("Waypoints").GetComponent<Waypoints>();
curvedLinePoints = GameObject.FindGameObjectsWithTag("Curved Line Point").ToList();
if(waypoints.moveInReverse == false)
{
goForward = true;
}
else
{
goForward = false;
}
if(goForward)
{
index = 0;
}
}
private void Update()
{
if (getonce == false)
{
numofposbetweenpoints = curvedLinePoints.Count;
getonce = true;
}
if (go == true && waypoints.lineRendererPositions.Count > 0)
{
if(goForward == false && getBackwardIndexOnce)
{
index = waypoints.lineRendererPositions.Count - 1;
getBackwardIndexOnce = false;
}
RotateTo();
Move();
}
}
private void Move()
{
Vector3 newPos = transform.position;
float distanceToTravel = speed * Time.deltaTime;
bool stillTraveling = true;
while (stillTraveling)
{
Vector3 oldPos = newPos;
// error exception out of bound on line 55 to check !!!!!
newPos = Vector3.MoveTowards(oldPos, waypoints.lineRendererPositions[index], distanceToTravel);
distanceToTravel -= Vector3.Distance(newPos, oldPos);
if (newPos == waypoints.lineRendererPositions[index]) // Vector3 comparison is approximate so this is ok
{
// when you hit a waypoint:
if (goForward)
{
bool atLastOne = index >= waypoints.lineRendererPositions.Count - 1;
if (!atLastOne)
{
index++;
counter++;
if (counter == numofposbetweenpoints)
{
c++;
counter = 0;
}
if (c == curvedLinePoints.Count - 1)
{
c = 0;
}
}
else { index--; goForward = false; }
}
else
{ // going backwards:
bool atFirstOne = index <= 0;
if (!atFirstOne)
{
index--;
counter++;
if (counter == numofposbetweenpoints)
{
c++;
counter = 0;
}
if (c == curvedLinePoints.Count - 1)
{
c = 0;
}
}
else { index++; goForward = true; }
}
}
else
{
stillTraveling = false;
}
}
transform.position = newPos;
}
private void RotateTo()
{
var distance = Vector3.Distance(capsule.position, curvedLinePoints[rotationIndex].transform.position);
if(distance < 0.1f)
{
rotationIndex++;
}
// Determine which direction to rotate towards
Vector3 targetDirection = curvedLinePoints[rotationIndex].transform.position -capsule.position;
// The step size is equal to speed times frame time.
float singleStep = rotationSpeed * Time.deltaTime;
// Rotate the forward vector towards the target direction by one step
Vector3 newDirection = Vector3.RotateTowards(capsule.forward, targetDirection, singleStep, 0.0f);
// Calculate a rotation a step closer to the target and applies rotation to this object
capsule.rotation = Quaternion.LookRotation(newDirection);
}
}
This is a screenshot of the hierarchy the platform is the moving transform in the script and the capsule is the capsule that should only rotate facing each time the next curvedLinePoint. There are over 4000 waypoints but only 10 curvedLinePoints. a bit confusing. The capsule should rotate facing each time the next curvedLinePoint while the transform is moving along the waypoints.
Screenshot
I tried this now :
private void RotateTo()
{
var distance = Vector3.Distance(capsule.position, curvedLinePoints[rotationIndex].transform.position);
if(distance < 0.1f)
{
rotationIndex++;
}
var lookPos = curvedLinePoints[rotationIndex].transform.position - capsule.position;
lookPos.y = 0;
var rotation = Quaternion.LookRotation(lookPos);
capsule.rotation = Quaternion.Slerp(capsule.rotation, rotation, Time.deltaTime * rotationSpeed);
}
and it's rotating bu when I'm looking on the capsule I don't see any of the axis pointing the next point for example in this screenshot the blue is facing to the right the green up the red more ore less down while the point it's moving to is on the left cube so the capsule rotates but why none of the axis is pointing at its direction?
Rotation
I also added eyes to the capsule on the blue axis thinking the blue axis is forward but the capsule is rotating the eyes are never facing the next target.
This screenshot is just to show the eyes on the blue axis direction.
Eyes
Related
The goal in the end is to rotate the object when he start moving so the object will facing towards the target he is moving to and also that the laser that coming out from the object eye will be facing the same target.
The object that i want to rotate is at first at position 0,0,0 because the object is a child. the player is holding the object by hand. Then the object is getting throw with animation towards a target.
The object is moving to the target but now i want the object to rotate also smooth slowly to the target in this case i set the rotation duration 1 second. and i see that the object rotation is changing but not facing the target.
This screenshot show the object while it's still in the player hand at position 0,0,0
Then the object get throw and start moving to the target at this time the object rotation change :
The object that throw have a child transform the eye and from the eye a laser beam should be coming out and then i want to make that the object will rotate towards the target so the laser from the eye will be facing the target.
I marked the eye with a small red circle :
In this screenshot i marked the target far away, the laser in green is not facing the target and also the eye is not facing the target. the laser should come out the eye and the whole object should facing the target so the laser also should be facing the target with the eye.
This screenshot is of the object before running the game the eye is facing forwards with the object. this is just to show that the eye is aligned with the object facing forward.
This script is attached to my player and make the object to be thrown to the target and also should make the object to be rotating towards the target :
The throw part that make the object move to the target is working fine but the rotating part is not :
private IEnumerator AnimateRotationTowards(Quaternion transformRot, Quaternion targetRot, float dur)
{
float t = 0f;
while (t < dur)
{
transformRot = Quaternion.Slerp(transformRot, targetRot, t / dur);
yield return null;
t += Time.deltaTime;
}
transformRot = targetRot;
}
Also i see that making transformRot = targetRot; for some reason there is a small message say unnecessary assignment not sure why.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ThrowObject : MonoBehaviour
{
public Transform objectToThrow;
public Transform target;
public Transform objectToThrowParent;
public float throwingSpeed;
public float waitAtTargetTime;
public float distanceToStopFromTarget;
public bool go = false;
public AnimationCurve animationCurve;
private Animator anim;
private bool startThrowAnimationOnce = true;
private bool reParent = false;
private bool startMovingBack = false;
private void Start()
{
anim = GetComponent<Animator>();
}
private void Update()
{
if (anim != null && startThrowAnimationOnce)
{
anim.SetTrigger("Throw");
startThrowAnimationOnce = false;
}
if (go)
{
objectToThrow.parent = null;
StartCoroutine(Throw());
StartCoroutine(AnimateRotationTowards(objectToThrow.rotation, target.rotation, 1f));
go = false;
}
if (reParent)
{
objectToThrow.position = objectToThrowParent.position;
}
if (startMovingBack)
{
if (Vector3.Distance(objectToThrow.position, objectToThrowParent.position) >= 0.1f)
{
float step = 5 * Time.deltaTime;
objectToThrow.position = Vector3.MoveTowards(objectToThrow.position, objectToThrowParent.position, step);
}
else
{
objectToThrow.position = objectToThrowParent.position;
objectToThrow.parent = objectToThrowParent;
objectToThrow.localPosition = new Vector3(0, 0, 0);
}
}
}
public void ThrowEvent()
{
go = true;
}
IEnumerator Throw()
{
while(Vector3.Distance(objectToThrow.position, target.position) >= distanceToStopFromTarget)
{
objectToThrow.position = Vector3.MoveTowards(
objectToThrow.position,
target.position,
throwingSpeed * Time.deltaTime
);
yield return null;
}
yield return new WaitForSeconds(waitAtTargetTime);
startMovingBack = true;
}
private IEnumerator AnimateRotationTowards(Quaternion transformRot, Quaternion targetRot, float dur)
{
float t = 0f;
while (t < dur)
{
transformRot = Quaternion.Slerp(transformRot, targetRot, t / dur);
yield return null;
t += Time.deltaTime;
}
transformRot = targetRot;
}
}
This script is attached to the object eye and fire the laser :
The target in this script is the same target as in the ThrowObject script :
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System;
using UnityEngine;
public class Hovl_DemoLasers : MonoBehaviour
{
public List<Transform> targets;
public GameObject FirePoint;
public Camera Cam;
public float MaxLength;
public GameObject[] Prefabs;
private Ray RayMouse;
private Vector3 direction;
private Quaternion rotation;
[Header("GUI")]
private float windowDpi;
private int Prefab;
private GameObject Instance;
private Hovl_Laser LaserScript;
private Hovl_Laser2 LaserScript2;
private bool rotateMouse = true;
private bool startLaser = true;
private float buttonSaver = 0f;
private Hovl_LaserDemo hovl_laserDemo;
private float maxDistance = 0;
private int closestIndex = 0;
void Start()
{
if (Screen.dpi < 1) windowDpi = 1;
if (Screen.dpi < 200) windowDpi = 1;
else windowDpi = Screen.dpi / 200f;
Counter(0);
}
void Update()
{
//Enable lazer
if (Input.GetMouseButtonDown(0))
{
Destroy(Instance);
Instance = Instantiate(Prefabs[Prefab], FirePoint.transform.position, FirePoint.transform.rotation);
Instance.transform.parent = transform;
LaserScript = Instance.GetComponent<Hovl_Laser>();
LaserScript2 = Instance.GetComponent<Hovl_Laser2>();
rotateMouse = true;
}
if (Input.GetMouseButtonDown(1))
{
rotateMouse = false;
}
if ((Input.GetKey(KeyCode.A) || Input.GetAxis("Horizontal") < 0) && buttonSaver >= 0.4f)// left button
{
buttonSaver = 0f;
Counter(-1);
}
if ((Input.GetKey(KeyCode.D) || Input.GetAxis("Horizontal") > 0) && buttonSaver >= 0.4f)// right button
{
buttonSaver = 0f;
Counter(+1);
}
buttonSaver += Time.deltaTime;
if (startLaser)
{
rotateMouse = false;
Destroy(Instance);
Instance = Instantiate(Prefabs[Prefab], FirePoint.transform.position, FirePoint.transform.rotation);
Instance.transform.parent = transform;
LaserScript = Instance.GetComponent<Hovl_Laser>();
LaserScript2 = Instance.GetComponent<Hovl_Laser2>();
hovl_laserDemo = Instance.GetComponent<Hovl_LaserDemo>();
startLaser = false;
}
if (targets != null)
{
maxDistance = Mathf.Infinity;
for (int i = 0; i < targets.Count; i++)
{
float distance = Vector3.Distance(gameObject.transform.position, targets[i].position);
if (distance < maxDistance)
{
maxDistance = distance;
closestIndex = i;
}
}
if (hovl_laserDemo != null)
{
float distance = Vector3.Distance(gameObject.transform.position, targets[closestIndex].position);
MaxLength = distance;
hovl_laserDemo.MaxLength = distance;
}
RaycastHit hit;
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hit, MaxLength))
{
RotateToMouseDirection(gameObject, targets[closestIndex].position);
}
else
{
RotateToMouseDirection(gameObject, targets[closestIndex].position);
}
}
if (Cam != null && rotateMouse)
{
RaycastHit hit;
var mousePos = Input.mousePosition;
RayMouse = Cam.ScreenPointToRay(mousePos);
if (Physics.Raycast(RayMouse.origin, RayMouse.direction, out hit, MaxLength))
{
RotateToMouseDirection(gameObject, hit.point);
}
else
{
var pos = RayMouse.GetPoint(MaxLength);
RotateToMouseDirection(gameObject, pos);
}
}
else
{
Debug.Log("No camera");
}
}
void OnGUI()
{
GUI.Label(new Rect(10 * windowDpi, 5 * windowDpi, 400 * windowDpi, 20 * windowDpi), "Use the keyboard buttons A/<- and D/-> to change lazers!");
GUI.Label(new Rect(10 * windowDpi, 20 * windowDpi, 400 * windowDpi, 20 * windowDpi), "Use left mouse button for shooting!");
}
void Counter(int count)
{
Prefab += count;
if (Prefab > Prefabs.Length - 1)
{
Prefab = 0;
}
else if (Prefab < 0)
{
Prefab = Prefabs.Length - 1;
}
}
void RotateToMouseDirection(GameObject obj, Vector3 destination)
{
direction = destination - obj.transform.position;
rotation = Quaternion.LookRotation(direction);
obj.transform.localRotation = Quaternion.Lerp(obj.transform.rotation, rotation, 1);
}
}
This is one situation where it is important to understand the difference between passing by value, or passing by reference. In your AnimateRotationTowards function, all of the parameters to that function are passed by value. Changes to those rotations will affect the local values within the function scope, but those are not the same values that get used in the scene.
What you probably want instead is a reference to some Transform object. If you assign a new rotation to that Transform, it will use that rotation in the scene.
It looks like this script component is attached to the GameObject that will be rotating. Is that right? If yes, you can set a new rotation by assigning a value to transform.rotation:
private IEnumerator AnimateRotationTowards(Quaternion transformRot, Quaternion targetRot, float dur)
{
float t = 0f;
while (t < dur)
{
transform.rotation = Quaternion.Slerp(transformRot, targetRot, t / dur);
yield return null;
t += Time.deltaTime;
}
transform.rotation = targetRot;
}
If you need to assign a rotation to some other GameObject, you can pass in a reference to that GameObject or its Transform and use that to assign the rotation.
For example there are 5 target and they are moving randomly around the turret.
Now i'm using only one target targets[0] and the turret is rotating facing the target and shoot laser to it.
Now i want to make it with multiple targets and that the turret will choose each time the closet target and will shoot to it the laser.
I changed this part added a for loop over the targets :
for (int i = 0; i < targets.Count; i++)
but i'm still using only one target, targets[0] i'm not sure how to add the distance part and the closet target choosing.
I tried this solution but now the turret(transform) is not rotating at all towards the selected target. for some reason this two lines return null on the closestTarget :
RotateToMouseDirection(gameObject, closestTarget.position);
The script with the changes :
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System;
using UnityEngine;
public class Hovl_DemoLasers : MonoBehaviour
{
public List<Transform> targets;
public GameObject FirePoint;
public Camera Cam;
public float MaxLength;
public GameObject[] Prefabs;
private Ray RayMouse;
private Vector3 direction;
private Quaternion rotation;
[Header("GUI")]
private float windowDpi;
private int Prefab;
private GameObject Instance;
private Hovl_Laser LaserScript;
private Hovl_Laser2 LaserScript2;
private bool rotateMouse = true;
private bool startLaser = true;
private float buttonSaver = 0f;
private Hovl_LaserDemo hovl_laserDemo;
private float maxDistance = 0;
private float distance;
private Transform closestTarget;
void Start ()
{
if (Screen.dpi < 1) windowDpi = 1;
if (Screen.dpi < 200) windowDpi = 1;
else windowDpi = Screen.dpi / 200f;
Counter(0);
}
void Update()
{
//Enable lazer
if (Input.GetMouseButtonDown(0))
{
Destroy(Instance);
Instance = Instantiate(Prefabs[Prefab], FirePoint.transform.position, FirePoint.transform.rotation);
Instance.transform.parent = transform;
LaserScript = Instance.GetComponent<Hovl_Laser>();
LaserScript2 = Instance.GetComponent<Hovl_Laser2>();
rotateMouse = true;
}
if (Input.GetMouseButtonDown(1))
{
rotateMouse = false;
}
//Disable lazer prefab
if (Input.GetMouseButtonUp(0))
{
/*if (LaserScript) LaserScript.DisablePrepare();
if (LaserScript2) LaserScript2.DisablePrepare();
Destroy(Instance,1);*/
}
if ((Input.GetKey(KeyCode.A) || Input.GetAxis("Horizontal") < 0) && buttonSaver >= 0.4f)// left button
{
buttonSaver = 0f;
Counter(-1);
}
if ((Input.GetKey(KeyCode.D) || Input.GetAxis("Horizontal") > 0) && buttonSaver >= 0.4f)// right button
{
buttonSaver = 0f;
Counter(+1);
}
buttonSaver += Time.deltaTime;
if (startLaser)
{
rotateMouse = false;
Destroy(Instance);
Instance = Instantiate(Prefabs[Prefab], FirePoint.transform.position, FirePoint.transform.rotation);
Instance.transform.parent = transform;
LaserScript = Instance.GetComponent<Hovl_Laser>();
LaserScript2 = Instance.GetComponent<Hovl_Laser2>();
hovl_laserDemo = Instance.GetComponent<Hovl_LaserDemo>();
startLaser = false;
}
if (targets != null)
{
for (int i = 0; i < targets.Count; i++)
{
distance = Vector3.Distance(transform.position, targets[i].position);
if (distance < maxDistance)
{
maxDistance = distance;
closestTarget = targets[i];
}
}
if (hovl_laserDemo != null)
{
MaxLength = distance;
hovl_laserDemo.MaxLength = distance;
}
if (Cam != null)
{
RaycastHit hit;
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hit, MaxLength))
{
RotateToMouseDirection(gameObject, closestTarget.position);
}
else
{
RotateToMouseDirection(gameObject, closestTarget.position);
}
}
}
if (Cam != null && rotateMouse)
{
RaycastHit hit;
var mousePos = Input.mousePosition;
RayMouse = Cam.ScreenPointToRay(mousePos);
if (Physics.Raycast(RayMouse.origin, RayMouse.direction, out hit, MaxLength))
{
RotateToMouseDirection(gameObject, hit.point);
}
else
{
var pos = RayMouse.GetPoint(MaxLength);
RotateToMouseDirection(gameObject, pos);
}
}
else
{
Debug.Log("No camera");
}
}
void OnGUI()
{
GUI.Label(new Rect(10 * windowDpi, 5 * windowDpi, 400 * windowDpi, 20 * windowDpi), "Use the keyboard buttons A/<- and D/-> to change lazers!");
GUI.Label(new Rect(10 * windowDpi, 20 * windowDpi, 400 * windowDpi, 20 * windowDpi), "Use left mouse button for shooting!");
}
void Counter(int count)
{
Prefab += count;
if (Prefab > Prefabs.Length - 1)
{
Prefab = 0;
}
else if (Prefab < 0)
{
Prefab = Prefabs.Length - 1;
}
}
void RotateToMouseDirection (GameObject obj, Vector3 destination)
{
direction = destination - obj.transform.position;
rotation = Quaternion.LookRotation(direction);
obj.transform.localRotation = Quaternion.Lerp(obj.transform.rotation, rotation, 1);
}
}
The old original script before the changes :
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System;
using UnityEngine;
public class DemoLasers : MonoBehaviour
{
public List<Transform> targets;
public GameObject FirePoint;
public Camera Cam;
public float MaxLength;
public GameObject[] Prefabs;
private Ray RayMouse;
private Vector3 direction;
private Quaternion rotation;
[Header("GUI")]
private float windowDpi;
private int Prefab;
private GameObject Instance;
private Hovl_Laser LaserScript;
private Hovl_Laser2 LaserScript2;
private bool rotateMouse = true;
private bool startLaser = true;
private float buttonSaver = 0f;
private Hovl_LaserDemo hovl_laserDemo;
void Start ()
{
if (Screen.dpi < 1) windowDpi = 1;
if (Screen.dpi < 200) windowDpi = 1;
else windowDpi = Screen.dpi / 200f;
Counter(0);
}
void Update()
{
//Enable lazer
if (Input.GetMouseButtonDown(0))
{
Destroy(Instance);
Instance = Instantiate(Prefabs[Prefab], FirePoint.transform.position, FirePoint.transform.rotation);
Instance.transform.parent = transform;
LaserScript = Instance.GetComponent<Hovl_Laser>();
LaserScript2 = Instance.GetComponent<Hovl_Laser2>();
rotateMouse = true;
}
if (Input.GetMouseButtonDown(1))
{
rotateMouse = false;
}
if ((Input.GetKey(KeyCode.A) || Input.GetAxis("Horizontal") < 0) && buttonSaver >= 0.4f)// left button
{
buttonSaver = 0f;
Counter(-1);
}
if ((Input.GetKey(KeyCode.D) || Input.GetAxis("Horizontal") > 0) && buttonSaver >= 0.4f)// right button
{
buttonSaver = 0f;
Counter(+1);
}
buttonSaver += Time.deltaTime;
if (startLaser)
{
rotateMouse = false;
Destroy(Instance);
Instance = Instantiate(Prefabs[Prefab], FirePoint.transform.position, FirePoint.transform.rotation);
Instance.transform.parent = transform;
LaserScript = Instance.GetComponent<Hovl_Laser>();
LaserScript2 = Instance.GetComponent<Hovl_Laser2>();
hovl_laserDemo = Instance.GetComponent<Hovl_LaserDemo>();
startLaser = false;
}
if (targets != null)
{
for (int i = 0; i < targets.Count; i++)
{
if (hovl_laserDemo != null)
{
float distance = Vector3.Distance(gameObject.transform.position, targets[0].position);
MaxLength = distance;
hovl_laserDemo.MaxLength = distance;
}
if (Cam != null)
{
RaycastHit hit;
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hit, MaxLength))
{
RotateToMouseDirection(gameObject, targets[0].position);
}
else
{
RotateToMouseDirection(gameObject, targets[0].position);
}
}
}
}
if (Cam != null && rotateMouse)
{
RaycastHit hit;
var mousePos = Input.mousePosition;
RayMouse = Cam.ScreenPointToRay(mousePos);
if (Physics.Raycast(RayMouse.origin, RayMouse.direction, out hit, MaxLength))
{
RotateToMouseDirection(gameObject, hit.point);
}
else
{
var pos = RayMouse.GetPoint(MaxLength);
RotateToMouseDirection(gameObject, pos);
}
}
else
{
Debug.Log("No camera");
}
}
void OnGUI()
{
GUI.Label(new Rect(10 * windowDpi, 5 * windowDpi, 400 * windowDpi, 20 * windowDpi), "Use the keyboard buttons A/<- and D/-> to change lazers!");
GUI.Label(new Rect(10 * windowDpi, 20 * windowDpi, 400 * windowDpi, 20 * windowDpi), "Use left mouse button for shooting!");
}
void Counter(int count)
{
Prefab += count;
if (Prefab > Prefabs.Length - 1)
{
Prefab = 0;
}
else if (Prefab < 0)
{
Prefab = Prefabs.Length - 1;
}
}
void RotateToMouseDirection (GameObject obj, Vector3 destination)
{
direction = destination - obj.transform.position;
rotation = Quaternion.LookRotation(direction);
obj.transform.localRotation = Quaternion.Lerp(obj.transform.rotation, rotation, 1);
}
}
Have 2 Variables in a Loop.
one is the index of the current closest target in you List,
and the other one the distance
int closestIndex = 0;
float maxDistance = 0;
for (int i = 0; i < targets.Count; i++)
{
//Here we calculate the distance of the current pos
//You can take any Formula e.g. the Manhattan Formula
float distance = Vector3.Distance(turretPositionOrSth, targets[i].position);
//if the distance is shorter than the current max Distance
if (distance < maxDistance)
{
maxDistance = distance;
closestIndex = i;
}
}
//Now you have your Index in the list to the closest Target,which you can use
//you can use now for example targets[closestIndex] for aiming or so
Also you don't need your already written loop anymore
Here's essentially the same answer as tthe one #cpaech gave, but there was a couple issues.
int closestIndex = 0;
float minDistance = float.MaxValue;
for (int i = 0; i < targets.Count; i++)
{
//Here we calculate the distance of the current pos
//You can take any Formula e.g. the Manhattan Formula
float distance = Vector3.Distance(turretPositionOrSth, targets[i].position);
//if the distance is shorter than the current max Distance
if (distance < maxDistance)
{
maxDistance = distance;
closestIndex = i;
}
}
It could also be helpful to have an attribute for the closest target and have
if (distance < maxDistance)
{
maxDistance = distance;
closestTarget = targets[i];
}
It looks like you have two requirements -- to find not just the closest enemy, but the closest enemy with direct line of sight to the turret?
You could sort the list of enemies by distance to the turret and iterate over it from closest to furthest until a valid target is found (requiring fewer raycasts might provide a small performance advantage if there were a massive number of enemies), or you could just try the raycast on every enemy, but here's a compromise that only tries the raycast if the enemy is closer than any previous enemy tested.
Transform target = null;
float lowestDistance = float.MaxValue;
if( cam != null && hovl_laserDemo != null && targets != null )
{
for( int i = 0; i < targets.Count; i++ )
{
float distance = Vector3.Distance(gameObject.transform.position, targets[i].position);
if( distance < lowestDistance )
{
RaycastHit hit;
if( Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hit, distance)
&& hit.transform == targets[i] )
{
// If our raycast successfully reached the intended target without being blocked by anything else
lowestDistance = distance;
target = targets[i];
}
}
}
}
if( target != null )
{
MaxLength = lowestDistance;
hovl_laserDemo.MaxLength = lowestDistance;
RotateToMouseDirection(gameObject, target.position);
}
else
{
// Do whatever you want to do when there is no valid target
}
Edit: Actually it looks like your raycast was succeeding if it hit anything, not just the intended target. I've tried to correct that above.
my problem today is simple (two problems if you count the fact that English isn't my first language), yet I can't manage to get a hold of it. I'm trying to make an up down game in c# and I'm trying to improve the movement scripts before working on anything else (since I don't want to resolve major bugs later).
The problem is that as soon as the player comes in contact with the NPC, it should stop moving in its general direction (it can move in 8 directions, using combinations of the W A S D keys). The NPC movement doesn't give any problems, it stops moving as soon as it perceives the player. Instead, when the player collides with the NPC it keeps on moving in that direction pushing it for what feels like half a second before stopping (as it should). So, to summarize, it doesn't immediately stop, which is unnerving since I've tried many things to make it stop (including freezing the position of the NPC but that just made things worse).
Now the codes:
The NPC movement code:
public class NPCFreeMovement : MonoBehaviour
{
public float speed = 2f; //how fast does the npc move
public float maxIdleTime = 3f; //max time the npc spends idling
public float maxChooseTime = 20f; //max time he spends moving
public float chooseDirTime = 0; //if 0, he chooses a direction
//possible directions
readonly Vector3[] directions = {Vector3.down, new Vector3(-0.5f, -0.5f, 0), Vector3.left, new Vector3(-0.5f, 0.5f, 0), Vector3.up,
new Vector3(0.5f, 0.5f, 0), Vector3.right, new Vector3(0.5f, -0.5f, 0)};
public int dir; //current direction
public bool canMove = true; //pretty self explainatory
public bool idle = true; //is the npc idle
public bool chase = false; //is the npc chasing after the player
public Vector3 goTo; //where to go if it's chasing after something
void Start()
{
StartCoroutine(Idle(maxIdleTime));
}
void Update()
{
chase = GetComponent<NPCLookForPlayerScript>().found;
if(canMove && !idle)
{
//move in the chosen direction
transform.position = Vector3.MoveTowards(transform.position, transform.position + directions[dir], Time.deltaTime * speed);
if (chooseDirTime > 0) //decrease movement time
{
chooseDirTime -= Time.deltaTime;
}
else
{
if(chase) StartCoroutine(Idle(0));
else StartCoroutine(Idle(Random.Range(0, maxIdleTime)));
}
}
}
//npc goes idle, then chooses a direction
public IEnumerator Idle(float f)
{
idle = true;
yield return new WaitForSeconds(f);
idle = false;
//here he chooses the direction depending of wether he's roaming or chasing
if (chase)
{
ChooseDirection();
chooseDirTime = maxChooseTime/4;
}
else
{
dir = Random.Range(0, directions.Length);
chooseDirTime = maxChooseTime;
}
}
//chooses a direction to take
void ChooseDirection()
{
Vector3 v = goTo - transform.position; //to check where to go
if (Mathf.Abs(v.x) <= 0.5)
{
if (v.y < 0) dir = 0; //go down
else dir = 4; //go up
}
if (Mathf.Abs(v.y) <= 0.5)
{
if (v.x < 0) dir = 2; //go left
else dir = 6; //go right
}
if (v.x < 0.5 && v.y < 0.5) dir = 1; //go down/left
if (v.x < 0.5 && v.y > 0.5) dir = 3; //go up/left
if (v.x > 0.5 && v.y > 0.5) dir = 5; //go up/right
if (v.x > 0.5 && v.y < 0.5) dir = 7; //go down/right
}
void OnCollisionEnter2D(Collision2D c)
{
if(c.gameObject.tag == "Obstacle") //stop and choose a new direction
{
StartCoroutine(Idle(Random.Range(0, maxIdleTime)));
}
if (c.gameObject.tag == "Player") //stop
{
canMove = false;
}
}
private void OnCollisionExit2D(Collision2D c)
{
if (c.gameObject.tag == "Player") //resume chasing the player
{
canMove = true;
}
}
}
The player's movement script:
public class PlayerFreeMovement : MonoBehaviour
{
public float speed = 2.0f; //movement speed
//where he's facing
public bool up;
public bool down;
public bool left;
public bool right;
public bool idle; //false if a movement button is pressed
public bool canMove = true;
//movement direction
public float dx;
public float dy;
public Vector3 v;
CollisionScript cs; //collision script
void Start()
{
down = true;
cs = GetComponent<CollisionScript>();
}
void Update() //move stickman in the direction of the keys and maintain the direction he's facing with animations
{
dx = 0;
dy = 0;
if (!(Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.S))) //check if idle
{
idle = true;
v = new Vector3();
}
else //the player's moving
{
idle = false;
AllFalse();
//set dx and dy
if (canMove && Input.GetKey(KeyCode.D))
{
if(!cs.hitRight) dx = 1;
right = true;
}
if (canMove && Input.GetKey(KeyCode.A))
{
if (!cs.hitLeft) dx = -1;
left = true;
}
if (canMove && Input.GetKey(KeyCode.W))
{
if (!cs.hitUp) dy = 1;
up = true;
}
if (canMove && Input.GetKey(KeyCode.S))
{
if (!cs.hitDown) dy = -1;
down = true;
}
//create the motion vector
v = new Vector3(dx, dy, 0);
if(dx !=0 && dy != 0)
{
v = v / 2;
}
}
//move
transform.position = Vector3.MoveTowards(transform.position, transform.position + v, Time.deltaTime * speed);
}
//avoid bug by deactivating all direction booleans before changing one
void AllFalse()
{
up = false;
down = false;
left = false;
right = false;
}
}
And finally, the collision script used to detect collisions in its path
public class CollisionScript: MonoBehaviour
{
//where is the wall
public bool hitDown= false;
public bool hitLeft = false;
public bool hitRight = false;
public bool hitUp = false;
//gizmos
public float collisionRadius = 0.5f;
public Vector2 downOffset = new Vector2(0, -0.5f);
public Vector2 rightOffset = new Vector2(0, 0.5f);
public Vector2 leftOffset = new Vector2(-0.5f, 0);
public Vector2 upOffset = new Vector2(0, 0.5f);
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
hitDown = Physics2D.OverlapCircle((Vector2)transform.position + downOffset, collisionRadius).gameObject!=gameObject;
hitRight = Physics2D.OverlapCircle((Vector2)transform.position + rightOffset, collisionRadius).gameObject != gameObject;
hitLeft = Physics2D.OverlapCircle((Vector2)transform.position + leftOffset, collisionRadius).gameObject != gameObject;
hitUp = Physics2D.OverlapCircle((Vector2)transform.position + upOffset, collisionRadius).gameObject != gameObject;
}
void OnDrawGizmos()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere((Vector2)transform.position + downOffset, collisionRadius);
Gizmos.DrawWireSphere((Vector2)transform.position + rightOffset, collisionRadius);
Gizmos.DrawWireSphere((Vector2)transform.position + leftOffset, collisionRadius);
Gizmos.DrawWireSphere((Vector2)transform.position + upOffset, collisionRadius);
}
}
If the waypoints are small size for example cubes at size 0.1 or 1 it's fine.
When i change the cubes size to 20-30 if there is a situation that there are two waypoints on the way but the enemy should get to the second waypoint he will stuck on the wall of the first waypoint will shake/stutter and will try to go to one of the sides and then in the end he will pass this waypoint and continue to the waypoint he should get the target.
It happen only when the waypoints(cubes) are very big and if a waypoint block the waypoint the enemy should get.
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityStandardAssets.Characters.ThirdPerson;
public class WayPoints : MonoBehaviour {
public GameObject[] waypoints;
public Transform target;
public float moveSpeed = 10f;
public float moveSpeed1 = 10f;
public float slowDownSpeed = 3f;
public float reverseSlowDownSpeed = 3f;
public float rotationSpeed = 1f;
private Transform myTransform;
private int targetsIndex = 0;
private Vector3 originalPosition;
private GameObject[] robots;
public Transform reverseTarget;
private int reverseTargetsIndex = 0;
private Vector3 reverseOriginalPosition;
public bool random = false;
void Awake()
{
myTransform = transform;
}
// Use this for initialization
void Start()
{
waypoints = GameObject.FindGameObjectsWithTag("ClonedObject");
robots = GameObject.FindGameObjectsWithTag("Robots");
AddColliderToWaypoints();
originalPosition = robots[0].transform.position;
reverseOriginalPosition = robots[1].transform.position;
}
// Update is called once per frame
void Update()
{
if (MyCommands.walkbetweenwaypoints == true)
{
WayPointsAI();
ReverseWayPointsAI();
}
DrawLinesInScene();
}
private void WayPointsAI()
{
if (targetsIndex == waypoints.Length)
targetsIndex = 0;
target = waypoints[targetsIndex].transform;
float distance = Vector3.Distance(robots[0].transform.position, target.transform.position);
robots[0].transform.rotation = Quaternion.Slerp(robots[0].transform.rotation, Quaternion.LookRotation(target.position - robots[0].transform.position), rotationSpeed * Time.deltaTime);
//move towards the player
if (distance < 30)
{
robots[0].transform.position += robots[0].transform.forward * slowDownSpeed * Time.deltaTime;
}
else
{
robots[0].transform.position += robots[0].transform.forward * moveSpeed * Time.deltaTime;
}
if (distance < target.transform.localScale.magnitude)
{
targetsIndex++;
}
}
private void ReverseWayPointsAI()
{
if (reverseTargetsIndex == 0)
reverseTargetsIndex = waypoints.Length -1;
reverseTarget = waypoints[reverseTargetsIndex].transform;
float distance = Vector3.Distance(robots[1].transform.position, reverseTarget.transform.position);
robots[1].transform.rotation = Quaternion.Slerp(robots[1].transform.rotation, Quaternion.LookRotation(reverseTarget.position - robots[1].transform.position), rotationSpeed * Time.deltaTime);
//move towards the player
if (distance < 30)
{
robots[1].transform.position += robots[1].transform.forward * reverseSlowDownSpeed * Time.deltaTime;
}
else
{
robots[1].transform.position += robots[1].transform.forward * moveSpeed1 * Time.deltaTime;
}
if (distance < reverseTarget.transform.localScale.magnitude)
{
reverseTargetsIndex--;
}
}
void RandomWayPointsAI()
{
if (random == true)
{
int index = Random.Range(0, waypoints.Length);
target = waypoints[index].transform;
}
}
void DrawLinesInScene()
{
// draw lines between each checkpoint //
for (int i = 0; i < waypoints.Length - 1; i++)
{
Debug.DrawLine(waypoints[i].transform.position, waypoints[i + 1].transform.position, Color.blue);
}
// draw a line between the original transform start position
// and the current transform position //
Debug.DrawLine(originalPosition, robots[0].transform.position, Color.red);
Debug.DrawLine(reverseOriginalPosition, robots[1].transform.position, Color.red);
// draw a line between current transform position and the next waypoint target
// each time reached a waypoint.
if (target != null)
Debug.DrawLine(target.transform.position, robots[0].transform.position, Color.green);
if (reverseTarget != null)
Debug.DrawLine(reverseTarget.transform.position, robots[1].transform.position, Color.green);
}
void AddColliderToWaypoints()
{
foreach (GameObject go in waypoints)
{
SphereCollider sc = go.AddComponent<SphereCollider>() as SphereCollider;
sc.isTrigger = true;
}
}
}
I tried to make
if (distance < reverseTarget.transform.localScale.magnitude)
I tried before it to make
if (distance < reverseTarget.tranform.localscale.x)
Or
if (distance < reverseTarget.tranform.localscale.x / 2)
Same with target and the first AI function.
But nothing is working. Maybe i should find the waypoints radius ?
Not sure how to solve it.
What OP seems to have wanted was a way to give the AI pathfinding so that when the AI wanted to go from (dynamic/code generated) point A to B and there was an obstacle between, the AI wouldn't get stuck in the obstacle.
Unity offers NavMesh and NavMesh Obstacle, a simple pathfinding tool Unity gives us to achieve a smart AI with little effort.
I am moving an object that consists of two cubes: left and right. These cubes are randomly generated on the y-axis upwards.
I am able to move them left and right with no problem, however, when I move one of the cubes left or right they all move.
How am I able to only move one of the cubes when touched only left or right rather than all of them? Here is my code below:
Generate cubes code:
public Transform block;
public Transform player;
private float objectSpawnedTo = 5.0f;
public static float distanceBetweenObjects = 5.0f;
private float nextCheck = 0.0f;
private ArrayList objects = new ArrayList();
void Start () {
maintenance(0.0f);
}
void Update () {
float playerX = player.position.y;
if(playerX > nextCheck)
{
maintenance(playerX);
}
}
private void maintenance(float playerX)
{
nextCheck = playerX + 30;
for (int i = objects.Count-1; i >= 0; i--)
{
Transform blck = (Transform)objects[i];
if(blck.position.y < (transform.position.y - 30))
{
Destroy(blck.gameObject);
objects.RemoveAt(i);
}
}
spawnObjects(5);
}
private void spawnObjects(int howMany)
{
float spawnX = objectSpawnedTo;
for(int i = 0; i<howMany; i++)
{
Vector3 pos = new Vector3(-3.5f,spawnX, 0);
//float firstRandom = Random.Range(-6.0f, 1.0f);
Transform blck = (Transform)Instantiate(block, pos, Quaternion.identity);
//blck.localScale+=new Vector3(firstRandom*2,0,0);
objects.Add(blck);
//pos = new Vector3(0,spawnX, 0);
//blck = (Transform)Instantiate(block, pos, Quaternion.identity);
//blck.localScale +=new Vector3((8.6f-firstRandom)*2,0,0);
//objects.Add(blck);
spawnX = spawnX + distanceBetweenObjects;
}
objectSpawnedTo = spawnX;
}
}
Cube-control code:
public float speed = 5;
// Use this for initialization
void Start () {
}
void Update() {
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved) {
// Get movement of the finger since last frame
Vector3 touchDeltaPosition = Input.GetTouch(0).deltaPosition;
// Move object across XY plane
transform.Translate(touchDeltaPosition.x * speed, 0, 0);
Vector3 boundaryVector = transform.position;
boundaryVector.x = Mathf.Clamp (boundaryVector.x, -5.5f, -2.8f);
transform.position = boundaryVector;
}
}
}
The problem is your cube-control code has no condition in it about which cube is selected : you simply wait for Input.touchCount to be > 0 and then move the cube. So all the cube having this script will move.
I think what you need to do is a raycast to check which cube has been "touched" and then only move it if raycast is successful :
[SerializeField]
private float speed = 0.5f;
private int MAX_TOUCH_COUNT = 5;
private bool[] touched;
protected void Start()
{
touched = new bool[MAX_TOUCH_COUNT];
}
void Update()
{
if (Input.touchCount > 0)
{
for(int i = 0; i < (Input.touchCount <= MAX_TOUCH_COUNT ? Input.touchCount : MAX_TOUCH_COUNT); i++)
{
if(touched[i] && Input.GetTouch(i).phase == TouchPhase.Ended)
{
touched[i] = false;
}
else if (!touched[i] && Input.GetTouch(i).phase == TouchPhase.Began)
{
Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(i).position);
RaycastHit hitInfo;
if (Physics.Raycast(ray, out hitInfo))
{
if (hitInfo.transform.GetComponentInChildren<*YOUR_CUBE_CONTROL_CLASS_NAME*>() == this)
{
touched[i] = true;
}
}
}
else if (touched[i] && Input.GetTouch(i).phase == TouchPhase.Moved)
{
// Get movement of the finger since last frame
Vector3 touchDeltaPosition = Input.GetTouch(i).deltaPosition;
// Move object across XY plane
transform.Translate(touchDeltaPosition.x * speed, 0, 0);
Vector3 boundaryVector = transform.position;
boundaryVector.x = Mathf.Clamp (boundaryVector.x, -5.5f, -2.8f);
transform.position = boundaryVector;
}
}
else
{
touched = false;
}
}
}
Hope this help,