I'm trying to let an object follow a predicent path in Unity.
First I calculate the path in Circle.cs
Then I store the points in the LineRender component
I let an object follow the positions in Path.cs
The problem : The LineRender (visual) does not match the Positions of the LineRender. See : Screenshot Unity
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Path : MonoBehaviour
{
// The linerender path to follow
public LineRenderer path;
// The speed at which to follow the path
public float speed = 5.0f;
// The current position on the path
private int currentPosition = 0;
void Update()
{
// Get the current position and target position on the path
Vector3 currentWaypoint = path.GetPosition(currentPosition);
Vector3 targetWaypoint = path.GetPosition(currentPosition + 1);
// Move towards the target waypoint
transform.position = Vector3.MoveTowards(transform.position, targetWaypoint, speed * Time.deltaTime);
// If we have reached the target waypoint, move to the next one
if (transform.position == targetWaypoint)
{
currentPosition++;
// If we have reached the end of the path, start again from the beginning
if (currentPosition >= path.positionCount)
{
currentPosition = 0;
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Circle : MonoBehaviour
{
public float radius = 3;
// Start is called before the first frame update
private int numPoints = 100;
private Vector3[] positions;
void Start()
{
positions = new Vector3[numPoints];
float angleIncrement = (Mathf.PI / 2) / numPoints; // Quarter circle in radians
for (int i = 0; i < numPoints; i++)
{
float angle = angleIncrement * i;
float x = radius * Mathf.Cos(angle);
float y = radius * Mathf.Sin(angle);
positions[i] = new Vector3(x, 0, y);
}
this.GetComponent<LineRenderer>().positionCount = numPoints;
this.GetComponent<LineRenderer>().SetPositions(positions);
}
}
I tried to adjust the scale of the LineRender.
By default the positions of a LineRenderer component are based on world-spaced coordinates.
Thus, you have two options for a fix here:
Set the useWorldSpace attribute on the LineRenderer to false (either in code or in the inspector)
Add the offset to the points, before you add them to the LineRenderer
Related
I am trying to make canvas UI object follow my camera. It works but I do not want the Y axis of the canvas object to get affected. That is, it should follow only in X and Z axis but Y should stay fixed. How do I do this?
I have tried doing
menuCanvas.transform.position = Vector3.Lerp(new Vector3(currentPos.x, -30, currentPos.z), targetPosition, smoothFactor);
but it changes to some other axis completely.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SmoothCameraFollow : MonoBehaviour
{
public GameObject menuCanvas;
public Camera FirstPersonCamera;
[Range(0, 1)]
public float smoothFactor = 0.5f;
public float offsetRadius = 0.3f;
public float distanceToHead = 4;
public void LateUpdate()
{
// make the UI always face towards the camera
menuCanvas.transform.rotation = FirstPersonCamera.transform.rotation;
var cameraCenter = FirstPersonCamera.transform.position + FirstPersonCamera.transform.forward * distanceToHead;
var currentPos = menuCanvas.transform.position;
// in which direction from the center?
var direction = currentPos - cameraCenter;
// target is in the same direction but offsetRadius
// from the center
var targetPosition = cameraCenter + direction.normalized * offsetRadius;
// finally interpolate towards this position
menuCanvas.transform.position = Vector3.Lerp(currentPos, targetPosition, smoothFactor);
}
}
menuCanvas.transform.position = new vector3 (cam.transform.position.x, menuCanvas.transform.position.y, cam.transform.position.z)
I have 3 Spheres and a lineRenderer which is connected to the spheres. I'm using Bezier curves to get a smooth curves between the spheres.
The code is looking like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LineController : MonoBehaviour
{
private LineRenderer lineRenderer;
public Transform p0;
public Transform p1;
public Transform p2;
void Start()
{
lineRenderer = GetComponent<LineRenderer>();
lineRenderer.sortingOrder = 1;
}
void Update()
{
DrawQuadraticBezierCurve(p0.position, p1.position, p2.position);
}
void DrawQuadraticBezierCurve(Vector3 point0, Vector3 point1, Vector3 point2)
{
lineRenderer.positionCount = 100;
float t = 0f;
Vector3 B = new Vector3(0, 0, 0);
for (int i = 0; i < lineRenderer.positionCount; i++)
{
B = (1 - t) * (1 - t) * point0 + 2 * (1 - t) * t * point1 + t * t * point2;
lineRenderer.SetPosition(i, B);
t += (1 / (float)lineRenderer.positionCount);
}
}
}
The Problem is, that the is invisible on some parts when I move the spheres.
I don't know why this is happening. I'm using Mixed Reality Toolkit. In the best case the line should be displayed as an 3D object like a long cube.
Maybe anybody is familiar with this kind of "error"?
I want to spawn random objects over the terrain and also over the plane or only on one of them.
The spawning objects for the terrain are working but I'm not sure how to do it with the plane.
To start I can't even find how to get the plane size width and length.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SideQuests : MonoBehaviour
{
public Terrain terrain;
public Plane plane;
public GameObject prefab;
public GameObject parent;
[Range(10, 1000)]
public int numberOfObjects = 10;
public float yOffset = 10f;
private float terrainWidth;
private float terrainLength;
private float xTerrainPos;
private float zTerrainPos;
private float planeWidth;
private float planeLength;
private float xPlanePos;
private float zPlanePos;
void Start()
{
//Get terrain size
terrainWidth = terrain.terrainData.size.x;
terrainLength = terrain.terrainData.size.z;
//Get terrain position
xTerrainPos = terrain.transform.position.x;
zTerrainPos = terrain.transform.position.z;
//Get plane size
planeWidth = plane.
generateObjectOnTerrain();
}
void generateObjectOnTerrain()
{
for (int i = 0; i < numberOfObjects; i++)
{
//Generate random x,z,y position on the terrain
float randX = UnityEngine.Random.Range(xTerrainPos, xTerrainPos + terrainWidth);
float randZ = UnityEngine.Random.Range(zTerrainPos, zTerrainPos + terrainLength);
float yVal = Terrain.activeTerrain.SampleHeight(new Vector3(randX, 0, randZ));
//Apply Offset if needed
yVal = yVal + yOffset;
//Generate the Prefab on the generated position
GameObject objInstance = Instantiate(prefab, new Vector3(randX, yVal, randZ), Quaternion.identity);
objInstance.name = "Quest";
objInstance.tag = "Quest";
objInstance.transform.parent = parent.transform;
}
}
}
Your code uses Plane which has an infinite size:
A plane is an infinitely large, flat surface that exists in 3D space and divides the space into two halves known as half-spaces.
So to answer your question, the length and width of a Plane can be referenced with Mathf.Infinity
You can project any position to a location on the plane by using Plane.ClosestPointOnPlane. If you can generate random positions, such as with Random.insideUnitSphere you can generate random points on the plane that way:
float spawnRange = 50f;
Vector3 spawnCenter = Vector3.zero;
Vector3 randomPointOnPlane = plane.ClosestPointOnPlane(spawnCenter
+ spawnRange * Random.insideUnitSphere);
(Note that if you use insideUnitSphere, you will have more spawns in the center of the sphere than the edges.)
Then, if you have a height you want them to spawn over the plane, you can add that:
float spawnHeight = 1f;
Vector3 randomPointOverPlane = Vector3.up * spawnHeight + randomPointOnPlane;
First I generate new objects in this example 1000 objects over the terrain in height 10 in random positions :
using System;
using UnityEngine;
using Random = UnityEngine.Random;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class CloneObjects : MonoBehaviour
{
public Terrain terrain;
public GameObject prefab;
public GameObject parent;
[Range(10, 1000)]
public int numberOfObjects;
public float yOffset = 10f;
private float terrainWidth;
private float terrainLength;
private float xTerrainPos;
private float zTerrainPos;
void Start()
{
//Get terrain size
terrainWidth = terrain.terrainData.size.x;
terrainLength = terrain.terrainData.size.z;
//Get terrain position
xTerrainPos = terrain.transform.position.x;
zTerrainPos = terrain.transform.position.z;
generateObjectOnTerrain();
}
void generateObjectOnTerrain()
{
for (int i = 0; i < numberOfObjects; i++)
{
//Generate random x,z,y position on the terrain
float randX = UnityEngine.Random.Range(xTerrainPos, xTerrainPos + terrainWidth);
float randZ = UnityEngine.Random.Range(zTerrainPos, zTerrainPos + terrainLength);
float yVal = Terrain.activeTerrain.SampleHeight(new Vector3(randX, 0, randZ));
//Apply Offset if needed
yVal = yVal + yOffset;
//Generate the Prefab on the generated position
GameObject objInstance = Instantiate(prefab, new Vector3(randX, yVal, randZ), Quaternion.identity);
objInstance.name = "Waypoint";
objInstance.tag = "Waypoint";
objInstance.transform.parent = parent.transform;
}
}
}
The result is :
And then on each object there is a script attached :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveObject : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
transform.position = new Vector3(Mathf.PingPong(Time.time * 3, Random.Range(2f, 9f)), transform.position.y, transform.position.z);
}
}
The problem is that the objects are not moving in ping pong from the original spawned positions like in the screenshot on the top the first screenshot but they are all moving first to the edge of the terrain and making the ping pong there :
PingPong is a lot like Lerp. You need to supply a time value to it, not a delta time. As such, you'll want to store the initial object height, and work out how high you want to pingpong on top of that.
This example code will do just that:
public float Speed;
private float intitialY;
private float pingPongHeight;
private void Start ( )
{
intitialY = transform.localPosition.y;
pingPongHeight = UnityEngine.Random.Range ( 2f, 9f );
}
private void Update ( )
{
var height = Mathf.PingPong ( Time.time * Speed, pingPongHeight );
var position = transform.localPosition;
position.y = intitialY + height;
transform.localPosition = position;
}
I've assumed you used a random value to determine how high each object will travel over the course of the pingpong.
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class Waypoints : MonoBehaviour
{
public GameObject[] waypoints;
public Transform target;
public float moveSpeed = 10f;
public float slowDownSpeed = 3f;
public float reverseSlowDownSpeed = 3f;
public float rotationSpeed = 1f;
private int targetsIndex = 0;
private Vector3 originalPosition;
private GameObject[] players;
public Transform reverseTarget;
private int reverseTargetsIndex = 0;
private Vector3 reverseOriginalPosition;
public bool random = false;
public bool getNextRandom = true;
// Use this for initialization
void Start()
{
waypoints = GameObject.FindGameObjectsWithTag("Blocks");
players = GameObject.FindGameObjectsWithTag("Player");
originalPosition = players[0].transform.localPosition;
}
// Update is called once per frame
void Update()
{
if (random == true)
{
RandomWayPointsAI();
}
else
{
WayPointsAI();
}
}
private void WayPointsAI()
{
if (targetsIndex == waypoints.Length)
targetsIndex = 0;
target = waypoints[targetsIndex].transform;
if (MovePlayer())
targetsIndex++;
}
private void ReverseWayPointsAI()
{
if (reverseTargetsIndex == 0)
reverseTargetsIndex = waypoints.Length - 1;
reverseTarget = waypoints[reverseTargetsIndex].transform;
if (MovePlayer())
reverseTargetsIndex--;
}
void RandomWayPointsAI()
{
if (random == true && getNextRandom)
{
int index = UnityEngine.Random.Range(0, waypoints.Length);
target = waypoints[index].transform;
getNextRandom = false;
}
getNextRandom = MovePlayer();
}
bool MovePlayer()
{
float distance = Vector3.Distance(players[0].transform.position, target.transform.position);
players[0].transform.localRotation = Quaternion.Slerp(players[0].transform.localRotation, Quaternion.LookRotation(target.position - players[0].transform.localPosition), rotationSpeed * Time.deltaTime);
//move towards the player
if (distance < 30)
{
players[0].transform.localPosition += players[0].transform.forward * slowDownSpeed * Time.deltaTime;
}
else
{
players[0].transform.localPosition += players[0].transform.forward * moveSpeed * Time.deltaTime;
}
if (distance < target.transform.localScale.magnitude)
return true;
else
return false;
}
void DrawLinesInScene()
{
// draw lines between each checkpoint //
for (int i = 0; i < waypoints.Length - 1; i++)
{
Debug.DrawLine(waypoints[i].transform.position, waypoints[i + 1].transform.position, Color.blue);
}
// draw a line between the original transform start position
// and the current transform position //
Debug.DrawLine(originalPosition, players[0].transform.position, Color.red);
Debug.DrawLine(reverseOriginalPosition, players[1].transform.position, Color.red);
// draw a line between current transform position and the next waypoint target
// each time reached a waypoint.
if (target != null)
Debug.DrawLine(target.transform.position, players[0].transform.position, Color.green);
if (reverseTarget != null)
Debug.DrawLine(reverseTarget.transform.position, players[1].transform.position, Color.green);
}
void AddColliderToWaypoints()
{
foreach (GameObject go in waypoints)
{
SphereCollider sc = go.AddComponent<SphereCollider>() as SphereCollider;
sc.isTrigger = true;
}
}
}
There are two problems with the script.
If the Move Speed is set to 3 i need to set the Rotation Speed at least to the 10 if i will set the Rotation Speed to 3 or 4 or 5 the rotation will be too wide and it will take much time to the player to get back on track after rotating.
But if it's 10 it seems he is almost rotating on the place so it's not good either.
I want to be able to change the player rotation speed but also to keep him on the waypoints track at any time. I mean that each waypoint the player is reaching to that he will get to it's center. In this case cubes so not only to touch the waypoint but to get to it's center then moving to the next waypoint.
Another problem i see is with the RandomWayPointsAI()
When i change it to use the random method i'm not sure if it's just picking up random positions around the grid or if it's picking random blocks(Cubes).
But it's never getting to a cube it' getting close or between two or sometimes on a cube and same as before also on the random i want to make the random waypoints not just positions but blocks and to get to each random block center.
Center i mean like standing on it.
A glaring problem in the showcased code is the use of local positions and rotations. When moving and rotating characters and objects in the world you should use world space.
Localposition and localrotation are based on the position and rotation of the parent object. Using those to move your object across the world will cause weird problems.
I think the following answer has a pretty accurate explanation of that stuff https://teamtreehouse.com/community/global-space-vs-local-space