• 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

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

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

LayerMask Not working when raycasting to Specific layer. 0 Answers

Too Low Amount of Tris in Statistics Window, Only in the Tens 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

Shooting was slow and dont know whats the best way to add sound and muzzle flash so no delay occur 0 Answers

Raycast pointing in the wrong direction 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