Unity, get the "actual" current Terrain? - c#

Unity has a function Terrain.sampleHeight(point) which is great, it instantly gives you the height of the Terrain underfoot rather than having to cast.
However, any non-trivial project has more than one Terrain. (Indeed any physically large scene inevitably features terrain stitching, one way or another.)
Unity has a function Terrain.activeTerrain which - I'm not making this up - gives you: the "first one loaded"
Obviously that is completely useless.
Is fact, is there a fast way to get the Terrain "under you"? You can then use the fast function .sampleHeight ?
{Please note, of course, you could ... cast to find a Terrain under you! But you would then have your altitude so there's no need to worry about .sampleHeight !}
In short is there a matching function to use with sampleHeight which lets that function know which Terrain to use for a given xyz?
(Or indeed, is sampleHeight just a fairly useless demo function, usable only in demos with one Terrain?)

Is there in fact a fast way to get the Terrain "under you" - so as to
then use the fast function .sampleHeight ?
Yes, it can be done.
(Or indeed, is sampleHeight just a fairly useless demo function,
usable only in demos with one Terrain?)
No
There is Terrain.activeTerrain which returns the main terrain in the scene. There is also Terrain.activeTerrains (notice the "s" at the end) which returns active terrains in the scene.
Obtain the terrains with Terrain.activeTerrains which returns Terrain array then use Terrain.GetPosition function to obtain its position. Get the current terrain by finding the closest terrain from the player's position. You can do this by sorting the terrain position, using Vector3.Distance or Vector3.sqrMagnitude (faster).
Terrain GetClosestCurrentTerrain(Vector3 playerPos)
{
//Get all terrain
Terrain[] terrains = Terrain.activeTerrains;
//Make sure that terrains length is ok
if (terrains.Length == 0)
return null;
//If just one, return that one terrain
if (terrains.Length == 1)
return terrains[0];
//Get the closest one to the player
float lowDist = (terrains[0].GetPosition() - playerPos).sqrMagnitude;
var terrainIndex = 0;
for (int i = 1; i < terrains.Length; i++)
{
Terrain terrain = terrains[i];
Vector3 terrainPos = terrain.GetPosition();
//Find the distance and check if it is lower than the last one then store it
var dist = (terrainPos - playerPos).sqrMagnitude;
if (dist < lowDist)
{
lowDist = dist;
terrainIndex = i;
}
}
return terrains[terrainIndex];
}
USAGE:
Assuming that the player's position is transform.position:
//Get the current terrain
Terrain terrain = GetClosestCurrentTerrain(transform.position);
Vector3 point = new Vector3(0, 0, 0);
//Can now use SampleHeight
float yHeight = terrain.SampleHeight(point);
While it's possible to do it with Terrain.SampleHeight, this can be simplified with a simple raycast from the player's position down to the Terrain.
Vector3 SampleHeightWithRaycast(Vector3 playerPos)
{
float groundDistOffset = 2f;
RaycastHit hit;
//Raycast down to terrain
if (Physics.Raycast(playerPos, -Vector3.up, out hit))
{
//Get y position
playerPos.y = (hit.point + Vector3.up * groundDistOffset).y;
}
return playerPos;
}

Terrain.GetPosition() = Terrain.transform.position = position in world
working method:
Terrain[] _terrains = Terrain.activeTerrains;
int GetClosestCurrentTerrain(Vector3 playerPos)
{
//Get the closest one to the player
var center = new Vector3(_terrains[0].transform.position.x + _terrains[0].terrainData.size.x / 2, playerPos.y, _terrains[0].transform.position.z + _terrains[0].terrainData.size.z / 2);
float lowDist = (center - playerPos).sqrMagnitude;
var terrainIndex = 0;
for (int i = 0; i < _terrains.Length; i++)
{
center = new Vector3(_terrains[i].transform.position.x + _terrains[i].terrainData.size.x / 2, playerPos.y, _terrains[i].transform.position.z + _terrains[i].terrainData.size.z / 2);
//Find the distance and check if it is lower than the last one then store it
var dist = (center - playerPos).sqrMagnitude;
if (dist < lowDist)
{
lowDist = dist;
terrainIndex = i;
}
}
return terrainIndex;
}

It turns out the answer is simply NO, Unity does not provide such a function.

You can use this function to get the Closest Terrain to your current Position:
int GetClosestTerrain(Vector3 CheckPos)
{
int terrainIndex = 0;
float lowDist = float.MaxValue;
for (int i = 0; i < _terrains.Length; i++)
{
var center = new Vector3(_terrains[i].transform.position.x + _terrains[i].terrainData.size.x / 2, CheckPos.y, _terrains[i].transform.position.z + _terrains[i].terrainData.size.z / 2);
float dist = Vector3.Distance(center, CheckPos);
if (dist < lowDist)
{
lowDist = dist;
terrainIndex = i;
}
}
return terrainIndex;
}
and then you can use the function like this:
private Terrain[] _terrains;
void Start()
{
_terrains = Terrain.activeTerrains;
Vector3 start_pos = Vector3.zero;
start_pos.y = _terrains[GetClosestTerrain(start_pos)].SampleHeight(start_pos);
}

public static Terrain GetClosestTerrain(Vector3 position)
{
return Terrain.activeTerrains.OrderBy(x =>
{
var terrainPosition = x.transform.position;
var terrainSize = x.terrainData.size * 0.5f;
var terrainCenter = new Vector3(terrainPosition.x + terrainSize.x, position.y, terrainPosition.z + terrainSize.z);
return Vector3.Distance(terrainCenter, position);
}).First();
}

Raycast solution: (this was not asked, but for those looking for Solution using Raycast)
Raycast down from Player, ignore everything that has not Layer of "Terrain" (Layer can be easily set in inspector).
Code:
void Update() {
// Put this on Player! Raycast's down (raylength=10f), if we hit something, check if the Layers name is "Terrain", if yes, return its instanceID
RaycastHit hit;
if (Physics.Raycast (transform.localPosition, transform.TransformDirection (Vector3.down), out hit, 10f, 1 << LayerMask.NameToLayer("Terrain"))) {
Debug.Log(hit.transform.gameObject.GetInstanceID());
}
}
At this point already, you have a reference to the Terrain by "hit.transform.gameObject".
For my case, i wanted to reference this terrain by its instanceID:
// any other script
public static UnityEngine.Object FindObjectFromInstanceID(int goID) {
return (UnityEngine.Object)typeof(UnityEngine.Object)
.GetMethod("FindObjectFromInstanceID", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)
.Invoke(null, new object[] { goID });
}
But as written above, if you want the Terrain itself (as Terrain object) and not the instanceID, then "hit.transform.gameObject" will give you the reference already.
Input and code snippets taken from these links:
https://answers.unity.com/questions/1164722/raycast-ignore-layers-except.html
https://answers.unity.com/questions/34929/how-to-find-object-using-instance-id-taken-from-ge.html

Related

Why is my Unity C# function returning NaN values?

I am trying to calculate gravity between bodies using the following script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Gravity : MonoBehaviour
{
public const float gravity = 10;
public float mass = 10f;
public bool active = true;
public Rigidbody rigidbody;
public Transform transform;
GameObject[] otherObjs;
Gravity[] otherGravities;
void Start () {
rigidbody = gameObject.GetComponent<Rigidbody>();
transform = gameObject.transform;
otherObjs = GameObject.FindGameObjectsWithTag("Body");
otherGravities = new Gravity[otherObjs.Length];
for (int i = 0; i < otherObjs.Length; i++)
{
otherGravities[i] = otherObjs[i].GetComponent<Gravity>();
}
}
void FixedUpdate () {
rigidbody.AddForce(CalculateGravity());
}
Vector3 CalculateGravity () {
Vector3 finalGravity = new Vector3(0, 0, 0);
foreach (Gravity current in otherGravities) {
float distance = Vector3.Distance(transform.position, current.transform.position);
Vector3 unNormalDirection = transform.position - current.transform.position;
Vector3 direction = unNormalDirection.normalized;
Vector3 gravitationalPull = ((mass * current.mass) / (distance * distance)) * direction;
finalGravity += gravitationalPull;
}
return finalGravity;
}
}
When running the code, I get the following error
rigidbody.force assign attempt for 'Sun' is not valid. Input force is { NaN, NaN, NaN }.
UnityEngine.Rigidbody:AddForce (UnityEngine.Vector3)
Gravity:FixedUpdate () (at Assets/Scripts/Gravity.cs:27)
I get this for both objects in the scene.
I can't seem to figure out why the function is returning NaN's, I've double checked that it is not dividing by zero. If you have any idea, please let me know.
I'm basing this on a few assumptions. It looks like you're probably calculating gravitational attraction to bodies from themselves, with a distance of 0, and dividing by that 0.
In the line otherObjs = GameObject.FindGameObjectsWithTag("Body"); you're getting all objects with the tag "Body" which probably includes objects you're applying gravity to. I'd try to filter them something like this:
void Start () {
rigidbody = gameObject.GetComponent<Rigidbody>();
transform = gameObject.transform;
otherObjs = GameObject.FindGameObjectsWithTag("Body");
int otherGravitiesLength = otherObjs.Length;
if (gameObject.CompareTag("Body")) {
//Must exclude this object from its own other gravities list.
//So don't include it in the length of that array.
otherGravitiesLength = otherGravitiesLength - 1;
}
otherGravities = new Gravity[otherGravitiesLength];
int insertIndex = 0;
for (int i = 0; i < otherObjs.Length; i++)
{
if (otherObjs[i] != gameObject) {
otherGravities[insertIndex++] = otherObjs[i].GetComponent<Gravity>();
}
}
}
I've never used Unity, so I'm making some guesses about the API from context, and a tiny bit of googling, so this answer may need some repair.
If it is possible for two distinct bodies that gravitationally attract to be in the same position, then you should probably still also test for a zero distance and handle that differently within CalculateGravity

Unity: How to rotate child weapon around pivot point in its local space

In my 2D game, the player Prefab has children which are weapons (like swords) so that when the player moves, the weapons translate in world space with his movements while maintaining a constant local position (until of course it is time to attack). The weapons automatically point towards nearby enemies, and swing when they get close enough.
I want the weapon to follow a swing arc by rotating around a pivot point defined at half of the weapon's range in the direction of the enemy. Once the weapon starts the swing, the arc's local position and rotation should remain unchanged and no longer care about the enemy position while the world arc will obviously translate with the player. The weapon should follow this arc purely relative to the player.
RotateAround seems to only work in world space, and thus causes strange problems when trying to rotate around an object in world space while the weapon's world position (as well as my desired pivot point's world position) would be translating with the player. Also, the point that I need it to rotate around needs to be relative to the local space, since when the player moves, the weapon needs to maintain its local arc while also translating with the player.
I also tried using Vector3.Slerp on the weapon's transform.localPosition, which seemed like it would be the perfect solution, but I can't seem to get the arc to match what I envision a good round swing would look like.
The attack consists of three parts: Backswing, Foreswing, and Recovery. The part that I care most about is the foreswing arc, as the others can be acheived easily with simply local rotations and lerping.
const int BACKSWING = 0;
const int FORESWING = 1;
const int RECOVER = 2;
float[] timeFrac = { .15f, .25f, .6f };
float[] rotations = { 120f, -240f, 120f };
float backSwingDistMultiplier = .5f;
//Swing Attack
public override IEnumerator Attack(float startAngle) {
var totalAttackTime = GetAttackTime();
var backSwingDist = backSwingDistMultiplier * Range;
var startPos = transform.localPosition;
var slerpCenterDiff = PerpDir(dir).normalized;
//Interpolation arrays
float[] swingTimes = { timeFrac[BACKSWING] * totalAttackTime,
timeFrac[FORESWING] * totalAttackTime,
timeFrac[RECOVER] * totalAttackTime };
float[] startAngles = { startAngle,
startAngle + rotations[BACKSWING],
startAngle + rotations[BACKSWING] + rotations[FORESWING] };
Vector3[] swingPositions = { startPos - (dir - slerpCenterDiff ) * backSwingDist,
startPos + dir * Range + slerpCenterDiff * backSwingDist };
Vector3[] slerpCenters = { (startPos + swingPositions[BACKSWING]) * .5f + slerpCenterDiff ,
((swingPositions[BACKSWING] + swingPositions[FORESWING]) * .5f) + slerpCenterDiff };
Vector3[] slerpStarts = { startPos - slerpCenters[BACKSWING],
swingPositions[BACKSWING] - slerpCenters[FORESWING]};
Vector3[] slerpEnds = { swingPositions[BACKSWING] - slerpCenters[BACKSWING],
swingPositions[FORESWING] - slerpCenters[FORESWING]};
timer = 0;
float percentDone;
//A swing attack has backswing, foreswing, and recovery
for (int swing = 0; swing <= 2; swing++) {
while (timer < swingTimes[swing]) {
percentDone = timer / swingTimes[swing];
//Backswing and Foreswing will slerp
if (swing < RECOVER) {
transform.localPosition = Vector3.Slerp(slerpStarts[swing], slerpEnds[swing], percentDone);
transform.localPosition += slerpCenters[swing];
} else { //Recover will lerp
transform.localPosition = Vector3.Lerp(swingPositions[FORESWING], startPos, percentDone);
}
transform.localRotation = Quaternion.Euler(0, 0,
startAngles[swing] + rotations[swing] * percentDone);
timer += Time.deltaTime;
yield return null;
}
transform.localRotation = Quaternion.Euler(0, 0, startAngles[swing] + rotations[swing]);
timer -= swingTimes[swing];
}
transform.localPosition = startPos;
}
I would just make an animation out of it, but I need the range to be dynamic, which is next to impossible to achieve with keyframes.
I was able to get my desired outcome by defining the arc centers in localPosition first:
var backswingArcCenter = (startPos + swingPositions[BACKSWING]) * 0.5f;
var foreswingArcCenter = (swingPositions[BACKSWING] + swingPositions[FORESWING]) * 0.5f;
and then calling RotateAround using that position added to the player's world space
if (swing == FORESWING) {
transform.RotateAround(player.transform.position + foreswingArcCenter,
Vector3.forward, Time.deltaTime / swingTimes[swing] * 270f);
} else if (swing == BACKSWING) {
transform.RotateAround(player.transform.position + backswingArcCenter,
Vector3.forward, Time.deltaTime / swingTimes[swing] * -180f);
}

