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
2
Question by kingstone426 · Apr 05, 2014 at 06:42 PM · 2dspritepixel

Pixel-perfect custom sprite pivot point

I'm playing around with some of the new 2D features introduced with Unity 4.3. For the main character, I've got a sprite sheet with varying frame dimensions. When the character stands still he uses smaller frames, but when he's swinging his sword around he needs more space.

My problem is that when I try to position the pivot point using the mouse it tends to get floating point values, not corresponding to exact pixel values. This prevents the animation from running pixel perfect and it all looks rather lame as the character kind of floats around with each frame.

The only possible solution I've come up with is to measure the pixel position in photoshop, recalculate that into a fractional value corresponding to the pivot position depending on the size of the frame and enter those numbers into the custom pivot x and y fields in the sprite editor. Obviously, that's not a very practical workflow.

The optimal workflow would be if I could simply get the pivot point to snap to exact pixel values, e.g., using the CMD key like snapping in the scene view. Is there another way to achieve what I'm after?

I know I could also use a single frame dimension, e.g. 64x64, use grid-slicing with a non-custom pivot point and position the sprite within that frame in photoshop to achieve pixel perfect pivot point, but that tends to produce a lot of white space in the textures because all frames must conform to the largest frame possible. Also it kind of goes against the whole idea of being able to set the pivot point on a per-frame basis.

Comment
Add comment · Show 4
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 Dave-Hampson ♦♦ · Apr 09, 2014 at 11:32 AM 0
Share

The only possible solution I've come up with is to measure the pixel position in photoshop, recalculate that into a fractional value corresponding to the pivot position depending on the size of the frame and enter those numbers into the custom pivot x and y fields in the sprite editor. Obviously, that's not a very practical workflow.

I don't know of a solution to your issue I'm afraid, but that workflow issue you mention sounds like a good thing to write an editor script for. Sometimes the time taken to write the script can outweigh the cost of entering numbers 'by hand', if there is a lot of work to do.

avatar image kingstone426 · Apr 09, 2014 at 10:56 PM 0
Share

Thanks for the suggestion Dave!

I'd really like to implement a auto-snap feature similar to the neat code snippet provided by karl_ in response to this question. That editor extension manipulates game objects in the scene view using the Selection class, but would it possible to write an editor extension to access the state and pivot points of the sprite editor window? How is the sprite editor exposed through the API, I can't seem to find anything about it in the documentation.

avatar image sandygk · Feb 18, 2015 at 09:53 PM 0
Share

I am looking for this feature too. I am also considering to implement it myself.

Here's an example of how to modify the pivot point of a sprite (or many) from script, I think it could be useful:

http://answers.unity3d.com/questions/761009/unity2d-sprite-editor-multiple-pivot-point-edit.html

Enhancing the Sprite Editor would be very cool, but I am not sure it will be possible. If you find any information about it please leave a comment.

avatar image schkorpio · Jul 24, 2015 at 07:51 AM 0
Share

Hello, I know this is an old thread, but just thought I would check on the off chance someone did end up making an snap to pixel script.

Thanks!

2 Replies

· Add your reply
  • Sort: 
avatar image
6

Answer by sandygk · Jul 24, 2015 at 03:32 PM

I ended up doing mine (find the code below).

