Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 14 Next capture
2021 2022 2023
2 captures
13 Jun 22 - 14 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
10
Question by getyour411 · Feb 26, 2014 at 08:56 AM · c#terraintreepainted

How Do I Interact With Terrain Trees?

How do I interact with Terrain Trees?

A terrain tree: painted via the Editor / Place Trees brush, or via use of the Mass Place Trees function, or via modification of terrainData from script. A terrain tree is not present in the project Hierarchy.

A placed tree: is visible in the Hierarchy and typically positioned manually or via scripts.

There is a common misconception that a developer, especially one working on a Sandbox, Build, RTS, Survival or Crafting system game, can build a functionality set on a placed tree (harvest, gather wood, lumberjack, chop, etc.) that might include scripts, triggers or components, turn that into a prefab then paint that tree onto the terrain and all the custom behaviours will follow. They do not. Nothing of the sort works.

This is a system shock to many developers (myself included) when they realize their whole concept needs to be reconsidered.

Comment
Add comment · Show 2
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image multinfs · Jul 06, 2014 at 03:17 PM 0
Share

Can you be more specific as for why this functionality won't work? Isn't the tree prefab cloned as-is and behaving as it would if it were placed directly in the game world as a gameobject?

Is this proven not to work too?

I'm quite interested in your thinking here since I might have to deal with this kind of functionality in my project.

avatar image getyour411 · Dec 20, 2014 at 09:45 PM 0
Share

Sorry to reply so late, but... 1) The tree prefab does not behave as-is when turned into a Terrain Tree. 2) Yes, proven. A simple test is to create a GObj Tree, add a script that prints 'you clicked me' on click, turn that into a prefab and paint it on your terrain as a Terrain Tree. You won't see anything when you click the Terrain tree - that's the crux of the issue really, scripts and such on GObj trees do not work when painted on as Terrain Trees.

2 Replies

· Add your reply
  • Sort: 
avatar image
13
Best Answer

Answer by getyour411 · Feb 26, 2014 at 08:57 AM

A sample solution is provided via two scripts.

Script #1 is attached to your Player. Script #2 is attached to a GameManager singleton-like object that is tagged as "GameManager", adapt to your needs.

