Problems when constructing a Fractal Tree - c#

I've been trying to use what I learned in the this fractal tutorial to make something I can use for my own project. I want to a script that can generate trees in Unity. I feel I've made a lot of progress, but have hit a wall.
The way I've set up the script is so that it scales the branches according to two parameters:
a) a publich 'childScale' variable.
b) the amount of branches sprouting from the previous branch.
The biggest problem has been that, when children are parented to a non-uniform-scale object, they become distorted in unintended ways. To bypass, I've made the prefab instances (which are 1, 1, 1 by default) children of other GameObjects. These GameObjects are also parents to other GameObjects that contain other prefab instances. This is problematic because the scaling principle of a fractal necessitates a continuous inheritance of scale from the parent, but the child prefab instances never pass anything along. So I end up having to adjust the proportions of the prefab instances to what they would be if they could inherit directly from one another. The below script 'works' because of the exponential modifier (middle of Start(), after 'else'), but only if all branches have the same amount of offshoots, i.e. only if the public min and max Branch Density variables are the same integer.
To summarize, I have two problems that I'd like your input on.
The main problem: How can I maintain the scaling integrity of the fractal principle despite the lack of a continous hierarchy while allowing for non-uniformity in the overall form of the tree?
A secondary problem, by far, is that my 'thicknessScaler' variable makes my branches too thin, especially the more there are. Right now it just divides 1 by the amount of offshoots, so I'd need one that doesn't shave off quite as much.
using UnityEngine;
using System.Collections;
public class TreeFractal : MonoBehaviour
{
public GameObject[] branches; // the last branch is pointed, while all the others are rounded
public int maxDepth;
public float childScale;
public float maxTwist; // OFF temporarily
public Vector3 baseBranchSize; // proportions of instantiated prefab
public int minBranchDensity; // amount of offshoots per node, randomized
public int maxBranchDensity;
public float branchTilt;
private int depth;
private int branchDensity;
private GameObject branch;
private GameObject instance;
private TreeFractal grandparent;
private float displace;
private float thicknessScaler = 1;
private float parentDensity;
private void Start()
{
if (depth < maxDepth)
branch = branches[0];
else
branch = branches[1];
instance = Instantiate(branch);
instance.transform.parent = transform; // prefabs (non-uniform proportions) are made the children of uniform-scaled Game Objects.
if (depth == 0)
{
displace = baseBranchSize.y;
instance.transform.localScale = baseBranchSize;
}
else //Multiplication by density^depth is to make up for the shrinking game objects, as the prefab instances do not pass on their scale
//while the GameObjects do. This only works when all 'depths' of the tree have the same amount of offshoots.
//Because the GameObject must remain uniform, all scaling of the y axis must occur in the prefab instance.
{
displace = baseBranchSize.y * Mathf.Pow(parentDensity, depth);
//if (depth == 2)3
print(baseBranchSize.y * Mathf.Pow(parentDensity, depth));
instance.transform.localScale = new Vector3
(
baseBranchSize.x,
baseBranchSize.y * Mathf.Pow(parentDensity, depth),
baseBranchSize.z
);
}
instance.transform.localPosition = new Vector3(0f, 1f * (displace / 2), 0f); //prefab instance pivots at one end of the GameObject, for rotations.
instance.transform.localRotation = Quaternion.Euler(0f, 0f, 0f);
transform.Rotate(Random.Range(-maxTwist * ((float)(depth + 1)/ maxDepth), maxTwist * ((float)(depth + 1) / maxDepth)), 0f, 0f);
//increases the potential randomized twist more, the smaller the branches get.
if (depth < maxDepth)
{
StartCoroutine(CreateChildren());
}
}
private void Update()
{
}
private IEnumerator CreateChildren()
{
branchDensity = Random.Range(minBranchDensity, maxBranchDensity + 1);
for (int i = 0; i < branchDensity; i++)
{
yield return new WaitForSeconds(Random.Range(0.1f, 0.5f));
Quaternion quaternion = BranchRotater(branchDensity, i);
new GameObject("Fractal Child").AddComponent<TreeFractal>().
Initialize(this, i, quaternion);
}
}
private Quaternion BranchRotater (int density, int childIndex) //returns the rotation of the branch depending on the index and amount of branches.
{
Quaternion quaternion = Quaternion.Euler
(0f,
(360 / density) * childIndex,
branchTilt
);
return quaternion;
}
private void Initialize(TreeFractal parent, int childIndex, Quaternion quaternion)
{
branches = parent.branches;
branchTilt = parent.branchTilt;
maxDepth = parent.maxDepth;
depth = parent.depth + 1;
baseBranchSize = parent.baseBranchSize;
maxTwist = parent.maxTwist;
transform.parent = parent.transform;
childScale = parent.childScale;
minBranchDensity = parent.minBranchDensity;
maxBranchDensity = parent.maxBranchDensity;
thicknessScaler = 1f / parent.branchDensity; // I need a better equation here. This scaler is too small.
transform.localScale = Vector3.one * childScale * thicknessScaler; // reproportions all 3 axes of child GameObject so that
//the child remains of uniform scale. This must then be compensated for in the scaling of said object's child-prefab.
parentDensity = parent.branchDensity;
transform.localPosition = Vector3.up * parent.displace; //positions child relative to its parent
transform.localRotation = quaternion;
}
}

