How can I calculate a moving object speed? - c#

The goal is to show both transform and speed movement speed in text1 and text2.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Follow : MonoBehaviour
{
public Transform targetToFollow;
public Text text;
public Text text1;
public Text text2;
public float lookAtRotationSpeed;
public float moveSpeed;
private float minMoveSpeed = 0f;
private Vector3 originPos;
// Start is called before the first frame update
void Start()
{
originPos = targetToFollow.position;
}
// Update is called once per frame
void Update()
{
Vector3 lTargetDir = targetToFollow.position - transform.position;
lTargetDir.y = 0.0f;
transform.rotation = Quaternion.RotateTowards(transform.rotation,
Quaternion.LookRotation(lTargetDir), Time.time * lookAtRotationSpeed);
var distance = Vector3.Distance(transform.position, targetToFollow.position);
text.text = "Transform Distance From Target " + distance.ToString();
float ms = moveSpeed;
if (distance > 5f)
{
ms = moveSpeed + 0.5f; //move faster
}
else if (distance < 1.5f)
{
ms = Mathf.Max(minMoveSpeed, ms - 0.3f); // move slower
}
else
{
default value that equal moveSpeed here
transform.position = Vector3.MoveTowards(transform.position, targetToFollow.position, Time.deltaTime * ms);
}
if(distance < 0.5f && originPos == targetToFollow.position)
{
ms = 0f;
}
transform.position = Vector3.MoveTowards(transform.position, targetToFollow.position, Time.deltaTime * ms);
originPos = targetToFollow.position;
}
}
Now I'm setting to text the transform distance from the target.
But I want to add now to text1 and text2 the speed movement of the transform and the target.
In the end there will be 3 texts ui's that will show the distance and movement speeds.
I tried like this for the target speed :
var speedPerSec = Vector3.Distance(originPos, targetToFollow.position) / Time.deltaTime;
text1.text = "Target Speed Per Second " + speedPerSec.ToString();
but when the target is moving the text of the speed the speedPerSec.toString() is blinking fast and not smooth like the distance in the first text.
maybe it's blinking because it's showing the speed a lot after the zero point like : 0.00656555 ?

Since you want to track the speed of multiple objects (your transform and the target) the easiest way is to introduce a separate object handling the tracking and use an array of these.
[Serializable]
private class SpeedTracking
{
public Transform trackedObject;
public Text uiOutput;
private Vector3 _position;
private float _speed;
public void Update(float elapsedTime, float changeThreshold)
{
Vector3 newPos = trackedObject.position;
float newSpeed = Vector3.Distance(newPos, _position) / elapsedTime;
if ((newSpeed == 0 && _speed != 0) || Mathf.Abs(newSpeed - _speed) > changeThreshold)
{
_speed = newSpeed;
uiOutput.text = $"speed: {newSpeed:F3}";
}
_position = newPos;
}
}
private const float SPEED_CHANGE_THRESHOLD = 0.001F;
[SerializeField]
private SpeedTracking[] _trackings;
void OnEnable()
{
if (_trackings.Any(t => t.trackedObject == null || t.uiOutput == null))
{
Debug.LogError("at least one invalid tracking found");
enabled = false;
}
}
void Update()
{
//[...]
UpdateTrackings();
}
void UpdateTrackings()
{
foreach (SpeedTracking tracking in _trackings)
{
tracking.Update(Time.deltaTime, SPEED_CHANGE_THRESHOLD);
}
}
I also added a format to your output string (F3) showing only 3 decimal digits. (This alone would solve the flickering, but why update the ui more often than necessary?)

If you only want to know how much velocity of object in specific time
you can do something like this.
public class VelocityDisplayer : MonoBehaviour
{
public Transform Object; // your target gameobject
public Text View; // your text object
private Vector3 _position; // last position of target
private void OnEnable() {
_position = Object.transform.position;
}
private void Update() {
var dt = Time.deltaTime;
var current = Object.transform.position;
var delta = Vector3.Distance(current, _position);
var velocity = delta / dt;
View.text = (velocity).ToString("#,##0.000");
// replace current position
_position = current;
}
}
but this show actual speed which depend on game world/physics of object not your setup movement speed variable
PS1. I prefer to use update because is UI scope
PS2. I prefer to split new class to track object

