How do I make a pool cue rotate around the cue ball? - c#

I want the pool cue to rotate around the cue ball as the player drags the mouse, I've played some pool games on the internet and they all seem to work this way. This will eventually be a browser game.
This game isn't mine, but it has the basic mechanic that I want (I don't care about the lines that are displayed, I just want the rotate and hit mechanic) https://www.crazygames.com/game/8-ball-billiards-classic
I have tried some orbiting scripts so far, none of them work.
I've tried some scripts that make objects orbit based on time hoping to make it so it orbits with the mouse dragging instead of time. I also can't get the cue to constantly face the cue ball.
This code has gotten me the closest.
public int vertexCount = 40;
public float lineWidth = 0.2f;
public float radius;
public bool circleFillscreen;
//circle variables
static float timeCounter = 0;
float width;
float height;
private LineRenderer lineRenderer;
private void Awake()
{
lineRenderer = GetComponent<LineRenderer>();
SetupCircle();
}
void Update()
{
timeCounter += Time.deltaTime;
float x = Mathf.Cos(timeCounter);
float y = 0;
float z = Mathf.Sin(timeCounter);
}
private void SetupCircle()
{
lineRenderer.widthMultiplier = lineWidth;
if (circleFillscreen)
{
radius = Vector3.Distance(Camera.main.ScreenToWorldPoint(new Vector3(0f, Camera.main.pixelRect.yMax, 0f)),
Camera.main.ScreenToWorldPoint(new Vector3(0f, Camera.main.pixelRect.yMin, 0f))) * 0.5f - lineWidth;
}
float deltaTheta = (2f * Mathf.PI) / vertexCount;
float theta = 0F;
lineRenderer.positionCount = -vertexCount;
for (int i = 0; i < lineRenderer.positionCount; i++)
{
Vector3 pos = new Vector3(radius * Mathf.Cos(theta), radius * Mathf.Sin(theta), 0f);
lineRenderer.SetPosition(i, pos);
theta += deltaTheta;
}
}
#if UNITY_EDITOR
private void OnDrawGizmos()
{
float deltaTheta = (2f * Mathf.PI) / vertexCount;
float theta = 0f;
Vector3 oldPos = Vector3.zero;
for (int i = 0; i < vertexCount + 1; i++)
{
Vector3 pos = new Vector3(radius * Mathf.Cos(theta), 0F,radius * Mathf.Sin(theta));
Gizmos.DrawLine(oldPos, transform.position + pos);
oldPos = transform.position + pos;
theta += deltaTheta;
}
}
#endif
}
Not really getting any error messages, code "works" but doesn't work.

Related

Offscreen target indicators - Unity 2D

