how to limit and clamp distance between two points in a Line renderer unity2d - unity3d

I am making a game which let you click on a ball and drag to draw a line renderer with two points and point it to a specific direction and when release I add force to the ball,
for now, I just want to know how can I limit the distance between those two points like give it a radius.

You can simply clamp it using a Mathf.Min.
Since you didn't provide any example code unfortunately here is some example code I made up with a simple plane with a MeshCollider, a child object with the LineRenderer and a camera set to Orthographic. You probably would have to adopt it somehow.
public class Example : MonoBehaviour
{
// adjust in the inspector
public float maxRadius = 2;
private Vector3 startPosition;
[SerializeField] private LineRenderer line;
[SerializeField] private Collider collider;
[SerializeField] private Camera camera;
private void Awake()
{
line.positionCount = 0;
line = GetComponentInChildren<LineRenderer>();
collider = GetComponent<Collider>();
camera = Camera.main;
}
// wherever you dragging starts
private void OnMouseDown()
{
line.positionCount = 2;
startPosition = collider.ClosestPoint(camera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, transform.position.z)));
var positions = new[] { startPosition, startPosition };
line.SetPositions(positions);
}
// while dragging
private void OnMouseDrag()
{
var currentPosition = GetComponent<Collider>().ClosestPoint(camera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, transform.position.z)));
// get vector between positions
var difference = currentPosition - startPosition;
// normalize to only get a direction with magnitude = 1
var direction = difference.normalized;
// here you "clamp" use the smaller of either
// the max radius or the magnitude of the difference vector
var distance = Mathf.Min(maxRadius, difference.magnitude);
// and finally apply the end position
var endPosition = startPosition + direction * distance;
line.SetPosition(1, endPosition);
}
}
This is how it could look like

I've written the following pseudo code, which may help you
float rang ;
Bool drag=true;
GameObject ball;
OnMouseDrag () {
if(drag) {
//Put your dragging code here
}
if (ball.transform.position>range)
Drag=false;
else Drage=true;
}

Related

how to visualize the direction of a moving gameobject as a vector in the scene?

so I have a moving game object and i want to visualize the direction of its velocity as an arrow ( vector ) ,
I could not find a function that enables me to show that vector in the scene !
Vector3 direction ;
and i'm stuck finding method to show this vector in the scene .
Suppose you're looking for a simple gizmo to visualise the velocity vector. In that case, you could use OnDrawGizmos() to draw it and then enable Gizmos within Game view using the Gizmos toggle located at the right-most of the window:
For the code itself, you could use Handles.ArrowHandleCap to visualise a Rigidbody's velocity as follows:
[RequireComponent(typeof(Rigidbody))]
public class VelocityVisualizer : MonoBehaviour
{
// The length of the arrow, in meters
[SerializeField] [Range(0.5F, 2)] private float arrowLength = 1.0F;
private Rigidbody _rigidbody;
private void Start()
{
_rigidbody = GetComponent<Rigidbody>();
}
private void OnDrawGizmos()
{
if (!Application.isPlaying) return;
var position = transform.position;
var velocity = _rigidbody.velocity;
if (velocity.magnitude < 0.1f) return;
Handles.color = Color.red;
Handles.ArrowHandleCap(0, position, Quaternion.LookRotation(velocity), arrowLength, EventType.Repaint);
}
}
Attach this script as a component of a game object that has a Rigidbody, and it will display its velocity direction using an arrow

How to make the enemy look at the player if the player is not hiding behind a wall?

The enemy must look at the player if the player comes into view. Below is the code how I did it. The problem is that the enemy sees the player through the wall. How can I fix the code so that the enemy does not see the player through obstacles?
I came up with this solution - to filter out all hits to the player, and then additionally filter out those that do not pass through the barrier. However, I don't know how to implement it.
RaycastHit[] hitsInfo = Physics.SphereCastAll(head.position, sightDistance, transform.forward, sightDistance);
for (int i = 0; i < hitsInfo.Length; i++)
if (hitsInfo[i].transform.gameObject.tag == "Player") {
transform.LookAt(hitsInfo[i].transform.position);
transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, 0);
Debug.DrawLine(head.position, hitsInfo[i].transform.position, Color.red, 0.05f, true);
break;
}
Below is an illustration: the player is white, the enemy is blue, the red is a raycast visualization.
You can use math to check whether the player in sight or not and than use raycast to filter all obstacles.
Here is an example:
using UnityEngine;
public class Enemy : MonoBehaviour
{
[SerializeField] private Transform _objectOfInterest;
[SerializeField] private float _sightAngle = 60f;
[SerializeField] private float _maxDistance = 10f;
private void Update()
{
if (InSight(_objectOfInterest))
{
Debug.Log("InSight");
if (BehindObstacle((_objectOfInterest)))
{
Debug.Log("BehindObstacle");
}
}
}
private bool InSight(Transform objectOfInterest)
{
var forwardDirection = transform.forward;
var directionToTarget = objectOfInterest.position - transform.position;
var angle = Mathf.Acos(Vector3.Dot(forwardDirection.normalized, directionToTarget.normalized));
if (angle * 100 <= _sightAngle) return true;
return false;
}
private bool BehindObstacle(Transform objectOfInterest)
{
var direction = objectOfInterest.position - transform.position;
var raycastDistance = direction.magnitude < _maxDistance ? direction.magnitude : _maxDistance;
var ray = new Ray(transform.position, direction.normalized);
var hits = new RaycastHit[16];
var hitsAmount = Physics.RaycastNonAlloc(ray, hits, raycastDistance);
Debug.DrawRay(transform.position, direction.normalized * raycastDistance);
if (hitsAmount == 0) return true;
foreach (var hit in hits)
{
if (hit.transform.TryGetComponent<Player>(out Player player) == false) // Player is empty MonoBehaviour in my case
{
return true;
}
return false;
}
return false;
}
}
All the math are in InSight method. forwardDirection is the direction where an enemy is looking. When we use "targetposition" - "myposition" in directionToTarget we getting vector that pointing from our position to target. Than we use simple function to get the angle at which the player is in relation to the enemy. The function is - angle = Acos(Dot(A, B)) where A and B are normalized vectors.
In BehindObstacle we just making raycast to player position and if there are any obstacle returning true. You can set sight angle and max distance on your opinion.
And don't use this in Update method (i'm using it only for test) or very often, it can cause performance issues.

