I'm trying to flip some animations in LibGDX, but because they are of different width, the animation plays weird. Here's the problem:
(the red dot marks the X/Y coordinate {0,0})
As you can see, when the animation plays "left" when you punch, the feet starts way behind than were it was, but when you punch right, the animations plays fine because the origin of both animations is the left corner, so the transition is smooth.
The only way I think of achieving what I want is to see what animation is playing and adjust the coordinates accordingly.
This is the code:
public static float draw(Batch batch, Animation animation, float animationState,
float delta,
int posX, int posY, boolean flip) {
animationState += delta;
TextureRegion r = animation.getKeyFrame(animationState, true);
float width = r.getRegionWidth() * SCALE;
float height = r.getRegionHeight() * SCALE;
if (flip) {
batch.draw(r, posX + width, posY, -width, height);
} else {
batch.draw(r, posX, posY, width, height);
}
return animationState;
}
Any suggestion is welcome as how to approach this.
Use some other batch.draw option (with other parameters). You can set "origin" parameters. It's like a hot spot...center of the image... So if you i.e. rotate, rotation will be done around that hot spot.
https://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/g2d/Batch.html
I didn't use it for flipping, but it should work the same way. But if it doesn't then you have to adjust coordinates on your own, make some list with X offset for every frame and add it for flipped images.
Other solution would be to have wider frame images and keep center of the character always match the center of the image. That way your images will be wider then they have to - you'll have some empty space, but for sane number of frame it's acceptable.
Related
I have a problem and although I serached everywhere I couldn't find a solution.
I have a stacked sprite and I'm rotating this sprite around the center of the screen. So I iterate over a list of sprites (stacked) and increase the y-coordinate by 2 every loop (rotation is increased step by step by 0.01f outside of the loop):
foreach(var s in stacked)
{
Vector2 origin = new Vector2(Basic.width / 2, Basic.height / 2);
Rectangle newPosition = new Rectangle(position.X, position.Y - y, position.Width, position.Height);
float angle = 0f;
Matrix transform = Matrix.CreateTranslation(-origin.X, -origin.Y, 0f) *
Matrix.CreateRotationZ(rotation) *
Matrix.CreateTranslation(origin.X, origin.Y, 0f);
Vector2 pos = new Vector2(newPosition.X, newPosition.Y);
pos = Vector2.Transform(pos, transform);
newPosition.X = (int)pos.X;
newPosition.Y = (int)pos.Y;
angle += rotation;
s.Draw(newPosition, origin, angle, Color.White);
y += 2;
}
This works fine. But now my problem. I want not only to rotate the sprite around the center of the screen but also around itself. How to achieve this? I can only set one origin and one rotation per Draw. I would like to rotate the sprite around the origin 'Basic.width / 2, Basic.height / 2' and while it rotates, around 'position.Width / 2, position.Height / 2'. With different rotation speed each. How is this possible?
Thank you in advance!
Just to be clear:
When using SpriteBatch.Draw() with origin and angle, there is only one rotation: the final angle of the sprite.
The other rotations are positional offsets.
The origin in the Draw() call is a translation, rotation, translate back. Your transform matrix shows this quite well:
Matrix transform = Matrix.CreateTranslation(-origin.X, -origin.Y, 0f) *
Matrix.CreateRotationZ(rotation) *
Matrix.CreateTranslation(origin.X, origin.Y, 0f);
//Class level variables:
float ScreenRotation, ScreenRotationSpeed;
float ObjectRotation, ObjectRotationSpeed;
Vector2 ScreenOrigin, SpriteOrigin;
// ...
// In constructor and resize events:
ScreenOrigin = new Vector2(Basic.width <<1, Basic.height <<1);
// shifts are faster for `int` type. If "Basic.width" is `float`:
//ScreenOrigin = new Vector2(Basic.width, Basic.height) * 0.5f;
// In Update():
ScreenRotation += ScreenRotationSpeed; // * gameTime.ElapsedGameTime.Seconds; // for FPS invariant speed where speed = 60 * single frame speed
ObjectRotation+= ObjectRotationSpeed;
//Calculate the screen center rotation once per step
Matrix baseTransform = Matrix.CreateTranslation(-ScreenOrigin.X, -ScreenOrigin.Y, 0f) *
Matrix.CreateRotationZ(ScreenRotation) *
Matrix.CreateTranslation(ScreenOrigin.X, ScreenOrigin.Y, 0f);
// In Draw() at the start of your code snippet posted:
// moved outside of the loop for a translationally invariant vertical y interpretation
// or move it inside the loop and apply -y to position.Y for an elliptical effect
Vector2 ObjectOrigin = new Vector2(position.X, position.Y);
Matrix transform = baseTransform *
Matrix.CreateTranslation(-ObjectOrigin.X, -ObjectOrigin.Y, 0f) *
Matrix.CreateRotationZ(ObjectRotation) *
Matrix.CreateTranslation(ObjectOrigin.X, ObjectOrigin.Y, 0f);
foreach(var s in stacked)
{
Vector2 pos = new Vector2(ObjectOrigin.X, ObjectOrigin.Y - y);
pos = Vector2.Transform(pos, transform);
float DrawAngle = ObjectRotation;
// or float DrawAngle = ScreenRotation;
// or float DrawAngle = ScreenRotation + ObjectRotation;
// or float DrawAngle = 0;
s.Draw(pos, SpriteOrigin, DrawAngle, Color.White);
}
I suggest moving the Draw() parameter away from destinationRectangle and use the Vector2 position directly with scaling. Rotations within square rectangles can differ up to SQRT(2) in aspect ratio, i.e. stretching/squashing. Using Vector2 incurs a cost of higher collision complexity.
I am sorry for the ors, but without complete knowledge of the problem...YMMV
In my 2D projects, I use the vector form of polar coordinates.
The Matrix class requires more calculations than the polar equivalents in 2D. Matrix operates in 3D, wasting cycles calculating Z components.
With normalized direction vectors (cos t,sin t) and a radius(vector length),in many cases I use Vector2.LengthSquared() to avoid the square root when possible.
The only time I have used Matrices in 2D is display projection matrix(entire SpriteBatch) and Mouse and TouchScreen input deprojection(times the inverse of the projection matrix)
I am creating a model of a solar system in processing, and after removing the background I noticed the planets were leaving a trail of their image behind them. The program runs fine when the background is back in, but I want to add a lot more and I am sure this is inefficient and will bog things down.
I am very new to processing, and I am really not sure how to solve this. Maybe delete previous images after a delay to create a shortened trail?
These are just the parts I think are important cherry picked from the code, this is just the example of one planet. Sorry if the code is clunky, any suggestions are happily accepted.
Planet p1;
void setup() {
mercury = loadImage("mercury.png")
p1 = new Planet(40, random(TWO_PI), 0.05);
}
void draw() {
//background(0)
translate(width / 2, height / 2);
p1.display1();
p1.orbit();
}
class Planet {
float radius;
float angle;
float distance;
float orbitSpeed;
Planet(float r, float d, float o) {
radius = r;
distance = d;
orbitSpeed = o;
angle = random(TWO_PI);
}
void orbit() {
angle = angle + orbitSpeed;
}
void display1() {
pushMatrix();
rotate(angle);
translate(distance, 0);
imageMode(CENTER);
image(mercury, radius, radius, 10, 10);
popMatrix();
}
}
I realized that this would probably happen, and I am not sure how to stop it.
The behavior you describe is simply the nature of computer graphics; it's how games, operating systems, and hardware displays all work – they clear and redraw everything every frame.
In Processing, graphic objects that are pushed to a buffer remain there indefinitely until the buffer is cleared or something is pushed on top of them (this is why the planets are leaving a trail without calling background() – previous frames remain in the buffer).
You are worried about the background() being inefficient. Don't be, as it's one of the fastest operations (simply sets the value of each pixel, as given by the user).
Processing does provide a clear() function, but this is equivalent to background(0).
If you're are still concerned about efficiency and speed, one way to speed up Processing is to use the FX2D renderer rather than default AWT renderer. Another way is cache drawn objects into PGraphics objects to prevent successive rasterization (since your planets are image files and not drawn with processing, you needn't worry about this).
Your code is simple enough that it doesn't need optimisations at this stage.
As micycle mentions, you are are drawing an image at a translated position, pretty similar to blitting.
In terms of the trails, one common trick you could use is not clear the screen completely, but draw a transparent rectangle as the background. The more transparency, the longer the trails.
Here's a tweaked version of your code:
// planet object
Planet p1;
// planet texture
PImage mercury;
void setup() {
size(300, 300);
// draw image from center
imageMode(CENTER);
// clear to black one
background(0);
// remove strokes (we'll use rect() later)
noStroke();
// set the fill to black but with 9/255 transparency (~3.5% transparent)
fill(0,9);
// init texture
mercury = loadImage("mercury.png");
// init planet
p1 = new Planet(40, random(TWO_PI), 0.05);
}
void draw() {
// draw a transparent rectangle instead of completely clearing the screen
rect(0,0,width,height);
// render planet
translate(width / 2, height / 2);
p1.display1();
p1.orbit();
}
class Planet {
float radius;
float angle;
float distance;
float orbitSpeed;
Planet(float r, float d, float o) {
radius = r;
distance = d;
orbitSpeed = o;
angle = random(TWO_PI);
}
void orbit() {
angle = angle + orbitSpeed;
}
void display1() {
pushMatrix();
rotate(angle);
translate(distance, 0);
image(mercury, radius, radius, 10, 10);
popMatrix();
}
}
It's an efficient quick'n'dirty hack as you won't need to store previous position and redraw multiple times, however it has it limitations in terms of the flexibility of the trails. Hopefully tweaking the fill() alpha parameter will get you the desired effect.
Later on if you're drawing many many many planets it things start running slow have a peak at VisualVM. Profile the CPU and see the methods that take the longest to complete and focus on those. Don't need to optimise everything, just the slowest calls. Remember that Processing have multiple renderers: JAVA2D is the default one, but there's also FX2D and P2D/P3D and they will behave differently. I strongly recommend optimising at the last moment (otherwise code might be less flexible and readable and will slow down development/iteration).
In Xiaomi devices, there are drawn an image outside of camera's letterbox
In other devices everything is correct
I attached both sumsung and xiaomi images, the screenshot that looks ugly is xiaomi, and good look in samsung
float targetaspect = 750f / 1334f;
// determine the game window's current aspect ratio
float windowaspect = (float)Screen.width / (float)Screen.height;
// current viewport height should be scaled by this amount
float scaleheight = windowaspect / targetaspect;
// obtain camera component so we can modify its viewport
Camera camera = GetComponent<Camera>();
// if scaled height is less than current height, add letterbox
if (scaleheight < 1.0f)
{
Rect rect = camera.rect;
rect.width = 1.0f;
rect.height = scaleheight;
rect.x = 0;
rect.y = (1.0f - scaleheight) / 2.0f;
camera.rect = rect;
}
try setting the image to clamp instead of repeat.
this will give the result of black borders but you won't have that weird texture
I don't know what caused that problem, however i solved it in a tricky way. I just added second camera to display black background. Only My main camera's viewport is letterboxed, but not second camera. So it made display to look good
I'm trying to make my UI elements work and remain the same for every different resolution. I added a Canvas Scaler to my Canvas and played around with the settings until it looked finished.
I then tried building the game and running it at few different resolutions to confirm that it was working. However, the Canvas Scaler doesn't seems to work.
http://prntscr.com/d1afz6
Above is some random resolution but that's how big my editor screen is and that's what I'm using as my reference resolution. That's also the hierarchy for this specific Canvas http://prntscr.com/d1aggx. It takes almost the whole screen when ran at 640x480. I have no clue why this is not working. I've read most of the unity guides on that but none of them seem to have that problem.
Ok, to fit something no matter the size of the screen, you have to use a separate coordinate system than Unity's absolute system. One of Unity's models is the View. The View is coordinates 0,0 at the top left, and 1,1 at the bottom right. Creating a basic Rect that handles that, is the following.
using UnityEngine;
namespace SeaRisen.nGUI
{
public class RectAnchored
{
public float x, y, width, height;
public RectAnchored(float x, float y, float width, float height)
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public static implicit operator Rect(RectAnchored r)
{
return new Rect
{
x = r.x * Screen.width,
y = r.y * Screen.height,
width = r.width * Screen.width,
height = r.height * Screen.height
};
}
}
}
Here, we take the normal Rect floats, the x,y coordinates along with a width and height. But these are in the values [0..1]. I don't clamp it, so it can be tweened on and off the screen with animation, if desired.
The following is a simple script that create's a button in the lower right corner of the screen, and resizes as the screen grows or shrinks.
void MoveMe()
{
RaycastHit hit;
if (Physics.Raycast(transform.position, -Vector3.up, out hit, float.MaxValue)
|| Physics.Raycast(transform.position, Vector3.up, out hit, float.MaxValue))
transform.position = hit.point + Vector3.up * 2;
}
void OnGUI()
{
if (GUI.Button(new RectAnchored(.9f, .9f, .1f, .1f), "Fix me"))
{
MoveMe();
}
}
The X is .9 to the right and Y .9 from the top, and width and height of .1, so the button is 1/10th of the screen in height and width, positioned in the bottom 1/10th of the screen.
Since OnGUI is rendered every frame (or so), the button rect updates with the screen resize automatically. The same would work in a typical UI, if you are using Update() to render your windows.
I hope this explains the difference between what I meant with absolute coordinates. Setting say the previous example (using absolutes) in 640x480, it'd be something like new Rect(576, 432, 64, 48) and it wouldn't scale. By using new RectAnchored(.9f, .9f, .1f, .1f) and have it rendered into UI space based on Screen size, then it scales automatically.
Well it is not surprise what default OpenGL screen coords system quite hard to operate with x-axis: from -1.0 to 1.0, y-axis: from -1.0 to 1.0, and (0.0,0.0) in center of screen.
So i decided to write some wrapper to local game coords with next main ideas:
Screen coords will be 0..100.0 (x-axis), 0..100.0 (y-axis) with (0.0,0.0) in bottom left corner of screen.
There are different screens, with different aspects.
If we draw quad, it must stay quad, not squashed rectangle.
By the quad i mean
quad_vert[0].x = -0.5f;
quad_vert[0].y = -0.5f;
quad_vert[0].z = 0.0f;
quad_vert[1].x = 0.5f;
quad_vert[1].y = -0.5f;
quad_vert[1].z = 0.0f;
quad_vert[2].x = -0.5f;
quad_vert[2].y = 0.5f;
quad_vert[2].z = 0.0f;
quad_vert[3].x = 0.5f;
quad_vert[3].y = 0.5f;
quad_vert[3].z = 0.0f;
I will use glm::ortho and glm::mat4 to achieve this:
#define LOC_SCR_SIZE 100.0f
typedef struct coords_manager
{
float SCREEN_ASPECT;
mat4 ORTHO_MATRIX;//glm 4*4 matrix
}coords_manager;
glViewport(0, 0, screen_width, screen_height);
coords_manager CM;
CM.SCREEN_ASPECT = (float) screen_width / screen_height;
For example our aspect will be 1.7
CM.ORTHO_MATRIX = ortho(0.0f, LOC_SCR_SIZE, 0.0f, LOC_SCR_SIZE);
Now bottom left is (0,0) and top right is (100.0, 100.0)
And it works, well mostly, now we can translate our quad to (25.0, 25.0), scale it to (50.0, 50.0) and it will sit at bottom-left corner with size of 50% percent of screen.
But problem is what it not quad anymore it looks like rectangle, because our screen width not equal with height.
So we use our screen aspect:
CM.ORTHO_MATRIX = ortho(0.0f, LOC_SCR_SIZE * CM.SCREEN_ASPECT, 0.0f, LOC_SCR_SIZE);
Yeah we get right form but another problem - if we position it at (50,25) we get it kinda left then center of screen, because our local system is not 0..100 x-axis anymore, it's now 0..170 (because we multiply by our aspect of 1.7), so we use next function before setting our quad translation
void loc_pos_to_gl_pos(vec2* pos)
{
pos->x = pos->x * CM.SCREEN_ASPECT;
}
And viola, we get right form squad at right place.
But question is - am i doing this right?
OpenGL screen coords system quite hard to operate with x-axis: from -1.0 to 1.0, y-axis: from -1.0 to 1.0, and (0.0,0.0) in center of screen.
Yes, but you will never use them directly. There's usually always a projection matrix, that transforms your coordinates into the right space.
we get it kinda left then center of screen, because our local system is not 0..100 x-axis anymore,
That's why OpenGL maps NDC space (0,0,0) to the screen center. If you draw a quad with coordinates symmetrically around the origin it will stay in the center.
But question is - am i doing this right?
Depends on what you want to achieve.