Instantiate predefined number of object along a raycast in Unity

I have a raycast that's being rendered every frame based on 2 points, and those 2 points change position each frame.
What I need is a system that doesn't need a direction, or a number of objects, but instead takes in 2 points, and then instantiates or destroys as many objects as necessary to get the instantiated objects from one side to another minus spaceBetweenPoints. If you wanted you could think of it as an Angry Birds Style slingshot HUD, except without gravity, and based on raycasts.
My Script:
public int numOfPoints; // The number of points that are generated (This would need to chnage based one the distance in the end)
public float spaceBetweenPoints; // The spacing between the generated points
private GameObject[] predictionPoints; // The prefab to be gernerated
private Vector2 firstPathStart; // The starting point for the raycast (Changes each frame)
private Vector2 firstPathEnd; // The ending point for the raycast (Changes each frame)
void start()
{
predictionPoints = new GameObject[numOfPoints];
for (int i = 0; i < numOfPoints; i++)
{
predictionPoints[i] = Instantiate(predictionPointPrefab, firePoint.position,
Quaternion.identity);
}
}
void Update
{
Debug.DrawLine(firstPathStart, firstPathEnd, UnityEngine.Color.black);
DrawPredictionDisplay();
}
void DrawPredictionDisplay()
{
for (int i = 0; i < numOfPoints; i++)
{
predictionPoints[i].transform.position = predictionPointPosition(i * spaceBetweenPoints);
}
}
Vector2 predictionPointPosition(float time)
{
Vector2 position = (Vector2)firstPathStart + direction.normalized * 10f * time;
return position;
}
The current system simply takes in a starting position, a direction, and then moves a preset number of objects in that direction based on time. This way of doing it also causes problems because it's endess instead of only going till the end of the raycast: (Pardon my drawing skills)
Blue line = raycast
Black dots = instantiated prefab
Orange dot = raycast orange
Green dot = end of raycast
Notes:
direction is the momentum which I set in the editor, I needed it to put together what I currently have working, but it shouldn't be necessary when running based on points.
If you ask me I would say it is kinda easy if you know little bit of Math trickery. I'm not saying that I'm very good at Math, but once you get it it's kind of easy to pull off next time. Here if I try to explain everything, i won't be able to explain clearly. Take a look as the code below I've commented the whole code so that you can understand easily.
Basically I used a Method called Vector2.Lerp() Liner Interpolation, which means that this method will return value between point1, and point2 based on the value of 3rd argument t which goes from 0 to 1.
public class TestScript : MonoBehaviour
{
public Transform StartPoint;
public Transform EndPoint;
public float spaceBetweenPoints;
[Space]
public Vector2 startPosition;
public Vector2 endPosition;
[Space]
public List<Vector3> points;
private float distance;
private void Update()
{
startPosition = StartPoint.position; //Setting Starting point and Ending point.
endPosition = EndPoint.position;
//Finding the distance between point
distance = Vector2.Distance(startPosition, endPosition);
//Generating the points
GeneratePointsObjects();
Debug.DrawLine(StartPoint.position, EndPoint.position, Color.black);
}
private void OnDrawGizmos()
{
//Drawing the Dummy Gizmo Sphere to see the points
Gizmos.color = Color.black;
foreach (Vector3 p in points)
{
Gizmos.DrawSphere(p, spaceBetweenPoints / 2);
}
}
private void OnValidate()
{
//Validating that the space between two objects is not 0 because that would be Raise an exception "Devide by Zero"
if (spaceBetweenPoints <= 0)
{
spaceBetweenPoints = 0.01f;
}
}
private void GeneratePointsObjects()
{
//Vlearing the list so that we don't iterate over old points
points.Clear();
float numbersOfPoints = distance / spaceBetweenPoints; //Finding numbers of objects to be spawned by dividing "distance" by "spaceBetweenPoints"
float increnment = 1 / numbersOfPoints; //finding the increment for Lerp this will always be between 0 to 1, because Lerp() takes value between 0 to 1;
for (int i = 1; i < numbersOfPoints; i ++)
{
Vector3 v = Vector2.Lerp(startPosition, endPosition, increnment * i); //Find next position using Vector2.Lerp()
points.Add(v);//Add the newlly found position in List so that we can spwan the Object at that position.
}
}
}
Update: Added, How to set prefab on the positions
I just simply Destroyed old objects and Instantiated new Objects. But remember instantiating and Destroying object frequently in your game in unity will eat-up memory on your player's machine. Os I would suggest you to use Object-Pooling. For the reference I'll add a link to tutorial.
private void Update()
{
startPosition = StartPoint.position; //Setting Starting point and Ending point.
endPosition = EndPoint.position;
//Finding the distance between point
distance = Vector2.Distance(startPosition, endPosition);
//Generating the points
GeneratePointsObjects();
//Update: Generating points/dots on all to location;
InstenciatePrefabsOnPositions();
Debug.DrawLine(StartPoint.position, EndPoint.position, Color.black);
}
private void InstenciatePrefabsOnPositions()
{
//Remove all old prefabs/objects/points
for (int i = 0; i < pointParent.childCount; i++)
{
Destroy(pointParent.GetChild(i).gameObject);
}
//Instantiate new Object on the positions calculated in GeneratePointsObjects()
foreach (Vector3 v in points)
{
Transform t = Instantiate(pointPrefab);
t.SetParent(pointParent);
t.localScale = Vector3.one;
t.position = v;
t.gameObject.SetActive(true);
}
}
Hope this helps please see below links for more reference
OBJECT POOLING in Unity
Vector2.Lerp
I hope I understood you right.
First, compute the A to B line, so B minus A.
To get the number of needed objects, divide the line magnitude by the objects' spacing. You could also add the diameter of the prediction point object to avoid overlapping.
Then to get each object position, write (almost) the same for loop.
Here's what I came up with, didn't tested it, let me know if it helps!
public class CustomLineRenderer : MonoBehaviour
{
public float SpaceBetweenPoints;
public GameObject PredictionPointPrefab;
// remove transforms if you need to
public Transform start;
public Transform end;
private List<GameObject> _predictionPoints;
// these are your raycast start & end point, make them public or whatever
private Vector2 _firstPathStart;
private Vector2 _firstPathEnd;
private void Awake()
{
_firstPathStart = start.position;
_firstPathEnd = end.position;
_predictionPoints = new List<GameObject>();
}
private void Update()
{
_firstPathStart = start.position;
_firstPathEnd = end.position;
// using any small value to clamp everything and avoid division by zero
if (SpaceBetweenPoints < 0.001f) SpaceBetweenPoints = 0.001f;
var line = _firstPathEnd - _firstPathStart;
var objectsNumber = Mathf.FloorToInt(line.magnitude / SpaceBetweenPoints);
var direction = line.normalized;
// Update the collection so that the line isn't too short
for (var i = _predictionPoints.Count; i <= objectsNumber; ++i)
_predictionPoints.Add(Instantiate(PredictionPointPrefab));
for (var i = 0; i < objectsNumber; ++i)
{
_predictionPoints[i].SetActive(true);
_predictionPoints[i].transform.position = _firstPathStart + direction * (SpaceBetweenPoints * i);
}
// You could destroy objects, but it's better to add them to the pool since you'll use them quite often
for (var i = objectsNumber; i < _predictionPoints.Count; ++i)
_predictionPoints[i].SetActive(false);
}
}

