Player is not moving toward target without unity built in functions - c#

I am trying to move my target object towards my player using custom codes(without built-in function of unity like MoveTowards)
Vector3 displacment = target.transform.position - player.transform.position;
//you can also get magnitude directly through displacement.magnitude
float xMagnitude = displacment.x * displacment.x;
float yMagnitude = displacment.y * displacment.y;
float zMagnitude = displacment.z * displacment.z;
float customMagnitude =Mathf.Sqrt(xMagnitude + yMagnitude + zMagnitude);
directionToMove = new Vector3(displacment.x / customMagnitude, displacment.y / customMagnitude, displacment.z / customMagnitude);
//directionToMove = displacment.normalized;
Vector3 velocity = directionToMove * speed;
Vector3 moveAmount = velocity * Time.deltaTime;
target.transform.position += moveAmount;
I first got the displacement between two vectors than i get its direction and pass it to my position with speed. but its direction is not towards the player. what i am doing wrong?

If you want to move your target towards the player, the displacment must be :
displacment = player.transform.position - target.transform.position;
To generate a vector given the initial point : A (x1, y1, z1) and final point B (x2, y2, z2) the calculation is the following :
v = B - A = (x2 - x1, y2 - y1, z2 - z1);

So let me get this straight: You want to move the target towards the player, not the player towards the target. Is this correct?
If you want to move the target object towards the player, then the displacement should be player.transform.position - target.transform.position, not the other way around.
If you really don't want to use the Unity functions, then here's how I would do your code:
Vector3 displacment = player.transform.position - target.transform.position;
float xMagnitude = displacment.x * displacment.x;
float yMagnitude = displacment.y * displacment.y;
float zMagnitude = displacment.z * displacment.z;
float customMagnitude =Mathf.Sqrt(xMagnitude + yMagnitude + zMagnitude);
//multiplying vector by scalar works the same,
//and is more readable (possibly faster too)
directionToMove = displacement * 1f/customMagnitude;
Vector3 velocity = directionToMove * speed;
Vector3 moveAmount = velocity * Time.deltaTime;
target.transform.position += moveAmount;

Related

Instantiate in circle objects look to center

A little bit confused - math is really not my strong point, so I'm not sure how to achieve.
Currently the objects instantiate, but I have no control over the distance from center or the rotation of each spawned object
public void instantiateInCircle()
{
for (int i = 0; i < amount; i++)
{
float radius = spawnDistance;
float angle = i * Mathf.PI * 2f / radius;
Vector3 newPos = transform.position + (new Vector3(Mathf.Cos(angle) * radius, spawnHeight, Mathf.Sin(angle) * radius ));
//Rotate objects to look at the center
GameObject instantiatedObject = Instantiate(itemToSpawn, newPos, Quaternion.Euler(0, 0, 0));
instantiatedObject.transform.LookAt(spawnAroundThis.transform);
//How to adjust the width of the radius, how far away from the center?
//Parent instantiated objects to disk
instantiatedObject.transform.parent = spawnAroundThis.transform;
instantiatedObject.transform.localScale = new Vector3(scale, scale, scale);
}
}
How to make the distance adjustable, move cubes in closer to center...?
Currently, you do not access the instantiated object, but the prefab instead. Cache the object and call the LookAt on them.
Since I do not know what type itemToSpawn is, I assumed it is a GameObject. You may want to use your type instead.
GameObject instantiatedObject = Instantiate(itemToSpawn, newPos, Quaternion.Euler(0, 0, 0));
instantiatedObject.transform.LookAt(spawnAroundThis.transform);
If you want to control the distance from center of the rotation:
for (int i = 0; i < amount; i++)
{
float radius = spawnDistance;
float angle = i * Mathf.PI * 2f / (float)amount; // divide by amount, NOT radius
// manipulate radius here as you want
Vector3 newPos = transform.position + (new Vector3(Mathf.Cos(angle) * radius, spawnHeight, Mathf.Sin(angle) * radius ));
...
}

Unity rigid object receives twice the force it is supposed to receive

