Search Unity

Suggestions for optimizing draw calls in billiard game

Discussion in 'iOS and tvOS' started by bigTimeOperator, May 5, 2009.

  1. bigTimeOperator

    bigTimeOperator

    Joined:
    Nov 16, 2007
    Posts:
    24
    As a learning exercise I made a simple billiards game with a pool table, 16 balls, a single light, a camera and a gui button to switch between shooting (flicking with finger) or orbiting the camera. Each Billiard ball has it's own texture and the pool table has a single texture.

    I'd been testing on a 2nd gen ipod touch and the performance was reasonable, however it runs a big sluggish on my first gen iphone when all the balls are visible. From reading other posts about iphone optimization I realize that the draw calls are a big factor and that generally anything beyond 30 draw calls starts to slow things down.

    I am currently getting 36 draw calls which I believe makes sense with 18 objects and a single camera and a light. Is there a reasonable way to reduce the number draw calls? What would you do? I can't really combine all the balls into a single mesh as they must dynamically react with one another, right?

    Also, what sort of performance increase, if any, can I expect by creating a single texture that is used for all the billiard balls? (I believe this would be called an atlas?)
     
  2. kenlem

    kenlem

    Joined:
    Oct 16, 2008
    Posts:
    1,630
    I have a similar problem with a game I have. It's not billiards but does have balls on the screen.

    Using a texture atlas won't help unless all those objects are combined into one. Each one would still be a draw call

    You could get rid of the light and your draw calls would be reduced by half. Of course, that sort of ruins the 3D look of the balls.

    I wonder if you could use a shader to render the texture on the ball then use the other UV coords to render the lighting highlights?
     
  3. MikaMobile

    MikaMobile

    Joined:
    Jan 29, 2009
    Posts:
    845
    You could merge all of your balls into a single mesh, with a single material, and skin this collection of merged balls to a series of 18 seperate bones in your 3d app. Then in Unity, you could attach to each bone whatever colliders, scripts, etc. that you're currently using on your seperate objects and it should work exactly the same, except it would be one skinned mesh instead of 18 seperate meshes. In most cases this should be substantially faster than the effect of 18 seperate, lit meshes, unless your balls have a very high vertex count. Skinned meshes are not "free", and take longer to computer the more verts they are manipulating. I suspect in your case it will be a significant gain in performance though.
     
  4. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Especially in Unity 1.1 where mesh skinning will apparently be much faster, I expect this technique will be even more effective.

    --Eric
     
  5. etoiles

    etoiles

    Joined:
    May 13, 2008
    Posts:
    216
    Depending how realistic your game has to be, you could just use solid colors for the balls and draw them as sprites...
     
  6. antenna-tree

    antenna-tree

    Joined:
    Oct 30, 2005
    Posts:
    5,324
    This is a common misconception. In the Editor if you do not set your light to "Force Vertex", or better yet set the graphics emulation to iPhone, then it will default to pixel lit which takes two draw calls. Vertex lighting only requires one draw call on the iPhone and vertex lighting is all the device can do. Even then though it still hits performance so if you can figure out how to do this with zero lights then do it.

    You can bake all the static geometry lighting with lightmaps or bake the lighting right into the vertices (I got decent results doing a combination of this for the warehouse demo).

    I think the best way to go for the balls themselves is the sprite technique. If you could animate the UVs of each sprite to show the effect of rolling it could be pretty convincing. Also putting a AO shadow decal under each ball would help sell the illusion.

    If performance wasn't a concern (yeah ok ;)) the most realistic way to go for the balls is to use a spheremap for reflection to simulate the lighting of the billiard room/table. Here's a shader that will do that... 2 draw calls for this shader, but with 16 balls (32), a table (1), a cue stick (1), and some GUI (1-4) you could keep it under 40 draw calls.

    Code (csharp):
    1. Shader "iPhone/reflective"
    2. {
    3.     Properties
    4.     {
    5.         _Color ("Main Color", Color) = (1,1,1,1)
    6.         _MainTex ("Base (RGB)", 2D) = "white" {}
    7.         _Reflect ("Reflection", 2D) = "black" { TexGen SphereMap }
    8.     }
    9.  
    10.     SubShader
    11.     {  
    12.         Pass
    13.         {
    14.             Name "REFLECT"
    15.             ZWrite Off
    16.             Blend SrcAlpha OneMinusSrcAlpha
    17.            
    18.             BindChannels {
    19.                 Bind "Vertex", vertex
    20.                 Bind "normal", normal
    21.                 Bind "texcoord", texcoord0 // main uses 1st uv
    22.             }
    23.                        
    24.             SetTexture [_MainTex] {
    25.                 combine texture
    26.             }
    27.             SetTexture [_Reflect] {
    28.                 combine texture, previous
    29.             }
    30.         }
    31.        
    32.     }
    33. }
    [Edit] Oops, I left the lightmap pass in this shader and wasn't thinking properly... this shader only requires one pass.
     
  7. bliprob

    bliprob

    Joined:
    May 13, 2007
    Posts:
    901
    You've mentioned this technique before, but I can't visualize it. How do you have 18 spheres in one contiguous mesh? (Screenshot, please?)

    And I take it each ball has a bone, but none of the bones are connected, so they're free to move anywhere?
     
  8. MikaMobile

    MikaMobile

    Joined:
    Jan 29, 2009
    Posts:
    845
    Yeah, people have a hard time coming to grips with the fact that a single mesh does not have to be contiguous.

    In maya for instance, I can make two poly spheres that are not touching each other at all, and then combine them into a single mesh. Even though the two objects share no vertices, maya (and unity) now consider them a single mesh, and the two objects can no longer be moved independently without actually manipulating the mesh itself (i.e. selecting the polygons and moving them). At this point you just have one transform, and one mesh that are simply shaped like two spheres.

    Now, the other thing that people have a hard time with is that bones are not just for characters or things with arms and legs - they're essentially just a tool for parenting the vertices of a single mesh to multiple transforms. There is no difference between a character's leg, vs. a collection of billiard balls that have all been merged together. Both represent a series of transforms (bones) that control different vertices of a single mesh - the leg just happens to have the bones parented to each other in a chain, and the mesh happens to be contiguous, but this is by no means a requirement for a skinned mesh. You could just as easily have 18 bones that aren't parented to each other at all, that each move a piece of a mesh that isn't connected physically to any other piece of that mesh.

    The end result is that you can position and script and add physics to these bones just like you would any game object in Unity, while keeping your draw calls super low. I've been using this technique for all of my Unity iPhone projects, and its been working out nicely.
     
  9. bigTimeOperator

    bigTimeOperator

    Joined:
    Nov 16, 2007
    Posts:
    24
    Thanks so much for the tips. So much to learn!
     
  10. Wadoman

    Wadoman

    Joined:
    Dec 8, 2007
    Posts:
    336
    Great Thread!

    @PirateNinja - I am curios on this method of combined one skinned mesh with bones. I am planning on using this method for my GUI, which I believe is what you did. Now your GUI had animation on it (e.g. push a button and it scales up, damage meter goes down). Now is this done in unity via script or animated in maya. This is the only area I am not clear on with this method.
     
  11. MikaMobile

    MikaMobile

    Joined:
    Jan 29, 2009
    Posts:
    845
    My GUI animation was all done through script, but you could use animation clips too. Generally speaking, I use maya animation for anything remotely complicated where I want very direct control (like my opening title screen/menu), and I'll use scripted animation when its something super simple that I just want to toss together (i.e. lerping transforms)
     
  12. Wadoman

    Wadoman

    Joined:
    Dec 8, 2007
    Posts:
    336
    Perfect! Thanks Pirate
     
  13. Dark-Table

    Dark-Table

    Joined:
    Nov 25, 2008
    Posts:
    315
    I just figured out how to do skinned meshes in Cheetah3D, so I decided to give PirateNinja's idea a try.

    Totally freakin works! I got 16 balls, the table, and an FPS counter running in 3 drawcalls. Runs at a fairly steady 30 fps too! I thought it would be harder to access the bones in the skinned mesh, but it's just an array of transforms.

    I attached a sample where the table tilts with the accelerometer and the balls roll around.
     

    Attached Files:

  14. DougMcFarlane

    DougMcFarlane

    Joined:
    Apr 25, 2009
    Posts:
    197
    I'm very new to 3D and Unity. (2 weeks!)
    Pirate's method of using bones sounds interesting.

    However, for quick level design, I define my levels via a text file, that has statements defining the location, size etc of all the game objects. Such as 'place a coin at position x,y,z', 'place a cube at position x, y, z, size x, y, z'.
    I have this working fine, and dynamically arrange the level by instantiating objects as needed and placing/sizing them where they should be.

    I want this on the iPhone, so am I to understand that each instantiated object will require an extra draw call? If so, then I'm restricted to 30ish objects to be in view at once?

    If I have a coin object from Blender (with one bone), can I dynamically duplicate the object at runtime (not instantiate it) into the same mesh, so now the coin mesh now has two coins and two bones? I assume by adding more entries into the vertice array somehow? Then keep doing this for each coin in the text file. Now for example, the original coin mesh now has 40 coins/40 bones. So the end result being one draw call per unique object? (Can I then later destroy/hide a coin/bone when collected?)

    Is this the best way? Or is what I'm trying to do would be too difficult?

    And will the upcoming iPhone 1.2 release that combines objects with the same material into one draw call fix this issue (so my current method would work fine)?

    Thanks for any advice.
     
  15. Dark-Table

    Dark-Table

    Joined:
    Nov 25, 2008
    Posts:
    315
    Each instantiated object would currently require an extra draw call. I believe that 1.1 would reduce the impact of this as long as the objects shared the same material (like the coins would).

    Currently with static objects you could use the "Combine Children" script to combine them into one draw call at runtime.

    For dynamic objects you could write something that dynamically modified a mesh and added and removed bones from the mesh so that you could add/remove coins. Or you could decide that no level would ever have more than 32 coins and just build a mesh with 32 bones in it and hide any unused coins by scaling them down and putting them off screen.

    The dynamic bone/mesh script would be an interesting intellectual challenge, but since 1.1 is visible on the horizon, I don't know that it would be worth the time.

    Also, for simple dynamic objects like coins you could use SpriteManager to aggregate them into one draw call. That's what I did for the game I'm working on. SpriteManager sprites don't have to be camera-facing.

    p.s. Building levels using a text editor is a little nutty when you have Unity's fantastic WYSIWYG level editor. But if it works...
     
  16. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Not really. Using text files allows you to have many levels without having to add a separate scene for each one, as well as many other benefits of storing levels in a small dynamic format like that, such as being able to easily share levels via the internet and so on.

    --Eric
     
  17. Dark-Table

    Dark-Table

    Joined:
    Nov 25, 2008
    Posts:
    315
    Yeah, storing levels in a text file is one thing, but editing and iterating them via text file seems tedious.
     
  18. DougMcFarlane

    DougMcFarlane

    Joined:
    Apr 25, 2009
    Posts:
    197
    Thanks for the tips.

    I found another thread from a person creating a random level (very similar concept):
    http://forum.unity3d.com/viewtopic.php?t=19830&highlight=draw+calls

    Like you suggested, the advice (from Eric5h5) was to attach the CombineChildren script to an empty game object, add the coins (my objects) and parent them to this game object, then active it. Will have to try that this weekend!

    Or wait until 1.1 (oops I said 1.2, confused by 1.02!), but not sure when that will be. But I have TONS more work yet, but came very far in only two weeks imo. Race is on!

    I was also thinking about your other idea and just limiting the number per level and design it that way in Blender. Would the model file then be say 32 times as big?

    The sprite idea would work for the coins, but not for the platforms (ground) and such (my cubes!), but would reduce my draw calls at least.

    Why the text files? I suck (not experienced enough) at 3D editing and manoevering! I want like 50 levels (simple, puzzle like), and dread creating them by hand. I could whip one up in minutes in a text file.

    Thanks again.

    *Edit*
    How long did it take me to type that?
    Two replies since!
    Ya, what Eric said!
     
  19. Dark-Table

    Dark-Table

    Joined:
    Nov 25, 2008
    Posts:
    315
    You should be careful about using CombineChildren on the coins. Once stuff has been combined using that script it becomes a single dumb mesh. That means you couldn't easily pick up one coin and have it disappear without the rest of coins.

    CombineChildren should mainly be used on static stuff like walls and floor.
     
  20. digableplanet

    digableplanet

    Joined:
    Apr 30, 2009
    Posts:
    9
    many thanks pirate. i'm new to unity and this seems like a great technique. i'm working on a game that will have many balls moving on-screen at once, so it helps a ton.

    if i did this right, my steps were:
    1) in cheetah, draw 18 spheres and combine them into one mesh. i added one joint to each sphere and adjusted the vertex weights so they only applied to each sphere.
    2) in unity, import the mesh
    3) create a gameObject out of the mesh. (drop it into the scene)
    4) in the hierarachy tab, expand the mesh's gameObject. this should show 18 joints. from here you can treat each joint as you would any other gameObject. so for each of the joints, i added a RigidBody and a SphereCollider. i set each sphere collider's material to a bouncy physic material
    5) draw a plane beneath the balls and set its collider to the bouncy material
    6) play the game and watch the balls bounce off the plane all with 2 draw calls.

    my question is can this make use of PreFabs somehow? adding the same components to all the joints, gets pretty repetitive.

    thanks again.
     
  21. MikaMobile

    MikaMobile

    Joined:
    Jan 29, 2009
    Posts:
    845
    Yup, sounds like you have the technique down. Unfortunately adding the components to each joint individually is a necessity - you could make a prefab of the whole collection of 18 balls, but not of an individual ball in this case, as the whole set is sharing a single mesh. If your game called for a ridiculous number of these things that are practically identical, you could make groups of 10, make a prefab of that, and save yourself a little sanity at the expense of a few extra draw calls. For my own organizational purposes, the GUI in zombieville and the particle effects are two distinct groups of bones - they could have been combined into a single thing.. heck, I could combine the whole GAME together if I wanted to go crazy. But if your draw calls are reasonably low, you can't really tell the difference between 12 and 15 for instance.

    Also something that you should be aware of is that normally unity will automatically stop rendering a mesh that is off screen - but in this case, all of your spheres are a single mesh, so if any one part of it is being rendered, it ALL needs to be rendered. I've been using this technique for 2d-style games, where my various individual objects are simple 1-poly planes, so rendering 100 of them at a time is no big deal. Intense usage of this technique with high-poly 3d objects could prove problematic though depending on the game. A billiards game seems fine, but its something everybody should know before applying this technique to their project.
     
  22. Dark-Table

    Dark-Table

    Joined:
    Nov 25, 2008
    Posts:
    315
    If you look at the example I posted on the previous page (billiards.zip), I use a slightly different technique that allows me to use prefabs.

    Instead of your step 4, I created a script called "BoneManager" and a prefab called "Ball." The prefab contains a sphere collider, a rigidbody, and a ball script. I put BoneManager on the top level of the skinned gameobject, and created 16 prefab balls and positioned them in the world. At startup each ball prefab attaches a bone to itself using the AttachBone(gameObject, boneNumber) function from BoneManager.

    Then in the LateUpdate of BoneManager, each bone is set to the position and orientation of the prefab it's attached to.

    This is kind of the same philosophy behind the LinkedSpriteManager script.
     
  23. digableplanet

    digableplanet

    Joined:
    Apr 30, 2009
    Posts:
    9
    thanks for the heads up pirate. this technique does seem most applicable to 2d-style.

    silentchujo - thanks for putting your examples up. it wasn't obvious to me that the ball gameObjects were actually generated from a PreFab as i didn't see a Prefab in your hierarchy.
     
  24. BigRedSwitch

    BigRedSwitch

    Joined:
    Feb 11, 2009
    Posts:
    724
    So I'm having a go with this (brilliant) technique too. I'm finding some oddness, though - if you assign a BoxCollider to the bone(using script), does it use the bone's envelopes from the 3D editing software (in my case, 3DS Max) in order to decide the size of the collider? Objects which worked perfectly previously are now floating in the air, but still acting like they're affected by physics...! :?
     
  25. MikaMobile

    MikaMobile

    Joined:
    Jan 29, 2009
    Posts:
    845
    Select your bone(s) and look at the size of your colliders, you will most likely have to change their scale manually. I think the default is just 1x1x1 units, which could be WAY off depending on the scale of your scene. I've always added colliders through the editor myself, rather than through script.
     
  26. BigRedSwitch

    BigRedSwitch

    Joined:
    Feb 11, 2009
    Posts:
    724
    Yeah, adding them through script seems to be a bit of a nightmare - they're all coming out with zero size, and so far, by "working around" the problem in Script, the game crashes when you build and run it through XCode... :(
     
  27. TriplePAF

    TriplePAF

    Joined:
    Aug 19, 2009
    Posts:
    246
    Silentchujo

    Many thanks for posting the Billiards example project. I have used the same idea for an Car Mesh and my project is now down from 17 to 5 draw calls. The hole thing is just running so smooth that I have to tweak the steering and suspension system again. :p


    Peter.
     
  28. Jesse_Pixelsmith

    Jesse_Pixelsmith

    Joined:
    Nov 22, 2009
    Posts:
    296
    Sorry to necro the thread a bit, but does anyone know how to do this skinned mesh process in 3ds max, or could point me in the direction of a simple tutorial?

    My skills in max are for basic/structural modeling and texturing. I haven't touched rigging or animating yet.
    I've looked at some rigging tutorials and they seem overly complex for what I'm trying to do.

    Basically in my prototype, I have a game board which has little inset pieces (the grey tiles) which read out a position so the game pieces know where to go to when clicked/touched. Currently there are 61 grey pieces as 61 different meshes - this seems inefficient so I wanted to use this process. The grey tiles don't even need to move.




    Also on a different note, I am currently using mesh colliders on the hex's because that is the only thing that seems to 100% fit. With this method I would probably have to give up the mesh collider right? I suppose I could use box colliders and shrink them down to a square and still cover 85% of the hex.
     
  29. KITT

    KITT

    Joined:
    Jul 17, 2009
    Posts:
    221
    @ Jalex - You should try add a Capsule Collider to each hex as a circular radius will cover most of the area, then use bounds or OnCollisionStay to detect if the object is within the bounds of your colliders .. The Capsule would off course be taller than the height of your objects that will sit on each Hexagon position.
     

    Attached Files:

  30. fancifulunicorn

    fancifulunicorn

    Joined:
    Oct 11, 2010
    Posts:
    1
    I know it's been a while since this has been discussed but does anyone know a way to do this for animated meshes? For example, could you have a bunch of animated and identical enemies combined as a single mesh and controlled independently using this method?
     
  31. Barbur

    Barbur

    Joined:
    Oct 30, 2009
    Posts:
    160
    Hello after long time :)

    I am trying to use your bone technique with 3D Studio Max but I have no idea how to attach verts to a specific bone. When I assign a bone to a sphere using the modifier Skin works fine, when I rotate the bone the sphere containing it rotates too but when I use two spheres with the same process and attach them to be a single mesh, when rotating one bone the entire mes (so both spheres) rotate. I have no idea how to assign a group of verts to a bone to allow individual sphere rotation. Any idea?

    Thanks :)