Unity objects bend around forcefield - unity3d

I want to achieve this image below where it looks like the towers are being influenced by forcefield.
It looks simple, right?
GameObject towers = GameObject.Find("towers");
GameObject ball = GameObject.Find ("ball");
foreach (Transform tower in towers.transform) {
Vector3 heading = ball.transform.position - tower.position;
float distance = heading.magnitude;
Vector3 direction = heading.normalized;
Vector3 newDir = Vector3.RotateTowards(ball.transform.position, heading, 1, 0.0F);
tower.rotation = Quaternion.LookRotation(newDir);
//Debug.DrawRay(ball.transform.position, newDir, Color.red);
}
This is the result:
The towers in the back are decent but the front is falling in the wrong direction. What happened? Also, is there a way to control how much influence the towers bend depending on the distance from the ball to the tower?

Here is a solution which uses an AnimationCurve to control the rotation angle of your towers according to the distance between them and your ball.
My animation curve has two keys :
(0, 90) // Meaning the tower will be rotated 90° when they are 0 units from the ball
(10, 0) // Meaning the tower will be rotated 0° when they are 10 units from the ball
public AnimationCurve effect;
private GameObject towers ;
private GameObject ball ;
private void Start()
{
towers = GameObject.Find("towers");
ball = GameObject.Find ("ball");
}
private void Update()
{
foreach (Transform tower in towers.transform)
{
Vector3 direction = (ball.position - tower.position);
Vector3 rotationAxis = Vector3.Cross( direction.normalized, Vector3.up );
float angle = effect.Evaluate( direction.magnitude ) ;
tower.rotation = Quaternion.AngleAxis( angle, rotationAxis );
// If you want your towers to look at your ball :
// tower.rotation = Quaternion.LookRotation( direction ) * Quaternion.Euler( -angle, 0, 0 );
}
}

Related

Get closest point between GameObject (inside the cube collider) and 3D cube collider

How do I calculate the distance of a game object (inside a cube collider) from the cube collider surface? The existing calculations were made from the cube surface outwards so I got 0 when I used the collider.closestpoint or collider.closestpointonbounds.
The simplest (but computationally not the cheapest) would be to not rely on your current collider for the distance, but to add a set of small colliders around the edge of the object (so 6 colliders, one per face of the cube). Using Collider.ClosestPoint() on all 6 faces and calculating the distance like that would give you the results you need.
First convert a point to local space.
var localPoint = transform.InverseTransformPoint(worldPoint);
var extents = collider.size * 0.5f;
var closestPoint = localPoint;
Compute the distance to each face.
var disx = extents.x - Mathf.Abs(localPoint.x);
var disy = extents.y - Mathf.Abs(localPoint.y);
var disz = extents.z - Mathf.Abs(localPoint.z);
Find the closest face (smallest distance) and move the closest point along this axis.
if(disx < disy)
{
if (disx < disz)
closestPoint.x = extents.x * Mathf.Sign(localPoint.x); //disx
else
closestPoint.z = extents.z * Mathf.Sign(localPoint.z); //disz
}
else
{
//......
}
Plus the offset of the collider, convert to world space.
closestPoint += collider.center;
transform.TransformPoint(closestPoint);
I don't know how efficient this is, but here is how I solved it:
public static Vector3 ClosetPointOnBounds(Vector3 point, Bounds bounds)
{
Plane top = new Plane(Vector3.up, bounds.max);
Plane bottom = new Plane(Vector3.down, bounds.min);
Plane front = new Plane(Vector3.forward, bounds.max);
Plane back = new Plane(Vector3.back, bounds.min);
Plane right = new Plane(Vector3.right, bounds.max);
Plane left = new Plane(Vector3.left, bounds.min);
Vector3 topclose = top.ClosestPointOnPlane(point);
Vector3 botclose = bottom.ClosestPointOnPlane(point);
Vector3 frontclose = front.ClosestPointOnPlane(point);
Vector3 backclose = back.ClosestPointOnPlane(point);
Vector3 rightclose = right.ClosestPointOnPlane(point);
Vector3 leftclose = left.ClosestPointOnPlane(point);
Vector3 closest = point;
float bestdist = float.MaxValue;
foreach (Vector3 p in new Vector3[] {
topclose, botclose, frontclose, backclose, leftclose, rightclose
})
{
float dist = Vector3.Distance(p, point);
if (dist < bestdist)
{
bestdist = dist;
closest = p;
}
}
return closest;
}
(note: this assumes and axis-aligned box, which is all I needed at the time. If you want to rotate it you will have to do more work to transform the point.)
You can Calculate by Vector3.Distance
some example
float minDistance =2;
float Distance = Vector3.Distance(other.position, transform.position);
if(Distance < minDistance)
{
//some code stuffs
}
else if(Distance > minDistance){
//some code stuffs
}
Useful information about Vector3.Distance and getting Distance from object
source: https://docs.unity3d.com/ScriptReference/30_search.html?q=Distance

