Camera on HTML 5 Canvas - html

first time poster here,
I have looked for a solution to this for days to no avail, if this has been asked I beg you to link me as I could not find it. Anyways, here goes:
I am working on a tile based canvas app and having issues with moving the camera. Essentially I want to keep the hero in the middle of the screen unless he comes near and edge. I have tried working with using translating the canvas as the hero moves but had issues with shearing. I ended up reworking my logic and now figure out where to start drawing based on the hero's coordinates and then keep my hero drawn at the center of the canvas, it works well until I go near the edges of the screen.
Here is my current code for drawing the map, leoX and leoY are the absolute coordinates of the hero, the canvas is 600X600
var topLeftX = Math.floor((leoX - 300)/33);
var topLeftY = Math.floor((leoY - 300)/33);
//draw map
for(var rowCtr=topLeftY;rowCtr<mapRows;rowCtr++){
for(var colCtr=topLeftX;colCtr<mapCols;colCtr++){
var tileId = tileMap[rowCtr][colCtr]+ mapIndexOffset;
var mapSourceX = Math.floor(tileId % 8 )*33;
var mapSourceY = Math.floor(tileId / 8 )*33;
context.drawImage(map, mapSourceX, mapSourceY,33,33,(colCtr-topLeftX)*33,(rowCtr- topLeftY)*33,33,33);
}
}
//code to draw leo
context.drawImage(tilesheet, sourceX,sourceY,32,48,300,300,32,48);
//update leo's position
leoX += leoDeltaX;
leoY += leoDeltaY;
EDIT: Link to example is now dead.

Related

Speech bubbles in Canvas/FabricJS?

Looking for a way to make a regular speech bubble in my website's FabricJS canvas. Now before you flag this post, I did see this question, it just has no proper answers and is designed for WordPress so it's not particularly of any use to me.
What I'm wanting is pretty clear: A speech bubble with text in it and a tail/handle that you can drag to point it to something.
I've found this library but I can't seem to get it to show up in my FabricJS canvas? If you could either explain to me how to add this library into my canvas or provide another way of making speech bubbles, that would be sublime.
I dug a bit into Fabric.js and managed to create a procedual speech bubble, but I'm not able to quickly convert it into a Fabric.js class (which would make sense if you want to have multiple speech bubbles on your canvas). Maybe it's still helpful for you or someone else https://codepen.io/timohausmann/pen/poywXzg
It basically creates a Textbox and based on the bounding box of the text updates the position of the Rect around it.
var bound = textbox.getBoundingRect();
rect.left = bound.left - boxPadding;
rect.top = bound.top - boxPadding;
rect.width = bound.width + (boxPadding*2);
rect.height = bound.height + (boxPadding*2);
For the tail I simply created a transparent Rect that you can drag around and use its coordinates to draw a polygon with three points between the "handle" and the textbox center [A]. To make sure the tail maintains a certain width no matter the position, I calculate the degree between handle and the speech bubble center [B]. To keep the position of textbox and handle in sync, I calculate how much textbox moved and simply add the difference to the handles position [C].
//calculate degree between textbox and handle [B]
var angleRadians = Math.atan2(handle.top - textbox.top,
handle.left - textbox.left);
var offsetX = Math.cos(angleRadians + (Math.PI/2));
var offsetY = Math.sin(angleRadians + (Math.PI/2));
//update the polygon [A]
poly.points[0].x = handle.left;
poly.points[0].y = handle.top;
poly.points[1].x = textbox.left - (offsetX * arrowWidth);
poly.points[1].y = textbox.top - (offsetY * arrowWidth);
poly.points[2].x = textbox.left + (offsetX * arrowWidth);
poly.points[2].y = textbox.top + (offsetY * arrowWidth);
//update the handle when the textbox moved [C]
if(textbox.left !== textbox.lastLeft ||
textbox.top !== textbox.lastTop) {
handle.left += (textbox.left - textbox.lastLeft);
handle.top += (textbox.top - textbox.lastTop);
handle.setCoords();
}
Disclaimer: I'm not a Fabric.js expert, maybe there are a few shortcuts possible with the library.
The answer by #Til Hausmann works nicely (thanks!).
I run into some problems when I tried to store and load the canvas data (via canvas.toJSON and canvas.loadFromJSON, resp.), though.
After some fiddling around, this could be resolved by
storing lastLeft and lastTop for both polygons in the updateBubble() method:
poly.lastLeft = Math.min(handle.left, textBox.left);
poly.lastTop = Math.min(handle.top, textBox.top);
setting the left / top properties for the polygons after the data were loaded:
canvas.loadFromJSON(jsonData, () => {
const poly = // ...
const poly2 = // ...
poly.left = poly.lastLeft;
poly.top = poly.lastTop;
poly2.left = poly2.lastLeft;
poly2.top = poly2.lastTop;
// ...
// Important:
canvas.renderAll();
});
passing the full set of shape properites to canvas.toJSON()
canvas.toJSON(
['lastLeft', 'lastTop'].concat(
Object.keys(handleProperties),
Object.keys(polyProperties),
Object.keys(poly2Properties),
Object.keys(textRectProperties)
))
I was surprised that step (3) is actually necessary, but it didn't work without it...