Circular Slider for Rotation in Unity

For this setup in Unity, there is a circular dial. The values of the rotation are being used to scale the model. The actual scaling code is removed because there is a bug in the way zAngle value is calculated. Ideally it should be in steady increments in all directions, but here, the values jump across horizontal and vertical axes but they grow slowly when the dial is dragged in other directions.
Is there a recommended practice for such cases when using the screen drag position to calculate the rotation value?
zAngle is the final angle value used to rotate the dial and would be also used after some clamping to scale the model, but that is not the requirement here.
public class CircularScaleDial : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
RectTransform rectTransform;
public GameObject model;
[SerializeField] Vector2 delta = Vector2.one;
[SerializeField] CanvasScaler canvasScaler;
private Vector3 startPosition;
[SerializeField] int zAngle;
Vector3 gd1 = Vector3.zero;
private void Awake()
{
rectTransform = GetComponent<RectTransform>();
}
public void OnBeginDrag(PointerEventData eventData)
{
startPosition = GetDelta_3(eventData);
}
int GetAngleFromPosition(Vector2 pos) {
var angleRadians = Mathf.Atan2(pos.y, pos.x);
var angleDegrees = angleRadians * Mathf.Rad2Deg;
return (int)angleDegrees;
}
Vector3 GetDelta_3(PointerEventData data)
{
Vector3 pos = new Vector3(data.position.x, data.position.y, 0);
Vector3 s2vp = Camera.main.ScreenToViewportPoint(pos);
return s2vp;
}
public void OnDrag(PointerEventData data)
{
if (data.dragging)
{
gd1 = GetDelta_3(data);
delta.x = gd1.x;
delta.y = gd1.y;
zAngle = GetAngleFromPosition(delta);
rectTransform.localRotation = Quaternion.Euler(new Vector3(0, 0, zAngle));
}
}
}
It was sort of fixed with a simpler code. Made other tweaks but this was the main change. The essence here is to resolve the delta along y. For this case I just take the y component but ideally we would take a dot product of two values to resolve one along the other.
public void OnBeginDrag(PointerEventData eventData)
{
startV2 = eventData.position;
}
public void OnDrag(PointerEventData data)
{
if (data.dragging)
{
endV2 = data.position;
Vector2 deltaV2 = endV2 - startV2;
deltaV2 = deltaV2.normalized;
rectTransform.Rotate(new Vector3(0, 0, -deltaV2.y), Space.Self);
startV2 = endV2;
}
}

Get difference between rotations and positions

