Reset/lock html5 canvas position to center on player when panning - html

I cannot figure out how to do this. I was translating the character and background at the same time, but if there's any hiccup, the character position slides out of the viewable area of the canvas, and I need the canvas translation to be based off the position of the player (hero.x, hero.y).
Currently I have
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 640;
canvas.height = 480;
//then in my update function
if (38 in keysDown && hero.y > 0){ //UP KEY PRESSED KEY
ctx.translate(0,12); //translate background position +12y
hero.y -= hero.speed * modifier; //move player up on the background image
else if (40 in keysDown && hero.y < 3750-64){ //DOWN KEY PRESSED
ctx.translate(0, -12);
hero.y += hero.speed * modifier;
}
}
That moves the player and the canvas but not guaranteed together...if it freezes at all, the player is off center or even off screen.
The viewable canvas area is 640x480, but the background image you can navigate on is 5,000 x 3750.
On the web browser, when it doesn't freeze, it works how I want, moving the player and background at the same pace as the character.
However, that same rate on the phone puts the player much faster than the screen translates which means the player walks right out of the viewable area even though it still moves the background.
If I do ctx.translate(hero.x, hero.y) and use the hero.x, hero.y coordinates of the player, or some variation of it minus an offset, it moves the background BY that measurement each time I press the key instead of moving it TO that position.
How can I make everything conditional on the players position to move both the player and background, but together, or automatically adjust next update() to center on the player....?????

How can I make everything conditional on the players position to move both the player and background, but together, or automatically adjust next update() to center on the player
Well, the easy way would be to actually always draw the player in the center! In other words, never ever change or translate his or her coordinates. Instead worrying about translating everything else in relation to that.
Since you want the player to always be in the center, you should always draw the player at the center of the canvas (640/2 x 480/2 for you)
Then you'll want to do is keep a canvas offset for X and Y and draw everything (Background, etc) using that offset, then reset the transformation and draw the hero in the plain old center.
So your draw function might look something like this:
function draw() {
ctx.clearRect(0,0,500,500);
// Save the default transformation matrix
ctx.save();
// Translate by the background's offset
ctx.translate(xoffset, yoffset);
// Draw the background (and maybe other things) translated
ctx.fillStyle = backgroundGradient;
ctx.fillRect(0,0,500,500);
// We restore to the default transformation
// This will draw the hero not translated at all
// That means we can always draw the hero in the center!
ctx.restore();
// hero is a black rectangle
ctx.fillRect(240, 240, 20, 20);
}
Here is a live example that works with mouse down and up. There is a big "sun" for the background that moves, while the "stays" rectangle stays still because it is literally always drawn in the same place:
http://jsfiddle.net/3CfFE/

Related

My startDrag rectangle isn't where I set it to be

