I'm creating a camera in canvas like the one in Super Smash Bros, where the center of the camera follows the center point of all players and scales to encompass all the players.
I have it set up to find the distance between the 2 players, and if it's larger than the canvas size, the camera scale lowers to decrease the size of the blocks, player sprites, etc.
ctx.scale(cameraS,cameraS);
ctx.translate(-(cameraX*cameraS)+(CANVAS_WIDTH/2),-(cameraY*cameraS)+(CANVAS_HEIGHT/2));
These are what scale and move the drawn images to a position relative to the screen.
This is the actual game using the code and as you can tell, the scaling and moving of the images is slightly incorrect, but I'm not sure why!
https://dl.dropboxusercontent.com/u/51784213/Conjugate/index.html
For reference, the red dot is the position centered between both players. The lines show the dead center of the actual canvas. When scaling is 1(no scaling at all), the red dot is completely centered as it should be. When the scaling starts to decrease, the red dot begins to move off center in weird directions.
For the code to be working correctly, the dot should be centered at all times, even during the scaling process!
Transformations are applied in the reverse order; so you are first translating and then scaling. This means that for a point (x, y), after the current transformation, you get
(
(x + CANVAS_WIDTH/2 - cameraX*cameraS) * cameraS,
(y + CANVAS_HEIGHT/2 - cameraY*cameraS) * cameraS
)
What's actually needed here is the canvas be translated by scaled (cameraX, cameraY) and then be offset by actual (CANVAS_WIDTH/2, CANVAS_HEIGHT/2), so that (cameraX, cameraY) is at center of the visible canvas.
Or rather, the transformation needed here for a point (x, y) is
(
(x - cameraX) * cameraS + CANVAS_WIDTH/2,
(y - cameraY) * cameraS + CANVAS_HEIGHT/2
)
Hence, the code becomes, if you choose to apply translate first,
ctx.scale(cameraS,cameraS);
ctx.translate(-cameraX+CANVAS_WIDTH/(2*cameraS),-cameraY+CANVAS_HEIGHT/(2*cameraS));
Or, if you choose to apply scaling first
ctx.translate(-cameraX*cameraS + CANVAS_WIDTH/2, -cameraY*cameraS + CANVAS_HEIGHT/2);
ctx.scale(cameraS, cameraS);
Working JSFiddle.
Related
I have composited my sprites to build a monster truck with customizable bumpers, cabs, spoilers wheels etc. The class that holds these Sprites is MTruck and I can draw it perfectly provided I stay with scale 1.0.
mWheels.setPosition(posX + 17 * scale, posY);
mCab.setPosition(posX + 22 * scale, posY + 7 * scale);
mFender.setPosition(posX, posY + 75 * scale);
mWheels is positioned at the y origin of the Truck and mFender at the x origin.
I've tried all sorts of values for scale and extracting it separate from the scale I apply to mWheels, mCab etc but all that happens is the sprites scale but their positions become misaligned.
I'm going to have to render to a texture and scale that as I whole if I can't crack this.
perhaps, Set origin could help you:
void setOrigin(float originX, float originY)
Sets the origin in relation to the sprite's position for scaling and
rotation.
float getOriginX()
The origin influences setPosition(float, float),
setRotation(float) and the expansion direction
of scaling setScale(float, float)
float getOriginY()
The origin influences setPosition(float, float),
setRotation(float) and the expansion direction of
scaling setScale(float, float)
http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/g2d/Sprite.html
NEW EDIT: maybe if you are customizing the vehicle on a menu for example, as is practicable after this the customized vehicle and create an image at runtime, and scale, an use this image in Sprite, for maybe it's easier, but it's just an idea
Using the first photo below, let's say:
The red outline is the stage bounds
The gray box is a Sprite on the stage.
The green box is a child of the gray box and has a rotation set.
both display object are anchored at the top-left corner (0,0).
I'd like to rotate, scale, and position the gray box, so the green box fills the stage bounds (the green box and stage have the same aspect ratio).
I can negate the rotation easily enough
parent.rotation = -child.rotation
But the scale and position are proving tricky (because of the rotation). I could use some assistance with the Math involved to calculate the scale and position.
This is what I had tried but didn't produce the results I expected:
gray.scaleX = stage.stageWidth / green.width;
gray.scaleY = gray.scaleX;
gray.x = -green.x;
gray.y = -green.y;
gray.rotation = -green.rotation;
I'm not terribly experienced with Transformation matrices but assume I will need to go that route.
Here is an .fla sample what I'm working with:
SampleFile
You can use this answer: https://stackoverflow.com/a/15789937/1627055 to get some basics. First, you are in need to rotate around the top left corner of the green rectangle, so you use green.x and green.y as center point coordinates. But in between you also need to scale the gray rectangle so that the green rectangle's dimensions get equal to stage. With uniform scaling you don't have to worry about distortion, because if a gray rectangle is scaled uniformly, then a green rectangle will remain a rectangle. If the green rectangle's aspect ratio will be different than what you want it to be, you'd better scale the green rectangle prior to performing this trick. So, you need to first transpose the matrix to offset the center point, then you need to add rotation and scale, then you need to transpose it away. Try this set of code:
var green:Sprite; // your green rect. The code is executed within gray rect
var gr:Number=green.rotation*Math.PI/180; // radians
var gs:Number=stage.stageWidth/green.width; // get scale ratio
var alreadyTurned:Boolean; // if we have already applied the rotation+scale
function turn():void {
if (alreadyTurned) return;
var mat:flash.geom.Matrix=this.transform.matrix;
mat.scale(gs,gs);
mat.translate(-gs*green.x,-gs*green.y);
mat.rotate(-1*gr);
this.transform.matrix=mat;
alreadyTurned=true;
}
Sorry, didn't have time to test, so errors might exist. If yes, try swapping scale, translate and rotate, you pretty much need this set of operations to make it work.
For posterity, here is what I ended up using. I create a sprite/movieClip inside the child (green) box and gave it an instance name of "innerObj" (making it the actually content).
var tmpRectangle:Rectangle = new Rectangle(greenChild.x, greenChild.y, greenChild.innerObj.width * greenChild.scaleX, greenChild.innerObj.height * greenChild.scaleY);
//temporary reset
grayParent.transform.matrix = new Matrix();
var gs:Number=stage.stageHeight/(tmpRectangle.height); // get scale ratio
var mat:Matrix=grayParent.transform.matrix;
mat.scale(gs,gs);
mat.translate(-gs * tmpRectangle.x, -gs * tmpRectangle.y);
mat.rotate( -greenChild.rotation * Math.PI / 180);
grayParent.transform.matrix = mat;
If the registration point of the green box is at one of it's corners (let's say top left), and in order to be displayed this way it has a rotation increased, then the solution is very simple: apply this rotation with negative sign to the parent (if it's 56, add -56 to parent's). This way the child will be with rotation 0 and parent -> -56;
But if there is no rotation applied to the green box, there is almost no solution to your problem, because of wrong registration point. There is no true way to actually determine if the box is somehow rotated or not. And this is why - imagine you have rotated the green box at 90 degrees, but changed it's registration point and thus it has no property for rotation. How could the script understand that this is not it's normal position, but it's flipped? Even if you get the bounds, you will see that it's a regular rectangle, but nobody know which side is it's regular positioned one.
So the short answer is - make the registration point properly, and use rotation in order to display it like in the first image. Then add negative rotation to the parent, and its all good :)
Edit:
I'm uploading an image so I can explain my idea better:
As you can see, I've created a green object inside the grey one, and the graphics INSIDE are rotated. The green object itself, has rotation of 0, and origin point - top left.
#Vesper - I don't think that the matrix will fix anything in this situation (remember that the green object has rotation of 0).
Otherwise I agree, that the matrix will do a pretty job, but there are many ways to do it :)
I am trying to make an actor follow the player's finger (long touch). I'm positive I have the math right, but the actor fails to move exactly to where the player touched.
Here is an illustration of my problem:
When the touch is near the top, the actor goes beyond the visible scene at the top.
When the touch is near the bottom, the actor goes out of the visible scene at the bottom.
Same goes for the left and right.
When the touch is performed in the middle of the scene the actor moves perfectly to the touch. In short, the further the touch is away from the middle the more pronounced the distance between the actor and the touch is. In other words; the closer the touch is to the middle, the closer the actor moves towards the touch.
Please note that when the touch was near the bottom or the top the distance between the touch and the actor was more pronounced then when the touch was on the right or the left; as the top/bottom are further from the mid point.
Here is the code used to follow the actor towards the touch:
Lang: Lua
Lib: Cocosd2-x 3.1
local velocity = 1.4
local x, y = self.sprite:getPosition()
-- self.dest[X/Y] are cached coordinates to where the actor should move next.
local angle = math.atan2(touch.y - y, touch.x - x)
local deltaX = velocity * math.cos(angle)
local deltaY = velocity * math.sin(angle)
local newX = x + deltaX
local newY = y + deltaY
self.sprite:setPositionX(newX)
self.sprite:setPositionY(newY)
Things I've tried:
Changed the scale of background layer and sprites. No change
Changed the algorithm used to compute the angle. No change.
Created a red dot and set its position to the exact touch x/y to determine if there was some weird transformation issue when determining the actor's point. The red dot was always perfectly under the touch.
Discovered the issue. When I created the Actor sprite I set its z-index to 100. When I uncommented out the call that set the z-index, everything worked perfectly. In my situation, this particular sprite must always be above all other sprites. What I did to fix the issue is set the z-index much lower than what I had originally set it to; which ended up being 15.
sprite:setPositionZ(15)
From my observation it appears that the sprite is having some type of scale applied to its position the larger the z-index is of the sprite.
Update 1
Using :setPositionZ(int) will unnecessarily scale your sprite bigger in some cases. I now use :setGlobalZOrder(int) with much better success:
sprite:setGlobalZOrder(15)
So I am super stoked to start using LibGDX for my first android title for OUYA and PC, but I am running into some snags with LibGDX. (All of my questions can be answered by looking at source, but I am really trying to understand the design choices as well).
To start with, the coordinate system. I created a project using the Project Setup jar, and it creates an OrthographicCamera like so
camera = new OrthographicCamera(1, h/w);
From my reading, I understand that LibGdx uses bottom left corner for 0,0 and yUp. Fine.
I see that it is pretty easy to change to y down if I want to, but I am not understanding the next bit of code that was created.
For the default sprite that gets created the position is set like so.
logoSprite.setOrigin(logoSprite.getWidth()/2, logoSprite.getHeight()/2);
logoSprite.setPosition(-logoSprite.getWidth()/2, -logoSprite.getHeight()/2);
When I run this basic program, I see the logo image I have added is centered on the screen. What I am trying to understand is why the values are negative in set position, and why is it using the sprite width and height instead of the graphics w and h of the view port? If I change to the screen width and height, then the image is drawn in some odd position in the lower right hand side of the screen.
My next question is sprite.setSize vs sprite.setScale. Why is the difference between the two? (They appear to do the same thing, except setScale leaves getWidth and getHeight unchanged).
Since my game will be using a 2D camera heavily for panning, zooming and rotation, I am trying to understand as much as I can about the libgdx framework before I start writing any code.
As a side note, I have a game development and math background and I have made several 2D and 3D games using XNA. I am finding LibGdx a bit frustrating as it does not abstract away OpenGL as much as I was expecting it to, and so far the 2D drawing I have been experimenting with seems to be more confusing than it should be!
I also wanted to note that I am planning to use spine for my animations. Should that change my choice to use y-up or y-down?
If you want to draw a sprite in center of screen, do this in your create method
logosprite.setposition(scrw/2-logosprite.getwidth()/2,scrh/2-logosprite.getheight/2);
here scrw is your viewport's width,
and scrh is your viewport's height,
this way your sprite will be in center of screen
sprite.setsize is used for setting size of the sprite and sprite.setscale is used when we scale a large/small texture so that its quality remains good in all devices(hdpi.mdpi,xhdpi,ldpi)..
no need to worry if you are using spine it works smoothly in libgdx..
You can use just this code if possible
logoSprite.setPosition(Gdx.graphics.getWidth()/2 - image.getWidth()/2,
Gdx.graphics.getHeight()/2 - image.getHeight()/2);
To center the sprite into the middle of the screen Where "image" is the Texture you have loaded/declared initially.
As for why it is coming in a odd position is due to the fact that you are using a camera.
Which changes the view a lot just go through the documentations of libgdx about camera here
In my case, I needed to set position of camera and then call update() method.
Then never forget camera's (0,0) is its center. Everything is being placed that way. My camera code:
private void cameralariUpdateEt() {
cameraGame.position.set(cameraGame.viewportWidth * 0.5f,
cameraGame.viewportHeight * 0.5f, 0);
cameraGame.update();
cameraScore.position.set(cameraScore.viewportWidth * 0.5f,
cameraScore.viewportHeight * 0.5f, 0);
cameraScore.update();
}
Call this method from inside render();
Step 1: Set the sprite origin to the position you would like it to rotate around.
// camera center point is (c.x, c.y)
logoSprite.setOrigin(c.x, c.y);
Step 2: make sure to set your sprite center to origin
logoSprite.setOriginCenter();
Step 3: Rotate your sprite
logoSprite.setRotation(Angle);
Step 4: Set the sprite position, [subtract half the sprites width and height to center the sprite]
logoSprite.setPosition(c.x - logoSprite.getWidth() / 2, c.y - logoSprite.getHeight() / 2)
I have a papervision3d sphere primitive that I am adding to a scene. The sphere is being sized with a radius of 285. However, because of my camera properties (zoom, focus and z) the sphere is rendering at 206px instead of 570px which is what I want (2 * radius).
A while back I found a blog post concerning this very issue and it was explained that you can set the z of any object in the scene to appear in 3D space the same size it would appear in 2D space by using the following:
mySphere.z = (this.camera.zoom * this.camera.focus) - Math.abs(this.camera.z) + radius;
The above worked when I was using several blocks, where radius was replaced with width/2. For some reason this does not work for the sphere.
Any ideas, pointers on how to get a sphere with a 570px diameter to show up on screen?
Thanks.
This cannot be achieved, at least not precisely. The perspective projection does not map a sphere to a circle. See how the spheres at the edges of this image are getting deformed?
(source: uni-stuttgart.de)