Unity: Touch Delay is too high - unity3d

I want to make a game, where you can throw items in 2D via drag and drop. This works fine, but the tracking of grabbing them is very bad. If I touch them in the top half the tracking won't or barely work.
Are there other methods to prevent this?
public class DropsBehaviour : MonoBehaviour {
//Declaring Variables
public int force = 10;
float maxSpeedPreset = 5;
float maxSpeed = 5;
float MaxThrowSpeed = 8;
GameManager gm;
Rigidbody rig;
Camera cam;
bool selected = false;
bool once = false;
void Start() {
gm = GameManager.FindMe();
rig = GetComponent<Rigidbody>();
cam = Camera.main;
}
void Update() {
if (selected) {
rig.AddForce((cam.ScreenToWorldPoint(Input.mousePosition) - transform.position) * force);
rig.velocity = Vector3.zero;
} else {
if (!once) {
maxSpeed = maxSpeedPreset * gm.gameSpeed;
rig.velocity = new Vector3(Mathf.Clamp(rig.velocity.x, -maxSpeed, maxSpeed), Mathf.Clamp(rig.velocity.y, -maxSpeed, maxSpeed), Mathf.Clamp(rig.velocity.z, -maxSpeed, maxSpeed));
} else {
maxSpeed = maxSpeedPreset;
rig.velocity = new Vector3(Mathf.Clamp(rig.velocity.x, -MaxThrowSpeed, MaxThrowSpeed), Mathf.Clamp(rig.velocity.y, -MaxThrowSpeed, MaxThrowSpeed), Mathf.Clamp(rig.velocity.z, -MaxThrowSpeed, MaxThrowSpeed));
}
}
}
void OnMouseDown() {
if (tag != "NoInteract") {
selected = true;
once = true;
}
}
void OnMouseUp() {
selected = false;
}
}