I've made a rectangle, a rectangular sprite and a circular sprite. The rectangular sprite has the exact same properties of my rectangle, and I set my circle to be positioned in the center of the stage, which is the middle of both of these rectangles.
After tracing the values of the rectangle, I can safely say that the rectangle is the same as the rect sprite. So then of course, if I wanted to drag the circle around the rectangle, it should stay within the bounds of it, right?
stop();
var isDragging:Boolean;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.display.Sprite;
import flash.geom.Point;
var px:Number = boundingBox.x;
var py:Number = boundingBox.y;
var h:Number = boundingBox.height;
var w:Number = boundingBox.width;
var box:Rectangle = new Rectangle(px,py,w,h);
trace(box);
var blob:Sprite = new Sprite();
blob.graphics.beginFill(0x000000);
blob.graphics.lineStyle(1.2, 0xFFFFFF);
blob.graphics.drawCircle(stage.stageWidth/2, stage.stageHeight/2, 18);
blob.graphics.endFill();
addChild(blob);
var box2:Sprite = new Sprite();
box2.graphics.lineStyle(1, 0xFFCC00);
box2.graphics.drawRect(px,py,w,h);
addChild(box2);
blob.addEventListener(MouseEvent.MOUSE_DOWN, drag);
function drag(event:MouseEvent):void {
event.target.startDrag(false, box);
isDragging = true
}
Well that doesn't seem be the case. Maybe I'm just a noob but I have no idea what's going on.
When I run the scene, everything looks normal.
The orange/dotted line is two rectangles I made, one is a movie clip that I used to set the properties of the rectangle I made in AS3 (dotted) and the orange is the AS3 sprite.
The circle is in the middle of the stage and it all looks as exactly how I wanted to be programmed.
But as soon as I click on it, it teleports over here.
And when I try to drag it, it seems as if the box's top left corner has been translated to that point all the way down there, and it only drags around in the bottom-right quadrant of the stage, and even drags further off screen.
I'm not sure if the rectangle that I put into the startDrag function is somehow distorted from the actual rectangle.
I have no idea why this happens, because when I tried to do this using a movie clip that I added to the stage, everything worked fine. But as soon as I try to do it programmatically, everything goes tits up.
I'd appreciate some help on why this isn't working. Thanks for reading.
There's your problem:
blob.graphics.drawCircle(stage.stageWidth/2, stage.stageHeight/2, 18);
You draw this circle and only seem to care about the circle:
if I wanted to drag the circle around the rectangle, it should stay within the bounds of it, right?
No. Just because you added something to the .graphics doesn't mean As3 automagically knows that this is what should stay within the bounding Rectangle that's being passed to startDrag().
The thing that stays within the bounding Rectangle is the registration point of the Sprite. That registration point is not where you drew the circle. It is in fact still at its default position of 0/0 in your first image. I added a green star at the registration point of blob. I also added a red rectangle that connects the registration point and your point. Think of this as some jelly that both your circle and the registration point are stuck in. This should help illustrate that just because you drew a circle somewhere doesn't mean that you are only dealing with that circle:
But as soon as I click on it, it teleports over here.
As soon as you click, the movement limitation of startDrag() is applied. The registration point has to be in the Rectangle that you specified and that's exactly what happens. The whole red jelly blob is moved over so that the green star is in the Rectangle:
To solve this, first create your blob in a reasonable fashion, which means drawing the circle around the registration point at 0/0 and then moving the Sprite object around if you want to have the circle at another position. This way, there's no red jelly and the green star is right at the center of the circle. The visual helpers that I added become superfluous, because your perception of the situation now matches with how it actually is.
const RADIUS:Number = 18;
var blob:Sprite = new Sprite();
blob.graphics.beginFill(0x000000);
blob.graphics.lineStyle(1.2, 0xFFFFFF);
blob.graphics.drawCircle(0, 0, RADIUS);
blob.x = stage.stageWidth/2;
blob.y = stage.stageHeight/2;
blob.graphics.endFill();
addChild(blob);
This will keep the blob within the bounds for the most part or more precisely: its center point. If you want to keep the entire circle within the bounds, you'd have to subtract the circle from the bounding Rectangle Minkowski-style. Mink-what? yeah...err, that's a fancy way of saying: make the bounding Rectangle smaller by the radius, so that this smaller boundary can be applied to the center of the radius to ensure that the whole circle stays within the bigger original boundary.
In your case Rectangle's inflate() should do:
var px:Number = boundingBox.x;
var py:Number = boundingBox.y;
var h:Number = boundingBox.height;
var w:Number = boundingBox.width;
var box:Rectangle = new Rectangle(px,py,w,h);
box.inflate(-RADIUS, -RADIUS);
trace(box);

rotation angle and spawn point of laser beam in flash game

