I have been making a script in Unity that measures how far a player has moved in the real world using XRNodes like this for example with the right hand:
InputTracking.GetLocalPosition(XRNode.RightHand)
at the start of the movement and then comparing it to the end position
Now I would like to get the distance moved, even if the player moved around in a circle.
Is the a method to do this with XRNodes? Measuring total distance moved during play?
Yes, well, you could just simply sum it up every frame like
// Stores the overall moved distance
private float totalMovedDistance;
// flag to start and stop tracking
// Could also use a Coroutine if that fits you better
private bool track;
// Store position of last frame
private Vector3 lastPos;
public void BeginTrack()
{
// reset total value
totalMovedDistance = 0;
// store first position
lastPos = InputTracking.GetLocalPosition(XRNode.RightHand);
// start tracking
track = true;
}
public void EndTrack()
{
// stop tracking
track = false;
// whatever you want to do with the total distance now
Debug.Log($"Total moved distance in local space: {totalMovedDistance}", this);
}
private void Update()
{
// If not tracking do nothing
if(!track) return;
// get current controller position
var currentPos = InputTracking.GetLocalPosition(XRNode.RightHand);
// Get distance moved since last frame
var thisFrameDistance = Vector3.Distance(currentPos, lastPos);
// sum it up to the total value
totalMovedDistance += thisFrameDistance;
// update the last position
lastPos = currentPos;
}
Related
I'm adding the option for players to move the camera to the sides. I also want to limit how far they can move the camera to the sides.
If the camera was aligned with the axis, I could simply move around X/Z axis and set a limit on each axis as to how far it can go. But my problem is that the camera is rotated, so I'm stuck figuring out how to move it and set a limit. How could I implement this?
using UnityEngine;
[RequireComponent(typeof(Camera))]
public class CameraController : MonoBehaviour
{
Camera cam;
Vector3 dragOrigin;
bool drag = false;
void Awake()
{
cam = GetComponent<Camera>();
}
void LateUpdate()
{
// Camera movement with mouse
Vector3 diff = (cam.ScreenToWorldPoint(Input.mousePosition)) - cam.transform.position;
if (Input.GetMouseButton(0))
{
if (drag == false)
{
drag = true;
dragOrigin = cam.ScreenToWorldPoint(Input.mousePosition);
}
}
else
{
drag = false;
}
if (drag)
{
// Here I want to set a constraint in a rectangular plane perpendicular to camera view
transform.position = dragOrigin - diff;
}
}
}
Transform in Unity comes with a handy Transform.right property, which regards the object's rotation. To move your camera sideways you could further utilize Lerp to make the movement smooth.
transform.position += transform.right * factor
moves an object to the right.
Use factor to adjust the desired distance and by doing so you can also set limits. Negative factor would mean moving left by the way:) Hope that helps!
It can be tricky to deal with constraints on rotated objects. The math behind this includes some vector/rotation math to figure out the correct limits relative to the object's orientation, and whether you've exceeded them.
Luckily though, Unity gives you some shortcuts to skip this math: Transform.InverseTransformPoint() and Transform.TransformPoint()! These two methods allow you to transform a point in world space into a point in local space, and vice versa.
That means that no matter how your camera is oriented, you can interpret a position from the orientation of the camera - and with just a couple extra steps, your X/Z constraints are usable because you can calculate X/Z from the camera's point of view.
Let's try to adapt your current script to use this:
using UnityEngine;
[RequireComponent(typeof(Camera))]
public class CameraController : MonoBehaviour
{
// Set the X and Z values in the editor to define the rectangle within
// which your camera can move
public Vector3 maxConstraints;
public Vector3 minConstraints;
Camera cam;
Vector3 dragOrigin;
bool drag = false;
Vector3 cameraStart;
void Awake()
{
cam = GetComponent<Camera>();
// Here, we record the start since we'll need a reference to determine
// how far the camera has moved within the allowed rectangle
cameraStart = transform.position;
}
void LateUpdate()
{
// Camera movement with mouse
Vector3 diff = (cam.ScreenToWorldPoint(Input.mousePosition)) - cam.transform.position;
if (Input.GetMouseButton(0))
{
if (drag == false)
{
drag = true;
dragOrigin = cam.ScreenToWorldPoint(Input.mousePosition);
}
}
else
{
drag = false;
}
if (drag)
{
// Now, rather than setting the position directly, let's make sure it's
// within the valid rectangle first
Vector3 newPosition = dragOrigin - diff;
// First, we get into the local space of the camera and determine the delta
// between the start and possible new position
Vector3 localStart = transform.InverseTransformPoint(cameraStart);
Vector3 localNewPosition = transform.InverseTransformPoint(newPosition);
Vector3 localDelta = localNewPosition - localStart;
// Now, we calculate constrained values for the X and Z coordinates
float clampedDeltaX = Mathf.Clamp(localDelta.x, minConstraint.x, maxConstraint.x);
float clampedDeltaZ = Mathf.Clamp(localDelta.z, minConstraint.z, maxConstraint.z);
// Then, we can use the constrained values to determine the constrained position
// within local space
Vector3 localClampedPosition = new Vector3(clampedDeltaX, localDelta.y, clampedDeltaZ)
+ localStart;
// Finally, we can convert the local position back to world space and use it
transform.position = transform.TransformPoint(localConstrainedPosition);
}
}
}
Note that I'm somewhat assuming dragOrigin - diff moves your camera correctly in its present state. If it doesn't do what you want, please include details on the unwanted behaviour and we can sort that out too.
I've searched different sources but I couldn't find what I directly need. I use a maincamera in my scene and when some conditions are satisfied, I want my camera to animate to the point that I will get from an object which is in the scene. I tried to use Animation Curves but didn't work exactly how I wanted. So I thought of changing the values of the keys of the animation. Is there way that I can change keys and at the same time animation would animate how I want it to animate.
I don't think animations are what you are looking for. I highly suggest you using a Tween asset or writing your own tweening library. Here is an example tweeting code that will move your object:
private void Start()
{
StartCoroutine(Move(5));
}
private IEnumerator Move(float duration)
{
var startPos = gameObject.transform.position;
var targetPos = new Vector3(0, 10, 0);
var timer = 0f;
while (timer < duration)
{
transform.position = Mathf.Lerp(startPos, targetPos, timer / duration);
timer += Time.deltaTime;
yield return null;
}
transform.position = targetPos;
}
This piece of code will take your gameObject and move it to the targetPos in duration seconds. What we do here is essentially called linear interpolation, which means that we change a value linearly from the start value to end value, and timer / duration is called interpolation value. The interpolation value must be between 0 and 1, where inputting 0 will give you the start value and 1 will give you the end value. You can smooth this interpolation value to get smoother movements.
My character is a car and I try to rotate it the direction it move, so far so good I succeeded to do that but once I stop moving the character flips back to the direction it was on the start.
Also how can I make my turns from side to the opposite site smooth ?
Here is my code so far:
[SerializeField] float driveSpeed = 5f;
//state
Rigidbody2D myRigidbody;
// Start is called before the first frame update
void Start()
{
myRigidbody = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
Move();
}
private void Move()
{
//Control of velocity of the car
float HorizontalcontrolThrow = CrossPlatformInputManager.GetAxis("Horizontal"); // Value between -1 to 1
float VerticalcontrolThrow = CrossPlatformInputManager.GetAxis("Vertical"); // Value between -1 to 1
Vector2 playerVelocity = new Vector2(HorizontalcontrolThrow * driveSpeed, VerticalcontrolThrow * driveSpeed);
myRigidbody.velocity =playerVelocity;
**//Direction of the car**
Vector2 direction = new Vector2(HorizontalcontrolThrow, VerticalcontrolThrow);
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
myRigidbody.rotation = angle;
}
I'm not sure about this, but maybe that last line "myRigidbody.rotation = angle" being called every frame is what is making your car reset its rotation.
Maybe change it to "myRigidbody.rotation *= angle" or "myRigidbody.rotation += angle".
It looks like it may be because HorizontalcontrolThrow and VerticalcontrolThrow are going to be reset when you release the controls. If it's resetting to its original orientation, then what's happening is that until you move, those two values are going to be at their default value. You then move and it affects the rotation. But when you release the controls, those values are back to the starting values again, and so is your rotation.
What you therefore need to do is try to separate the HorizontalcontrolThrow and VerticalcontrolThrow from the rest of the code, which should only be activated when at least one of these two variables are not at their default setting (I can't remember what the axis functions return at the moment).
Edit:
An IF statement should suffice (some rough pseudo code):
if (horizontalAxis != default || verticalAxis != default)
{
Rotate/Move
}
I solved the snap rotation using Quaternion at rotation, the issiu I had with it was to convert it from 3d to 2d, through the guide of this clip: youtube.com/watch?v=mKLp-2iseDc and made my adjustments it works just fine !
I have a GameObject that takes position coordinates from a file. This GameObject moves with vibration rather than smoothly. (as if moving back and forth.)
Here is the code that provides the movement:
int counter = 0;
void Update()
{
var maxDistance = speed_* Time.deltaTime;
var oldPosition = transform.position;
var newPosition = Vector3.MoveTowards(transform.position, positions[counter], maxDistance);
var actualDistance = Vector3.Distance(newPosition, oldPosition);
var distanceRemainder = maxDistance - actualDistance;
if (distanceRemainder > 0.0001)
{
newPosition = Vector3.MoveTowards(newPosition, positions[counter], distanceRemainder);
counter++;
}
transform.localPosition = newPosition;
}
NOTE: The data read from the file is in the "positions" array (x,y,z coordinates).
When I lower the 300f value in the variable maxDistance, the vibration stops and the motion becomes more fluid. However, Gameobject speed is also slowing down. How can I ensure a fast and smooth movement?
SOLUTION:
While looking for a solution of the problem, I came across the this topic. It helped me learn the source of the problem.
I have observed that the GameObject is not vibrating in Scene view, it was moving smoothly. But the object seemed to be vibrating in GameView. The problem is not the moving object, it's the camera function I write to follow it.
The camera function that was written to follow the object has been updated and the problem has disappeared.
One can see more of the solution by going to Post #13 in the link above.
Thanks to everyone trying to help.
This is caused cause you are using your distanceRemainder as your maxDistanceDelta, which I think is incorrect, if you want a smooth movement, you should multiply it for the Time.deltaTime, try:
newPosition = Vector3.MoveTowards(newPosition, positions[counter], distanceRemainder*Time.deltaTime);
Or simply declare speed variable and do:
newPosition = Vector3.MoveTowards(newPosition, positions[counter], speed*Time.deltaTime);
I assume what you want is your object moving with a constant speed to the first position. Once it reaches it, move to the next one.
I would do it in a simple Coroutine which is better to understand and maintain than doing stuff in Update:
private void Start()
{
// todo assign positions
StartCoroutine(RunPositions());
}
private IEnumerator RunPositions()
{
foreach(var position in positions)
{
while(!Mathf.Approximately(Vector3.Distance(transform.position, position), 0))
{
var maxDistance = speed_* Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, positions[counter], maxDistance);
// render and continue in the next frame
yield return null;
}
}
}
If you are fine with a precision of 0.00001 you can also simply use
while(transform.position != position)
instead.
I have a problem that iv been trying to figure out for a couple of says and i cant seem to pin point the cause of it! I have created a first person shooter that consists of a few enemies on a small map. I have two scenes (the main menu and the game level). When my player dies it is takes to the main menu from which you can choose to play the game again. This then reloads the level again. The first time the game is run it runs without any problems. However when i doe and press the play game button again it returns to me a message which states the following "MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it." from the code below I can only see two types which are of GameObject. I have tried to remove the muzzleFlash to see if that is the issue however it makes no difference. I have unticked all the static boxes as i read that this may be the cause of the problem but this did not resolve the problem. this script below is attached to an enemy, I have a PlayerShot script attached to the FPS. Please could someone help?
// speed of the AI player
public var speed:int = 5;
// speed the ai player rotates by
public var rotationSpeed:int = 3;
// the waypoints
public var waypoints:Transform[];
// current waypoint id
private var waypointId:int = 0;
// the player
public var player:GameObject;
// firing toggle
private var firing:boolean = false;
// the Mesh Renderer of the Muzzle Flash GameObject
private var muzzleFlashAgent:GameObject;
/**
Start
*/
function Start()
{
// retrieve the player
player = GameObject.Find("First Person Controller");
// retrieve the muzzle flash
muzzleFlashAgent = GameObject.Find("muzzleFlashAgent");
// disable the muzzle flash renderer
muzzleFlashAgent.active = false;
}
/**
Patrol around the waypoints
*/
function Patrol()
{
// if no waypoints have been assigned
if (waypoints.Length == 0)
{
print("You need to assign some waypoints within the Inspector");
return;
}
// if distance to waypoint is less than 2 metres then start heading toward next waypoint
if (Vector3.Distance(waypoints[waypointId].position, transform.position) < 2)
{
// increase waypoint id
waypointId++;
// make sure new waypointId isn't greater than number of waypoints
// if it is then set waypointId to 0 to head towards first waypoint again
if (waypointId >= waypoints.Length) waypointId = 0;
}
// move towards the current waypointId's position
MoveTowards(waypoints[waypointId].position);
}
/**
Move towards the targetPosition
*/
function MoveTowards(targetPosition:Vector3)
{
// calculate the direction
var direction:Vector3 = targetPosition - transform.position;
// rotate over time to face the target rotation - Quaternion.LookRotation(direction)
transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.LookRotation(direction), rotationSpeed * Time.deltaTime);
// set the x and z axis of rotation to 0 so the soldier stands upright (otherwise equals REALLY bad leaning)
transform.eulerAngles = Vector3(0, transform.eulerAngles.y, 0);
// use the CharacterController Component's SimpleMove(...) function
// multiply the soldiers forward vector by the speed to move the AI
GetComponent (CharacterController).SimpleMove(transform.forward * speed);
// play the walking animation
animation.Play("walk");
}
/**
Update
*/
function Update()
{
// calculate the distance to the player
var distanceToPlayer:int = Vector3.Distance(transform.position, player.transform.position);
// calculate vector direction to the player
var directionToPlayer:Vector3 = transform.position - player.transform.position;
// calculate the angle between AI forward vector and direction toward player
// we use Mathf.Abs to store the absolute value (i.e. always positive)
var angle:int = Mathf.Abs(Vector3.Angle(transform.forward, directionToPlayer));
// if player is within 30m and angle is greater than 130 (IN FRONT) then begin chasing the player
if (distanceToPlayer < 30 && angle > 130)
{
// move towards the players position
MoveTowards(player.transform.position);
// if not firing then start firing!
if (!firing) Fire();
}
// if player is within 5m and BEHIND then begin chasing
else if (distanceToPlayer < 5 && angle < 130)
{
// move towards the players position
MoveTowards(player.transform.position);
// if not firing then start firing!
if (!firing) Fire();
}
else
{
// patrol
Patrol();
// stop firing
firing = false;
}
}
/**
Fire at the player
*/
function Fire()
{
// toggle firing on
firing = true;
// check if still firing
while (firing)
{
// hit variable for RayCasting
var hit:RaycastHit;
// range of weapon
var range:int = 30;
// fire the ray from our position of our muzzle flash, forwards "range" metres and store whatever is detected in the variable "hit"
if (Physics.Raycast(muzzleFlashAgent.transform.position, transform.forward, hit, range))
{
// draw a line in the scene so we can see what's going on
Debug.DrawLine (muzzleFlashAgent.transform.position, hit.point);
// if we hit the player
if (hit.transform.name == "First Person Controller")
{
// inform the player that they have been shot
player.GetComponent(PlayerShot).Shot();
// play gunshot sound
audio.PlayOneShot(audio.clip);
// show muzzle flash for X seconds
muzzleFlashAgent.active = true;
yield WaitForSeconds(0.05);
muzzleFlashAgent.active = false;
// wait a second or two before firing again
yield WaitForSeconds(Random.Range(1.0, 2.0));
}
}
// wait till next frame to test again
yield;
}
}
this is the PlayerShot which destroys the gameobject.
// the sound to play when the player is shot
public var shotSound:AudioClip;
// the number of lives
public var lives:int = 3;
/**
Player has been shot
*/
function Shot ()
{
// play the shot audio clip
audio.PlayOneShot(shotSound);
// reduce lives
lives--;
// reload the level if no lives left
if (lives == 0)
{
// destroy the crosshair
Destroy(GetComponent(CrossHair));
// add the camera fade (black by default)
iTween.CameraFadeAdd();
// fade the transparency to 1 over 1 second and reload scene once complete
iTween.CameraFadeTo(iTween.Hash("amount", 1, "time", 1, "oncomplete", "ReloadScene", "oncompletetarget", gameObject));
}
}
/**
Reload the scene
*/
function ReloadScene()
{
// reload scene
Application.LoadLevel("MainMenu");
}
The script attached to the enemy along with the BasicAI is the SoldierShot Script which destroys the gameobject. below is the script.
public var ragdoll:GameObject;
/**
Function called to kill the soldier
*/
function Shot()
{
// instantiate the ragdoll at this transform's position and rotation
Instantiate(ragdoll, transform.position, transform.rotation);
Destroy(GetComponent(BasicAI));
// destroy the animated soldier gameobject
Destroy(gameObject);
}
It seems that you keep a reference to the destroyed GameObject (or am I wrong?).
As opposed to what happens in common C# (and probably javascript) programs that when you have a reference to an object, it will never be garbage collected, in Unity if you destroy the object all your references to it will go to null.
Easy fix, you could delete the gameObject in the "Game Level" and instantiate your soldier on the fly with:
var instance : GameObject = Instantiate(Resources.Load("Soldier"));
You just need to make a Resources folder in your project folder and put the prefab for your solder into it.