To use it, you just need to add the script inside the Editor Folder (be sure the file is called SpritePivotPositioner.cs). Then, in Unity, go to Windows -> Sprite Pivot Positioner. Select the sprite you want to modify. You'll notice the sprite has a small square, that's the current pivot, if the square is red it means the pivot is not in a pixel perfect position (otherwise it would be green). Click anywhere on the sprite to select a new pivot and click Apply Changes and that's it. I hope it helps. If you have any doubts just give me a shout.

 using System;
 using UnityEditor;
 using UnityEngine;
 
 internal class SpritePivotPositioner : EditorWindow
 {
     #region Private Fields
 
     private static Texture2D _rectTexture;
     private static bool _newPivotSelected;
     private static int _newPivotXPixel;
     private static int _newPivotYPixel;
     private static Sprite _sprite;
 
     private static float _frameMargin = 2;
     private static int _scale = 6;
     private static Color _borderColor = Color.gray;
     private static float _shade = 0.7f;
     private static Color _frameColor = new Color(_shade, _shade, _shade);
 
     private static Color _currentPivotColor = Color.green;
     private static Color _mousePosColor = Color.gray;
     private static Color _newPivotColor = Color.blue;
     private static Color _misplacedPivotColor = Color.red;
 
     private float _outerFrameWidth;
     private float _outerFrameHeight;
     private float _innerFrameWidth;
     private float _innerFrameHeight;
     private float _x;
     private float _y;
 
     #endregion Private Fields
 
     #region Public Methods
 
     [MenuItem("Window/Sprite Pivot Positioner")]
     public static void ShowWindow()
     {
         EditorWindow.GetWindow(typeof(SpritePivotPositioner));
     }
 
     #endregion Public Methods
 
     #region Private Methods
 
     private void Update()
     {
         Repaint();
     }
 
     private void OnGUI()
     {
         if (!SelectSprite()) return;
         DrawSprite();
         DrawMousePos();
         DrawNewPivot();
         DrawCurrentPivot();
         ProcessMouseClick();
         ProcessApplyButton();
     }
 
     private bool SelectSprite()
     {
         if (Selection.activeObject != null &&
             Selection.activeObject is UnityEngine.Sprite)
         {
             if (_sprite != (Sprite)Selection.activeObject)
             {
                 _sprite = (Sprite)Selection.activeObject;
                 _newPivotSelected = false;
             }
 
             return true;
         }
         return false;
     }
 
     private void DrawSprite()
     {
         _innerFrameWidth = _sprite.rect.width * _scale;
         _innerFrameHeight = _sprite.rect.height * _scale;
 
         _outerFrameWidth = _innerFrameWidth + 2 * _frameMargin;
         _outerFrameHeight = _innerFrameHeight + 2 * _frameMargin;
 
         var rect = EditorGUILayout.GetControlRect(true, _outerFrameHeight);
         _x = rect.min.x;
         _y = rect.min.y;
 
         //draw the rect that fills the scroll:
         GUIExtensions.DrawRect(new Rect(_x, _y, _outerFrameWidth, _outerFrameHeight), _borderColor, ref _rectTexture);
 
         //draw the background colour of each frame:
         _x += _frameMargin;
         _y += _frameMargin;
         GUIExtensions.DrawRect(new Rect(_x, _y, _innerFrameWidth, _innerFrameHeight), _frameColor, ref _rectTexture);
 
         //draw the sprite
         Texture texture = _sprite.texture;
         Rect textureRect = _sprite.textureRect;
         var textureCoords = new Rect(textureRect.x / texture.width, textureRect.y / texture.height,
                                      textureRect.width / texture.width, textureRect.height / texture.height);
         var positionRect = new Rect(_x, _y, _innerFrameWidth, _innerFrameHeight);
         GUI.DrawTextureWithTexCoords(positionRect, texture, textureCoords);
     }
 
     private void DrawPixel(float x, float y, Color color, bool invertY = true)
     {
         bool intergerPosition = (x == Math.Floor(x) && y == Math.Floor(y));
         x = _x + x * _scale;
         if (invertY) y = _sprite.rect.height - 1 - y;
         y = _y + y * _scale;
         GUIExtensions.DrawRect(new Rect(x, y, _scale, _scale), intergerPosition ? color : _misplacedPivotColor, ref _rectTexture);
     }
 
     private void DrawCurrentPivot()
     {
         float x = _sprite.pivot.x;
         float y = _sprite.pivot.y;
         DrawPixel(x, y, _currentPivotColor);
     }
 
     private void DrawNewPivot()
     {
         if (!_newPivotSelected) return;
         DrawPixel(_newPivotXPixel, _newPivotYPixel, _newPivotColor);
     }
 
     private void DrawMousePos()
     {
         int x, y;
         if (GetMousePixel(out x, out y))
         {
             DrawPixel(x, y, _mousePosColor, false);
         }
     }
 
     private bool GetMousePixel(out int x, out int y)
     {
         x = (int)((Event.current.mousePosition.x - _x) / _scale);
         y = (int)((Event.current.mousePosition.y - _y) / _scale);
         return x >= 0 && x < _sprite.rect.width &&
                y >= 0 && y < _sprite.rect.height;
     }
 
     private void ProcessMouseClick()
     {
         int x, y;
         if (GetMousePixel(out x, out y) &&
             Event.current.isMouse)
         {
             _newPivotSelected = true;
             _newPivotXPixel = x;
             _newPivotYPixel = (int)_sprite.rect.height - 1 - y;
         }
     }
 
     private void ProcessApplyButton()
     {
         GUI.enabled = _newPivotSelected;
         if (!GUILayout.Button("Apply Changes")) return;
         GUI.enabled = true;
 
         string path = AssetDatabase.GetAssetPath(_sprite.texture);
         var textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
         var spritesheet = textureImporter.spritesheet;
         for (int i = 0; i < spritesheet.Length; i++)
         {
             if (spritesheet[i].name != _sprite.name)
                 continue;
             textureImporter.isReadable = true;
             var spriteMetaData = spritesheet[i];
 
             spriteMetaData.alignment = (int)SpriteAlignment.Custom;
             float xFraction = _newPivotXPixel / (float)_sprite.rect.width;
             float yFraction = _newPivotYPixel / (float)_sprite.rect.height;
             spriteMetaData.pivot = new Vector2(xFraction, yFraction);
 
             spritesheet[i] = spriteMetaData;
             textureImporter.spritesheet = spritesheet;
             textureImporter.isReadable = false; //apparently this must be before the AssetDatabase.ImportAsset(...) call
             AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
             break;
         }
         _newPivotSelected = false;
     }
 
     private void OnDestroy()
     {
         _rectTexture = null;
     }
     #endregion Private Methods
 }
 
 public class GUIExtensions
 {
     static private GUIStyle _rectStyle;

     //I am passing the rectTexture rather than using a local 
     //static variable because it will leak memory otherwise
     public static void DrawRect(Rect position, Color color, ref Texture2D _rectTexture)
     {
         if (_rectTexture == null)
         {
             _rectTexture = new Texture2D(1, 1);
             _rectTexture.hideFlags = HideFlags.DontSaveInEditor;
         }

         if (_rectStyle == null)
         {
             _rectStyle = new GUIStyle();
         }

         _rectTexture.SetPixel(0, 0, color);
         _rectTexture.Apply();

         _rectStyle.normal.background = _rectTexture;

         GUI.Box(position, GUIContent.none, _rectStyle);
     }
 }
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 schkorpio · Jul 24, 2015 at 11:58 PM 0
Share

