How to apply an Easing algorithm in Unity? - c#

I want to move the Camera with a smooth motion so I am writing this script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraScroll : MonoBehaviour {
public GameObject targetCamera;
private bool isScrolling = false;
private Vector3 initialPosition;
private Vector3 scrollTarget;
private float scrollDuration;
private float scrollTick;
private float timeStartedScrolling;
#region Easing
float EasingInOutSine(float time, float start, float change, float duration) {
return -change/2 * (Mathf.Cos(Mathf.PI*time/duration) - 1) + start;
}
float EasingOutSine(float time, float start, float change, float duration) {
dynamic td = time/duration;
return change * Mathf.Sin(td * (Mathf.PI/2)) + start;
}
float EasingInOutBack(float time, float start, float change, float duration) {
float td2 = time/(duration/2.0f);
float s = 1.70158f;
if ((td2) < 1.0f) {
return change/2.0f*(td2*td2*(((s*(1.525f))+1.0f)*td2 - (s*(1.525f)))) + start;
} else {
return change/2.0f*((td2-2.0f)*(td2-2.0f)*(((s*(1.525f))+1.0f)*(td2-2.0f) + (s*(1.525f))) + 2) + start;
}
}
#endregion
void Start() {
if (targetCamera == null) {
targetCamera = GameObject.Find("Main Camera");
}
// Tests
//Scroll(new Vector3(4.0f, 0.0f, -10f), 2f); // seems to be working?
//Scroll(new Vector3(4.0f, 0.0f, -10f), 0.3f); // easing gets cut off
Scroll(new Vector3(4.0f, 0.0f, -10f), 1f); // easing gets cut off
}
void Update() {
if (isScrolling) {
scrollTick += Time.deltaTime;
float s = scrollTick / scrollDuration;
float timeSinceStarted = Time.time - timeStartedScrolling;
float percentageComplete = timeSinceStarted / scrollDuration;
if (percentageComplete > 1.0f) {
isScrolling = false;
Debug.Log("Scrolling ended");
} else {
// time, start, change, duration
var easing = EasingInOutSine(Time.time, 0, Mathf.Clamp01(Time.time*scrollDuration), scrollDuration);
targetCamera.transform.position = Vector3.Lerp(initialPosition, scrollTarget, easing);
}
}
}
void Scroll(Vector3 targetPosition, float duration) {
scrollDuration = duration;
scrollTarget = targetPosition;
initialPosition = targetCamera.transform.position;
timeStartedScrolling = Time.time;
isScrolling = true;
}
}
The problem is that the motion of the camera is cutoff and the easing is not applied properly if the duration is set to be 1 second or lower
I'd like the script to be able to use different easing algorithms in the future and play with deltaTime properly, I am not sure if I am doing it the right way

I think you just need a constant change.
With an increasing change the slow down effect of the easing will be overpowered by the exponential movement caused by the increasing change. This effect is most apparent in low duration movements because it will never reach the value ceiling slower so it will keep speeding up until the end.
Since your using your easing algorithm to output a percentage to throw into a lerp, you probably want to use a change of 1 so that the easing interpolates between 0 and 1.

Related

Unity grappling more like Tarzan

