I'm currently trying to make an object curve a little and not just move straight to the destination how would I do that with Vector3.MoveTowards?
Right now I only have this object moving towards another object called "gotocoin"; it just moves straight any help is appreciated thank you!
void Update()
{
if (going == true)
{
transform.position = Vector3.MoveTowards(transform.position, gotocoin.transform.position, 3 * Time.deltaTime);
}
}
As mentioned in the comments, you can use Bezier Curve to solve your problem. I have written a simple code to demonstrate. It first calculates all the points between an initial position and the target position and then moves the object, which the script is attached to, to each of them. Changing control1 and control2 will change the shape of the curve. The Gizmos method is for debugging and can be removed.
[SerializeField] private Transform target;//the destination
[SerializeField] private Vector3 offset = new Vector3(4.58f, 4.52f, 0);//determines arch of the curve
private const float DistanceToTarget = 1;
private Vector3 _initialPosition;
private List<Vector3> _allPositions;
private int _counter;
private void Start()
{
_initialPosition = transform.position;
_allPositions = new List<Vector3>(100);
for (var i = 0; i < 100; i++)
{
var newPosition = CubicCurve(_initialPosition, _initialPosition + offset, _initialPosition + offset,
target.position, (float)i / 100);
_allPositions.Add(newPosition);
}
}
private void Update()
{
if (_counter < _allPositions.Count)
{
transform.position = Vector3.MoveTowards(transform.position, _allPositions[_counter], Time.deltaTime);
if (Vector3.Distance(transform.position, _allPositions[_counter]) < DistanceToTarget) _counter++;
}
}
private Vector3 CubicCurve(Vector3 start, Vector3 control1, Vector3 control2, Vector3 end, float t)
{
return (((-start + 3 * (control1 - control2) + end) * t + (3 * (start + control2) - 6 * control1)) * t +
3 * (control1 - start)) * t + start;
}
//since _initialPosition is set on start, the drawn curve is from (0,0,0) if the code is not executed
private void OnDrawGizmos()
{
for (var i = 0; i < 100; i++)
{
var newPosition = CubicCurve(_initialPosition, _initialPosition + offset, _initialPosition + offset,
target.position, (float)i / 100);
Gizmos.DrawSphere(newPosition, 1f);
}
}
Try using animation curves in unity's animation system. Hope this helps😀
Related
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DropDown : MonoBehaviour
{
public GameObject dropdownPrefab;
public int numberOfObjects;
public float speed = 1.5f;
public float duration = 5f;
public Vector3 startPos;
public Vector3 endPos;
public float distanceToMove = 2f;
public float gap;
private List<GameObject> dropDownObjects = new List<GameObject>();
private List<Vector3> endPositions = new List<Vector3>();
private void Start()
{
for (int i = 0; i < numberOfObjects; i++)
{
dropDownObjects.Add(Instantiate(dropdownPrefab, transform.parent));
endPositions.Add(new Vector3(dropDownObjects[i].transform.position.x,
dropDownObjects[i].transform.position.y + i + 2,
dropDownObjects[i].transform.position.z) - Vector3.up * distanceToMove);
}
startPos = dropDownObjects[0].transform.position;
}
private void OnMouseDown()
{
StartCoroutine(StartDropObjects());
}
private IEnumerator StartDropObjects()
{
for (int i = 0; i < dropDownObjects.Count; i++)
{
// Random wait period before rotation starts
if (i == 0)
{
//yield return new WaitForSeconds(0);
}
else
{
//yield return new WaitForSeconds(Random.Range(0, 2f));
}
yield return new WaitForSeconds(2f);
StartCoroutine(Drop(dropDownObjects[i].transform, duration, endPositions[i]));
}
}
private IEnumerator Drop(Transform objectToDrop, float duration, Vector3 endpos)
{
float t = 0.0f;
while (t < duration)
{
t += Time.deltaTime;
objectToDrop.transform.position = Vector3.MoveTowards(objectToDrop.transform.position, endpos, speed * Time.deltaTime);
yield return null;
}
}
}
The objects should be moving down but when I'm adding the gap in this case the gap is i + 2 for example if I = 0 then the gap is 2 then I = 1 so the gap will be 3 then I = 2 the gap is 4.
endPositions.Add(new Vector3(dropDownObjects[i].transform.position.x,
dropDownObjects[i].transform.position.y + i + 2,
dropDownObjects[i].transform.position.z) - Vector3.up * distanceToMove);
But I want to have equal gaps between the objects. For example if I will set the gap value to 1 there will be equal spaces/gaps between the objects and if the gap value is 5 then equal 5 between each two objects.
The problem now is that I getting higher each time by 1 so the gaps are not equal.
And also the objects are moving up and not down when I'm doing: + i + 2
In the screenshot the objects moved up instead down and the top two objects seems too close to each other. And the gaps are not equal.
But the idea is when the objects are moving to the target destination endPositions that each one will move to his on endPosition depending on the gap.
So in the end the objects should be like this :
The first one is the one on the most bottom. The last one is the one on the top.
The first dropDownObjects[0] object moved to his endPosition the farest endPosition. In this case distanceToMove is set to 2.
Then dropDownObjects1 is moving to his distanceToMove - the gap. And so on. This is the logic that in the end after all objects move they will have equal gaps.
And the first moved object is the one that should get to the distnaceToMove.
Problem is not here:
endPositions.Add(new Vector3(dropDownObjects[i].transform.position.x,
dropDownObjects[i].transform.position.y + i + 2,
dropDownObjects[i].transform.position.z) - Vector3.up * distanceToMove);
But here:
private IEnumerator Drop(Transform objectToDrop, float duration, Vector3 endpos)
{
float t = 0.0f;
while (t < duration)
{
t += Time.deltaTime;
objectToDrop.transform.position = Vector3.MoveTowards(objectToDrop.transform.position, endpos, speed * Time.deltaTime);
yield return null;
}
}
Because you are using the same duration for all objects, but they have a different distance. The last objects just don't have time to finish their movement.
You can check this by changing your method Drop(...) to this code:
private IEnumerator Drop(Transform objectToDrop, float duration, Vector3 endpos)
{
while (objectToDrop.transform.position != endpos)
{
objectToDrop.transform.position = Vector3.MoveTowards(objectToDrop.transform.position, endpos, speed * Time.deltaTime);
yield return null;
}
}
I would change how you're determining your endPositions to make it easier to configure.
I would define a starting distance and a max distance and you can calculate the gap programmatically by interpolating between the starting distance and minDistanceToMove maxDistanceToMove using Mathf.Lerp with a t of (float)i/(numberOfObjects-1):
public float minDistanceToMove = 0.5f;
public float maxDistanceToMove = 2f;
private void Start()
{
for (int i = 0; i < numberOfObjects; i++)
{
dropDownObjects.Add(Instantiate(dropdownPrefab, transform.parent));
float t = (float)i/(numberOfObjects-1);
float distDown = Mathf.Lerp(minDistanceToMove,maxDistanceToMove,t);
endPositions.Add(new Vector3(dropDownObjects[i].transform.position.x,
dropDownObjects[i].transform.position.y,
dropDownObjects[i].transform.position.z) - Vector3.up * distDown);
}
startPos = dropDownObjects[0].transform.position;
}
Alternatively, you can set only a minDistanceToMove and a gap size and then calculate distDown that way:
public float minDistanceToMove = 0.5f;
public float gapSize = 0.5f;
private void Start()
{
for (int i = 0; i < numberOfObjects; i++)
{
dropDownObjects.Add(Instantiate(dropdownPrefab, transform.parent));
float distDown = minDistanceToMove + gapSize * i;
endPositions.Add(new Vector3(dropDownObjects[i].transform.position.x,
dropDownObjects[i].transform.position.y,
dropDownObjects[i].transform.position.z) - Vector3.up * distDown);
}
startPos = dropDownObjects[0].transform.position;
}
Either way, you then need to change Drop to last for however long it takes for the panel to drop to its destination:
private IEnumerator Drop(Transform objectToDrop, float duration, Vector3 endpos)
{
// duration is unused. It is in the method signature here to avoid
// confusion when swapping with the alternative below
while (objectToDrop.transform.position != endPos)
{
objectToDrop.transform.position = Vector3.MoveTowards(
objectToDrop.transform.position, endpos, speed * Time.deltaTime);
yield return null;
}
}
Alternatively, you can make each panel take the given duration and calculate the speed necessary to traverse the distance:
private IEnumerator Drop(Transform objectToDrop, float duration, Vector3 endpos)
{
float mySpeed = Vector3.Distance(
objectToDrop.transform.position, endpos) / duration;
while (objectToDrop.transform.position != endPos)
{
objectToDrop.transform.position = Vector3.MoveTowards(
objectToDrop.transform.position, endpos, mySpeed * Time.deltaTime);
yield return null;
}
}
I asked this question two years ago. Not ever having success, I abandoned the idea until recently.
I have since been able to semi-fix / replicate the mechanic. However, all the objects seem to jump to their next position, with some duplicating their "leader's" position.
The orange is the head, with the body parts being green.
As you can see from the commented out code below, I have tried multiple permutations to get the children to follow their leader smoothly with the distance between each body-part just being the circle colliders radius.
My thought was, if the "leader" has moved the distance of the radius, then the follower can move towards the leaders old position. This give the leader time to move.
But the only one that seems to semi work, is the un-commented one.
Can anyone see the problem?
FollowTheLeader.cs
public class FollowTheLeader : MonoBehaviour
{
[Header("Head")]
public GameObject bodyPart;
public int bodyLength = 6;
[Header("Move Speed")]
[Range(0.25f, 2.0f)] public float moveMin = 0.5f;
[Range(0.25f, 2.0f)] public float moveMax = 2.0f;
[Header("Change Directions")]
[Range(0.25f, 2.0f)] public float changeMin = 0.5f;
[Range(0.25f, 2.0f)] public float changeMax = 2.0f;
[SerializeField]
private Vector2 oldPosition;
public Vector2 OldPosition { get => oldPosition; set => oldPosition = value; }
[SerializeField]
private Vector2 moveDirection = new Vector2(0, -1);
public Vector2 MoveDirection { get => moveDirection; set => moveDirection = value; }
[Header("Child")]
public int index;
public bool isChild;
public FollowTheLeader leader;
public float leaderDistance;
private CircleCollider2D m_collider2D;
private Rigidbody2D body2d;
private float moveSpeed;
private float moveTimePassed;
private float changeDirInterval;
private void Awake()
{
m_collider2D = GetComponent<CircleCollider2D>();
body2d = GetComponent<Rigidbody2D>();
AddBodyParts();
DefineDirection(moveDirection);
}
private void AddBodyParts()
{
if (isChild || bodyPart == null)
return;
//The head will generate its body parts. Each body part will have reference to the one before it.
FollowTheLeader temp = this;
for (int i = 1; i <= bodyLength; i++)
{
GameObject bp = Instantiate(bodyPart, transform);
bp.transform.SetParent(null);
//bp.transform.position = transform.position;
bp.transform.position = new Vector2(i * m_collider2D.radius, 0);
bp.name = $"Body {i}";
FollowTheLeader c = bp.AddComponent<FollowTheLeader>();
c.isChild = true;
c.index = i;
c.OldPosition = bp.transform.position;
c.leader = temp;
// cache the parent for the next body part
temp = c;
}
}
private void Start()
{
OnNewDirection();
}
private void FixedUpdate()
{
//Store the old postion for the next child
OldPosition = body2d.position;
// If child
if (isChild)
{
// Calculate the leaders distance
leaderDistance = Vector2.Distance(OldPosition, leader.OldPosition);
// We only want to move if the parent is as far away as the m_collider2D.radius.
if (leaderDistance < m_collider2D.radius)
return;
// BARELY ANY MOVEMENT
//body2d.MovePosition(leader.OldPosition.normalized);
//body2d.MovePosition(leader.OldPosition.normalized * moveSpeed);
//body2d.MovePosition(leader.OldPosition.normalized * moveSpeed * Time.deltaTime);
//body2d.MovePosition(leader.OldPosition.normalized * parentDistance * moveSpeed * Time.deltaTime);
//body2d.MovePosition(leader.OldPosition.normalized * m_collider2D.radius * parentDistance * moveSpeed * Time.deltaTime);
//FLYS ALL OVER THE PLACE
//body2d.MovePosition(body2d.position + leader.OldPosition.normalized);
//body2d.MovePosition(body2d.position + leader.OldPosition.normalized * moveSpeed);
//body2d.MovePosition(body2d.position + leader.OldPosition.normalized * moveSpeed * Time.deltaTime);
//body2d.MovePosition(body2d.position + leader.OldPosition.normalized * parentDistance * moveSpeed * Time.deltaTime);
//body2d.MovePosition(body2d.position + leader.OldPosition.normalized * m_collider2D.radius * moveSpeed * Time.deltaTime);
//body2d.MovePosition(body2d.position + leader.OldPosition.normalized * m_collider2D.radius * parentDistance * moveSpeed * Time.deltaTime);
// BARELY ANY MOVEMENT
//body2d.MovePosition(leader.OldPosition * moveSpeed);
//body2d.MovePosition(leader.OldPosition * moveSpeed * Time.deltaTime);
//body2d.MovePosition(leader.OldPosition * parentDistance * moveSpeed * Time.deltaTime);
//body2d.MovePosition(leader.OldPosition * m_collider2D.radius * parentDistance * moveSpeed * Time.deltaTime);
//FLYS ALL OVER THE PLACE
//body2d.MovePosition(body2d.position + leader.OldPosition);
//body2d.MovePosition(body2d.position + leader.OldPosition * moveSpeed);
//body2d.MovePosition(body2d.position + leader.OldPosition * moveSpeed * Time.deltaTime);
//body2d.MovePosition(body2d.position + leader.OldPosition * parentDistance * moveSpeed * Time.deltaTime);
//body2d.MovePosition(body2d.position + leader.OldPosition * m_collider2D.radius * moveSpeed * Time.deltaTime);
//body2d.MovePosition(body2d.position + leader.OldPosition * m_collider2D.radius * parentDistance * moveSpeed * Time.deltaTime);
// KINDA FOLLOWS BUT ALL SEEM TO JUMP INTO THE SAME POSITION AS SEEN IN THE GIF
body2d.MovePosition(leader.OldPosition);
return;
}
// HEAD ONLY
// Countdown to next direction change
moveTimePassed += Time.deltaTime;
if (moveTimePassed >= changeDirInterval)
{
OnNewDirection();
}
// Calculate the next position
body2d.MovePosition(body2d.position + MoveDirection.normalized * moveSpeed * Time.deltaTime);
}
public void OnNewDirection()
{
moveTimePassed = 0;
moveSpeed = Random.Range(moveMin, moveMax);
changeDirInterval = Random.Range(changeMin, changeMax);
RandomDirection();
}
private void RandomDirection()
{
switch (Random.Range(0, 4))
{
case 0:
DefineDirection(Vector2.up);
break;
case 1:
DefineDirection(Vector2.right);
break;
case 2:
DefineDirection(Vector2.down);
break;
case 3:
DefineDirection(Vector2.left);
break;
default:
DefineDirection(Vector2.down);
break;
}
}
public void DefineDirection(Vector2 direction)
{
if (direction.Equals(Vector2.up))
{
MoveDirection = Vector2.up;
}
if (direction.Equals(Vector2.down))
{
MoveDirection = Vector2.down;
}
if (direction.Equals(Vector2.left))
{
MoveDirection = Vector2.left;
}
if (direction.Equals(Vector2.right))
{
MoveDirection = Vector2.right;
}
}
}
Many different ways you can approach it but let me show you one way.
Snake - moves the leader forward, creates new points in the path, manages minions
Path - ring buffer with of all the points
Minion - follow the path based on the distance from the leader
Here's an example with gizmos showing:
Green is the leader
Red is the head of the path
Blue is the tail of the path
The snake is where the main logic is at.
The snake moves forward automatically. When the distance between the leader and the last point is greater than RADIUS we create a new point. We then move all of the minions along the path of points.
public class Snake : MonoBehaviour
{
public const float RADIUS = 1f; // distance between minions
public const float MOVE_SPEED = 1f; // movement speed
public Vector2 dir = Vector2.up; // movement direction
public float headDist = 0f; // distance from path 'head' to leader (used for lerp-ing between points)
public Path path = new Path(1); // path points
public List<Minion> minions = new List<Minion>(); // all minions
public Minion Leader => minions[0];
void Awake()
{
path.Add(this.transform.position);
AddMinion(new Knight());
}
void AddMinion(Minion minion)
{
// Initialize a minion and give it an index (0,1,2) which is used as offset later on
minion.Init(minions.Count);
minions.Add(minion);
minion.MoveOnPath(path, 0f);
// Resize the capacity of the path if there are more minions in the snake than the path
if (path.Capacity <= minions.Count) path.Resize();
}
void FixedUpdate()
{
MoveLeader();
MoveMinions();
}
void MoveLeader()
{
// Move the first minion (leader) towards the 'dir'
Leader.transform.position += ((Vector3)dir) * MOVE_SPEED * Time.deltaTime;
// Measure the distance between the leader and the 'head' of that path
Vector2 headToLeader = ((Vector2)Leader.transform.position) - path.Head().pos;
// Cache the precise distance so we can reuse it when we offset each minion
headDist = headToLeader.magnitude;
// When the distance between the leader and the 'head' of the path hits the threshold, spawn a new point in the path
if (headDist >= RADIUS)
{
// In case leader overshot, let's make sure all points are spaced exactly with 'RADIUS'
float leaderOvershoot = headDist - RADIUS;
Vector2 pushDir = headToLeader.normalized * leaderOvershoot;
path.Add(((Vector2)Leader.transform.position) - pushDir);
// Update head distance as there is a new point we have to measure from now
headDist = (((Vector2)Leader.transform.position) - path.Head().pos).sqrMagnitude;
}
}
void MoveMinions()
{
float headDistUnit = headDist / RADIUS;
for (int i = 1; i < minions.Count; i++)
{
Minion minion = minions[i];
// Move minion on the path
minion.MoveOnPath(path, headDistUnit);
// Extra push to avoid minions stepping on each other
Vector2 prevToNext = minions[i - 1].transform.position - minion.transform.position;
float distance = prevToNext.magnitude;
if (distance < RADIUS)
{
float intersection = RADIUS - distance;
minion.Push(-prevToNext.normalized * RADIUS * intersection);
}
}
}
}
Path is a ring buffer, Head() gives you the newest point that was added, you can use Head(index) to get the head and offset it in a direction(+/-). Minions use it to fetch points that are just behind the head: path.Head(-1).
public class Path
{
public Vector2[] Points { get; private set; }
public int Capacity => Points.Length;
int head;
public Path(int capacity)
{
head = 0;
Points = new Vector2[capacity];
}
public void Resize()
{
Vector2[] temp = new Vector2[Capacity * 2];
for (int i = 0; i < temp.Length; i++)
{
temp[i] = i < Capacity ? Head(i + 1) : Tail();
}
head = Capacity - 1;
Points = temp;
}
public void Add(Vector2 pos)
{
int prev = Mod(head, Capacity);
Next();
int next = Mod(head, Capacity);
Points[next].pos = pos;
}
public Vector2 Head()
{
return Points[head];
}
public Vector2 Head(int index)
{
return Points[Mod(head + index, Capacity)];
}
public Vector2 Tail()
{
return Points[Mod(head + 1, Capacity)];
}
public Vector2 Tail(int index)
{
return Points[Mod(head + 1 + index, Capacity)];
}
void Next()
{
head++;
head %= Capacity;
}
int Mod(int x, int m)
{
return (x % m + m) % m;
}
}
A minion contains an index, which tells us the placement of the minion within the snake (first, second, third). We use this index to get the two points needed for interpolation. path.Head(-0) will give us the leader's point. path.Head(-1) will give us the first minion's point.
public class Minion : MonoBehaviour
{
int index;
public Init(int index)
{
this.index = index;
}
// Move the minion along the path
public void MoveOnPath(Path path, float dist)
{
Vector2 prev = path.Head(-index);
Vector2 next = path.Head(-index + 1);
// Interpolate the position of the minion between the previous and the next point within the path. 'dist' is the distance between the 'head' of the path and the leader
this.transform.position = Vector2.Lerp(prev.pos, next.pos, dist);
}
// Push the minion to avoid minions stepping on each other
public void Push(Vector2 dir)
{
this.transform.position += (Vector3)dir;
}
}
I've stripped out a lot of code to make the example simpler. I hope you get the basic idea and will be able to implement your own solution.
I tried recreating this scenario in 3D and got somewhat the same behaviour as yours.
First, you want to use twice the radius, as each child would otherwise overlap half of the parent since it is the center of the circle that is being moved.
I used a different approach to move the children than you did. The result is a smooth, snake-like motion:
The code is simple enough:
First I rotate the child to point its forward axis at the leader's position
Second the length from the current position to the desired position is calculated. I subtract twice the radius, as the new position would otherwise result in overlapping spheres
I use the translate function to move the GameObject in the forward direction using the calculated magnitude.
// KINDA FOLLOWS BUT ALL SEEM TO JUMP INTO THE SAME POSITION AS SEEN IN THE GIF
//body2d.MovePosition(leader.OldPosition);
transform.LookAt(leader.transform);
float length = leaderDistance - (m_collider2D.radius * 2);
transform.Translate(transform.forward * length, Space.World);
return;
The result is a smooth, predictable motion. You can even turn off kinematic on the rigidbody to enable collisions.
Hope that helped you.
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.
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
I have a Controller which moves my object diagonally. The left arrow should move the player forward and to the left 45 degrees, and the right arrow the same to the right. I would like to move the player relatively to its current position. Right now it moves relatively to the point(0,0,0).
My code:
public class JollyJumper : MonoBehaviour {
protected CharacterController control;
public float fTime = 1.5f; // Hop time
public float fRange = 5.0f; // Max dist from origin
public float fHopHeight = 2.0f; // Height of hop
private Vector3 v3Dest;
private Vector3 v3Last;
private float fTimer = 0.0f;
private bool moving = false;
private int number= 0;
private Vector2 direction;
public virtual void Start () {
control = GetComponent<CharacterController>();
if(!control){
Debug.LogError("No Character Controller");
enabled=false;
}
}
void Update () {
if (fTimer >= fTime&& moving) {
var playerObject = GameObject.Find("Player");
v3Last = playerObject.transform.position;
Debug.Log(v3Last);
v3Dest = direction *fRange;
//v3Dest = newVector* fRange + v3Last;
v3Dest.z = v3Dest.y;
v3Dest.y = 0.0f;
fTimer = 0.0f;
moving = false;
}
if(Input.GetKeyDown(KeyCode.LeftArrow)){
moving = true;
direction = new Vector2(1.0f, 1.0f);
number++;
}
if(Input.GetKeyDown(KeyCode.RightArrow)){
moving = true;
direction = new Vector2(-1.0f, 1.0f);
number++;
}
if(moving){
Vector3 v3T = Vector3.Lerp (v3Last, v3Dest, fTimer / fTime);
v3T.y = Mathf.Sin (fTimer/fTime * Mathf.PI) * fHopHeight;
control.transform.position = v3T;
fTimer += Time.deltaTime;
}
}
}
How can resolve this? Any ideas? Thanks a lot!
The short answer is: you hard-coded two locations you want to jump to: points (1, 1) and (-1, 1). You should create new Vector each time you start jumping. Replace each
direction = new Vector2(1.0f, 1.0f);
with this line:
v3Dest = transform.position + new Vector3(1.0f, 0, 1) * fRange;
and it should work.
While I'm on it, there are some other things I want to point:
There is a lot of floating point error after each jump. Notice that in your code v3T will never be equal to v3Dest (you never actually reach your destination), because you switch the moving flag earlier. You should explicitly set your position to v3Dest when the jump is over.
You are checking jump timers etc. every frame. A more elegent solution is to start a coroutine.
You use a sinusoid as your jump curve, which looks ok, but using a parabola would be conceptually more correct.
Right now it is possible to start next jump mid-air (I'm not sure whether it is intended or not)
Here is some code you may use that avoids those problems:
using System.Collections;
using UnityEngine;
public class Jumper : MonoBehaviour
{
#region Set in editor;
public float jumpDuration = 0.5f;
public float jumpDistance = 3;
#endregion Set in editor;
private bool jumping = false;
private float jumpStartVelocityY;
private void Start()
{
// For a given distance and jump duration
// there is only one possible movement curve.
// We are executing Y axis movement separately,
// so we need to know a starting velocity.
jumpStartVelocityY = -jumpDuration * Physics.gravity.y / 2;
}
private void Update()
{
if (jumping)
{
return;
}
else if (Input.GetKeyDown(KeyCode.LeftArrow))
{
// Warning: this will actually move jumpDistance forward
// and jumpDistance to the side.
// If you want to move jumpDistance diagonally, use:
// Vector3 forwardAndLeft = (transform.forward - transform.right).normalized * jumpDistance;
Vector3 forwardAndLeft = (transform.forward - transform.right) * jumpDistance;
StartCoroutine(Jump(forwardAndLeft));
}
else if (Input.GetKeyDown(KeyCode.RightArrow))
{
Vector3 forwardAndRight = (transform.forward + transform.right) * jumpDistance;
StartCoroutine(Jump(forwardAndRight));
}
}
private IEnumerator Jump(Vector3 direction)
{
jumping = true;
Vector3 startPoint = transform.position;
Vector3 targetPoint = startPoint + direction;
float time = 0;
float jumpProgress = 0;
float velocityY = jumpStartVelocityY;
float height = startPoint.y;
while (jumping)
{
jumpProgress = time / jumpDuration;
if (jumpProgress > 1)
{
jumping = false;
jumpProgress = 1;
}
Vector3 currentPos = Vector3.Lerp(startPoint, targetPoint, jumpProgress);
currentPos.y = height;
transform.position = currentPos;
//Wait until next frame.
yield return null;
height += velocityY * Time.deltaTime;
velocityY += Time.deltaTime * Physics.gravity.y;
time += Time.deltaTime;
}
transform.position = targetPoint;
yield break;
}
}