I am making a game where bullets can start orbiting a player. The orbits are circular, and not elliptical. Bullets have a drag factor of 0.
At first, I simply calculated the next position in the orbit, and divided the delta position by fixedDeltaTime to obtain its new velocity.
Unfortunately, it meant that fast bullets instead follow polygonal paths, and often miss a target.
I wanted to improve the precision by giving them an inward force, and a velocity nearly tangential to the orbit, so that they follow parabola segments around the player instead of straight lines.
The parabola segments are defined by their start and end point, and their vertices, which must lay on the orbit arc, with a velocity equal to the velocity I was previous using ((endpos - startpos) / fixedDeltaTime).
To calculate the parabola, I calculate the midway points on the arc and segment, and their difference is proportional to the force applied.
so we use the following names
fdt = fixedDeltaTime
t = fdt / 2
f = the force applied during the incoming physics frame
v0 = the velocity at the start of the frame
v1 = the velocity at the middle of the frame (at the vertex of the parabola)
dp1 = the relative position at the middle of the frame (midpos - startpos) and vertex of the parabola
dp2 = the relative position at the end of the frame (endpos - startpos)
The force is defined by these two equations:
// at vertex, only the "lateral" velocity remains
v1 = dp2 / fdt
// the difference between dp2 / 2 and dp1 is what the force can apply over half a frame
dp2 / 2 - dp1 = f * 0.5tt
therefore
(dp2 / 2 - dp1) / (0.5 * t * t) = f
(dp2 / 2 - dp1) / (0.5 * fdt/2 * fdt/2) = f
(dp2 - dp1 * 2) * 4 / (fdt * fdt) = f
//v0 is then easily calculated
v0 = v1 - t * force
v0 = (dp2 / fdt) - force * (fdt / 2)
We then get this working code:
Vector3 startPos = _rigidbody.position;
if (firstFrame == false && Vector3.Distance(predictedNextFramePos, startPos) > 0.01f)
Debug.Log("WTF");
Vector3 nextPosition;
GetLocalOrbitPos(nextTime, out nextPosition);
nextPosition += _parent.GetPosition();
float fdt = Time.fixedDeltaTime;
float halfTime = (time + nextTime) / 2f;
Vector3 halfPosition;
GetLocalOrbitPos(halfTime, out halfPosition);
halfPosition += _parent.GetPosition();
Vector3 dp2 = nextPosition - startPos;
Vector3 dp1 = halfPosition - startPos;
Vector3 force = (dp2 - 2 * dp1) * 4 / (fdt * fdt);
Vector3 v0 = (dp2 / fdt) - (force * fdt / 2f);
Vector3 deltaPosPredicted = PhysicsTools.GetMovement(v0, force, fdt);
if (Vector3.Distance(deltaPosPredicted, dp2) > 0.001f)
Debug.Log("position prediction error: " + Vector3.Distance(deltaPosPredicted, dp2));
predictedNextFramePos = deltaPosPredicted + startPos;
Vector3 deltaHPosPredicted = PhysicsTools.GetMovement(v0, force, fdt / 2f);
if (Vector3.Distance(deltaHPosPredicted, dp1) > 0.001f)
Debug.Log("position prediction error: " + Vector3.Distance(deltaHPosPredicted, dp1));
//drawing the startpos, midpos, endpos triangle
Debug.DrawLine(startPos, startPos + dp2, Color.magenta, Time.fixedDeltaTime * 2);
Debug.DrawLine(startPos, startPos + dp1, Color.red, Time.fixedDeltaTime * 2);
Debug.DrawLine(startPos + dp2, startPos + dp1, Color.red, Time.fixedDeltaTime * 2);
//drawing v0 and force
Debug.DrawLine(startPos, startPos + force, Color.gray, Time.fixedDeltaTime * 2);
Debug.DrawLine(startPos, startPos + v0, Color.blue, Time.fixedDeltaTime * 2);
//drawing the parabola arc
{
Vector3 pos = startPos;
Vector3 vel = v0;
for (int i = 0; i < 10; i++)
{
Vector3 offset = PhysicsTools.GetMovementUpdateVelocity(ref vel, force, Time.fixedDeltaTime / 10f);
Debug.DrawLine(pos, pos + offset, Color.green, Time.fixedDeltaTime * 2);
pos += offset;
}
}
// Old version
// Vector3 deltaPosition = nextPosition - _rigidbody.position;
// Vector3 velocity = deltaPosition / t;
// SetPhysicsState(_rigidbody.position, velocity, time);
//Applying results
SetPhysicsState(startPos, v0, time);
_rigidbody.AddForce(force / 2f, ForceMode.Acceleration);
I am using my physics helper class
public static class PhysicsTools
{
public static Vector3 GetMovement(Vector3 velocity, Vector3 force, float time)
{
return (velocity * time) + 0.5f * force * (time * time);
}
public static Vector3 GetMovementUpdateVelocity(ref Vector3 velocity, Vector3 force, float time)
{
Vector3 ret = (velocity * time) + 0.5f * force * (time * time);
velocity += force * time;
return ret;
}
}
Everything works fine, but if, and only if, I divide the force by two when applying it. My own simulation using PhysicsTools does not require such tampering.
Here's a picture of one of my tests, with the force factor applied to both the physics engine and the PhysicsTools simulation. You can see that the simulated lines go off into the distance, but not the actual projectile, which stays in its weird pentagram, as it should.
Here we can see it working as intended (still with the applied force reduced)
My question, why would I need to divide that damned force by two?
Well here folks is what happen when you make assumptions.
I assumed that ForceMode.Continuous meant that the force would be applied continuously through the frame. That is not the case.
The unity physics engine is incapable of any kind of continuous acceleration or parabola casting. Any object moves in straight lines, and AddForce simply modifies the velocity right then and there.
It turns out that simply dividing the force by two was enough to reset the starting velocity to my previous linear solution to the problem, and that the only reason that objects seemed to react outside of the polygon was that my bullet collider was much wider than I thought it was.
Please read this post for more information: https://answers.unity.com/questions/696068/difference-between-forcemodeforceaccelerationimpul.html
The only solution to the problem is to increase the physics framerate, or to use your own raycasting solution, which comes with a slew of other problems.

