Auto Movement Between 2 points issue - c#

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

Related

Swerve Control Unity C#

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! :)

Ease-in and ease-out with Vector2.MoveTowards

I've seen a lot of examples of easing with Vector2.Lerp.
But I want to use Vector2.MoveTowards because I am randomizing the distance my NPC is traveling, and I want the speed of the NPC to always be constant regardless of the distance traveled. Ideally, would like to control the easing granularly with an AnimationCurve if that's possible, but a smooth step function would be ok too. Here is the simplified code that I'm using now (using Behavior Designer so the methods are a bit different):
private readonly float Speed = 1.5f;
public SharedVector2 BugDirection;
public override void OnStart()
{
BugDirection.Value = new Vector2(Random.Range(0.1f, 0.5f), transform.position.y);
}
public override TaskStatus OnUpdate()
{
if (Vector2.Distance(transform.position, BugDirection.Value) == 0)
{
return TaskStatus.Success;
}
transform.position =
Vector2.MoveTowards(transform.position, BugDirection.Value, Speed * Time.deltaTime);
return TaskStatus.Running;
}
Okey so there is one first issue: Never use == for comparing two float values!
Even a situation like 5f * 0.2f / 10f == 1f might fail because due to floating point precision it might actually be 0.9999999 or 1.0000001.
Instead you usually rather check against a certain range like e.g.
if(Mathf.Abs(a - b) <= someThreshold)
Unity for this also offers Mathf.Approximately and using
if(Mathf.Approximately(a, b))
basically equals using
if(Mathf.Abs(a - b) <= Mathf.Epsilon)
where Epsilon is
The smallest value that a float can have different from zero.
So much for your current stop condition.
I just assume for now this is happening somewhere outside of a MonoBehaviour so a Coroutine would not be an option.
However, what you basically want to achieve is having a value that continously grows from 0 to 1 linear (the time) mapped onto an eased-in and eased-out movement curve.
So assuming that the target position is not updated between the start and the ending of a movement I would do it like e.g.
private readonly float Speed = 1.5f;
private Vector2 target;
private Vector2 start;
private float timePassed;
private float duration;
public override void OnStart()
{
target = new Vector2(Random.Range(0.1f, 0.5f), transform.position.y);
start = transform.position;
duration = Vector2.Distance(start, target) / speed;
timePassed = 0f;
}
public override TaskStatus OnUpdate()
{
if (timePassed >= duration)
{
transform.position = target;
return TaskStatus.Success;
}
var factor = timePassed / duration;
//MAGIC
transform.position = Vector2.Lerp(start, target, factor);
timePassed += Time.deltaTime;
return TaskStatus.Running;
}
So far this will be a linear movement just as before ;)
So let's fill in the MAGIC.
One very simple option for ease-in and ease-out is using Mathf.SmoothStep like e.g.
factor = Mathf.SmoothStep(0, 1, factor);
or you could give the AnimationCurve another shot now using
factor = yourAnimationCurve.Evaluate(factor);
which gives you complete control over the smoothing of the movement and yet results in the total average velocity of speed as if you would have applied it linear.

Moving sphere gameObject between 3 points in a loop

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;
}

How to realistically reflect a 3d sphere in Unity with C#

