I'm trying to create a "progress bar" of sorts but the clipping doesn't seem to work the way I expect it to. Is this something I am doing wrong or something I've misinterpreted?
The draw() routine that should clip:
#Override
public void draw(SpriteBatch batch, float parentAlpha) {
Rectangle scissors = new Rectangle();
Rectangle clipBounds = new Rectangle(getX(), getY(), getWidth() * 0.75f, getHeight());
ScissorStack.calculateScissors(
getStage().getCamera(),
getStage().getGutterWidth(),
getStage().getGutterHeight(),
getStage().getCamera().viewportWidth,
getStage().getCamera().viewportHeight,
batch.getTransformMatrix(),
clipBounds, scissors);
if (ScissorStack.pushScissors(scissors)) {
super.draw(batch, parentAlpha);
ScissorStack.popScissors();
}
}
}
Complete sample code for ClipTest group class, TestScreen and screenshot.
ClipTest is a subclass of group used to demonstrate the "bug".
ClipImage is a subclass of Image, which performs the clipping on draw().
ClipTest has 2 images, background and foreground.
The background is a black image and it should always be the full size of the progress bar.
The foreground is a white image and it's width is clipped depending on the percentage of the bar.
The strange result I've found is that although the foreground is using the clipping class, the background image is the one actually clipped.
The expected result was created using photoshop (as I couldn't produce it via code).
Any idea what's wrong?
Actual drawing doesn't happen until the Batch "flushes", its not the draw call you need to wrap, as that just queues up drawing to be done later.
You need to make sure the OpenGL draw calls happen between enabling and disabling your scissors, so add a flush after the draw. See
https://github.com/libgdx/libgdx/wiki/Clipping,-with-the-use-of-scissorstack
Because a draw call might cause a flush to happen, you need to keep the draw calls inside the active-scissor region. You may also need to flush or end the batch before starting the active scissor region to prevent queued draw calls from before the scissor start getting flushed inside the active scissor region.
Related
I have a custom view that allow me to draw lines on canvas and it works fine .
what I want is to see the drawing while its drawing in the foreground and make the user control the speed of drawing and can it be convert the drawing period to video ?
my code for drawing lines is
public ArrayList<BlockDraw> drawers = new ArrayList<>();
public void draw(Canvas canvas) {
for (BlockDraw blockDraw : drawers) {
canvas.drawLines(blockDraw.list.pointlist, 0, blockDraw.list.count, blockDraw.paint);
}}
If you have a custom view, onDraw gets called when the view needs to update, and you draw on the canvas to create the new image. It's just a single image, drawLines is just a convenient way of drawing a bunch of stuff.
You can call invalidate() at the end of onDraw to make it fire next frame, so you're constantly redrawing the canvas - that will let you create an animation by changing what you draw over time, e.g. gradually increasing the end value in drawLines from 0 to blockDraw.list.count (depending on the "draw speed" you let the user set).
That would just let you draw 1 line, then 2 lines, then 3 etc. If you want to animate each line actually extending from its start point, like you're watching it being drawn by a pen, that's a lot more complex, but you'd do it the same way! Each frame, calculate what needs to be drawn and draw it, depends on how complex you want to make it.
Video is a whole other thing, the emulator will let you record video of your app if that's what you want.
I am trying to render some sprites and I need the line batch.setProjectionMatrix(cam.combined); in order for the sprites to move independently from the camera, however when I add this line the sprites suddenly turn abnormally large, but without it they are just normal. I have the suspicion that it changes the world dimensions or something like that, because the the boundaries I set for the player sprite are also expanded, just like the size of the sprites, but the background (a tiledmap rendered with a OrthogonalTiledMapRenderer) stays the same.
This is the normal rendering as it should be: https://ibb.co/m4T2GT
This is the rendering i get when using setProjectionMatrix: https://ibb.co/j0EL38
(I used an external site for the pictures because for some reason I could not add them here)
This is my render function.
player.draw(..);
and
e.draw(..);
just call the draw function of the Sprite class.
public void render() {
handleInput();
renderer.setView(cam);
renderer.render();
batch.setProjectionMatrix(cam.combined);
batch.begin();
player.draw(batch);
for (Entity e: enemies) e.draw(batch);
batch.end();
}
You can use the float: cam.zoom to change how large everything appears. Change it in setup or just after you initialise the camera object, then it should remain at that zoom.
If that doesn't work, you might need to set the map rendering to the camera's projection matrix as well.
Take a look at this screenshoot:
I want to split my UI to two parts, controls and game draw area, but I don't want the controls to overlap the draw area, I want the 0,0 of the game draw area, start above the controls area.
Is that possible to do with Libgdx?
As you said you want each view to have its own coordinate system thus splitting the screen technique as answered by Julian will do the trick, but it's not all to properly use it.
To make it fully works, you should have 2 separate OrthographicCamera one for game draw view, and another for control view. I suggest also to create 2 Viewport mapping to each camera as well. In my experience, when working in multiple camera situation, always create associate Viewport for it. Better for changes that could introduce in the future (i.e. adapt to any screen resolution), debugging purpose like checking touching, position etc.
So combine splitting technique with camera/viewport management, you will have robust system to work for each area independently.
Code
I provided the following code as it's used and working in my game, but changed variable names to fit your need. It's in Kotlin, but should be relatively easy to see it as Java.
You initialize things first for game area stuff.
// create a camera
gameAreaCamera = OrthographicCamera()
gameAreaCamera.setToOrtho(false, GAME_WIDTH, GAMEVIEW_HEIGHT)
gameAreaCamera.update()
// create a viewport associated with camera
gameAreaViewport = ExtendViewport(GAME_WIDTH, GAMEVIEW_HEIGHT, gameAreaCamera)
Next, for control area stuff.
// create a camera
controlAreaCamera = OrthographicCamera()
controlAreaCamera.setToOrtho(false, GAME_WIDTH, CONTROLVIEW_HEIGHT)
controlAreaCamera.update()
// create a viewport associated with camera
controlAreaViewport = ExtendViewport(GAME_WIDTH, CONTROLVIEW_HEIGHT, controlAreaCamera)
PS: Notice width and height of each view. It's set to occupy area as per your intention.
Now you should have something like this in render() method.
override fun render() {
// clear screen
Gdx.gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT)
// draw game area
drawGameArea()
// draw control area
drawControlArea()
}
For your drawGameArea() assume that sb is your SpriteBatch that you maintain it in the current class,
private fun drawGameArea() {
// the following twos to let system know we will operate against game area's camera
// set the projection matrix
sb.projectionMatrix = gameAreaCamera.combined
// set gl viewport
Gdx.gl.glViewport(0,0,GAME_WIDTH, GAMEVIEW_HEIGHT)
// draw your stuff here...
sb.begin()
...
sb.end()
}
That would goes in the same way for drawControlArea()
private fun drawControlArea() {
// the following twos to let system know we will operate against control area's camera
// set the projection matrix
sb.projectionMatrix = controlAreaCamera.combined
// set gl viewport
Gdx.gl.glViewport(0,GAMEVIEW_HEIGHT,GAME_WIDTH, CONTROLVIEW_HEIGHT)
// draw your stuff here...
sb.begin()
...
sb.end()
}
Note Gdx.gl.glViewport() we supply it with target rectangle area to draw things on.
Viewports are not directly used, but it's more to tell the system which kind of screen resizing strategy to fit your game's graphic into screen, and for better debugging purpose. You can read more here.
Two frequently used options you will use most is ExtendViewport and FitViewport. If you want the game to be appeared and cover entire area of the screen without affect aspect-ratio with no black-bars (black on left-side or right-side), ExtendViewport is likely to be what you want, or if you want the similar effect but with black-bars thus game screen will be the same for every player (thus provide no advantage over player with wide-screen) then FitViewport is your choice.
Checking Hit On UI
I guess you will need this, so I should include it too.
Whenever you need to check whether such UI element is clicked (or tapped) by user, then you can have the following in its corresponding update() method to check whether touching position is within bound of such object's bounding area or not.
The following code safely and work very well with ExtendViewport, but it also should work the same with other Viewport as the code doesn't require any specific information from ExtendViewport. So it's generic.
fun update(dt: Float, cam: Camera: viewport: Viewport) {
// convert screen coordinate to world coordinate
val location = Vector3(Gdx.input.getX(), Gdx.input.getY(), 0f)
cam.unproject(location, viewport.screenX.toFloat(), viewport.screenY.toFloat(), viewport.screenWidth.toFloat(), viewport.screenHeight.toFloat())
if ((Gdx.input.isTouched() &&
boundingRect.contains(location.x, location.y)) {
// do something here...
}
}
You can use two cameras and position them as shown in your image. I think this post could give you a hint: Split-Screen in LibGDX
I've been trying to draw an image of a block with an eye on the screen. I want to be able to animate more than one texture in the same way, so drawing the animations by hand is not an option. The problem is that when I move the eyelid sprite higher up than completely closed, part of the eyelid shows above the expected bounds like this. Obviously, this is a problem as it looks quite unnatural. I'd like to either have an alternative solution to this problem, or be able to crop the eyelid's Sprite object to fit behind the rest of the image. The final image consists of the eyeball, pupil (as I want to be able to animate this too), eyelid, main body and the outlines, drawn in that order. The render function looks like this:
public void render(float delta) {
float height = this.eyelid.getHeight();
float eyeHeight = height*0.7f;
this.eyelid.setY(this.eye.getY()+(eyeHeight*((100-this.lid)/100f)));
batch.begin();
this.eye.draw(batch);
this.pupil.draw(batch);
this.eyelid.draw(batch);
this.main.draw(batch);
this.shade.draw(batch);
batch.end();
}
this.lid is the % of how closed the eye is, and the image for the eyelid itself can be found here. How could I solve this problem, or how could I crop the sprite? I don't want to have to reload the texture as a sprite every frame.
I think what you're looking for is the ScissorStack class, which is documented on the libGDX wiki. You can use this object to clip around the eye's frame so that the top of the lid doesn't show.
How can I draw something in JPanel that will stay the same and not be repainted, I am doing a traffic simulation program and I want the road to be drawn once because It will not change.
Thanks
I'm not sure you actually want your road to never be repainted - repaint events fire (for example) when your window is resized, or when it becomes visible following another window obstructing it. If your panel never repaints then it'll look peculiar.
As far as I remember, Swing will only fire appropriate paint events for these circumstances, so you should be OK following the usual method of subclassing JPanel with a suitable override:
public class RoadPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// your drawing code here
}
}
If you cache your road into an image or another graphics format (to save calculating the display data multiple times) once drawn once, this might save you some time on subsequent paints.
To my knowledge, no, unless there is a trick with transparent overlays.
Most graphical applications I saw (and did) just re-draw the whole panel on each repaint. Now, you can do that once, in a graphic buffer, and then just paint the whole background at once, quickly, by copying the graphic buffer to the JPanel. It should be faster than calling all graphical primitives to draw the road.
Or, the way some 2D games do, perhaps paint it once and update the moving parts, like sprites: it needs to erase the old place used by the sprites (restore the background there) and re-draw the sprites at the new place. So you still have a copy of the road in a graphic buffer but instead of re-drawing it whole each time, you update only some small parts. Can be slightly faster.
The component will need to be repainted every time that the panel is obscured (ie frame minimized/another window put on top). Therefore drawing something only once will not work as you want it to. To make parts that do not change be drawn more efficiently you can draw them once to a 'buffer' image, and then just draw this buffer each time that the panel or component needs to be redrawn.
// Field that stores the image so it is always accessible
private Image roadImage = null;
// ...
// ...
// Override paintComponent Method
public void paintComponent(Graphics g){
if (roadImage == null) {
// Create the road image if it doesn't exist
roadImage = createImage(width, height);
// draw the roads to the image
Graphics roadG = roadImage.getGraphics();
// Use roadG like you would any other graphics
// object to draw the roads to an image
} else {
// If the buffer image exists, you just need to draw it.
// Draw the road buffer image
g.drawImage(roadImage, 0, 0, null);
}
// Draw everything else ...
// g.draw...
}
What I do is set a boolean value to whether or not a certain part needs to be redrawn. Then, in the paintComponent() method I can check the value and redraw the certain thing, or not.
protected void paintComponent(Graphics g){
super.paintComponent(g);
if (drawRoad) {
drawRoadMethod(g);
}
drawTheRest(g);
}
Kinda like that.