I'm newbie in Unity3D scripting and trying to learn how it works internally for my simple projects, what are the best practices. Here I have a simple scene with a cube in it and trying to animate it, going left to certain point in time, then reversing back to negative value and then back in loop (on x axis). Direction is set in class public boolean property. By default it is negative value (which means it should go in positive direction, sorry for confusion). If it is positive it should go negative. However I have noticed that when I change this boolean value in Update method of script, it reverses back to original value? (when set to true, default is false). Then my object gets stack between going true and false and not moving in any direction. However if I set this property as static property in class, it does not resets and works just as intended (loops fine). I do not know why is it resets and here I'm completely confused.
public class CubeAnim : MonoBehaviour
{
public bool directionnegative;
// Update is called once per frame
void Update()
{
float nv = 25.0f * Time.deltaTime;
float posx = transform.position.x;
if (posx > 20.0f)
{
if (!directionnegative)
{
directionnegative = true;
}
}
else if (posx < -20.0f)
{
if (directionnegative)
{
directionnegative = false;
}
}
if(directionnegative)
{
nv = -(nv);
}
transform.Translate(nv, 0, 0);
deltaTime += (Time.deltaTime - deltaTime) * 0.1f;
float fps = 1.0f / deltaTime;
string log = "posx: " + transform.position.x + "\ndir: " + directionnegative + " transx: " + nv + "\nfps: " + Mathf.Ceil(fps).ToString();
Debug.Log(log);
}
}
And if I declare directionnegative as static bool script works fine and cube animation is properly going in one direction, then in another direction, then reverses:
public static bool directionnegative;
Related
So i'm currently making a game, and i've recently added a level editor, but the placing tool does not work how i wanted it to.
https://youtu.be/MuUvnVTL6eg
If you've watched this video, you've probably realized that the block placing works pretty much how placing rectangles in ms pain with alt does, and i want it to work like placing rectangles in ms pain without alt xd.
I'm using this code to place the block:
if (Input.GetKeyDown(KeyCode.Mouse0)){
startDrawPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
tmpObj = spawnObject(blocks[selected].gameObject, startDrawPos);
drawing = true;
}
if (Input.GetKey(KeyCode.Mouse0)){
Vector2 mPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 tmpScale = new Vector2(startDrawPos.x - mPos.x, startDrawPos.y - mPos.y);
tmpObj.transform.localScale = tmpScale;
}
if (Input.GetKeyUp(KeyCode.Mouse0))
{
drawing = false;
var scale = tmpObj.transform.localScale;
//Code below destroys the object if it's too small to avoid accidental placements
if (scale.x <= 0.1 && scale.x > -0.1 || scale.y <= 0.1 && scale.y > -0.1)
{
Destroy(tmpObj);
}
}
(All of this code is in the Update() function)
(spawnObject function just instantiates the object prefab)
There is a bit more code but it has nothing to do with the position of the block, it just detect which block is selected and decides if it can be resized or not.
I solved this problem. But because your complete script is not in question, I rebuilt the code with IEnumerator, Here, by pressing the left mouse button, IEnumerator is activated and all commands are grouped in one method to make the code more efficient.
private void Update()
{
if (Input.GetKeyDown(KeyCode.Mouse0)) StartCoroutine(DrawRect());
}
How does the Desktop Rect formula work?
By running IEnumerator, the code first records the starting point of the mouse. It also makes a simple cube because I do not have access to your objects. Now until the mouse is pressed. Resize Rect to the difference between current and recorded points. The only thing is that to avoid ALT control, you have to place it between the current and initial points. The reason for adding the camera forward is to be seen in the camera.
cubeObject.transform.position = (startDrawPos + currentDrawPos) / 2;
The final structure of the DrawRect is as follows:
public IEnumerator DrawRect()
{
drawing = true;
var scale = Vector2.zero;
var startDrawPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
var cubeObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
while (Input.GetKey(KeyCode.Mouse0))
{
var currentDrawPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
cubeObject.transform.position = (startDrawPos + currentDrawPos) / 2 + Camera.main.transform.forward * 10;
scale = new Vector2(startDrawPos.x - currentDrawPos.x, startDrawPos.y - currentDrawPos.y);
cubeObject.transform.localScale = scale;
yield return new WaitForEndOfFrame();
}
if (scale.x <= 0.1 && scale.x > -0.1 || scale.y <= 0.1 && scale.y > -0.1) Destroy(cubeObject);
drawing = false;
}
I am creating an elevator of which constantly moves from Point A to Point B.
I cannot just use transform.position ( location here * speed here etc) since I have a player that has a Rigidbody, and if using that it would flicker my player out whenever I am on the elevator. I also tried parenting the Player whenever its on the elevator (and de-parents it when it jumps etc), that fixes the flickering however it somewhat bugs the player's jumping mechanism.
Last resort is using a Rigidbody to my elevator and moves it with this code:
private void FixedUpdate()
{
Vector2 l_mypos = new Vector2(transform.position.x, transform.position.y);
Vector2 l_target = new Vector2(_targetPoint.position.x, _targetPoint.position.y);
if (l_mypos != l_target)
{
MoveElevator(l_target);
Debug.Log(l_mypos + " - " + l_target);
}
else
Debug.Log("reached");
}
private void MoveElevator(Vector2 toTarget)
{
Vector2 direction = (toTarget - (Vector2)transform.position).normalized;
_elevatorRB.MovePosition((Vector2)transform.position + direction * _speed * Time.deltaTime);
}
This moves the elevator towards the direction given, however it doesn't reach the "reached" condition. I placed a debug.log there to see both mpos and target to see the differences. It ends with 0, 10, 0 - 0, 10, 0 meaning both my target and the elevator's position is already the same. However it doesn't reach the else condition, and the elevator keeps flickering at Point B.
The == operator for Vector2 uses an estimation of 0.00001 for equality.
However it is very possible that you overshoot the target in
(Vector2)transform.position + direction * _speed * Time.deltaTime
since your final velocity speed * Time.deltaTime is certainly greater than 0.00001. (Except your speed is smaller than 0.0006 which I doubt.)
The value you see in the Debug.Log is the result of a Vector3.ToString which uses human readable rounded values and does not show the actually float values! From the source code
public override string ToString()
{
return ToString(null, CultureInfo.InvariantCulture.NumberFormat);
}
public string ToString(string format)
{
return ToString(format, CultureInfo.InvariantCulture.NumberFormat);
}
public string ToString(string format, IFormatProvider formatProvider)
{
if (string.IsNullOrEmpty(format)) format = "F1"; // <- !! HERE !!
return UnityString.Format("({0}, {1}, {2})", x.ToString(format, formatProvider), y.ToString(format, formatProvider), z.ToString(format, formatProvider));
}
You should rather use Vector2.MoveTowards which avoids this overshooting.
The API of Vector3.MoveTowards actually explains it better then the one of Vector2.MoveTowards
Calculate a position between the points specified by current and target, moving no farther than the distance specified by maxDistanceDelta.
Use the MoveTowards member to move an object at the current position toward the target position. By updating an object’s position each frame using the position calculated by this function, you can move it towards the target smoothly. Control the speed of movement with the maxDistanceDelta parameter. If the current position is already closer to the target than maxDistanceDelta, the value returned is equal to target; the new position does not overshoot target. To make sure that object speed is independent of frame rate, multiply the maxDistanceDelta value by Time.deltaTime
private void FixedUpdate()
{
// Vector3 and Vector2 have implicit operators allowing to use
// both types exchangeably.
// In order actively to convert them you can simply typecast between them
var l_mypos = (Vector2) transform.position;
var l_target = (Vector2) _targetPoint.position;
if (l_mypos != l_target)
{
MoveElevator(l_target);
Debug.Log(l_mypos + " - " + l_target);
}
else
{
Debug.Log("reached");
}
}
private void MoveElevator(Vector2 toTarget)
{
var pos = Vector2.MoveTowards(transform.position, toTarget, _speed * Time.deltaTime);
_elevatorRB.MovePosition(pos);
}
I made a simple script that goes to one waypoint and then to the next.
My problem is that it seems to be a delay while going from waypoint1 to waypoint2 and i don't know why:
¿Why is that delay happening and how can i remove it?
using UnityEngine;
using System.Collections;
public class Missile : MonoBehaviour
{
public Vector3 finalTarget;
public Transform forwardObject;
public GameObject impactAreaPrefab;
float smoothingDelay = 0.1f;
bool fired = false;
bool powerPhase = true;
Vector3 currentTarget;
private void OnEnable() {
fire(new Vector3(-25.29f, 0.5f, -10.638f));
}
void fire(Vector3 coords) {
currentTarget = forwardObject.position;
finalTarget = coords;
Instantiate(impactAreaPrefab, finalTarget, Quaternion.identity);
fired = true;
}
void Update() {
if (!fired) {
return;
}
if (powerPhase && transform.position == currentTarget) {
powerPhase = false;
currentTarget = finalTarget;
smoothingDelay = 0.05f;
}
transform.position = Vector3.Lerp(transform.position, currentTarget, Time.deltaTime / smoothingDelay);
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(Vector3.RotateTowards(transform.forward, currentTarget, 1, 0.0f)), Time.deltaTime / smoothingDelay);
}
}
That's happening, because you're using lerp not exactly properly. If you want to get linear movement you should cache your first argument (position/rotation on beginning) and provide increasing third parameter. This delay is happening because if your bullet is very close to final position and it's still trying to get there, but your current distance |finalPos - transform.position| is so small that your step Time.deltaTime/smoothingDelay is almost not moving it.
Vector3 startPos;
Vector3 finalPos;
float currentT = 0.0f;
void Update()
{
currentT += Time.deltaTime;
transform.position = Vector3.Lerp(startPos, finalPos, currentT);
}
Checking if Vector3 == Vector3 is also not a good idea. Use pattern from above and check if currentT is larger or equal to 1. If it's true then you're on final position. You get also some control over movement duration by dividing currentT.
So First thing read these post to get better understanding of Lerp function-
https://chicounity3d.wordpress.com/2014/05/23/how-to-lerp-like-a-pro/
http://www.kinematicsoup.com/news/2016/8/9/rrypp5tkubynjwxhxjzd42s3o034o8
You should have a better understanding of lerp now.
In summary lerp does a really simple thing. Say u have two values X and Y. For this example let us give them some value, X = 0, Y = 1, Now you want to get a value some percent between them, like u want to get a value which is 50% from X and Y. You can guess the answer is 0.5. The lerp equation for this would be
Mathf.Lerp(0, 1, 0.5f);
So simply- given two values, x and y, Mathf.Lerp returns a value that is t percent between them.
Now to properly use Lerp you need to cache the position before starting the lerp. Most times I use a coroutine to get this effect works pretty well and then u can use animation curve to change the third parameter to create some crazy good effects. For example on using a animation curve just comment i will write it.
For this problem of yours you have two options-
1) Lerp like a pro using Animation curve to manipulate the speed. Remember u can create animation curves in runtime too.
IENumerator Move(Transform toMove, Vector3 end, float duration){
Vector3 startPos = toMove.position;
float elapsed = 0f;
while(elapsed < duration){
elapsed += Time.deltaTime;
toMove.position = Vector3.Lerp(startPos, end, elapsed / duration);//manipulate the last parameter to move the object linearly
yield return null;//basically wait for next frame
}
toMove.position = end;//after lerp ends
}
Now you can instead of duration use speed and then with it you calculate the time required and change the speed to make it faster
float distance = Vector3.Distance(startPos, end);
toMove.position = Vector3.Lerp(startPos, end, elapsed / (distance/(speed * multiplier)));
2) Use Vector3.MoveTowards - This function moves a point to a end point with a given maximum step, requires three paramters, (currentPosition, end, step), u can multiply step with variable to control the speed, both work really good.
Using this is much easier in most cases
Example-
float step = speed * Time.deltaTime;//to make it framerate independent
transform.position = Vector3.MoveTowards(transform.position, end, step * multiplier);
Hope this helps. I am sorry I was unable to format my answer properly, hopefully will get better at answering. Any edits to improve the answer are welcomed :)
I recommend using iTween for smooth movement.
I modified iTween at some point for me to be able to do anything I want. like this:
public static void Rotate (Transform transform, Vector3 target, float transitionTime, Action onEnd = null, bool ignoreTimescale = false, iTween.EaseType ease = iTween.EaseType.easeInOutQuad, float delay = 0)
{
Vector3 from, to;
from = transform.localEulerAngles;
to = target;
Action <object> onUpdateAction = (rotation =>
{
transform.localEulerAngles = (Vector3) rotation;
});
Action <object> onCompleteAction = (data =>
{
if (onEnd != null)
onEnd ();
});
Hashtable hash = new Hashtable ();
hash.Add ("from", from);
hash.Add ("to", to);
hash.Add ("time", transitionTime);
hash.Add ("delay", delay);
hash.Add ("easetype", iTween.EaseType.easeInOutQuad);
hash.Add ("ignoretimescale", ignoreTimescale);
hash.Add ("onupdate", onUpdateAction);
hash.Add ("oncomplete", onCompleteAction);
iTween.ValueTo (transform.gameObject, hash);
}
That gives me full control in a variety of scenarios.
Here is the code if you want to implement it.
https://drive.google.com/open?id=1nLEEYTp-q4Kfh2n3nWQJcMXmPNtVPLLP
this is what i want to achieve
I am currently trying to build a RADAR sensor on unity. I am currently using spherecast. How do i set the view angle of the sphere cast and also how do i read the angle at which an object is present in front of it.
What i have used now is Vector3.angle but this shows 160 degrees if the object is directly infront of the radar instead it should be showing 90 degrees.
Ill paste the code that i have implemented below
Any guidance is appreciated.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class spherecast : MonoBehaviour
{
Rigidbody rb;
public List<GameObject> CurrentHitObjects = new List<GameObject>();
//public GameObject curobject;
public float radius;
public float maxdist;
public LayerMask layermask;
public float velocity;
public Time deltatime;
public Vector3 previous;
private Vector3 origin;
private Vector3 direction;
private float hitdist;
// Use this for initialization
void Start()
{
foreach (GameObject cur in CurrentHitObjects)
{
previous = cur.transform.position;
}
}
// Update is called once per frame
void Update()
{
origin = transform.position;
direction = transform.forward;
hitdist = maxdist;
CurrentHitObjects.Clear();
RaycastHit[] hits = Physics.SphereCastAll(origin, radius, direction, maxdist, layermask, QueryTriggerInteraction.UseGlobal);
foreach (RaycastHit hit in hits)
{
Plane[] planes = GeometryUtility.CalculateFrustumPlanes(Camera.main);
if (GeometryUtility.TestPlanesAABB(planes, hit.collider.bounds))
{
float angle = Vector3.Angle(transform.forward, hit.point.normalized);
float degree = Mathf.Acos(angle) * Mathf.Rad2Deg;
Vector3 pos = hit.point;
Debug.Log(hit.collider.name + "position =" + pos);
CurrentHitObjects.Add(hit.transform.gameObject);
hitdist = hit.distance;
Debug.Log(hit.transform.name + "Distance ="+ hitdist);
Debug.Log(hit.collider.name + "Angle = " + angle);
velocity = ((hit.transform.position - previous).magnitude) / Time.deltaTime;
previous = hit.transform.position;
Debug.Log(hit.transform.name + "Velocity =" + velocity);
}
else
{
return ;
}
}
}
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
Debug.DrawLine(origin, origin + direction * hitdist);
Gizmos.DrawWireSphere(origin + direction * hitdist, radius);
}
}
As far as I can tell your code doesn't do anything. My first tip would be to remove all of your commented out code, but after that here is why your code does nothing at all:
You pass an array of objects to your script. Fine so far.
You take this entire array of objects 'CurrentHitObjects' and pass the transform.position of every single object to a single vector3. This means that all the values are overwritten except for the last one. This would be a big problem if you were trying to find the position of every single object. This would instead require vector3[]. But there is another bigger problem.
'previous', which holds transform.position of the objects is not used anywhere. So you are not actually finding the location of anything.
You use start() (which only runs once by the way) to iterate through your object array, but then you clear, CurrentHitObjects.Clear();, right at the beginning of update() (which runs many times per second by the way). The problem here, is that if you hoped to use CurrentHitObjects for anything in your code, you can't because you have wiped it before you even start doing anything with it.
Your raycast[] is shooting towards nothing. Seems to me like it just shoots forward.
You are finding the angle between the forward direction and the forward direction?
Honestly there are a lot of major problems with this code. I don't mean to be harsh, but it looks like you copy and pasted someone else's code and don't know how to use it. This needs a complete rework. If you know how to code I would throw it out and start over again. See my comment on your answer for a better way to do what you want.
If you don't know how to code, you should not be asking for freebie working code on stackoverflow. Try a unity forum instead. If you are trying to get better, see my above comments.
I am newbie in Unity platform. I have 2D game that contains 10 boxes vertically following each other in chain. When a box goes off screen, I change its position to above of the box at the top. So the chain turns infinitely, like repeating Parallax Scrolling Background.
But I check if a box goes off screen by comparing its position with a specified float value. I am sharing my code below.
void Update () {
offSet = currentSquareLine.transform.position;
currentSquareLine.transform.position = new Vector2 (0f, -2f) + offSet;
Vector2 vectorOne = currentSquareLine.transform.position;
Vector2 vectorTwo = new Vector2 (0f, -54f);
if(vectorOne.y < vectorTwo.y) {
string name = currentSquareLine.name;
int squareLineNumber = int.Parse(name.Substring (11)) ;
if(squareLineNumber < 10) {
squareLineNumber++;
} else {
squareLineNumber = 1;
}
GameObject squareLineAbove = GameObject.Find ("Square_Line" + squareLineNumber);
offSet = (Vector2) squareLineAbove.transform.position + new Vector2(0f, 1.1f);
currentSquareLine.transform.position = offSet;
}
}
As you can see, when I compare vectorOne.y and vectorTwo.y, things get ugly. Some boxes lengthen and some boxes shorten the distance between each other even I give the exact vector values in the code above.
I've searched for a solution for a week, and tried lots of codes like Mathf.Approximate, Mathf.Round, but none of them managed to compare float values properly. If unity never compares float values in the way I expect, I think I need to change my way.
I am waiting for your godlike advices, thanks!
EDIT
Here is my screen. I have 10 box lines vertically goes downwards.
When Square_Line10 goes off screen. I update its position to above of Square_Line1, but the distance between them increases unexpectedly.
Okay, I found a solution that works like a charm.
I need to use an array and check them in two for loops. First one moves the boxes and second one check if a box went off screen like below
public GameObject[] box;
float boundary = -5.5f;
float boxDistance = 1.1f;
float speed = -0.1f;
// Update is called once per frame
void Update () {
for (int i = 0; i < box.Length; i++) {
box[i].transform.position = box[i].transform.position + new Vector3(0, speed, 0);
}
for (int i = 0; i < box.Length; i++)
{
if(box[i].transform.position.y < boundary)
{
int topIndex = (i+1) % box.Length;
box[i].transform.position = new Vector3(box[i].transform.position.x, box[topIndex].transform.position.y + boxDistance, box[i].transform.position.z);
break;
}
}
}
I attached it to MainCamera.
Try this solution:
bool IsApproximately(float a, float b, float tolerance = 0.01f) {
return Mathf.Abs(a - b) < tolerance;
}
The reason being that the tolerances in the internal compare aren't good to use. Change the tolerance value in a function call to be lower if you need more precision.