• 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 /
  • Help Room /
avatar image
1
Question by KillHour · Nov 05, 2015 at 06:36 PM · 3doptimizationraycastinglayermaskfog-of-war

Optimizing fog of war in 3D environment

So after spending a while thinking about how to do proper fog of war in a top down game with an arbitrary number of levels, I decided to implement the obvious solution (raycasting). I expected this to give me unacceptable performance, and it does (big surprise). It's not as terrible as I assumed it would be, but it still takes between 50-60ms on my machine (i5-3570k @ stock). The big killer here is finding the game objects within the camera frustum to test against. The actual raycasting only takes ~5ms, which, while not great, I'm okay with. Here's the code I ended up using:

 void UpdateGeometryLayers ()
 {

     GameObject[] allObjects = GameObject.FindGameObjectsWithTag ("SpawnedObject");
     Plane[] frustumPlanes = GeometryUtility.CalculateFrustumPlanes (camera);
     bool isViable;
     bool isVisible;

     if (!liveLayerData) {
         foreach (GameObject go in allObjects) {
             for (int l = 0; l < go.transform.GetChild (0).childCount; l++) {
                 go.transform.GetChild (0).transform.GetChild (l).gameObject.layer = 10;
             }
         }
     }

     foreach (GameObject go in allObjects) {
         isViable = false;
         isVisible = false;
         for (int l = 0; l < go.transform.GetChild (0).childCount; l++) {
             GameObject subObject = go.transform.GetChild (0).transform.GetChild (l).gameObject;
             if (GeometryUtility.TestPlanesAABB (frustumPlanes, subObject.GetComponent<MeshRenderer> ().bounds))
                 isViable = true;
         }
         if (isViable) {
             Collider[] colliders = go.GetComponentsInChildren<Collider> ();
             Collider closestCollider = colliders [0];

             foreach (Collider collider in colliders) {
                 RaycastHit hit;
                 int layers = 0;
                 for (int i = 12; i >= 0; i--) {
                     layers |= (1 << i);
                 }

                 Vector3 closestPoint = collider.ClosestPointOnBounds (head.transform.position);
                 Vector3 direction = closestPoint - head.transform.position;

                 //Debug.DrawRay (head.transform.position, direction, Color.blue);

                 if (Physics.Raycast (head.transform.position, direction, out hit, visionDistance, layers)) {
                     Bounds hitLocation = new Bounds (hit.point, new Vector3 (0.1F, 0.1F, 0.1F));
                     if (hitLocation.Contains (closestPoint))
                         isVisible = true;
                 }

                 //Increases accuracy, esp for stairs, at a large performance cost
                 if (multipassLayerCalc) {
                     Vector3 highestPointA = new Vector3 (collider.bounds.max.x, collider.bounds.max.y, collider.bounds.max.z);
                     direction = highestPointA - head.transform.position;
                     if (Physics.Raycast (head.transform.position, direction, out hit, visionDistance, layers)) {
                         Bounds hitLocation = new Bounds (hit.point, new Vector3 (0.1F, 0.1F, 0.1F));
                         if (hitLocation.Contains (highestPointA))
                             isVisible = true;
                     }

                     Vector3 highestPointB = new Vector3 (collider.bounds.min.x, collider.bounds.max.y, collider.bounds.min.z);
                     direction = highestPointB - head.transform.position;
                     if (Physics.Raycast (head.transform.position, direction, out hit, visionDistance, layers)) {
                         Bounds hitLocation = new Bounds (hit.point, new Vector3 (0.1F, 0.1F, 0.1F));
                         if (hitLocation.Contains (highestPointB))
                             isVisible = true;
                     }
                     Vector3 highestPointC = new Vector3 (collider.bounds.max.x, collider.bounds.max.y, collider.bounds.min.z);
                     direction = highestPointC - head.transform.position;
                     if (Physics.Raycast (head.transform.position, direction, out hit, visionDistance, layers)) {
                         Bounds hitLocation = new Bounds (hit.point, new Vector3 (0.1F, 0.1F, 0.1F));
                         if (hitLocation.Contains (highestPointB))
                             isVisible = true;
                     }

                     Vector3 highestPointD = new Vector3 (collider.bounds.min.x, collider.bounds.max.y, collider.bounds.max.z);
                     direction = highestPointD - head.transform.position;
                     if (Physics.Raycast (head.transform.position, direction, out hit, visionDistance, layers)) {
                         Bounds hitLocation = new Bounds (hit.point, new Vector3 (0.1F, 0.1F, 0.1F));
                         if (hitLocation.Contains (highestPointB))
                             isVisible = true;
                     }
                 }
             }
             if (isVisible) {
                 if ((go.transform.position.y < head.transform.position.y))
                     for (int l = 0; l < go.transform.GetChild (0).childCount; l++)
                         go.transform.GetChild (0).transform.GetChild (l).gameObject.layer = 8; //Visible
                 else
                     for (int l = 0; l < go.transform.GetChild (0).childCount; l++)
                         go.transform.GetChild (0).transform.GetChild (l).gameObject.layer = 11; //Culled Known
             } else {
                 if ((go.transform.position.y < head.transform.position.y))
                     for (int l = 0; l < go.transform.GetChild (0).childCount; l++) {
                         if (go.transform.GetChild (0).transform.GetChild (l).gameObject.layer == 8 ||
                             go.transform.GetChild (0).transform.GetChild (l).gameObject.layer == 9 ||
                             go.transform.GetChild (0).transform.GetChild (l).gameObject.layer == 11)
                             go.transform.GetChild (0).transform.GetChild (l).gameObject.layer = 9; //Known
                     }
                 else
                     for (int l = 0; l < go.transform.GetChild (0).childCount; l++) {
                         if (go.transform.GetChild (0).transform.GetChild (l).gameObject.layer == 8 ||
                             go.transform.GetChild (0).transform.GetChild (l).gameObject.layer == 9 ||
                             go.transform.GetChild (0).transform.GetChild (l).gameObject.layer == 11)
                             go.transform.GetChild (0).transform.GetChild (l).gameObject.layer = 11; //Culled Known
                     }
             }
         }
     }
     liveLayerData = true;
     bakedLayerData = false;
 }

And a screenshot to show what it looks like in practice:

fog of war

You can see the geometry currently visible is in color, the the geometry not visible is in black and white without shadows, and the unknown geometry is invisible.

There has to be a better way to do this than literally checking every object in the scene every frame. If I can get the total update time down to < 10ms, I can do an update every 100ms or so, and that would probably be okay.

fog-of-war.png (201.4 kB)
Comment
Add comment
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

0 Replies

· Add your reply
  • Sort: 

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.

Welcome to Unity Answers

The best place to ask and answer questions about development with Unity.

To help users navigate the site we have posted a site navigation guide.

If you are a new user to Unity Answers, check out our FAQ for more information.

Make sure to check out our Knowledge Base for commonly asked Unity questions.

If you are a moderator, see our Moderator Guidelines page.

We are making improvements to UA, see the list of changes.



Follow this Question

Answers Answers and Comments

33 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 avatar image avatar image avatar image avatar image

Related Questions

My ray pointing into infinity (I suppose) 0 Answers

Accessing Raycast collider from another script 0 Answers

LayerMask Not working when raycasting to Specific layer. 0 Answers

Raycast pointing in the wrong direction 0 Answers

Raycasting keeps getting blocked by objects on a layer it should be ignoring,Raycast blocked by layer it shouldnt be blocked by 0 Answers


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