In Cocos2d (Android, JAVA), I use CCRotateBy to rotate one CCNode with several tiles in it as children. I want to use the center of the center tile as a point of rotation, so I thought I'd use Anchorpoint.
However, it doesn't matter what value I give to the Anchorpoint, tiles keep rotating around the bottom left of my screen. How come?
(the tiles are CCNodes, collected in two lists, tilesSelected and secondaryTilesSelected)
// I create one node which holds all the tiles I want to rotate
CCNode tilesToRotate = CCNode.node();
tilesToRotate.addChild(tilesSelected.get(0), 0, 99);
// then, I add the 4 tiles around the previous, center tile
for (int i=0; i < secondaryTilesSelected.size(); i++){
tilesToRotate.addChild(secondaryTilesSelected.get(i), 0, 99);
}
// So, if I change 700,700 hereunder to different values, it doesn't change the centerpoint for Rotation. I guess I don't get it...
addChild(tilesToRotate);
tilesToRotate.setAnchorPoint(CGPoint.make(700,700));
CCAction r90 = CCRotateBy.action(1f, 90f);
tilesToRotate.runAction(r90);
anchorPoint is a factor in the range 0,0 (lower left corner) to 1,1 (upper right corner of content)
You are setting the anchor far too far away from the node at 700,700
Related
I've got a JPanel which renders an SVGDiagram, set as the viewport view of a JScrollPane. The scrollpane seems to work OK, and I can zoom in and out of the diagram, too; and given a MouseEvent, I can find nearby SVGElements using the "pick" method.
I'd like to be able to center the scrollpane on a given ShapeElement, but I'm having trouble. I'd thought that the center of the bounding box of the element would be the center of the shape in the diagram's coordinate system, but this appears to be wrong:
ShapeElement c;
Rectangle2D bounds = c.getBoundingBox();
Point center = new Point((int) bounds.getCenterX(), (int) bounds.getCenterY());
The returned center appears to (a) offset the Y-coordinate by more than the diagram's height (elements near the bottom of the diagram appear to have "centers" with small negative Y values, elements near the top of the diagram appear to have "centers" with large negative Y values), and (b) scale the X-coordinate (with no zoom, elements near the left edge of the diagram appear to have centers with X values near zero, while elements near the right edge of the diagram appear to have centers with X values roughly near 1/2 the diagram's width).
Obviously, when svgSalamander renders the Shapes to the screen, it's putting them in "the right place" -- but whatever transforms it uses are available only when it is rendering to the screen, which doesn't help me to find the coordinates of the center of an element when it's not on the screen.
Has anyone had any luck trying to center a given ShapeElement?
Once again, just taking the time to write the question pushed me in the direction of the answer.
The key is that "pick" also has to relate the diagram's coordinate frame to the coordinate frame of each Shape. Following the code, SVGRoot.pick creates an identity AffineTransform, then concatenates to it, in turn, the viewXForm of the SVGRoot, then the xform of each TransformableElement it encounters until it gets to a ShapeElement; there, ShapeElement.pick checks whether the given Rectangle intersects with its Shape as transformed by the concatenation of AffineTransforms.
So, to find the center of a given ShapeElement, one need only walk up its parents, collecting any TransformableElement.getXForm() results, append the viewXForm of the root element, create the concatenation of all those transforms from that of the root down, and apply that transform to the center of the target ShapeElement's bounding box:
synchronized Point2D getCenter(ShapeElement e) throws SVGException {
List<AffineTransform> ats = new ArrayList<>();
for (SVGElement el = e; el != null; el = el.getParent())
if (el instanceof TransformableElement) {
AffineTransform a = ((TransformableElement) el).getXForm();
if (a != null)
ats.add(a);
}
ats.add(diagram.getRoot().getViewXform());
AffineTransform xform = new AffineTransform();
for (AffineTransform a : <an Iterable that iterates over ats in reverse> )
xform.concatenate(a);
return xform.transform(
new Point2D.Double(
e.getBoundingBox().getCenterX(),
e.getBoundingBox().getCenterY()
), null);
}
I did have to add (a trivial) SVGRoot.getViewXform(), but that's all.
I have a Rectangle class that represents a d-dimension rectangle with 2*d numbers for every dimension. For every dimension i have a lower and an upper bound. Dimensionality stores the number of dimensions of the rectangle, and for the lower and upper bounds i use a double array.
I want to create 2 methods that they have as input another rectangle object of the same Dimensionality and return the minimum and maximum distance between the rectangles, im trying to do this using the minimum/maximum distances of each of their projections in every axis. I also have a method that creates the projections.
//returns 2 position array
public double[] project(int x)
{
//x is the selected dimension
double proj[] = new double[2];
proj[0] = this.lb[x];
proj[1] = this.ub[x];
return proj;
}
you can see on the third set of shapes what i want to do more clearly
https://s15.postimg.org/l8aijyl1n/imageedit_2_6689786765.jpg
Find mutual orientation of rectangles (as direction of vector between centers, it is enough to get signs of center.x and center.y differences)
Depending on orientation quadrant, get distances from selected edges of the first rectangle to selected edges of the second. Using signs of these distances, find extremal distances:
For the case at the picture (direction in the 4th quadrant) one have to check distances from right bottom corner of the first rectangle to the left and top edges of the second one, and from left top corner of the second - to the right and bottom edges of the first one.
min_dx = rect2.left - rect1.right
min_dy = rect2.top - rect.bottom
if both values > 0 (++ case)
min_dist = sqrt(min_dx^2 + min_dy^2) //corner-corner case
+- case
min_dist = min_dx
-+ case
min_dist = min_dy
-- case
min_dist = 0
Seems the same approach might be used for higher dimensions. Number of cases becomes too high, so it is worth to select planes for checking with indexes corresponding to center-center direction vector components' signs
I used an canvas to draw multiple textures on it. these textures are rectangles and now I want to use these textures with parts of them invisble, so I could draw background colors behind the textures to have teh same texture with different colors without adding the same picture with different colors.
I tried to add Rects like this:
for(Coordinate c : ch.getVisibleCoords()) {
ShapeDrawable sD = new ShapeDrawable();
Rect r = new Rect(c.getxS(),
c.getyS(),
(sh.getScreenWidth()-c.getxS()-sh.getTSize()),
(sh.getScreenHeight()-c.getyS()-sh.getTSize()));
sD.setBounds(r);
textureColorRects.add(sD);
}
each coordinate represents an texture the xS and yS values are the positions at the screen, for example coordinate 1|1 could have xS=0 | yS=0 and 2|1 xS=48 (48=texturesize) | yS=0. I tried this with ShapeDrawable and Rectangles itself, in the first case it will draw everything the same color expect of one y-line and in the other case it will draw just some buggy shit.
Is there another way to do this or may I didn't understood how to setup those rectangles, I can't figure out how that left, top, right, bottom stuff works.
The rest of the code is here for you so you can see how I draw the ShapeDrawables:
int i = 0;
for(Coordinate c : ch.getVisibleCoords()) {
ShapeDrawable sD = textureColorRects.get(i);
Paint color = new Paint();
color.setColor(c.getLandscape().getType().getColor());
color.setStyle(Paint.Style.FILL);
sD.getPaint().set(color);
sD.draw(canvas);
}
The textureColorRects is a list containing all ShapeDrawables.
Thank you very much for reading.
I found an solution it's a problem other people had too (was just hard to find) it's a bit hard to understand how the Rect works the values for left, top, right and bottom are seen like the beginning and the ed point for example I want a rectangle of the size 16*16 and at the point x=5|y=18 on the screen, so I need to set the right value to x+size (5+16) and the bottom to y+size (18+16). The lft and top can be set to the left upper edge of the rect (start position).
I'm having a little problem with figuring something out (Obviously).
I'm creating a 2D Top-down mmorpg, and in this game I wish the player to move around a tiled map similar to the way the game Pokemon worked, if anyone has ever played it.
If you have not, picture this: I need to load various areas, constructing them from tiles which contain an image and a location (x, y) and objects (players, items) but the player can only see a portion of it at a time, namely a 20 by 15 tile-wide area, which can be 100s of tiles tall/wide. I want the "camera" to follow the player, keeping him in the center, unless the player reaches the edge of the loaded area.
I don't need code necessarily, just a design plan. I have no idea how to go about this kind of thing.
I was thinking of possibly splitting up the entire loaded area into 10x10 tile pieces, called "Blocks" and loading them, but I'm still not sure how to load pieces off screen and only show them when the player is in range.
The picture should describe it:
Any ideas?
My solution:
The way I solved this problem was through the wonderful world of JScrollPanes and JPanels.
I added a 3x3 block of JPanels inside of a JScrollPane, added a couple scrolling and "goto" methods for centering/moving the JScrollPane around, and voila, I had my camera.
While the answer I chose was a little more generic to people wanting to do 2d camera stuff, the way I did it actually helped me visualize what I was doing a little better since I actually had a physical "Camera" (JScrollPane) to move around my "World" (3x3 Grid of JPanels)
Just thought I would post this here in case anyone was googling for an answer and this came up. :)
For a 2D game, it's quite easy to figure out which tiles fall within a view rectangle, if the tiles are rectangular. Basically, picture a "viewport" rectangle inside the larger world rectangle. By dividing the view offsets by the tile sizes you can easily determine the starting tile, and then just render the tiles in that fit inside the view.
First off, you're working in three coordinate systems: view, world, and map. The view coordinates are essentially mouse offsets from the upper left corner of the view. World coordinates are pixels distances from the upper left corner of tile 0, 0. I'm assuming your world starts in the upper left corner. And map cooridnates are x, y indices into the map array.
You'll need to convert between these in order to do "fancy" things like scrolling, figuring out which tile is under the mouse, and drawing world objects at the correct coordinates in the view. So, you'll need some functions to convert between these systems:
// I haven't touched Java in years, but JavaScript should be easy enough to convey the point
var TileWidth = 40,
TileHeight = 40;
function View() {
this.viewOrigin = [0, 0]; // scroll offset
this.viewSize = [600, 400];
this.map = null;
this.worldSize = [0, 0];
}
View.prototype.viewToWorld = function(v, w) {
w[0] = v[0] + this.viewOrigin[0];
w[1] = v[1] + this.viewOrigin[1];
};
View.prototype.worldToMap = function(w, m) {
m[0] = Math.floor(w[0] / TileWidth);
m[1] = Math.floor(w[1] / TileHeight);
}
View.prototype.mapToWorld = function(m, w) {
w[0] = m[0] * TileWidth;
w[1] = m[1] * TileHeight;
};
View.prototype.worldToView = function(w, v) {
v[0] = w[0] - this.viewOrigin[0];
v[1] = w[1] - this.viewOrigin[1];
}
Armed with these functions we can now render the visible portion of the map...
View.prototype.draw = function() {
var mapStartPos = [0, 0],
worldStartPos = [0, 0],
viewStartPos = [0, 0];
mx, my, // map coordinates of current tile
vx, vy; // view coordinates of current tile
this.worldToMap(this.viewOrigin, mapStartPos); // which tile is closest to the view origin?
this.mapToWorld(mapStartPos, worldStartPos); // round world position to tile corner...
this.worldToView(worldStartPos, viewStartPos); // ... and then convert to view coordinates. this allows per-pixel scrolling
mx = mapStartPos[0];
my = mapStartPos[y];
for (vy = viewStartPos[1]; vy < this.viewSize[1]; vy += TileHeight) {
for (vx = viewStartPos[0]; vx < this.viewSize[0]; vy += TileWidth) {
var tile = this.map.get(mx++, my);
this.drawTile(tile, vx, vy);
}
mx = mapStartPos[0];
my++;
vy += TileHeight;
}
};
That should work. I didn't have time to put together a working demo webpage, but I hope you get the idea.
By changing viewOrigin you can scroll around. To get the world, and map coordinates under the mouse, use the viewToWorld and worldToMap functions.
If you're planning on an isometric view i.e. Diablo, then things get considerably trickier.
Good luck!
The way I would do such a thing is to keep a variable called cameraPosition or something. Then, in the draw method of all objects, use cameraPosition to offset the locations of everything.
For example: A rock is at [100,50], while the camera is at [75,75]. This means the rock should be drawn at [25,-25] (the result of [100,50] - [75,75]).
You might have to tweak this a bit to make it work (for example maybe you have to compensate for window size). Note that you should also do a bit of culling - if something wants to be drawn at [2460,-830], you probably don't want to bother drawing it.
One approach is along the lines of double buffering ( Java Double Buffering ) and blitting ( http://download.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html ). There is even a design pattern associated with it ( http://www.javalobby.org/forums/thread.jspa?threadID=16867&tstart=0 ).
The issue involves an Android Path shape. It's a triangle that I'm using as an arrow to point towards objects on a screen Canvas. This is for a 2d game. player in the middle of the screen, objects around him and offscreen.
These arrows are supposed to rotate around the center of the screen, with a radius so that they rotate in a circle around the player. The arrows point towards objects that the player needs to move towards.
What I have right now is somewhat working, but the arrows are zipping around the circle at ridiculous speeds. Funny enough, they're pointing in the right direction, but they aren't staying at the right point on the circle. (if arrow is pointing northeast, arrow should be on the northeast part of the circle, etc)
I'm sure it's because of the math. I'm probably using atan2 wrong. Or canvas.translate wrong. Or maybe I shouldn't be using atan2 at all. Help! :)
Here is the code:
// set the shape of our radar blips
oBlipPath.moveTo(0, -5);
oBlipPath.lineTo(5, 0);
oBlipPath.lineTo(0, 5);
// Paint all the enemies and radar blips!
for(int i=0; i<iNumEnemies; i++){
if (oEnemies[i].draw(canvas, (int)worldX, (int)worldY)){
//calculate the degree the object is from the center of the screen.
//(user is the player. this could be done easier using iWidth and iHeight probably)
//we use a world coordinate system. worldY and worldX are subtracted
fDegrees = (float)Math.toDegrees(Math.atan2((oEnemies[i].getEnemyCenterY()-worldY)-user.getShipCenterY(), (oEnemies[i].getEnemyCenterX()-worldX)-user.getShipCenterX()));
canvas.save();
//get to the center
canvas.translate((iWidth / 2) , (iHeight / 2) );
//move a little bit depending on direction (trying to make arrows appear around a circle)
canvas.translate((float)(20 * Math.cos(fDegrees)), (float)(20* Math.sin(fDegrees)));
//rotate canvas so arrows will rotate and point in the right direction
canvas.rotate(fDegrees);
//draw arrows
canvas.drawPath(oBlipPath, oBlipPaint);
canvas.restore();
}
}
Affine transformations are are not commutative. They are typically applied in an apparent last-specified-first-applied order. As an alternative, consider the rotate() variation that rotates about a point.
Well, I've got it doing what I wanted, but I don't really know how. I threw in some random numbers until things showed up on the screen the way I wanted. If anyone wants to clue me in as to a better way to do this, I'm all ears.
The code:
// set the shape of our radar blips
oBlipPath.moveTo(0, -5);
oBlipPath.lineTo(6, 0);
oBlipPath.lineTo(0, 5);
oBlipMatrix.setRotate(45, 0, 0);
oBlipPath.transform(oBlipMatrix);
// Paint all the enemies and radar blips!
for(int i=0; i<iNumEnemies; i++){
oEnemies[i].draw(canvas, (int)worldX, (int)worldY);
if (oEnemies[i].bActive){
//calculate the degree the object is from the center of the screen.
//(user is the player. this could be done easier using iWidth and iHeight probably)
//we use a world coordinate system. worldY and worldX are subtracted
fDegrees = (float)Math.toDegrees(Math.atan2((oEnemies[i].getEnemyCenterY()-worldY)-(iHeight / 2), (oEnemies[i].getEnemyCenterX()-worldX)-(iWidth / 2)));
canvas.save();
//get to the center
canvas.translate((iWidth / 2 + 50) , (iHeight / 2 + 50) );
//move a little bit depending on direction (trying to make arrows appear around a circle)
//canvas.translate((float)(20 * Math.cos(fDegrees)), (float)(20* Math.sin(fDegrees)));
//rotate canvas so arrows will rotate and point in the right direction
canvas.rotate(fDegrees-45, -50, -50);
//draw arrows
canvas.drawPath(oBlipPath, oBlipPaint);
canvas.restore();
}
}
For whatever reason, I have to subtract 45 degrees from the canvas rotation, but add 45 degrees to the matrix rotation of the path shape. It works, but why?! :)