Strange outputs for a moving platform in Unity

First off, sorry it this isn't written very well, I've spend hours debugging this and I'm very stressed. I'm trying to make a moving platform in unity that can move between way-points, I don't want to have to have tons of gameobjects in the world taking up valuable processing power though so I'm trying to use something I can just add to the script through the editor.
The only problem is that it seems to be doing this at an incredible speed:
Black = The Camera View, Blue = The platform and where it should be going based on waypoints, Red = What it is currently doing.
I've spend hours trying to find a fix but I have no idea why it's doing this.
My Script on the Platform:
public Vector3[] localWaypoints;
Vector3[] globalWaypoints;
public float speed;
public bool cyclic;
public float waitTime;
[Range(0, 2)]
public float easeAmount;
int fromWaypointIndex;
float percentBetweenWaypoints;
float nextMoveTime;
void Start()
{
globalWaypoints = new Vector3[localWaypoints.Length];
for (int i = 0; i < localWaypoints.Length; i++)
{
globalWaypoints[i] = localWaypoints[i] + transform.position;
}
}
void Update()
{
Vector3 velocity = CalculatePlatformMovement();
transform.Translate(velocity);
}
float Ease(float x)
{
float a = easeAmount + 1;
return Mathf.Pow(x, a) / (Mathf.Pow(x, a) + Mathf.Pow(1 - x, a));
}
Vector3 CalculatePlatformMovement()
{
if (Time.time < nextMoveTime)
{
return Vector3.zero;
}
fromWaypointIndex %= globalWaypoints.Length;
int toWaypointIndex = (fromWaypointIndex + 1) % globalWaypoints.Length;
float distanceBetweenWaypoints = Vector3.Distance(globalWaypoints[fromWaypointIndex], globalWaypoints[toWaypointIndex]);
percentBetweenWaypoints += Time.deltaTime * speed / distanceBetweenWaypoints;
percentBetweenWaypoints = Mathf.Clamp01(percentBetweenWaypoints);
float easedPercentBetweenWaypoints = Ease(percentBetweenWaypoints);
Vector3 newPos = Vector3.Lerp(globalWaypoints[fromWaypointIndex], globalWaypoints[toWaypointIndex], easedPercentBetweenWaypoints);
if (percentBetweenWaypoints >= 1)
{
percentBetweenWaypoints = 0;
fromWaypointIndex++;
if (!cyclic)
{
if (fromWaypointIndex >= globalWaypoints.Length - 1)
{
fromWaypointIndex = 0;
System.Array.Reverse(globalWaypoints);
}
}
nextMoveTime = Time.time + waitTime;
}
return newPos - transform.position;
}
struct PassengerMovement
{
public Transform transform;
public Vector3 velocity;
public bool standingOnPlatform;
public bool moveBeforePlatform;
public PassengerMovement(Transform _transform, Vector3 _velocity, bool _standingOnPlatform, bool _moveBeforePlatform)
{
transform = _transform;
velocity = _velocity;
standingOnPlatform = _standingOnPlatform;
moveBeforePlatform = _moveBeforePlatform;
}
}
void OnDrawGizmos()
{
if (localWaypoints != null)
{
Gizmos.color = Color.red;
float size = .3f;
for (int i = 0; i < localWaypoints.Length; i++)
{
Vector3 globalWaypointPos = (Application.isPlaying) ? globalWaypoints[i] : localWaypoints[i] + transform.position;
Gizmos.DrawLine(globalWaypointPos - Vector3.up * size, globalWaypointPos + Vector3.up * size);
Gizmos.DrawLine(globalWaypointPos - Vector3.left * size, globalWaypointPos + Vector3.left * size);
}
}
}
UPDATE: Upon further testing I found that if the first object in my localWaypoint array is set to 0,0,0 and my 2nd object is set to 1,0,0 then the platform will spiral to the right, making sure to hit the waypoints as it's spiraling, and then spiraling out into nowhere like in the image above. But if I set my first object to 0,0,0 and my second object to -1,0,0 then the object will act the same way as before, but will spiral to the left as displayed in this image. (The second image has also bee updated to display how the platfrom makes sure to hit both waypoints before is spirals out into nowhere).
I've also noticed that if I set both waypoints to 0,0,0 then the platform stays still, these 2 things prove that it has somthing to do with the way the waypoints are being handled and not some other script or parent object interfering.
Using the updated numbers ([0,0,0], [1,0,0]) works in my test app. However, if I put a rotation on the object's Y axis, then I see behavior like you are seeing. In Update, if you change:
transform.Translate(velocity);
to
transform.Translate(velocity, Space.World);
You should see your desired behavior. Note that "transform.Translate(velocity)" is the same as "transform.Translate(velocity, Space.Self)". Your translation is being rotated.
If you are curious, take a look at this for more information on how the values in the transform are applied:
https://gamedev.stackexchange.com/questions/138358/what-is-the-transformation-order-when-using-the-transform-class

