Drawing bounding box around contours JavaCV? - java

I am wondering how to draw a bounding box around contours using JavaCV. I know the area of pixels and the center point. I also found a way to find the pixel width to find the distance. I feel a bounding box would be more accurate to find the pixel width to find the distance then what I am doing. Any help would be great or if you know another way to find the distance that would be great. Thanks...
import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.opencv_core.CvMemStorage;
import org.bytedeco.javacpp.opencv_core.IplImage;
import org.bytedeco.javacpp.opencv_videoio.CvCapture;
import static org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_imgproc.*;
public class Webcam {
public static void main(String[] args) throws Exception {
CvCapture capture = opencv_videoio.cvCreateCameraCapture(0);
IplImage img1, imghsv, imgbin;
CvScalar minc = cvScalar(95,125,75,0), maxc = cvScalar(145,255,255,0);
CvSeq contour1 = new CvSeq(), contour2;
CvMemStorage storage = CvMemStorage.create();
CvMoments moments = new CvMoments(Loader.sizeof(CvMoments.class));
double areaMax = 1000, areaC = 0;
double m01, m10, m_area, focal, width, obj_width, obj_height;
double distance;
//focal is (pixel width * distance in inches) / object width
focal = 144.4;
//Real objects width in inches
obj_width = 3.5;
//Real objects height in inches
obj_height = 3.5;
int posX=0, posY=0;
int cRad = 100;
while(true)
{
img1 = opencv_videoio.cvQueryFrame(capture);
opencv_imgproc.cvSmooth(img1, img1, CV_MEDIAN, 13, 0, 0, 0);
imgbin = IplImage.create(cvGetSize(img1), 8, 1);
imghsv = IplImage.create(cvGetSize(img1), 8, 3);
if(img1 == null) break;
cvCvtColor(img1, imghsv, CV_BGR2HSV);
cvInRangeS(imghsv, minc, maxc, imgbin);
contour1 = new CvSeq();
areaMax = 1000;
cvFindContours(imgbin, storage, contour1, Loader.sizeof(CvContour.class), CV_RETR_LIST, CV_LINK_RUNS, cvPoint(0,0));
contour2 = contour1;
while(contour1 != null && !contour1.isNull())
{
areaC = cvContourArea(contour1, CV_WHOLE_SEQ, 1);
if(areaC > areaMax)
{
areaMax = areaC;
}
contour1 = contour1.h_next();
}
while(contour2 != null && !contour2.isNull())
{
areaC = cvContourArea(contour2, CV_WHOLE_SEQ, 1);
if(areaC < areaMax)
{
cvDrawContours(imgbin, contour2, CV_RGB(0,0,0),CV_RGB(0,0,0),0,CV_FILLED,8,cvPoint(0,0));
}
contour2 = contour2.h_next();
}
cvMoments(imgbin, moments, 1);
m10 = cvGetSpatialMoment(moments, 1, 0);
m01 = cvGetSpatialMoment(moments, 0, 1);
m_area = cvGetCentralMoment(moments, 0, 0);
posX = (int) (m10/m_area);
posY = (int) (m01/m_area);
if(posX > 0 && posY > 0)
{
cRad = (int) (100 / (5000/m_area));
cvCircle(img1, cvPoint(posX, posY), 5, cvScalar(0,255,0,0), 9, 0, 0);
}
//Change numbers after m_area to size of object
width = java.lang.Math.sqrt((m_area/(obj_height*obj_width)));
distance = (obj_width * focal) / width;
cvFlip(img1, img1, 1);
cvFlip(imgbin, imgbin , 1);
opencv_highgui.cvShowImage("Color",img1);
opencv_highgui.cvShowImage("CF",imgbin);
char c = (char) opencv_highgui.cvWaitKey(15);
if(c == 27) break;
if(c == 'q')
{
System.out.print("Width in pixels ");
System.out.println(width);
System.out.print("Distance in inches ");
System.out.println(distance);
}
}
}
}
This is what I have This is what I want . I am able to find all the blue contours and have the background turned black. I would just like to draw a bounding box around the blue pixels to help find the distance better to the object and to make sure we are tracking the right object.

