Posted on by

gfx2-iconFor those who have been following the development of Corona’s Graphics 2.0 engine, you’ll quickly be introduced to one of the useful new features: anchor points. Anchor points are used to align display objects around a particular relative point within the object’s “bounding box” which can be considered as an imaginary rectangular box traced around the image.

The anchor point of an object controls position, rotation, and transformation.

Let’s consider an analog clock as an example, using this image of the clock’s hand. The overall image size is 412×64:
clock-hands-hi-v1
If you want to rotate this object around the center point, represented by the red ×, it’s clearly not centrally located in the image bounding box. Thus, you must change the anchor point to that location using the anchorX and anchorY properties. For each axis, these values range from 0 to 1, with 0.5 (default) being the center point.

object.anchorX = 0.5
object.anchorY = 0.5

For our clock hand graphic, we need to calculate this value based on the image size. The vertical value of the rotation point is centered, so we can keep the anchorY value at 0.5. On the x axis, we can calculate the anchorX value as follows:

93 ÷ 412 = 0.2257

With these two values, our 2.0-compatible code may look like this:

local secondHand = display.newImageRect( "images/clock-hands-hi.png", 412, 64 )
secondHand.anchorX = 0.2257
secondHand.anchorY = 0.5
secondHand.x = display.contentCenterX
secondHand.y = display.contentCenterY

timer.performWithDelay( 1000, function() secondHand.rotation = secondHand.rotation + 6; end, 0 )

Now, when you run the sample project, you’ll see that the hand spins around its center point as intended.

If you want an image aligned around its top-left point, set both of the anchor points to 0. If you want it aligned around the bottom-right, set both to 1. Also note that anchor points can be specified before or after positioning the object. If specified after, the object will move (shift) in respect to the new anchor point, although this can be overridden by anchoring the children to the group as described in the next section.

Anchor Points and Groups

Generally speaking, groups don’t care about anchor points because they are “unbounded.” In other words, as a group’s children move within it, the group’s bounding box changes to accommodate its new size. However, if you need to align, rotate, or move a group and its children, there is a special property that you must set on the group. This property is named .anchorChildren and it will cause the objects in the group to behave as if they were a single rectangle respecting the anchor point set on the group.

local group = display.newGroup()
local rects = {}

for i = 1,5 do
   rects[i] = display.newRect(math.random(150)+100, math.random(150)+100, math.random(100)+50, math.random(100)+50)
   rects[i]:setFillColor(math.random(255)/255, math.random(255)/255, math.random(255)/255)
   group:insert( rects[i] )
end

group.anchorChildren = true
group.x = display.contentCenterX
group.y = display.contentCenterY

timer.performWithDelay( 1000, function() group.rotation = group.rotation-20; end, 0 )

This will cause the group and its children to re-position around its top left point, then rotate -20 degrees as a singular unit.

For display groups in Graphics 2.0, .anchorChildren is false by default.

Groups, being infinite in width and height, don’t have an actual center. Applying the .anchorChildren property and setting it to true causes the group to behave as if it had a limited width and height. This width and height is based on the bounding box all of its children. Without this flag, or if it’s set to false, the children will rotate around the group’s origin which, in this case, is the center because the code repositioned the group to be in the center of the screen. The children will now orbit around this center point, similar to the way a planet orbits the sun.

By setting the value to true, the group pretends that it has a bounding box around the children. In this situation, the children now move as if part of a singular unit, preserving the width/height of the bounding box and the relative positioning of the children to each other. In this case, the anchoring controls the alignment of the bounding box to the group’s origin, just like how the anchoring controls the alignment of a rectangle object relative to its origin. To continue our astronomy analogy, this is more like how the Earth rotates on its axis.

It’s best to run this block of code and change the .anchorChildren value from true to false and see how it differs.

Anchor Points and Containers

Containers are another new construct in Graphics 2.0. You can think of them as display groups that have a defined bounding box. All content outside of the container’s bounds is automatically masked (clipped). Containers work like groups in that you use the :insert method to insert display objects into it. Similarly, moving the container moves all of its contents.

Despite the similarities, containers behave differently in some respects, primarily in how children behave. Unlike groups, the default for .anchorChildren is true. Thus, when you move a container, the children inside will move in lock-step with the clipping bounds. In contrast, if you want to move the clipping box without moving the objects inside, set .anchorChildren to false.