I'm trying to point to an object when it's off-screen. The objects are static.
public class SpawnIndicator : MonoBehaviour
{
public Camera UIcamera;
public GameObject point;
public Canvas canvas;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (point.GetComponent<Renderer>().isVisible)
{
gameObject.GetComponent<SpriteRenderer>().color = Color.clear;
}
else
{
gameObject.GetComponent<SpriteRenderer>().color = Color.white;
//POSITION
Vector3 pointPos = UIcamera.WorldToScreenPoint(point.transform.position);
pointPos.z = 0;
pointPos.x = Mathf.Clamp(pointPos.x, (Screen.width * 0.01f), (Screen.width * 0.99f));
pointPos.y = Mathf.Clamp(pointPos.y, (Screen.height * 0.01f), (Screen.height * 0.99f));
pointPos -= new Vector3((Screen.width/2), (Screen.height/2), 0);
gameObject.transform.localPosition = pointPos;
//ROTATION
gameObject.GetComponent<SpriteRenderer>().color = Color.white;
Vector3 vectorToTarget = point.transform.position - gameObject.transform.position;
float angle = Mathf.Atan2(vectorToTarget.y, vectorToTarget.x) * Mathf.Rad2Deg;
angle -= 90;
Quaternion newRotation = Quaternion.AngleAxis(angle, Vector3.forward);
gameObject.transform.rotation = newRotation;
}
}
}
This works fine when the screen size is 1920x1080 (the reference size for my canvas). However at lower sizes, the objects sit awawy from the edges, and in larger sizes they sit outside of the edges.
Figured it out.
WorldToScreenPoint was returning a value based on the reference resolution (1920x1080) so different resolutions ended up mismatched. I think that's what was going wrong, regardless, I've got the solution.
I found this method to convert a world position to canvas position.
public static Vector3 WorldToScreenSpace(Vector3 worldPos, Camera cam, RectTransform area)
{
Vector3 screenPoint = cam.WorldToScreenPoint(worldPos);
screenPoint.z = 0;
Vector2 screenPos;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(area, screenPoint, cam, out screenPos))
{
return screenPos;
}
return screenPoint;
}
I'd also changed the rest of the code a bit while trying to fix this before finding that solution (and coincidentally was using the canvas size) so here's the full script:
public class SpawnIndicator : MonoBehaviour
{
public Camera UIcamera;
public GameObject point;
public Canvas canvas;
Vector3 pointPos;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (point.GetComponent<Renderer>().isVisible)
{
gameObject.GetComponent<SpriteRenderer>().color = Color.clear;
}
else
{
gameObject.GetComponent<SpriteRenderer>().color = Color.white;
Vector3[] canvasPoints = new Vector3[4];
canvas.GetComponent<RectTransform>().GetLocalCorners(canvasPoints);
Vector3 pointPos = WorldToScreenSpace(point.transform.position, UIcamera, canvas.GetComponent<RectTransform>());
float xMin = canvasPoints[0].x * 0.98f;
float xMax = canvasPoints[2].x * 0.98f;
float yMin = canvasPoints[0].y * 0.8f;
float yMax = canvasPoints[2].y * 0.98f;
//POSITION
if (pointPos.x <= xMin) pointPos.x = xMin;
if (pointPos.x >= xMax) pointPos.x = xMax;
if (pointPos.y <= yMin) pointPos.y = yMin;
if (pointPos.y >= yMax) pointPos.y = yMax;
pointPos.z = 0f;
gameObject.transform.localPosition = pointPos;
//ROTATION
gameObject.GetComponent<SpriteRenderer>().color = Color.white;
Vector3 vectorToTarget = point.transform.position - gameObject.transform.position;
float angle = Mathf.Atan2(vectorToTarget.y, vectorToTarget.x) * Mathf.Rad2Deg;
angle -= 90;
Quaternion newRotation = Quaternion.AngleAxis(angle, Vector3.forward);
gameObject.transform.rotation = newRotation;
}
}
public static Vector3 WorldToScreenSpace(Vector3 worldPos, Camera cam, RectTransform area)
{
Vector3 screenPoint = cam.WorldToScreenPoint(worldPos);
screenPoint.z = 0;
Vector2 screenPos;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(area, screenPoint, cam, out screenPos))
{
return screenPos;
}
return screenPoint;
}
}

How to rotate around an object without using unity's built-in functions?

i want to rotate a cube around a 1x1 pipe with arrow keys. (left and right).
The problem is i cannot use built-in functions which sets transform's position and location directly. (Such as transform.lookAt, transform.Rotate or transform.RotateAround). Because I need the vector values of rotation's euler and position for multiple stuff before i modify the value of the transform i want to rotate.
I tried different techniques but no luck so far.
I tried using sin-cos for rotating but could not figure out how to make it work for both rotation and position.
_timer += Time.deltaTime * _larvaSpeed;
float x = -Mathf.Cos(_timer) * distanceBetweenCenter;
float y = Mathf.Sin(_timer) * distanceBetweenCenter;
Here is what i want to achieve. By pressing right or left, move and rotate the object around the pipe.
The result i want. (If i pressed right arrow key a litte bit).
I would appreciate any help. Thank you!
here is the solution using circle mathematics and I strongly recommended not use it, it's just to understand the circular move using circle equation as #FaTaLL ask in the comments
Circle equation...
(x1 - x2)^2 + (y1 - y2)^2 = r^2
x1, y1 is the cube position
x2, y2 is the pipe position
r is the distance between cube and pipe;
using UnityEngine;
public class Rotating : MonoBehaviour
{
public GameObject pipe;
public float Delta;
Vector3 nextpos;
bool compareY;
bool next;
int switchx;
float storeVarAxis;
float x, y, r;
private void Start()
{
next = true;
switchx = 1;
compareY = true;
x = transform.position.x - pipe.transform.position.x;
y = transform.position.y - pipe.transform.position.y;
storeVarAxis = y;
r = Mathf.Sqrt(x * x + y * y);
}
private void Update()
{
if (next)
{
if (compareY == true)
{
y -= Delta * Time.deltaTime;
if (y <= -storeVarAxis)
{
y = -storeVarAxis;
compareY = false;
switchx = -1;
}
}
else
{
y += Delta * Time.deltaTime;
if (y >= storeVarAxis)
{
y = storeVarAxis;
compareY = true;
switchx = 1;
}
}
float v = r * r - y * y;
x = Mathf.Sqrt(Mathf.Abs(v));
nextpos = new Vector3(pipe.transform.position.x + x * switchx, pipe.transform.position.y + y, transform.position.z);
next = false;
}
transform.position = Vector3.MoveTowards(transform.position, nextpos, 1f * Time.deltaTime);
if(Vector3.Distance(transform.position, nextpos) < .05) transform.position = nextpos;
if (transform.position.x.Equals(nextpos.x) && transform.position.y.Equals(nextpos.y)) next = true;
}
}
well, the recommended way is using this simple script
using UnityEngine;
public class Rotating : MonoBehaviour
{
public float speed;
public GameObject pipe;
float r, angle;
Vector3 startpos;
private void Start()
{
r = Mathf.Abs(transform.position.y - pipe.transform.position.y);
angle = 0;
transform.position = pipe.transform.position;
startpos = transform.position;
}
void Update()
{
angle = angle + speed * Time.deltaTime;
transform.rotation = Quaternion.EulerAngles(0,0, angle);
transform.position = startpos + (transform.rotation * new Vector3(r, 0, 0));
}
}
I think Quaternion * Vector3 is what you are looking for. Luckily the box's rotation in its own local coordinates is the same rotation you need to apply to the box's position.
public float speed; //how fast to rotate
public float radius; //radius of the cylinder
public float angle; //angle around it
void Update()
{
if (Input.GetKey(KeyCode.LeftArrow))
{
angle = angle + speed * Time.deltaTime;
}
if (Input.GetKey(KeyCode.RightArrow))
{
angle = angle - speed * Time.deltaTime;
}
//figure out the rotation (from euler angles i guess??)
var quat = Quaternion.EulerAngles(new Vector3(0, angle, 0));
//ok uh what is the box position?? lets just multiply
var unrotated_position = new Vector3(radius, 0, 0);
var rotated_position = quat * unrotated_position;
this.transform.position = rotated_position;
//oh yea and also rotate the box in its own local coordinates
this.transform.rotation = quat;
}