If you want to draw a bouding box around a contour, you can do that with just using:
Rect rect = opencv_imgproc.boundingRect(contour);
opencv_imgproc.rectangle(src, rect, Scalar.GREEN);

Related

Rotating shape and drawing it at the original position

I'm trying to draw a rotated shape at a given point. To give an example, in the following image, the red rectangle is a non-rotated rectangle drawn at a point and then the blue rectangle is rotated and drawn at the same position. The blue rectangle is the outcome I'm aiming for.
I've been experimenting and trying different methods. Currently, here is what I used for the image:
Point point = new Point(300, 300);
Dimension dim = new Dimension(200, 100);
double radians = Math.toRadians(30);
g.setColor(new java.awt.Color(1f, 0f, 0f, .5f));
g.fillRect(point.x, point.y, dim.width, dim.height);
translate(g, dim, radians);
g.rotate(radians, point.getX(), point.getY());
g.setColor(new java.awt.Color(0f, 0f, 1f, .5f));
g.fillRect(point.x, point.y, dim.width, dim.height);
private static void translate(Graphics2D g, Dimension dim, double radians) {
if (radians > Math.toRadians(360)) {
radians %= Math.toRadians(360);
}
int xOffsetX = 0;
int xOffsetY = 0;
int yOffsetX = 0;
int yOffsetY = 0;
if (radians > 0 && radians <= Math.toRadians(90)) {
xOffsetY -= dim.getHeight();
} else if (radians > Math.toRadians(90) && radians <= Math.toRadians(180)) {
xOffsetX -= dim.getWidth();
xOffsetY -= dim.getHeight();
yOffsetY -= dim.getHeight();
} else if (radians > Math.toRadians(180) && radians <= Math.toRadians(270)) {
xOffsetX -= dim.getWidth();
yOffsetX -= dim.getWidth();
yOffsetY -= dim.getHeight();
} else {
yOffsetX -= dim.getWidth();
}
int x = rotateX(xOffsetX, xOffsetY, radians);
int y = rotateY(yOffsetX, yOffsetY, radians);
g.translate(x, y);
}
private static int rotateX(int x, int y, double radians) {
if (x == 0 && y == 0) {
return 0;
}
return (int) Math.round(x * Math.cos(radians) - y * Math.sin(radians));
}
private static int rotateY(int x, int y, double radians) {
if (x == 0 && y == 0) {
return 0;
}
return (int) Math.round(x * Math.sin(radians) + y * Math.cos(radians));
}
This works for rectangles but doesn't work for other types of shapes. I'm trying to figure out if there is a way to accomplish this for every type of shape. Also note that the code is just for testing purposes and there are a lot of bad practices in it, like calling Math.toRadians so much.
Something like this?
It can be achieved using a rotate transform first, then using the bounds of the rotated shape as a basis, the translate transform can be used to shift it back to meet the top most y and leftmost x values of the original rectangle.
See the getImage() method for one implementation of that.
int a = angleModel.getNumber().intValue();
AffineTransform rotateTransform = AffineTransform.getRotateInstance((a*2*Math.PI)/360d);
// rotate the original shape with no regard to the final bounds
Shape rotatedShape = rotateTransform.createTransformedShape(rectangle);
// get the bounds of the rotated shape
Rectangle2D rotatedRect = rotatedShape.getBounds2D();
// calculate the x,y offset needed to shift it to top/left bounds of original rectangle
double xOff = rectangle.getX()-rotatedRect.getX();
double yOff = rectangle.getY()-rotatedRect.getY();
AffineTransform translateTransform = AffineTransform.getTranslateInstance(xOff, yOff);
// shift the new shape to the top left of original rectangle
Shape rotateAndTranslateShape = translateTransform.createTransformedShape(rotatedShape);
Here is the complete source code:
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.EmptyBorder;
public class TransformedShape {
private JComponent ui = null;
JLabel output = new JLabel();
JToolBar tools = new JToolBar("Tools");
ChangeListener changeListener = (ChangeEvent e) -> {
refresh();
};
int pad = 5;
Rectangle2D.Double rectangle = new Rectangle2D.Double(pad,pad,200,100);
SpinnerNumberModel angleModel = new SpinnerNumberModel(30, 0, 90, 1);
public TransformedShape() {
initUI();
}
private BufferedImage getImage() {
int a = angleModel.getNumber().intValue();
AffineTransform rotateTransform = AffineTransform.getRotateInstance((a*2*Math.PI)/360d);
Shape rotatedShape = rotateTransform.createTransformedShape(rectangle);
Rectangle2D rotatedRect = rotatedShape.getBounds2D();
double xOff = rectangle.getX()-rotatedRect.getX();
double yOff = rectangle.getY()-rotatedRect.getY();
AffineTransform translateTransform = AffineTransform.getTranslateInstance(xOff, yOff);
Shape rotateAndTranslateShape = translateTransform.createTransformedShape(rotatedShape);
Area combinedShape = new Area(rotateAndTranslateShape);
combinedShape.add(new Area(rectangle));
Rectangle2D r = combinedShape.getBounds2D();
BufferedImage bi = new BufferedImage((int)(r.getWidth()+(2*pad)), (int)(r.getHeight()+(2*pad)), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(new Color(255,0,0,127));
g.fill(rectangle);
g.setColor(new Color(0,0,255,127));
g.fill(rotateAndTranslateShape);
g.dispose();
return bi;
}
private void addModelToToolbar(String label, SpinnerNumberModel model) {
tools.add(new JLabel(label));
JSpinner spinner = new JSpinner(model);
spinner.addChangeListener(changeListener);
tools.add(spinner);
}
public final void initUI() {
if (ui!=null) return;
ui = new JPanel(new BorderLayout(4,4));
ui.setBorder(new EmptyBorder(4,4,4,4));
ui.add(output);
ui.add(tools,BorderLayout.PAGE_START);
addModelToToolbar("Angle", angleModel);
refresh();
}
private void refresh() {
output.setIcon(new ImageIcon(getImage()));
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = () -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
ex.printStackTrace();
}
TransformedShape o = new TransformedShape();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
};
SwingUtilities.invokeLater(r);
}
}
You have a shape, any shape.
You have a point (px,py) and you want to rotate the shape around this point and angle ag measured counter-clokwise.
For each point of the shape the proccess has three steps:
Translate to (px,py)
Rotate
Translate back to (0,0)
The translation is fully simple
xNew = xOld - px
yNew = yOld - py
The rotation is a bit less simple
xRot = xNew * cos(ag) - yNew * sin(ag)
yRot = xNew * sin(ag) + yNew * cos(ag)
Finally the translation back:
xDef = xRot + px
yDef = yRot + py
A bit of explanation: Any transformation can be seen in two ways: 1) I move the shape 2) I move the axis-system. If you think about it, you'll find that the trasnsformation is relative: seen from the axis point of view or seen from the shape point of view.
So, you can say "I want coordinates in the translated system", or you can also say "I want the coordinates of the translated shape".
It doesn't matter what point of view you chose, the equations are the same.
I'm explaining this so much, just to achieve you realize which is the positive direction of the angle: clockwise or counter-clockwise.

