I am defining three gameObject points where a sphere gameObject should travel from one position to another in a loop, such that 1-2-3-1-2-3....(as in a triangle). I am able to achieve the movement with Vector3.MoveTowards() function but it only takes in 2 points. Is there a way to achieve the same with multiple points? (atleast 3 or more)
public class SlideBetweenPoints : MonoBehaviour
{
public Transform pointA, pointB, pointC;
public float speed;
void Update ()
{
float step = speed * Time.deltaTime;
transform.position = Vector3.MoveTowards(pointA.position , pointB.position , step);
}
}
Yes simply move towards one point and when you reached it go to the next one.
Note that currently you are always starting again from pointA. In order to continuously move towards a target position you have to rather use
transform.position = Vector3.MoveTowards(transform.position, targetPosition, step);
I would rather use a more general List like
public class SlideBetweenPoints : MonoBehaviour
{
public List<Transform> points;
public float speed;
private int index;
void Update ()
{
transform.position = Vector3.MoveTowards(transform.position, points[index].position, speed * Time.deltaTime);
if(transform.position == points[index].position)
{
// increase index with wrap around
index = (index + 1) % points.Count;
}
}
}
Where transform.position == points[index] uses a precision of 1e-5 for equality. If you really need it you could also check for exact matching positions:
if(Mathf.Approximately(0, (transform.position- points[index].position).sqrMagnitude))
{
index = (index + 1) % points.Count;
}
Related
Hi I am creating a hyper casual game with unity, but I have encountered a problem with the swerve control (I have also seen many git hubs but even these do not work perfectly)
I've put this in my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
private float lastframeposx;
private float movefactorx;
public float MoveFactorX => movefactorx;
public Camera m_MainCam;
private float speed = 2.0f;
[SerializeField]
GameObject character;
[SerializeField] private float swerveSpeed = 0.5f;
[SerializeField] private float maxSwerveAmount = 1f;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
transform.position += Vector3.forward * speed * Time.deltaTime;
Cammina();
/*Vector3 destra = Camera.main.ScreenToWorldPointt(Input.touches[i].position);
transform.position += Vector3.zero destra;*/
}
void Cammina()
{
if(Input.GetMouseButtonDown(0))
{
lastframeposx = Input.mousePosition.x;
float swerveAmount = Time.deltaTime * swerveSpeed * MoveFactorX;
swerveAmount = Mathf.Clamp(swerveAmount, -maxSwerveAmount, maxSwerveAmount);
transform.Translate(swerveAmount, 0, 0);
}
else if (Input.GetMouseButton(0))
{
movefactorx = Input.mousePosition.x - lastframeposx;
lastframeposx = Input.mousePosition.x;
float swerveAmount = Time.deltaTime * swerveSpeed * MoveFactorX;
swerveAmount = Mathf.Clamp(swerveAmount, -maxSwerveAmount, maxSwerveAmount);
transform.Translate(swerveAmount, 0, 0);
}
else if(Input.GetMouseButtonUp(0))
{
movefactorx = 0f;
float swerveAmount = Time.deltaTime * swerveSpeed * MoveFactorX;
swerveAmount = Mathf.Clamp(swerveAmount, -maxSwerveAmount, maxSwerveAmount);
transform.Translate(swerveAmount, 0, 0);
}
}
/*void vaidoveschiacciato()
{
if (Input.touchCount > 0 && Input.touches[0].phase == TouchPhase.Began)
{
Ray ray = Camera.main.ScreenPointToRay(Input.touches[0].position);
RaycastHit hit;
if(Physics.Raycast(ray, out hit))
{
if(hit.collider != null)
{
transform.position += hit.collider.GetComponent<Transform.position> * speed * Time.deltaTime;
}
}
}
}*/
}
1 Problem: he don't go when the finger is
2 Problem: How do I eliminate the movement from right to left (Without making it go out of the path)
(Langauge: C#)
The problem: When you swerve, it swerves just in the direction, there is no limits on how far it goes.
How I would fix this: I would put the movement to change it through a function. This could clamp it, so the higher the distance to the center of the track, the less it swerves. Or, you can altogether check if the distance is a maximum and then stop swerving.
Note: you can use other functions to do this (they just have to flatten out the larger the input).
Smooth, good looking bell curve way
For example you could use a bell curve. Look one up if you've never seen one before. It is at it's highest possible output of one, at a zero input. When it gets hiher or lower, it outputs lower to zero.
the simplest equation is y = i-(x2). The lower i is (above 1), the wider the curve, or the larger the output is for a larger input. I made a graph here.
x can be the distance to the center of the track, so you should adjust i, so the maximum distance from the track is flat.
This output is what you should change swerveAmount to.
The flatter parts of the graoh is how much you will swerve when you are that distance from the center (x axis)
Alternatively
You could just check the distance, and if it passes a certain distance don't translate it.
Let me know in the omments if there are any problems! :)
My current player movement code provides 0.32 translation on each key click.
If it is clicked 2 times very quickly, it should be 0.64.
I provide this with coroutine, but it is affected by fps.
How do I ensure that it is not affected by fps?
I know Time.deltaTime, but how do I get exactly 0.32 if I multiply by Time.deltaTime?
My current move code:
public float move = 0.32f; //movement limitation
public float repeat = 4; // I perform 0.32 in 4 parts to be more smooth.
void Update()
{
if (Input.GetKeyDown(KeyCode.LeftArrow) //Exapmle key. I have 4 ways to move
{
StartCoroutine("Left");
}
}
IEnumerator Left()
{
for (int i = 1; i <= repeat; i++)// as I said, I perform 0.32 in 4 parts to be more smooth.
{
transform.position = transform.position + new Vector3(-move / repeat, 0, 0);
yield return null;
}
}
If you wanted to be smooth rather use a fixed speed and do e.g.
// distance to move
public float move = 0.32f;
// speed in Unity units per second
public float speed = 1f;
void Update()
{
if (Input.GetKeyDown(KeyCode.LeftArrow)
{
StartCoroutine(Left);
}
}
IEnumerator Left()
{
// Pre-calculate the target position
// This is the huge advantage of IEnumerators -> you can store local variables over multiple frames
var targetPosition = transform.position + Vector3.left * move;
// track how far you have already moved
var moved = 0f;
// Loop until you moved enough
while(moved < move)
{
// the step is now frame-rate independent and moves the object with a fixed value per second
var step = speed * Time.deltaTime;
// every frame move one such step towards the target without overshooting
transform.position = Vector3.MoveTowards(transform.position, targetPosition, step);
// keep track of the movement
moved += step;
yield return null;
}
// and to end up with a clean value in the end
transform.position = targetPosition;
}
However
If it is clicked 2 times very quickly, it should be 0.64
this wouldn't be covered. In order to do this I would actually use a completely different approach without Coroutines at all and rather simply use e.g.
// distance to move
public float move = 0.32f;
// speed in Unity units per second
public float speed = 1f;
Vector3 targetPosition;
void Start()
{
targetPosition = transform.position;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.LeftArrow)
{
targetPosition += Vector3.left;
}
...
transform.position = Vector3.MoveTowards(transform.position, targetPosition, speed * Time.deltaTime);
}
so now you can smash your move keys in any direction as much as you like and it will simply sum them all up into the one targetPosition and always keep moving towards it smoothly but with a fixed velocity.
or another alternative would be to interpolate and rather make the object move faster if it is further away from the target but slower the closer it comes
// 5 here is an arbitrary number based on experience -> tweak it according to your needs
public float interpolation = 5f;
...
transform.position = Vector3.Lerp(transform.position, targetPosition, interpolation * Time.deltaTime);
i can't understand how can i make it so that my camera will approach an object and after reaching a certain distance from it,it will go back to the initial position, and so on.
Here is my code:
public class Camera : MonoBehaviour
{
public GameObject cameraLook;
float speed = 10f;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Vector3 distance = (transform.position - cameraLook.transform.position);
Debug.Log(distance);
transform.LookAt(cameraLook.transform.position);
if (distance.x > 30)
{
go();
}
if (distance.x < 0)
{
goBack();
}
}
void go()
{
transform.position += transform.forward * speed * Time.deltaTime;
}
void goBack()
{
transform.position-= transform.forward * speed * Time.deltaTime;
}
}
Update is called once every frame.
What currently happens in your code is
Frame 1
You reach a distance of x > 30
you call go once
Frame 2
Now your distance might be < 30 but still > 0 due to the movement last frame
You do nothing at all anymore
or the same with
Frame 1
You reach a distance of < 0
you call go once
Frame 2
Now your distance might be > 0 but still < 30 due to the movement last frame
You do nothing at all anymore
Then also it is very "dangerous" / unreliable to simply check the global X axis difference between your objects. This assumes that your camera is definitely moving along the X axis.
Instead I would rather use something like e.g.
public class Camera : MonoBehaviour
{
public GameObject cameraLook;
[Toolbox("The minimum distance the camera should stay away from the object when moving closer")]
[Min(0f)] public float minDistance = 0f;
[Toolbox("The maximum distance the camera should move away from the object")]
[Min(0f)] public float maxDistance = 30f;
[Toolbox("How fast shall the camera travel. If your target objects moves have in mind that this needs to be bigger then the movement speed of your target object ;)")]
[Min(0f)] public float moveUnitsPerSecond = 10f;
[Toolbox("If true starts moving towards otherwise starts moving away")]
public bool startMovingTowards = true;
private bool isMovingTowards;
private void Start()
{
isMovingTowards = startMovingTowards;
}
void Update()
{
// first look at the target object
transform.LookAt(cameraLook.transform.position);
// get the desired final distance based on the move direction flag
var targetDistance = isMovingTowards ? minDistance : maxDistance;
// get the desired final position based on the desired distance
// simply moving the distance backwards away from the object
var targetPosition = cameraLook.transform.position - transform.forward * targetDistance;
// Move smoothly with the given moveUnitsPerSecond to the desired final position
transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveUnitsPerSecond * Time.deltaTime);
// is the desired final position reached (within a precision of 0.00001)
if(transform.position == targetPosition)
{
// then simply invert the move direction
isMovingTowards = !isMovingTowards;
}
}
}
I have a question. I have an object that moves unlimited between two points.
For this we used the PingPong method.
public Vector3 a;
public Vector3 b;
public float speed;
void Update()
{
transform.position = Vector3.Lerp(a, b, Mathf.PingPong(Time.time * speed, 1.0f));
}
It all works perfectly, but it is a problem when I try to change the speed directly from the code. So I have another class where I change the speed.
if (index == numIndex + 1 || index == numIndex - 1)
{
Debug.Log("Good!");
other.transform.parent.GetComponent<BadWormsController>().speed -= 0.2f;
}
else
Debug.Log("Bad");
The second-order code is executed when the collision occurs between the player and the object moving from point a to point b.
It's ok, the speed changes, but the object resets again, so it does not go any further on the axis, but it starts again, so it changes its position.
What is wrong and how to change, if someone has idea, that when changing speed, the object does not change its position, but to continue on.
Instead of using Time.time make a new variable for storing its progress. Then you can adjust this value to negate the difference in position.
public Vector3 a;
public Vector3 b;
public float speed;
public float distance;
void Update(){
distance += Time.deltaTime;
transform.position = Vector3.Lerp(a, b, Mathf.PingPong(distance * speed, 1.0f));
}
Then when you change the speed you add/remove the difference from the distance
// get BadWormsController as var since we use it multiple times
BadWormsController bwc = other.transform.parent.GetComponent<BadWormsController>();
float multiplyer = 0.2f/bwc.speed; // get % of distance to remove
bwc.speed -= 0.2f; //change speed
bwc.distance -= bwc.distance*miltiplyer; // remove difference
I am trying to achieve basic racing game. Infinite racing game, movement method like a subway surfers. I have a problem about changing lane. I dont want to teleport to other lane, I want to smoothly. I am newbee in unity, I have try Lerp method but it is not working.
using UnityEngine;
using System.Collections;
public class VehicleController : MonoBehaviour
{
public float drift;
public Vector3 positionA;
public Vector3 positionB;
public Vector3 positionC;
public Vector3 positionD;
private Transform tf;
private Rigidbody rb;
private Vector3 vehiclePos;
void Awake()
{
//rb = GetComponent<Rigidbody> ();
tf = transform;
}
void Update()
{
vehiclePos = tf.position;
if (Input.GetKey( KeyCode.Space ))
DecreaseSpeed ();
else
IncreaseSpeed ();
if (Input.GetKeyDown (KeyCode.A))
{
MoveToRight ();
Debug.Log( "Move to Right!" );
}
if (Input.GetKeyDown (KeyCode.D))
{
MoveToLeft ();
Debug.Log( "Move to Left!" );
}
}
void FixedUpdate()
{
tf.Translate (Vector3.forward * speed * Time.deltaTime);//My Movement Method.
}
void MoveToLeft()
{
if (vehiclePos.position.x == positionA.x)
vehiclePos = Vector3.Lerp (vehiclePos.position, positionB, Time.deltaTime * drift);
}
void MoveToRight()
{
if (vehiclePos.position.x == positionB.x)
vehiclePos = Vector3.Lerp (vehiclePos.position, positionA, Time.deltaTime * drift);
}
}
First: Don't use == for position.x, since it's a floating-point (decimals) value and in this case it would be very rare for it to return "true". Here's some info about comparing floats.
Second: It doesn't look like you're connecting your actual position with vehiclePos anywhere. transform.position is what you want there.
Third: Input.GetAxis() is a cleaner way to deal with direction input. Instead of specifically calling out each button you can deal with just one float value between -1 and 1. It will also let you reconfigure the keys easily.
Fourth: In an infinite runner it is better to have the world move towards your character and camera than to have the character and camera actually move forward. Floating point numbers get less precise as you move further away from zero, so you should have your action take place relatively close to the world origin (0,0,0) point if you can.
If you want to press the button once to change lanes, you should keep an integer variable that saves which lane you're currently in. If you press LEFT you subtract one, and if you press RIGHT you add one. You should also add a check to make sure it stays within the desired range.
Then in Update() you just need to ALWAYS Lerp towards that X value. You can use Mathf.Lerp to lerp only one variable at a time if you want.
public int laneNumber = 0;
public int lanesCount = 4;
bool didChangeLastFrame = false;
public float laneDistance = 2;
public float firstLaneXPos = 0;
public float deadZone = 0.1f;
public float sideSpeed = 5;
void Update() {
//"Horizontal" is a default input axis set to arrow keys and A/D
//We want to check whether it is less than the deadZone instead of whether it's equal to zero
float input = Input.GetAxis("Horizontal");
if(Mathf.Abs(input) > deadZone) {
if(!didChangeLastFrame) {
didChangeLastFrame = true; //Prevent overshooting lanes
laneNumber += Mathf.roundToInt(Mathf.Sign(input));
if(laneNumber < 0) laneNumber = 0;
else if(laneNumber >= lanesCount) laneNumber = lanesCount - 1;
}
} else {
didChangeLastFrame = false;
//The user hasn't pressed a direction this frame, so allow changing directions next frame.
}
Vector3 pos = transform.position;
pos.x = Mathf.Lerp(pos.x, firstLandXPos + laneDistance * laneNumber, Time.deltaTime * sideSpeed);
transform.position = pos;
}
You could likely just use this code as-is, but I suggest you look it over and try to figure out an understanding of how and why it works. A newbie today who always seeks to improve their skill can do something amazing next week. Hope this helps. :)