I am trying to figure out the rotation angle and generation point of a laser beam that shoots up from a Cannon on Mouse Click. I am using ActionScript 3 and Flash for the same.
I rotate the beam based on my mouse cursor position which I feel works just fine. The
The issue is the generation point of my laser beam and it goes out of order. I want it to snap to the cannon ie its rotation point has to be the cannon. How do I do this in Flash?
Please have a look at the image file to get so that I am more clear.
Here is the code snippet that does the rotation and position logic in actionscript
laserBeamRight = new RightLaserBeam();
stage.addChild(laserBeamRight);
laserBeamRight.x = 812.65;
laserBeamRight.y = 400.1;
var angle2:Number = Math.atan2(stage.mouseY - laserBeamRight.y, stage.mouseX - laserBeamRight.x);
laserBeamRight.rotation = 180 * angle2/Math.PI;
I have hardcoded values for the position. They represent the right cannons position in the stage.
Here is the image file that shows the problem.
So I want the beam is targeting the mouse crosshair which is fine but I want it to be fixed and rotated around the cannon.
Another image with two beams at different angles. X position is right but Y position looks out of place because of the angle
One last image which clearly shows my problem.
The X position is right so is the Y position but it is originating from the center point of the beam and not the end point or the tail of the beam. I want the tail of the beam to be snapped to the cannons position. I tried changing the pivot point of the beam movie clip inside flash to the tail but that did not help.
Any idea?
First of all, adjust laser MC so that its anchor point is at its beginning instead of the middle, then align it to the turret's center as you do already. Then, when you are about to hit an object at specific (x,y), calculate rotation using Math.atan2() for the translated coordinates of mouse cursor (employ turret.globalToLocal(new Point(event.stageX,event.stageY)), then you should scale the laser so its end will hit the cursor position, change its scaleX. Note that you should do all transitions in one coordinate space (turret's), you apparently turn the turret's cannon part already, so you can do the same for your laser, and add it as part of a turret, probably positioning it behind the cannon part. An example:
// laser MC is like this: *>>>>>>>-------
// * is anchr point, laser is aligned righwards
const laserlength:Number=500; // the length of the laser, the X of the point you want to hit the cursor
....
function fireLaser(e:MouseEvent):void {
// creates a laser
var laser:Laser=new Laser();
laser.x=turret.centerX; // where to position the laser beginning
laser.y=turret.centerY; // relative to the turret
turret.addChildAt(laser,0); // add as a part of turret, behind everything
var cursor:Point=turret.globalToLocal(new Point(e.stageX,e.stageY));
laser.rotation=Math.atan2(cursor.y-laser.y,cursor.x-laser.x)*180/Math.PI;
// rad-to-deg conversion
laser.scaleX=Point.distance(cursor,new Point(turret.centerX,turret.centerY))/laserlength;
// lasers.push(laser); // expecting you to do this so laser should fade with time
}

Canvas gradient performance

I am currently programming a little game using canvas. For the game I need some kind of fog which hides the most part of the map and only a small area around the player should be visible. Therfor I use a second canvas to overlay the one where the game takes place and fill it with a gradient (from transparent to black):
function drawFog(){
fogc.clearRect(0,0,700,600);
// Create gradient
var grd=fogc.createRadialGradient(player.getPosX(),player.getPosY(),0,player.getPosX(),player.getPosY(),100);
grd.addColorStop(0,"rgba(50,50,50,0)");
grd.addColorStop(1,"black");
// Fill with gradient
fogc.fillStyle=grd;
fogc.fillRect(0,0,700,600);
}
Unfortunatly this is causing huge perfomance problems since it will be redrawn for every frame.
I wanted to ask if there might be a better solution to achieve the same effect with a better performance.
Cache the gradient to an off-screen canvas then draw in the canvas with drawImage() instead:
Create an off-screen canvas the size of the fog
Draw in the gradient
Use off-screen canvas as an image when you need the fog.
This way the processing creating and calculating the gradient is eliminated. Drawing an image is basically a copy operation (there is a little bit more, but performance is very good).
function createFog(player) {
// Create off-screen canvas and gradient
var fogCanvas = document.createElement('canvas'),
ctx = fogCanvas.getContext('2d'),
grd = fogc.createRadialGradient(player.getPosX(),
player.getPosY(),
0,player.getPosX(),
player.getPosY(),100);
fogCanvas.width = 700;
fogCanvas.height = 700;
grd.addColorStop(0,"rgba(50,50,50,0)");
grd.addColorStop(1,"black");
// Fill with gradient
ctx.fillStyle = grd;
ctx.fillRect(0,0,700,600);
return fogCanvas;
}
Now you can simply draw in the canvas returned from the above function instead of creating the gradient every time:
var fog = createFog(player);
ctx.drawImage(fog, x, y);