2D car control with mouse position in unity 2D

i am new in unity. I want to car game with mouse control to Unity 2D. I was trying this code but not working. Car vibrates when i move mouse over car. I want it to work perfectly when the mouse hovers over the car. how can i do this? my code is as follows:
private void OnMouseOver()
{
// Distance from camera to object. We need this to get the proper calculation.
float camDis = cam.transform.position.y - my.position.y;
// Get the mouse position in world space. Using camDis for the Z axis.
Vector3 mouse = cam.ScreenToWorldPoint (new Vector3 (Input.mousePosition.x, Input.mousePosition.y, camDis));
float AngleRad = Mathf.Atan2 (mouse.y - my.position.y, mouse.x - my.position.x);
float angle = (180 / Mathf.PI) * AngleRad;
body.rotation = angle;
Vector3 temp = Input.mousePosition;
temp.z = 10f; // Set this to be the distance you want the object to be placed in front of the camera.
this.transform.position = Camera.main.ScreenToWorldPoint(temp);
}
I'm not too clear on the effect you want to achieve, but if you just want the object to move and turn gradually instead of instantly changing, that can be achieved using Vector3.MoveTowards and Quaternion.RotateTowards, e.g.:
private void OnMouseOver()
{
// Distance from camera to object. We need this to get the proper calculation.
float camDis = cam.transform.position.y - my.position.y;
// Get the mouse position in world space. Using camDis for the Z axis.
Vector3 mouse = cam.ScreenToWorldPoint (new Vector3 (Input.mousePosition.x, Input.mousePosition.y, camDis));
float AngleRad = Mathf.Atan2 (mouse.y - my.position.y, mouse.x - my.position.x);
float angle = (180 / Mathf.PI) * AngleRad;
//body.rotation = angle; //??
float turnSpeed = 200f;
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.Euler(0, 0, angle), turnSpeed * Time.deltaTime);
Vector3 temp = Input.mousePosition;
temp.z = 10f; // Set this to be the distance you want the object to be placed in front of the camera.
float moveSpeed = 10f;
transform.position = Vector3.MoveTowards(transform.position, Camera.main.ScreenToWorldPoint(temp), moveSpeed * Time.deltaTime);
}
Edit in response to comment: If you want it to move only when the player begins the drag on the car, then yes, putting it in OnMouseDrag() would work. If you want it to move when the player drags from anywhere on the screen, you'd want to put the movement code in Update() and check whether the left mouse button is being held down using Input.GetMouseButton(0).
If you wanted it to keep moving towards the last mouse position (e.g. player can click on the screen and it will move there while the mouse button is not being held down), you'd need to keep the last mouse location in a class variable and move towards that in Update().
Incidentally if you want it to move a bit more like a car, you could always move it forwards while it turns towards the mouse, rather than moving it directly towards the mouse even if it's facing a different direction.
Here's an example but be aware that I've changed a few things that didn't seem necessary to me, like using my.position rather than transform.position. If you use it you may need to adapt it to suit the rest of your code.
public float maxTurnSpeed = 250f;
public float maxSpeed = 8f;
public float stopDistance = 0.5f;
public float slowDistance = 2.5f;
private void Update()
{
if( !Input.GetMouseButton(0) ) // If the mouse button is NOT being held down this frame
return; // Don't move. (Ideally you would decelerate the car rather than stopping it immediately though.)
// Remove the above two lines and move all of this to OnMouseDrag if you want to require the drag to begin on this object to move it.
// Also note: this code now assumes the object begins in the desired z position and doesn't change it, rather than forcing a z position.
// Distance from camera to object. We need this to get the proper calculation.
float camDis = transform.position.z - Camera.main.transform.position.z; // Changed this to use z instead of y as it appeared to be a mistake(?)
// Get the mouse position in world space. Using camDis for the Z axis.
Vector3 mouse = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, camDis));
float distanceFromMouse = Vector3.Distance(transform.position, mouse);
// If we're further away from the mouse than stopDistance, move.
if( distanceFromMouse > stopDistance )
{
float speedMultiplier = 1.0f;
float rotationMultiplier = 1.0f;
// If we're closer to the mouse than slowdistance, slow down proportionately to the remaining distance from stopDistance
if( distanceFromMouse < slowDistance )
{
speedMultiplier = (distanceFromMouse - stopDistance) / (slowDistance - stopDistance);
}
// Reduce turning speed as we approach stopDistance, but not by as much as speed is reduced
if( speedMultiplier < 0.5f )
rotationMultiplier = speedMultiplier * 2f;
float AngleRad = Mathf.Atan2(mouse.y - transform.position.y, mouse.x - transform.position.x);
float angle = (180 / Mathf.PI) * AngleRad;
// Turn the car towards facing the mouse position
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.Euler(0, 0, angle), maxTurnSpeed * rotationMultiplier * Time.deltaTime);
// Move the car towards its transform.right vector.
transform.position += transform.right * (maxSpeed * speedMultiplier * Time.deltaTime);
}
}