Related

Unity grappling more like Tarzan

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.

How can I add a slowdown effect when object is getting close to a target?

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

Why the lerp function not working for the instantiated randomized Gameobjects?

The code helps in moving the game object in a continousy loop. I want the randomized generated cubes to follow the same pattern too. I didn't add the condition for stopping the generation of game objects when it completes one round. Currently, the generated game objects don't move.
The ultimate idea is to generate splash scene. I would like to know if the following way is gpu efficient too!
using UnityEngine;
using System.Collections.Generic;
public class IntegratedScrpt : MonoBehaviour
{
public List<GameObject> splashImagesGOList;
public float InvokeRate = 10f;
public GameObject cube01;
private int selection;
// Loop mode variables
private Vector3 startPosition;
private Vector3 endPosition;
private float distance = 54f;
private float distanceCovered;
private float currentDistance;
//for Vector Lerp
private float currentLerpTime = 0f;
private float lerpTime = 9f;
private float t;
void Start()
{
startPosition = splashImagesGOList[1].transform.position;
Debug.LogError("selection VALUE AT" + selection);
endPosition = Vector3.back * distance;
}
void Update()
{
InvokeRepeating("pickpoints", 1.0f, InvokeRate);
//loop mode
distanceCovered = Vector3.Distance(startPosition, endPosition);
currentDistance = distanceCovered * t;
currentLerpTime += Time.deltaTime;
if (currentLerpTime == lerpTime)
{
currentLerpTime = lerpTime;
}
if (currentLerpTime > lerpTime)
{
currentLerpTime = 0f;
}
t = currentLerpTime / lerpTime;
Move();
if (currentDistance == 64)
{
splashImagesGOList[selection].transform.position = startPosition;
Move();
}
Debug.LogError("SELECTION" + selection);
}
// method for making the gameobjects move
public void Move()
{
splashImagesGOList[selection].transform.position = Vector3.Lerp(startPosition, endPosition, t);
}
// code for instantiating the gameobjects
void pickpoints()
{
foreach (GameObject cube01 in splashImagesGOList)
{
int selection = UnityEngine.Random.Range(0, splashImagesGOList.Count);
// Instantiate(splashImagesGOList[selection], cube01.transform.position, cube01.transform.rotation);
Instantiate(splashImagesGOList[selection], startPosition, cube01.transform.rotation);
}
}
}
The reason the instantiated gameobjects aren't moving is because you're not assigning to any of their positions! splashImagesGOList[selection].transform.position is the position of one of the prefabs. In your current code, you instantiate the objects and then never interact with the instantiated objects.
You should have each object handle its own movement by separating out your movement logic into a different script and attaching the script to each of the prefabs in your list. You can use Mathf.Repeat to do the sort of looping your current code seems to mean to do.
Now, it's not clear what kind of pattern you are trying to achieve with the repeated simultaneous random instantiation but regardless of that, you probably don't mean to put InvokeRepeating in Update. Additionally, you should have some kind of end condition to cease the repeated PickPoints calls with CancelInvoke("PickPoints");. Creating an ever increasing number of objects is not gpu efficient ;)
Altogether, these changes might look like this:
public class SpashImageMover : MonoBehaviour
{
public Vector3 startPosition;
public Vector3 endPosition;
public float3 lerpTime;
private float t = 0; // in case code in Start is removed
void Start()
{
// remove these two lines if you don't want the objects synchronized
t = Mathf.Repeat(Time.time/lerpTime, 1f);
transform.position = Vector3.Lerp(startPosition, endPosition, t);
}
void Update()
{
t = Mathf.Repeat(t + Time.deltaTime / lerpTime, 1f);
transform.position = Vector3.Lerp(startPosition, endPosition, t);
}
}
public class IntegratedScrpt : MonoBehaviour
{
public List<GameObject> splashImagesGOList;
public float InvokeRate = 10f;
private int selection;
// Loop mode variables
private Vector3 startPosition;
private Vector3 endPosition;
//for Vector Lerp
private float lerpTime = 9f;
// end condition for PickPoints
private bool invokingPickPoints;
private float pickPointsTimeRemaining = 27f;
void Start()
{
startPosition = splashImagesGOList[1].transform.position;
Debug.LogError("selection VALUE AT" + selection);
endPosition = Vector3.back * distance;
InvokeRepeating("PickPoints", 1.0f, InvokeRate);
invokingPickPoints = true;
}
void Update()
{
if (invokingPickPoints)
{
pickPointsTimeRemaining -= Time.deltaTime;
if (pickPointsTimeRemaining <= 0 )
{
CancelInvoke("PickPoints");
invokingPickPoints = false;
}
}
}
// code for instantiating the gameobjects
void PickPoints()
{
foreach (GameObject cube01 in splashImagesGOList)
{
int selection = UnityEngine.Random.Range(0, splashImagesGOList.Count);
// Instantiate(splashImagesGOList[selection], cube01.transform.position, cube01.transform.rotation);
GameObject newGO = Instantiate(splashImagesGOList[selection], startPosition, cube01.transform.rotation);
SpashImageMover mover = newGO.GetComponent<SpashImageMover>();
mover.startPosition = startPosition;
mover.endPosition = endPosition;
mover.lerpTime = lerpTime;
}
}
}
As a sidenote, if you now find you don't like how you're instantiating the objects, that would be more appropriate for a different question with a very descriptive explanation of what you are trying to achieve. It's too broad of a question to try and address that here.

