How to make Touch Controls like mini Golf King - unity3d

Goal
Hello Guys! I am new with unity 3-D . I want to make controls exactly like Mini Golf king . Like when ball stops pointers becomes visible on the screen . On clicking that pointer and dragged its scale changes and points always towards ball.
This is what I want to achieve
MY Approach
Initially according to my knowledge I Have implemented something using ray-cast. In the pointer I placed 3-d pointer as child object of ball . when i touch on the screen I send a ray cast to world from screen that detects Ball if i am touching Ball the i show Pointer and on every point on the screen i am continuously sending ray-casts on the world and changing pointer direction accordingly but i think it is not the good way on performance point
RaycastHit GenerateRayCast(Vector2 position, Camera camera,LayerMask mask) { // GENERATE RAY CAST AT GIVEN DIRECTION IN 3D WORLD
RaycastHit hito;
Ray ray = camera.ScreenPointToRay(position);
Physics.Raycast(ray, out hito, rayLength, mask);
return hito;
}
#endregion
#region Updatemethod
void FixedUpdate () {
if (IsBallInStopPos) { // IF BALL IN STATIC POSITOIN THEN LISTEN FOR TOUCH INPUTS
//if (Input.GetMouseButton(0) && !EventSystem.current.IsPointerOverGameObject())
if (Input.touchCount > 0 && !EventSystem.current.IsPointerOverGameObject()) // CHECK FOR SREEN TOUCH
{
touchInput = Input.touches[0];
// touchInput = Input.mousePosition; // get touch information
hitObject = GenerateRayCast(touchInput.position, Camera.main, mask);
if (touchInput.phase == TouchPhase.Began)
{
if (hitObject.collider.name == "Pointer") { // IF TOUCHED ON POINTER :
if (touchPointSet == false) {
touchPointSet = true;
touchStartPoint = hitObject.point; // SETTING START TOUCH POINT
}
}
}
else if (touchInput.phase == TouchPhase.Moved) {
newScale= Mathf.Clamp (Vector3.Distance(pointerChild.transform.position , hitObject.point),1f,2f);
pointerPivot.transform.localScale = Vector3.one * newScale;// SETTING SCALE ACCORDING TO DRAG VALUE
if (touchPointSet == true) { // WHEN TOUCH DRAGGED AND PREVIOULY POINTER IS TOUCHED ROTATE THE POINTER AT GIVEN DIRECTION
newDirection = new Vector3(hitObject.point.x, transform.position.y, hitObject.point.z);
transform.LookAt(2 * transform.position - newDirection);
canShoot = true;
}
}
}
else if (touchInput.phase == TouchPhase.Ended)
{
if (newScale <= 1f)
return;
if (IsBallInStopPos && canShoot )
{
//shooted = true;
newScale = newScale -1;// AS SCALE VALUE IS BETWEEN 1 AND 2 SO SUBSTACT FROM TO MAKE IT BETWEEN
pointerPivot.gameObject.SetActive(false);
rigidBody.constraints = RigidbodyConstraints.None;
GetComponent<Rigidbody>().AddRelativeForce(new Vector3(0f, 0f, 10000f * newScale));
// Time.timeScale = 2f;
canShoot = false;
IsBallInStopPos = false; // AFTER SHOOT : after the ball is shooted now the ball is not in staticstatic condition
}
}
}

Related

Unity 2D. Why raycasting does not work properly?

With this code I am changing color of gun object if raycast can hit player. Color changes as soon as Player walks into raycast radius. However color won't switch back from red to green as soon as player either goes away (more than Range distance) or hide behind walls. It will eventually switch back to green but not right away. Can you please help me figure out what seems to be the problem?
void Update()
{
Vector2 targetPos = Target.position;
Direction = targetPos - (Vector2)transform.position;
RaycastHit2D rayInfo = Physics2D.Raycast(transform.position, Direction, Range, layerMask);
if (rayInfo)
{
if (rayInfo.collider.gameObject.tag == "Player")
{
if (Detected == false)
{
Detected = true;
Gun.GetComponent<SpriteRenderer>().color = Color.red;
}
}
else
{
if (Detected == true)
{
Detected = false;
Gun.GetComponent<SpriteRenderer>().color = Color.green;
}
}
}
}
When your raycasthit doesn't hit anything with specified LayerMask you don't change your gun's color maybe this is the issue. if your reset your gun color it can solve. Here is the example
//define gunColor variable it is default color of your gun
Color gunColor=Color.green;
void Update()
{
Vector2 targetPos = Target.position;
Direction = targetPos - (Vector2)transform.position;
RaycastHit2D rayInfo = Physics2D.Raycast(transform.position, Direction, Range, layerMask);
if (rayInfo)
{
gunColor=rayInfo.collider.gameObject.CompareTag("Player")?Color.red:Color.green,
}else{
//when raycasthit doesnt hit any thing set gun color to green;
gunColor=Color.green;
}
//update gun color
Gun.GetComponent<SpriteRenderer>().color = gunColor;
}

Unity2D object rotation on Mobile device

I want to know if there is a way to rotate the object only when the user click and drag on the gameobject. If the user click and drag anywhere else, the gameobject should not rotate.
Object with this script on it is rotating no matter where the user clicks.
void Update()
{
if (Input.touchCount > 0)
{
touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Moved)
{
Vector2 dir = Camera.main.ScreenToWorldPoint(Input.GetTouch(0).position) - Camera.main.ScreenToWorldPoint(transform.position);
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
Quaternion rotation = Quaternion.AngleAxis(angle - 90, Vector3.forward);
transform.rotation = rotation;
transform.rotation = rotationZ * transform.rotation;
}
}
Your current code indeed only checks for touches .. it doesn't matter whether these are "hitting" anything or not.
You could e.g. use a Physics.Raycast in order to check if your touch is above an object using
// store the direction from object to touch when the touch started
private Vector2 startDirection;
// store the objects orientation when the touch started
private Quaternion startRotation;
// only process touch moved if the touch actually started above this object
private bool isValidTouch;
// The collider of this object
[SerializeField] Collider _collider;
// The main camera
[SerializeField] Camera _mainCamera;
private void Awake()
{
if(!_collider) _collider = GetComponent<Collider>();
if(!_mainCamera) _mainCamera = Camera.main;
}
void Update()
{
if (Input.touchCount > 0)
{
touch = Input.GetTouch(0);
var touchPosition = touch.position;
switch(touch.phase)
{
case TouchPhase.Began:
// Check if this touch actually is over this object
if(_collider.RayCast(_mainCamera.ScreenPointToRay(touchPosition), out var hit))
{
// Now store initial data
// You want to do ONLY ONE:
// - Either transform the touch into worldspace (more complicated)
// - OR transform the position into screenspace (easier)
// You definitely do not want to do both!
startDirection = (touchPosition - (Vector2)_mainCamera.WorldToScreenPoint(transform.position));
startRotation = transform.rotation;
isValidTouch = true;
}
break;
case TouchPhase.Moved
if(isValidTouch)
{
// Get the current touch direction
var direction = (touchPosition - (Vector2)_mainCamera.WorldToScreenPoint(transform.position));
// Get the angle between them
var differenceAngle = Vector3.SignedAngle(startDirection, direction, Vector3.forward);
// rotate the original rotation according to the current angle
Quaternion rotation = startRotation * Quaternion.AngleAxis(differenceAngle, Vector3.forward);
transform.rotation = rotation;
}
break;
case TouchPhase.Ended
isValidTouch = false;
break;
}
}
}

Unity camera trembling on collision

The script works perfectly, exactly as it should, except when zooming too much and the SphereCast collides with the mesh. When it hits a mesh, I want the camera to just stop, and not try to go through the mesh.
if(Input.touchCount == 2)
{
Touch first = Input.GetTouch(0);
Touch second = Input.GetTouch(1);
origin = mainCamera.transform.position;
direction = mainCamera.transform.forward;
RaycastHit hit;
if(Physics.SphereCast(origin, sphereRadius, direction, out hit, maxDistance))
{
inCollision = true;
}
else
{
inCollision = false;
}
if(first.phase == TouchPhase.Began || second.phase == TouchPhase.Began)
{
initialDistance = Vector3.Distance(first.position, second.position);
}
if(first.phase == TouchPhase.Moved || second.phase == TouchPhase.Moved)
{
movedDistance = Vector3.Distance(first.position, second.position) - initialDistance;
if(inCollision)
{
movedDistance = -Mathf.Abs(movedDistance);
}
mainCamera.transform.localPosition = new Vector3(0, 0, mainCamera.transform.localPosition.z + movedDistance * zoomSensitivity * Time.deltaTime);
initialDistance = Vector3.Distance(first.position, second.position);
}
}
I tried to:
Set movedDistance to 0 when SphereCast hits the mesh;
Clamp mainCamera.transform.localPosition.z to mainCamera.transform.localPosition.z, but only when SphereCast hits the mesh, so the z doesn't zoom more than it's current value.
As commented before you all the time move forward, collide, move backwards, don't collide anymore, move forward etc.
You could probably rather not move backwards but check if you would collide if you would move and do not move at all in this case:
if(first.phase == TouchPhase.Began || second.phase == TouchPhase.Began)
{
initialDistance = Vector3.Distance(first.position, second.position);
}
else if(first.phase == TouchPhase.Moved || second.phase == TouchPhase.Moved)
{
movedDistance = Vector3.Distance(first.position, second.position) - initialDistance;
// Only check until here if you WOULD collide
origin = mainCamera.transform.position;
var targetPosition = origin + mainCamera.transform.forward * movedDistance * Time.deltaTime * zoomSensitivity;
direction = mainCamera.transform.forward;
// only move if there WILL BE no collision
if(!Physics.SphereCast(targetPosition, sphereRadius, direction, out var hit, maxDistance))
{
mainCamera.position = targetPosition;
}
initialDistance = Vector3.Distance(first.position, second.position);
}
From the example for SphereCast you can see that you could even take the hit.distance into account in order to at least move the camera the closest possible.

Using RayCastAll instead of RayCast

I have two gameobjects roller and marker. When I use physics raycast it works, but with raycastall it doesn't. I need to use raycastall, because i need to iterate through colliders. When marker is on roller normal raycast doesn't work and I need to use raycastall.
This code works:
if (Input.touchCount > 0)
{
// get mouse position in screen space
// (if touch, gets average of all touches)
Vector3 screenPos = Input.mousePosition;
// set a distance from the camera
screenPos.z = 10.0f;
// convert mouse position to world space
Ray cameraRay = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
Physics.Raycast(cameraRay, out hit);
if (hit.collider.tag == "Floor")
{
transform.position = hit.point;
}
/*
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
// get current position of this GameObject
Vector3 newPos = transform.position;
// set x position to mouse world-space x position
newPos.x = worldPos.x;
newPos.z = worldPos.z-3f;
// apply new position
transform.position = newPos;
*/
if (Input.GetTouch(0).phase == TouchPhase.Ended)
{
Destroy(gameObject);
touchhandler = GameObject.Find("Roller").GetComponent<TouchHandler>();
touchhandler.markerexists = false;
}
}
And this doesn't:
if (Input.touchCount > 0)
{
// get mouse position in screen space
// (if touch, gets average of all touches)
Vector3 screenPos = Input.mousePosition;
// set a distance from the camera
screenPos.z = 10.0f;
// convert mouse position to world space
Ray cameraRay = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit[] hits;
hits = Physics.RaycastAll(Camera.main.ScreenPointToRay(Input.mousePosition), 100.0F);
while (i < hits.Length)
{
RaycastHit hit = hits[i];
if (hit.collider.tag == "Floor")
{
transform.position = hit.point;
}
i++;
}
if (Input.GetTouch(0).phase == TouchPhase.Ended)
{
Destroy(gameObject);
touchhandler = GameObject.Find("Roller").GetComponent<TouchHandler>();
touchhandler.markerexists = false;
}
}

How to check rotation of an object in Unity3d?

I'm a unity3d learner. I have a problem with rotation of an object. I want to rotate objects about 40 degrees along the z axis. If the objects rotation has reached 40 degrees, I want something to happen. Here is my code.
foreach(Touch touch in Input.touches) {
if(touch.phase != TouchPhase.Ended && touch.phase != TouchPhase.Canceled) {
var target = Quaternion.Euler (0, 0,-40);
transform.rotation = Quaternion.Slerp(transform.rotation, target, Time.deltaTime * smooth);
if (transform.rotation.eulerAngles.z == -40) {
toggle = true;
speech = "blah blah blah";
snake = man;
}
}
}
The if(transform.rotation.eulerAngles.z == -40) line of code is not working. So I don't know if the rotation degree has reached 40 degree or not. How do I check if the rotation degree has reached 40 degrees?
I don't understand your code intent.
EulerAngles is not set negative only positive value(Vector3)
-if(eulerAngles.z == -40) is not work you try change value -40 -> 320
If you want scenario ontouch -> object rotation -> event
try this code.
float rotTime = 1f; // rotation duration
Vector3 rotValue = new Vector3(0, 0, -40f); // rotation value
void Update () {
foreach (Touch touch in Input.touches)
if (touch.phase == TouchPhase.Began) OnTouchEvent();
}
void OnTouchEvent()
{
StopCoroutine("rotationCoroutine");
StartCoroutine("rotationCoroutine");
}
IEnumerator rotationCoroutine()
{
float startTime = Time.time;
Vector3 startRot = transform.eulerAngles;
Vector3 endRot = startRot;
endRot += rotValue;
while (Time.time - startTime <= rotTime)
{
transform.eulerAngles = Vector3.Slerp(startRot, endRot,(Time.time - startTime) / rotTime);
yield return null; // wait 1 frame
}
//rotation end
MyAction();
}
void MyAction()
{
Debug.Log("rotation end");
//toggle = true;
//speech = "blah blah";
//snake = man;
}
Good Luck :D