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
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'm working on a desktop only project using the libgdx game library, and I'm looking to implement a way for when the user scrolls horizontally to pan the camera. I'd like to avoid using libgdx's scene2d if possible. However, I can't see to find a way to capture horizontal scrolls. Is there anyway to detect a horizontal mouse scroll in Libgdx? I'd like to avoid using libgdx's scene2d if possible. And if there is not, is there a way to do it using plain java, without using awt, swing, or javafx?
Horizontal mouse scrolls? There is no such thing unless you are using a mouse with multiple scroll wheels. Usually the mouse has one scroll wheel.
However, if you mean moving the mouse horizontally, you should be able to capture that using getDeltaX(). Note that it's a raw input, so you will want to divide it by the screen's width in order to make movement the same on any monitor you use. You may also want to include a sensitivity multiplier so that the user may choose how fast panning is.
It's often helpful to allow rotation of the camera using the same input by checking whether a key is held down. Consider this pseudocode:
void updateCamera() {
if (LeftShiftPressed()) {
RotateCamera(getNormalizedX());
} else {
PanCamera(getNormalizedX());
}
}
float getNormalizedX() {
return float(getDeltaX()) / float(getScreenWidth())
}
void PanCamera(float x_movement) {
// pan by x_movement * pan_multiplier
}
void RotateCamera(float x_movement) {
// rotate by x_movement * rotation_multiplier
}
I'm trying to make my first game in JavaFX, where a player object (not a Node) will move inside a level. However, the full level shouldn't be visible inside the window, only a part of it surrounding the player should be. I've searched quite a bit for a solution to this that works with the way I've done my project, but haven't been successful. The following code describes how I've set up my GUI:
private VBox appRoot;
private Pane gameRoot;
private Canvas canvas;
private GraphicsContext gc;
private HBox uiRoot;
This is the different panes I use. The appRoot contains both the gameRoot and the uiRoot, in that order. The gameRoot contains the canvas. Everything inside the level, including the level itself, is drawn onto the canvas, like this:
canvas = new Canvas(ROOM_WIDTH, ROOM_HEIGHT);
gc = canvas.getGraphicsContext2D();
createLevel(ROOM_WIDTH, ROOM_HEIGHT, CELL_WIDTH, CELL_HEIGHT);
gc.scale(2, 2);
drawLevel(gc, ROOM_WIDTH, ROOM_HEIGHT, CELL_WIDTH, CELL_HEIGHT);
player.render(gc);
createLevel and drawLevel just creates a level and draws squares where different blocks should be. The player object's render method draws the player at its x- and y-coordinates. As you can see I've scaled the canvas, so as of now the entire level isn't shown when the game is started.
The problem is that I can't make it so that what is shown in the window is the things surrounding the player object. I've seen some code utilizing the methods setLayoutX and setLayoutY, but I've been unable to reproduce this so that it works.
I would be very grateful if anybody could help me out with this, and hopefully without changing too much of how the drawing is done.
Thanks! :)
Also, here are two pictures showing how it looks; the first is when the player is not inside the current view, and the second is with the player in the view.
Image without player in view
Image with player in view
Do not us the Canvas directly to display the level. Instead you can use an image that represents your level and then have the image in an ImageView. On that ImageView you can define a viewport of the area of the image that is visible.
To get the image you can either use your Canvas node and take a snapshot (See this question). This is probably the easiest way with what you have. Or you can forego the Canvas altogether and draw the image. If the levels are predefined (and not generated at runtime), this seems the best approach, then you can supply the images as resources along with the code.
I am hoping the saveLayer methods will allow me to do draw onto different "layers" and then once the drawing has finished, merge the layers with the canvas in whichever order i choose.
The obvious question is "why dont you just rearrange your drawing operations instead?" The answer is I can't:
I have a Path that I need to draw onto a Canvas. In the background/lowest z-index I want to draw the path closed and with a few additional points using a fill style. Then on top of that, I want to draw an outline of only the points that were originally in the Path.
Since I cannot undo the adding of points to the Path, my only choices are to clone the path, or to draw to a second layer which can later be placed on top of everything else.
saveLayer() seems to offer that functionality but it doesnt behave the way I was expecting. The basic flow of my operations is like this:
int overlay = canvas.saveLayer(...);
// drawing operations for my uppermost layer
...
int background = canvas.saveLayer(...);
// drawing operations for my background layer
...
// merge the offscreen background bitmap with the canvas:
canvas.restoreToCount(background);
// merge the offscreen overlay bitmap with the canvas:
canvas.restoreToCount(overlay);
When the code runs, the ordering of background and overlay have not changed at all; what gets drawn first is at the bottom and what gets drawn last is on top. Even stranger to me is that I can completely comment out both calls to restoreToCount() and nothing changes. According to the javadoc, nothing should be drawn to the canvas until a balancing restore() is invoked.
Obviously I am completely misunderstanding the function of this method. Can anybody help me to understand saveLayer's usage, or perhaps suggest an alternate way to layer my drawing operations?
Thx!
Nick
saveLayer() does not let you rearrange the layers in a random order. The only way to do it is to draw in offscreen bitmaps yourself. Note also that your view's parent will call save()/restore() around your onDraw() call, which will cause your layers to be composited.
I'm writing a simple painting program. To simulate a pencil drawing, I've stored the drawn points in a set, so when the window is resized the points won't be gone, and whenever another point is added I call the repaint method (the paint method draws all paints in the array on screen). But when the number of points increases, the program runs slowly.
Is there a more efficient method to do this?
The fastest way to get constant-speed repainting is to store the entire image that's drawn as a bitmap, and of course update it when the user draws. This way, you can redraw the right thing, and even add scrollbars and the like if you want. You can keep the array of points for an "undo" feature, for example.
If instead you want to make a vector-drawing program, then you have to keep the individual primitive operations (such as line, rectangles, etc). In that case it still pays off to keep an image for fast repainting, and now the trick becomes how to efficiently update that image when the user modifies the drawing (e.g. by resizing a rectangle). One way to do that is to tile the drawing area into lots of smaller ones, so you only need to recompute the underlying image for the parts that intersect with the object being modified by the user.
In both techniques, you'd use double-buffering just so that the user doesn't perceive flicker. But with double-buffering alone, the "redraw the whole set of points" would still be slow.
Since you haven't give any code, I am guessing that you are using primitive drawing methods (like draw a line or point).
A good option would be to use the Double Buffering technique to do the painting.
Look up GeneralPath.
What you can do is create a Shape class, that'll vary according to what you are going to draw (Rectangle, Triangle, Point, Line, etc.). You should do this even though now you're only drawing points.
When you create your Shape have code that checks what Shape to create, something like a switch/case statement:
...
case PENCIL:
if (generalPath == null) {
generalPath = new GeneralPath();
generalPath.moveTo(p1.x, p1.y);
} else {
generalPath.lineTo(p2.x, p2.y);
}
shape = generalPath;
break;
...
In the code that draws, simply call repaint and the Shape will be drawn.