How can I change position of instantiate objects (clones)?

I created a series of sphere clones in my game. After that I adapted the scale so that they appear smaller. However, now there is a gap between these spheres ... and I would have to change the position of this instatiate game objects. I changed my code already exactly at this position but nothing happens. So please I need your help! How can I do this? I would have very small spheres which are located near together.
Here the code:
using UnityEngine;
using System.Collections;
public class SineWave : MonoBehaviour {
private GameObject plotPointObject;
private int numberOfPoints= 100;
private float animSpeed =1.0f;
private float scaleInputRange = 8*Mathf.PI; // scale number from [0 to 99] to [0 to 2Pi] //Zahl vor Mathf, Anzahl Bön
private float scaleResult = 2.5f; // Y Achse Range
public bool animate = true;
GameObject[] plotPoints;
// Use this for initialization
void Start () {
if (plotPointObject == null) //if user did not fill in a game object to use for the plot points
plotPointObject = GameObject.CreatePrimitive(PrimitiveType.Sphere); //create a sphere
//add Material to the spheres , load material in the folder Resources/Materials
Material myMaterial = Resources.Load("Materials/green", typeof(Material)) as Material;
plotPointObject.GetComponent<MeshRenderer> ().material = myMaterial;
//change the scale of the spheres
//plotPointObject.transform.localScale = Vector3.one * 0.5f ;
plotPointObject.transform.localScale -= new Vector3(0.5f,0.5f,0.5f);
plotPoints = new GameObject[numberOfPoints]; //creat an array of 100 points.
//plotPointObject.GetComponent<MeshRenderer> ().material =Material.Load("blue") as Material
//plotPointObject.transform.localScale -= new Vector3 (0.5F, 0.5F, 0.5F); //neu: change the scale of the spheres
for (int i = 0; i < numberOfPoints; i++)
{
plotPoints[i] = (GameObject)GameObject.Instantiate(plotPointObject, new Vector3(i -
(numberOfPoints/2), 0, 0), Quaternion.identity); //this specifies
what object to create, where to place it and how to orient it
}
//we now have an array of 100 points- your should see them in the hierarchy when you hit play
plotPointObject.SetActive(false); //hide the original
}
Thank you already in advance!
Edit:
As I said in the comment I achieved now to place my spheres without a gap in between. However, as soon as I animate my spheres (with a sine wave) there is still that gap between the spheres. How can I adapt this? Should I copy the code of the Start function in the Update function?
I would be very happy to get some help. Thank you very much!
enter code here void Update()
{
for (int i = 0; i < numberOfPoints; i++)
{
float functionXvalue = i * scaleInputRange / numberOfPoints; // scale number from [0 to 99] to [0 to 2Pi]
if (animate)
{
functionXvalue += Time.time * animSpeed;
}
plotPoints[i].transform.position = new Vector3(i - (numberOfPoints/2), ComputeFunction(functionXvalue) * scaleResult, 0);
//print (plotPointObject.GetComponent<MeshRenderer> ().bounds.size.x);
// put the position information of sphere clone 50 in a vector3 named posSphere
posSphere = plotPoints [50].transform.position;
}
//print position of sphere 50 in console
//print (posSphere);
}
float ComputeFunction(float x)
{
return Mathf.Sin(x);
}
}
I think you could make the Barış solution.
For each new object that you are instantiating, you will set his position to the lasted instantiated position adding the size of the object itself, or whatever distance that you want they have from each other.
var initialPosition = 0;
var distanceFromEachOther = 20;
for (int i = 0; i < numberOfPoints; i++) {
var newPos = new Vector3(initialPosition + (i * distanceFromEachOther), 0, 0);
plotPoints[i] = (GameObject)GameObject.Instantiate(plotPointObject, newPos, Quaternion.identity);
}
That will make a gap between the spheres at X pivot, depending on their size. Change the distanceFromEachOther var, adjusting for your needs.
You could also get the object distance with plotPointObject.GetComponent<MeshRenderer>().bounds.size, so distanceFromEachOther could be, for example distanceFromEachOther = plotPointObject.GetComponent<MeshRenderer>().bounds.size.x + 5. So then you will have the objects with a perfectly distance of 5 from each other.
give this a try:
Transform objectToSpawn;
for (int i = 0; i < numberOfPoints; i++)
{
float someX = 200;
float someY = 200;
Transform t = Instantiate(objectToSpawn, new Vector3(i -(numberOfPoints/2), 0, 0), Quaternion.identity) as Transform;
plotPoints[i] = t.gameObject;
t.position = new Vector(someX, someY);
}

Categories

Resources