Java OpenCV detect and crop circullar/elliptical shapes

I am trying to detect and crop circullar/elliptical shapes of different sizes.
This is an example of an image I am trying to do the detection and croping.
Input Image
The result I am trying to get in the aforementioned image is 3 cropped images
looking like this:
segmented part 1, segmented part 2, segmented part 3
Another image could look like this: different image
Just like the previous image, I am trying to do the same to this one.
The shapes are dramatically smaller from the first one.
Can this be achieved algorithmically or should I look for a machine learning-like solution?
Note: The final image has been applied by the following filters: Gaussian Blur, Grayscale, Threshold, Contour and Morphological Dilation.
[EDIT]
The code I have written(not working as intended):
findReference() finds a shape in the middle of the image and returns its rectangle.
private Rect findReference(Mat inputImage) {
// clone the image
Mat original = inputImage.clone();
// find the center of the image
double[] centers = {(double)inputImage.width()/2, (double)inputImage.height()/2};
Point image_center = new Point(centers);
// finding the contours
ArrayList<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();
Imgproc.findContours(inputImage, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
// finding best bounding rectangle for a contour whose distance is closer to the image center that other ones
double d_min = Double.MAX_VALUE;
Rect rect_min = new Rect();
for (MatOfPoint contour : contours) {
Rect rec = Imgproc.boundingRect(contour);
// find the best candidates
if (rec.height > inputImage.height()/2 & rec.width > inputImage.width()/2){
continue;
}
Point pt1 = new Point((double)rec.x, (double)rec.y);
Point center = new Point(rec.x+(double)(rec.width)/2, rec.y + (double)(rec.height)/2);
double d = Math.sqrt(Math.pow((double)(pt1.x-image_center.x),2) + Math.pow((double)(pt1.y -image_center.y), 2));
if (d < d_min)
{
d_min = d;
rect_min = rec;
}
}
// showReference( rect_min, original);
return rect_min;
}
I use the rectangle as reference and create a bigger one and a smaller one, so that similar shapes fit in the dimensions of the smaller and bigger rectangle.
findAllEllipses() tries to find similar shapes fitting in the smaller and bigger rectangles. After that it draws ellipses around the found shapes.
private Mat findAllEllipses(Rect referenceRect, Mat inputImage) {
float per = 0.5f;
float perSquare = 0.05f;
Rect biggerRect = new Rect();
Rect smallerRect = new Rect();
biggerRect.width = (int) (referenceRect.width / per);
biggerRect.height = (int) (referenceRect.height / per);
smallerRect.width = (int) (referenceRect.width * per);
smallerRect.height = (int) (referenceRect.height * per);
System.out.println("reference rectangle height: " + referenceRect.height + " width: " + referenceRect.width);
System.out.println("[" + 0 +"]: biggerRect.height: " + biggerRect.height + " biggerRect.width: " + biggerRect.width);
System.out.println("[" + 0 +"]: smallerRect.height: " + smallerRect.height + " smallerRect.width: " + smallerRect.width);
//Finding Contours
List<MatOfPoint> contours = new ArrayList<>();
Mat hierarchey = new Mat();
Imgproc.findContours(inputImage, contours, hierarchey, Imgproc.RETR_TREE,
Imgproc.CHAIN_APPROX_SIMPLE);
System.out.println("the numbers of found contours is: " + contours.size());
int sum = 0;
//Empty rectangle
RotatedRect[] rec = new RotatedRect[contours.size()];
for (int i = 0; i < contours.size(); i++) {
rec[i] = new RotatedRect();
if(contours.get(i).toArray().length >= 5 ){
Rect foundRect = Imgproc.boundingRect(contours.get(i));
// Rect foundBigger = new Rect();
// Rect foundSmaller = new Rect();
//
// foundBigger.width = (int) (foundBigger.width + foundBigger.width * per);
// foundBigger.height = (int) (foundBigger.height + foundBigger.height * per);
//
// foundSmaller.width = (int) (foundRect.width - foundRect.width * per);
// foundSmaller.height = (int) (foundRect.height - foundRect.height * per);
if (
(biggerRect.height >= foundRect.height && biggerRect.width >= foundRect.width)
&& (smallerRect.height <= foundRect.height && smallerRect.width <= foundRect.width)
&& (((foundRect.width - foundRect.width * perSquare) <= foundRect.height) && ((foundRect.width + foundRect.width * perSquare) >= foundRect.height))
&& (((foundRect.height - foundRect.height * perSquare) <= foundRect.width) && ((foundRect.height + foundRect.height * perSquare) >= foundRect.width))
) {
System.out.println("[" + i +"]: foundRect.width: " + foundRect.width + " foundRect.height: " + foundRect.height);
System.out.println("----------------");
rec[i] = Imgproc.fitEllipse(new MatOfPoint2f(contours.get(i).toArray()));
sum++;
}
}
Scalar color_elli = new Scalar(190, 0, 0);
Imgproc.ellipse(inputImage, rec[i], color_elli, 5);
}
System.out.println("found ellipses: " + sum);
// trytest(ImageUtils.doResizeMat(outputImage),0,0);
return inputImage;
}
Unfortuantelly there are several variables that are hardcoded into the method.
This is used to make the smaller and bigger rectangles (used as a percentage)
float per = 0.5f;
perSquare is used to get shapes closer to a square (fluctuated width height)
float perSquare = 0.05f;
This code might work in some images, while on others will not find a single shape, like I mentioned the shapes are circullar/elliptical and of different sizes.

Corner detection not accurate

I'm trying to detect corners, but the coordinates I get are always off-center and saddle-points are detected Multiple times.
I tried cornerHarris, cornerMinEigenVal, preCornerDetect, goodFeaturesToTrack, and cornerEigenValsAndVecs, but they all seem to lead to the same result. I haven't tried findChessboardCorners because my corners are not laid out in a nice grid of n×m, are not all saddle-type, and many more reasons.
What I have now:
Given the (pre-processed) camera image below with some positive, negative, and saddle corners:
After cornerHarris(img, energy, 20, 9, 0.1) (I increased blockSize to 20 for illustrative purposes but small values don't work either) I get this image:
It seems to detect 10 corners but the way they are positioned is odd. I superimposed this image on the original to show my problem:
The point of highest matching energy is offset towards the inside of the corner and there is a plume pointing away from the corner. The saddle corners seem to generate four separate plumes all superimposed.
Indeed, when I perform a corner-search using this energy image, I get something like:
/
What am I doing wrong and how can I detect corners accurately like in this mock image?
[[edit]] MCVE:
public class CornerTest {
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
private static Mat energy = new Mat();
private static Mat idx = new Mat();
public static void main(String... args) {
Mat byteImage = Highgui.imread("KXw7O.png");
if (byteImage.channels() > 1)
Imgproc.cvtColor(byteImage, byteImage, Imgproc.COLOR_BGR2GRAY);
// Preprocess
Mat floatImage = new Mat();
byteImage.convertTo(floatImage, CvType.CV_32F);
// Corner detect
Mat imageToShow = findCorners(floatImage);
// Show in GUI
imageToShow.convertTo(byteImage, CvType.CV_8U);
BufferedImage bufImage = new BufferedImage(byteImage.width(), byteImage.height(), BufferedImage.TYPE_BYTE_GRAY);
byte[] imgArray = ((DataBufferByte)bufImage.getRaster().getDataBuffer()).getData();
byteImage.get(0, 0, imgArray);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(new JLabel(new ImageIcon(bufImage)));
frame.pack();
frame.setVisible(true);
}
private static Mat findCorners(Mat image) {
Imgproc.cornerHarris(image, energy, 20, 9, 0.1);
// Corner-search:
int minDistance = 16;
Core.MinMaxLocResult minMaxLoc = Core.minMaxLoc(
energy.submat(20, energy.rows() - 20, 20, energy.rows() - 20));
float thr = (float)minMaxLoc.maxVal / 4;
Mat tmp = energy.reshape(1, 1);
Core.sortIdx(tmp, idx, 16); // 16 = CV_SORT_EVERY_ROW | CV_SORT_DESCENDING
int[] idxArray = new int[idx.cols()];
idx.get(0, 0, idxArray);
float[] energyArray = new float[idx.cols()];
energy.get(0, 0, energyArray);
int n = 0;
for (int p : idxArray) {
if (energyArray[p] == -1) continue;
if (energyArray[p] < thr) break;
n++;
int x = p % image.cols();
int y = p / image.cols();
// Exclude a disk around this corner from potential future candidates
int u0 = Math.max(x - minDistance, 0) - x;
int u1 = Math.min(x + minDistance, image.cols() - 1) - x;
int v0 = Math.max(y - minDistance, 0) - y;
int v1 = Math.min(y + minDistance, image.rows() - 1) - y;
for (int v = v0; v <= v1; v++)
for (int u = u0; u <= u1; u++)
if (u * u + v * v <= minDistance * minDistance)
energyArray[p + u + v * image.cols()] = -1;
// A corner is found!
Core.circle(image, new Point(x, y), minDistance / 2, new Scalar(255, 255, 255), 1);
Core.circle(energy, new Point(x, y), minDistance / 2, new Scalar(minMaxLoc.maxVal, minMaxLoc.maxVal, minMaxLoc.maxVal), 1);
}
System.out.println("nCorners: " + n);
// Rescale energy image for display purpose only
Core.multiply(energy, new Scalar(255.0 / minMaxLoc.maxVal), energy);
// return image;
return energy;
}
}

Libgdx Scaling coordinates for different devices

I have made a simple game where you click on a object and it should dissapear. It works fine on desktop with the dimensions 240 x 480 but on my phone the dimensions are wider like 1920 x 1080 so the touch down coordinates are different so on desktop it might log the touch 100 x 50 but if i tapped the same place on phone it would be 400 x 200 so i just want to scale them or use the same.
private double WidthScale = (272 / Gdx.graphics.getWidth());
private double HeightScale = (408 / Gdx.graphics.getHeight());
private Array<Rectangle> rockets;
private long lastDropTime = 0;
private float tap_X = 0;
private float tap_Y = 0;
public GameRenderer() {
cam = new OrthographicCamera();
cam.setToOrtho(false, 408, 272);
batch = new SpriteBatch();
rockets = new Array<Rectangle>();
spawnRocket();
}
public void render(){
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
cam.update();
batch.setProjectionMatrix(cam.combined);
batch.begin();
for(Rectangle rocket : rockets) {
batch.draw(AssetLoader.rocket ,rocket.x, rocket.y,
rocket.width, rocket.height);
}
batch.end();
if (Gdx.input.justTouched()){
tap_X = (int) (Gdx.input.getX() * HeightScale);
tap_Y = (int) ((Gdx.graphics.getHeight()-Gdx.input.getY()) * WidthScale);
Gdx.app.log("MyTag", String.valueOf(tap_X));
Gdx.app.log("MyTag", String.valueOf(tap_Y));
Gdx.app.log("MyTag", String.valueOf(HeightScale));
Gdx.app.log("MyTag", String.valueOf(WidthScale));
}
Iterator<Rectangle> iter = rockets.iterator();
while(iter.hasNext()) {
Rectangle rocket = iter.next();
rocket.y -= 70 * Gdx.graphics.getDeltaTime();
if(rocket.y + 32 < 0) iter.remove();
if (rocket.x < tap_X && tap_X < rocket.x + rocket.width) {
Gdx.app.log("MyTag", "getRekt");
if (tap_Y > rocket.y && tap_Y < rocket.y + rocket.height) {
Gdx.app.log("MyTag", "poo");
iter.remove();
}
}
}
if(TimeUtils.nanoTime() - lastDropTime > 1000000000) spawnRocket();
}
private void spawnRocket() {
Gdx.app.log("MyTag", "Rocket Spawned");
Rectangle rocket = new Rectangle();
rocket.x = MathUtils.random(0 , 272 - 16);
rocket.y = 408 + rocket.height;
rocket.height = 32;
rocket.width = 16;
rockets.add(rocket);
lastDropTime = TimeUtils.nanoTime();
}
In order to work with different screen sizes, you have to deal with viewports, there's an entirely section in libGDX wiki that shows how to work with it. A fast example could be the following (however, I suggest to read the wiki and try others viewports by yourself):
// Declare a viewport object
private Viewport v;
public GameRenderer() {
// initialize after your camera initialization
v = new FitViewport(408, 272, cam); // <- I have use a FitViewport
// but you can use others
// I guess you could remove the setToOrtho() but not too sure...
// ... rest of code
}
You have to properly dispose and update this object *
A thing you should consider is, when you use something like Gdx.input.getX() you are getting a screen current position, you have to transform this coordinate system to your world coordinate system. Wherever you use something like that you have to do the following:
Vector3 v = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0);
v = cam.unproject(v);
// now v has your coordinate world system and you can properly make use of it
// you can do now something like:
tap_x = (int) v.x;
tap_y = (int) v.y;
Hope you find this useful.

Centering map or camera 2D game

I've tried so many solutions that it's possible that my code is a bit mixed up, but whatever I try, it just won't work.
Basically I made a map with Tiled, where my player can run around and bump into stuff. I want the whole map to be visible for the whole time (it's 20 by 15, 64 pixels a tile). The camera doesn't need to move around or follow the player, it has to stay still at the center of the map.
The problem is that the map only shows in the upper right corner of the screen. When I centered the camera to the map itself it messed up the collission detection, (bumping into trees while they were not visible & walking through visible trees). So what I want to do is center the map to 0,0 where my camera also is (at least I think..).
Another thing I'd like to accomplish is that the size of the map gets resized to match different mobile phones. Tried to accomplish this with the stretchviewport, but haven't been able to test this.
public class PlayScreen implements Screen {
TiledMap map;
OrthogonalTiledMapRenderer mapRenderer;
OrthographicCamera cam;
float unitScale = 1 / 64f;
OrthogonalTiledMapRenderer renderer = new OrthogonalTiledMapRenderer(map, unitScale);
Viewport viewport;
public void show() {
map = new TmxMapLoader().load("maps/map.tmx");
mapRenderer = new OrthogonalTiledMapRenderer(map);
cam = new OrthographicCamera(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
cam.setToOrtho(false);
viewport = new StretchViewport(1280, 960, cam);
bounds = new ArrayList<Rectangle>();
for(int i = 0; i < 20; i++){
for(int j = 0; j < 15; j++){
TiledMapTileLayer cur = (TiledMapTileLayer) map.getLayers().get(1);
Cell cell = new Cell();
Vector3 center = new Vector3(cur.getWidth() * cur.getTileWidth() / 2, cur.getHeight() * cur.getTileHeight() / 2, 0);
cam.position.set(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2, 0);
cam.update();
if(cur.getCell(i,j) != null){ //null = first layer != --> if its not
cell = cur.getCell(i, j);
System.out.println(i + ", " + j + ", " + cell.getTile().getId());
bounds.add(new Rectangle(i * 64, j * 64, 64 , 64));
}
}
}
public void render(float delta) {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
mapRenderer.setView(cam);
mapRenderer.render();
cam.position.set(0, 0, 0);
cam.update();
batch.setProjectionMatrix(cam.combined);
batch.begin();
batch.draw(player.getCurrentFrame(), player.getPosition().x , player.getPosition().y);
player.update();
for(int i = 0; i < bounds.size(); i++){
if(bounds.get(i).overlaps(player.getBounds())){
int x = (int)bounds.get(i).x / 64;
int y = (int)bounds.get(i).y / 64;
TiledMapTileLayer cur = (TiledMapTileLayer)map.getLayers().get(1);
Cell cell = cur.getCell(x, y);
if(cell.getTile().getProperties().containsKey("blocked")){
System.out.println("bush");
}
player.reAdjust();
}
}
batch.end();
}
public void resize(int width, int height) {
viewport.update(width, height);
}
Nevermind, I deleted: cam.position.set(0, 0, 0); and everything seems to work just fine. Guess I already made some changes what caused it to work, just didn't see it cause this was still around.

Categories