If you build your prefab like this you can apply the parent-to-child scale at the root node of the prefab and the nonuniform at the subnode. If you now attach the child's root to the root node only the uniform scale will carry over.
prefab:
[root]
|--->[non-uni]
| |---> mesh
.
.
[childRoot]

Related

How to make my child object correctly rotate around the parent?

I previously had the below code, which I used to orientate my object towards where the right analog stick on my controller was (using the unity input system).
public void OnAim(InputAction.CallbackContext context)
{
aimDir = context.ReadValue<Vector2>();
}
void HandleRotation()
{
if (aimDir.magnitude >= 0.5f)
{
var aimAngle = Mathf.Atan2(aimDir.y, aimDir.x) * Mathf.Rad2Deg - 90f;
rb.rotation = aimAngle;
}
}
This worked by rotating the entire parent, however I now want to keep the parent object rotation fixed whilst just rotating the child object around it. I tried to rewrite the code:
void HandleRotation()
{
if (aimDir.magnitude >= 0.5f)
{
var aimAngle = Mathf.Atan2(aimDir.y, aimDir.x) * Mathf.Rad2Deg - 90f;
shootPoint.transform.RotateAround(transform.position, new Vector3(0, 0, 1), aimAngle);
}
}
But this makes it constantly spin rather than just smoothly rotating towards where my right analog stick is facing. How do I fix this?
Fistly, in this:
var aimAngle = Mathf.Atan2(aimDir.y, aimDir.x) * Mathf.Rad2Deg - 90f;
Your aimAngle is not rad but degree so, i think you should use
rb.rotation = Qaternion.Eualar(aimAngle);
instead of
rb.rotation = aimAngle;
In your question, you could create an empty GameObject, put it in your child object(which you want to rotate)'s parent then put your child object in this empty object. Then rotate this empty object. This way, you have a rotating child object seems like parent turning and it is turning around it. Also, you awaided to rotate parent too.
This can be done with Vector3.RotateTowards() and localPosition. Attach the shootPoint game object to a parent game object, attach a script with the following code to the parent object, and give the script a reference to shootPoint.
public Transform shootPoint;
public float distance = 2f;
public float speed = 5f;
private Vector3 _pos;
private Vector2 _aimDir;
public void OnAim(InputAction.CallbackContext context) =>
aimDir = context.ReadValue<Vector2>();
private void Update()
{
if (_aimDir.magnitude >= float.Epsilon)
_pos = new Vector3(_aimDir.x, 0f, _aimDir.y).normalized;
shootPoint.localPosition =
Vector3.RotateTowards(
shootPoint.localPosition,
_pos * distance,
speed * Time.deltaTime,
0f);
}

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);
}
}

How would you lerp the positions of two matrices?

