Unity3D Version 4.5.3f3 Pro.
I'm new to Unity and C# and I'm struggling to understand why my object isn't updating (moving) to the new Vector3 value I'm trying to send.
Background: I'm passing in an array of 3 values using sockets into unity. I receive the values just fine when doing a Debug.Log of the data. The data updates on a set time incrementing each second (ie. "Position" : "-10, 10, 10" a second later "Position" : "-11, 11, 11" and so on)
I have struggled understanding Vector3 but have come up with this.
Empty object with Script:
public void PlayerMove(SocketIOEvent e)
{
Debug.Log("e.data: " + e.data);
string newVectorString = e.data.ToString ();
Debug.Log("newVectorString: " + newVectorString);
string[] temp = newVectorString.Split(',');
float x = float.Parse(temp[0]);
float y = float.Parse(temp[1]);
float z = float.Parse(temp[2]);
Vector3 newPosition = new Vector3(x,y,z);
otherPlayer.GetComponent<OtherPlayer>().startPosition = newPosition;
}
Results of the two logs (which are constantly updating) are:
e.data: {"Position":"-19,19,0"}
UnityEngine.Debug:Log(Object)
and
newVectorString: {"Position":"-19,19,0"}
UnityEngine.Debug:Log(Object)
So I'm receiving data yay! and I have moved it into string 'newVectorString'. I'm then attempting to create a new vector by splitting the string and passing that over GetComponent as follows...
otherPlayer.GetComponent<OtherPlayer>().startPosition = newPosition;
Next part.
Object "otherPlayer" with script 'OtherPlayer.cs':
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class OtherPlayer : MonoBehaviour
{
public Vector3 startPosition;
public Vector3 currentPosition = Vector3.zero;
void Start ()
{
}
void Update ()
{
Debug.Log("Is there anybody out there??: " + startPosition.ToString ());
if (startPosition != Vector3.zero)
{
currentPosition = startPosition;
startPosition = Vector3.zero;
transform.position = Vector3.Lerp (transform.position, currentPosition, 50 * Time.deltaTime);
}
else
{
transform.position = Vector3.Lerp (transform.position, currentPosition, 50 * Time.deltaTime);
}
}
}
The result of the latest Debug 'startPosition.ToString ()':
Is there anybody out there??: (0.0, 0.0, 0.0)
UnityEngine.Debug:Log(Object)
How can I pass these values in correctly
How can I confirm they are in vector3 format
Thank you for your answers.
Vector3 vec = otherPlayer.position;
this will copy the value of position into vec. Now you have two independent duplicates. Changing one will not affect the other.
You need to keep a reference to PlayerMove in OtherPlayer and track the changes of position.
public class OtherPlayer : MonoBehaviour{
private OtherScript script= null;
private void Start(){
this.script= GetOtherScript(); // you should know how to get it
}
void Update ()
{
Debug.Log("Is there anybody out there??: " + this.script.startPosition.ToString ());
if (startPosition != Vector3.zero)
{
currentPosition = this.script.startPosition;
startPosition = Vector3.zero;
transform.position = Vector3.Lerp (transform.position, currentPosition, 50 * Time.deltaTime);
}
else
{
transform.position = Vector3.Lerp (transform.position, currentPosition, 50 * Time.deltaTime);
}
}
}
Don't worry about performance because one level higher nor memory, this is absolutely nothing.
EDIT: To explain further on why:
Consider you have a paper A with (10,10,10) and you take paper B and read the values and write them onto paper B. Now A and B read (10,10,10). Change paper A to (20,20,20), paper B has no reason to change automatically, so it is still (10,10,10). This is what you are doing.
Vector3 currentPosition = newPosition;
To fix the issue, take paper A and write (10,10,10) on it, then put it on the desk. Take a paper O and write (desk) on it. Now take a paper W and write (O, Paper B) on it. W has a method that will use O to write on B. So it goes to read on O, O tells desk, then you go the desk and read (10,10,10) and write that on B. All is well.
You update A to (20,20,20), paper W runs the method again, it goes to O that tells Desk, you go to the desk to find A and read (20,20,20) and writes it over on B. Tadaaaa. It works fine. As you can see it is a bit slower but nothing that will affect your performance. This is the case I tell to use.
Vector3 currentPosition = scriptTransform.position;
scriptTransform is the paper O position is the paper A on the desk. currentPosition is the paperB. W is the OtherPlayer script.
You OtherPlayer script seems alright: it will smoothly move the gameObject to a certain position whenever you edit startPosition (although you didn't name your variable in a very understandable way:
startPosition should be something like newTargetPosition
currentPosition should be something like targetPosition
transform.Position is always the exact position of the object (in case it wasn't clear for you)
What I think you should check: (see the comments)
public void PlayerMove(SocketIOEvent e)
{
Debug.Log("e.data: " + e.data);
string newVectorString = e.data.ToString ();
Debug.Log("newVectorString: " + newVectorString);
// CHECK THIS:
// temp[0] is equal to {"Position":"-19 --> Won't parse
string[] temp = newVectorString.Split(',');
float x = float.Parse(temp[0]);
float y = float.Parse(temp[1]);
float z = float.Parse(temp[2]);
Vector3 newPosition = new Vector3(x,y,z);
// Check this: print new position to see if it is correct
// Check if otherPlayer is the object you expect to have here
otherPlayer.GetComponent<OtherPlayer>().startPosition = newPosition;
}
Related
I've been having trouble with Quaternion lerps. I'm simply looking to rotate my character 90 degrees based on their current rotation. The script below executes that almost perfectly, except for the fact that my character rotates a full 90 degrees long before rotationTime reaches the max value of 1. For some reason, the value of rotationTime is not properly synced with the progress of the lerp, and I can't seem to figure out why. What am I missing?
public class Movement : MonoBehaviour
{
bool Rotating = false;
Quaternion targetRotation;
public float rotationTime = 0f;
public float speed = 0.1F;
private Rigidbody rb;
private void Awake(){
rb = GetComponent<Rigidbody>();
}
public void Turn(InputAction.CallbackContext context){ //executes when 'E' is pressed
if (context.started && Rotating == false) {
targetRotation = Quaternion.Euler(0,transform.eulerAngles.y + 90f,0);
rotationTime = 0;
Rotating = true;
}
}
void Update() {
if (Rotating == true) {
rotationTime = rotationTime + Time.deltaTime * speed;
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation,rotationTime);
};
if (rotationTime > 1) {
Rotating = false;
}
}
}
I suspect that the main issue here is that you are using transform.rotation in your Quaternion.Lerp. You change it every frame, so every frame the start rotation will be closer to the target rotation. You should add some kind of _initRotation variable, and set it to transform.rotation in your Turn method. I mean something like this:
public void Turn(InputAction.CallbackContext context)
{
if (context.started && Rotating == false)
{
targetRotation = Quaternion.Euler(0,transform.eulerAngles.y + 90f,0);
_initRotation = transform.rotation;
rotationTime = 0;
Rotating = true;
}
}
...
void Update()
{
...
transform.rotation = Quaternion.Lerp(_initRotation, targetRotation,rotationTime);
...
}
Also, you have a logical issue with the lerp function. It does not affect the result in your particular case, but it can cause problems later.
You increment your rotation time by Time.deltaTime * speed every frame, it is not correct as it is not time passed from the start of the rotation.
According to the Quaternion.Lerp documentation, t value is always clamped to [0, 1]. So it is more convenient to use normalized time value instead of abstract speed value (right now it has no physical sense, it is just a multiplier).
It would be much clearer to use something like this:
void Update()
{
...
rotationTime += Time.deltaTime;
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, rotationTime / fullRotationTime);
...
}
Generally, I almost always work with carriers, and I recommend that you do the same.
public class Movement : MonoBehaviour
{
bool Rotating = false;
Vector3 targetRotation;
public float rotationTime = 0f;
public float speed = 0.1F;
private Rigidbody rb;
private void Awake()
{
rb = GetComponent<Rigidbody>();
}
public void Turn(InputAction.CallbackContext context)
{ //executes when 'E' is pressed
if (context.started && Rotating == false)
{
targetRotation = new Vector3(0, transform.eulerAngles.y + 90f, 0);
rotationTime = 0;
Rotating = true;
}
}
void Update()
{
if (Rotating == true)
{
rotationTime = rotationTime + Time.deltaTime * speed;
transform.eulerAngles = Vector3.Lerp(transform.eulerAngles, targetRotation, rotationTime);
};
if (rotationTime > 1)
{
Rotating = false;
}
}
}
Also consider using coroutines for more readable code and to improve performance.
Good work!
As it was mentioned already by this answer one of your main issues is that every time you use a new transform.rotation as interpolation start point
=> Your rotation starts fast and then gets slower and slower the closer you reach the target value.
There other issues here though:
You are using the transform.rotation.eulerAngles! From the API:
When using the .eulerAngles property to set a rotation, it is important to understand that although you are providing X, Y, and Z rotation values to describe your rotation, those values are not stored in the rotation. Instead, the X, Y & Z values are converted to the Quaternion's internal format.
When you read the .eulerAngles property, Unity converts the Quaternion's internal representation of the rotation to Euler angles. Because, there is more than one way to represent any given rotation using Euler angles, the values you read back out may be quite different from the values you assigned. This can cause confusion if you are trying to gradually increment the values to produce animation.
To avoid these kinds of problems, the recommended way to work with rotations is to avoid relying on consistent results when reading .eulerAngles particularly when attempting to gradually increment a rotation to produce animation. For better ways to achieve this, see the Quaternion * operator.
And then in general, since I see there is a Rigibody involved you shouldn't set or read anything directly via the Transform component at all but rather only go through that rigidbody! Otherwise you might break/fight against the physics resulting in strange behavior and breaking collision detection.
In your case in my eyes it is way easier to control the entire thing in a Corouine which avoids the need for all the class fields and imho is way easier to understand, control and maintain:
public class Movement : MonoBehaviour
{
public float speed = 0.1F;
[SerializeField] private Rigidbody rb;
private bool isRotating = false;
private void Awake()
{
if(!rb) rb = GetComponent<Rigidbody>();
}
public void Turn(InputAction.CallbackContext context)
{
//executes when 'E' is pressed
if (context.started && !isRotating)
{
StartCoroutine(RotateRoutine());
}
}
private IEnumerator RotateRoutine()
{
// little safety check first
if(Mathf.Approximately(speed, 0f)
{
yield break;
}
// Just to be really sure you avoid any concurrent routines
if (isRotating)
{
yield break;
}
// Lock so no other routine ca be started
isRotating = true;
// wait until we are in the next physics frame
yield return new WaitForFixedUpdate();
// store the initial rotation -> go throug the rigibody not the transform
var start = rb.rotation;
var end = start * Quaternion.Euler(0, 90f, 0);
var duration = 1 / speed;
for (var rotationTime = 0f; rotationTime < duration; rotationTime += Time.deltaTime)
{
// this would be a linear growing factor from 0 to 1
var factor = rotationTime / duration;
// optionally you could add ease in and out at the ends
// basically you can add whatever curve function you like to grow from 0 to 1 within the given rotationTime
factor = Mathf.SmoothStep(0, 1, rotationTime);
// interpolate from start to end while the factor grows from 0 to 1
var rotation = Quaternion.Slerp(start,end, factor);
// again for rigibdoy rather do this instead of going through transform
rb.MoveRotation();
// again wait for the next physics update
yield return new WaitForFixedUpdate();
}
// Just to be sure to end on clean values
rb.MoveRotation(end);
// release the lock for the next routine to start
isRotating = false;
}
}
I am a freshman design student and they've asked us to create a game on unity3D without much training on it so needless to say I don't know much except for the super basic stuff. I don't know anything about c# and I've been having an issue making a gameobject teleport. I've spent 6 hours searching for a solution online and the only conclusion I got to was that my object is probably having issues teleporting because of the way I am controlling it - something to do with the controller remembering the last position before the teleport and returning to it. I have no idea how to fix it though.
So this is what my scene looks like: I have a sphere as my character, I move it to this other object that has a collider as trigger which then teleports my sphere to a different point (black object) on the terrain. As soon as my object reaches there, it starts sliding back to the point where the teleport happened. I even tried edit > project settings > physics > auto sync transforms as many suggested that and it worked for them.
This is the code by which I control my player:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyPlayer : MonoBehaviour
{
public float speed = 1;
public float spacing = 1;
private Vector3 pos;
// Use this for initialization
void Awake()
{
pos = transform.position;
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.W))
pos.x += spacing;
if (Input.GetKeyDown(KeyCode.S))
pos.x -= spacing;
if (Input.GetKeyDown(KeyCode.D))
pos.z -= spacing;
if (Input.GetKeyDown(KeyCode.A))
pos.z += spacing;
transform.position = Vector3.MoveTowards(transform.position, pos, speed * Time.deltaTime);
}
}
and I also have a camera that follows the sphere using this code
using UnityEngine;
using System.Collections;
public class CompleteCameraController : MonoBehaviour {
public GameObject player; //Public variable to store a reference to the player game object
private Vector3 offset; //Private variable to store the offset distance between the player and camera
// Use this for initialization
void Start ()
{
//Calculate and store the offset value by getting the distance between the player's position and camera's position.
offset = transform.position - player.transform.position;
}
// LateUpdate is called after Update each frame
void LateUpdate ()
{
// Set the position of the camera's transform to be the same as the player's, but offset by the calculated offset distance.
transform.position = player.transform.position + offset;
}
}
and I have another code on the camera that makes me be able to look around using my mouse
using UnityEngine;
using System.Collections;
public class FlyCamera : MonoBehaviour
{
/*
Writen by Windexglow 11-13-10. Use it, edit it, steal it I don't care.
Converted to C# 27-02-13 - no credit wanted.
Simple flycam I made, since I couldn't find any others made public.
Made simple to use (drag and drop, done) for regular keyboard layout
wasd : basic movement
shift : Makes camera accelerate
space : Moves camera on X and Z axis only. So camera doesn't gain any height*/
float mainSpeed = 700.0f; //regular speed
float shiftAdd = 950.0f; //multiplied by how long shift is held. Basically running
float maxShift = 2000.0f; //Maximum speed when holdin gshift
float camSens = 0.25f; //How sensitive it with mouse
private Vector3 lastMouse = new Vector3(255, 255, 255); //kind of in the middle of the screen, rather than at the top (play)
private float totalRun = 1.0f;
void Update()
{
lastMouse = Input.mousePosition - lastMouse;
lastMouse = new Vector3(-lastMouse.y * camSens, lastMouse.x * camSens, 0);
lastMouse = new Vector3(transform.eulerAngles.x + lastMouse.x, transform.eulerAngles.y + lastMouse.y, 0);
transform.eulerAngles = lastMouse;
lastMouse = Input.mousePosition;
//Mouse camera angle done.
//Keyboard commands
float f = 0.0f;
Vector3 p = GetBaseInput();
if (Input.GetKey(KeyCode.LeftShift))
{
totalRun += Time.deltaTime;
p = p * totalRun * shiftAdd;
p.x = Mathf.Clamp(p.x, -maxShift, maxShift);
p.y = Mathf.Clamp(p.y, -maxShift, maxShift);
p.z = Mathf.Clamp(p.z, -maxShift, maxShift);
}
else
{
totalRun = Mathf.Clamp(totalRun * 0.5f, 1f, 1000f);
p = p * mainSpeed;
}
p = p * Time.deltaTime;
Vector3 newPosition = transform.position;
if (Input.GetKey(KeyCode.Space))
{ //If player wants to move on X and Z axis only
transform.Translate(p);
newPosition.x = transform.position.x;
newPosition.z = transform.position.z;
transform.position = newPosition;
}
else
{
transform.Translate(p);
}
}
private Vector3 GetBaseInput()
{ //returns the basic values, if it's 0 than it's not active.
Vector3 p_Velocity = new Vector3();
if (Input.GetKey(KeyCode.W))
{
p_Velocity += new Vector3(0, 0, 1);
}
if (Input.GetKey(KeyCode.S))
{
p_Velocity += new Vector3(0, 0, -1);
}
if (Input.GetKey(KeyCode.A))
{
p_Velocity += new Vector3(-1, 0, 0);
}
if (Input.GetKey(KeyCode.D))
{
p_Velocity += new Vector3(1, 0, 0);
}
return p_Velocity;
}
}
Please let me know if there's a specific part of my code that I need to edit to resolve this or alternatively if you have a different code that won't give me this issue, that would make my life so much easier. If I need to edit something or you're sharing a code, please respond with the complete (corrected) code because otherwise I will just be even more confused.
I know this is a super long post and I am sorry but I am really desperate. It's been really hard studying online and basically having to teach myself all of this. This is for a final project so I will really appreciate any help you can throw my way. Thank you for reading and thanks for any help in advance.
EDIT: The teleport code is executing fine because I do teleport to the chosen location, I just end up sliding back to the point which I teleported from.
This is the teleporting code I am using.
using UnityEngine;
using System.Collections;
public class Teleport : MonoBehaviour
{
public GameObject ui;
public GameObject objToTP;
public Transform tpLoc;
void Start()
{
ui.SetActive(false);
}
void OnTriggerStay(Collider other)
{
ui.SetActive(true);
if ((other.gameObject.tag == "Player") && Input.GetKeyDown(KeyCode.E))
{
objToTP.transform.position = tpLoc.transform.position;
}
}
void OnTriggerExit()
{
ui.SetActive(false);
}
}
Ok, so the main reason why your character is drifting back to original position is that the pos variable in the MyPlayer script stays the same after teleporting.
Remedy for that will be changing pos variable from Teleport script after objToTP.transform.position = tpLoc.transform.position;. Something like objToTP.gameobject.GetComponent<MyPlayer>().pos = tpLoc.transform.position;
But make sure that objToTP has component MyPlayer and pos in MyPlayer is public.
Once again: it's a simple way to resolve your problem. In a real project you should create more flexible architecture, but that's a different story.
I believe that you sliding back because you moving player with transform.position = Vector3.MoveTowards in Update().
And you moving it to coordinates that was got from your input
Problem:
If I get the movement to work correctly, then the collision meshes are not detected. If I get the collision meshes detected, then the movement doesn't work correctly.
Brief summary of project:
I have a 3D environment with non-moveable objects (with collider meshes) and a moveable gameobject (rigid body with x2 box colliders) that I am controlling using haptic devices (basically a 3D joystick) through a UDP connection in an C++ app that I have put together and is running while the Unity application runs. The communication between the haptic devices and unity is perfectly fine. I am using the position information passed from the haptic device as my variables for moving my gameobject. Again, The position data arrives to Unity just fine; the method for using the position data with appropriate conditions and functions within Unity is where I am currently stuck.
Things I've tried:
If I use transform.localPosition (hapticDevicePosition); then the movement is great, but it ignores the colliders and passes through everything. I read online and understand that transform.localPosition will basically move my object on top of other objects without regards to physics. I also read that I may be able to introduce a ray that is like 0.000001 in front of my object such that it prevents movement if the ray interacts with any other object. This might be a way to still be able to use transform.localPosition? I'm not sure and I've never used rays so it would be difficult for me to set that script up correctly.
I've tried AddForce. This behaves very oddly. It only gives me 2 force outputs instead of 3...i.e., I can only move in 2 of the 3 axis. I don't understand why its behaving this way. The colliders are detected, however.
I've tried rb.MovePosition (rb.position + posX + posY + posZ) and various combinations of *Time.timeDelay and *speed as well. This also doesn't work correctly. The colliders are detected, but the movement either doesn't work at all, or is not working correctly.
Conclusion:
I've played with my script for the last 4 hours and some (not all) of the things that I attempted are commented out so they are still visible (please see code attached below). I will be reading more online explanations and trying out different code and update here if I work out a solution. If anyone has some pointers or suggestions, in the meantime, I would greatly appreciate it.
Thanks!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FalconPegControl_2 : MonoBehaviour {
// Define needed variables
private TestUDPConnection udpListener;
public Vector3 realObjectCurrentPos;
private Vector3 realObjectLastPos;
public Vector3 realObjectCurrentRot;
private Vector3 realObjectLastRot;
public Vector3 realObjectPosChange;
public Vector3 realObjectRotChange;
private Quaternion rotation;
//public float pi = 3.14f;
private Rigidbody rb;
private int control = 0;
public bool collisionOccurred = false;
//public float thrust = 1000;
//public CalibrationManager calibrationManager;
// Use this for initialization
void Start () {
udpListener = GetComponentInParent<TestUDPConnection>();
collisionOccurred = false;
rb = GetComponent<Rigidbody> ();
SharedRefs.falconPegControl = this;
}
public void OffControl ()
{
control = 0;
}
public void CollisionDuplicateFix ()
{
collisionOccurred = true;
}
// Update is called once per frame
void FixedUpdate () {
//WITHOUT UNITY AXIS CONVERSION:
//realObjectCurrentPos[0] = udpListener.xPosReal; //[m]
//realObjectCurrentPos[1] = udpListener.yPosReal; //[m]
//realObjectCurrentPos[2] = udpListener.zPosReal; //[m]
//===============================
//Unity axis conversions:
//CHAI3D --> Unity
//(x, y, z) --> (x, -z, y)
//CHAI3D: realObjectCurrentPos[0], [1], [2] is CHIA3D (x, y, z)
//Also, to compensate for the workspace available to the Falcon Device (~0.04, ~0.06, ~0.06)
//adding a value of x10 allows it to reach the default hemisphere successfully
//updated comment: the sign values that work (-, +, -)
//===============================
//Unity conversion for rotation (using Falcon devices)
//Since one falcon is for translation and the other is for rotation,
//the rotation information is a conversion of translational information
//in other words, max range of (~0.04, ~0.06, ~0.06) has been converted into a max range of (90, 90, 90)
//using basic algebra (i.e., (90/0.04))
//thus giving the user the full range of 180 degrees (from 90 degrees to -90 degrees)
realObjectCurrentPos[0] = udpListener.xPosReal * (-5); //[m]
realObjectCurrentPos[1] = udpListener.zPosReal * (5); //[m]
realObjectCurrentPos[2] = udpListener.yPosReal * (-5); //[m]
realObjectCurrentRot [0] = udpListener.xRot * (90f / 0.04f); //degrees
realObjectCurrentRot [1] = udpListener.yRot * (90f / 0.06f); //degrees
realObjectCurrentRot [2] = udpListener.zRot * (90f / 0.06f); //degrees
if (Input.GetKeyDown ("1")) {
control = 1;
SharedRefs.stopWatch.startTimer ();
}
if (Input.GetKeyDown ("space"))
{
OffControl ();
}
if (control==1)
{
Vector3 posUnity = new Vector3 (realObjectCurrentPos[0], realObjectCurrentPos[1], realObjectCurrentPos[2]);
rb.AddForce (posUnity);
//Vector3 tempVect = new Vector3(realObjectCurrentPos[0], realObjectCurrentPos[1], realObjectCurrentPos[2]);
//Vector3 startPoint = new Vector3 (0f, 0.0225f, 0f);
//tempVect = tempVect * speed * Time.deltaTime;
//transform.localPosition = realObjectCurrentPos; //[m]
//var unityX = Vector3.Scale (posTemp, Vector3.right);
//var unityY = Vector3.Scale (posTemp, Vector3.up);
//var unityZ = Vector3.Scale (posTemp, Vector3.forward);
//Vector3 unityX = new Vector3 (Vector3.Scale (posTemp, Vector3.right), Vector3.Scale (posTemp, Vector3.up), Vector3.Scale (posTemp, Vector3.forward));
//Vector3 unityY = new Vector3 (Vector3.Scale (posTemp, Vector3.up));
//Vector3 unityZ = new Vector3 (Vector3.Scale (posTemp, Vector3.forward));
//rb.MovePosition (rb.position + unityX + unityY + unityZ);
//transform.localPosition = (startPoint + tempVect); //[m]
transform.localRotation = Quaternion.Euler(realObjectCurrentRot); //[m]
realObjectLastPos = realObjectCurrentPos;//[m]
realObjectLastRot = realObjectCurrentRot;//[m]
realObjectPosChange = realObjectCurrentPos - realObjectLastPos; //[m]
realObjectRotChange = realObjectCurrentRot - realObjectLastRot;
}
else if (control==0)
{
Vector3 stop = new Vector3 (0, 0, 0);
rb.constraints = RigidbodyConstraints.FreezePositionZ | RigidbodyConstraints.FreezeRotationZ;
rb.constraints = RigidbodyConstraints.FreezePositionX | RigidbodyConstraints.FreezeRotationX;
rb.constraints = RigidbodyConstraints.FreezePositionX | RigidbodyConstraints.FreezeRotationX;
rb.velocity = (stop);
}
}
}
Also, updated from #Ali Baba's comments:
I haven't had time to test the other methods yet, but by using AddForce and playing with the drag and a force modifier variable, I was able to get control over all three axes (actually 6DOF because I also have rotational control from a 2nd external device) and I also have much better control over my gameobject than before (specifically due to the drag and force modifier variable adjustments). This may be the best solution, but I originally needed to get my position to change based on the position of the external devices that I am using. I'm adding a basic, slimmed down, adjusted code which uses AddForce and allows for keycontrol adjustments of the drag and my force modifier variable in case other beginners see this thread also. In the meantime, I will try to get the other functions (MovePosition, etc) working and update on the results.
Slim, basic drag/variable testing code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Real_Controller : MonoBehaviour {
// Define needed variables
private TestUDPConnection udpListener;
public Vector3 realObjectCurrentPos;
public Vector3 realObjectCurrentRot;
private Quaternion rotation;
private Rigidbody rb;
private float increaseForce = 23;
// Use this for initialization
void Start () {
udpListener = GetComponentInParent<TestUDPConnection>();
rb = GetComponent<Rigidbody> ();
rb.drag = 1.24f;
}
// Update is called once per frame
void FixedUpdate () {
if (Input.GetKeyDown ("q"))
{
rb.drag -= 0.1f;
Debug.Log ("drag is: " + rb.drag);
}
if (Input.GetKeyDown ("w"))
{
rb.drag += 0.1f;
Debug.Log ("drag is: " + rb.drag);
}
if (Input.GetKeyDown ("a")) {
increaseForce -= 1f;
Debug.Log ("increased force is: " + increaseForce);
}
if (Input.GetKeyDown ("s")) {
increaseForce += 1f;
Debug.Log ("increase force is: " + increaseForce);
}
realObjectCurrentPos[0] = udpListener.xPosReal * (-increaseForce); //[m]
realObjectCurrentPos[1] = udpListener.zPosReal * (increaseForce); //[m]
realObjectCurrentPos[2] = udpListener.yPosReal * (-increaseForce); //[m]
Vector3 forceDirection = realObjectCurrentPos - transform.localPosition;
rb.AddForce (forceDirection * forceDirection.magnitude);
realObjectCurrentRot [0] = udpListener.xRot * (90f / 0.04f); //degrees
realObjectCurrentRot [1] = udpListener.yRot * (90f / 0.06f); //degrees
realObjectCurrentRot [2] = udpListener.zRot * (90f / 0.06f); //degrees
transform.localRotation = Quaternion.Euler(realObjectCurrentRot); //[m]
}
}
Instead of placing the gameObject at the exact position of your controller, you could try applying a force in the direction of the position you want your gameObject to be in:
if (control==1)
{
Vector3 forceDirection = realObjectCurrentPos - transform.localPosition;
rb.AddForce (forceDirection);
transform.localRotation = Quaternion.Euler(realObjectCurrentRot)
}
The force applied here is linear to the distance between the position of the gameObject and the real object, so this basically behaves like a spring. You should try multiplying the force by different factors and test:
rb.AddForce (forceDirection * 0.5f);
Or scale it quadratically:
rb.AddForce (forceDirection * forceDirection.magnitude);
Whatever feels best
I have one really big issue with my game . When i try press play when he spawn first prefab everything is correct X position is -0.604f , z is -1.49f..But when i spawn second object the second and the third object get spawned on wrong position of x and z.
On the second object X position is not good ( in this case second Z position of object is -1.208 and i need here same value as before -0.604) I really don't know why this happens and I don't know how i can change Z position when i spawn third object in other side so if first two objects are on right and left and when i spawn third object he get Z position of -4.968 which is not correct because I need on third position on Z = -1.655984 and i am getting on third object z position of ~-5f.
Here is my code to spawn objects..
public class StackPiecesSpawner : Singleton<StackPiecesSpawner> {
public GameObject stackPrefab;
[SerializeField] public Transform StackPieceSpawnPoint;
[SerializeField] public float PieceSpawnSize = 0.1f;
[SerializeField] public float PieceSpawnWidth = 1.0f;
[SerializeField] private float speed = 1f;
public Vector3 OriginalSpawnPointPosition;
protected override void Awake()
{
base.Awake();
OriginalSpawnPointPosition = StackPieceSpawnPoint.position;
}
public void SpawnNextStackPiece()
{
//StackPieceSpawnPoint.position += new Vector3(0f,PieceSpawnSize,0f);
StackPieceSpawnPoint.position += new Vector3(0.604f, 0.1f, -1.656f);
//GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);
GameObject go = GameObject.Instantiate(stackPrefab);
//go.transform.localScale = new Vector3(PieceSpawnWidth,PieceSpawnSize,PieceSpawnWidth);
go.transform.localScale = new Vector3(0.4f, 0.4f,0.05f);
go.transform.position = nextSpawnPosition();
go.AddComponent<Rigidbody>();
go.GetComponent<Rigidbody>().isKinematic = true;
go.AddComponent<StackPiece>();
go.transform.parent = transform;
go.transform.name = "StackPiece_" + (StackPiecesManager.StackPieces.Count + 1);
StackPiece piece = go.GetComponent<StackPiece>();
piece.TargetTransform = StackPieceSpawnPoint;
piece.Speed = speed;
StackPiecesManager.Instance.AddNewPiece(piece);
}
private Vector3 nextSpawnPosition()
{
System.Random rnd = new System.Random();
int num = rnd.Next(0, 100);
Vector3 modifier = new Vector3();
modifier = num < 50 ? Vector3.back : Vector3.left;
modifier *= StackPiecesManager.Instance.SpawnDistance;
Vector3 returned = (StackPieceSpawnPoint.position - modifier) + Vector3.up * (StackPiecesManager.Instance.ExtraHeight);
// returned = (StackPieceSpawnPoint.position) + Vector3.up * (StackPiecesManager.Instance.ExtraHeight);
return returned;
}
public void Reset()
{
StackPieceSpawnPoint.position = OriginalSpawnPointPosition;
}
}
I cannot completely answer this question as not all the code and context is available, however, I could maybe point you in the right direction.
Parent-child relationship
In the SpawnNextStackPiece you instantiate a prefab and have its current parent transform equal the current transform(of the object this script is attached to), check if the transform you're changing the parent to is shifting each time a prefab is instantiated. I tend to avoid changing parent transforms, I usually try to attach it to different parents and change the actual objects offset not the parents, it can be easier to manage. I suspect go.transform.parent = transform; is causing a problem.
Diagnosing the problem through zeroing out values
Try forcefully making the z-axis 0 so you can see how much it is getting offset by each time it spawns, that may make diagnosing the problem much easier. Do this with StackPieceSpawnPoint.position = new Vector3(0.604f, 0.1f,0f);
I am trying to animate a platform prefab that is spawned (the platform object itself is within an empty because its position changes) DOWN when a Runner object collides with it (onCollisonEnter) and UP on collision exit.
I have followed answers given to my old question here verbatim- Unity, C# - cannot make object move down from current y position ONCE on collision enter? but can't get animations using the Animator to work despite having this in my animator as directed:
One person who answered suggested I use Lerp to animate the platform prefab entirely using code. I have researched Lerp but as I need 2 separate states for Down/Idle Down (to make platform stay down) and the same for up/idle up I do not know how to do this.
How can I achieve this effect using Lerp programmatically? Is it possible to achieve the effect I am going for?
ERROR:
EDIT:
how i spawn (dequeue then enqueue) my platforms:
nextPosition += new Vector3 (
Random.Range (minGap.x, maxGap.x) + scale.x,
Random.Range (minGap.y, maxGap.y),
Random.Range (minGap.z, maxGap.z));
Transform o = objectQueue.Dequeue();
o.localScale = scale;
o.localPosition = position;
//o.localEulerAngles = rotation;
o.gameObject.SetActive (true);
int materialIndex = Random.Range(0, materials.Length);
o.GetComponent<Renderer>().material = materials[materialIndex];
o.GetComponent<Collider> ().material = noFrictionMaterial;
platform newScript = new o.GetComponent<platform>(); //getting an error here when I tried to implement your code
objectQueue.Enqueue (o);
if(nextPosition.y < minY){
nextPosition.y = minY + maxGap.y;
}
else if(nextPosition.y > maxY){
nextPosition.y = maxY - maxGap.y;
}
This is what the vectors should ALWAYS be and what I set them at originally in start():
up = new Vector3 (transform.position.x, transform.position.y, transform.position.z);
down = new Vector3 (transform.position.x, transform.position.y-2f, transform.position.z);
Since the platforms aren't actually spawning, are you sure that's the error? Can you help me?
This should work for you:
using UnityEngine;
using System.Collections;
public class Collision : MonoBehaviour {
public Vector3 up, down;
public bool isUp = false;
public float speed = 5f;
float startTime,
length;
bool isMoving = false;
void Start () {
//initialise the platform to a position
if (isUp) transform.position = up;
else transform.position = down;
length = Vector3.Distance(up, down);
}
void Update()
{
if (isUp)
{
//move Down
transform.position = Vector3.Lerp(up, down, ((Time.time - startTime) * speed) / length);
}
else
{
//move Up
transform.position = Vector3.Lerp(down, up, ((Time.time - startTime) * speed) / length);
}
}
//move down
void OnCollisionEnter(Collision col)
{
if (!isMoving)
{
startTime = Time.time;
isMoving = true;
}
}
//move up
void OnCollisionExit(Collision col)
{
if (!isMoving)
{
startTime = Time.time;
isMoving = true;
}
}
}
The thing you have to watch out for is making sure that the "runner" stays on the platform until it's on the bottom so that it can move up again. You could add a check so that it moves back up regardless with another bool it it doesn't work for you. Hope it helps :)
EDIT
What lerp (short for linear interpolation) does is pick points between two vectors based on the progression of the time component (0 to 1). So in order for the platform to go up and down it needs a starting point and an end point, in this case it's up or down (up to the upper position down for the other).
If you want to create a new platform without causing an error you need to set these values after creating it so that everything works correctly (if you leap from any position it will jump aggressively in the first step of the lerp). For example:
//after instantiating the new platform object
scriptName newObject = newGameobject.GetComponent<scriptName>();
newObject.up = (up Vector);
newObject.down = (down Vector);