Your Ad Here

Simple Flash Snap-To-Grid (Flash AS 2.0)

NOTE: the grid I’m using in this tutorial is explained in my “full-stage grid” post.

I use this setup fairly often these days, so I thought I’d give an introduction to the concept, and I’ll add more complex ideas later on. First, here’s the full source code. Copy and paste this into the first frame of a new flash document, and run it.

var size:Number = 100;
var columns:Number = Math.ceil(Stage.width / size);
var rows:Number = Math.ceil(Stage.height / size);
var ball_move:Boolean = false;

var ball:MovieClip = this.createEmptyMovieClip(”ball”, this.getNextHighestDepth());
ball.lineStyle(70, 0×0, 100);
ball.lineTo(0, 1);
ball._x = 200;
ball._y = 200;

ball.onPress = function () {

this.startDrag();
ball_move = true;

}

ball.onMouseUp = function () {

this.stopDrag();
ball_move = false;

}

ball.onMouseMove = function () {

if (!ball_move) return;
this._x = _xmouse - _xmouse%size;
this._y = _ymouse - _ymouse%size;
updateAfterEvent();

}

function drawSquare (who:String, to_x, to_y) {

this.createEmptyMovieClip(who, this.getNextHighestDepth());
this[who].lineStyle(1, 0×0, 100);
this[who].moveTo(0, 0);
this[who].lineTo(size, 0);
this[who].lineTo(size, size);
this[who].lineTo(0, size);
this[who]._x = to_x;
this[who]._y = to_y;

}

for (var c=0; c<columns; c++) {

for (var r=0; r<rows; r++) {

drawSquare(”c_”+c+”_r_”+r, c*size, r*size);

}

}

That’s it! pretty simple. Let’s walk through it.

  • First, we setup some variables. Notice here, I’ve arbitrarily chosen 100 to be our size. Play with this to adjust the grid completely, as we use “size” as the primary variable throughout the script.

var size:Number = 100;
var columns:Number = Math.ceil(Stage.width / size);
var rows:Number = Math.ceil(Stage.height / size);
var ball_move:Boolean = false;

  • Next, we create the easiest possible circle shape in the Drawing API. This is a clean little trick that flash lets you get away with, and for the purposes of this tutorial, it works fine.

var ball:MovieClip = this.createEmptyMovieClip(”ball”, this.getNextHighestDepth());
ball.lineStyle(70, 0×0, 100);
ball.lineTo(0, 1);
ball._x = 200;
ball._y = 200;

  • As you can see, we’re just creating a 1px line with a 70px stroke, which is by default rounded. This gives us our “ball”. We then move it to x/y 200, so we can easily access it.
  • Next, we setup the ball to be dragable on the stage. Notice in the function below that we’re triggering both the startDrag function AND we’re toggling a boolean for whether the ball can move. This helps to keep the code clean and easy to follow later (at least, for me it does).

ball.onPress = function () {
this.startDrag();
ball_move = true;
}

ball.onMouseUp = function () {
this.stopDrag();
ball_move = false;
}

  • TIP: notice we didn’t set ball.onRelease, instead we used ball.onMouseUp. The reason for this is onMouseUp will trigger when you release the mouse button no matter where the mouse is in relation to the ball. onRelease, however, will only trigger if the mouse button is released while the cursor is over the ball.
  • Next, we set the ball to snap to a grid. This is separate from the drag function just to keep things clean.