Script #1 - attached to Player, will look for GameManager tagged object so change that line if you do something different.

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 
 // Purpose: demonstrate script interface to interact with terrain trees
 
 // Steps: Attach this to main(parent/top) Player gameObject, or adjust myTransform to your hierarchy,
 // define Inspector values for harvestTreeDistance, respawnTimer
 // Setup a prefab tree with CAPSULE collider, how to at bottom of
 // http://docs.unity3d.com/Documentation/Components/terrain-Trees.html
 // paint terrain tree 
 
 // Assign the non-collider prefab version of the tree to felledTree
 // press Play, left click on terrain tree
 
 // Harvested terrain tree info is passed to a manager object QM_ResourceManager for respawn management,
 // you'll need that too or you could comment out any functionality related to manager 
 
 // Note: this is not a demo of modifying terrainData permanently - there's enough risk involved with that
 // such that you shouldn't try it unless you know what you are doing.
 
 
 public class QM_HarvestTerrainTree : MonoBehaviour {
 
     // Player, Range
     public int harvestTreeDistance;        // Set [Inspector] min. distance from Player to Tree for your scale?
     public bool rotatePlayer = true;    // Should we rotate the player to face the Tree? 
     private Transform myTransform;        // Player transform for cache
 
     // Terrains, Hit
     private Terrain terrain;            // Derived from hit...GetComponent<Terrain>
     private RaycastHit hit;                // For hit. methods
     private string lastTerrain;            // To avoid reassignment/GetComponent on every Terrain click
 
     // Tree, GameManager
     public GameObject felledTree;        // Prefab to spawn at terrain tree loc for TIIIIIIMBER!
     private QM_ResourceManager rMgr;    // Resource manager script
     public float respawnTimer;            // Duration of terrain tree respawn timer
 
     void Start () {
 
         if (harvestTreeDistance <= 0) {
             Debug.Log ("harvestTreeDistance unset in Inspector, using value: 6");
             harvestTreeDistance = 6;
         }
 
         if (respawnTimer <= 0) {
             Debug.Log ("respawnTimer unset in Inspector, using quick test value: 15");
             respawnTimer = 15;
         }
 
         myTransform = transform;
         lastTerrain = null;
         rMgr = GameObject.FindGameObjectWithTag ("GameManager").GetComponent<QM_ResourceManager> ();
 
     }
 
 
     void Update () {
     
         if (Input.GetMouseButtonUp (0)) {
 
             Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
             if (Physics.Raycast (ray, out hit, 30f)) {
 
                 // Did we click a Terrain?
                 if(hit.collider.gameObject.GetComponent<Terrain>() == null)
                     return;
 
                 // Did we click on the same Terrain as last time (or very first time?)
                 if(lastTerrain == null || lastTerrain != hit.collider.name) {
                     terrain = hit.collider.gameObject.GetComponent<Terrain>();
                     lastTerrain = terrain.name;
                 }
 
                 // Was it the terrain or a terrain tree, based on SampleHeight()
                 float groundHeight = terrain.SampleHeight(hit.point);
 
                 if(hit.point.y - .2f > groundHeight) {
 
                     // It's a terrain tree, check Proximity and Harvest
                     if(CheckProximity()) 
                         HarvestWood();
 
                 }
             }
         }
     }
 
 
     private bool CheckProximity() {
         bool inRange = true;
         float clickDist = Vector3.Distance (myTransform.position, hit.point);
 
         if (clickDist > harvestTreeDistance) {
             Debug.Log ("Out of Range");
             inRange = false;
         }
 
         return inRange;
 
     }
 
     private bool CheckRecentUsage(string _terrainName, int _treeINDEX) {
         bool beenUsed = false;
 
         for (int cnt=0; cnt < rMgr.managedTrees.Count; cnt++) {
             if (rMgr.managedTrees[cnt].terrainName == _terrainName && rMgr.managedTrees [cnt].treeINDEX == _treeINDEX) {
                 Debug.Log ("Tree has been used recently");
                 beenUsed = true;
             }
         }
 
         return beenUsed;
     }
 
 
     private void HarvestWood() {
         int treeIDX = -1;
         int treeCount = terrain.terrainData.treeInstances.Length;
         float treeDist = harvestTreeDistance;
         Vector3 treePos = new Vector3 (0, 0, 0);
                 
         // Notice we are looping through every terrain tree, 
         // which is a caution against a 15,000 tree terrain
 
         for (int cnt=0; cnt < treeCount; cnt++) {
             Vector3 thisTreePos = Vector3.Scale (terrain.terrainData.treeInstances [cnt].position, terrain.terrainData.size) + terrain.transform.position;
             float thisTreeDist = Vector3.Distance (thisTreePos, hit.point);
 
             if (thisTreeDist < treeDist) {
                 treeIDX = cnt;
                 treeDist = thisTreeDist;
                 treePos = thisTreePos;
             }
         }
 
 
         if (treeIDX == -1) {
             Debug.Log ("Out of Range");
             return;
         }
 
         if(!CheckRecentUsage(terrain.name, treeIDX)) {
 
             // Success - all tests passed
             // Place a cube to show the tree, the ResourceManager will remove it after a time
             // Obviously tweak to your liking, just a visual aid to show it worked
         
             GameObject marker = GameObject.CreatePrimitive (PrimitiveType.Cube);
             marker.transform.position = treePos;
 
             // Example of spawning a placed tree at this location, just for demo purposes
             // it will slide through terrain and disappear in 4 seconds
             GameObject fellTree = Instantiate(felledTree,treePos,Quaternion.identity) as GameObject;
             fellTree.gameObject.AddComponent<Rigidbody>();
 
             Destroy(fellTree,4);
 
             // Add this terrain tree and cube to our Resource Manager for demo purposes
             rMgr.AddTerrainTree(terrain.name, treeIDX, Time.time+respawnTimer, marker.transform);
 
             if (rotatePlayer) {
                 Vector3 lookRot = new Vector3 (hit.point.x, myTransform.position.y, hit.point.z);
                 myTransform.LookAt (lookRot);
             }
         
             // There are too many guesses to be made here about your game mechanics to code the rest
             // For example, you might..
         
             // Start Animation
             // Play Sound Clip
             // Give player an Inventory item
             // Bump lumberjacking skill
             // Random.Range[] spawn a Forest Protector Spirit of Woe
             // etc.
 
         }
     }
 }


