Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 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
0
Question by GambinoInd · Jul 23, 2013 at 01:59 AM · performance

Improving Performance of Map Loading

My project will load map chunks from the resources, and only load the map chunks nearby. This works perfectly. The issue is right now I'm adding pathfinding to my project and I'm using raycasting to determine if an area is blocked by an object and is walkable.

This cycles through the entire map and sends out raycasts to determine unwalkable areas, and the Y points of each height level's floor.

 private ArrayList clipMap = new ArrayList ();
 private ArrayList heightMap = new ArrayList ();
 private int squareCount = (int)(Config.MAP_SIZE / Config.CLIP_MAP_SQUARE_SIZE);
 private    Color[] colors = new Color[] {Color.red, Color.blue, Color.green, Color.yellow, Color.cyan};
 
 public void BuildClipMap (float baseX, float baseZ, int HighestFloor)
 {
     for (int i = 0; i < HighestFloor; i++) {            
         int layerMask = (1 << Config.OBJECT_MAP_LAYER + i);
         for (int x2 = 0; x2 < squareCount; x2+= 1) {
             for (int z2 = 0; z2 < squareCount; z2+= 1) {
                 float x = x2 * Config.CLIP_MAP_SQUARE_SIZE;
                 float z = z2 * Config.CLIP_MAP_SQUARE_SIZE;
                 RaycastHit hit = new RaycastHit ();
                 if (Physics.Linecast (new Vector3 (x + baseX + (Config.CLIP_MAP_SQUARE_SIZE / 2), 0, z + baseZ + (Config.CLIP_MAP_SQUARE_SIZE / 2)), 
                     new Vector3 (x + baseX + (Config.CLIP_MAP_SQUARE_SIZE / 2), Config.MAP_HEIGHT + 100, z + baseZ + (Config.CLIP_MAP_SQUARE_SIZE / 2)), out hit, layerMask)) {
                     if (hit.collider != null) {
                         clipMap.Add (new ClipMapEntry (i, Mathf.FloorToInt (x / Config.CLIP_MAP_SQUARE_SIZE), Mathf.FloorToInt (z / Config.CLIP_MAP_SQUARE_SIZE)));
                         Debug.DrawLine (new Vector3 (x + baseX + (Config.CLIP_MAP_SQUARE_SIZE / 2), hit.point.y, z + baseZ + (Config.CLIP_MAP_SQUARE_SIZE / 2)), new Vector3 (x + baseX + (Config.CLIP_MAP_SQUARE_SIZE / 2), hit.collider.bounds.size.y + hit.point.y, z + baseZ + (Config.CLIP_MAP_SQUARE_SIZE / 2)), colors.Length > i ? colors [i] : Color.gray, 120.0f);
                     }
                 }                    
             }
         }
     }
 }
 
 public void BuildHeightMap (float baseX, float baseZ, int HighestFloor)
 {
     for (int i = 0; i < HighestFloor; i++) {
         int layerMask = (1 << Config.FLOOR_MAP_LAYER + i);
         for (int x2 = 0; x2 < squareCount; x2+= 1) {
             for (int z2 = 0; z2 < squareCount; z2+= 1) {
                 float x = x2 * Config.CLIP_MAP_SQUARE_SIZE;
                 float z = z2 * Config.CLIP_MAP_SQUARE_SIZE;
                 RaycastHit hit2 = new RaycastHit ();
                 if (Physics.Linecast (new Vector3 (x + baseX + (Config.CLIP_MAP_SQUARE_SIZE / 2), Config.MAP_HEIGHT + 100, z + baseZ + (Config.CLIP_MAP_SQUARE_SIZE / 2)), 
                     new Vector3 (x + baseX + (Config.CLIP_MAP_SQUARE_SIZE / 2), 0, z + baseZ + (Config.CLIP_MAP_SQUARE_SIZE / 2)), out hit2, layerMask)) {
                     if (hit2.collider != null) {
                         heightMap.Add (new HeightMapEntry (i, Mathf.FloorToInt (x / Config.CLIP_MAP_SQUARE_SIZE), Mathf.FloorToInt (z / Config.CLIP_MAP_SQUARE_SIZE), hit2.point.y));
                         Debug.DrawLine (new Vector3 (x + baseX + (Config.CLIP_MAP_SQUARE_SIZE / 2), hit2.point.y, z + baseZ + (Config.CLIP_MAP_SQUARE_SIZE / 2)), new Vector3 (x + baseX + (Config.CLIP_MAP_SQUARE_SIZE / 2), hit2.point.y + 0.15f, z + baseZ + (Config.CLIP_MAP_SQUARE_SIZE / 2)), Color.white, 120.0f);
                     }
                 }
             }
         }
     }
 }

I can set the CLIP_MAP_SQUARE_SIZE to be bigger or smaller (bigger = faster, but much less exact). I want it to be 0.5, but calling this method on each map chunk (250x250) takes about 2000MS at 0.5, and 500MS at 1.0.

I need to reduce that time by a lot, and have 0.5 load at around 200MS Max. Does anyone have any ideas? I'd really appreciate it.

This is what these methods do (visually with debug): alt text

Comment
Add comment · Show 24
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 Benproductions1 · Jul 23, 2013 at 02:04 AM 0
Share

There is no way you will be able to make this much faster than it already is. You will ins$$anonymous$$d need to find other methods to do your pathfinding

avatar image GambinoInd · Jul 23, 2013 at 02:24 AM 0
Share

I actually got it down to about 200$$anonymous$$S by reducing the CLIP_$$anonymous$$AP_SQUARE_SIZE to 2.0 ins$$anonymous$$d of 0.5. I was noticing how many nodes were there under my character and it was completely unecessary. Now ins$$anonymous$$d of firing 250,000 raycasts it sends out about 16,000. Still, I would love it faster :/

avatar image Benproductions1 · Jul 23, 2013 at 02:42 AM 0
Share

The more accurate, the slower it will be. You need a different method all together if you want proper performance improvements :)

avatar image GambinoInd · Jul 23, 2013 at 04:54 AM 0
Share

Can you think of any other methods?

avatar image Benproductions1 · Jul 23, 2013 at 06:43 AM 1
Share

If you're loading map chunks, that I'm guessing are not too small, you counld just pre-calculate the grid and save it alongside the chunk.

Show more comments

1 Reply

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

Answer by GambinoInd · Jul 24, 2013 at 06:59 AM

After optimizing this like crazy by using Lists instead of ArrayLists, and also pre-computing the data the speed was relatively the same.

But, due to precomputing the data, loading the data used none of Unity's functions and could be run using a seperate thread. I started using seperate threads per map chunk, and this caused my entire computer to lag as it was using my entire processor. To fix this I isolated it to just one seperate thread, and it's running flawlessly now at 9 Milliseconds per map chunk, instead of 500 Milliseconds per map chunk earlier.

Thanks to all who helped me!!

I wrote my own secondary thread handler if anybody wishes to use it:

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 using System.Threading;
 
 public class SecondaryThread {
     
     public delegate void Action();
     private static Thread secondThread;
     private static bool stopThread;
     private static List<Action> functionList = new List<Action>();
 
     public static void addTask(Action method) {
             functionList.Add (method);
             if(secondThread == null) {
                  secondThread = new Thread(new ThreadStart(loop));
                 secondThread.Start ();
             }
     }
     
     public static void close() {
         if(secondThread == null)
             return;
         stopThread = true;
         secondThread.Interrupt();    
         secondThread.Abort();
     }
     
     private static void loop() {
         while (stopThread == false) {
                 for(int i = 0; i < functionList.Count; i++) {
                     Action entry = (Action) functionList[i];
                     entry();
                     functionList.RemoveAt(i);
                 }
         }
     }    
 }

Use it like this:

 private void methodName() {
   doStuff();
 }
 
 SecondaryThread.addTask(methodName);