ball.onMouseMove = function () {
if (!ball_move) return;
this._x = _xmouse - _xmouse%size;
this._y = _ymouse - _ymouse%size;
updateAfterEvent();
}

  • NOTE: notice that I didn’t wrap the entire function in an “if” statement. Instead, I reversed my if statement to check, for a false, and return nothing if ball_move is false. This is another of my favorite tricks to keep the code clean and not unnecessarily indented.
  • ALSO NOTE: This is where the magic happens! If you don’t look closely, you’ll miss it!What we’re using here is something called “Modulus”. It simply returns the remainder of a division operation assuming no decimal part. What that means is simply that it handles the “SNAPPING” that makes this such a useful script. Essentially, it constantly calculates where it’s closest snap-point is, and moves the ball to it. We’re calculating the X value of the mouse modulus the size we want (the increment of the snap, if you will), and subtracting that from the current X value of the mouse (which is the reason we had to use onMouseUp earlier) which gives us the coordinates we need. Do the same with Y value, and you’ve got a snapping grid!
  • Next, we make the function to draw the grid itself onto the screen. You can comment out the next two steps to see this work without a grid. This function takes 3 variables. “who” is the dynamic name of the movie clip to be created. “to_x” and “to_y” tells the function where to place the square on the grid. This keeps the logic out of the function itself, allowing it to stay true to it’s name, and just “drawSquare”

function drawSquare (who:String, to_x, to_y) {
this.createEmptyMovieClip(who, this.getNextHighestDepth());
this[who].lineStyle(1, 0×999999, 100);
this[who].moveTo(0, 0);
this[who].lineTo(size, 0);
this[who].lineTo(size, size);
this[who].lineTo(0, size);
this[who]._x = to_x;
this[who]._y = to_y;
}

  • Finally, we draw the grid using a simple for loop

for (var c=0; c<columns; c++) {
for (var r=0; r<rows; r++) {
drawSquare(”c_”+c+”_r_”+r, c*size, r*size);
}
}

  • Notice here that we’ve simply used the variables that we created in the very beginning to now break apart the screen into a grid. Using those variables, we first run horizontally across the page, then vertically, calling our drawSquare function at each interval, creating a dynamic name for the square (I’ve used “c_1_r_4″ format so that I can use this code later to know exactly where the ball is at all times). Also, we calculate the x/y position of each square by multiplying it’s placement in the list (it’s column and row number) by the “size” variable we set at the top.