Cast x number of rays in all direction in Unity

I am trying to cast x(500) rays in a circular direction(360) in unity. so that 500 rays get cast on equidistance to form a complete circle. I tried something but it leaves out some area I don't know why. how do I make it full circle?
my code
float number_of_rays = 500;
float angle = 360 / number_of_rays;
float cast_angle = 0;
for (int i = 0; i < number_of_rays; i++)
{
var dir = Quaternion.Euler(0, 0, cast_angle) * transform.right;
RaycastHit2D hit = Physics2D.Raycast(transform.position, dir, Mathf.Infinity);
if (hit)
{
points.Add(hit.point);
Debug.DrawLine(transform.position, hit.point, Color.green,1f);
}
cast_angle += angle;
}
Consider that maybe it just doesn't find a raycast hit in the space you don't get your rays drawn.
This code seems to work for me.
float number_of_rays = 500;
float totalAngle = 360;
float delta = totalAngle / number_of_rays;
Vector3 pos = transform.position;
const float magnitude = 5;
for (int i = 0; i < number_of_rays; i++)
{
var dir = Quaternion.Euler(0, 0, i * delta) * transform.right;
Debug.DrawRay(pos, dir * magnitude, Color.green);
}

2d Trajectory Line Prediction not acurate

I'm current facing a problem where in my trajectory line is very inaccurate
Please see the attached image below
Here's where it goes
Here's my code so far
private void Start()
{
line = new GameObject[maxDots];
for (int i = 0; i < line.Length; i++)
{
var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
go.transform.localScale = new Vector3(0.5f, 0.5f, 0.5f);
line[i] = go;
}
startPos = transform.position;
}
on my OnMouseUp is for shooting the ball
//TEMPORARY
private void OnMouseUp()
{
// Disable IsKenematic
GetComponent<Rigidbody2D>().isKinematic = false;
// Add the Force
Vector2 dir = startPos - (Vector2)transform.position;
GetComponent<Rigidbody2D>().AddForce(dir * force);
//Remove the script (not the gameobject)
Destroy(this);
}
And on my OnMouseDrag is for the ball to keep in radius cause I set a limit for the dragging of the ball
private void OnMouseDrag()
{
DisplayLine();
//Convert mouse potision to world position
Vector2 p = Camera.main.ScreenToWorldPoint(Input.mousePosition);
//Keep it in a certain radius
float radius = 1.8f;
Vector2 dir = p - startPos;
if (dir.sqrMagnitude > radius)
{
dir = dir.normalized * radius;
//Set position
transform.position = startPos + dir;
}
}
Here's my method of displaying the line
void DisplayLine()
{
line[0].transform.position = transform.position;
Vector3 v3 = transform.position;
float y = (forces * (home - transform.position)).y;
float t = 0.0f;
v3.y = 0.0f;
for(int i = 1; i < line.Length; i++)
{
v3 += forces * (home - transform.position) * spacing;
t += spacing;
v3.y = y * t + 0.5f * Physics2D.gravity.y * t * t + transform.position.y;
line[i].transform.position = v3;
}
}
What I am trying to do is that create a trajectory prediction line for my game I know I am almost there but still couldn't get the exact output that I want. Could someone help me. Thank you.
Use the same function to calculate both.
Then you won't have the problem also any change on trajectory calculation results in only one change not two.
Solved it
line[0].transform.position = transform.position;
Vector2 v2 = transform.position;
float y = (force *( startPos - (Vector2)transform.position)).y;
float t = 0.0f;
v2.y = 0.0f;
for(int i = 1; i < line.Length; i++)
{
v2 += force * (startPos - (Vector2)transform.position) * spacing;
t += spacing;
v2.y = y * t + 0.5f * Physics2D.gravity.y * t * t + transform.position.y;
line[i].transform.position = v2;
}
I need to change from vector3 to vector2 so I need to cast (vector2) between those transform.positions.y

