I tried to make a script that moves an object back and forth between two points. But it just flies in the ifinity. I tried to find the problem whole evening but idk.
here is the code:
using UnityEngine;
public class MovementBetweenPoints : MonoBehaviour {
public Transform[] keyPoints;
public float speed;
private int currentKeyPoint;
// Use this for initialization
void Start ()
{
transform.position = keyPoints[0].position;
currentKeyPoint = 1;
}
// Update is called once per frame
void Update ()
{
if (transform.position == keyPoints[currentKeyPoint].position)
{
currentKeyPoint++;
}
if (currentKeyPoint >= keyPoints.Length)
{
currentKeyPoint = 0;
}
transform.position = Vector3.MoveTowards(transform.position, keyPoints[currentKeyPoint].position, speed * Time.deltaTime);
}
}
Your script works fine as it is. you need to make sure that speed is set to a value greater than 0 in the inspector, and that the keypoints array contains some gameobjects in the inspector too, and you are good to go
I'm sure the problem comes with this part of the code where you check if the position of the object is equal at some waypoint. Instead of:
if (transform.position == keyPoints[currentKeyPoint].position)
{
currentKeyPoint++;
}
try to do something less agressive, and give a bit of margin like:
if (Vector3.Distance(transform.position - keyPoints[currentKeyPoint].position) <= min_Distance)
{
currentKeyPoint++;
}
because it's almost impossible that two objects with different speeds match at the same point. Instead of this, you'll use min_Distance to check it.
Related
I sticked my character to an empty object and applied Rigidbody, script to that empty object(To change the pivot); nevertheless, the character is not moving with this code:
public class main_character : MonoBehaviour
{
public float speed;
private float max_vel = 4f;
private bool grounded = true;
private bool flip = true;
private Rigidbody2D main_vel;
private void Awake()
{
main_vel = GetComponent<Rigidbody2D>();
}
void FixedUpdate()
{
float now_vel = Mathf.Abs(main_vel.velocity.x);
if (Input.GetKey("d") && flip)
{
flip_side();
if (now_vel < max_vel)
{
main_vel.AddForce(new Vector2(speed, 0f) * Time.deltaTime);
}
}
if(Input.GetKey("a") && !flip)
{
!flip_side();
if (now_vel < max_vel)
{
main_vel.AddForce(new Vector2(-speed, 0f) * Time.deltaTime);
}
}
}
void flip_side()
{
flip = !flip;
transform.Rotate(0f, 180f, 0f);
}
}
you either need to add a speed value to the top of the class, or dynamically generate it. I would also put an exception or debug that catches when you press the button while speed is set to zero.
if speed accelerates you're going to need to build it or call it in update and give it an initial speed + speed based on delta time. I would also clamp it to max speed there instead, as it prevents large values being tracked by the game engine.
--Regarding an empty parent situation, If there is no physics components, the child should retain the motion of it's parent. That being said, you must have a rigidbody to add force to. You look like you might be using 2d so I have less experience with those rigidbodies, but I imagine it would have to not be kinematic to accept force as you are doing. After checking for that, I would maybe add some debug that posts your vector2 and speed from inside your max_vel check.
Trying to create simple endless moving platform with 3 cubes of scale 70 on z(Player will not move forward, will just move left/right). The RepositionPlatform script is attached to each platform/cube which is responsible for movement and checks the z position of each platform and if it is <= -100.0f, then position is changed to (0,0,200.0f).
Problem is sometimes there is a little gap between the platforms(cubes) or there is a little overlap which I don't want.
Platforms should be placed one after each other without any gap or overlap!!!
Can anyone help find the issue looking at the script or suggest any other better way ?
The script below is attached to 3 platform game objects!!!
public class RepositionPlatform : MonoBehaviour
{
private GameObject platformGO;
[SerializeField]
private float speed;
// Start is called before the first frame update
void Start()
{
platformGO = this.gameObject;
Debug.Log("In RepositionPlatform Start method - "+ platformGO.name);
}
// Update is called once per frame
void Update()
{
Debug.Log("In RepositionPlatform Update Method- " + platformGO.name);
platformGO.transform.Translate(Vector3.back * Time.deltaTime * speed);
Transform platformTransform = platformGO.transform;
if(platformTransform.position.z <= -100.0f)
{
platformTransform.position = new Vector3(0,0,200.0f);
}
}
}
Probably because speed is a floating point value. You should read up on them if you haven't already.
Long story short, you aren't accounting for how far below -100 the value might have gone, you're just resetting it.
If you translate it instead, you will preserve any extra distance beyond -100 that the transform might have gone.
Try this instead:
If (transform.position.z < -100){
transform.Translate(new Vector3(0,0,200));
}
Edit
Should be Z value, not X
I'm trying to add sound to my pushable object and just have a simple if statement for checking if the pushable object is moving. The concept is quite simple, if the object is moving the sound should play and when it's not moving it shouldn't. The problem however is, that when I debug the value there is a 0 every 5 frames or so. This causes the sound to work inconsistently. The script I have is really simple, and I have tried changing to fixedupdate, but it didn't work. I had read somewhere that physics calculations are done in fixedUpdate.
public class PushableObject : MonoBehaviour
{
Rigidbody rb;
AudioSource audioS;
bool rbIsMoving = false;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
audioS = GetComponent<AudioSource>();
}
// Update is called once per frame
void FixedUpdate()
{
if (rb.velocity == Vector3.zero)
{
Debug.Log("Not moving");
audioS.volume = 0f;
}
else
{
Debug.Log("Moving");
audioS.volume = 0.1f;
}
}
}
Edit: I have just figured out that if the player pushes the pushable object into the wall the sound is still playing, for this reason I think I have to change the way too determine if the object is moving or not.
This happens due to single precision floating point.
You never (should) compare two float values directly since they might logically be equal (e.g. 1 = 5*0.2) but for the computer they might be different by a small "epsilon"
So Unity decides that Vector3 simply uses only a precision of 0.00001 for equality for ==
Rather use Mathf.Approximately which uses a very small Epsilon instead.
Than easier than comparing each component of the rb.velocity against 0 what you actually want is the rb.velocity.magnitude which is actually the overall speed.
if (Mathf.Approximately(rb.velocity.magnitude, 0))
Update
Alternatively store the last position and compare it to the current one using Vector3.Distance either again with Mathf.Approximately
private Vector3 lastPosition;
private void LateUpdate()
{
if(Mathf.Approximately(Vector3.Distance(lastPosition, transform.position), 0))
{
//...
}
else
{
//...
lastPosition = transform.positiom;
}
}
Or with a custom threshold
if(Vector3.Distance(lastPosition, transform.position) <= someThreshold)
or this time you actually can use == if a threshold of 0.00001 is what you want
if(lastPosition == transform.position)
You can check if the RigidBody is sleeping:
if (!rb.IsSleeping()
{
//it's moving
}
else
{
//it's not
}
Or check if the transform position has moved since last frame:
Vector3 lastposition;
Gameobject go = somegameobject;
function Update()
{
if (lastposition == go.transform.position)
{
//not moving
}
else
{
//moving
}
lastposition = go.transform.position;
}
You can use Transform.hasChanged to check if the player position has changed on the last update
if (!this.transform.hasChanged)
{
print("Player is not moving");
}
transform.hasChanged = false;
I want a simple circle AI to follow some invisible game objects I have down. It goes to the first one then stops.
I put a debug thing in the second if statement and its not logging. I have also tried changing the second if statement to else.
using UnityEngine;
using System.Collections;
public class FollowPath : MonoBehaviour
{
public Transform[] target;
public float speed;
private int current = 0;
void Update()
{
if(transform.position != target[current].position)
{
Vector2 pos = Vector2.MoveTowards(transform.position, target[current].position, speed * Time.deltaTime);
GetComponent<Rigidbody2D>().MovePosition(pos);
}
if(transform.position == target[current].position)
{
current++;
Debug.Log("2");
}
}
}
You almost never want to compare positions in games for equality, but rather check that they are closer than a reasonably small distance.
Instead of:
if(transform.position == target[current].position)
try something like this which just checks that you're "reasonably close" as defined by whatever you think is acceptable (0.01f in my example here):
if((transform.position - target[current].position).magnitude < 0.01f)
Basically, I have a game where tickets appear at the top of the screen. When a ticket is completed, it gets destroyed, and if there's a gap between the remaining tickets, the tickets slide down to fill in the gap. I actually have it working just fine, but they kind of just jump to the positions and it doesn't look very pretty.
How can I move the objects to their target position smoothly? I've tried messing with Lerp and Smoothdamp, but both of those seem to be producing strange results (tickets go all over the place. Here's the relevant code I'm working with:
public void SlideRail()
{
ticketList.RemoveAll(x => x == null);
int count = ticketList.Count;
for (int i = destroyedIndex; i < ticketList.Count; i++)
{
if (ticketList[i] != null)
{
ticketList[i].transform.Translate(Vector3.left * 35);
ticketList[i].GetComponent<Ticket>().index--;
}
}
indexer = railPosition.IndexOf(ticketList.Last().GetComponent<Ticket>().item_pos);
up = false;
}
DestroyedIndex is the index of the object that was destroyed (so that we only move the objects after it in the list)
Up is a flag that activates SlideRail(), when its set to false the method ends and the next method continues.
SlideRail is then called in the Update() method:
void Update()
{
if (up && ticketList.Count > 2)
SlideRail();
if (Time.time > nextTicket)
{
nextTicket = Time.time + timeBetweenTickets;
StartCoroutine(WaitToPrint());
CreateTicket();
UpdatePosition();
}
}
I feel like I should be able to do this using Translate, but I must be missing something.
With movement like this you should usually include Time.deltaTime (as a factor in translate) to bind the movement to the frame rate. [Edit: Translate this sentence to framerate-independant, that's what I meant.]
Now, to the actual point, it looks like you do the movement in just one frame and thus you get this jump (you always set up to false at the end of SlideRail).
A possible way of doing this is to specify a target position upon trigger (task complete, need to push over) and than keep calling ticketList[i].transform.position = Vector3.MoveTowards(ticketList[i].transform.position, targetPosition, speed * Time.deltaTime); for each object (framed by a "reached target position" check, e.g. using if(Vector3.Distance(ticketList[i].transform.position, targetPosition) > someThreshold). You can add an else to this to directly set the position when in threshold range (make the threshold small so it's not visible, e.g. 0.1f), though you usually only need this for non-linear functions like lerp which slow down at the end and can stall. [Edit: Because of floating point imprecision it's actually mostly always a good idea to use a threshold.]
Using iTween, it can be simplified into a one line call:
iTween.MoveTo(someGameObject, iTween.Hash("y", targetY,
"islocal", true,"easetype", iTween.EaseType.spring,
"time", 0.2f));
I've wrote this piece of code for myself before, maybe it helps you.
using System;
using System.Collections;
using UnityEngine;
public class MoveController : MonoBehaviour
{
public float moveTime;
public AnimationCurve moveSpeedCurve;
public AnimationCurve movePositionCurve;
public void StartMoving(Vector2 destination, Action action = null)
{
StartCoroutine(Move(destination, action));
}
IEnumerator Move(Vector3 destination, Action action = null)
{
float currentTime = 0;
float perc = 0;
Vector3 currentPos = transform.localPosition;
while (perc != 1)
{
currentTime += Time.deltaTime;
if (currentTime > moveTime)
{
currentTime = moveTime;
}
perc = moveSpeedCurve.Evaluate(currentTime / moveTime);
transform.localPosition = Vector2.LerpUnclamped(currentPos, destination, movePositionCurve.Evaluate(perc));
yield return null;
}
if (action != null)
action();
}
}