HTML5 Canvas (SIMPLE PAINT APPLICATION)

I'm working on a paint application and I'm trying to centre the canvas onto the middle of the screen . Any attempts I made the detection was off(still at the top left of the screen) but it was visually appearing in the centre of the screen.
Basically it wont draw onto the canvas when I moved it to the centre of the screen.
Any help would be much appreciated , Thanks....
I HAVE MY CODE BELOW ...
It's not clear from your question how you're centring it, but you need to account for any offset of elements which contain your canvas when you attempt to map the mouse position to a position on the canvas. You can do this by including the offsetLeft and offsetTop values (see docs) of the containing element when you do your calculations.
The following will work if you're offsetting the position of the div which wraps the canvas (which I've given an id to make it easier to reference):
function move(e) {
// Get the div containing the canvas. Would be more efficient to set this once on document load
var container = document.getElementById('container');
if((e.button == 0) && (mouseIsDown)) {
g.beginPath();
document.onselectstart = function(){ return false; }
//g.fillStyle = "red";
// Account for the offset of the parent when drawing to the canvas
g.arc((e.x - container.offsetLeft) - brush, (e.y - container.offsetTop) - brush, brush, 0, Math.PI * 2);
g.fill();
g.closePath();
}
}
And a simplistic demo using your fiddle here.

Resize as a function of distance between mc's