Rotate 2D Sprite with Virtual Joystick

I am trying to rotate a GameObject along z-axis using Joystick, but its rotating y-axis. I might missed some math calculation. Any Help??
Reference Image
void FixedUpdate()
{
// get input from joystick
// get input from joystick
rightJoystickInput = rightJoystick.GetInputDirection();
float xMovementRightJoystick = rightJoystickInput.x; // The horizontal movement from joystick 02
float zMovementRightJoystick = rightJoystickInput.y; // The vertical movement from joystick 02
// if there is only input from the right joystick
if (rightJoystickInput != Vector3.zero)
{
// calculate the player's direction based on angle
float tempAngle = Mathf.Atan2(zMovementRightJoystick, xMovementRightJoystick);
xMovementRightJoystick *= Mathf.Abs(Mathf.Cos(tempAngle));
zMovementRightJoystick *= Mathf.Abs(Mathf.Sin(tempAngle));
// rotate the player to face the direction of input
Vector3 temp = transform.position;
temp.x += xMovementRightJoystick;
temp.z += zMovementRightJoystick;
Vector3 lookDirection = temp - transform.position;
if (lookDirection != Vector3.zero)
{
rotationTarget.localRotation = Quaternion.Slerp(rotationTarget.localRotation, Quaternion.LookRotation(lookDirection) * Quaternion.Euler(0, 45f, 0), rotationSpeed * Time.deltaTime);
}
}
}
You don't need most of the code in your question and this is really simple.
1.Find the angle with Mathf.Atan2 then multiple it with Mathf.Rad2Deg.
2.Use Quaternion.Euler(new Vector3(0, 0, angle)) to get the rotation then apply it to the Object.
This should be one in the Update function not FixedUpdate because FixedUpdate is used to move Rigidbody Objects.
public Transform rotationTarget;
public bool flipRot = true;
void Update()
{
rightJoystickInput = rightJoystick.GetInputDirection();
float horizontal = rightJoystickInput.x;
float vertical = rightJoystickInput.y;
float angle = Mathf.Atan2(horizontal, vertical) * Mathf.Rad2Deg;
angle = flipRot ? -angle : angle;
rotationTarget.rotation = Quaternion.Euler(new Vector3(0, 0, angle));
}
If using Rigidbody2D then use Rigidbody2D.MoveRotation in the FixedUpdate function. The rest of the code stays the-same.
public Rigidbody2D rg2d;
public bool flipRot = true;
void FixedUpdate()
{
rightJoystickInput = rightJoystick.GetInputDirection();
float horizontal = rightJoystickInput.x;
float vertical = rightJoystickInput.y;
float angle = Mathf.Atan2(horizontal, vertical) * Mathf.Rad2Deg;
angle = flipRot ? -angle : angle;
rg2d.MoveRotation(angle);
}
EDIT:
But only the problem is when i leave joystick its rotation is setting
to 0 instantly which looks too odd. How can i fix it?
You have to detect when you release the joystick in OnPointerUp then slowly lerp the joystick thump back to the Zero position. You also have to lerp the current target object angle to zero or to its default value and this should be done in a coroutine function. When OnPointerDown is called, stop the current coroutine function. Prevent the code in FixedUpdate from running when finger is released so that it won't interfere with the coroutine function.
For the sake of completeness, below is the combination of a Joystick code and the Rigidbody answer above:
public class VirtualJoystickController : MonoBehaviour,
IDragHandler, IPointerUpHandler, IPointerDownHandler
{
private Image bgImg;
private Image joystickImg;
public float mas_distance = 7f;
void Start()
{
bgImg = GameObject.Find("JoystickBGImage").GetComponent<Image>(); // the joysticks background
joystickImg = GameObject.Find("Joystickthumb").GetComponent<Image>(); // the joystick object to use
}
private Vector3 _inputDirection = Vector3.zero;
//the movementDirection
public Vector3 joystickInputDirection
{
set
{
//Change only if value is different from old one
if (_inputDirection != value)
{
_inputDirection = value;
Debug.Log("Dir: " + _inputDirection);
}
}
get
{
return _inputDirection;
}
}
public void OnDrag(PointerEventData eventData)
{
dragJoyStick(eventData);
}
void dragJoyStick(PointerEventData eventData)
{
Vector3 tempDir = Vector3.zero;
Vector2 pos = Vector2.zero;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle
(bgImg.rectTransform,
eventData.position,
eventData.pressEventCamera,
out pos))
{
pos.x = (pos.x / bgImg.rectTransform.sizeDelta.x);
pos.y = (pos.y / bgImg.rectTransform.sizeDelta.y);
float x = (bgImg.rectTransform.pivot.x == 1) ? pos.x * 2 + 1 : pos.x * 2 - 1;
float y = (bgImg.rectTransform.pivot.y == 1) ? pos.y * 2 + 1 : pos.y * 2 - 1;
tempDir = new Vector3(x, y, 0);
if (tempDir.magnitude > 1)
{
tempDir = tempDir.normalized;
}
joystickImg.rectTransform.anchoredPosition = new Vector3(
tempDir.x * (bgImg.rectTransform.sizeDelta.x / mas_distance),
tempDir.y * (bgImg.rectTransform.sizeDelta.y / mas_distance));
joystickInputDirection = tempDir;
}
}
public void OnPointerDown(PointerEventData eventData)
{
released = false;
//Stop current coroutine
if (retCoroutine != null)
StopCoroutine(retCoroutine);
if (eventData.pointerCurrentRaycast.gameObject == bgImg.gameObject ||
eventData.pointerCurrentRaycast.gameObject == joystickImg.gameObject)
{
OnDrag(eventData);
}
}
public void OnPointerUp(PointerEventData eventData)
{
released = true;
//Stop current coroutine then start a new one
if (retCoroutine != null)
StopCoroutine(retCoroutine);
retCoroutine = StartCoroutine(SlowReturn(returnTime));
}
IEnumerator SlowReturn(float duration)
{
RectTransform thumbstickTransform = joystickImg.rectTransform;
Vector3 toPosition = Vector3.zero;
float counter = 0;
//Get the current position of the object to be moved
Vector2 currentThumb = thumbstickTransform.anchoredPosition;
while (counter < duration)
{
counter += Time.deltaTime;
//Slowly returns thumbstick
Vector2 tempThumbStickVal = Vector2.Lerp(currentThumb, toPosition, counter / duration);
joystickInputDirection = tempThumbStickVal;
thumbstickTransform.anchoredPosition = tempThumbStickVal;
//Slowly returns the target Object to original pos
float tempTargetObjAngle = Mathf.Lerp(angle, originalAngle, counter / duration);
rg2d.MoveRotation(tempTargetObjAngle);
yield return null;
}
}
public float returnTime = 1.0f;
public Rigidbody2D rg2d;
public bool flipRot = true;
const float originalAngle = 0;
bool released = true;
float angle;
Coroutine retCoroutine;
void FixedUpdate()
{
if (released)
return;
float horizontal = joystickInputDirection.x;
float vertical = joystickInputDirection.y;
angle = Mathf.Atan2(horizontal, vertical) * Mathf.Rad2Deg;
angle = flipRot ? -angle : angle;
rg2d.MoveRotation(angle);
}
}
your function calculates a point where you want to look at:
Vector3 temp = transform.position;
temp.x += xMovementRightJoystick;
temp.z += zMovementRightJoystick;
Vector3 lookDirection = temp - transform.position;
this point is on the XZ plane, that is why the car rotates on Y axis
if you want to rotate on Z axis, calculate a point on XY plane like this:
Vector3 temp = transform.position;
temp.x += xMovementRightJoystick;
temp.y += zMovementRightJoystick;
Vector3 lookDirection = temp - transform.position;
PS: i don't know why you multiply with Quaternion.Euler(0, 45f, 0) - that is a constant angle on Y axis, it just means that each lookDirection will be rotated for 45 degrees - i would have to see your scene to know why you need this...

Categories