Unity movement near point

I have spaceships that chase each other. They currently move to their target exactly, but I want to replicate physics you might see on water or in zero gravity, where the object would overshoot its target, turn and move back toward it. Possibly hovering around the target back and forth. I've tried addforce and addrelativeforce, and velocity, but those don't seem to give me the desired effect. Any ideas?
This is my code...
Vector3 dir = (new Vector3(x, y, 0) - transform.parent.position).normalized;
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
Quaternion q = Quaternion.AngleAxis(angle, Vector3.forward);
float heading = Mathf.Atan2(dir.x, dir.y);
transform.parent.rotation = Quaternion.Slerp(transform.parent.rotation, Quaternion.Inverse(Quaternion.Euler(0f, 0f, heading * Mathf.Rad2Deg)), Time.deltaTime * 12f);
//transform.parent.position += dir * speed * Time.deltaTime;
//rb.AddForce(dir);
rb.velocity = new Vector3(dir.x * speed, dir.y * speed, 0);
Here's how I achieve that AI in a 2D space shooter:
void FixedUpdate()
{
var target = Player != null ? PlayerObject : Target; // If player is dead, go for secondary target
Vector2 dir = -(Vector2)(transform.position - target.transform.position + targetOffset); // Direction
// dir.magnitude is the distance to target
if (dir.magnitude > reaction * rangeModifier) // If I'm far away from the target, slowly rotate towards it
{
// calculate angle toward target and slowly rotate child ship object toward it in the speed of turnRate
float attackAngle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg - 90;
ship.transform.rotation = Quaternion.Slerp(ship.transform.rotation, Quaternion.AngleAxis(attackAngle, Vector3.forward), turnRate * Time.deltaTime);
}
else // If I'm close to the target just keep going straight ahead and fire until I'm too far away again
{
My.weapon.Shoot(dir);
}
_rb.velocity = ship.transform.up.normalized * My.Movementspeed; // Add velocity in the direction of the ship's rotation
}
This will give you an AI that goes in eight (8) shapes around the target. If you have many objects running this code I recommend adding a random offset to the target, to simulate swarming and more realistic flight aim.
I added comments to explain some details of the code.

Calculating Angular Velocity Needed to Travel a Certain Distance Via the Magnus Effect

