Related
If I have two instances called block1 and block2. And they move off the stage. It scrolls down the y position and it respawns back on top. But I don't want the x/y position colliding with the other blocks? I want it to respawn back to position, but I want it randomized but at the same time I don't want it touching each other?
Heres my code:
if (block1.y > stage.stageHeight)
{
block1.y = -550;
block1.x = (Math.floor(Math.random() * (maxNum - minNum + 5)) + minNum);
}
I'm pretty sure I'm calculating the respawn coordinates the wrong way, but I'm not sure how to put it in a random x and y position without colliding with other blocks.
A very simple method can be just to spawn your box, do a collision check, then if collision, remove and respawn and recheck until you find an empty spot where it fits
This is obviously quite inefficient, but is pretty simple to implement quickly if you have some sort of collision detection already working. Keep in mind if there is no spot that it can fit in, then it'll loop forever so you may want to set a max try count or something of that sort.
How fast/well it'll actually work will depend on if the spawn area is pretty sparse or pretty dense, which will increase/decrease the percentage that it'll find a good empty spot the first few times.
There is some room for improvement, going down this path though, such as if your collision detection system gives a minimum translation vector, you could just move the new shape over and use that position to spawn.
Other simple methods could involve keeping track of known occupied positions and adjusting your random range to avoid those values.
This page shows some animations in HTML5 canvas. If you look at the source of the scroller, there's a statement to save the context after clearing the rectangle and restoring it after the animation. If I substitute the restore statement with another ctx.clearRect(0, 0, can.width, can.height statement, nothing works. I thought the restore is restoring the cleared rectangle but it seems its restoring more info. What's that extra info that's needed for the next frame?
I am not looking for HTML5 textbook definitions of Save and Restore but I want to understand why they are needed in this specific example.
UPDATE
It's frustrating to get an answer where I specifically already mentioned in the question I don't want to get the definitions of save() and restore(). I already know Save() saves the state of the context and Restor()e restores it. My question is very specific. Why is restore() used in the manner in the example when all the Save did is saved an empty canvas. Why is restoring an empty canvas not the same as clearing it?
Canvas state isn't what's drawn on it. It's a stack of properties which define the current state of the tools which are used to draw the next thing.
Canvas is an immediate-mode bitmap.
Like MS Paint. Once it's there, it's there, so there's no point "saving" the current image data, because that would be like saving the whole JPEG, every time you make a change, every frame...
...no, the state you save is the state which will dictate what coordinate-orientation, dimension-scale, colour, etc, you use to draw the NEXT thing (and all things thereafter, until you change those values by hand).
var canvas = document.createElement("canvas"),
easel = canvas.getContext("2d");
easel.fillStyle = "rgb(80, 80, 120)";
easel.strokeStyle = "rgb(120, 120, 200)";
easel.fillRect(x, y, width, height);
easel.strokeRect(x, y, width, height);
easel.save(); // stores ALL current status properties in the stack
easel.rotate(degrees * Math.PI / 180); // radians
easel.scale(scale_X, scale_Y); // any new coordinates/dimensions will now be multiplied by these
easel.translate(new_X, new_Y); // new origin coordinates, based on rotated orientation, multiplied by the scale-factor
easel.fillStyle = "gold";
easel.fillRect(x, y, width, height); // completely new rectangle
// origin is different, and the rotation is different, because you're in a new coordinate space
easel.clearRect(0, 0, width, height); // not even guaranteed to clear the actual canvas, anymore
easel.strokeRect(width/2, height/2, width, height); // still in the new coordinate space, still with the new colour
easel.restore(); // reassign all of the previous status properties
easel.clearRect(0, 0, width, height);
Assuming that you were only one state-change deep on the stack, that last line, now that your canvas' previous state was restored, should have successfully cleared itself (subpixel shenanigans notwithstanding).
So as you can see, it has very, VERY little to do with erasing the canvas.
In fact, it has nothing to do with erasing it, at all.
It has to do with wanting to draw something, and doing the basic outlining and sweeping colours/styles, and then manually writing in the colours for the smaller details on top, and then manually writing all of the styles back the way they were before, to go back to sweeping strokes for the next object, and on and on...
Instead, save general states that will be reused, create a new state for smaller details, and return to the general state, without having to hard-code it, every time, or write setter functions to set frequently-used values on the canvas over and over (resetting scale/rotation/affine-transforms/colours/fonts/line-widths/baseline-alignment/etc).
In your exact example, then, if you're paying attention, you'll see that the only thing that's changing is the value of step.
They've set the state of a bunch of values for the canvas (colour/font/etc).
And then they save. Well, what did they save?
You're not looking deep enough. They actually saved the default translation (ie: origin=0,0 in original world-space).
But you didn't see them define it?
That's because it's defined as default.
They then increase the step 1 pixel (actually, they do this first, but it doesn't matter after the first loop -- stay with me here).
Then they set a new origin point for 0,0 (ie: from now on, when they type 0,0 that new origin will point to a completely different place on the canvas).
That origin point is equal to x being the exact middle of the canvas, and y being equal to the current step (ie: pixel 1 or pixel 2, etc... and why the difference between starting at 0 and starting at 1 really doesn't matter).
Then what do they do?
They restore.
Well, what have they restored?
...well, what have they changed?
They're restoring the point of origin to 0,0
Why?
Well, what would happen if they didn't?
If the canvas is 500px x 200px, and it starts at 0,0 in our current screen space... ...that's great...
Then they translate it to width/2, 1
Okay, so now when they ask to draw text at 0,0 they'll actually be drawing at 250, 1
Wonderful. But what happens next time?
Now they're translating by width/2, 2
You think, well, that's fine... ...the draw call for 0,0 is going to happen at 250, 2, because they've set it to clear numbers: canvas.width/2, 2
Nope. Because current 0,0 is actually 250,1 according to our screen. And one translation is relative to its previous translation...
...so now you're telling the canvas to start at it's current-coordinates' 0,0 and go left 250, and down 2.
According to the screen (which is like a window, looking at the map, and not the map, itself) we're now 500px to the right, and 3 pixels down from where we started... And only one frame has gone by.
So they restore the map's coordinates to be the same origin as the screen's coordinates (and the rotation to be the same, and the scale, and the skew, etc...), before setting the new one.
And as you might guess, by looking at it, now, you can see that the text should actually move top to bottom. Not right to left, like the page says...
Why do this?
Why go to the trouble of changing the coordinate-system of the drawing-context, when the draw commands give you an x and y right there in the function?
If you want to draw a picture on the canvas, and you know how high and wide it is, and where you'd like the top-left corner to be, why can't you just do this:
easel.drawImage(myImg, x, y, myImg.width, myImg.height);
Well, you can.
You can totally do that. There's nothing stopping you.
In fact, if you wanted to make it zoom around the screen, you could just update the x and y on a timer, and call it a day.
But what about if you were drawing a game character? What if the character had a hat, and had gloved hands, and big boots, and all of those things were drawn separate from the character?
So first you'd say "well, he's standing at x and y in the world, so x plus where his hand is in relation to his body would be x + body.x - hand.x...or was that plus..."
...and now you have draw calls for all of his parts that are all looking like a notebook full of Grade 5 math homework.
Instead, you can say: "He's here. Set my coordinates so that 0,0 is right in the middle of my guy". Now your draw calls are as simple as "My right hand is 6 pixels to the right of the body, my left hand is 3 pixels to the left".
And when you're done drawing your character, you can set your origin back to 0,0 and then the next character can be drawn. Or, if you want to attempt it, you can then translate from there to the origin of the next character, based on the delta from one to the other (this will save you a function call per translation). And then, if you only saved state once the whole time (the original state), at the end, you can return to 0,0 by calling .restore.
The context save() saves stuff like transformation color among other stuff. Then you can change the context and restore it to have the same as when you saved it. It works like a stack so you can push multiple canvas states onto the stack and recover them.
http://html5.litten.com/understanding-save-and-restore-for-the-canvas-context/
I'm not very good with radial calculations, I can't imagine thus I can't be sure. I need some explanation of Math.atan2() thing, please.
Usual task - to make an object rotate after the mouse. I get the differences, get the angle, I see angles in the text areas and DIRECTLY the object does follow the mouse. What I need now is everything to be smooth. I need angles to be 0-360 but after 180 object rotation becomes -180 and counts backwards, and mouse rotation becomes -90 after 270 and also counts back to 0.
More deeply, I want a smooth rotation, it means a set speed of say 2 per frame, to meet the mouse angle the shortest way. It takes to set conditions and I can't do that cause I don't even understand the logic of these values. They are almost random! I don't need it to be done or copied, I need to understand to move on so if you could please explain how does it work and what I do wrong...
Code is simple:
angle = Math.atan2(deltaY,deltaX)/(Math.PI/180) + 90; //+90 cause it lacks it to look at the mouse//
Object01.rotation = angle;
So the problem is I don't even get how it works... if 2 values are different the object can't point at the mouse but it does. Numbers lie and if I need something based on these numbers it will be wrong. Very wrong... Need organization. Meaning I want everything to be ready for further coding that will be based on the rotations to not jump up and down cause of misfit ends.
Add: Explanation of how does it happen, what I described above. Why such a chaos of the values? And an advice on how could I arrange it for further coding, just as I said. Animation alone wont work if I want to make rotation an element of important events such as shooting direction and aiming speed. Or changes of speed rotation of a lockpicked lock. Or anything much more complicated that wont work if I don't make straight and clear values: from A to Z, from 1 to 10, no 8s between 2 and 3, no R before B, no mouse angle 270 when object facing it -90 when they both started from 0 and reached 180 together.
Oh, and as I said, mouse facing works but when I try to make a certain speed of chasing mouse the shortest way it turns the object wrong directions in all 4 quarters. I assume it's also about this arctangens thing that has issues with delta values becoming negative in different quarters. And when I change it, some other value goes wrong... So I need to know exactly what I'm doing to know what's wrong and how to fix it. So yep. Need explanation. Please.
Add2: angleZ = Math.atan2(oppSide,adjSide)/(Math.PI/180);
So I divided rotation to 4 quarters, for each I count atan as opp. side to adj. side, then add 90, 180 and 270 respectively. My mouse rotation does 360, but the object that follow through simple object.rotation = angleZ; still goes to 180, then from -180 to 0 on the left side. Why does it ignore the simple command? The rotation fits but I need it to be equal, no surprises! Why is it happening? How can a number I directly set to be equal to another number as a base of the action change itself to the one of same rotation but completely different number? It doesn't even know it's degrees! It's as simple as "object.rotation, please be equal to the number I choose!"
It's just different coordinate systems. Like how x starts at 0 at the left of the stage, goes +x to the right, and -x to the left, object rotation starts at 0˚ pointing up, and goes +180˚ clockwise and -180˚ anti-clockwise.
Math.atan2 happens to start at 0 pointing left (-x), and go +270˚ clockwise and -90˚ anti-clockwise, which is annoying, but it just means you have to convert between coordinate systems by adding 90˚.
You can spin something around over and over of course, so the numbers jump so that they always stay within the same range, because 361˚ is the same as 1˚, and -270˚ is the same as 90˚. You can tell an object to rotate outside of the -180˚ to 180˚ range, and it will normalise the rotation to within those values.
As mitim described, to smoothly animate rotation you'll either need to use Event.ENTER_FRAME, a Timer, or a tweening library like TweenLite. Most tweening libraries can work out the shortest rotation direction for you, but otherwise you can simply calculate both and see which is smaller.
As an idea, since it seems like you know the angle you need to rotate towards and it's direction, would it be easier to just animate towards that angle to get your smooth rotation? Basically treat it like any other animatable property and just add on your rotation speed (2 degrees it looks like) per frame tick, until it reaches the desired rotation.
Find angle amount needed to rotate by
Figure out if clockwise or counter clockwise direction and set the rotation amount. This can be figured out by checking if the angle is great then 180 / positive or negative
Add the rotation amount * direction every frame tick, until the desired rotation is less then or equal to the rotation amount per frame
Set rotation to desired rotation
I need to do an endless horizontal scroll of elements within a parent MovieClip.
No matter what ever method I try, an element of 'drift' occurs and eventually the elements start to overlap.
I've tried using relative recursive tweening for each element according
but this method seems prone to quite a bit of error after repeated starts and stops.
//CODE START
function doScroll():void {
TweenLite.to(this, .25, {x:"20", ease:Linear.easeNone,onUpdate:checkPos,onComplete:doScroll});
}
//CODE END
I've reverted to doing absolute tweens to a predefined position using a contant speed. This seems to be more accurate but still some 'drift' occurs.
//CODE START
//_dest is predefined
var speed:Number = 500;
var dist:Number = this.x - _dest;
var distAbs:Number = dist < 0 ? -dist : dist;
//kludge to get constant velocity by recalculating time every frame
_time = distAbs / speed;
TweenLite.to(this, _time, {x:_dest, ease:Linear.easeNone,onComplete:reset});
//CODE END
Thought this should be very simple.
Can anyone point me to any possible tutorials or make any suggestions?
Any help appreciated.
Solution/Discussion at http://forums.greensock.com/viewtopic.php?f=1&t=6800
(warning: this is gonna require a rather lengthy explanation...)
It's a logic problem in your code. In your onUpdate, you were running conditional logic such that if the x position is beyond 980, it kills the tween and moves x back to -980 and starts things over. You're doing that for each individual item, each of which begins at a different position. That initial position affects when it crosses that threshold, thus when they reposition, the offsets are different.
For example, let's say item1 starts at an x position of 0 and item2 starts at 490 and both start moving at 400 pixels per second and your frame rate is 60, thus they'll move 6.66666 pixels per frame. Item1 will take 147 frames to hit 980. However, item2 will take 74 frames (actually 73.5, but there's no such thing as a half-frame) to cross the 980 threshold, but when it does so it will be at an x position of 983.333333. At that point it jumps back to -980 due to your conditional logic, but notice that it traveled an EXTRA 3.333333 pixels. You intended Item1 and item2 to travel at the exact same velocities and they do during the tween, but your onUpdate logic is misaligning them on the reposition such that in the end, some are traveling more than others which affects their overall velocity.
Another issue has to do with the fact that Flash rounds x/y coordinates of DisplayObjects to the nearest 0.05. So when you do your manual reposition (wrap), small rounding errors creep in. For example, let's say TweenLite sets the exact x value to 980.799. Flash will actually round that to 980.75. Then when you reposition it like this.x -= 980 and then tween it, the value would have just lost almost 0.05 pixels on that round. Do that many times and it can add up to a half-pixel or whole pixel (or more). All your items are crossing the threshold at slightly different spots, thus the rounding errors aren't the same, thus you start seeing slight variances in the spacing. Again, this is NOT an issue with the tweening engine. You'll see that the engine itself sets the values correctly, but Flash rounds them internally when applied to DisplayObjects.
A solution was posted at http://forums.greensock.com/viewtopic.php?f=1&t=6800 that includes an FLA and support files.
As others have suggested, I'd recommend having a single chunk of code that manages ALL the items that you're aligning/scrolling. It would lay things out from a single reference point so that everything lines up perfectly every time. You could tween a getter/setter that applies the logic. I use that technique all the time and it works great. You can see a smaller-scale example in the code I attached in the above URL (the scrollX getter/setter in ItemBase.as)
If you will be tweening all background elements at the same rate indefinitely on a single dimension - why not use a Timer and bypass tweening libraries entirely?
I'm drawing rectangles at random positions on the stage, and I don't want them to overlap.
So for each rectangle, I need to find a blank area to place it.
I've thought about trying a random position, verify if it is free with
private function containsRect(r:Rectangle):Boolean {
var free:Boolean = true;
for (var i:int = 0; i < numChildren; i++)
free &&= getChildAt(i).getBounds(this).containsRect(r);
return free;
}
and in case it returns false, to try with another random position.
The problem is that if there is no free space, I'll be stuck trying random positions forever.
There is an elegant solution to this?
Let S be the area of the stage. Let A be the area of the smallest rectangle we want to draw. Let N = S/A
One possible deterministic approach:
When you draw a rectangle on an empty stage, this divides the stage into at most 4 regions that can fit your next rectangle. When you draw your next rectangle, one or two regions are split into at most 4 sub-regions (each) that can fit a rectangle, etc. You will never create more than N regions, where S is the area of your stage, and A is the area of your smallest rectangle. Keep a list of regions (unsorted is fine), each represented by its four corner points, and each labeled with its area, and use weighted-by-area reservoir sampling with a reservoir size of 1 to select a region with probability proportional to its area in at most one pass through the list. Then place a rectangle at a random location in that region. (Select a random point from bottom left portion of the region that allows you to draw a rectangle with that point as its bottom left corner without hitting the top or right wall.)
If you are not starting from a blank stage then just build your list of available regions in O(N) (by re-drawing all the existing rectangles on a blank stage in any order, for example) before searching for your first point to draw a new rectangle.
Note: You can change your reservoir size to k to select the next k rectangles all in one step.
Note 2: You could alternatively store available regions in a tree with each edge weight equaling the sum of areas of the regions in the sub-tree over the area of the stage. Then to select a region in O(logN) we recursively select the root with probability area of root region / S, or each subtree with probability edge weight / S. Updating weights when re-balancing the tree will be annoying, though.
Runtime: O(N)
Space: O(N)
One possible randomized approach:
Select a point at random on the stage. If you can draw one or more rectangles that contain the point (not just one that has the point as its bottom left corner), then return a randomly positioned rectangle that contains the point. It is possible to position the rectangle without bias with some subtleties, but I will leave this to you.
At worst there is one space exactly big enough for our rectangle and the rest of the stage is filled. So this approach succeeds with probability > 1/N, or fails with probability < 1-1/N. Repeat N times. We now fail with probability < (1-1/N)^N < 1/e. By fail we mean that there is a space for our rectangle, but we did not find it. By succeed we mean we found a space if one existed. To achieve a reasonable probability of success we repeat either Nlog(N) times for 1/N probability of failure, or N² times for 1/e^N probability of failure.
Summary: Try random points until we find a space, stopping after NlogN (or N²) tries, in which case we can be confident that no space exists.
Runtime: O(NlogN) for high probability of success, O(N²) for very high probability of success
Space: O(1)
You can simplify things with a transformation. If you're looking for a valid place to put your LxH rectangle, you can instead grow all of the previous rectangles L units to the right, and H units down, and then search for a single point that doesn't intersect any of those. This point will be the lower-right corner of a valid place to put your new rectangle.
Next apply a scan-line sweep algorithm to find areas not covered by any rectangle. If you want a uniform distribution, you should choose a random y-coordinate (assuming you sweep down) weighted by free area distribution. Then choose a random x-coordinate uniformly from the open segments in the scan line you've selected.
I'm not sure how elegant this would be, but you could set up a maximum number of attempts. Maybe 100?
Sure you might still have some space available, but you could trigger the "finish" event anyway. It would be like when tween libraries snap an object to the destination point just because it's "close enough".
HTH
One possible check you could make to determine if there was enough space, would be to check how much area the current set of rectangels are taking up. If the amount of area left over is less than the area of the new rectangle then you can immediately give up and bail out. I don't know what information you have available to you, or whether the rectangles are being laid down in a regular pattern but if so you may be able to vary the check to see if there is obviously not enough space available.
This may not be the most appropriate method for you, but it was the first thing that popped into my head!
Assuming you define the dimensions of the rectangle before trying to draw it, I think something like this might work:
Establish a grid of possible centre points across the stage for the candidate rectangle. So for a 6x4 rectangle your first point would be at (3, 2), then (3 + 6 * x, 2 + 4 * y). If you can draw a rectangle between the four adjacent points then a possible space exists.
for (x = 0, x < stage.size / rect.width - 1, x++)
for (y = 0, y < stage.size / rect.height - 1, y++)
if can_draw_rectangle_at([x,y], [x+rect.width, y+rect.height])
return true;
This doesn't tell you where you can draw it (although it should be possible to build a list of the possible drawing areas), just that you can.
I think that the only efficient way to do this with what you have is to maintain a 2D boolean array of open locations. Have the array of sufficient size such that the drawing positions still appear random.
When you draw a new rectangle, zero out the corresponding rectangular piece of the array. Then checking for a free area is constant^H^H^H^H^H^H^H time. Oops, that means a lookup is O(nm) time, where n is the length, m is the width. There must be a range based solution, argh.
Edit2: Apparently the answer is here but in my opinion this might be a bit much to implement on Actionscript, especially if you are not keen on the geometry.
Here's the algorithm I'd use
Put down N number of random points, where N is the number of rectangles you want
iteratively increase the dimensions of rectangles created at each point N until they touch another rectangle.
You can constrain the way that the initial points are put down if you want to have a minimum allowable rectangle size.
If you want all the space covered with rectangles, you can then incrementally add random points to the remaining "free" space until there is no area left uncovered.