Hi all,
I’m working on chopping tress in my survival game. Everything works just fine, after I destroy tree (created with terrain tool), I Instantiate new tree. That’s fine. Problem begins when I add Rigidbody to that tree. Right before tree should fall down, it moves to side (for no reason), then it immediately start falling down. It would be perfect, if tree wouldn’t teleport to another position (just a little, but everyone can see it).
I tried to resize collider on tree, changed drag, angular drag, I tried everything, but nothing helped.
Any help would be appreciate.
GameObject tree = Instantiate(FallingTreePrefabs[TreeInstances[damaged_tree.index].prototypeIndex], damaged_tree.position, Quaternion.Euler(0, rotation, 0));
tree.transform.localScale += newScale;
Rigidbody rb = tree.AddComponent<Rigidbody>();
rb.constraints = RigidbodyConstraints.FreezeRotationY;
rb.mass = 0.1f;
There is full script if anyone would be interested. It’s attached to an empty game object
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class TreeChopping : MonoBehaviour
{
public GameObject[] FallingTreePrefabs;
private List<TreeInstance> TreeInstances;
private float distance;
public int maxTreeHealth;
private List<DamagedTree> damagedTrees;
private void Start()
{
TreeInstances = new List<TreeInstance>(Terrain.activeTerrain.terrainData.treeInstances);
damagedTrees = new List<DamagedTree>();
distance = 5;
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
hit();
}
}
private void hit()
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, distance))
{
if (hit.collider.name != Terrain.activeTerrain.name)
{
return;
}
//we hit terrain
float sampleHeight = Terrain.activeTerrain.SampleHeight(hit.point);
if (hit.point.y <= sampleHeight + 0.01f)
{
return;
}
//we hit tree!
treeHit(hit);
}
}
private void treeHit(RaycastHit hit)
{
Vector3 closestTreePosition = new Vector3();
TerrainData terrain = Terrain.activeTerrain.terrainData;
int closestTreeIndex = findClosestTree(hit, terrain, out closestTreePosition);
damageTree(terrain, closestTreePosition, closestTreeIndex);
}
private int findClosestTree(RaycastHit hit, TerrainData terrain, out Vector3 closestTreePosition)
{
closestTreePosition = default;
TreeInstance[] treeInstances = terrain.treeInstances;
float maxDistance = float.MaxValue;
int closestTreeIndex = 0;
for (int i = 0; i < treeInstances.Length; i++)
{
TreeInstance currentTree = treeInstances[i];
Vector3 currentTreeWorldPositision = Vector3.Scale(currentTree.position, terrain.size) + Terrain.activeTerrain.transform.position;
float distance = Vector3.Distance(currentTreeWorldPositision, hit.point);
if (distance < maxDistance)
{
maxDistance = distance;
closestTreeIndex = i;
closestTreePosition = currentTreeWorldPositision;
}
}
return closestTreeIndex;
}
private void damageTree(TerrainData terrain, Vector3 closestTreePosition, int closestTreeIndex)
{
foreach(DamagedTree tree in damagedTrees)
{
if(tree.position == closestTreePosition)
{
takeDamage(terrain, tree);
return;
}
}
//tree was not damaged yet
damagedTrees.Add(new DamagedTree(closestTreePosition, closestTreeIndex, maxTreeHealth-1));
}
private void takeDamage(TerrainData terrain, DamagedTree tree)
{
if(tree.treeHealth == 1)
{
destroyTree(terrain, tree);
}
else
{
tree.decreaseHealth();
}
}
private void destroyTree(TerrainData terrain, DamagedTree damaged_tree)
{
//calculate scale and rotation for new falling tree
float heightscale = TreeInstances[damaged_tree.index].heightScale;
float widthscale = TreeInstances[damaged_tree.index].widthScale;
Vector3 newScale;
newScale.x = widthscale - 1;
newScale.y = heightscale - 1;
newScale.z = widthscale - 1;
float rotation = TreeInstances[damaged_tree.index].rotation * 180 / 3.14f;
GameObject TreePrefab = FallingTreePrefabs[TreeInstances[damaged_tree.index].prototypeIndex];
Vector3 position = damaged_tree.position;
//remove tree from terrain
TreeInstances.RemoveAt(damaged_tree.index);
terrain.treeInstances = TreeInstances.ToArray();
//destroy collider
float[,] heights = terrain.GetHeights(0, 0, 0, 0);
terrain.SetHeights(0, 0, heights);
GameObject tree = Instantiate(TreePrefab, position, Quaternion.Euler(0, rotation, 0));
tree.transform.localScale += newScale;
StartCoroutine(treeFall(tree));
}
private IEnumerator treeFall(GameObject tree)
{
yield return new WaitForSeconds(1);
Rigidbody rb = tree.AddComponent<Rigidbody>();
rb.constraints = RigidbodyConstraints.FreezeRotationY;
rb.mass = 0.1f;
}
}
public class DamagedTree
{
public Vector3 position;
public int index;
public int treeHealth;
public DamagedTree(Vector3 position, int index, int treeHealth)
{
this.position = position;
this.index = index;
this.treeHealth = treeHealth;
}
public void decreaseHealth()
{
this.treeHealth--;
}
}