I have a simple project which includes pathfinding with obstacle avoidance. Now, I have void Steer which steers the object around since the path is not straight. then I have void AvoidObstacles which has raycast and the obstacle avoidance part basically.
Whenever the object steers, it calls the AvoidObstacles function. The problem now is, at the start, whenever it hasn't call in the Steer function yet, because it is a straigh line, it passes through the object not avoiding it.
Here are some of the codes used:
public Vector3 Steer(Vector3 target, bool bFinalPoint = false)
{
//Calculate the directional vector from the current position towards the target point
Vector3 desiredVelocity = (target - transform.position);
float dist = desiredVelocity.magnitude;
AvoidObstacles(ref desiredVelocity);
//Normalise the desired Velocity
desiredVelocity.Normalize();
//Calculate the velocity according to the speed
if (bFinalPoint && dist < 10.0f)
desiredVelocity *= (curSpeed * (dist / 10.0f));
else
desiredVelocity *= curSpeed;
//Calculate the force Vector
Vector3 steeringForce = desiredVelocity - velocity;
Vector3 acceleration = steeringForce / mass;
return acceleration;
}
here is the other one
public void AvoidObstacles(ref Vector3 desiredVelocity)
{
RaycastHit hit;
Vector3 leftRay = transform.position;
Vector3 rightRay = transform.position;
//leftRay.x -= 2;
//rightRay.x += 2;
Debug.DrawLine(transform.position,(transform.forward * 5) + transform.position,Color.green);
if(Physics.Raycast(transform.position, transform.forward,out hit, minimumDistToAvoid))
{
Debug.DrawLine(transform.position,(transform.forward * 10) + transform.position,Color.red);
if(hit.transform != transform)
{
//dir += hit.normal * 50;
//Get the normal of the hit point to calculate the new direction
Vector3 hitNormal = hit.normal;
hitNormal.y = 0.0f; //Don't want to move in Y-Space
//Get the new directional vector by adding force to vehicle's current forward vector
desiredVelocity = transform.forward + hitNormal * force;
}
}
}
and here is my update
void Update ()
{
//Unify the speed
curSpeed = speed * Time.deltaTime;
targetPoint = path.GetPoint(curPathIndex);
//If reach the radius within the path then move to next point in the path
if(Vector3.Distance(transform.position, targetPoint) < path.Radius)
{
//Don't move the vehicle if path is finished
if (curPathIndex < pathLength - 1)
curPathIndex ++;
else if (isLooping)
curPathIndex = 0;
else
return;
}
//Move the vehicle until the end point is reached in the path
if (curPathIndex >= pathLength )
return;
//Calculate the next Velocity towards the path
if(curPathIndex >= pathLength - 1 && !isLooping)
velocity += Steer(targetPoint, true);
else
velocity += Steer(targetPoint);
transform.position += velocity; //Move the vehicle according to the velocity
transform.rotation = Quaternion.LookRotation(velocity); //Rotate the vehicle towards the desired Velocity
//AvoidObstacles(ref Vector3 desiredVelocity);
dir.Normalize();
}
Maybe someone can help me out buy letting me know how can I call the avoidobstacle function outside of steer? Maybe in update or start perhaps.
I can't really use other algorithms, just this one right here. TIA
Related
So, Hey guys I am new to unity. I have a small doubt. I have a player who is a child of the topcube in a 3 stackcube placed upon eachother.
These cubes have a target position to move once the user clicks on them.
Like imagine there are 3 points in my world. POINT A with location coordinates as(0,0,1),POINT B with (0,0,2),POINT C with (0,0,3) and the 3 stack cube is place on (0,0,0) with the player attached as a child to topcube in that 3stackcube.
All these points(A,B,C) has a script called targetpoint with a variable bool isFilled(default as false) in them which becomes true when one of the cube reaches to its target position.
Further im checking whenever the cubes reaches their target position make isFilled true and check to see if there is a child attached if yes get the animator of the child and trigger jump animation. The jump animation is an inplace animation.
So I want to programmatically move my character +1 towards the direction he is facing (if he is facing z move + 1 in z, if x move +1 in x like this)when the cube he is attached reached its target position while also playing jump animation.
I did a code. it doesnt seem to be working. And sorry for huge paragraphs. Im totally new to coding and asking doubts. Any help will be helpful thanks.
[SerializeField] public List<targetpoint> waypoints;
[SerializeField] float moveSpeed = 2f;
[SerializeField] AudioClip[] surferSounds;
[SerializeField] GameObject particleToPlay;
int waypointIndex = 0;
float t;
//Cached Reference
AudioSource audioSource;
//State
public bool move = false;
void Start()
{
transform.position = this.transform.position;
t = 0f;
}
void FixedUpdate()
{
if (move == true)
{
MoveTarget();
}
}
void MoveTarget()
{
//Time.timeScale = 0.1f;
if (waypointIndex <= waypoints.Count - 1)
{
var targetPosition = waypoints[waypointIndex].transform.position;
transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveSpeed * Time.deltaTime);
if (transform.position == targetPosition)
{
//Debug.Log(t);
if (waypoints[waypointIndex].isFilled == false)
{
waypoints[waypointIndex].isFilled = true;
AudioClip clip = surferSounds[UnityEngine.Random.Range(0, surferSounds.Length)];
var storeToDestroy = Instantiate(particleToPlay, targetPosition, Quaternion.identity);
Destroy(storeToDestroy , 5f);
audioSource.PlayOneShot(clip);
move = false;
}
else if(waypoints[waypointIndex].isFilled == true)
{
waypointIndex++;
targetPosition = waypoints[waypointIndex].transform.position;
transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveSpeed * Time.deltaTime);
}
if (this.gameObject.transform.childCount > 0)
{
var storeChild = gameObject.transform.GetChild(1).gameObject;
StartCoroutine(GravityAndJump(storeChild,storeChild.transform.position+1*transform.forward,1f));
}
else
{
return;
}
}
}
}
IEnumerator GravityAndJump(GameObject child, Vector3 newPosition , float time)
{
var elapsedTime = 0f;
var startingPosition = child.transform.position;
while(elapsedTime < time)
{
child.GetComponent<Animator>().SetTrigger("shouldJump?");
child.transform.position = Vector3.Lerp(startingPosition, newPosition, (elapsedTime / time));
elapsedTime += Time.deltaTime;
yield return null;
}
//storeChild.GetComponent<Animator>().SetFloat("JumpSpeed", 1f);
//yield return new WaitForSeconds(1f);
//gameObject.GetComponentInChildren<Rigidbody>().useGravity = true;
}
}
So I want to programmatically move my character +1 towards the direction he is facing (if he is facing z move + 1 in z, if x move +1 in x like this)
You can get the forward direction for a GameObject using with transform.forward you can use this to calculate target position in front of the GameObject.
Set target position some distance in front of the transform
targetPosition = transform.position + (transform.forward * distance);
Move towards target position at some speed.
transform.position = Vector3.Lerp(transform.position, targetPosition, Time.deltaTime * followSpeed);
Determining arrival to targetPosition
When it comes to determining if transform has arrive to target destination you should measure the distance instead of comparing that the vectors are the same.
So replace this:
if (transform.position == targetPosition){}
With something like this:
if(Vector3.Distance(transform.position, targetPosition) < .001f){
transform.position = targetPosition;
}
Unless you strictly set two vectors to same values it's likely that they will never be considered equal due to how floating point numbers work.
Im trying to make a peice of code that instantiates a gameobject, set its rotation to face the direction of the cursor from the player character at that moment and move towards that direction with constant speed for 2 seconds then stop. However my piece of coded is moving the gameobject towards the cursor direction but the speed gets changed depending on how far my cursor is from the player character.
private IEnumerator Rake()
{
Vector3 relativepos =
Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
Quaternion rotation = Quaternion.LookRotation(relativepos, Vector3.up);
float timepassed = 0;
GameObject WcastRB =
Instantiate(Wcast, gameObject.transform.position, rotation);
Rigidbody2D rg;
rg = WcastRB.GetComponent<Rigidbody2D>();
while (timepassed < 2)
{
timepassed += Time.deltaTime;
rg.velocity = WcastRB.transform.forward * 1000 * Time.deltaTime;
if (timepassed >= 2)
{
rg.velocity = WcastRB.transform.forward * 0;
}
yield return null;
}
}
this is what I have made.
Try this out, I found that because transform.forward was dependent on the object's rotation(and subsequently the original click position).
When the click was too close to the object the transform.forward Vector2 had a magnitude(length) of less than one, causing the speed to slow down.
By increasing the magnitude of the velocity to exactly 1f it should go consistent speeds in all directions
IEnumerator Rake()
{
Vector3 relativepos = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
Quaternion rotation = Quaternion.LookRotation(relativepos, Vector3.up);
float timepassed = 0;
GameObject WcastRB = Instantiate(Wcast, gameObject.transform.position, rotation);
Rigidbody2D rg;
rg = WcastRB.GetComponent<Rigidbody2D>();
Vector2 velocity = rg.transform.forward;
velocity.Normalize();
while (timepassed < 2)
{
timepassed += Time.deltaTime;
rg.velocity = velocity * 1000 * Time.deltaTime;
if (timepassed >= 2)
{
rg.velocity = new Vector2();
}
yield return null;
}
}
Edit:
Removed self-implemented .Normalize() because I forgot that i also used the built in .Normalize() literally right before it.
I have a problem with moving from one place to another in Unity over time. I would like my character to move from current position to current + 1 on Y. Unfortunately, looks like it does not get the current position properly or something, since if I debug what I wrote, it says that the magnitude is always 1, so the point is moving with me. Shouldn't it just check the current position and add 1 to Y, move to that position and then check again? I have no idea what's wrong with this code, or if it's strictly connected with how Unity checks positions and things in real time?
public bool moving = false;
private Vector3 dir;
void FrontMovement()
{
Vector3 movement = new Vector3(-Mathf.Sin(transform.eulerAngles.z * Mathf.PI / 180), Mathf.Cos(transform.eulerAngles.z * Mathf.PI / 180), 0f); // always moving front, even after rotation
if (moving == false)
{
dir = movement - transform.position;
moving = true;
return;
}
transform.Translate(dir.normalized * Time.deltaTime);
if(dir.magnitude <= Time.deltaTime)
{
Debug.Log("Finished movement");
moving = false;
}
}
void FixedUpdate()
{
Debug.Log(dir.magnitude);
FrontMovement();
}
I would also like to know how to do rotations over time.
https://docs.unity3d.com/ScriptReference/Vector3.Lerp.html
lerp also works for rotations
// Movement speed in units per second.
public float speed = 1.0F;
// Time when the movement started.
private float startTime;
// Total distance between the markers.
private float journeyLength;
void StartMoving() {
// Keep a note of the time the movement started.
startTime = Time.time;
Vector3 modifiedPosition = transform.position;
transform.position.y += 1.0f;
// Calculate the journey length.
journeyLength = Vector3.Distance(transform.position, modifiedPosition.position);
moving = true;
}
// Move to the target end position.
void Update()
{
if (moving) {
// Distance moved equals elapsed time times speed..
float distCovered = (Time.time - startTime) * speed;
// Fraction of journey completed equals current distance divided by total distance.
float fractionOfJourney = distCovered / journeyLength;
// Set our position as a fraction of the distance between the markers.
transform.position = Vector3.Lerp(startMarker.position, endMarker.position, fractionOfJourney);
if (fractionOfJourney >= 1.0f) {
moving = false;
}
}
}
You could use Coroutine + Vector3.Lerp to move with a specified amount of time:
public IEnumerator MoveToPosition(Transform transform, Vector3 position, float timeToMove)
{
var currentPos = transform.position;
var t = 0f;
while(t <= 1f)
{
t += Time.deltaTime / timeToMove;
transform.position = Vector3.Lerp(currentPos, position, t);
yield return null;
}
transform.position = position;
}
you call the coroutine in Start Method
StartCoroutine(MoveToPosition(transform, newposition, timeToMove))
You could use the same logic for Rotating, with Quaternion.Lerp or Slerp and Quaternion.LookRotation, of course you have lot of sample with rotation over time on WEB!! google is your friend...
public IEnumerator RotateToDirection(Transform transform, Vector3 position, float timeToRotate)
{
var startRotation = transform.rotation;
var direction = position - transform.position;
var finalRotation = Quaternion.LookRotation(direction);
var t = 0f;
while (t <= 1f)
{
t += Time.deltaTime / timeToRotate;
transform.rotation = Quaternion.Lerp(startRotation, finalRotation, t);
yield return null;
}
transform.rotation = finalRotation;
}
I'm working on a small experimental project in Unity. I have a 2d sprite that moves forward with a velocity but I want it to turn left or right in a wide arc and keep moving in that direction on keypress.
I've tried to tweak its angular velocity to get the desired affect. Looks unnatural and it won't stop rotating.
Tried Lerping. Looks unnatural as well.
Code Snippet 1:
bool forward = true;
Vector3 movement;
void FixedUpdate()
{
if (forward)
{
//Moves forward
movement = new Vector3(0.0f, 0.1f, 0.0f);
rb.velocity = movement * speed;
}
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
forward = false;
movement = new Vector3(-0.05f, 0.05f, 0.0f);
rb.velocity = movement * speed;
rb.angularVelocity = 30;
}
if (transform.rotation.z == 90)
{
movement = new Vector3(-0.1f, 0.0f, 0.0f);
rb.velocity = movement * speed;
rb.angularVelocity = 0;
}
}
Code Snippet 2:
void Update(){
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
Vector3 target = transform.position + new Vector3(-0.5f, 0.5f, 0);
transform.position
=Vector3.Lerp(transform.position,target,Time.deltaTime);
transform.eulerAngles = Vector3.Lerp(transform.rotation.eulerAngles,
new Vector3(0, 0, 90), Time.deltaTime);
}
}
Can anyone point me in the right direction of what is the actual correct way to implement this?
Not entirely sure if this is what youre trying to accomplish but here's some pseudo code of what I came up with to get you started...
Essentially, when a direction is pressed, you want to increase the velocity in that direction until all of the velocity is pointed that way. At the same time you want to decrease the velocity in the direction you were previously going until it is zero.
This is a simplified formula however - if you truly wanted the velocity to be constant throughout the entirety of the arc you would have to use some geometry, knowing that V=(velX^2 + velY^2)^.5 but this would get you pretty close...
float yvel = 1f, xvel;
float t;
void Update()
{
GetComponent<Rigidbody2D>().velocity = new Vector2(xvel, yvel);
t += Time.deltaTime;
if (Input.GetKeyDown(KeyCode.D))
{
t = 0;
StartCoroutine(Move());
}
}
private IEnumerator Move()
{
while (t < 2) // at time t, yvel will be zero and xvel will be 1
{
yvel = 1 - .5f * t; // decrease velocity in old direction
xvel = .5f * t; // increase velocity in new direction
yield return null;
}
}
I have a scene that my camera doesn't follow my player. When player reaches the end of camera I want player to can't go further (out of camera view). How can I do this?
My codes for movement
public class PlayerBlueController : MonoBehaviour {
public float speed;
private float x;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void FixedUpdate () {
x = Input.GetAxis ("Horizontal") / 100 * speed;
transform.Translate (x,0,0);
}
}
As you can see from this. It gets out of camera's view.
I noticed you used a Collider2D. You should be using Rigidbody2D.MovePosition instead of transform.Translate or you'll likely run into issues when transform.Translate is used.
1.Take the final move position and convert it to new position in ViewPortPoint with Camera.main.WorldToViewportPoint
2.Apply a limit with Mathf.Clamp to the result in #1.
3.Convert the ViewPortPoint back to world point with Camera.main.ViewportToWorldPoint.
4.Finally, move it with Rigidbody2D.MovePosition.
The code below is modified from this answer to include restriction to screen boundary.
Move without Rigidbody:
Use only if collision and physics are NOT required:
public float speed = 100;
public Transform obj;
public void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
//Move only if we actually pressed something
if ((h > 0 || v > 0) || (h < 0 || v < 0))
{
Vector3 tempVect = new Vector3(h, v, 0);
tempVect = tempVect.normalized * speed * Time.deltaTime;
Vector3 newPos = obj.transform.position + tempVect;
checkBoundary(newPos);
}
}
void checkBoundary(Vector3 newPos)
{
//Convert to camera view point
Vector3 camViewPoint = Camera.main.WorldToViewportPoint(newPos);
//Apply limit
camViewPoint.x = Mathf.Clamp(camViewPoint.x, 0.04f, 0.96f);
camViewPoint.y = Mathf.Clamp(camViewPoint.y, 0.07f, 0.93f);
//Convert to world point then apply result to the target object
obj.position = Camera.main.ViewportToWorldPoint(camViewPoint);
}
Move Object with Rigidbody2D:
Use if collision and physics are required:
public float speed = 100;
public Rigidbody2D rb;
public void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
//Move only if we actually pressed something
if ((h > 0 || v > 0) || (h < 0 || v < 0))
{
Vector3 tempVect = new Vector3(h, v, 0);
tempVect = tempVect.normalized * speed * Time.deltaTime;
//rb.MovePosition(rb.transform.position + tempVect);
Vector3 newPos = rb.transform.position + tempVect;
checkBoundary(newPos);
}
}
void checkBoundary(Vector3 newPos)
{
//Convert to camera view point
Vector3 camViewPoint = Camera.main.WorldToViewportPoint(newPos);
//Apply limit
camViewPoint.x = Mathf.Clamp(camViewPoint.x, 0.04f, 0.96f);
camViewPoint.y = Mathf.Clamp(camViewPoint.y, 0.07f, 0.93f);
//Convert to world point then apply result to the target object
Vector3 finalPos = Camera.main.ViewportToWorldPoint(camViewPoint);
rb.MovePosition(finalPos);
}
image not respond .
but you can check player location
x = Input.GetAxis ("Horizontal") / 100 * speed;
if(gameobject.transform.x > someValue)
x=0
gameobject will be OBJECT in scene that u attach class to it.
another way is place 2 empty gameobject with collider as invisibleWall and get collider to player
Possible solution is:
1.Get the coordinates of your screen cornes (top left, top right, bottom left, bottom right). You can get this coordinates using Screen.height and Screen.width.
2.Convert this coordinates using the camera you need with Camera.ScreenToWorldPoint.
3.Get your player coordinates and check that they are inside the rect which is formed by 4 coordinates of the screen corners.
You need to limit your transform's position based on the edges of the camera. Here is an answer describing the different coordinate systems in unity
You're probably looking to do something like this:
float xMin = Camera.main.ViewportToWorldPoint(Vector3.zero).x;
float xMax = Camera.main.ViewportToWorldPoint(Vector3.one).x;
Vector3 currentPos = transform.position;
float dx = Input.GetAxis ("Horizontal") / 100 * speed;
Vector3 desiredPos = new Vector3(currentPos.x + dx, currentPos.y, currentPos.z);
Vector3 realPos = desiredPos;
if(desiredPos.x > xMax)
realPos.x = xMax;
else if(desiredPos.x < xMin)
realPos.x = xMin;
transform.position = realPos;
Read up here for more info on ViewportToWorldPoint(), it's extremely useful to become comfortable with the different coordinate spaces and how you can convert between them.