In my code, I create 2 matrices that store information about two different meshes, using the following code:
Mesh mesh;
MeshFilter filter;
public SphereMesh SphereMesh;
public CubeMesh CubeMesh;
public Material pointMaterial;
public Mesh pointMesh;
public List<Matrix4x4> matrices1 = new List<Matrix4x4>();
public List<Matrix4x4> matrices2 = new List<Matrix4x4>();
[Space]
public float normalOffset = 0f;
public float globalScale = 0.1f;
public Vector3 scale = Vector3.one;
public int matricesNumber = 1; //determines which matrices to store info in
public void StorePoints()
{
Vector3[] vertices = mesh.vertices;
Vector3[] normals = mesh.normals;
Vector3 scaleVector = scale * globalScale;
// Initialize chunk indexes.
int startIndex = 0;
int endIndex = Mathf.Min(1023, mesh.vertexCount);
int pointCount = 0;
while (pointCount < mesh.vertexCount)
{
// Create points for the current chunk.
for (int i = startIndex; i < endIndex; i++)
{
var position = transform.position + transform.rotation * vertices[i] + transform.rotation * (normals[i].normalized * normalOffset);
var rotation = Quaternion.identity;
pointCount++;
if (matricesNumber == 1)
{
matrices1.Add(Matrix4x4.TRS(position, rotation, scaleVector));
}
if (matricesNumber == 2)
{
matrices2.Add(Matrix4x4.TRS(position, rotation, scaleVector));
}
rotation = transform.rotation * Quaternion.LookRotation(normals[i]);
}
// Modify start and end index to the range of the next chunk.
startIndex = endIndex;
endIndex = Mathf.Min(startIndex + 1023, mesh.vertexCount);
}
}
The GameObject starts as a Cube mesh, and this code stores the info about the mesh in matrices1. Elsewhere in code not shown, I have it so that the Mesh changes to a Sphere and then changes matricesnumber to 2, then triggers the code above to store the info of the new Sphere mesh in matrices2.
This seems to be working, as I'm able to use code like Graphics.DrawMesh(pointMesh, matrices1[i], pointMaterial, 0);
to draw 1 mesh per vertex of the Cube mesh. And I can use that same line (but with matrices2[i]) to draw 1 mesh per vertex of the Sphere mesh.
The Question: How would I draw a mesh for each vertex of the Cube mesh (info stored in matrices1) on the screen and then make those vertex meshes Lerp into the positions of the Sphere mesh's (info stored in matrices2) vertices?
I'm trying to hack at it with things like
float t = Mathf.Clamp((Time.time % 2f) / 2f, 0f, 1f);
matrices1.position.Lerp(matrices1.position, matrices2.position, t);
but obviously this is invalid code. What might be the solution? Thanks.
Matrix4x4 is usually only used in special cases for directly calculating transformation matrices.
You rarely use matrices in scripts; most often using Vector3s, Quaternions and functionality of Transform class is more straightforward. Plain matrices are used in special cases like setting up nonstandard camera projection.
In your case it seems to me you actually only need to somehow store and access position, rotation and scale.
I would suggest to not use Matrix4x4 at all but rather something simple like e.g.
// The Serializable makes them actually appear in the Inspector
// which might be very helpful in you case
[Serializable]
public class TransformData
{
public Vector3 position;
public Quaternion rotation;
public Vector3 scale;
}
Then with
public List<TransformData> matrices1 = new List<TransformData>();
public List<TransformData> matrices2 = new List<TransformData>();
you could simply lerp over
var lerpedPosition = Vector3.Lerp(matrices1[index].position, matrices2[index].position, lerpFactor);
And if you then need it as a Matrix4x4 you can still create it ad-hoc like e.g.
var lerpedMatrix = Matrix4x4.Translation(lerpedPosition);
or however you want to use the values.

How to instantiate object on correct position?

