Html Canvas - Translate, Rotate, Clip - Faster restores with using context.restore - html

After asking a question regarding animation speed a few days ago, the stackoverflow gang once again solved my problem. However, this has led to another question. [The more you know, the more you realise you don't know.]
Basically the fewer state changes to my canvas, the faster things will go. If I am just changing the fillStyle, then using ctx.save and ctx.restore is overkill, as all states are restored. Overkill = Slow. Instead just keep the oldvalue of fillStyle somewhere and put just that back in once you have finished.
So how do you do this for ctx.translate(x, y), ctx.rotate(angle) and ctx.clip()?
How can I restore these guys to their states before my changes WITHOUT having to use ctx.restore?

Your can untransform by using negative values.
ctx.translate(100,100);
// draw lots of stuff
ctx.translate(-100,-100);
ctx.scale(.75,.50);
// draw stuff
ctx.scale(-.75,-.50);
ctx.rotate(Math.PI/4);
// draw stuff
ctx.rotate(-Math.PI/4);
If you do multiple transforms, you must undo them in reverse order
ctx.translate(100,100);
ctx.scale(.75,.50);
ctx.rotate(Math.PI/4);
// draw lots of stuff
ctx.rotate(-Math.PI/4);
ctx.scale(-.75,-.50);
ctx.translate(-100,-100);
But when translating (moving) a few items, it's faster to use an offset instead of a transform.
strokeRect(20+100,20+100,50,30);
fillRect(20+100,20+100,50,30);
Clipping is semi-permanent so you must save/restore the entire context state to undo clip:
context.save();
// define a clipping path
context.clip();
// draw stuff
context.restore();
Transforms are done using a transformation matrix. Canvas gives you access to that matrix using the context.setTransform method.
scaleX=.75;
scaleY=.50;
skewX=0;
skewY=0;
translateX=100;
translateY=100;
context.setTransform(scaleX, skewX, skewY, scaleY, translateX, translateY);
// draw stuff
context.setTransform(-scaleX, -skewX, -skewY, -scaleY, -translateX, -translateY);
To also set the matrix for rotation, you must set a combination of the scale & skew values like this:
var radianAngle=Math.PI/4;
var cos=Math.cos(radianAngle);
var sin=Math.sin(radianAngle);
context.setTransform(cos,sin,-sin,-cos,0,0);
// draw stuff
context.setTransform(-cos,-sin,sin,cos,0,0);
To do rotation along with other transforms, just add the rotation values to the scale and skew values.
context.setTransform(scaleX+cos, skewX+sin, skewY-sin, scaleY-cos, translateX, translateY);
// draw stuff
context.setTransform(-scaleX-cos, -skewX-sin, -skewY+sin, -scaleY+cos, -translateX, -translate);

Just to correct the assumption of the question :
False : • The whole context state is saved/restored when using save()/restore() methods.
Let's be modest : An idea that comes to mind in 30 seconds is most likely to be found (and improved) by the developers of major Browsers. So truth is :
True : • Saving the context does almost nothing, and restore applies only on what just occurred.
If in doubt, you can look at the code, but it takes quite some time to be familiar with it (i did it with webKit's canvas => confirmed for this one ).
But it's much easier to look at the various jsperf made on the subject : they show that the gain when hand-saving/restoring one or two properties is moderate to small ==> only what changes is restored.
When hand-saving/restoring more things, then save and restore becomes faster because of Javascript's overhead.
(http://jsperf.com/save-restore-vs-translate-twice/4)
Another thing : talking about 'overkill' seems very exaggerated. Not only because, as seen before, context's save might be faster, but also because, best win is 2X, so we are talking about proudly taking 2ns instead of 4ns for the save. This has to be compared to the time taken for the draw, and might very well not be worth it.
Two last things :
• the bug risk induced by manually saving/restoring ('oops ! i forgot to restore that in that function !').
• the rounding errors that might occur (scale(x,x) => then scale(1/x, 1/x) )
In fact you can save time with no risk is by :
1) batching commands : whenever possible (it all depends on your app, really), batch all commands that expects a given context state.
2) similarly, you can define conventions/rules that prevents you to save/restore the context. For instance : 'always set fillStyle just before filling'. This way you never have to worry about current fillSyle. What you can do here also greatly depends on your app (and wether it's using external APIs or not), but can save a great deal of time for numerous draws.
So my advice would be to use the manual save/restore only for obvious simple case (ex: you just change globalAlpha), and to use conventions/rules to reduce the context state changes to a minimum.

Related

Is there a way to have smooth/subpixel motion without turning on smoothing on graphics?

I'm creating this 2D, pixel art game. When the camera follows the player (it uses easing), on the final approach, the position gets several subpixel adjustments.
If I have smoothing ON (on my graphic assets), the graphics look good (sharp. it's pixel art) but the subpixel motion is jerky/jumpy.
If I have smoothing OFF, the subpixel motion is smooth, but the pixel art graphics look blurry.
I'm using Flash player v21. I've tried this with Starling and with Flash's display list.
You have a pixelated object that is moving in increments of less than the pixel size, but you don't want to restrict your mathematical easing to integers, or even worse, factors of 8 or what have you. The solution I am using in my project for this exact issue is posted below (I just got it working last week!)
Concept
create a driver that is controlled by the easing using floating point numbers.
Allow this driver to then control where the actual display object is rendered. We can use a constraint to only allow the display object to render on your chosen resolution.
Code Example
// you'll put these lines or equivalent in the correct spots for your particular needs.
// SCALE_UP will be your resolution control. If your pixels are 4 pixels wide, use 4.
const SCALE_UP: int = 4;
var d:CharacterDriver = new CharacterDriver();
var c:Character = new Character();
c._driver = d; // I've found it useful to be able to reference the driver
d._drives = c; // or the thing the driver drives via the linked object.
// you don't have to do this.
then when you are ready to do your easing of the driver:
function yourEase(c:Character, d:CharacterDriver):void{
c.x = Math.ceil(d.x - Math.ceil(d.x)%SCALE_UP);//this converts a floating point number into a factor of SCALE_UP
c.y = Math.ceil(d.y - Math.ceil(d.y)%SCALE_UP);
Now this will make your character move around 4 pixels at a time, but still be able to experience easing!
The bit with the modulo (%) operator is the key. For instance, 102-102%4 = 100. 103-103%4 = 100. 104-104%4 = 104.
In case anyone is confused by that, look at what 102%4 does: 4 goes into 102 25 times with a remainder of 2. so 102%4 = 2. Then 102 - 2 = 100.
In your case, since the "camera" is following the player (i.e. the background is moving, right?) then you really need to apply drivers to everything in the background instead, but it is basically the same idea.
Hope this helps.
since you specifically mentioned the "final approach" i think your problem comes from the fact that the easing equations puts your graphics at fractional coordinates, especially while getting closer to the target, but you should also notice it during the rest of the animation.
depending on the easing "engine" that you're using you should be able to set a "round values" flag, so all the coordinates set will be integer values and not fractional
if that's not possible, find a way in your display objects to round the x and y values every time they change

Transparency issues with 3d particles and 3d models, libgdx

I got some strange issues with transparency and 3d particles. A short vid to illustrate:
https://youtu.be/ZHKI1X3MjhY
As you can see I have a 3d particle effect, fire burning. Inside it is a 3 model with no alpha blending and it shows just fine. then in the far distance there is a small skeleton (with blending and alphatest turned on) and it also shows just fine through the fire. Then I turn camera and look at the warrior skeleton and it just disappear and instead you see what is behind him. I turn camera again and the mage skeleton also vanishes, but you can see the trees a bit further away just fine and they have the exact same settings for blending and alpha test. If I move the character say 20 yards away it also starts showing through the fire effect.
So it seems to have something to do with distance from the 3d particle effect...
The 3d particle batch is an extended BillboardParticleBatch like this:
protected Renderable allocRenderable(){
BlendingAttribute ba=new BlendingAttribute(GL20.GL_SRC_ALPHA, GL20.GL_ONE,1f);
Renderable r = super.allocRenderable();
r.material = new Material( ba,
// new DepthTestAttribute(GL20.GL_LEQUAL, 0.0f, 0.5f, true),
// r.material.set(new FloatAttribute(FloatAttribute.AlphaTest, 0.0f),
TextureAttribute.createDiffuse(texture));
return r;
}
All the characters and the trees are created with following attributes:
if (alpha) {
FloatAttribute floatAttribute = new FloatAttribute(FloatAttribute.AlphaTest, 0.5f);
BlendingAttribute blendingAttribute = new BlendingAttribute(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA, 1f);
for (int i = 0; i < bulletEntity.modelInstance.materials.size; i++){
bulletEntity.modelInstance.materials.get(i).set(blendingAttribute);
bulletEntity.modelInstance.materials.get(i).set(floatAttribute);
}
}
The models are drawn first then the particles, I tried changing order but no difference. I have tried a lot of different setups for alphatest, depthtest and blendingattribute but can not find anything that works.
EDIT
I removed the Blending attribute from the 3d-models and now it looks as it should regarding the particle effect. However I need most materials on my character models to have blending set..
Anyone got any clue why this is happening when I enable blending?
I also tried to use the BillboardParticleBatch without extending it in case I had done something there but the effect then is even worse. All models with blending enabled appear in-front of the particle effect even though they stand behind it.
ModelBatch sorts your render calls (check this link, really, it is a must read), to avoid incorrect behavior (as you're experiencing). The actual sorting/rendering happens at the call to ModelBatch#end. By default it uses the DefaultRenderableSorter, which is a default implementation. Of course, because that implementation isn't aware of your scene, it might not fit exactly your needs.
The DefaultRenderableSorter tries to guess the location of each model based on their transformation matrix. Based on that location and the camera's location it will sort them so that:
First all opaque objects are rendered from front to back (because whatever is behind an opaque object isn't visible anyway, so that reduces unneeded calls to the fragment shader).
Secondly all transparent objects are rendered from back to front (because as soon as a transparent object is rendered then everything that is rendered after that and is behind it, will not be visible).
To decide whether an object is transparent, the BlendingAttribute#blended member is used. (So you could, if you really wanted to, set that member to false to force it to be treated (sorted) as if it was opaque)
So, the order in which you call ModelBatch#render is not necessarily the order in which they are actually executed. If you want to force to render whatever you've added to the batch in between, then call the ModelBatch#flush(). Of course, doing this a lot defeats some of the purpose of ModelBatch in the first place.
Instead you could implement your own RenderableSorter which has more knowledge about your scene and can therefor do a better job sorting than the default implementation. (however if flush() works for you and there's no other issue, then just flush might be the easiest solution for you).
That said, there a various other solutions you could try as well. E.g. the regions of the particles are fully transparent, so the fragment shader might as well discard those all together. Try adding FloatAttribute.AlphaTest with a value of 0.5f to the particles. If that messes with your blending then gradually reduce the value to e.g. 0.05f.
Also, you could add a DepthTestAttribute with depthMask set to false (new DepthTestAttribute(false)). This will prevent the particles from writing to the depth buffer. (but also might cause other things to show in front of the particles).

AS3 - Moving a clip (player) along a path

I'll try to explain my problem in a clear and short way.
I'm writing a grid-game. In this game when the player clicks somewhere, the player moves, along the grid with a path calculated by the computer (because there's obstacles that the player avoid : walls...) to the final point.
It's an isometric grid but it's like it was a basic 2D grid.
So I have my path, which is a Point Array (key points screen coordinates) :
path : [x=10,y=100],[x=40,y=172], .. etc.
To display the movement of the player, I tried to use tweens (TweenLite/TweenMax).
There's no option on TweenLite to wait for a tween to finish before starting the next. In any case the solutions seem complicated (shitty delays/onFinish:function).
The solution I found is acceptable : TweenLite provides a LinePath2D function which works exactly like I wished. The only problem is that it works with only one function (path is the complete array):
var pathanimate:LinePath2D = new LinePath2D(path);
pathanimate.addFollower(Player);
TweenMax.to(pathanimate, 1, { progress:1, ease:Linear.easeNone });
So I can't "touch" anything during the movement. INCLUDING the aspect of the sprite of the player that must changes with the direction during each step of the path (it would be more simple if it LinePath worked with a loop).
I don't know if this is clear (i'm french huh) but I see only but two options :
Keep the LinePath and have, on each frame, some kind of counter/timer that "measures" the direction of the player. Could be heavy in ressources, but keeps the LinePath2D that works very well alone
Find another solution
I'd be glad to hear your ideas and code !
Thanks in advance
Actually I can provide several solutions, but they will include many lines of code. Briefly speaking:
System of waypoints. You have algorithm to set collection of waypoints to your character, and render him (updating from the main gaming loop), and character reaches one waypoint after another by shifting them from the collection.
Working with tweens, append them (so there will be queue).
In both options, you can set new path for the character, all you need is simple logic to approximate current position of the character to the closest grid point, and calculate new path from there, It's very easy with basic grid systems, like:
closestGridCellX = Math.round(this.x / gridCellSize);
closestGridCellY = Math.round(this.y / gridCellSize);

hitTestPoint with 'shapeFlag=true' doesn't work in AS3

I simply added a sprite in AS3:
Sprite myspr = new Sprite();
myspr.addChild(mybitmap);
addChild(myspr);
Then I added an event. I did hitTestPoint for checking mouse is over my sprite or not.
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseCheck);
private function mouseCheck(evt:MouseEvent):void {
var xx:int = stage.mouseX;
var yy:int = stage.mouseY;
if(myspr.hitTestPoint(xx, yy, true)) {
...
// I'm checking mouse over here.
}
evt.updateAfterEvent();
}
Problem is: hitTestPoint gives true when mouse comes to full boundary box. But it should give true only if mouse comes on transparent isometric sprite.
Is there a solution for this, thanks in advance.
this should help. You need pixel perfect detection.
Actionscript 3 pixel perfect collision. How to? (learning purposes)
http://www.freeactionscript.com/2011/08/as3-pixel-perfect-collision-detection/
http://www.anotherearlymorning.com/2009/07/pixel-perfect-collision-detection-in-actionscript-3/
http://old.troygilbert.com/2009/08/pixel-perfect-collision-detection-revisited/
There's a few ways I usually do hit testing.
1) The easiest way is to use a an already made class that you can find online. Some people much smarter than me have created complex classes that allow for much better pixel to pixel interaction. The ones listed by Paras are all good. The problem with these is, for newer users, it can be hard to understand all the code and how to implement them. Usually it is simple once you understand what is going on though. You just replace your hit test with the class file and then enter in the correct arguments.
2) Another method is to actually go into the symbol, create a new layer, and then draw a rectangle(just turn the alpha down to 0%) where you want the hit test to work. This may seem like a stupid method, after all we are just confined to a square once again. BUT, it will actually work MUCH better than you'd expect. Just draw the square maybe slightly smaller than the height and width of your character you're detecting the hit test on, and you should be good to go. Give it an instance name (the hit square that is) and then just perform the hitTest with that square instead of the actual sprite. It works wonderfully and is a very simple solution. For what you're explaining though, this sounds like it might not work. This method is more from a gamer standpoint. It looks good when attacking and getting hit by enemies, but isn't necessarily exact. Also, if you want to do this with two characters (maybe a large attack hitting an enemy) simply draw a hit box for both sprites. This is probably a little more basic than using a pre-made pixel perfect hit detection test, but it works extremely well and takes only a few minutes.

How to slow-down bone animation through code?

I've drawn a tentacle on the screen with ik bones that I want to bend against the player when he's close enough. I have gotten this to work, but the animation is happening too fast, and even though I throw all kinds of:
myMover.limitByTime = true;
myMover.timeLimit = 4000;
myMover.limitByIteration = true;
myMover.iterationLimit = 1;
myMover.limitByDistance = true
myMover.distanceLimit=1000;
code I think might slow it down, it doesn't slow down at all. How do I fix this? :S Im not sure if this is good form but I also have a somewhat related question: How do i get flash to recognize the armature if I don't have it set to runtime as opposed to authortime? Because if I set it to authortime then the following code returns null:
tentacle = IKManager.getArmatureByName("tentacle");
trace(tentacle);
Now the problem with runtime is that some clever users might be able to manipulate my tentaclemonster with the mouse :|
As I understand the IKMover limits, they're intended to control maximum processing time, not the animation. You can adjust the speed of each bone by selecting it in the IDE and looking under the "Location" properties.
I'd suggest leaving the speed at 100% and removing the limits though, then animating the target point directly (and calling moveTo on each frame). That way you have more precise control over the speed.