I hope this hasn't been asked too much before. When I search I only get questions pertaining to rescaling to window size.
Now my question. I got one space ship firing a beam against another ship. I want the beam to show for some time and I want it to "bridge" the two ships. In other words, I want the beam to extend its width between the two ships.
I try to do this with a dot movie clip that is 1 pixel wide and high (and aligned left edge). I try to resize it with the following code: (target is the ship to be fire at and owner is the ship firing)
dist.vx = target.x - owner.x;
dist.vy = target.y - owner.y;
dist.dist = Math.sqrt(dist.vx*dist.vx + dist.vy*dist.vy);
width = dist.dist;
x = owner.x;
y = owner.y;
rotation = Math.atan2(target.y-y, target.x-x)*180/Math.PI;
This doesn't work as intended because 1) dot also gets alot bigger in the other dimension - how can I "turn off" this behavior? and 2) sometimes it seems to get way to wide - but only in certain angles...
Any suggestions on either solving the heigh/width scaling or on another way to achieve the same effect?
(I'm new to coding and flash.) Thanks!
By resizing a dot, you will have a rectangle...
You can dynamically create a sprite covering both ships and moveTo the hit point of one ship then lineTo the other ship... You do not need distance calculation at all. What you have to do is being careful on the placement of the sprite. So that you can calculate relative hitting points by simple math.
Suppose you have mc space contining mc ship1 and mc ship2, and hit point coords on ships are named hx, hy and you will use sprite s, calculation will be as follows.
// calculate hit points relative to mc space
var s1HX:int = ship1.x + ship1.hx,
s1HY:int = ship1.y + ship1.hy,
s2HX:int = ship2.x + ship2.hx,
s2HY:int = ship2.y + ship2.hy,
// sprite relative moveTo lineTo coords will be these.
mX: int, mY: int,
lX: int, lY: int;
// top left of sprite will be minimum of the hit coords.
s.x = (s1HX <= s2HX)? s1HX : s2HX;
s.y = (s1HY <= s2HY)? s1HY : s2HY;
// now we can get sprite relative moveTo lineTo coordinates:
mX = s1HX - s.x;
mY = s1HY - s.y;
lX = s2HX - s.x;
lY = s2HY - s.y;
The rest is implementation with using these with fancy line styles etc...
To create a new sprite:
var s:Sprite = new Sprite();
Adding / removing it to/from mc space:
space.addChild(s);
space.removeChild(s);
For graphics use the graphics object of sprite.
s.graphics
For setting line styles you can use:
s.graphics.lineStyle(...) ,
s.graphics.lineBitmapStyle(...),
s.graphics.lineGradientStyle(...)
Functions, please read the manual for usage.
After setting the line style to draw the line use:
s.graphics.moveTo(mX,mY);
s.graphics.lineTo(lX,lY);
For pulsating effects you have to do a little more complicated things such as using tween class which you can read about here: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/fl/transitions/Tween.html
Note that:
Sprites are no complicated magic, they are like mc's but they do not have timelines etc.
Sprites try to scale when width or height change programmatically. So do not touch them, moveTo lineTo automatically sets the size of a sprite...

Scaling tile grid

I'm working on my own tile bliting engine, this one is using hexagonal tiles - but I think it doesn't differ much from regular tiles.
I have huge x,y array of tiles and they have their x,y coordinates for rendering on canvas, I iterate only the ones that should be visible on canvas in current camera position.
So I'm stuck with scaling and cant resolve this on my own.
Here is my code for drawing tiles on canvas:
public function draw():Void{
clearCanvas(); //Clear canvas (bitmapData)
var _m:Matrix;
iterateTiles(function(_tile:HexTile):Void{ // loop every tile that is visible on screen
_m = new Matrix();
_m.translate(_tile.x + cameraPoint.x,_tile.y + cameraPoint.y);//Get pre calculated tile x,y and add camera x,y
_m.scale(matrixScale, matrixScale);
drawToCanvas(_tile,_m);//Send to draw tile on canvas using Matrix
},true);
}
This works nice and fast but only problem is it scales tiles from left top corner (like regular scale would work)
Before scale
After scale
My question is how to transform tiles to always scale from center. So if tile 10:10 is in center of screen before scaling, then it should
stay there after scaling.
Sorry, I misunderstood the question, but I think I've got it now:
// Scale the distance from the original point to the center of the canvas
var xDistance:Number = ((_tile.x + cameraPoint.x) - xCenter) * matrixScale;
var yDistance:Number = ((_tile.y + cameraPoint.y) - yCenter) * matrixScale;
// Add the distances to the center of the canvas. This is where you want the tile
// to appear.
var x:Number = xCenter + xDistance;
var y:Number = yCenter + yDistance;
// Because the coordinate is going to be scaled, you need to increase it first.
x = (1 / matrixScale) * x;
y = (1 / matrixScale) * y;
_m.translate(x, y);
I have not tested this, I've just drawn it out on graph paper. Let me know if it works.

Actionscript 3.0 problem - Why is it giving an error even when I've re-checked the code?

I'm using Adobe Flash CS4 professional for this Actionscript 3.0 project
(http://tutorials.flashmymind.com/2009/02/rotating-menu-via-actionscript-3/)
I even tried following the suggestions in the comments as well but this error always shows up:
TypeError: Error #1010: A term is undefined and has no properties.
at rotating_menu_fla::MainTimeline/frame1()
(for complete details, kindly click the link - http://i429.photobucket.com/albums/qq19/tsujzpie/screenshot_03.jpg)
I've been following every step of the tutorial but I'm stumped over the coding...
Here's the code by the way...
//Save the center coordinates of the stage
var centerX:Number=stage.stageWidth/2;
var centerY:Number=stage.stageHeight/2;
//The number of items we will have (feel free to change!)
var NUMBER_OF_ITEMS:uint=5;
//Radius of the menu circle (horizontal and vertical)
var radiusX:Number=200;
var radiusY:Number=100;
//Angle difference between the items (in radians)
var angleDifference:Number = Math.PI * (360 / NUMBER_OF_ITEMS) / 180;
//How fast a single circle moves (we calculate the speed
//according to the mouse position later on...)
var angleSpeed:Number=0;
//Scaling speed of a single circle
var scaleSpeed:Number=0.0002;
//This vector holds all the items
//(this could also be an array...)
var itemVector:Array = new Array ('1', '2', '3', '4','5');
//This loop creates the items and positions them
//on the stage
for (var i:uint = 0; i < NUMBER_OF_ITEMS; i++) {
//Create a new menu item
var item:Item = new Item();
//Get the angle for the item (we space the items evenly)
var startingAngle:Number=angleDifference*i;
//Set the x and y coordinates
item.x=centerX+radiusX*Math.cos(startingAngle);
item.y=centerY+radiusY*Math.sin(startingAngle);
//Save the starting angle of the item.
//(We have declared the Item class to be dynamic. Therefore,
//we can create new properties dynamically.)
item.angle=startingAngle;
//Add an item number to the item's text field
item.itemText.text=i.toString();
//Allow no mouse children
item.mouseChildren=false;
//Add the item to the vector
itemVector.push(item);
//Add the item to the stage
addChild(item);
}
//We use ENTER_FRAME to animate the items
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
//This function is called in each frame
function enterFrameHandler(e:Event):void {
//Calculate the angle speed according to mouse position
angleSpeed = -(mouseX - centerX) / 5000;
//Loop through the vector
for (var i:uint = 0; i < NUMBER_OF_ITEMS; i++) {
//Save the item to a local variable
var item:Item=itemVector[i];
//Update the angle
item.angle+=angleSpeed;
//Set the new coordinates
item.x=centerX+radiusX*Math.cos(item.angle);
item.y=centerY+radiusY*Math.sin(item.angle);
//Calculate the vertical distance from centerY to the item
var dy:Number=centerY-item.y;
//Scale the item according to vertical distance
item.scaleY = (dy / radiusY)+2;
//Set the x scale to be the same as y scale
item.scaleX=item.scaleY;
//Adjust the alpha according to y scale
item.alpha=item.scaleY+1.1;
}
}
I find it odd - it may be that the code is right but I don't know if these steps has messed up the project...
3 - Convert the circle into a movie clip...
4 - In the “Item” movie clip, create a dynamic text field in the center of the circle (in a new layer).
5 - Set the text to align center. Type some number in the text field. Give the text field an instance name of “itemText”. Embed numerals...
6 - Remove the Item movie clip from the stage. We will create all the items dynamically via ActionScript 3.
I could've given more screenshots but since I'm a new user, I'm only allowed a max of two - and contrary to this tag, I'm not doing an Android app.
(I'll give you added info once any of you would reply to this question...)
I have to admit that the steps 3 to 6 is confusing and didn't made sense to me - especially with step six, when you have to remove the movie clip from the stage. For me, if I were to do that, what would the script work on then?
Any idea what I did wrong?
EDIT:
Thanks, I realized my mistake - thanks for the tip :-)
But now, I tried to modify the code of this tutorial a bit to have words appear inside the circles (like "Home", "About", etc....) like what I've typed in the line in the screenschot - http://i429.photobucket.com/albums/qq19/tsujzpie/modifiedlineincode_00.jpg
But inspite of what I believe are the appropriate changes I've done, an error showed up still - (kindly see here - http://i429.photobucket.com/albums/qq19/tsujzpie/newerrorincode_00.jpg)
Why is that happening? Before I forget, in what section of the code must I insert a line that will make a clicked button display the info corresponding to it? (that is, if I click on the "Contact" or any of the buttons a window will appear beneath the menu on the stage...)
That errors means ActionScript has no clue what an Item is. Make sure you've ticked Export for ActionScript on your Item MovieClip in library and that the class is named Item also.
Update:
The tutorial you're following mentions itemVector is a Vector of Item instances, not an array of Strings like you're code suggests. This why you're getting the error displayed in your screenshot. It means ActionScript can not convert an Item to a String.
An easy fix is to create another array for the menu item labels:
var itemVector:Array = [];// = new Array ('1', '2', '3', '4','5');
var itemLabels:Array = ["Home","About","Contact","Gallery"];
NUMBER_OF_ITEMS = itemLabels.length;
and in the for loop swap this item.itemText.text=i.toString(); for this tem.itemText.text=itemLabels[i];
It seems that you don't fully grasp the difference between variable types. I would suggest getting familiar with the basics of as3 before moving forward. Also paying more attention to your code and fully understanding code you use written by others will keep you out of trouble. However you will still encounter errors. You might find explanations for those errors on the Flash Error Database.
Regarding the Carosel tutorial, also have a look at this video. It might help explain things better.
The compiler can not find the Item class.
Go into your library and open up the properties for the "Item" MovieClip.
Verify that is is set for export to actionscript.