I've got two objects in World Space.
One is a cube has no parent.
The second is a triangle and it has a parent.
I change the cube's position and rotation.
And now I need to put cube at its first position but move triangle in the parent (local) in such position to fit the same position as if the cube won't be placed at the previous position.
Somwhere store the original position and rotation of cube
Vector3 origPosition = cube.transform.position;
Quaternion origRotation = cube.transform.rotation;
Get the offset values between cube and triangle
Vector3 posOffset = triangle.transform.position - cube.transform.position;
Quaternion rotOffset = Quaternion.Inverse(cube.transform.rotation) * triangle.transform.rotation;
(Re)Set cube and Triangle into place
cube.transform.position = origPosition;
cube.transform.rotation = origRotation;
triangle.transform.position = origPosition + posOffset;
triangle.transform.rotation = origRotation * rotOffset;
Example
public class CubeMover : MonoBehaviour
{
public Transform cube;
public Transform triangle;
private Vector3 origPosition;
private Quaternion origRotation;
// Start is called before the first frame update
private void Start()
{
origPosition = cube.transform.position;
origRotation = cube.transform.rotation;
}
[ContextMenu("Test")]
public void ResetCube()
{
Vector3 posOffset = triangle.transform.position - cube.transform.position;
Quaternion rotOffset = Quaternion.Inverse(cube.transform.rotation) * triangle.transform.rotation;
cube.transform.position = origPosition;
cube.transform.rotation = origRotation;
triangle.transform.position = origPosition + posOffset;
triangle.transform.rotation = origRotation * rotOffset;
}
}
(Had no Triangle so I used the Cylinder ... I hope that's fine for you ^^)

Restricting movement of a 2d object to backward only in UNITY

I'm making a simple project in Unity where there is a Ball attached to a SpringJoint2d component the ball is on an angled slope, like in the image below:
I simply want the user to be able to drag the ball backward along the edge of the slope only,in other words I don't want the user to be able to move the ball away from the slope or into it.
I'v been trying several ways I thought could do the job hers the script of the dragging with what I tried:
(This Is the updated version)
public class ball : MonoBehaviour
{
public Rigidbody2D rb;
public Transform spring;
public Transform calcpoint;
private Vector3 start;
private Vector3 end;
private bool isPressed = false;
RaycastHit2D[] hits = new RaycastHit2D[2];
RaycastHit2D[] hits2 = new RaycastHit2D[2];
float factor = 0;
private void OnMouseDown()
{
if (!isPressed)
{
isPressed = true;
rb.isKinematic = true;
}
}
private void OnMouseUp()
{
isPressed = false;
rb.isKinematic = false;
StartCoroutine(release());
}
/// <summary>
/// release the ball from the spring joint after a small amount of time
/// </summary>
/// <returns></returns>
IEnumerator release()
{
yield return new WaitForSeconds(0.1f);
rb.GetComponent<SpringJoint2D>().enabled = false;
}
// Update is called once per frame
void Update()
{
if (isPressed)
{
if (Vector3.Distance(spring.position, rb.position) > 3f || spring.position.x < (rb.position.x - 1)) return;//restrict the dragging of the ball to not go beyond the spring point and not too far back
float angle = 0;
if (checkGround() > 1)//if we hit the slope with the ray cast downward from the mouse/Tap position
{
angle = Mathf.Abs(Mathf.Atan2(hits[1].normal.x, hits[1].normal.y) * Mathf.Rad2Deg); //get angle
factor = (float)(((45 - angle) * 0.02) + 1) * (angle / 45);//an inaccurate formula to offset the ball to be on top of the slope that works just fine with some glitches
rb.position = hits[1].point + new Vector2(0, factor * 1f);//position the ball at the point were the ray cast downward from the mouse hit
//(that puts the ball center on the line of the slope) so I offset it usinf the formula above
}
}
}
private int checkGround()
{
int h = Physics2D.RaycastNonAlloc(Camera.main.ScreenToWorldPoint(Input.mousePosition), -Vector2.up, hits); //cast downwards
return h;
}
}
here are the settings on the ball:
and the slope setup:
The dragging of the ball works fine ,at one point the player could drag it in the air or into the slope, I managed to fix that with the new code so now the player could only drag it on the edge, though my calculations are still a bit flawed and when the slopes angle is changed the ball would dip a bit inside the slope and that causes some problems at release.
The method used to try to solve the problem is simple, when the player start dragging the ball I cast a ray from the mouse downward and pit the ball on the point of impact with the slope ,offsetting it to sit on top of it,right ow the problem is that the offsetting part is not accurate enough.
I hope I explained myself a bit better this time Thanks:)
After lots of trial and error I did manage to come up with a perfect solution to the problem so I thought I might as well share the answer maybe it will help someone.
here is the updated code I changed my method of restricting the movement completely now I use simple linear line equation as shown below:
private void OnMouseDown()
{
if (!isPressed)
{
//cast a ray on the slope
if (checkGround() > 1)
{
angle = Mathf.Abs(Mathf.Atan2(hits[1].normal.x, hits[1].normal.y) * Mathf.Rad2Deg); //get angle
slope = Mathf.Tan(angle * Mathf.Deg2Rad);//get the slope steepiness
}
isPressed = true;
rb.isKinematic = true;
}
}
private void OnMouseUp()
{
isPressed = false;
rb.isKinematic = false;
StartCoroutine(release());
}
void Update() {
if (isPressed)
{
xMove = Camera.main.ScreenToWorldPoint(Input.mousePosition).x - spring.position.x;//get how much the mouse moved backward or forward
if (xMove < -3f ) xMove = -3f; //restrict the drag range to 3 backward
if (xMove > 0.3f) xMove = 0.3f;//restrict the drag range to 0.3 forward
xpos = spring.position.x+xMove;//since the ball and the spring start at exactly the same position the new ball's x position would be the spring x + the x movement we calculated above
ypos = (xMove * slope)- spring.position.y; //the y posistion would be y=mx+b so the the x movement * the slop steepiness - the starting y position
rb.position = new Vector2(xpos, -ypos);//set the new position of the ball
}
}
private int checkGround()
{
int h = Physics2D.RaycastNonAlloc(Camera.main.ScreenToWorldPoint(Input.mousePosition), -Vector2.up, hits); //cast downwards
return h;
}