How can I rotate the camera around group of objects and not only a single one?

The camera is child of one soldier only and also the target is this soldier.
And the script attached to the camera.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraMove : MonoBehaviour
{
public Transform target;
public float speed = 0.1f;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
transform.RotateAround(target.transform.position, new Vector3(0, 1, 0), 100 * Time.deltaTime * speed);
}
}
But now I want to do two things. To make the camera to rotate around the whole soldiers and not only the specific one. And also to make the camera stop slowly when it's facing the soldiers. Now the camera is behind when starting the game.
Using a bool flag if true to make the camera rotate around the soldiers until it's facing them then stop rotation and keep moving with the soldiers.
If unchecked false make the camera rotating around the solders none stop.
UPDATE what I tried so far:
This code will make it rotating around all the soldiers and it's working fine:
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class CameraMove : MonoBehaviour
{
public float speed = 0.1f;
private List<GameObject> Soldiers = new List<GameObject>();
// Use this for initialization
void Start()
{
Soldiers.AddRange(GameObject.FindGameObjectsWithTag("Soldier"));
}
// Update is called once per frame
void Update()
{
RotateAround();
}
private void RotateAround()
{
transform.RotateAround(GetAverageLocationOfSoliders(), new Vector3(0, 1, 0), 100 * Time.deltaTime * speed);
}
private Vector3 GetAverageLocationOfSoliders()
{
var total = new Vector3();
foreach (var soldier in Soldiers)
total += soldier.transform.position;
return total / Soldiers.Count(); // Assuming Soldiers is List<Soldier>
}
}
Then I tried to add a slowDown bool flag variable, But I messed it all and it's not working at all.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class CameraMove : MonoBehaviour
{
public float speed = 0.1f;
public bool slowDown = false;
private List<Vector3> SoldiersPositions = new List<Vector3>();
private List<Vector3> SoldiersFacingDirection = new List<Vector3>();
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void FixedUpdate()
{
RotateAround();
}
private void RotateAround()
{
var getSoldiers = GameObject.FindGameObjectsWithTag("Soldier");
foreach (GameObject soldier in getSoldiers)
{
SoldiersPositions.Add(soldier.transform.position);
SoldiersFacingDirection.Add(soldier.transform.forward);
}
var Center = GetAverageLocationOfSoliders();
var FacingDirections = GetAverageFacingDirectionOfSoldiers();
if (slowDown == true)
{
var D = transform.position - Center;
var CamAngle = Vector3.Angle(D, FacingDirections);
speed = speed - CamAngle;
}
transform.RotateAround(Center, new Vector3(0, 1, 0), 100 * Time.deltaTime * speed);
SoldiersPositions = new List<Vector3>();
SoldiersFacingDirection = new List<Vector3>();
}
private Vector3 GetAverageLocationOfSoliders()
{
var total = new Vector3();
foreach (var soldier in SoldiersPositions)
total += soldier;
return total / SoldiersPositions.Count(); // Assuming Soldiers is List<Soldier>
}
private Vector3 GetAverageFacingDirectionOfSoldiers()
{
var total = new Vector3();
foreach (var soldierfacingdir in SoldiersFacingDirection)
total += soldierfacingdir;
return total / SoldiersFacingDirection.Count();
}
}
I'm not sure if the first code example only for the rotation is fine the way I wrote it. It's working but not sure if this is a good way to do the code ?
It seems to me that in the first code only the rotation the camera is a bit shaking or stuttering I mean the camera is not moving smooth when rotating around. It's almost hard to see in the game view in the editor but you can see it a bit in the scene view I think.
The reason I'm calling RotateAround(); in the Update is that the soldiers are in a move they are walking forward non stop.
How should I do the slowDown part ?
UPDATE 2:
This is the full coed now:
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class CameraMove : MonoBehaviour
{
[Header("Spin")]
public bool spin = false;
public Vector3 Direction;
[Range(0, 300)]
public float speed = 10f;
public bool randomSpeed = false;
public bool randomDirection = false;
[Range(0f, 100f)]
public float timeDirChange;
public Vector3 defaultDirection;
[Space(5)]
[Header("Move in circles")]
public bool moveInCircles = true;
public GameObject rotateAroundTarget;
public Vector3 axis;//by which axis it will rotate. x,y or z.
public float rotationSpeed; //or the speed of rotation.
public float upperLimit, lowerLimit, delay;// upperLimit & lowerLimit: heighest & lowest height;
public bool randomHeight = false;
public bool stopRotatingWhenFacing = false;
private float height, prevHeight, time;//height:height it is trying to reach(randomly generated); prevHeight:stores last value of height;delay in radomness;
[Space(5)]
[Header("Follow objects")]
public GameObject[] objectsToFollow;
public bool randomFollow;
private float nextRotationTime = 0f;
private int counter = 0;
private List<GameObject> Soldiers = new List<GameObject>();
// Use this for initialization
void Start()
{
Soldiers.AddRange(GameObject.FindGameObjectsWithTag("Soldier"));
}
private void Update()
{
if (randomSpeed)
{
speed = UnityEngine.Random.Range(0, 300);
}
if (spin)
{
if (randomDirection == false)
{
nextRotationTime = 0;
timeDirChange = 0;
Direction = defaultDirection;
}
else
{
if (Time.time > nextRotationTime)
{
nextRotationTime += timeDirChange;
RandomDirection();
}
}
transform.Rotate(Direction, speed * Time.deltaTime);
}
else
{
timeDirChange = 0;
randomDirection = false;
randomSpeed = false;
}
if (moveInCircles)
{
MoveInCircles();
}
}
private void RandomDirection()
{
Direction = new Vector3(UnityEngine.Random.Range(-1, 1), UnityEngine.Random.Range(-1, 1), UnityEngine.Random.Range(-1, 1));
while (Direction == new Vector3(0, 0, 0))
{
counter++;
Direction = new Vector3(UnityEngine.Random.Range(-1, 1), UnityEngine.Random.Range(-1, 1), UnityEngine.Random.Range(-1, 1));
if (counter == 2)
{
Direction = new Vector3(1, 0, 0);
break;
}
}
counter = 0;
}
private void MoveInCircles()
{
var F = GetAverageDirectionsOfSoliders();
var D = transform.position - GetAverageLocationOfSoliders();
var angle = Vector3.Angle(D, F);
if (angle < 5f)
{
rotationSpeed -= 0.1f;
}
transform.RotateAround(GetAverageLocationOfSoliders(), axis, rotationSpeed);
time += Time.deltaTime;
//Sets value of 'height' randomly within 'upperLimit' & 'lowerLimit' after delay
if (time > delay)
{
prevHeight = height;
if (randomHeight)
{
height = UnityEngine.Random.Range(lowerLimit, upperLimit);
}
time = 0;
}
if (randomHeight == false)
{
height = transform.position.y;
}
if (randomHeight)
{
//Mathf.Lerp changes height from 'prevHeight' to 'height' gradually (smooth transition)
transform.position = new Vector3(transform.position.x, Mathf.Lerp(prevHeight, height, time), transform.position.z);
}
else
{
transform.position = new Vector3(transform.position.x, height, transform.position.z);
}
}
private Vector3 GetAverageLocationOfSoliders()
{
var total = new Vector3();
foreach (var soldier in Soldiers)
{
total += soldier.transform.position;
}
return total / Soldiers.Count(); // Assuming Soldiers is List<Soldier>
}
private Vector3 GetAverageDirectionsOfSoliders()
{
var totalf = new Vector3();
foreach (var soldier in Soldiers)
{
totalf += soldier.transform.forward;
}
return totalf / Soldiers.Count();
}
}
The RotateAround part is working fine:
transform.RotateAround(GetAverageLocationOfSoliders(), axis, rotationSpeed);
But the slow down part is not working. It's not slowing down at all when the camera is facing the soldiers.
This is how I calculate the average transform.forward vector of all the soldiers:
private Vector3 GetAverageDirectionsOfSoliders()
{
var totalf = new Vector3();
foreach (var soldier in Soldiers)
{
totalf += soldier.transform.forward;
}
return totalf / Soldiers.Count();
}
Then inside the MoveInCircles method I did:
var F = GetAverageDirectionsOfSoliders();
var D = transform.position - GetAverageLocationOfSoliders();
var angle = Vector3.Angle(D, F);
if (angle < 5f)
{
rotationSpeed -= 0.1f;
}
But it's never getting to the line:
rotationSpeed -= 0.1f;
To rotate around all the soldiers:
Get the center of the all the soldiers (sum the world position of all soldiers and divide by N number of soldiers)
Get the maximum distance of the soldiers from the center
Rotate the camera about the center, and ensure that it is further than the max distance from the center
To slow the camera down when facing the soldiers' front:
Average out the transform.forward vector of all the soldiers. We call this vector F. This is fair since all your soldiers will typically be facing in the same general direction for this request to even make sense.
Calculate the direction D, which is the direction from the soldiers' center to the camera. This is easy: D = camera.transform.position - soldiersCenter
Finally, find the acute angle between D and F, using Vector3.Angle(). If this angle is lower than a certain threshold, decrease the moveSpeed of the camera.
The actual code is easy to write, but I'll let you practice. Let me know if you need any help
From the sounds of it, it seems you have it already able to orbit around a single soldier, so do to more than one, simply take the average of their locations.
Pseudo code:
transform.RotateAround(GetAverageLocationOfSoliders(), ...);
...
private static Vector3 GetAverageLocationOfSoliders
{
var total = new Vector3();
foreach(var soldier in Soldiers)
total += soldier.transform.position;
return total / Soliders.Count(); // Assuming Soldiers is List<Soldier>
}
Now, Vector3 might not have stuff like Vector3 += Vector3 or Vector3 / int, but if that's the case just create your own methods were you do that manually (adding vectorA.x + vectorB.x, and vectorA.x / num, etc.
As far as making the camera stop when it's in front, that's a bit trickier. You might want to do some checks first to make sure they all have the same rotation, and then check, each Update, if the look at rotation of the camera will point to one of the soldiers.
But, if you want it to slow down, then, instead of checking if the lookAtRotation can point to a soldier, check if it can point to some offset of the camera's rotation, like so:
Pseudo Code:
lookRotation(transform.Rotation + offset) // Use something like this to find Soldiers[0]
Then, if that finds one, you can use a Lerp on your speed to lerp back down to a speed of 0.
You will also have to maintain a List<Soldier> or Soldier[] somehow.