From Graphics 1.0 to 2.0

A few other things have changed in Graphics 2.0 in relation to object positioning. Let’s examine these further and learn how Graphics 2.0 behaves differently.

Positioning Inconsistencies

In 1.0, there are some inconsistencies in object positioning upon creation. While most display objects let you pass in position coordinates when creating the object, some do not. More importantly, how these items are positioned is also inconsistent. In the case of some display objects like a display.newRect(), the top-left corner of the rectangle is defined at the position specified in the function call, and the width and height values extend to the right and downward:

local myBox = display.newRect( 0, 0, 100, 100 )

With this call, you get a 100×100 pixel box with its top-left corner located at 0,0 on the screen. However, that box’s actual x and y coordinates are 50,50, since the default position is considered at its center.

In Graphics 2.0, object positioning is now consistently based around the center reference point. Thus, to get the same square in the same position as illustrated above, the command is:

local myBox = display.newRect( 50, 50, 100, 100 )

Reference Points

In version 1.0, most developers aligned objects using reference point constants based on the most common relative locations around which objects might be aligned:

local myBox = display.newRect( 0, 0, 100, 100 )
myBox:setReferencePoint( display.TopLeftReferencePoint )
myBox.x = 100
myBox.y = 100

In Graphics 2.0, reference points have been deprecated. Anchor points must be used in their place unless you decide to use V1 Compatibility Mode.

Version 1.0 Compatibility Mode

We understand that there is a large code base that uses reference points and we want to minimize the impact on you. You can continue to use reference points simply by adding one line to your config.lua file:

application =
{
   content =
   {
      graphicsCompatibility = 1,  --this turns on V1 Compatibility mode
      width = 320,
      height = 480,
      scale = "letterbox"  --zoom to fill screen, possibly cropping edges
   },
}

This flag also changes most display object constructors to use top-left when creating objects, just like version 1.0. This being said, we encourage you to convert to anchor points instead of reference points.

Default Anchor Point

Because some apps may need to change the default behavior of display objects to return to a top-left type creation, Graphics 2.0 also adds a couple of new functions that let you set the “default anchor point” before items are created. For example:

display.setDefault( "anchorX", 0 )
display.setDefault( "anchorY", 0 )

After this declaration, all new display objects will use the top left as their anchor point (objects created beforehand will remain unaffected). Your code likely exists at a nexus where some of your objects will be perfectly happy with a center anchor point, while other objects will be out of place. Depending on how many need adjusting, you can use these defaults to help reduce the number of edits in your code. For example, if most of your app works with centered objects but you have a scene where you draw several top-left oriented text objects, you can do this:

display.setDefault( "anchorX", 0 )
display.setDefault( "anchorY", 0 )

text1 = display.newText( "Hello", 10, 10, "Helvetica", 12 )
text2 = display.newText( "World", 10, 24, "Helvetica", 12 )
text3 = display.newText( "How are you?", 10, 38, "Helvetica", 12 )

display.setDefault( "anchorX", 0.5 )
display.setDefault( "anchorY", 0.5 )

This will cause those three display.newText() calls to resort to their previous top-left positioning and then, afterward, the default anchor point is reverted back to center.

In Summary

Anchor points are just one of several new features in Graphics 2.0. Hopefully once you use them and see how the relational, flexible alignment can work to your benefit, they will become a staple of your development.

If you’re a Corona Pro or Enterprise subscriber, please download a recent Graphics 2.0 Daily Build and test out the sample project using anchor points.


Posted by . Thanks for reading...