[AS3]Rotate a Sprite around it's center

I need some help with a really specific problem.
I've looked and found some answers, but nothiung as specific as what I need.
I'm using flashdevelop, and for now, I've a rectangle (it's a Sprite) following my mouse cursor (centered).
I'd like to apply a rotation on it when I scroll my mouse wheel, but I need the rotation to apply on it's center, and I need the object to stay centered on my mouse cursor at the same time.
And I also need to apply the rotation in radians, not in degree.
For now I got this :
var mod:Number;
if (e.delta <= 0) {
mod = -0.1;
}else {
mod = 0.1;
}
Could someone help me with it please ? After what I've seen it seems that I've to use matrix, and complex maths, but I've no idea how to do what I want to do with this.
You just need to keep the registration point of the sprite in center of the rectangle to rotate it about it's center. When you draw a sprite from (0,0) the registration point is top-left and not center. Here's how you make it center:
var rectangleSprite:Sprite = new Sprite();
rectangleSprite.graphics.lineStyle(3,0x00ff00);
rectangleSprite.graphics.beginFill(0x0000FF);
//please note that i am not drawing the sprite from (0,0),
//instead i start from (-width/2,-height/2)
rectangleSprite.graphics.drawRect(-150,-75,300,150);
rectangleSprite.graphics.endFill();
And for degree to radian conversion you just need multiply your value in radians by (180/Math.PI) before applying your rotation.
var rotationInDegrees = rotationInRadians*(180/Math.PI);

displacementMapfilter floats with object below it

I've been creating a magnifier class using a standard displacementMapfilter similar to Adobe's and numerous other examples: http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7da2.html
The filter is being used at the application level so it filters everything, and there's a large number of image elements that can be dragged, rotated, and scaled around the screen, and when passed below this filter it magnifies them. I also intend to add some more functionality to allow dragging the filter around.
The filter works as intended, magnifying everything that passes by it. The weird thing is whenever an image is being dragged outside of the top or left edge of the screen, the filter floats in that direction the distance it takes before the image is entirely offscreen (ie: the filter floats 500 pixels left if a picture 500 pixels wide is pulled offscreen to the left.)
I'm using an enterFrame listener to constantly update its position, and it works as follows:
private function onEnterFrame(e:Event):void {
dPoint.x = stage.stageWidth - radius;
dPoint.y = stage.stageHeight - radius;
dFilter = new DisplacementMapFilter(map, dPoint, BitmapDataChannel.RED, BitmapDataChannel.BLUE, 100, 100, DisplacementMapFilterMode.IGNORE, 0x000000, 0);
// The application is a displayObject itself, so just apply filters to "this"
this.filters = [dFilter];
}
So this code should anchor the filter at the bottom right of the screen, yet somehow whenever an image is dragged off, the filter drifts with it. Is there any reason a filter would do that, and any way I could stop it?
Dragging an image off the stage changes the size and shape of the rectangle the filter is being applied to (picture the filter as if it's taking a snapshot of everything on the stage). When the image moves off the top left, it means that (0, 0) on the filter is actually at the top left corner of the image.
If you check the bounds of the stage (in the stage's own coordinate space), you should see top and left become negative numbers when you drag an image off:
stage.getBounds(stage).top;
stage.getBounds(stage).left;
Cancelling out any negative bounds should keep your filter in the correct position:
var stageBounds:Rectangle = stage.getBounds(stage);
if (stageBounds.left < 0) {
dPoint.x -= stageBounds.left;
}
if (stageBounds.top < 0) {
dPoint.y -= stageBounds.top;
}