Aight so I've been at this for several days and with how much it's slowing the project down I've made the hard decision to come beg the internet.
I'm working on a physics-based golf game in Unity and am working on implementing the magnus effect. This is my calculation for tangential force based on torque:
void FixedUpdate()
{
float _BallRadius = .0427f;
float _halfmass = Mathf.Sqrt(_BallRadius*.5f );
float _vv = new Vector3(GetComponent<Rigidbody>().velocity.x, 0,
GetComponent<Rigidbody>().velocity.z).magnitude;
//MAGNUS FORMULA (SIDESPIN ONLY)//
float _mag = -((1.429f * Mathf.Abs(_vv)* (2 * Mathf.PI * _halfmass *
GetComponent<Rigidbody>().angularVelocity.y))) * (_BallRadius * .5f);
Vector3 _xmagdir = new Vector3(GetComponent<Rigidbody>().velocity.x, 0, GetComponent<Rigidbody>().velocity.z);
GetComponent<Rigidbody().AddForce(Vector3.Cross(_xmagdir.normalized,Vector3.up)*_mag, ForceMode.Acceleration);
}
This works well enough, but now I need to figure out the correct amount to give the ball to get this result:
Diagram of Desired Curve
I've tried several different approaches, and I've come to the conclusion that this is mostly comes down to trigonometry. Here is where I'm at currently (this is before the tangential direction is applied to the final velocity vector):
Vector3 GetMagnusTorque(float Distance, Vector3 V_BallForce, float xdir)
{
float _BallRadius = .0427f;
float _halfmass = Mathf.Sqrt(_BallRadius*.5f);
Vector3 v = GO_Camera.transform.InverseTransformDirection(V_BallForce);
float HorizontalRange = Distance;
//THIS IS THE LATERAL DISTANCE WE NEED TO TRAVEL//
float opposite = (Mathf.Abs(Mathf.Tan(Mathf.Deg2Rad * xdir))) * (HorizontalRange*.5f);
//THIS IS THE TIME IT WILL NEED TO TAKE//
float time = ((2 * v.magnitude * Mathf.Sin((2 * (v.normalized.y )))) / -Physics.gravity.y);
//THIS IS THE SPEED THE MAGNUS EFFECT WILL NEED TO PRODUCE//
float linearSpeed =Mathf.Abs((opposite/time))*Mathf.Sign(xdir)Mathf.Abs((opposite/time))*Mathf.Sign(xdir)*(Time.timeScale+Time.fixedDeltaTime);
return GO_Camera.transform.TransformDirection((((linearSpeed /( 2 * Mathf.PI * _halfmass * (_BallRadius * .5f))) / 1.429f))*Mathf.Deg2Rad*GO_PGolfBall.transform.up);
}
Logic behind this
The result is very inconsistent based on xdir and V_BallForce, sometimes travels further, sometimes barely at all. This has been a pretty sobering adventure in discovering that I'm pretty bad at math.
If anyone has any advice for this issue I would forever be in your debt.
Update: Here is some visualization of the issue:
visualization
Update: I figured I should include a code sample of the function GetMagnusTorque is called from, to better put things in context:
void BallApplyForce()
{
//_rmult is a [0,1] value that is dependent on the accuracy of the player's swing//
float _rmult = GetRMult();
//GETS BALL READY BY ENABLING PHYSICS AND SETTING ITS ROTATION TO THE CAMERA//
GO_PGolfBall.transform.rotation = new Quaternion(0, GO_Camera.transform.rotation.y, GO_Camera.transform.rotation.z, GO_Camera.transform.rotation.w);
GO_PGolfBall.GetComponent<SCR_GOLFBALLCONTROL>().B_PhysicsActive = true;
//YDIR = PITCH, XDIR = YAW. V_ContactPoint is a [-1,1] Vector2 used similar to Mario Golf//
Vector3 _vdir = Vector3.zero;
float ydir = ((Mathf.Rad2Deg * DIC_Clubs[I_CurrentClub].LandAngle) + (-2.5f * (.1f) * (DIC_Clubs[I_CurrentClub].LoftAngle * Mathf.Rad2Deg)));
float _xdir = Mathf.Rad2Deg * (-(V_ContactPoint.x) * (DIC_Clubs[I_CurrentClub].LoftAngle)*3f);
_vdir.y = ydir;
_vdir = _vdir.normalized;
_vdir.y *= Mathf.Rad2Deg;
//MAX DISTANCE OF THE CLUB BEING USED TO METERS//
float _dist = ((DIC_Clubs[I_CurrentClub].MaxDistance * F_UPower) * _rmult) * .9144f;
//GET FORWARD AND UPWARDS VELOCITIES BASED ON THE DISTANCE GIVEN, XDIR ISN'T FACTORED FOR NOW//
float Vi = Mathf.Sqrt(_dist * -Physics.gravity.y / (Mathf.Sin(Mathf.Deg2Rad * _vdir.y * 2)));
float Vy, Vz;
Vy = Vi * Mathf.Sin(Mathf.Deg2Rad*_vdir.y);
Vz = Vi * Mathf.Cos(Mathf.Deg2Rad*_vdir.y);
GO_Camera.transform.eulerAngles = new Vector3(0, GO_Camera.transform.eulerAngles.y, 0);
Vector3 _velo = GO_Camera.transform.TransformVector(new Vector3(0f, Vy, Vz));
//CALCULATE VERTICAL ANGULAR VELOCITY, THIS DOESNT NEED TO FOLLOW ANY SORT OF PATTERN FOR NOW//
float _verRoll = Mathf.Sign(V_ContactPoint.y - .1f) * ((.7135f) * _dist) * Mathf.Sin(DIC_Clubs[I_CurrentClub].LoftAngle +
((Mathf.Abs(V_ContactPoint.y + .1f)) * (DIC_Clubs[I_CurrentClub].LaunchAngle))) * 60 * Mathf.Deg2Rad;
GO_PGolfBall.GetComponent<Rigidbody>().AddTorque(Vector3.Scale(GO_PGolfBall.transform.right, new Vector3(1, 0, 0)) * _verRoll, ForceMode.VelocityChange);
//CALCULATE HORIZONTAL ANGULAR VELOCITY//
Debug.Log("MAGNUS CALC:" + GetMagnusTorque(_dist, _velo, _xdir));
GO_PGolfBall.GetComponent<Rigidbody>().AddTorque(GetMagnusTorque(_dist, _velo, _xdir), ForceMode.VelocityChange);
//APPLY XDIR TO THE CAMERA ANGLE AND RECALCULATE VELOCITY//
GO_Camera.transform.eulerAngles = new Vector3(0, GO_Camera.transform.eulerAngles.y+_xdir, 0);
_velo = GO_Camera.transform.TransformVector(new Vector3(0f, Vy, Vz));
//APPLY VELOCITY//
GO_PGolfBall.transform.GetComponent<Rigidbody>().AddForce(_velo, ForceMode.VelocityChange);
}
Update 11/21: I'm currently in the process of faking it through trial and error. But for those still interested in solving this anomaly, I may have found in issue in how I'm calculating the direction to apply the side-spin derived force to the ball. Hitting the ball with a lob wedge, which has a maximum distance of 70 yards and a loft angle of 60 deg, the desired curve looks something like this: Lob Wedge This obviously makes no sense, because the ball flies behind the golfer for a short period of time. I'll report back with the final numbers and calculations, though it will not answer the question it could help point it in the correct direction.