Script #2 is attached to a GameManager singleton-like object that is tagged as "GameManager", adapt to your needs.

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 
 // Attach this to a Singleton-like GameObject of which there is only and will ever only be one,
 // ala classic GameManager object.
 
 public class QM_ResourceManager : MonoBehaviour {
 
     // Define List component
     public class QM_Tree {
         public string terrainName { get; set; }
         public int treeINDEX { get; set; }
         public float respawnTime { get; set; }
         public Transform marker { get; set; }
 
     // Constructor
     public QM_Tree(string _terrainName, int _treeINDEX, float _respawnTime, Transform _marker) {
         terrainName = _terrainName;
         treeINDEX = _treeINDEX;
         respawnTime = _respawnTime;
         marker = _marker;
     }
         
     }
 
     // Tree Harvest script access
     public List<QM_Tree> managedTrees = new List<QM_Tree>();
 
     void Start() {
         // Scan for tree to "respawn" (remove cube, make available again) every 15 seconds
         // Adjust to your needs using a fast spawn here for demo
         InvokeRepeating ("RespawnTree", 15, 15);
     }
 
 
     private void RespawnTree() {
 
         if (managedTrees.Count == 0)
             return;
 
         // Removing the demo cube and allowing tree to be used again
         for (int cnt=0; cnt < managedTrees.Count; cnt++) {
             if(managedTrees[cnt].respawnTime < Time.time) {
                 Destroy(managedTrees[cnt].marker.gameObject);
                 managedTrees.RemoveAt(cnt);
                 return;
             }
 
         }
     }
 
 
     public void AddTerrainTree(string _terrainName, int _treeIDX, float _respawnTime, Transform _marker) {
 
         managedTrees.Add (new QM_Tree(_terrainName, _treeIDX, _respawnTime, _marker));
     }
 }

Comment
Add comment · Show 5 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image getyour411 · Feb 26, 2014 at 09:01 AM 0
Share

Additional methods, posts, ideas & approaches

http://answers.unity3d.com/questions/420154/modify-existing-mass-place-trees-function.html

http://forum.unity3d.com/threads/110354-Finally-Removing-trees-AND-the-colliders

http://forum.unity3d.com/threads/28855-Destroyable-Trees

A collider-less solution: http://answers.unity3d.com/questions/685405/mass-place-trees-tagging.html

avatar image getyour411 · Feb 26, 2014 at 09:01 AM 0
Share

Comment #2

avatar image Raptor-Deamon · Mar 03, 2017 at 06:47 PM 0
Share

I have a freeze when i click with this script, can you help me ?

I have put Q$$anonymous$$_HarvestTerrainTree.cs in a empty game object, and Q$$anonymous$$_HarvestTerrainTree to my firstpersoncontroller.

I think it's because their is lot of tree in my maps, but if you have another idea ?

avatar image NeeDLeSs · Dec 23, 2018 at 07:47 AM 0
Share

I have a problem. The tree i've harvested doesn't disappear, the fell tree just spawnes and get pushed to the side because of the original tree's collider.

avatar image Balddog123 · Jun 15, 2019 at 06:22 PM 0
Share

For Q$$anonymous$$_HarvestTerrainTree,

On line 151 it states: Vector3 thisTreePos = Vector3.Scale(terrain.terrainData.treeInstances[cnt].position, terrain.terrainData.size) + terrain.transform.position;

Change this to: Vector3 thisTreePos = Vector3.Scale(terrain.terrainData.GetTreeInstance(cnt).position, terrain.terrainData.size) + terrain.transform.position;

using the array "treeInstances" is costly to performance and causes a pause mid game. using "GetTreeInstance()" is seemless. I used this with 10,000 trees and there was no issue!

avatar image
0

Answer by JayCrossler · Sep 08, 2017 at 03:00 AM

Is there a way to remove that tree (treeidx) from rendering? Or, because it's static, is it always going to be in the scene?

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

29 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Distribute terrain in zones 3 Answers

Saving Loading Terrain trees 0 Answers

Multiple Cars not working 1 Answer

RTS game Any help about Hide mesh or destroy trees Terrain? 1 Answer

How to make toon shader work with terrain place trees? 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges