Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 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 /
  • Help Room /
avatar image
0
Question by tiny-ant · Mar 13, 2016 at 09:45 AM · getcomponentmaterialsframerateisometric

Inexplicable loss of frame rate

Hi there,

I have a weird problem. I'm startig to work on an isometric game with a tile-based, changeable map. Each tile has it's own renderer and for material I use an atlas, so i can switch for example from grass to road by changing the offset of the material. That all works fine. It even runs pretty solid, when I zoom all the way out so I have thousands of tiles on screen at once. The weird thing is: When I call a function, which makes changes to every tile the framerate drops dramatically. And I am not talking about the frame in wich the changes are made. That I would understand. It drops and stays down, even if i do not really change anything at all. For Example: I start the game. Zoom all the way out. Framerate is 40. Each Tile has a Material Offset of (0.25, 0.5). I call a function which sets all Material Offsets for testing purpose to (0.25, 0.5), so it doesn't really change anything, but the framerate drops to 5 and stays there, as if the function was called every frame, but it's not.

I have an Input_script...

 function Update() {
     if (Input.GetKeyDown(KeyCode.C)) {
         SolidColors();
     }
 }
 
 function SolidColors() {
     for (var TmpParcel: ParcelManager_script in GameObject.FindObjectsOfType(ParcelManager_script)) {
         TmpParcel.ApplySolidColor();
     }
 }

And here is the function I call in each "parcel", wich manages 9 by 9 tiles:

 function ApplySolidColor() {
     var TmpTile: GameObject;
     for (var t: int = 1; t < 82; t++) {
         TmpTile = transform.GetChild(t).gameObject;
 TmpTile.GetComponent(MeshRenderer).material.mainTextureOffset = Vector2(0.25, 0.5);
     }
 }

This is the first time I write a question, because I wouldn't even know what to search for. I just don't understand. To be clear: This does a lot in the frame it is called in, so I expect a drop in framerate, but this kills the framerate permanently. What am I missing?

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

3 Replies

· Add your reply
  • Sort: 
avatar image
0

Answer by troien · Mar 13, 2016 at 10:27 AM

As to the why this is I know the answer, as to how to fix it properly I still don't know :(

Calling Renderer.material creates a copy of the material and uses the copy instead. If you open the frame debugger window, you'll probably see a huge difference at the moment you set the materials, as instead of all using the same material, from then on they use all seperate materials and they don't get batched anymore.

If you want to change all the tiles in the same way, the fix is easy, as you only need to call the same thing with the Renderer.sharedMaterial for ONLY ONE of your renderers (sinse otherwise you would be setting the same material a lot of times) or simply by referencing the material through a public variable in your script and change that one. Once you update that one material, all renderers will render with the updated material. NOTE however that updating a sharedmaterial from code also updates your material in the project folder as it is the same material, changes to it in playmode persist outside of playmode. To fix that you could during initialization create a copy of the material in code, and use that one copy by setting all renderer's sharedMaterial to be that copy, that way the original stays unaffected.

If you want to make individual changes to each material, then I don't know what you should do, you could edit the uv of the meshes itself, as having 2 different meshes doesn't break batching as long as they use the same material (and they can sinse changing the uv changes the visible texture anyway). But then you'll perhaps sacrifice a lot of ram on meshes just for uv's...

I once read somewhere while looking into this that you could use MaterialPropertyBlocks for that, but I never got that to work in a way that it actually improved my framerate so I'm not sure whether these things actually work for that purpose or that I just did something wrong...

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

Answer by Riaan-Walters · Mar 13, 2016 at 03:32 PM

A Couple of quick wins on performance would be to get rid of all the API calls you do

'FindObjectsOfType', 'GetComponent', MeshRenderer.material, transform.GetChild, transform.gameObject

These are the things i see at a glance which can all be replaced by a field holding a reference so you dont do that many API Calls.

for example in a single 'ApplySolidColor' you do 5*82 API calls, all just to get to the child's material.

i suggest re-writing the ParcelManager_script so it caches the children's material's once (awake) if possible, it sounds trivial, and to some extent it is, but the performance gain is substantial

Comment
Add comment · Show 1 · 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 Bunny83 · Mar 15, 2016 at 04:35 AM 0
Share

Are you sure you read the question carefully? All he does in that script is only executed once when he presses the key "C". That code isn't executed every frame.

From the question:

To be clear: This does a lot in the frame it is called in, so I expect a drop in framerate, but this kills the framerate permanently

Also it's true that some method can be quite heavy, but when used in the editor or in a PC build you can still use several of those without any problems. Also there are huge differences between the methods you've mentioned. $$anonymous$$eshRenderer.material, transform.GetChild and transform.gameObject are neglectable as they just return an already internally cached value. $$anonymous$$eshRenderer.material might create a duplicate of the material if the renderer doesn't have it's own instance yet, but that's a one time event. GetComponent is a bit heavier but you can still do several hundreds per frame without any trouble. FindObjectsOfType is a magnitude slower than GetComponent, but it also depends on the actual project. If there are many objects loaded(scene objects as well as assets) it's quite slow. If there are only a few it's pretty fast.

avatar image
0

Answer by Bunny83 · Mar 13, 2016 at 02:29 PM

Like @troien explained you basically destroy the advantage of using an atlas by manipulating the material of each tile. The material offset is a global shader property. So when changing that you have to use seperate materials. When using seperate materials they can't be batched since they don't use the same material.

The solution is: Don't change the material offset. You have to change the mesh itself. More precisely the UV coordinates of the mesh. The key is that the texture information has to be part of the vertex data which comes into the shader and not some global shader constant. So each tile is rendered with the same material but each tile can map to any tile in your atlas. The atlas material shouldn't have any scale or offset in the first place. So a quad usually maps the the whole texture with UV coordinates like this:

 (0,0)
 (1,0)
 (1,1)
 (0,1)

Now you have to set UV coordinates of the tile you actually want. I guess your atlas is an evenly tiled 4x4 texture? (so 16 different tile textures in the atlas?)

If you think about the Uv coords as a Rect you can simply say:

 public static Rect GetUVAtlasCoord(int aXTile, int aYTile, int aIndex)
 {
     int x = aIndex % aXTile;
     int y = aIndex / aXTile;
     float xSize = 1f / aXTile;
     float ySize = 1f / aYTile;
     return new Rect(xSize * x, ySize*y, xSize, ySize);
 }

This will calculate the UV coordinates of an evenly tiled atlas. You pass the tile count in each direction and a simple texture index in the range of (0 to (aXTile*aYTile-1)) so in the case of 4x4 it's (0 - 15)

The returned Rect can simply be used to set the UV coordinates of a quad. Just use xMin, yMin and xMax, yMax. Keep in mind that Uv coordinates usually start at the bottom left of the texture.

Comment
Add comment · Show 1 · 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 tiny-ant · Apr 21, 2016 at 06:25 PM 0
Share

Thank You!

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

6 People are following this question.

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

Related Questions

How do I change the color of a material in mesh render? 0 Answers

Problem to read an array from another script but on the same object 1 Answer

Unity3d Script does not work anymore 1 Answer

How dose getcomponentwork ? 0 Answers

NullReferenceException: Object Reference not set to an instance of an object 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