Change audio pitch based on rigidbody speed

We have some sound for when the player is moving or rolling being the player is a ball. We want to increase the pitch of the audio the faster the ball goes. I tried the below code but it doesn't do anything. I think it's because the value of p comes out too small.
I remember reading somewhere that there is something built in to handle this but I can't think of where I saw or it what it was called.
Thanks in advance!
void FixedUpdate()
{
#if UNITY_EDITOR || UNITY_STANDALONE
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector3 move = new Vector3(-moveHorizontal, 0.0f, -moveVertical);
move = move * (speed / 15f);
//maxSpeed = maxSpeed / 5;
#else
// Player movement in mobile devices
// Building of force vector
Vector3 move = new Vector3(-Input.acceleration.x, 0.0f, -Input.acceleration.y);
// Adding force to rigidbody
move = move * (speed / 15f);
//move = movement * speed * Time.deltaTime;
#endif
rigidbdy.AddForce(move);
var p = rigidbdy.velocity.magnitude / speed;
audio.pitch = Mathf.Clamp(p, 1.0f, 2.0f); // p is clamped to sane values
//Limits the max speed
if (rigidbdy.velocity.magnitude > maxSpeed)
{
rigidbdy.velocity = rigidbdy.velocity.normalized * maxSpeed;
}
}
You can use the map function for easy control over the pitch value.
float mapValue(float mainValue, float inValueMin, float inValueMax, float outValueMin, float outValueMax)
{
return (mainValue - inValueMin) * (outValueMax - outValueMin) / (inValueMax - inValueMin) + outValueMin;
}
You pass in AudioSource.pitch to the mainValue parameter.
For the inValueMin value, you pass in the default/MIN value of the Rigidbody.velocity.magnitude which is 0.
For the inValueMax value, you pass in the MAX value your ball can go.
You can easily determine this number with Debug.Log("RB: " + ballRigidbody.velocity.magnitude); and running the game. 10 seems to be fine for this. You must determine your own value.
The default AudioSource.pitch value is 1, so outValueMin parameter should be 1.
The outValueMax parameter will be the maximum pitch you think is acceptable to you. I found 1.5 to be ok for this so 1.5 will be used for outValueMax.
Whatever you get from the mapValue function is what you assign to the AudioSource.pitch. This gives you much more control over the pitch of you sound. You can read more about this function on the Arduino site.
Remove your current Audio code and replace it with this:
float rigidBodyMangintude = rigidbdy.velocity.magnitude;
float pitch = mapValue(rigidBodyMangintude, 0f, 10f, 1f, 1.5f);
audio.pitch = pitch;
Debug.Log("Pitch: " + pitch);
The mapValue function is at the top of this answer.

Categories