Unity - Projectile Motion, find the angle needed to hit coordinates x,y

Plenty of questions asked around the same topic, but nothing seems to be working for me.
The problem is simple, a player and an enemy are on the x,y plane. I want to launch my projectile at a calculated angle in such way that the projectile will hit the enemy at it's coordinates.
I've tried implementing both
Angle of Reach and Angle required to hit x,y
Both of these implementation end up doing the same for me; Shooting but not hitting the target in this manner
Any help or pointers would be much appreciated! Thank you
Here is the code:
public Rigidbody projectile;
public float projectileSpeed;
public float Firerate = 9f;
private float nextfire;
private GameObject enemy;
private float gravity = Physics.gravity.y;
private Vector3 directionalVector;
// Start is called before the first frame update
void Start()
{
enemy = GameObject.FindGameObjectWithTag("enemy");
}
void Update()
{
directionalVector = enemy.transform.position - transform.position;
}
void FixedUpdate()
{
nextfire = Time.time + (1 / Firerate);
float projectileSpeed2 = projectileSpeed * projectileSpeed;
float projectileSpeed4 = projectileSpeed2 * projectileSpeed2;
float x = enemy.transform.position.x;
float y = enemy.transform.position.y;
float x2 = x * x;
float theta = Mathf.Atan(projectileSpeed2-Mathf.Sqrt(projectileSpeed4-gravity*(gravity*x2+2*y*projectileSpeed2))/gravity*x);
print(theta);
Vector3 releaseVector = (Quaternion.AngleAxis(theta, Vector3.up) * directionalVector).normalized;
Debug.DrawRay(transform.position, releaseVector, Color.red,0.5f);
Rigidbody instantiatedProjectile = Instantiate(projectile, transform.position, transform.rotation) as Rigidbody;
instantiatedProjectile.velocity = releaseVector * projectileSpeed;
}
}
Why not avoid the problem of finding the angle, and just move the bullet based on the direction on where it first saw the enemy.
(target.transform.position - transform.position).normalized;
It will return a Vector direction to the target.
When the projectile moves, just move it based on this direction.
No headache needed in calculating angles :)
Edit
I made a function before to 'convert' an angle to direction:
protected Vector2 DetermineBulletMoveDirection(float shootingAngle) {
// Determine the direction of the bullet travel on the x and y axis.
float bulletDirectionX = transform.position.x + Mathf.Sin((shootingAngle * Mathf.PI) / 180);
float bulletDirectionY = transform.position.y + Mathf.Cos((shootingAngle * Mathf.PI) / 180);
// Determines the direction this bullet should be moving.
Vector2 bulletDirection = new Vector2(bulletDirectionX, bulletDirectionY);
return (bulletDirection - (Vector2)transform.position).normalized;
}
It takes in an angle, and converts it into a direction based on where the shooter is currently at.
The angle should start from Vector.down, and rotates clockwise.
The next problem is to find out the angle between you and the enemy.
This is the simplest solution I could think of, here is a diagram first:
Notice that you can use TOACAHSOH on this?
So all you have to do, is to 'virtually' align the Y axis of the shooter to the origin.(Apply the movement to the shooter too!)
Do the same thing for the shooter, but on the x-axis this time.
And you would be able to achieve that state where you have a triangle with a 90-degree.
From there on, you can calculate the angle to rotate from Vector.down to the enemy.
Just make sure you move both of the objects back to it's initial position.
After fighting this for a while I found a solution.
In the end I ended up using the Angle of Reach. The second error was that Mathf.Atan returns radians and not degrees, while Quantion.AngleAxis takes in angles. The third and the final one was the fact that Unity uses left hand coordinate system as opposed to the usual right hand system which I was used to.
Here is the final piece of code:
public class TargetAndShoot : MonoBehaviour
{
public Rigidbody projectile;
public float projectileSpeed;
public float firerate;
private float nextfire;
private GameObject enemy;
private float gravity = Physics.gravity.y;
// Start is called before the first frame update
void Start()
{
enemy = GameObject.FindGameObjectWithTag("enemy");
}
void Update()
{
if (Time.time >= nextfire)
{
nextfire = Time.time + (1 / firerate);
float distance = enemy.transform.position.x - transform.position.x;
Vector3 directionalVector = enemy.transform.position - transform.position;
float v2 = projectileSpeed * projectileSpeed;
float v4 = v2 * v2;
float x = enemy.transform.position.x;
float x2 = x * x;
float y = enemy.transform.position.y;
float theta = 0.5f*Mathf.Asin((gravity * distance) / (projectileSpeed * projectileSpeed));
Vector3 releaseVector = (Quaternion.AngleAxis(theta * Mathf.Rad2Deg, -Vector3.forward) * directionalVector).normalized;
Debug.DrawRay(transform.position, releaseVector*5, Color.cyan, 0.5f);
Rigidbody instantiatedProjectile = Instantiate(projectile, transform.position, transform.rotation) as Rigidbody;
instantiatedProjectile.velocity = releaseVector * projectileSpeed;
}
}
}