I see quite a few issues in the code snippet you posted that could cause stuttering.
First, if you are making something in 2D, it's recommended to use Rigidbody2D and Collider2D.
Then, you shouldn't edit a rigidbody's velocity by hand, as stated in the doc. That's what add force is made for.
When updating anything related to physics, you should do it in FixedUpdate, not Update.
I assume you also forgot to put " once = false " on the first pass ? Really not sure about this one.
I edited your script following the points stated before, while keeping the rigidbody 3D. I dind't tested it, but hopefully it will correct the weird drag behavior you're experiencing! (note that I removed the GameManager part since i can't know what it does)
public class DropsBehaviour : MonoBehaviour
{
public int force = 10;
private const float MaxSpeedPreset = 5;
private float _maxSpeed = 5;
private const float MaxThrowSpeed = 8;
private Rigidbody _rig;
private Camera _cam;
private bool _selected;
private bool _once;
private Vector3 _targetVelocity;
private void Start()
{
_rig = GetComponent<Rigidbody>();
_cam = Camera.main;
}
private void Update()
{
if (_selected) return;
if (!_once)
{
_maxSpeed = MaxSpeedPreset * 10;
_targetVelocity = new Vector3(Mathf.Clamp(_rig.velocity.x, -_maxSpeed, _maxSpeed),
Mathf.Clamp(_rig.velocity.y, -_maxSpeed, _maxSpeed),
Mathf.Clamp(_rig.velocity.z, -_maxSpeed, _maxSpeed));
}
else
{
_maxSpeed = MaxSpeedPreset;
_targetVelocity = new Vector3(Mathf.Clamp(_rig.velocity.x, -MaxThrowSpeed, MaxThrowSpeed),
Mathf.Clamp(_rig.velocity.y, -MaxThrowSpeed, MaxThrowSpeed),
Mathf.Clamp(_rig.velocity.z, -MaxThrowSpeed, MaxThrowSpeed));
_once = false; //Dunno if it's intended or not ?
}
}
private void FixedUpdate()
{
if (_selected)
{
_rig.AddForce((_cam.ScreenToWorldPoint(Input.mousePosition) - transform.position) * force);
_rig.velocity = Vector3.zero;
return;
}
var velocity = _rig.velocity;
var velocityChange = _targetVelocity - velocity;
_rig.AddForce(velocityChange, ForceMode.VelocityChange);
}
private void OnMouseDown()
{
if (CompareTag("NoInteract")) return;
_selected = true;
_once = true;
}
private void OnMouseUp()
{
_selected = false;
}
}
On a side note, I personnaly don't like tags, I would use a LayerMask instead since it's somewhat related to physics. I'd probably replace the OnMouse events with a Raycast too, that way you would have a lot more control on what is happening on the physic side.

Related

Char animation breaks when trying to manipulate one arm with Obi Rope in Unity(mobile game)

I am trying to keep my character always idle-animated, even when User stretches his arm by pulling it with touchscreen. The character's arms are purely fists and body, with an obi rope in-between. Body animation works with obi-rope until any hand is being stretched,then the hand part of animation breaks, and it keeps moving but the hand is static. Same applies to another hand and legs :(
I don't know how to even describe it in 1 sentence, so googling didn't help me much. Please, help me out. I don't want to turn off animation when my character stretches arms, it look super artificial
If the code would be of any use:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerHit : MonoBehaviour
{
[SerializeField] private Image circleThisBodyPart;
private Vector3 startScale;
private Vector3 firstPosition;
private Vector3 lastPosition;
private Quaternion startRotation;
private bool isHit = false;
private bool isFirstAttack = true;
private int stage = 0;
private Vector3 posAfterMouseUp;
private void Start()
{
startRotation = transform.rotation;
startScale = transform.localScale;
firstPosition = transform.position;
}
private void LateUpdate()
{
if (isHit)
{
this.transform.rotation = startRotation;
this.transform.localScale = startScale;
if (stage == 1)
{
this.gameObject.transform.position = Vector3.MoveTowards(transform.position, new Vector3(lastPosition.x, lastPosition.y, lastPosition.z), 20f * Time.deltaTime);
if (this.transform.position == lastPosition)
{
stage = 2;
}
}
if (stage == 2)
{
this.gameObject.transform.position = Vector3.MoveTowards(transform.position, firstPosition, 20f * Time.deltaTime);
transform.localScale = startScale;
if (this.transform.position == firstPosition)
{
PlayerAttackBody.isAttack = false;
GlobalEvents.ShowPlayerCircles?.Invoke();
stage = 0;
}
}
}
}
private void OnMouseDown()
{
if (isFirstAttack)
{
GlobalEvents.StartGame?.Invoke();
isFirstAttack = false;
}
PlayerAttackBody.isAttack = true;
GlobalEvents.HidePlayerCircles?.Invoke(circleThisBodyPart);
GlobalEvents.ChangePosToCameraDistance?.Invoke(this.gameObject);
}
private void OnMouseDrag()
{
this.transform.localScale = startScale;
this.transform.rotation = startRotation;
}
private void OnMouseUp()
{
posAfterMouseUp = transform.position;
GlobalEvents.ChangePosToCameraDistance?.Invoke(null);
Vector3 currentPos = this.transform.position;
lastPosition = new Vector3(-currentPos.x, -currentPos.y+2.9f, -currentPos.z);
isHit = true;
stage = 1;
}
}

How to select an object to be picked up whenever the camera is pointed to that object?

I'm trying to pick up objects in unity. I have a gameObject called LookObject. Whenever the camera points to an object, the name of that object will be stored in LookObject, then when I press Space the object gets picked up. it is working but not completely. The issue I'm facing is that when I look at an object then look at another direction, the lookObject still shows the name of the object I was looking at (it doesn't update).
please see this image:
as shown in the image, the reticle is not pointing to the object. but it is still showing Cube as the Look Object.
Here is PlayerInteractions class:
GameObject[] targetObjects;
List<GameObject> targetObjectsList;
[Header("InteractableInfo")]
public float sphereCastRadius = 0.5f;
public int interactableLayerIndex;
private Vector3 raycastPos;
public GameObject lookObject;
private PhysicsObjects physicsObject;
private Camera mainCamera;
public GameObject winUI;
private InteractiveObjects interactiveObjects;
[Header("Pickup")]
[SerializeField] private Transform pickupParent;
public GameObject currentlyPickedUpObject;
private Rigidbody pickupRB;
[Header("ObjectFollow")]
[SerializeField] private float minSpeed = 0;
[SerializeField] private float maxSpeed = 300f;
[SerializeField] private float maxDistance = 8f;
private float currentSpeed = 0f;
private float currentDist = 0f;
[Header("Rotation")]
public float rotationSpeed = 100f;
// Quaternion lookRot;
[SerializeField] GameObject TargetsCanvas;
static bool strikeThrough = false;
private void Start()
{
mainCamera = Camera.main;
targetObjects = GameObject.FindGameObjectsWithTag("TargetObj");
targetObjectsList = new List<GameObject>();
foreach (var obj in targetObjects)
{
var mytext = CreateText(TargetsCanvas.transform);
mytext.text = "• Find The " + obj.name;
Debug.Log(""+ obj.name);
}
}
//A simple visualization of the point we're following in the scene view
private void OnDrawGizmos()
{
Gizmos.color = Color.yellow;
Gizmos.DrawSphere(pickupParent.position, 0.5f);
}
void Update()
{
//Here we check if we're currently looking at an interactable object
raycastPos = mainCamera.ScreenToWorldPoint(new Vector3(Screen.width / 2, Screen.height / 2, 0));
RaycastHit hit;
if (Physics.SphereCast(raycastPos, sphereCastRadius, mainCamera.transform.forward, out hit, maxDistance, 1 << interactableLayerIndex))
{
lookObject = hit.collider.transform.gameObject;
}
//if we press the button of choice
if (Input.GetKeyDown(KeyCode.Space))
{
//and we're not holding anything
if (currentlyPickedUpObject == null)
{
//and we are looking an interactable object
if (lookObject != null )
{
PickUpObject();
if (!targetObjectsList.Contains(lookObject.gameObject))
{
targetObjectsList.Add(lookObject.gameObject);
if (targetObjectsList.Count == targetObjects.Length)
{
// Time.timeScale = 0f;
// winUI.SetActive(true);
// SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
// Time.timeScale = 1f;
}
}
}
}
//if we press the pickup button and have something, we drop it
else
{
BreakConnection();
}
}
}
private void FixedUpdate()
{
if (currentlyPickedUpObject != null)
{
currentDist = Vector3.Distance(pickupParent.position, pickupRB.position);
currentSpeed = Mathf.SmoothStep(minSpeed, maxSpeed, currentDist / maxDistance);
currentSpeed *= Time.fixedDeltaTime;
Vector3 direction = pickupParent.position - pickupRB.position;
pickupRB.velocity = direction.normalized * currentSpeed;
//Rotation//
// lookRot = Quaternion.LookRotation(mainCamera.transform.position - pickupRB.position);
// lookRot = Quaternion.Slerp(mainCamera.transform.rotation, lookRot, rotationSpeed * Time.fixedDeltaTime);
// pickupRB.MoveRotation(lookRot);
}
}
//Release the object
public void BreakConnection()
{
pickupRB.constraints = RigidbodyConstraints.None;
currentlyPickedUpObject = null;
lookObject = null;
physicsObject.pickedUp = false;
currentDist = 0;
}
public void PickUpObject()
{
physicsObject = lookObject.GetComponentInChildren<PhysicsObjects>();
currentlyPickedUpObject = lookObject;
pickupRB = currentlyPickedUpObject.GetComponent<Rigidbody>();
pickupRB.constraints = RigidbodyConstraints.FreezeRotation;
physicsObject.playerInteractions = this;
}
Here is the code attached to objects:
public float waitOnPickup = 0.2f;
public float breakForce = 35f;
[HideInInspector] public bool pickedUp = false;
[HideInInspector] public PlayerInteractions playerInteractions;
private void OnCollisionEnter(Collision collision)
{
if (pickedUp)
{
if (collision.relativeVelocity.magnitude > breakForce)
{
playerInteractions.BreakConnection();
}
}
}
//this is used to prevent the connection from breaking when you just picked up the object as it sometimes fires a collision with the ground or whatever it is touching
public IEnumerator PickUp()
{
yield return new WaitForSecondsRealtime(waitOnPickup);
pickedUp = true;
}
Here is an image of the object inspector:
how can I make it accurately showing the objects I'm looking at?
A simple fix for this would be to set lookObject to null if the SphereCast returns false since that would indicate you are no longer looking at a valid object. Simply adding else lookObject = null; to the end of the first if statement in your Update() method should do the trick.

Creating top down 2D whip mechanic

What Whip should look like
I'm trying to create a whip that can extend in any direction the mouse is facing after pressing a certain button. If there are "grabbable" objects in the way such as an enemy or box, it should latch onto those objects and pull them around to collide with other objects for a certain amount of time.
I know that I need the different sprite shots of the whip extending and latching for animation, but I have no idea how to implement this in code and how to get the whip to stop short if it detects a "grabbable" object.
Attach this script to your player, this should get the job done:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public Transform player = null;
public float speed = 30f;
public string grabbableTag = "grabbable";
private LineRenderer line = null;
private float timer = 0f;
private bool grabbing = false;
private bool reached = false;
private Vector2 from = Vector2.zero;
private Vector2 to = Vector2.zero;
private Vector2 target = Vector2.zero;
private Transform grabbable = null;
private void Start()
{
player = transform;
line = new GameObject("Line").AddComponent<LineRenderer>();
line.startColor = Color.red;
line.endColor = Color.red;
// Assign a material
line.gameObject.SetActive(false);
reached = false;
grabbing = false;
}
private void Update()
{
if(grabbing)
{
Grabbing();
}
else
{
if (Input.GetMouseButtonDown(0))
{
Grab();
}
}
}
private void Grab()
{
Vector3 mousePosition = Input.mousePosition;
mousePosition.z = Vector3.Distance(player.position, Camera.main.transform.position);
to = Camera.main.ScreenToWorldPoint(mousePosition);
from = player.position;
Vector2 direction = (to - from).normalized;
float distance = Vector2.Distance(from, to);
RaycastHit2D[] hits = Physics2D.RaycastAll(from, direction, distance);
grabbable = null;
for (int i = 0; i < hits.Length; i++)
{
if (hits[i].transform.tag == grabbableTag)
{
grabbable = hits[i].transform;
break;
}
}
if (grabbable != null)
{
distance = Vector2.Distance(player.position, grabbable.position);
to = from + direction * distance;
}
grabbing = true;
reached = false;
target = from;
timer = 0;
line.gameObject.SetActive(true);
line.positionCount = 2;
line.SetPosition(0, from);
line.SetPosition(1, from);
}
private void Grabbing()
{
if (reached)
{
target = Vector2.Lerp(target, from, speed * Time.deltaTime);
if (target == from)
{
GrabDone(grabbable);
grabbing = false;
line.gameObject.SetActive(false);
}
}
else
{
target = Vector2.Lerp(target, to, speed * Time.deltaTime);
if(target == to)
{
reached = true;
}
}
line.SetPosition(1, target);
if (reached && grabbable != null)
{
grabbable.position = target;
}
}
private void GrabDone(Transform grabbed)
{
if(grabbed != null)
{
// Do somthing ...
Destroy(grabbed.gameObject);
}
}
}

How can I drag the mouse on a map to navigate it?

I am making a map game in unity. I want to drag the mouse on the map resulting in the player viewing different areas of the map (yes, like Hears of Iron IV). I have tried using the 'onDrag' method but I can not find the proper code for it. Can someone please link some documentation or required code for this method?
Thank you.
If you want to change camera position you can simply do it with this code:
public float dragSpeed = 2;
private Vector3 dragOrigin;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
dragOrigin = Input.mousePosition;
return;
}
if (!Input.GetMouseButton(0)) return;
Vector3 pos = Camera.main.ScreenToViewportPoint(dragOrigin - Input.mousePosition);
Vector3 move = new Vector3(pos.x * dragSpeed, 0, pos.y * dragSpeed);
transform.Translate(move, Space.World);
}
With this, you can start and do other things.
If you want to move the map instead of the camera you can also try :
private bool isDragging;
public void OnMouseDown()
{
isDragging = true;
}
public void OnMouseUp()
{
isDragging = false;
}
void Update()
{
if (isDragging) {
Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
transform.Translate(mousePosition);
}
}
this work as Drag and Drop any 2d objects in unity.
the other answer does not work since the map should also move while button is held.. here is the fix
public float dragSpeed = 2;
private Vector3 dragOrigin;
private bool dragStarted = false;
if (Input.GetMouseButtonDown(1))
{
dragOrigin = Input.mousePosition;
dragStarted = true;
}
if (Input.GetMouseButtonUp(1))
{
dragStarted = false;
return;
}
if (dragStarted)
{
Vector3 pos = main_camera.ScreenToViewportPoint(dragOrigin - Input.mousePosition);
Vector3 move = new Vector3(pos.x * dragSpeed, 0, pos.y * dragSpeed);
main_camera.transform.Translate(move, Space.World);
}
this also assumes your camera is pointed downwards or facing north..

