Search Unity

Same Custom Physic Shape

Discussion in '2D' started by darthlaseris, Oct 16, 2019.

  1. darthlaseris

    darthlaseris

    Joined:
    Apr 23, 2019
    Posts:
    1
    Hi

    I´m working in my tileset and I have to give a custom physic shape to my ground tiles to set the proper collider. My question is if there is any way in which I could copy/paste the same physic shape to all my tiles, since doing one by one by hand is not a precise way to do it.
     
  2. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    There isn't any way within Unity, but if you open up the .meta file for a sprite, you will find a section for physics shape data. You can copy that to the other sprite's .meta and save it.
     
  3. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    1,068
  4. nate3543

    nate3543

    Joined:
    Dec 28, 2019
    Posts:
    1
    Having the same problem. All my tiles need to have the same custom collision box. I would definitely appreciate if we got a drop-down selection for collision type in Tilemap Collider 2D instead of changing every sprite manually. Perhaps there could be the same options in this drop-down as in the tile asset collision settings (ie. sprite & grid). Then we could select a sprite that would be used for the collision of all tiles in a set, and that sprite could have a custom physics shape.
     
  5. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    Oh, man.. this is kind of thing that it should really be in the editor. It's 2020 July and it is still not featured. Can we please have this feature? Simple copy and paste should just work!
     
  6. Zephus

    Zephus

    Joined:
    May 25, 2015
    Posts:
    356
    @ChuanXin Any update on this? Seems like such a basic feature. I don't get why I have to redraw the physics shape for every single tile when I have dozens of variations of the same one.
     
  7. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    1,068
    Sorry, this has not been prioritised yet unfortunately. We hope to get to this and other Sprite Editor improvements soon!
     
    Zephus likes this.
  8. Imaginesto

    Imaginesto

    Joined:
    Apr 15, 2020
    Posts:
    9
    I believe I've come up with a solution. I put together a mini tutorial to help. Lmk if it works for you!

    ...
     

    Attached Files:

    pturow, sederfan and SunnysideGames like this.
  9. SunnysideGames

    SunnysideGames

    Joined:
    May 14, 2014
    Posts:
    25
    Hey today I managed to put together a script to easily copy / paste sprite physics shape to multiple selected sprites, thanks to some code I found on this post:
    https://issuetracker.unity3d.com/is...-raises-an-error-when-using-it-in-editor-mode

    SpritePhysicsShapePost Context Menu.png


    Here's the code (Unity 2019.4) :

    SpritePhysicsShapePostprocessor.cs

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEditor;
    3. using UnityEngine;
    4. using UnityEngine.U2D;
    5.  
    6.     public class SpritePhysicsShapePostprocessor : AssetPostprocessor
    7.     {
    8.         SerializedObject serializedImporter;
    9.  
    10.         void OnPostprocessSprites(Texture2D texture, Sprite[] sprites)
    11.         {
    12.             if (SpritePhysicsShapeEditor.copiedShapes.Count > 0 && SpritePhysicsShapeEditor.destinationSprites.Count > 0)
    13.             {
    14.                 for (int i = 0; i < sprites.Length; i++)
    15.                 {
    16.                     if (SpritePhysicsShapeEditor.destinationSprites.Contains(sprites[i].name))
    17.                     {
    18.                         SpritePhysicsShapeEditor.SetPhysicsShape(assetImporter as TextureImporter, sprites[i].name, SpritePhysicsShapeEditor.copiedShapes);
    19.                     }
    20.                 }
    21.  
    22.                 SpritePhysicsShapeEditor.destinationSprites.Clear();
    23.             }
    24.         }
    25.     }
    26.  
    27.     public class SpritePhysicsShapeEditor : ModuleModelEditor
    28.     {
    29.         //tatic Sprite copiedSprite;
    30.         static public List<Vector2[]> copiedShapes = new List<Vector2[]>();
    31.         static public List<string> destinationSprites = new List<string>();
    32.  
    33.         [MenuItem("Assets/Tools/Sprites/Copy Physics Shape", true)]
    34.         static bool CheckSpriteMenu()
    35.         {
    36.             if (Selection.activeObject is Texture2D)
    37.             {
    38.                 var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(Selection.activeObject)) as TextureImporter;
    39.  
    40.                 if (importer.textureType == TextureImporterType.Sprite && importer.spriteImportMode != SpriteImportMode.Multiple)
    41.                 {
    42.                     var sprite = AssetDatabase.LoadAssetAtPath<Sprite>(AssetDatabase.GetAssetPath(Selection.activeObject));
    43.  
    44.                     if (sprite)
    45.                     {
    46.                         return true;
    47.                     }
    48.                 }
    49.             }
    50.             else if (Selection.activeObject is Sprite)
    51.             {
    52.                 return true;
    53.             }
    54.             return false;
    55.         }
    56.  
    57.         [MenuItem("Assets/Tools/Sprites/Paste Physics Shape", true)]
    58.         static bool CheckSpritesToModifyMenu()
    59.         {
    60.             if (copiedShapes.Count > 0)
    61.             {
    62.                 var sprites = GetSpritesFromSelection();
    63.  
    64.                 if (sprites.Count > 0)
    65.                 {
    66.                     return true;
    67.                 }
    68.             }
    69.  
    70.             return false;
    71.         }
    72.  
    73.         [MenuItem("Assets/Tools/Sprites/Copy Physics Shape", false)]
    74.         static void CopySpritePhysicsShape()
    75.         {
    76.             var sprite = Selection.activeObject as Sprite;
    77.  
    78.             if (!sprite && Selection.activeObject is Texture2D)
    79.             {
    80.                 sprite = AssetDatabase.LoadAssetAtPath<Sprite>(AssetDatabase.GetAssetPath(Selection.activeObject));
    81.             }
    82.  
    83.             copiedShapes.Clear();
    84.  
    85.             TextureImporter importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(sprite)) as TextureImporter;
    86.  
    87.             copiedShapes = GetPhysicsShape(importer, sprite.name);
    88.         }
    89.      
    90.         [MenuItem("Assets/Tools/Sprites/Paste Physics Shape", false)]
    91.         static void PasteSpritePhysicsShape()
    92.         {
    93.             List<Sprite> sprites = GetSpritesFromSelection();
    94.  
    95.             if (sprites.Count > 0)
    96.             {
    97.                 //Undo.RecordObjects(sprites.ToArray(), "Paste sprites physics shape");
    98.  
    99.                 for (int i = 0; i < sprites.Count; i++)
    100.                 {
    101.                     var path = AssetDatabase.GetAssetPath(sprites[i]);
    102.                     destinationSprites.Add(sprites[i].name);
    103.  
    104.                     TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;
    105.  
    106.                     AssetDatabase.ForceReserializeAssets(new string[] { importer.assetPath }, ForceReserializeAssetsOptions.ReserializeMetadata);
    107.                     importer.SaveAndReimport();
    108.                 }
    109.             }
    110.         }
    111.  
    112.         public static List<Sprite> GetSpritesFromSelection()
    113.         {
    114.             List<Sprite> sprites = new List<Sprite>(Selection.GetFiltered<Sprite>(SelectionMode.Unfiltered));
    115.             destinationSprites.Clear();
    116.  
    117.             var textures = Selection.GetFiltered<Texture2D>(SelectionMode.Unfiltered);
    118.          
    119.             if (textures.Length > 0)
    120.             {
    121.                 for (int i = 0; i < textures.Length; i++)
    122.                 {
    123.                     var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(textures[i])) as TextureImporter;
    124.  
    125.                     if (importer.textureType == TextureImporterType.Sprite && importer.spriteImportMode != SpriteImportMode.Multiple)
    126.                     {
    127.                         var objects = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(textures[i]));
    128.  
    129.                         for (int k = 0; k < objects.Length; k++)
    130.                         {
    131.                             if (objects[k] is Sprite)
    132.                             {
    133.                                 sprites.Add(objects[k] as Sprite);
    134.                             }
    135.                         }
    136.                     }
    137.                 }
    138.             }
    139.  
    140.             return sprites;
    141.         }
    142.      
    143.         public static void SetPhysicsShape(TextureImporter importer, string spriteName, List<Vector2[]> data)
    144.         {
    145.             var physicsShapeProperty = GetPhysicsShapeProperty(importer, spriteName);
    146.             physicsShapeProperty.ClearArray();
    147.  
    148.             for (int j = 0; j < data.Count; ++j)
    149.             {
    150.                 physicsShapeProperty.InsertArrayElementAtIndex(j);
    151.                 var o = data[j];
    152.                 SerializedProperty outlinePathSP = physicsShapeProperty.GetArrayElementAtIndex(j);
    153.                 outlinePathSP.ClearArray();
    154.  
    155.                 for (int k = 0; k < o.Length; ++k)
    156.                 {
    157.                     outlinePathSP.InsertArrayElementAtIndex(k);
    158.                     outlinePathSP.GetArrayElementAtIndex(k).vector2Value = o[k];
    159.                 }
    160.             }
    161.  
    162.             physicsShapeProperty.serializedObject.ApplyModifiedPropertiesWithoutUndo();
    163.         }
    164.      
    165.         public static List<Vector2[]> GetPhysicsShape(TextureImporter importer, string spriteName)
    166.         {
    167.             var physicsShapeProperty = GetPhysicsShapeProperty(importer, spriteName);
    168.             var physicsShape = new List<Vector2[]>();
    169.  
    170.             for (int j = 0; j < physicsShapeProperty.arraySize; ++j)
    171.             {
    172.                 SerializedProperty physicsShapePathSP = physicsShapeProperty.GetArrayElementAtIndex(j);
    173.                 var o = new Vector2[physicsShapePathSP.arraySize];
    174.  
    175.                 for (int k = 0; k < physicsShapePathSP.arraySize; ++k)
    176.                 {
    177.                     o[k] = physicsShapePathSP.GetArrayElementAtIndex(k).vector2Value;
    178.                 }
    179.  
    180.                 physicsShape.Add(o);
    181.             }
    182.  
    183.             return physicsShape;
    184.         }
    185.  
    186.         private static SerializedProperty GetPhysicsShapeProperty(TextureImporter importer, string spriteName)
    187.         {
    188.             SerializedObject serializedImporter = new SerializedObject(importer);
    189.  
    190.             if (importer.spriteImportMode == SpriteImportMode.Multiple)
    191.             {
    192.                 var spriteSheetSP = serializedImporter.FindProperty("m_SpriteSheet.m_Sprites");
    193.  
    194.                 for (int i = 0; i < spriteSheetSP.arraySize; i++)
    195.                 {
    196.                     if (importer.spritesheet[i].name == spriteName)
    197.                     {
    198.                         var element = spriteSheetSP.GetArrayElementAtIndex(i);
    199.  
    200.                         return element.FindPropertyRelative("m_PhysicsShape");
    201.                     }
    202.                 }
    203.  
    204.             }
    205.  
    206.             return serializedImporter.FindProperty("m_SpriteSheet.m_PhysicsShape");
    207.         }
    208.     }
     
    Last edited: Aug 18, 2020
    Imaginesto likes this.
  10. SunnysideGames

    SunnysideGames

    Joined:
    May 14, 2014
    Posts:
    25
    What are the odds we revive this thread with a solution at the same time haha ^^

    Thanks for the tip, I forgot presets existed at all !
     
    Imaginesto likes this.
  11. Imaginesto

    Imaginesto

    Joined:
    Apr 15, 2020
    Posts:
    9
    haha Thats Awesome! And for sure, thank you! Can't wait to try it out! :D
     
    SunnysideGames likes this.
  12. SoulGameStudio

    SoulGameStudio

    Joined:
    Jan 18, 2016
    Posts:
    46
    @Imaginesto dude, you're a genius you know that :D
    Thanks for the easy solution!
     
  13. Lo-renzo

    Lo-renzo

    Joined:
    Apr 8, 2018
    Posts:
    1,510
    As of 2020.2 there's this built-in feature https://unity3d.com/unity/whats-new/2020.2.0
     
  14. Zephus

    Zephus

    Joined:
    May 25, 2015
    Posts:
    356
    That's definitely helpful, and I've been using it, but you still need to click on every single tile and copy it manually to the other sprite. "Paste All" for some reason means to paste the currently copied shape to every other tile, when it would be way more usefull if you could copy multiple tiles.
     
  15. ArcticPinou

    ArcticPinou

    Joined:
    Dec 24, 2015
    Posts:
    11
  16. sederfan

    sederfan

    Joined:
    May 2, 2022
    Posts:
    4
    Thank you very much this worked like a Charm.