Comment
Add comment · Show 11 · 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 Tarlius · Jul 24, 2013 at 07:29 AM 0
Share

I assume you mean you could do the pre-loading in 9ms then leave the heavy stuff going in the background to finish whenever and not that putting it in another thread made the actual algorithm faster?

I'm still pretty shocked how slow importing the data was though... Care to share the algorithm? $$anonymous$$aybe we can improve it

avatar image GambinoInd · Jul 24, 2013 at 07:33 AM 0
Share

Yes, Pre-loading. It's a 9$$anonymous$$S freeze on the main thread.

I'm sure we can, and I really appreciate it. It's still a bit messy, but it's much better than before:

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 using System.IO;
 
 public class Clip$$anonymous$$ap
 {
     
     private List<string> clip$$anonymous$$ap = new List<string> ();
     private List<string> height$$anonymous$$ap = new List<string> ();
     private int regionX, regionY;
     
     
     private void ThreadAction ()
     {
         string path1 = "Assets/Resources/" + Config.HEIGHT_$$anonymous$$AP_DIRECTORY;
         string fileName1 = path1 + regionX + "." + regionY + ".txt";
         string heightText = File.ReadAllText(fileName1);
         if (heightText != null) {
             string[] text = heightText.Split ('-');
             for (int i = 0; i < text.Length; i++) {
                 string[] subText = text [i].Split (',');
                 if (subText.Length == 4) {
                     height$$anonymous$$ap.Add (int.Parse (subText [0]) + "," + int.Parse (subText [1]) + "," + int.Parse (subText [2]) + "," + float.Parse (subText [3]));
                 }
             } 
         }
         string path2 = "Assets/Resources/" + Config.CLIP_$$anonymous$$AP_DIRECTORY;
         string fileName2 = path2 + regionX + "." + regionY + ".txt";
         string clipText = File.ReadAllText(fileName2);
         if (clipText != null) {
             string[] text = clipText.Split ('-');
             for (int i = 0; i < text.Length; i++) {
                 string[] subText = text [i].Split (',');
                 if (subText.Length == 3) {
                     clip$$anonymous$$ap.Add (int.Parse (subText [0]) + "," + int.Parse (subText [1]) + "," + int.Parse (subText [2]));    
                 }
             }
         }
     }
     
     public Clip$$anonymous$$ap (int regionX_, int regionY_)
     {
         regionX = regionX_;
         regionY = regionY_;
         SecondaryThread.addTask (this.ThreadAction);        
     }
     
     public List<string> getClip$$anonymous$$ap ()
     {
         return clip$$anonymous$$ap;    
     }
     
     public List<string> getHeight$$anonymous$$ap ()
     {
         return height$$anonymous$$ap;    
     }
     
 }
 
avatar image Tarlius · Jul 24, 2013 at 09:42 AM 0
Share

Is there a reason you don't use a binary format? It'd be far faster to parse. Do you need it in a human-readable format? Without considering the overheads from the splits, the difference in file size alone would result in about 75% less reading.

If you do have a strong reason for keeping it human readable, you could perhaps use \n as the separator between entries, and read the file with File.ReadAllLines or iterate over File.ReadLines. This would save you the huge Split() at least, although without profiling it I can't be sure just how much difference it would make...

avatar image GambinoInd · Jul 24, 2013 at 09:47 AM 0
Share

I actually just updated it to Binary Format. And that's a great idea, splitting it with \n. Split is such a bother sometimes, and it looks messy as well. Thank you very much!

avatar image chris_taylor · Jul 24, 2013 at 09:47 AM 0
Share

it might be better to use ThreadPool.queueuserworkitem ins$$anonymous$$d of your SecondaryThread.addTask;

 ThreadPool.queueuserworkitem(() => DoSomething(5));
 
 void DoSomething(int num){
     Debug.log(num);
 }
Show more comments

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

20 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

Related Questions

How do I get my game to run faster? 4 Answers

performance optimization 5 Answers

Mesh collider cost 1 Answer

Advice about overdraw 1 Answer

Memory Managmenet 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