21 Responses to “Tutorial: Anchor Points in Graphics 2.0”

    • Rob Miracle

      If you’re using the normal ones (TopLeft, Center, CenterRight, etc.), then Anchor points will actually save you quite a bit of code and time. obj.anchorX = 0; obj.anchorY = 0 is less than obj:setReferencePoint(display.TopLeftReferecenePoint). Secondly, they can be set as defaults, so if you want to left, center align a bunch of display.newText() objects, you can set the default, create the fields, and then set the default back to center when done.

      Now if you are using variable reference points using .xReference and .yReference, it’s probably a bit of a trade off. I found it a bit faster to determine the anchor point (92 pixels in / 412 total width) rather than having to do 412 / 2 – 92 and remembering to make it negative. But either works.

      Reply
      • Sky Jay

        What about adding % option to the list? Like using “anchorX = 50%” to centre objects. It will be extending “V2 anchorX” exactly like “V1 center reference point”, that way we wont have to calculate x and y of objects.

        Reply
  1. Thomas Vanden Abeele

    Following up on my original post, would it be possible to include a function that would allow the “old” style using pixel values, and have it automatically convert? Something like:

    image.calcAnchor(-42,0)

    Having these functions automatically do the division by total width and height and setting both anchor points? This might be related to my very personal workflow, but the alternative seems to be lots and lots of painstaking manual measurements in photoshop and mindnumbing divisions.

    Reply
    • gtt

      You can easily write an helper function that accepts and object and x, y coordinates and converts to 0.0-1.0 values:

      local min, max = math.min, math.max
      local function setAnchorCoordinates( object, xReference, yReference )
      object.anchorX, object.anchorY = min( 1, max( 0, xReference / object.width ) ), min( 1, max( 0, yReference / object.height ) )
      end

      Reply
      • Sean

        I found this idea very useful, but it does have a bug. You need to add .5 to the values generated because xReference or yReference assume that 0 is the center of the object (unless they are in a group it seems).

        function setAnchorCoordinates( object, xReference, yReference )
        object.anchorX = math.min( 1, math.max( 0, (xReference / object.width)+.5) )
        print(“x”,object.anchorX,” width “, object.width)
        object.anchorY = math.min( 1, math.max( 0, (yReference / object.height)+.5 ) )
        print(“y”,object.anchorY,” width “, object.height)
        end

        Reply
  2. David

    Hey Rob. Great tutorial – any word on Physics? I’ve been having problems with it in my game – something about hybrid mode not obeying the anchor points…

    Thanks,
    David

    Reply
    • Rob Miracle

      I would recommend posting in the Graphics 2.0 with problems that you have and if you have what looks like a reproducible bug, the engineers would like a project with the necessary art and things so they can visually see what you’re seeing (filed with the Report a bug link at the top.)

      Reply
    • Walter

      As with reference points in the 1.0 engine, anchor points are not honored by the physics system.

      Reply
  3. Antti

    What is the difference between object.anchorX and object.xReference besides units? Are xReference and yReference also deprecated?

    Reply
    • Walter

      Yes, xReference and yReference are not available in the G2.0 engine, even in v1 mode, as we could not simulate them properly in the G2.0 engine

      Reply
      • Thomas

        Hmmm… That explains why my graphics were all screwed up, even in compatibility mode. Just checking: I am still able to finish my (big) current project and build for iOS 7 using the current (non-g2.0) builds, right?

        Reply
  4. Krishna Raj Salim

    Hi, this is not working with my corona version: 2013.1202 (2013.8.28), even the sample code doe not seem to work. I still get the object rotation with respects to it’s visual centre point .

    Reply
    • Rob Miracle

      Hi Krishna. This is a Graphics 2.0 feature which is only available to Pro and Enterprise subscribers and is only in daily builds numbered greater than 2000.

      Reply
  5. JJ

    Hello there,

    I’ve been testing the new anchor points but I seem to be running into some kind of issue when it’s used with texturepacker sprites.

    Basically when I set the anchorX of my image to 0 (was previously display.CenterLeftReferencePoint), it loses it “hitbox” and doesn’t work with eventListener attached. Essentially it’s just

    myImage.anchorX = 0
    myImage:addEventListener(“touch”,myImageClicked)

    It doesn’t register the touch event unless I click on the very left side of the image.

    I’ve tried this with a regular display.newRect and it works perfectly for the rectangle however it’s not working with display.newSprite with sprites from texturepacker.

    Hope you understand what I meant.

    Thanks

    Reply
  6. Davut Engin

    Hello,
    I used negative yReference in my project for rotating gauge pointer.
    But I cant migrate it to Graphics 2.0.
    Because anchorX and anchorY doesnot support negative value.
    I think it have to support negative values.
    And also it have to support number range more then 0 to 1.

    Thanks :)

    Reply
  7. simran

    Nice tutorial, but I have this huge confusion! The object is rotating at an anchor point os obviously its x, y coordinates are never changing, what If I had to determine collision of an object with this hand which is continuously rotating? I mean how do I get points along the length of its hands with its changing position, anyone, please?

    Reply

Leave a Reply

  • (Will Not Be Published)