I've been trying to realistically reflect a 3d sphere on the walls of a box for a while now in Unity. For some reason, the reflection is generally correct, but when the ball hits a wall in certain directions, the reflection is incorrect.
To illustrate what happens to the ball upon hitting a wall: T = top wall, R = right wall, L = left wall, and B = bottom wall. Let r = the ball comes/goes to the right, l = for the left, and s = the ball stops/slows down significantly. The instructions below take this format: Xyz, where X = the wall the ball is about to hit, y = the ball's initial direction, z = the reflection. The game has a top-down perspective, and the instructions are based on the wall's perspective. I'm also new to C#, so the code is potentially eye burning.
Instructions: Tll, Trl; Bll, Brl; Rls or after hitting another wall Rlr, Rrl; Lls or after hitting another wall Llr, Lrl
Generally, when the ball stops, it jumps in the air. I wonder if this is because the angle reflects along the wrong axis, but why would this only sometimes happen? Also, when only one key is held, the ball bounces back and forth until it leaves the arena. I know about discrete and continuous hit detection, and the setting is on discrete, but the walls generally contain the ball well enough, with this case being the exception.
What I tried:
Figuring out how to use Vector3.Reflect. I do not understand what variables this function should contain and how to apply this to my code. I did look at the Unity Documentation page for help, but it did not answer my question.
Changing the negative signs, as the angle has to be a reflection on the y-axis, and this does change how the reflections work, but does not solve the problem. The current way the negatives are ordered are the most ideal I found.
Giving the ball a physics material for bounciness.
Adding a small number to the denominator in the arctan equation to help prevent a division by zero. This did not help at all.
Creating different equations (basically changing the negatives) for varying combinations of positive and negative accelerations. Since there is a certain positive or negative acceleration associated with each button press (see Movement script), and there seems to be an issue with said signs, I wondered if associating each acceleration with its own set of equations could solve the problem. It did not work.
Checked if the walls were at different angles.
Deleting the variables xA and yA, or placing them in different spots.
Experimented with finding the speed of the object, but had no idea how to implement it.
The code for the Movement script for the player named Controller:
public class Movement : MonoBehaviour
{
public static float xAcceleration = 0.0f;
public static float yAcceleration = 0.0f;
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.W)) //If the key W is pressed:
{
Vector3 position = this.transform.position; //Variable position is set to transform the players placement in the game.
if (yAcceleration >= -5 && yAcceleration <= 5) //If the y vector of the acceleration is >= -5 and <= 5:
{
yAcceleration = yAcceleration + 0.01f; //The y vector of the acceleration increases by 0.01 as long as the key W is pressed.
}
position.z = position.z + (0.1f * yAcceleration); //The position of the object on the z-axis (pretend it is the y-axis in the game world) is transformed by its original position plus its speed times its yAcceleration.
this.transform.position = position; //The gameObject is now transformed to a position equal to the variable position by the z-axis.
}
else //If the key W is let go of:
{
Vector3 position = this.transform.position;
position.z = position.z + (0.1f * yAcceleration);
this.transform.position = position; //The position of the gameObject continues to update, but its acceleration does not change. Basically, it continues to move forward.
}
//The rest of the code is very similar to the above, but I included it just in case there was something wrong.
if (Input.GetKey(KeyCode.S))
{
Vector3 position = this.transform.position;
if (yAcceleration >= -5 && yAcceleration <= 5)
{
yAcceleration = (yAcceleration) - 0.01f;
}
position.z = position.z + (0.1f * yAcceleration);
this.transform.position = position;
}
else
{
Vector3 position = this.transform.position;
position.z = position.z + (0.1f * yAcceleration);
this.transform.position = position;
}
if (Input.GetKey(KeyCode.A))
{
Vector3 position = this.transform.position;
if (xAcceleration >= -5 && xAcceleration <= 5)
{
xAcceleration = (xAcceleration) - 0.01f;
}
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
else
{
Vector3 position = this.transform.position;
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
if (Input.GetKey(KeyCode.D))
{
Vector3 position = this.transform.position;
if (xAcceleration >= -5 && xAcceleration <= 5)
{
xAcceleration = (xAcceleration) + 0.01f;
}
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
else
{
Vector3 position = this.transform.position;
position.x = position.x + (0.1f * xAcceleration);
this.transform.position = position;
}
}
}
This is the code for the collider and reflection:
public class Collider : MonoBehaviour
{
public float xA;
public float yA;
void OnCollisionEnter(Collision collision) //If a gameObject enters the collision of another object, this immediately happens once.
{
if (gameObject.tag == "Boundary") //If the gameObject has a tag named Boundary:
{
yA = -Movement.yAcceleration; //yA stores the value of yAcceleration after being called from script Movement as a negative. Its a reflection.
Movement.xAcceleration = (Movement.xAcceleration * -Mathf.Atan(yA / Movement.xAcceleration)); //xAcceleration is changed based on this equation: A * artan(A_y / A_x). The 0.000001 was here, adding to A_x to help prevent a 0 as the denominator.
xA = Movement.xAcceleration; //This is declared now...
Movement.yAcceleration = (Movement.yAcceleration * Mathf.Atan(Movement.yAcceleration / xA)); //This uses xA because Movement.xAcceleration is changed, and the yAcceleration calculation is based on the xAcceleration prior the collision.
}
}
void OnCollisionStay(Collision collision)
{
if (gameObject.tag == "Boundary")
{
yA = Movement.yAcceleration; //The same thing happens as before.
Movement.xAcceleration = (Movement.xAcceleration * -Mathf.Atan(yA / Movement.xAcceleration));
xA = Movement.xAcceleration;
Movement.yAcceleration = (Movement.yAcceleration * Mathf.Atan(Movement.yAcceleration / xA));
Movement.xAcceleration = -Movement.xAcceleration / 2; //On collision, the ball is reflected across the x-axis at half its speed.
Movement.yAcceleration = Movement.yAcceleration / 2; //yAcceleration is half its original value.
}
}
}
The picture below is the game setup. I apologize that it is a link; I do not have enough Reputation to merit a loaded image on this page. Also, if anything is unclear, please message me.
https://i.stack.imgur.com/VREV4.png
I would really appreciate the help. Thanks!
One very important note here: As soon as there is any Rigidbody involved you do not want to set any values through the .transform - This breaks the physics and collision detection!
Your Movement should rather alter the behavior of the Rigidbody e.g. by simply changing its Rigibody.velocity
I would then also place the collision check directly into the balls's component and check whether you hit a wall ("Boundary")
Then another note: Your code is currently frame-rate dependent. It means that if your target device runs with only 30 frames per second you will add 0.3 per second to the acceleration. If you run however on a more powerful device that manages to run with 200 frames per second then you add 2 per second.
You should rather define the de/increase per second and multiply it by Time.deltaTime
All together maybe something like this
public class Movement : MonoBehaviour
{
// Adjust these settings via the Inspector
[SerializeField] private float _maxMoveSpeed = 5f;
[SerializeField] private float _speedIncreasePerSecond = 1f;
// Already reference this via the Inspector
[SerializeField] private Rigidbody _rigidbody;
private void Awake()
{
if(!_rigidbody) _rigidbody = GetComponent<Rigidbody>();
}
// Get User Input in Update
private void Update()
{
var velocity = _rigidbody.velocity;
velocity.y = 0;
if (Input.GetKey(KeyCode.W) && velocity.z < _maxMoveSpeed)
{
velocity.z += _speedIncreasePerSecond * Time.deltaTime;
}
if (Input.GetKey(KeyCode.S) && velocity.z > -_maxMoveSpeed)
{
velocity.z -= _speedIncreasePerSecond * Time.deltaTime;
}
if (Input.GetKey(KeyCode.A) && velocity.x > -_maxMoveSpeed)
{
velocity.x -= _speedIncreasePerSecond * Time.deltaTime;
}
if (Input.GetKey(KeyCode.D) && velocity.x < _maxMoveSpeed)
{
velocity.x += _speedIncreasePerSecond * Time.deltaTime;
}
// clamp to the max speed in case you move diagonal
if(velocity.magnitude > _maxMoveSpeed)
{
velocity = velocity.normalized * _maxMoveSpeed;
}
_rigidbody.velocity = velocity;
}
}
And then finally simply add a PhysicsMaterial with desired settings to the walls and ball.
I used Friction = 0f and Bounciness = 0.7f for ball and walls. For slow movements you also might want/have to adjust the Bounce Threshold in the Project's Physics Settings otherwise there will be no bouncing if the velocity is smaller then 2 by default.
This depends a bit on your definition of "realistic". I disabled gravity so the ball also has no rotation and angular friction:

How to change lane smoothly

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. :)

Categories