Getting the distance of wall(Multiple objects with same tag)

I'm trying to get the distance between my player, and the nearest object with the tag 'wall' however I can't seem to get it to work.
To my knowledge my code isn't working at all.
So my question is;
What am I doing wrong? Again, I want to find the distance from my player and the nearest object with the tag 'wall'. If I'm near a object with the tag 'wall' I want it to set the variable to true.(nearWall = true) then once I'm away from the object(About 10.0f) I want it back to false.(nearWall = false)
This is the code I have been working with.
using UnityEngine;
using System.Collections;
public class PlayerMotor : MonoBehaviour {
private CharacterController controller;
private Vector3 moveVector;
private float speed = 2.0f;
private float verticalVelocity = 0.0f;
private float gravity = 12.0f;
private bool nearWall;
public GameObject playerObject;
GameObject closestObject;
float distance = Mathf.Infinity;
public float distanceToWall = Mathf.Infinity;
private void Start() {
nearWall = false;
playerObject = GameObject.Find("Player");
distanceToWall = 0;
controller = GetComponent<CharacterController> ();
}
public void getNearestWall()
{
if (distance <= 10.0f) {
nearWall = true;
print ("Near wall!");
}
else
nearWall = false;
}
GameObject findNearestWall()
{
GameObject[]objectArray;
objectArray = GameObject.FindGameObjectsWithTag("wall");
Vector3 position = playerObject.transform.position;
foreach(GameObject currentObject in objectArray)
{
Vector3 distanceCheck = currentObject.transform.position - position;
float currentDistance = distanceCheck.sqrMagnitude;
if (currentDistance < distance)
{
closestObject = currentObject;
distance = currentDistance;
}
}
return closestObject;
}
private void Update()
{
findNearestWall ();
moveVector = Vector3.zero;
if (controller.isGrounded)
{
verticalVelocity = -0.5f;
}
else
{
verticalVelocity -= gravity * Time.deltaTime;
}
if (Input.GetMouseButton (0)) {
if (!nearWall) {
if (Input.mousePosition.x > Screen.width / 2)
moveVector.x = speed;
else
moveVector.x = -speed;
}
else
{
moveVector.x = transform.forward.x * speed;
transform.Rotate(new Vector3(0, -90, 0));
}
}
moveVector.y = verticalVelocity;
moveVector.z = transform.forward.z * speed;
controller.Move (moveVector * Time.deltaTime);
}
}
One thing is that you are not calling getNearestWall() method - which is actually changing the flag - anywhere.
And second why don't you just try:
currentDistance = Vector3.Distance(currentObject.transform.position, position);
When calculating distance
first of all you need to call getNearestWall(); inside the Update() method ( after findNearestWall() of course ). also what you are doing now is getting the minimal distance the player reached in the whole game. you might want to add distance = Mathf.Infinity; in top of findNearestWall() so it will something like this:
GameObject findNearestWall()
{
GameObject[] objectArray;
objectArray = GameObject.FindGameObjectsWithTag("wall");
distance = Mathf.Infinity;
Vector3 position = playerObject.transform.position;
foreach (GameObject currentObject in objectArray)
{
Vector3 distanceCheck = currentObject.transform.position - position;
float currentDistance = distanceCheck.sqrMagnitude;
if (currentDistance < distance)
{
closestObject = currentObject;
distance = currentDistance;
}
}
return closestObject;
}
now whenever you get near a wall it should print Near wall!
note:
also you are calling FindObjectsWithTag() at an Update method which might significantly drain your processing power. you might want to avoid that by declaring a private GameObject[] objectArray in the class.
and then use objectArray = GameObject.FindGameObjectsWithTag("wall"); once at Awake() or Start()

Categories

Resources