I have one really big issue with my game . When i try press play when he spawn first prefab everything is correct X position is -0.604f , z is -1.49f..But when i spawn second object the second and the third object get spawned on wrong position of x and z.
On the second object X position is not good ( in this case second Z position of object is -1.208 and i need here same value as before -0.604) I really don't know why this happens and I don't know how i can change Z position when i spawn third object in other side so if first two objects are on right and left and when i spawn third object he get Z position of -4.968 which is not correct because I need on third position on Z = -1.655984 and i am getting on third object z position of ~-5f.
Here is my code to spawn objects..
public class StackPiecesSpawner : Singleton<StackPiecesSpawner> {
public GameObject stackPrefab;
[SerializeField] public Transform StackPieceSpawnPoint;
[SerializeField] public float PieceSpawnSize = 0.1f;
[SerializeField] public float PieceSpawnWidth = 1.0f;
[SerializeField] private float speed = 1f;
public Vector3 OriginalSpawnPointPosition;
protected override void Awake()
{
base.Awake();
OriginalSpawnPointPosition = StackPieceSpawnPoint.position;
}
public void SpawnNextStackPiece()
{
//StackPieceSpawnPoint.position += new Vector3(0f,PieceSpawnSize,0f);
StackPieceSpawnPoint.position += new Vector3(0.604f, 0.1f, -1.656f);
//GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);
GameObject go = GameObject.Instantiate(stackPrefab);
//go.transform.localScale = new Vector3(PieceSpawnWidth,PieceSpawnSize,PieceSpawnWidth);
go.transform.localScale = new Vector3(0.4f, 0.4f,0.05f);
go.transform.position = nextSpawnPosition();
go.AddComponent<Rigidbody>();
go.GetComponent<Rigidbody>().isKinematic = true;
go.AddComponent<StackPiece>();
go.transform.parent = transform;
go.transform.name = "StackPiece_" + (StackPiecesManager.StackPieces.Count + 1);
StackPiece piece = go.GetComponent<StackPiece>();
piece.TargetTransform = StackPieceSpawnPoint;
piece.Speed = speed;
StackPiecesManager.Instance.AddNewPiece(piece);
}
private Vector3 nextSpawnPosition()
{
System.Random rnd = new System.Random();
int num = rnd.Next(0, 100);
Vector3 modifier = new Vector3();
modifier = num < 50 ? Vector3.back : Vector3.left;
modifier *= StackPiecesManager.Instance.SpawnDistance;
Vector3 returned = (StackPieceSpawnPoint.position - modifier) + Vector3.up * (StackPiecesManager.Instance.ExtraHeight);
// returned = (StackPieceSpawnPoint.position) + Vector3.up * (StackPiecesManager.Instance.ExtraHeight);
return returned;
}
public void Reset()
{
StackPieceSpawnPoint.position = OriginalSpawnPointPosition;
}
}
I cannot completely answer this question as not all the code and context is available, however, I could maybe point you in the right direction.
Parent-child relationship
In the SpawnNextStackPiece you instantiate a prefab and have its current parent transform equal the current transform(of the object this script is attached to), check if the transform you're changing the parent to is shifting each time a prefab is instantiated. I tend to avoid changing parent transforms, I usually try to attach it to different parents and change the actual objects offset not the parents, it can be easier to manage. I suspect go.transform.parent = transform; is causing a problem.
Diagnosing the problem through zeroing out values
Try forcefully making the z-axis 0 so you can see how much it is getting offset by each time it spawns, that may make diagnosing the problem much easier. Do this with StackPieceSpawnPoint.position = new Vector3(0.604f, 0.1f,0f);

Create GameObjects the have gravity based on size