10 comments so far

  1. Ruslan December 19, 2006 10:25 pm

    Hi! This what I got , when I tried to run your program:

    **Error** Scene=Scene 1, layer=Layer 1, frame=1:Line 6: This type of quotation mark is not allowed in ActionScript. Please change it to a standard (straight) double quote.
    var ball:MovieClip = this.createEmptyMovieClip(”ball”, this.getNextHighestDepth());

    **Error** Scene=Scene 1, layer=Layer 1, frame=1:Line 7: ‘)’ or ‘,’ expected
    ball.lineStyle(70, 0×0, 100);

    Total ActionScript Errors: 2 Reported Errors: 2

  2. Simpsons225 January 25, 2007 2:32 pm

    Here is the complete script without errors:

    var size:Number = 100;
    var columns:Number = Math.ceil(Stage.width/size);
    var rows:Number = Math.ceil(Stage.height/size);
    var ball_move:Boolean = false;
    var ball:MovieClip = this.createEmptyMovieClip(”ball”, this.getNextHighestDepth());
    ball.lineStyle(70, 0×000000, 100);
    ball.lineTo(0, 1);
    ball._x = 200;
    ball._y = 200;
    ball.onPress = function() {
    this.startDrag();
    ball_move = true;
    };
    ball.onMouseUp = function() {
    this.stopDrag();
    ball_move = false;
    };
    ball.onMouseMove = function() {
    if (!ball_move) {
    return;
    }
    this._x = _xmouse-_xmouse%size;
    this._y = _ymouse-_ymouse%size;
    updateAfterEvent();
    };
    function drawSquare(who:String, to_x, to_y) {
    this.createEmptyMovieClip(who, this.getNextHighestDepth());
    this[who].lineStyle(1, 0×000000, 100);
    this[who].moveTo(0, 0);
    this[who].lineTo(size, 0);
    this[who].lineTo(size, size);
    this[who].lineTo(0, size);
    this[who]._x = to_x;
    this[who]._y = to_y;
    }
    for (var c = 0; c

  3. Simpsons225 January 25, 2007 2:36 pm

    Sry the one above was not complete, and remember to erase the doublequotes in flash and re-write them yourself. For some reason wordpress formats the text wrong :(

    var size:Number = 100;
    var columns:Number = Math.ceil(Stage.width/size);
    var rows:Number = Math.ceil(Stage.height/size);
    var ball_move:Boolean = false;
    var ball:MovieClip = this.createEmptyMovieClip(”ball”, this.getNextHighestDepth());
    ball.lineStyle(70, 0×000000, 100);
    ball.lineTo(0, 1);
    ball._x = 200;
    ball._y = 200;
    ball.onPress = function() {
    this.startDrag();
    ball_move = true;
    };
    ball.onMouseUp = function() {
    this.stopDrag();
    ball_move = false;
    };
    ball.onMouseMove = function() {
    if (!ball_move) {
    return;
    }
    this._x = _xmouse-_xmouse%size;
    this._y = _ymouse-_ymouse%size;
    updateAfterEvent();
    };
    function drawSquare(who:String, to_x, to_y) {
    this.createEmptyMovieClip(who, this.getNextHighestDepth());
    this[who].lineStyle(1, 0×000000, 100);
    this[who].moveTo(0, 0);
    this[who].lineTo(size, 0);
    this[who].lineTo(size, size);
    this[who].lineTo(0, size);
    this[who]._x = to_x;
    this[who]._y = to_y;
    }
    for (var c = 0; c

  4. Simpsons225 January 25, 2007 2:39 pm

    GOD DAMNIT!! WORD PRESS CUTS OFF MY COMMENTS!!! HERE IS THE LAST PART!!!!

    continues on same line as last piece of code from above:

  5. faisal March 28, 2007 3:04 pm

    nice tut, I had couple of errors, but got rid of them works fine now.

    Errors:
    1.lineStyle(1,0×0000ff,100): x in the rgb hexadecimal
    part has to be erased and rewritten.
    2.The double quotes have to be replaced.

    As well, the above code has ball clip created first.
    The
    createEmptyMovieClip(”ball”,this.getNextHighestDepth())
    along with it’s mouse handlers have to be placed after the for loop that generates the grid.
    Currently your code generates the grid and places circle behind it, this is due to getNextHighestDepth.

  6. Meshach March 30, 2007 9:35 pm

    faisal, Thanks for the feedback. We’ll try and get the bug fixes in the next version of this post. Thanks for reading.

  7. Benjamin April 17, 2007 10:13 pm

    Thanks, this was helpful. I think the problem with the copying of the code into Flash stems from the punctuation coming through as actual punctuation and not standard hash marks. I had to do a find replace on all the quotes and semi-colons.

    Ben

  8. Jasper May 19, 2007 5:32 pm

    Hey, thanks for this code. The _xmouse%size part is real usefull, though I still don’t really get how it works.

    By the way; I think this part:

    this._x = _xmouse - _xmouse%size;

    can be improved by adding half the gridsize to it:

    this._x = _xmouse - _xmouse%size + size/2;

    This makes the snapping feel a little better. Instead of having to move a hundered pixels to the right and having to move just 1 pixel to the left to make the ball move, you have to move it 50 pixels in either way to make it move if you add half the gridsize.

    Same goes for the y ofcourse

  9. Jasper May 19, 2007 5:36 pm

    Its me again.

    I was wondering why the ball was shaking when I moved the mouse, but not far enough to make the ball move.
    When I looked at this part:

    ball.onPress = function () {
    this.startDrag();
    ball_move = true;
    }

    I got it. Why are you using this.startDrag?
    You are setting the var ‘ball_move’ to ‘true’. And in the
    ball.onMouseMove you are checking for that var to see if it’s true. If so, the ball is allowed to move.
    So why use startDrag?
    You can leave that part out

    ball.onPress = function () {
    ball_move = true;
    }

    And it will move more fluidly

  10. Murten Saerbi August 8, 2008 3:21 pm

    Hey,

    I thought you might be interested in my post about snapping to grids in actionscript 3. You can check it out here: http://www.murtensaerbi.be/blog/index.php/2008/08/08/actionscript-30-snap-to-grid/

Leave a comment

Please be polite and on topic. Your e-mail will never be published.