I'm trying to find a clean way to "move the camera" of a canvas element.
This for my prototype game (side scroller). I'd love to think there's a better solution than moving the whole set of nodes to simulate a "camera" moving around.
Am almost certain to have read a simple how-to (using offsets?) but the fact I don't find anything like that starts to raise doubts... have I imagined reading that!?
Thanks to help me clarify...
J
Presumably you redraw your whole game scene 30 times a second (more or less)
You need to redraw your whole game scene but first translate the Canvas context by some offset.
context.translate(x,y) is precisely what you want. You'll want to read up on the use of that as well as the save() and restore() methods.
When you translate the context, everything drawn afterwards is shifted by that amount.
So you constantly draw something (maybe an enemy) at 50,50 using drawImage(badguy,50,50). Then the player moves, which changes the x of translate to -1 (because the player is moving to the right) instead of 0. You still draw the enemy sprite with the command drawImage(badguy,50,50), but when you draw it the enemy shows up as if it were at 49,50 because of the context.translate(-1,0) command shifting everything before its drawn.
Of course when you get into performance you'll want to be making sure that you are only ever drawing things that can actually be seen on the screen! If your are far down the level with context.translate(-2000,0), you dont want to be drawing objects at 50,50 anymore, only ones that intersect the viewable area.
Related
I'm trying to write a simple flash game like Space Invaders. When the ship shoots I check if bullet hits the enemy with simple if like below:
if (bullet.hitTestObject(enemy)) {
var explosion = new Explosion(enemy.x, enemy.y);
stage.addChild(explosion);
explosions.push(explosion);
//Rest of logic like removing bullet and enemy from stage
}
What I expected to see was an Explosion instance appearing somewhere around the coords where bullet hit the enemy and that it would stay in place. Instead explosion seems to be appearing completely elsewhere and is moving in the same direction the enemy was (opposite direction to bullet). It seems that my assumption about successfully getting coordinates in a way presented above isn't right. Is there any other way to get it at least approximately? It doesn't have to be pixel-perfect, but I don't want explosion to appear on the other side of the stage.
Thanks for any suggestions.
It's not about getting the coordinates but having in mind where your children are added.
If the container is moved around, and you add the explosion to other container, the coordinates will mess up - each object has an internal coordinate system starting from 0,0.
Best advise would be to manually understand where's the difference and where are your children added. Common way to fix this is to add all your children at 0,0. This will give you full control and nevertheless an idea of how Flash works.
If you are unable to do so, you can always use globalToLocal and localToGlobal methods that will help you convert those coordinates to a global (Stage) ones, and vise versa.
I'm working on a small game in Flash (AS3) that generates levels using small tiles.
The location,width,height and other properties of the tile are stored in an array upon generation. Each tile is added to the same container movieclip. When all tiles have been generated, the container movieclip is converted to a bitmap, all tiles are removed from the stage, and the original container is removed from the stage and then deleted. The bitmap is the only remaining "graphic" on the stage, aside from the player.
This yields far better performance than allowing all 60,000+ tiles to be rendered individually during gameplay; however, the framerate still reduces as the number of tiles is increased. This makes no sense, as all the tiles are added to a single bitmap that is always the same size, regardless of the amount of tiles.
With 20,000 tiles during generation, the game runs at the full FPS, but with 60,000 it runs relatively choppy, probably around 4-5 FPS.
I've removed collision detection and any/every script that runs through the tile array to rule out the possibility that some other CPU intensive part of the script is lagging the framerate.
Any idea why, despite the fact that all tiles have been removed from the stage and their container is deleted, the game is still running slow even though the background bitmap contains the same amount of data regardless of the number of tiles generated?
Here is the last part of the level generation algorithm which converts the container movieclip to a bitmap and then removes all the tiles:
var temp_bitmap_data:BitmapData = new BitmapData(this.stage_background_mc.width,this.stage_background_mc.height);
temp_bitmap_data.draw(this.stage_background_mc);
this.stage_background_bitmap = new Bitmap(temp_bitmap_data);
main_class.main.stage.addChild(this.stage_background_bitmap);
for (var block in blocks_array)
{
//every block in blocks_array has a child that is the actual graphic of the tile
blocks_array[block]["block"].removeChild(blocks_array[block]["block"].getChildAt(0));
if (blocks_array[block]["type"] == "bg_block")
{
this.stage_background_mc.removeChild(blocks_array[block]["block"]);
blocks_array[block]["block"] = null;
}
if (blocks_array[block]["type"] == "path_block")
{
//path_blocks have a second child in addition to the first one that's already been removed. the second child is the only other child
blocks_array[block]["block"].removeChild(blocks_array[block]["block"].getChildAt(0));
this.stage_background_mc.removeChild(blocks_array[block]["block"]);
}
}
this.stage_background_mc = null;
[Edit]
Here is a picture of the game to give you a better idea of what's going on:
[Update:]
Even removing the final created bitmap from the stage, ending up with only 1 child on stage, and setting that removed bitmap to null doesn't improve the speed.
A couple of thoughts.
First, you're sorta half taking advantage of AS3's quick work with blitting. You've got the right idea about having only one single Bitmap on the stage, but the steps before (adding DisplayObjects to a MovieClip and the doing draw on that MovieClip) isn't the fastest process. For one thing, BitmapData.draw is slower than BitmapData.copyPixels (here is a post on that). One of the best ways to get speed is to pre-blit all of your graphics, and store them as BitmapData. Then, keep Object references (containing position, etc) for each graphic. In your render loop, run through each object, and use copyPixels to copy the graphic's pixel information to the appropriate positon in your single, on-stage Bitmap. This way, all slow BitmapData.draw commands have happened upfront, and now you are just pushing pixels, which Flash is really fast at.
That process isn't without its downsides, however. For instance, you can't do easy transforms with it. You'd have to pre-blit all frames of a rotation, for instance, in order to rotate the individual graphic (since you can't rotate a BitmapData, and you aren't using draw on a DisplayObject). Still, it is mighty fast if you can live with the limitations.
Here is a post that serves as a tutorial of the process I explained above. It is old, but informative none-the-less.
Finally, 60,000 is a very large number. Flash isn't exactly a "speed demon." No matter how optimized, eventually you'll hit a limit. Just something to keep in mind.
Oh, and this additional post gives some great tips on performance in Flash.
I'm working on a side scroller, and for the enemy I'm making a turret. I'm trying to make the turret aim at the player but I cant seem to get it right. Below is a rough sketch of what I want to achieve:
I want the barrel (dark blue), to aim/rotate to its pointing at the player.
I have uploaded a YouTube video of my scene:
http://www.youtube.com/watch?v=eeP47VoX9uA&feature=youtu.be
This is what I have so far (loop):
function enterFrameHandler(e : Event) : void{
_turretBarrel.rotation = Math.atan2(enTarget.x, enTarget.y) * 180/Math.PI;
}
What this does is only rotate the barrel when I jump, and the barrel isn't even aiming at the player, also the barrel doesn't change rotation when I walk on the other side of the turret.
My enTarget.x is always central to the stage and the scene (including the turret) moves around the player left and right (x)... Only the enTarget.y moves (jump/high platform).
I'm slightly new to Flash and ActionScript. If anyone could help me out, or point me in the right direction then that would be great.
Thanks
1) Make sure you got the right numbers and the position of the avatar and the turret are in the same coordinate space. A simple trace of each would do. In this case you probably want the world (relative to stage) position of both clips. Make sure they make sense compared to top left corner of the screen (0, 0).
2) Remember that _turretBarrel.rotation is a rotation that ranges from -180 to 180 so this would need to be taken into consideration when calculating angles.
3) Make sure you use the corresponding degrees/radians where appropriate.
4) force focus on avatar, run the game and see if the bounds looks ok. Then do the same thing with the turret.
Another good thing in general for debugging purposes is to setup some kind of debug graphics. i.e. draw a line of what you think is the direction vector to verify your numbers and calculations.
On a side note:
This is what the majority of programming is; Debugging. Assume nothing but hard facts, get your numbers from the debugger (probably quicker), or trace output. If you're still using the horrible flash professional IDE. I would really recommend getting one with a proper debugger like FlashDevelop (free) or Flash Builder (commercial)
Oliver, it looks like you are calculating the tangens of wrong angle (between player and X-axis). You need something like the following:
function enterFrameHandler(e : Event) : void{
_turretBarrel.rotation = Math.atan2(enTarget.x - barrel.x, enTarget.y - barrel.y) * 180/Math.PI;
}
I'm working on a game that allows players to click on a card and drag it across the screen in any random direction. There are a total of 64 100x80 overlapping cards on a 800x800 canvas at any one time and each one is a procedural draw. As some of you probably suspect, canvas doesn't like redrawing that entire canvas for every move. To work around this, I'm utilizing a buffer canvas to draw the card and then attempting to paint that buffer canvas to the main canvas using drawImage(). To ensure there is no drawing buildup, I clear the region of the canvas associated with where I plan to drawImage() using a clearRect().
The problem I'm experiencing is that because the (x,y) coordinates used for the clearRect() and drawImage() are coming from the location of the mouse, if the user moves too fast, the coordinates will differ from the time drawImage() was last executed to the time clearRect() is called during the next draw sequence. The result is residual draw from the last sequence - proportionate to how fast the card is being dragged.
I tried maintaining the (x,y) coordinates from the drawImage() and using those (instead of the current mouse location) for the clearRect() in the next sequence. However, now instead of residual draw being shown, residual we have residual clear (erase).
Thoughts on how I can address this?
NOTE: My animation rate is handled using RequestAnimationFrame and not SetInterval().
Assuming your canvas is static during the drag drop operation, a pretty easy way to get a good increase in performance would be to just cache the rendering.
In other words, when a drag drop operation begins, save the current canvas into another one. Stop all rendering except for the one involved with dragging the card. Now, whenever you need to repaint, simply repaint from your copy-canvas. Since you're basically just copying from one to another, it should be pretty fast.
On each processing cycle, you would take the current position of the dragged card, fill that with data from the copy, then redraw the dragged card in the new position.
Other approaches you could try would be to use some kind of a placeholder for the drag. For example, consider using a same-sized DIV which you display while dragging. This should have the benefit of not requiring to touch the canvas while dragging and thus also run a better performance for it.
I'm currently working on a game with html5/js, using box2dweb for the collision but I'm running into the issue where I am required to scroll the level with the player. Box2d renders directly to the 2d context so I think in it's current state there is no way to shift the render portion of the canvas?
In as3 you could just put everything in a movieclip and change it's position accordingly.
So, is it possible in anyway to have control of a camera of some sorts or the render portion of the canvas object to shift it's 'position' to keep the player centered at all times?
Thanks in advance,
M0rph3v5
Box2D, by itself, doesn't draw anything, it just calculates positions/collisions.
It offers the so-called "Debug Drawing", but it's purpose is... debug.
Anyway you could start from there to add all the needed features.
http://code.google.com/p/box2d/wiki/FAQ
Yeah I figured, turns out I had to use a context.translate right before the debugdraw as well to 'shift' everything. Got it working nicely now.
EDIT:
The code I'm currently using
context.save();
context.translate(-1*xpos+(canvas.width/2),-1*ypos+(canvas.height/2));
context.rotate(cars[carid].angle);
context.drawImage(carSprite, -carspritewidth/2, -carspriteheight/2);
context.restore();
where xpos and ypos are the x and y positions of the car, after that i just draw the actual car sprite at 0,0 (with the carsize divided as the center).