Unity platform arm rotation with weapon offset

Hello I'm having trouble with calculating the rotation of my characters arm to account for the guns offset from the shoulder pivot to aim the muzzle of the gun at the mouse. But when it rotates the arms it also has to update the new weapon offsets causing the guns rotation to jitter if the mouse position + weapon offset is closer then the muzzle of the gun.
public void rotateArm(){
Vector3 centerPosition = armPivotPos.position;
Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition)+updatedWeaponOffset();
mousePosition.z = armPivotPos.position.z;
Vector3 dir = centerPosition - mousePosition;
float angle = Mathf.Atan2(dir.y, dir.x) * -Mathf.Rad2Deg;
Quaternion temp = Quaternion.AngleAxis(angle, -Vector3.forward);
armPivotPos.eulerAngles = new Vector3(temp.eulerAngles.x, temp.eulerAngles.y, temp.eulerAngles.z);
}
public vector3 updatedWeaponOffset(){
Vector3 weaponOffset = armPivotPos.position - muzzlePos.position;
return weaponOffset;
}

Tilt Control Similar to Temple run

I want to create tilt effect just like temple run.I have a character controller(player) in the game that is moving on its forward by controller.Move(transform.forward) after which I am apply tilt to it to lean it left and right .Previously for the tilt I tried modifying the player position by using transform.translate /tranform.position directly through the accelerometer readings like this :
mytransform.translate(acceleration.x*Time.delaTime*5.0f);
but that had a problem that when I shake the device my camera starts to jerk and the player also then I used the following code to create tilt on positive Z axis
Vector3 dir = new Vector3(accel.x*8f, 0,0);
if (dir.sqrMagnitude > 1)
{
dir.Normalize();
}
dir.x = Mathf.Round(dir.x * 10f) / 10f;
//mytemp is used for temp storage of player position added with the acceleration
mytemp = mytransform.position+(mytransform.right*dir.x*Time.deltaTime*5.0f);
Vector3 diffVec=Vector3.zero;
///position of the element on which the player is colliding;
Vector3 col_pos=Collidingelement.transform.position;
Vector3 unitvec=new Vector3(1,0,0);
//removing x and z of the collider
diffVec= Vector3.Scale(col_pos,unitvec);
//nullify the y,z of the updated mytransform position so that the distance is only measured on X
Vector3 ppos = Vector3.Scale(mytemp,unitvec);
//calculate the distance between player & colliding element
disti=Vector3.Distance( ppos,diffVec);
disti=Mathf.Clamp(disti,-1.5f,1.5f);
disti = Mathf.Round(disti * 10f) / 10f;
//update the player position and apply tilt to it if the distance is less than 1.5f
if(disti<=1.50f)
{
mytransform.position=mytemp;
}
now this has a problem that if lets say I have a value of 0.1 in the acceleration its keep on update my temp and if the distance is less my player will start leaning towards a side though I held my device on same position and acceleration value was always 0.1
Why don't you just calculate delta acceleration.
bool isMoved = false;
Vector2 lastAccel = Vector2.Zero;
Vector2 margin = new Vector2(0.05f, 0.05f); //not processing if its in margin.
Vector2 deltaAccel = currAccel - lastAccel ;
if(deltaAccel.x < margin.x)
{
//don't process
isMove = false;
}
else
{
isMove = true;
lastAccel = currAccel;
}
if(isMove)
{
//Process with deltaAccel here
}