I am trying to make a grappling hook more fluent but as of right now it is very choppy and does not have the right feel. It currently makes a line and pulls the player there. I have not tried anything yet because I am not even sure we're to start on fixing this. Here is all the grappling code below. `using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(SFPSC_PlayerMovement))] // PlayerMovement also requires Rigidbody
public class SFPSC_GrapplingHook : MonoBehaviour
{
public bool IsGrappling
{
get { return isGrappling; }
}
private SFPSC_PlayerMovement pm;
private Rigidbody rb;
private int segments;
private void Start()
{
segments = rope.segments;
pm = this.GetComponent<SFPSC_PlayerMovement>();
rb = this.GetComponent<Rigidbody>();
}
private bool isGrappling = false;
private void Update()
{
if (crossHairSpinningPart != null)
{
// we need 2 raycasts bc w/ 1 you can grapple through colliders which isn't good
if (Physics.Raycast(SFPSC_FPSCamera.cam.transform.position, SFPSC_FPSCamera.cam.transform.forward, out hitInfo, maxGrappleDistance, layerMask))
{
hitName = hitInfo.collider.name;
if (Physics.Raycast(SFPSC_FPSCamera.cam.transform.position, SFPSC_FPSCamera.cam.transform.forward, out hitInfo, maxGrappleDistance))
{
if (hitName != hitInfo.collider.name)
goto _else;
crossHairSpinningPart.gameObject.SetActive(true);
crossHairSpinningPart.Rotate(Vector3.forward * crossHairSpinSpeed * Time.deltaTime);
goto _out;
}
}
_else:
crossHairSpinningPart.gameObject.SetActive(false);
}
_out:
if (!isGrappling)
{
if (Input.GetKeyDown(SFPSC_KeyManager.Grapple))
Grapple();
return;
}
else
{
if (!Input.GetKey(SFPSC_KeyManager.Grapple))
UnGrapple();
GrappleUpdate();
return;
}
}
[Header("Properties")]
public float maxGrappleDistance = 100.0f;
public SFPSC_Rope rope;
public float maximumSpeed = 100.0f;
public float deceleration = 2500.0f; // This is how much the player is going to decelerate after stopped grappling
public float deceleratingTime = 1.4f; // This is the time the decelerating is going to act on the player after stopped grappling
public RectTransform crossHairSpinningPart;
public float crossHairSpinSpeed = 200.0f;
public float distanceToStop = 2.0f;
public LayerMask layerMask;
public float grappleCooldown = 1.0f;
private bool isBlocked = false;
private Transform location; // the grappled location
private RaycastHit hitInfo;
private string hitName;
public void Grapple()
{
if (isBlocked)
return;
// we need 2 raycasts bc w/ 1 you can grapple through colliders which isn't good
if (Physics.Raycast(SFPSC_FPSCamera.cam.transform.position, SFPSC_FPSCamera.cam.transform.forward, out hitInfo, maxGrappleDistance, layerMask))
{
hitName = hitInfo.collider.name;
if (Physics.Raycast(SFPSC_FPSCamera.cam.transform.position, SFPSC_FPSCamera.cam.transform.forward, out hitInfo, maxGrappleDistance))
{
if (hitName != hitInfo.collider.name)
return;
// We create a GameObject and we parent it to the grappled object.
// If we don't parent it to the object and the object moves the player is stuck only on one location instead of the moving object.
location = new GameObject().transform;//Instantiate(new GameObject(), hitInfo.point, Quaternion.identity).transform;
location.position = hitInfo.point;
location.parent = hitInfo.collider.transform;
if (decelerateTimer != 0.0f)
StopCoroutine(Decelerate());
pm.DisableMovement();
// Rope attaching
rope.segments = (int)((hitInfo.distance / maxGrappleDistance) * segments);
rope.Grapple(transform.position, hitInfo.point);
rb.useGravity = false;
isGrappling = true;
}
}
}
private Vector3 grappleForce;
public void UnGrapple()
{
if (!isGrappling)
return;
if (location != null)
Destroy(location.gameObject);
if (decelerateTimer == 0.0f)
StartCoroutine(Decelerate());
else
decelerateTimer = 0.0f;
pm.EnableMovement();
// Rope detaching
rope.UnGrapple();
Invoke("UnblockGrapple", grappleCooldown);
rb.useGravity = true;
isGrappling = false;
}
private void UnblockGrapple()
{
isBlocked = false;
}
private float decelerateTimer = 0.0f, max;
private IEnumerator Decelerate()
{
WaitForEndOfFrame wfeof = new WaitForEndOfFrame();
max = deceleratingTime * Mathf.Clamp01(targetDistance / 10.0f) * Mathf.Clamp01(rb.velocity.magnitude / 30.0f);
for (; decelerateTimer < max; decelerateTimer += Time.deltaTime)
{
rb.AddForce(-rb.velocity.normalized * deceleration * (1.0f - decelerateTimer / max) * Mathf.Clamp01(rb.velocity.sqrMagnitude / 400.0f) * Time.deltaTime, ForceMode.Acceleration);
yield return wfeof;
}
decelerateTimer = 0.0f;
}
private Vector3 dir;
private float speed = 0.0f, targetDistance;
private void GrappleUpdate()
{
if (location == null)
return;
targetDistance = Vector3.Distance(transform.position, location.position);
rope.segments = (int)((targetDistance / maxGrappleDistance) * segments);
dir = (location.position - transform.position).normalized;
rb.velocity = Vector3.Lerp(rb.velocity, dir * maximumSpeed * Mathf.Clamp01(targetDistance / (4.0f * distanceToStop)), Time.deltaTime);
// Rope updating
rope.UpdateStart(transform.position);
rope.UpdateGrapple();
}
private Vector3 ClampMag(Vector3 vec, float maxMag)
{
if (vec.sqrMagnitude > maxMag * maxMag)
vec = vec.normalized * maxMag;
return vec;
}
}
`
Try using FixedUpdate instead of Update for physics based work (basically all of your code in Update right now). Update is dependent on your computer's clock speed and refresh rate (more or less), and gets called at fairly irregular intervals, because the next update is called in the next frame, after the present frame has finished processing. FixedUpdate makes it frame-rate independent.
Also, you can cap your framerate using Application.targetFrameRate and cap it to a decent FPS.
You could also multiply your movement with Time.deltaTime for smoother movement, although this is a standard practice and yet debatable for use as a smoothing value.

How can I calculate a moving object speed?

The goal is to show both transform and speed movement speed in text1 and text2.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Follow : MonoBehaviour
{
public Transform targetToFollow;
public Text text;
public Text text1;
public Text text2;
public float lookAtRotationSpeed;
public float moveSpeed;
private float minMoveSpeed = 0f;
private Vector3 originPos;
// Start is called before the first frame update
void Start()
{
originPos = targetToFollow.position;
}
// Update is called once per frame
void Update()
{
Vector3 lTargetDir = targetToFollow.position - transform.position;
lTargetDir.y = 0.0f;
transform.rotation = Quaternion.RotateTowards(transform.rotation,
Quaternion.LookRotation(lTargetDir), Time.time * lookAtRotationSpeed);
var distance = Vector3.Distance(transform.position, targetToFollow.position);
text.text = "Transform Distance From Target " + distance.ToString();
float ms = moveSpeed;
if (distance > 5f)
{
ms = moveSpeed + 0.5f; //move faster
}
else if (distance < 1.5f)
{
ms = Mathf.Max(minMoveSpeed, ms - 0.3f); // move slower
}
else
{
default value that equal moveSpeed here
transform.position = Vector3.MoveTowards(transform.position, targetToFollow.position, Time.deltaTime * ms);
}
if(distance < 0.5f && originPos == targetToFollow.position)
{
ms = 0f;
}
transform.position = Vector3.MoveTowards(transform.position, targetToFollow.position, Time.deltaTime * ms);
originPos = targetToFollow.position;
}
}
Now I'm setting to text the transform distance from the target.
But I want to add now to text1 and text2 the speed movement of the transform and the target.
In the end there will be 3 texts ui's that will show the distance and movement speeds.
I tried like this for the target speed :
var speedPerSec = Vector3.Distance(originPos, targetToFollow.position) / Time.deltaTime;
text1.text = "Target Speed Per Second " + speedPerSec.ToString();
but when the target is moving the text of the speed the speedPerSec.toString() is blinking fast and not smooth like the distance in the first text.
maybe it's blinking because it's showing the speed a lot after the zero point like : 0.00656555 ?
Since you want to track the speed of multiple objects (your transform and the target) the easiest way is to introduce a separate object handling the tracking and use an array of these.
[Serializable]
private class SpeedTracking
{
public Transform trackedObject;
public Text uiOutput;
private Vector3 _position;
private float _speed;
public void Update(float elapsedTime, float changeThreshold)
{
Vector3 newPos = trackedObject.position;
float newSpeed = Vector3.Distance(newPos, _position) / elapsedTime;
if ((newSpeed == 0 && _speed != 0) || Mathf.Abs(newSpeed - _speed) > changeThreshold)
{
_speed = newSpeed;
uiOutput.text = $"speed: {newSpeed:F3}";
}
_position = newPos;
}
}
private const float SPEED_CHANGE_THRESHOLD = 0.001F;
[SerializeField]
private SpeedTracking[] _trackings;
void OnEnable()
{
if (_trackings.Any(t => t.trackedObject == null || t.uiOutput == null))
{
Debug.LogError("at least one invalid tracking found");
enabled = false;
}
}
void Update()
{
//[...]
UpdateTrackings();
}
void UpdateTrackings()
{
foreach (SpeedTracking tracking in _trackings)
{
tracking.Update(Time.deltaTime, SPEED_CHANGE_THRESHOLD);
}
}
I also added a format to your output string (F3) showing only 3 decimal digits. (This alone would solve the flickering, but why update the ui more often than necessary?)
If you only want to know how much velocity of object in specific time
you can do something like this.
public class VelocityDisplayer : MonoBehaviour
{
public Transform Object; // your target gameobject
public Text View; // your text object
private Vector3 _position; // last position of target
private void OnEnable() {
_position = Object.transform.position;
}
private void Update() {
var dt = Time.deltaTime;
var current = Object.transform.position;
var delta = Vector3.Distance(current, _position);
var velocity = delta / dt;
View.text = (velocity).ToString("#,##0.000");
// replace current position
_position = current;
}
}
but this show actual speed which depend on game world/physics of object not your setup movement speed variable
PS1. I prefer to use update because is UI scope
PS2. I prefer to split new class to track object

How can I add a slowdown effect when object is getting close to a target?

using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngine.AI;
public class Waypoints : UnityEngine.MonoBehaviour
{
public List<Transform> waypoints = new List<Transform>();
public float movementSpeed = 5.0f;
public float slowdownSpeed = 1f;
public float rotationSpeed = 2.0f;
public float waypointDistance = 0.1f;
public float slowdownDistance = 7f;
public bool moveBackward = false;
public bool moveLoop = false;
public bool includeTransformPosition = false;
private Transform targetWaypoint;
private int targetWaypointIndex = 0;
private int lastWaypointIndex;
private bool includeTransform = true;
private GameObject go;
// Use this for initialization
void Start()
{
go = new GameObject();
go.transform.position = transform.position;
if (moveBackward && waypoints.Count > 2)
{
lastWaypointIndex = 0;
targetWaypoint = waypoints[waypoints.Count - 1];
}
else
{
lastWaypointIndex = waypoints.Count - 1;
targetWaypoint = waypoints[targetWaypointIndex]; //Set the first target waypoint at the start so the enemy starts moving towards a waypoint
}
}
// Update is called once per frame
void Update()
{
if (includeTransformPosition && includeTransform)
{
waypoints.Insert(0,go.transform);
includeTransform = false;
}
else
{
if (includeTransformPosition == false)
{
waypoints.Remove(go.transform);
includeTransform = true;
}
}
float movementStep = movementSpeed * Time.deltaTime;
float rotationStep = rotationSpeed * Time.deltaTime;
Vector3 directionToTarget = targetWaypoint.position - transform.position;
Quaternion rotationToTarget = Quaternion.LookRotation(directionToTarget);
transform.rotation = Quaternion.Slerp(transform.rotation, rotationToTarget, rotationStep);
float distance = Vector3.Distance(transform.position, targetWaypoint.position);
CheckDistanceToWaypoint(distance);
if(slowdownDistance < 7f)
{
movementSpeed -= movementSpeed * Time.deltaTime;
}
transform.position = Vector3.MoveTowards(transform.position, targetWaypoint.position, movementStep);
}
void CheckDistanceToWaypoint(float currentDistance)
{
if (currentDistance <= waypointDistance)
{
targetWaypointIndex++;
UpdateTargetWaypoint();
}
}
void UpdateTargetWaypoint()
{
if (targetWaypointIndex > lastWaypointIndex)
{
targetWaypointIndex = 0;
}
targetWaypoint = waypoints[targetWaypointIndex];
}
}
At this part I'm trying to slowdown the movement speed but it's not changing the speed at all :
if(slowdownDistance < 7f)
{
movementSpeed -= movementSpeed * Time.deltaTime;
}
What I'm trying to do when the transform start to move increase the speed slowly to some constant speed and then when the transform is getting closer to the waypoint then if the distance is less then 7 decrease the speed down to 0 so the object will stop at the waypoint then after X seconds move back the transform to the transform original position(go.transform) with the same increasing decreasing speed movement.
but I can't even make the first simple slowdown.
you set slowdownDistance to 7, i dont see you ever reducing it below 7 but you have if statement that executes only it it is under 7 do you reduce it elsewhere?
You probably planned to compare slowdownDistance to some distance rather than comparing it to its own value.
If you want to slow down the whole scene you could use a:
while(distance < range)
{
Time.timeScale = 1 - (distance / value);
}
with this you can change the value and so when the player come close time will be slown down mostly.
With the same idea you can do:
while(distance < range)
{
speed = speedInitial - (distance / value);
}
If you want to change just the speed of the player

Quaternion.Lerp clock rotation

I am trying to rotate the hand of the clock on click, but nothing is actually happening, none of the arrows is actually moving so I probably did something wrong... any help would be much appreciated, thank you
script below
public class Clock : MonoBehaviour {
public GameObject minuteHand;
public GameObject hourHand;
private Quaternion targetRotation;
float speed = 0.1f;
void Start()
{
targetRotation = transform.rotation;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
minuteHand.transform.rotation = Quaternion.Lerp(minuteHand.transform.rotation, targetRotation, Time.deltaTime * speed);
}
} }
Your code is only executed exactly in one single frame when the mouse button goes down.
Also that is not how Lerp works. Lerp expects a factor between 0 and 1 and interpolates both values. If you multiply your speed (0.1) and the Time.deltaTime which for 60FPS has a value of about 1/60f = 0.017f you get a resulting factor of about 0.0017f => You will always stay pretty close to the first value
especially since additionally your targetRotation always equals the current transform.rotation!
I assume what you wanted is: Every click move the minute 6° ahead (360°/60 minutes = 6°/minute).
Once a full circle is done move the hour 30° ahead (360°/12 hours = 30°/hour)
public class Clock : MonoBehaviour
{
public GameObject minuteHand;
public GameObject hourHand;
Quaternion originalRotationMinute;
Quaternion originalRotationHour;
int minutes;
int hours;
// Store original orientations
void Start()
{
originalRotationMinute = minuteHand.transform.rotation;
originalRotationHour = hourHand.transform.rotation;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
// First calculate the new time
minutes++;
if(minutes == 60)
{
minutes = 0;
hours++;
if(hours = 12)
{
hours = 0;
}
}
// Now update the rotations with the new time
// I'm assuming here you want to rotate around the Z axis
// if this is not the case change this according to your needs
var targetMinuteRotation = originalRotationMinute * Quaternion.Euler(0, 0, 6.0f * minutes);
var targetHourRotation = originalRotationHour * Quaternion.Euler(0, 0, 30.0f * hours);
minuteHand.transform.rotation = targetMinuteRotation;
hourHand.transform.rotation = targetHourRotation;
}
}
}
This would make the clock "jump" to the target rotations. If what you rather wanted is a smooth rotation (what I assume from the usage of Lerp) I would probably use a Coroutine for that:
public class Clock : MonoBehaviour
{
public GameObject minuteHand;
public GameObject hourHand;
float speed = 0.1f;
Quaternion originalRotationMinute;
Quaternion originalRotationHour;
int minutes;
int hours;
void Start()
{
originalRotationMinute = minuteHand.transform.rotation;
originalRotationHour = hourHand.transform.rotation;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
// cancel the current routines
StopAllCoroutines();
// calculate the new time
minutes++;
if(minutes == 60)
{
minutes = 0;
hours++;
if(hours = 12) hours = 0;
}
// I'm assuming here you want to rotate around the Z axis
// if this is not the case change this according to your needs
var targetMinuteRotation = originalRotationMinute * Quaternion.Euler(0, 0, 6.0f * minutes);
var targetHourRotation = originalRotationHour * Quaternion.Euler(0, 0, 30.0f * hours);
// This time instead of directly setting the values start coroutines
// to rotate the hands smoothly according to the given speed
StartCoroutine (RotateTo(minuteHand, targetMinuteRotation));
StartCoroutine (RotateTo(hourHand, targetHourRotation));
}
}
// rotate the given object to the given target rotation
// according to the given speed
private IEnumerator RotateTo(GameObject obj, Quaternion targetRotation)
{
var targetTransform = obj.transform;
var current = targetTransform.rotation;
// I your are not rotating on the Z axis change this accordingly to your needs
var difference = targetRotation.eulerAngels.z - current.eulerAngles.z;
if(Mathf.Approximately(0, difference)) yield break;
var duration = difference / speed;
var timePassed = 0f;
while(timePassed <= duration)
{
var factor = timePassed / duration;
targetTransform.rotation = Quaternion.Lerp(current, targetRotation, factor);
timePassed += Time.deltaTime;
yield return null;
}
// t be sure set the final rotation once in the end
targetTransform.rotation = targetRotation;
}
}
Typed on smartphone but I hope the idea gets clear
For me only this works. You can't modify speed to make it even slower, but it looks fine (may be it will be helpful to someone)
float speed = 0.1f //how fast rotate
private void FixedUpdate()
{
nextRot = hour * -30;
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(0, 0, nextRot), speed);
}

Move object in fixed Bezier curve using mouse or touch (unity3d)

I am trying to move an object with mouse or touch but on particular fixed curve path after selecting an object.
I created a fixed path using bezier curve, and movement of object in path is working fine if i am using keyboard inputs using Input.GetAxisRaw("Horizontal"), but i want to based on mouse drag or touch.
using System;
using UnityEngine;
public class Collector : MonoBehaviour
{
public Transform startPoint;
public Transform middlePoint;
public Transform endPoint;
public float curveSpeed = 0.5f;
//public float speed = 0f;
private int _direction = 1;
private bool _isObjectSelected;
private Vector3 _mouseLastPosition;
private float _journeyLength;
private Vector3 _offsetPos;
private float _currentTime = 0;
private void Start()
{
_journeyLength = Vector3.Distance(startPoint.position,
endPoint.position);
UpdateJourney(0);
}
private void OnMouseDown()
{
if (_isObjectSelected)
return;
_offsetPos = Vector3.zero;
_mouseLastPosition = Input.mousePosition;
_isObjectSelected = true;
}
private void OnMouseUp()
{
_isObjectSelected = false;
}
private void OnMouseExit()
{
_isObjectSelected = false;
}
private void OnMouseDrag()
{
if (_isObjectSelected)
{
Debug.LogError("Mouse drag");
Vector3 currentPosition = Input.mousePosition;
_offsetPos += currentPosition - _mouseLastPosition;
float distCovered = _offsetPos.y / _journeyLength;
UpdateJourney(distCovered);
_mouseLastPosition = currentPosition;
}
}
private void UpdateJourney(float time)
{
if (time < 0)
time = 0;
else if (time > 1)
time = 1;
_currentTime = time;
transform.position =
QuadraticCurve(startPoint.position,
middlePoint.position,
endPoint.position,
_currentTime);
transform.rotation = Quaternion.Euler(
new Vector3(0, 0,
QuadraticCurve(0, 45, 90, _currentTime)));
}
private void Update()
{
// moving on path using keyboard input
float direction = Input.GetAxisRaw("Horizontal");
if (Math.Abs(direction) > 0.1f)
{
_currentTime += Time.deltaTime * curveSpeed * direction;
UpdateJourney(_currentTime);
}
}
private static Vector3 Lerp(Vector3 start, Vector3 end, float time)
{
return start + (end - start) * time;
}
private static Vector3 QuadraticCurve(Vector3 start, Vector3 middle, Vector3 end, float time)
{
Vector3 point0 = Lerp(start, middle, time);
Vector3 point1 = Lerp(middle, end, time);
return Lerp(point0, point1, time);
}
private static float QuadraticCurve(float start, float middle, float end, float time)
{
float point0 = Mathf.Lerp(start, middle, time);
float point1 = Mathf.Lerp(middle, end, time);
return Mathf.Lerp(point0, point1, time);
}
}
There are a few issues:
You have a Debug.LogError. You shouldn't do this since usually this interrupts the execution. By default the ErrorPause option in the Unity console is enabled. You should either remove this entirely (since Debug.Log in Update in general are very perfomance intense).
One of the main issues here will be that the dragged object does not stay under the mouse but ruther follows the given curve. Therefore, OnMouseExit will get called and OnMouseDrag is interrupted.
You can also simply keep track of the traveled distance using the mouse start position and compare it to the current one. No need for additionally increase a third variable.
I would therefore rather change the code and use
private Vector3 _mouseStartPosition;
// remove OnMouseExit
// remove OnMouseUp
// remove OnMouseDrag
private void OnMouseDown()
{
if (_isObjectSelected) return;
_mouseStartPosition = Input.mousePosition;
_isObjectSelected = true;
}
private void WhileDragging()
{
if (!_isObjectSelected) return;
//Debug.LogError("Mouse drag");
var currentPosition = Input.mousePosition;
var offsetPos = currentPosition - _mouseStartPosition;
// You might want to use the curveSpeed here as well
// like kind of sensitivity
var distCovered = offsetPos.y * curveSpeed / _journeyLength;
UpdateJourney(distCovered);
}
// instead of using OnMouseExit, OnMouseUp and OnMouseDrag rather
// do it in Update
private void Update()
{
if (_isObjectSelected)
{
// use this to detect mouse up instead
if (Input.GetMouseButtonUp(0))
{
_isObjectSelected = false;
}
}
// call it here instead of using OnMouseDrag
WhileDragging();
...
}
Note: This of course still only uses the traveled distance in Y direction. If you rather wanted to get the closest point on the bezier curves this gets a lot more complex and you might want to look it up (Nearest point on cubic bezier curve to a given point, Nearest point on a quadratic bezier curve, Closest point on Bezier spline, etc ...)
Thanks #derHugo,
I will try to resolve the problem with this (Nearest point on cubic bezier curve to a given point, Nearest point on a quadratic bezier curve, Closest point on Bezier spline, etc ...)
although i want a simple solution instead of complex code.
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
Ray ray = _mainCamera.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, Mathf.Infinity))
{
if (hit.transform.gameObject == this.gameObject)
{
_mouseStartPosition = _mainCamera.ScreenToWorldPoint(Input.mousePosition);
_isObjectSelected = true;
}
}
}
if (Input.GetMouseButtonUp(0))
{
if (_isObjectSelected)
{
_isObjectSelected = false;
//Debug.LogError("Gettting false");
}
}
if (_isObjectSelected)
{
Vector3 mousePosition = _mainCamera.ScreenToWorldPoint(Input.mousePosition);
mousePosition.z = 0f;
//Debug.LogError(mousePosition + " , " + transform.position);
Vector3 distanceVector = mousePosition - _mouseStartPosition;
float distance = distanceVector.magnitude;
Vector3 normalDistance = distanceVector.normalized;
//Debug.LogError(distance + " " + normalDistance);
if (distance > 0.1f)
{
if (normalDistance.y > 0.1f)
{
_currentTime -= Time.deltaTime * distance * 3f;
_shieldMovement.UpdateJourney(_currentTime);
}
else if (normalDistance.x > 0.1f)
{
_currentTime += Time.deltaTime * distance * 3f;
_shieldMovement.UpdateJourney(_currentTime);
}
}
_mouseStartPosition = mousePosition;
}
so far, i did this in update method, its not perfect but working ok.

Categories

Resources