How to have same size of objects in different screen resolutions?

I'm working on a 2D game in Unity. I'm developing it for android and it's in portrait mode. I have placed one wall on left side of screen and one wall on right side of screen. The problem is the distance between those 2 walls is different in different screen resolutions. So, my character can't jump from one wall to other with the same velocity being applied.
I'm attaching an image to make my point clear.
What can I do so that in different devices, I get the same distance between these 2 walls? Any help will be appreciated.
EDIT: This is how I'm placing the walls right now.
screenLeft = Camera.main.ScreenToWorldPoint (new Vector3 (0f, 0f, 0f)).x;
screenRight = Camera.main.ScreenToWorldPoint (new Vector3 (Screen.width, 0f, 0f)).x;
rightWallSizeX = rightWall.GetComponent<SpriteRenderer> ().bounds.size.x;
leftWallSizeX = leftWall.GetComponent<SpriteRenderer> ().bounds.size.x;
rightWall.transform.position = new Vector3 (screenRight - rightWallSizeX/2, 0f, 0f);
leftWall.transform.position = new Vector3 (screenLeft + leftWallSizeX/2, 0f, 0f);
If you design your level using the editor:
Then the output aspect will be correct regardless its value:
As a bonus I've crafted you some code that lets the player stick to a wall and jump to the opposite side of it :D
using UnityEngine;
namespace Assets
{
public class PlayerController : MonoBehaviour
{
private const string Ground = "Ground";
private const string Wall = "Wall";
private Rigidbody2D _body;
private Vector2 _cNormal;
private GameObject _cWall;
private bool _joyAction;
private float _joyClimb;
private float _joyMove;
private State _state;
public float ForceClimb = 20.0f;
public float ForceJump = 5.0f;
public float ForceJumpFromWall = 10.0f;
public float ForceMove = 10.0f;
public float MaxMove = 15.0f;
private void Start()
{
_state = State.Air;
}
private void OnEnable()
{
_body = GetComponent<Rigidbody2D>();
}
private void Update()
{
_joyMove = Input.GetAxis("Horizontal");
_joyClimb = Input.GetAxis("Vertical");
_joyAction = Input.GetButtonDown("Fire1");
}
private void FixedUpdate()
{
var air = _state == State.Air;
var ground = _state == State.Ground;
var wall = _state == State.Wall;
if (air || ground)
{
var canJump = ground && _joyAction;
if (canJump)
{
var force = new Vector2(0.0f, ForceJump);
_body.AddForce(force, ForceMode2D.Impulse);
_state = State.Air;
}
var move = transform.InverseTransformDirection(_body.velocity).x;
if (move < MaxMove)
{
var force = new Vector2(_joyMove*ForceMove, 0.0f);
_body.AddRelativeForce(force);
}
}
else if (wall)
{
var climbing = Mathf.Abs(_joyClimb) > 0.0f;
if (climbing)
{
_body.AddForce(new Vector2(0, ForceClimb*_joyClimb));
}
else
{
var jumpingOut = _joyAction;
if (jumpingOut)
{
TryUnstickFromWall();
_body.AddForce(_cNormal*ForceJumpFromWall, ForceMode2D.Impulse);
}
}
}
}
private void OnGUI()
{
GUILayout.Label(_state.ToString());
}
public void OnCollisionEnter2D(Collision2D collision)
{
var c = collision.collider;
var t = c.tag;
if (t == Ground)
{
_state = State.Ground;
TryUnstickFromWall(); // fixes wall-sticking
}
else if (t == Wall && _state == State.Air) // jumping to wall
{
var wall = collision.gameObject;
var joint2D = wall.AddComponent<FixedJoint2D>();
var contact = collision.contacts[0];
var normal = contact.normal;
Debug.DrawRay(contact.point, normal, Color.white);
// stick 2 wall
joint2D.anchor = contact.point;
joint2D.frequency = 0.0f;
joint2D.autoConfigureConnectedAnchor = false;
joint2D.enableCollision = true;
_body.constraints = RigidbodyConstraints2D.FreezePositionX;
_body.gravityScale = 0.125f;
// save these
_cWall = wall;
_cNormal = normal;
// update state
_state = State.Wall;
}
}
public void OnCollisionExit2D(Collision2D collision)
{
if (collision.collider.tag == Ground)
{
_state = State.Air;
}
}
private void TryUnstickFromWall()
{
if (_cWall != null)
{
_body.constraints = RigidbodyConstraints2D.None;
_body.gravityScale = 1.0f;
var joint2D = _cWall.GetComponent<FixedJoint2D>();
if (joint2D != null) Destroy(joint2D);
_cWall = null;
}
}
}
internal enum State
{
Air,
Ground,
Wall
}
}
To try it:
create a layout like the one in picture 1
add a Rigidbody2D and BoxCollider2D to all of them
set their rigidbodies to be kinematic except for the player
add this script to the player
set walls tag to Wall
set ground tag to Ground
Then tweak it to your needs ...
EDIT:
You will have to encompass transform.InverseTransformDirection(_body.velocity).x with Mathf.Abs() to be correct, also at times the collision normal will be reversed making the player stuck to the wall, I let you figure this one :)