I've been experimenting with different ways of moving a image over a grid of tiles for a game, but I've been unable to get a working implementation.
First I tried using a grid layout to hold a bunch of Tiles that extended Canvas and drew themselves. This drew the tiles nicely, however it seems that I am unable to draw my Player object on top of them. Originally, the Player also extended Canvas and I intended to have the widget on top of the tiles. It seems like this is impossible.
I then tried to have the Tile simply extend nothing, and just hold the image. I then hold each Tile in a 2D array and draw each Tile by a nested for loop, using the int from the for loop, multiplied by the image size, to draw Tile's Image. I put this code in a PaintListener inside of my constructor for my Map class that extended Canvas and dropped my Map onto my Shell in a Fill layout, but the PaintListener never gets called (I tested with a print statement).
What implementation could I use to draw the Tiles at the start of the game, then allow me to control the movement of my Player image?
I did something similar.
Using a PaintListener I get the calls when the Widget needs to be repainted. In my paint function, I loop over a tile array (wrapped in a World class) and draw all tiles. Afterwards I use the same technique with a worldObjects array/class:
public class WorldWidget extends Canvas {
WorldWidget() {
addPaintListener(new PaintListener() {
#Override
public void paintControl(PaintEvent e) {
WorldWidget.this.paintControl(e);
}
});
}
protected void paintControl(PaintEvent e) {
GC gc = e.gc;
for (short y = 0; y < world.getHeight(); y++) {
for (short x = 0; x < world.getWidth(); x++) {
final ITile tile = world.getTile(x, y);
final Image image = ImageCache.getImage(tile);
gc.drawImage(image, x * tileSize, y * tileSize);
}
}
// Here is used a similar loop, to draw world objects
}
}
This is obviously a condensed code example, as the class is part of an editor and reacts on mouse clicks and movement amongst other things.
When I did a tile based simulation while ago I did it this way:
I had 2 layers of the tile map - one for the terrain and second for the units.
The map itself was represented by a JPanel.
So roughly you got this for the JPanel:
public void paintComponent(Graphics graphics) {
// create an offscreen buffer to render the map
if (buffer == null) {
buffer = new BufferedImage(SimulationMap.MAP_WIDTH, SimulationMap.MAP_HEIGHT, BufferedImage.TYPE_INT_ARGB);
}
Graphics g = buffer.getGraphics();
g.clearRect(0, 0, SimulationMap.MAP_WIDTH, SimulationMap.MAP_HEIGHT);
// cycle through the tiles in the map drawing the appropriate
// image for the terrain and units where appropriate
for (int x = 0; x < map.getWidthInTiles(); x++) {
for (int y = 0; y < map.getHeightInTiles(); y++) {
if (map.getTerrain(x, y) != null) {
g.drawImage(tiles[map.getTerrain(x, y).getType()], x * map.getTILE_WIDTH(), y * map.getTILE_HEIGHT(), null);
}
}
}
if (map.getSimulationUnits() != null) {
for (Unit unit : map.getSimulationUnits()) {
g.drawImage(tiles[unit.getUnitType()], (int) Math.round(unit.getActualXCor() * map.getTILE_WIDTH()), (int) Math.round(unit.getActualYCor() * map.getTILE_HEIGHT()),
null);
}
}
// ...
// draw the buffer
graphics.drawImage(buffer, 0, 0, null);
}
Logic:
private Terrain[][] terrain = new Terrain[WIDTH][HEIGHT];
/** The unit in each tile of the map */
private Unit[][] units = new Unit[WIDTH][HEIGHT];
Then you have your game loop where you update the position of the units and other things, basically render() and update() the game. Check the links I've provided below.
NOTE
Since you are making a simple game this post about making game loops will be definitely useful for you. This hopefully also answer your question about moving the object on the map.
This site will be also very helpful since you will probably need to detect collision at some point too.
Related
So I'm creating a game using Javax.swing library for my uni coursework.
I have created a window and I have successfully written code to procedurally generate a game map.
However, I am unable to change the focus of the map. What I mean is that the map is always stuck in one corner of the screen. (IE: Location is set to 0,0, hence the Graphics g (the map) is put in that location going outwards.)
I would like to be able to move the "camera" so that different areas of the map can be viewed by the player.
Bellow I have pasted my method that draws the map onto the screen. Could anyone tell me what I could do to have the camera move at runtime. AKA: to shift the map left or right.
I thought of having a Graphics object that will hold the map, and then I'd only draw a subImage of that Graphics object, but considering how the map will be redrawn every frame (For animation purposes) that just means that I'll have even more graphics to redraw.
The map is 6,400 * 6,400 Pixels
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
try {
for(int x = 0; x < OverworldMap.MAP_X_SIZE; x++){
for(int y = 0; y < OverworldMap.MAP_Y_SIZE; y++){
for(int layer = 0; layer < OverworldMap.MAP_LAYER_SIZE; layer++) {
g.drawImage(OverworldMap.getTileAt(x, y, layer).getSprite(), x * SPRITE_SIZE, y * SPRITE_SIZE, null);
}
}
}
} catch (Exception e) {
LauncherClass.printErrorLog(e);
}
}
The best / easiest way to solve this is to put a JScrollPane around your JPanel, and make the JPanel the size of your image. You don't need to worry about only repainting the right part of your image - Java is pretty smart about only drawing the parts that are on screen. Note that you can show or hide the ScrollBars, but if you hide them you need to add logic to activate scrolling through some other mechanism
You cannot store a Graphics object and use it later. It is only valid for the duration of the paint method to which it is passed.
You can, however, simply offset your painting:
Image sprite = OverworldMap.getTileAt(x, y, layer).getSprite();
g.drawImage(sprite, x * SPRITE_SIZE - playerX, y * SPRITE_SIZE - playerY, this);
(Notice that the last argument to drawImage should be this.)
I'm working on a Java program with Processing library, that plots/draws millions of galaxies (the coordinates do not change) in 3D-space (as simple points) and allows translating, rotating and scaling the view in real time.
I'm using Processing to have GPU acceleration on rendering the view, but the points have to be given to the graphics card by the CPU anyway. So currently my draw() method contains a for loop which "draws" the points one by one. Since nothing about the points changes, only the camera angle, position and scale, it is too slow for my needs.
Current draw():
transformSpace();
for (int i = 0; i < visibleBodies.length; i++) {
Body b = visibleBodies[i];
point(b.x, -b.z, -b.y); // Z-up world
}
Could all the points be handled somehow differently, so draw() wouldn't have to contain a massive loop? I've been unsuccessful in trying to find a way to feed the coordinates to the GPU in a separate one-time process or make all the data one big object.
You could draw the to a PGraphics ahead of time, and then just draw that PGraphics when you want to draw your bodies.
PGraphics image;
void setup() {
size(500, 500);
image = createGraphics(500, 500);
image.beginDraw();
for (int i = 0; i < visibleBodies.length; i++) {
Body b = visibleBodies[i];
image.point(b.x, -b.z, -b.y); // Z-up world
}
image.endDraw();
}
void draw() {
background(255, 0, 0);
image(image, 0, 0, width, height);
}
More info can be found in the Processing reference.
I'm porting a class that that was previously done in Swing to JavaFX 8. It displays a UI element that looks like an analog electric voltage meter with a half circle surrounded by a collection of "tic marks" at regular intervals. In the Swing version the class was an extension of JPanel and the tic marks were drawn in paintComponent(Graphics g) as follows:
private Line2D ticLine = new Line2D.Float(0, LINE_ROOT_Y, TIC_LENGTH, LINE_ROOT_Y);
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// Draw tic marks
if (ticCount > 0)
{
g2.draw(ticLine); // First tic
AffineTransform ticTrans = new AffineTransform();
// Draw all additional tics rotated around half circle
for (int i = 1; i < ticCount; i++)
{
ticTrans.rotate(Math.toRadians(ticGap),
METER_MIDDLE, METER_BASE_Y);
g2.draw(ticTrans.createTransformedShape(ticLine));
}
}
}
This worked fine.
Now with JavaFX I'm using a class extending VBox. It contains 2 stacked Canvas objects. One of which will draw the static elements like the half circle and tic marks, and the other for the regularly moving meter line. On that first Canvas I was hoping to use a similar loop as I did in the Swing version to easily redraw the first tic mark in a ticCount # of additional positions around the half circle. So I tried the following which compiled and ran but only drew that first tic mark:
// Called from the constructor:
MeterGC = MeterCanvas.getGraphicsContext2D();
Line ticLine = new Line(0, LINE_ROOT_Y, TIC_LENGTH, LINE_ROOT_Y);
// Draw tic marks
if (ticCount > 1)
{
MeterGC.setStroke(Color.GRAY);
MeterGC.setLineWidth(BASIC_LINE_WIDTH);
MeterGC.strokeLine(ticLine.getStartX(), ticLine.getStartY(),
ticLine.getEndX(), ticLine.getEndY());
Rotate ticTrans = new Rotate(Math.toRadians(ticGap), METER_MIDDLE, METER_BASE_Y);
for (int i = 1; i < ticCount; i++)
{
ticLine.getTransforms().add(ticTrans);
MeterGC.strokeLine(ticLine.getStartX(), ticLine.getStartY(),
ticLine.getEndX(), ticLine.getEndY());
}
}
It may be that trying to Transform a Shape object like this only works when drawing to a scene and not on a Canvas. Or that I have to do something after the "ticLine.getTransforms().add(ticTrans)" line to get them to apply to the line. Am I at least close? Or is there a much better way to do what I'm trying here?
What you are doing wrong
In your sample code you are applying the transform to a Line object (which you never display).
How to fix it
You need to set the transform on the canvas GraphicsContext before you stroke the line on the canvas.
Sample code
For an example, see:
How to draw image rotated on JavaFX Canvas?
/**
* Sets the transform for the GraphicsContext to rotate around a pivot point.
*
* #param gc the graphics context the transform to applied to.
* #param angle the angle of rotation.
* #param px the x pivot co-ordinate for the rotation (in canvas co-ordinates).
* #param py the y pivot co-ordinate for the rotation (in canvas co-ordinates).
*/
private void rotate(GraphicsContext gc, double angle, double px, double py) {
Rotate r = new Rotate(angle, px, py);
gc.setTransform(r.getMxx(), r.getMyx(), r.getMxy(), r.getMyy(), r.getTx(), r.getTy());
}
This is my revised for-loop and it works perfectly. Noticing that converting the angle in Rotate to radians is no longer necessary in JavaFX was also key in getting it to work.
for (int i = 1; i < ticCount; i++)
{
Rotate ticTrans = new Rotate(ticGap * i, METER_MIDDLE, METER_BASE_Y);
MeterGC.setTransform(ticTrans.getMxx(), ticTrans.getMyx(), ticTrans.getMxy(),
ticTrans.getMyy(), ticTrans.getTx(), ticTrans.getTy());
MeterGC.strokeLine(ticLine.getStartX(), ticLine.getStartY(),
ticLine.getEndX(), ticLine.getEndY());
}
I avoided the word bitmap in the title as bitmap in this context usually (?) refers to the bitmap from the underlying image.
I have an image that is segmented into a number of different regions. For each region I have a map of ones and zeros (a bitmap) where 1 represents inside the region and zero outside the region. Not every part of the image is covered with a region, and the regions may overlap. The images are of the dimension (480x360).
What I would like to do is to overlay the image with a transparent red when you hoover the region with your mouse. My problem is that my current method is very slow and it takes a second or two before the overlay appears.
My current approach is using a JLayer over my ImagePanel (extension of JPanel drawing a BufferedImage). Then my instance of the LayerUI draws the overlay when the mouse is moved:
public class ImageHighlightLayerUI extends LayerUI<JPanel> {
private boolean mouseActive;
private Point mousePoint;
private byte[][][] masks;
public void paint(Graphics g, JComponent c) {
super.paint(g, c);
if (mouseActive) {
byte[][] curMask = null;
// Find which region the mouse intersect
for (int i = 0; i < masks.length; i++) {
if (masks[i][mousePoint.x][mousePoint.y] == 1) {
curMask = masks[i];
break;
}
}
// Outside region --> don't draw overlay
if (curMask == null) return;
//Transparent red
g.setColor(new Color((float)1.0,
(float)0.0, (float)0.0, (float)0.8));
//Draw the mask
for(int x = 0; x < curMask.length; x++)
for(int y = 0; y < curMask[y].length; y++)
if (curMask[x][y] == 1)
g.fillRect(x, y, 1, 1);
}
}
}
So, how can I make this more efficient? I open to suggestions using other ways than a JLayer. Can I use my bitmap in some "magic" way with some swing-method? Can I mix it with the underlying bitmap from the BufferedImage? Is removing transparancy the only thing that'll help me? (Which is something I would like to keep)
Two other side problems which are not necessarily related to the question, but I have yet to solve:
The overlay is repainted every time the mouse moves. This seems like a waste of resources.
When regions are overlapping, how do I choose which one to paint?
I am working on a platformer game that will use tile maps, which I don't know if is a good idea!
I've made a neat tile map editor with tools for setting a spawn point etc. but now that I want to be able to test play the game after editing map and for future use I need of course integrate physics which I've done with Box2D which comes with LibGDX!
I am creating a method to create a collision map from tile map which has data if tile is collideable or not!
So I came up with this great idea:
loop through the map and if we find a colliding tile loop through its neighbor tiles and see if they're colliding too, and do this til noncolliding tile is found when we set width and height for the colliding rectangle
after we got bunch of rectangle I sort them in order from biggest square to smallest so we get biggest pieces and I add the rectangles to final list and check against the final rect if any of them overlaps with current body so I don't have overlapping bodys
But you know, code tells more than 1000 words, right?
public void createBody() {
List<Rectangle> allRects = new ArrayList<Rectangle>();
for(int x = 0; x < info.getWidth(); x++) {
for(int y = 0; y < info.getHeight(); y++) {
if(tiles[x][y].getInfo().isColliding()) {
int width = 1;
int height = 1;
//loop through neighbors horizontally
for(int i = 0; i < info.getWidth() - x; i++) {
if(!tiles[x + i][y].getInfo().isColliding()) {
//if tile is not clipped, we set width to i which is current x offset
width = i;
break;
}
}
//only if width is bigger than zero can the rect have any tiels..
if(width > 0) {
boolean breakingBad = false;
//loop through neighbors horizontally
for(int j = 0; j < info.getHeight() - y; j++) {
//loop though neigbors vertizally
for(int i = 0; i < width; i++) {
//check if tile is not colliding
if(!tiles[x + i][y + j].getInfo().isColliding()) {
//and if so, we set height to j which is current y offset
height = j;
//breaking bad aka leaving both loops
breakingBad = true;
break;
}
}
if(breakingBad) {
break;
}
}
}
if(width * height > 0)
allRects.add(new Rectangle(x, y, width, height));
}
}
}
Collections.sort(allRects, new Comparator<Rectangle>() {
#Override
public int compare(Rectangle o1, Rectangle o2) {
Integer o1Square = o1.width * o1.height;
Integer o2Square = o2.width * o2.height;
return o2Square.compareTo(o1Square);
}
});
List<Rectangle> finalRects = new ArrayList<Rectangle>();
mainloop:
for(Rectangle rect: allRects) {
for(Rectangle finalRect: finalRects) {
if(finalRect.contains(rect)) {
continue mainloop;
}
}
finalRects.add(rect);
}
for(Rectangle rect: finalRects) {
PolygonShape polyShape = new PolygonShape();
polyShape.setAsBox((float)rect.getWidth() / 2, (float)rect.getHeight() / 2, Vector2.tmp.set((float)rect.getCenterX(), (float)rect.getCenterY()), 0f);
mapBody.createFixture(polyShape, 1);
polyShape.dispose();
}
}
however this sill seems pretty inefficient because for some reasons its still creating smaller fixtures than it could be possible, for example in upper right corner
also its creating single fixtures in the corners of the center rectangle and I can't figure out why!
Is the whole idea all inefficient, and should I use other method or manually create collision maps or what could be the best idea?
Originally each tile was its own fixture which caused weird bugs on their edges as expected
First off, a custom tile mapping tool is a great idea on the surface, but you're reinventing the wheel.
libGDX has built-in support for TMX maps.
http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/maps/tiled/TmxMapLoader.html
Instead of using your homebrew editor, you can use a full featured editor such as this Tiled - http://www.mapeditor.org/
So once you have a better system in place for your maps, I would look at this from an object oriented perspective. Since you want to use box2d physics, each collidableTile HAS A body. So all you need to do is assign a physics body to each collidableTile, and set the size according to your standard tile size.
Don't forget that there is a difference between the box2d world and your game screen, where box2d is measured in metric units, and your screen is measured in pixels. So you need to do some math to set positions and size properly. If you want a set of tiles to share a body, you may want to pass in the body as a parameter when you construct each collidableTile, and then adjust the size of the body based on how many adjacent tiles you can find. More complex shapes for the physics body may be more complex.
You can also save resources by setting those tiles to 'sleep', where box2d does a reduced simulation on those bodies until it detects a collision. If you're only using box2d for collision detection on terrain, you may want to consider other options, like using shape libraries to detect intersections, and then setting the box2d physics on your player characters body to stop downward acceleration while there is contact, or something.