Thanks very much for sharing your script!

I'm getting this error however: Assets/Editor/SpritePivotPositioner.cs(92,17): error CS0103: The name `GUIExtensions' does not exist in the current context

Is GUIExtensions so kind of plugin?

avatar image sandygk · Jul 25, 2015 at 03:46 AM 0
Share

Sorry, I forgot to include the class GUIExtensions in the script. It should work fine now.

avatar image schkorpio · Jul 25, 2015 at 05:21 AM 0
Share

Brilliant, thanks again. This is exactly what I was after!

Can I buy you a beer online? :)

avatar image sandygk · Jul 25, 2015 at 02:40 PM 0
Share

hehe, no need for the beer, happy to help :)

avatar image ChadrickEvans · Mar 01, 2016 at 06:11 PM 0
Share

This script is exactly what I was looking for, however, the window appears to be quite large on screen. I went into the script and altered the "_scale" but that made pixel snapping very inaccurate. Is there a way to have this window be resizable?

Show more comments
avatar image
0

Answer by Avash · May 01, 2016 at 07:59 PM

It shows only an empty tab.

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 haskell33 · May 04, 2016 at 02:53 PM 0
Share

After following the instructions in OP's post, what you want to do is create a Sprite Sheet (Sprite using "$$anonymous$$ultiple" mode). After your various sprites are sliced, in the Project tab you will have the original image file, and if you expand it, the virtual sprites are all listed below. You want to have any of these virtual sprites selected before you select Windows -> Sprite Pivot Positioner. Did it work?

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

29 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

Related Questions

Pivot Point Change Doesn't Apply 3 Answers

How to eliminate gaps between 2d colliders? 2 Answers

Is it possible to make 2D Sprites in Unity,Is it possible to make 2D Sprites in Unity or is Blender more suitable? 2 Answers

Animation doesn't play all the way through unless holding the key down 1 Answer

A Question about sprites 3 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