I've built a fractal based object generator in c# and unity that builds branches of objects that then bounce off each other using Colliders and Rigidbodies. Right now they hit each other and keep moving farther and farther apart. What I'd like to do it assign each object a certain level of gravitational attraction so that even as they're repelled through a collision they draw themselves back in. I've got everything except working except for the gravity side of things. Does anyone have experience with this who wouldn't mind giving me some direction? Thanks!
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class BuildFractal : MonoBehaviour
{
public Mesh[] meshes;
public Material material;
public Material[,] materials;
private Rigidbody rigidbody;
public int maxDepth; // max children depth
private int depth;
public float childScale; // set scale of child objects
public float spawnProbability; // determine whether a branch is created or not
public float maxRotationSpeed; // set maximium rotation speed
private float rotationSpeed;
public float maxTwist;
public Text positionText;
// Create arrays for direction and orientation data
private static Vector3[] childDirections = {
Vector3.up,
Vector3.right,
Vector3.left,
Vector3.forward,
Vector3.back,
// Vector3.down
};
private static Quaternion[] childOrientations = {
Quaternion.identity,
Quaternion.Euler(0f, 0f, -90f),
Quaternion.Euler(0f, 0f, 90f),
Quaternion.Euler(90f, 0f, 0f),
Quaternion.Euler(-90f, 0f, 0f),
// Quaternion.Euler(180f, 0f, 0f)
};
private void Start ()
{
rotationSpeed = Random.Range(-maxRotationSpeed, maxRotationSpeed);
transform.Rotate(Random.Range(-maxTwist, maxTwist), 0f, 0f);
if (materials == null)
{
InitializeMaterials();
}
// Select from random range of meshes
gameObject.AddComponent<MeshFilter>().mesh = meshes[Random.Range(0, meshes.Length)];
// Select from random range of colors
gameObject.AddComponent<MeshRenderer>().material = materials[depth, Random.Range(0, 2)];
// Add a collider to each object
gameObject.AddComponent<SphereCollider>().isTrigger = false;
// Add Rigigbody to each object
gameObject.AddComponent<Rigidbody>();
gameObject.GetComponent<Rigidbody>().useGravity = false;
gameObject.GetComponent<Rigidbody>().mass = 1000;
// Create Fractal Children
if (depth < maxDepth)
{
StartCoroutine(CreateChildren());
}
}
private void Update ()
{
transform.Rotate(0f, rotationSpeed * Time.deltaTime, 0f);
}
private IEnumerator CreateChildren ()
{
for (int i = 0; i < childDirections.Length; i++)
{
if (Random.value < spawnProbability)
{
yield return new WaitForSeconds(Random.Range(0.1f, 1.5f));
new GameObject("Fractal Child").AddComponent<BuildFractal>().Initialize(this, i);
}
/*if (i == childDirections.Length)
{
DestroyChildren();
}*/
// positionText.text = transform.position.ToString(this);
}
}
private void Initialize (BuildFractal parent, int childIndex)
{
maxRotationSpeed = parent.maxRotationSpeed;
// copy mesh and material references from parent object
meshes = parent.meshes;
materials = parent.materials;
maxTwist = parent.maxTwist;
// set depth and scale based on variables defined in parent
maxDepth = parent.maxDepth;
depth = parent.depth + 1;
childScale = parent.childScale;
transform.parent = parent.transform; // set child transform to parent
// transform.localScale = Vector3.one * childScale;
transform.localScale = Vector3.one * Random.Range(childScale / 10, childScale * 1);
transform.localPosition = childDirections[childIndex] * (Random.Range((0.1f + 0.1f * childScale),(0.9f + 0.9f * childScale)));
transform.localRotation = childOrientations[childIndex];
spawnProbability = parent.spawnProbability;
}
private void InitializeMaterials ()
{
materials = new Material[maxDepth + 1, 2];
for (int i = 0; i <= maxDepth; i++)
{
float t = i / (maxDepth - 1f);
t *= t;
// Create a 2D array to hold color progressions
materials[i, 0] = new Material(material);
materials[i, 0].color = Color.Lerp(Color.gray, Color.white, t);
materials[i, 1] = new Material(material);
materials[i, 1].color = Color.Lerp(Color.white, Color.white, t);
}
// materials[maxDepth, 0].color = Color.white;
materials[maxDepth, 1].color = Color.white;
}
}
Depends how accurate your gravity simulation has to be. Assuming all objects in your simulation have the same density, you could use Mesh.bounds to roughly estimate their volume:
Vector3 size = myMesh.bounds.size;
float volume = size.x * size.y * size.z * scale; // scale could be childScale in your case
Since your simulation is a fractal, you will have to apply childScale in each of your fractal's iterations. But you don't have to recalculate the base volume of your mesh if it doesn't change.
As for the gravity simulation:
This might get quite complex with a high number of objects. You would have to simulate a whole gravity field.
The calculation for only two objects interacting with each other is rather simple. The forces applied to the bodies attracting each other can be calculated by the Newtonian formula
F1 = F2 = G * m1 * m2 / r^2
(see: https://en.wikipedia.org/wiki/Gravitational_constant)
But you may have far more objects than two in your system. You would have to calculate the above relationship for each object -- between each object. And for each object, you would have to add all calculated forces and than apply the resulting force.
Let's say you have N objects in your scene, you would have to do (N-1) of the above calculations for each object. That yields N^(N-1) calculations, which will get out of hand quite quickly, especially if you doing it in a fractal structure.
To get hold of this immense complexity, you could introduce a range of influence, so only nearby objects have an effect on each other. Although this will further reduce the accuracy of your simulation.

Categories

Resources