make x y work the same on all devices android studio - java

I just began to develop a app with java, and I only got some experience in C. In my code in Activity.java (in android studio) I got things like, just to give some examples:
meteorite1.setX(meteoritePlacementX(meteorite1.getX()));
meteorite1.setY(-2000);
gnome.setX(330);
gnome.setY(800);
meteorite2.setX(meteoritePlacementX(meteorite2.getX()));
meteorite2.setY(meteoritePlacementY(meteorite1.getY()));
meteorite3.setX(meteoritePlacementX(meteorite3.getX()));
meteorite3.setY(meteoritePlacementY(meteorite2.getY()));
meteorite4.setX(meteoritePlacementX(meteorite4.getX()));
meteorite4.setY(meteoritePlacementY(meteorite3.getY()));
meteorite5.setX(meteoritePlacementX(meteorite5.getX()));
meteorite5.setY(meteoritePlacementY(meteorite4.getY()));
meteoritedestruction1.setX(0);
meteoritedestruction1.setY(-2000);
meteoritedestruction2.setX(0);
meteoritedestruction2.setY(-2000);
meteoritedestruction3.setX(0);
meteoritedestruction3.setY(-2000);
meteoritedestruction4.setX(0);
meteoritedestruction4.setY(-2000);
meteoritedestruction5.setX(0);
meteoritedestruction5.setY(-2000);
star1.setX(300);
star2.setX(150);
star3.setX(50);
star4.setX(500);
star5.setX(600);
star6.setX(350);
star7.setX(80);
star8.setX(450);
tinystar1.setX(302);
tinystar2.setX(240);
tinystar3.setX(57);
tinystar4.setX(660);
tinystar5.setX(400);
star1.setY(300);
star2.setY(-300);
star3.setY(-100);
star4.setY(100);
star5.setY(300);
star6.setY(500);
star7.setY(700);
star8.setY(900);
tinystar1.setY(300);
tinystar2.setY(-400);
tinystar3.setY(-200);
tinystar4.setY(150);
tinystar5.setY(30);
and
public float meteoritePlacementX(float X){
float MeteoriteNewX = 0f;
int random = (int )(Math.random() * 480 - 50);
MeteoriteNewX = random;
return MeteoriteNewX;
}
Which workes fine, but just on my phone (720 x 1280 pixels (~294 ppi pixel density)) which I tested my code at. Now I published my app, but on other device, the layout of the app is totally out of sync (which makes sense to me now, cause x and y are different for every screen). Buttons and pictures workes fine, but moving object like
meteorite1.setY(meteorite1.getY() + 20);
where I use x and y are broken on other devices. I use the relative layout.
So long story short; Is there a way to change x and y, so it becomes relative to the screen? Otherwise I need to change the whole code.

In general using placement based on hard coded pixel values is not a good practice. Not only would this break with backwards compatibility but also think about what you would have to do when 2k+ phones come out, you would need an entire refactor. Look at this question and the answer by Guillaume Perrot you can get get the maximum and minimum pixel values relative to the user's phone and use those instead of the 480 - 50 and your star set functions.
For the movement do
DisplayMetrics displayMetrics = new DisplayMetrics();
WindowManager wm = (WindowManager)getApplicationContext().getSystemService(Context.WINDOW_SERVICE); // the results will be higher than using the activity context object or the getWindowManager() shortcut
wm.getDefaultDisplay().getMetrics(displayMetrics);
int maxWidth = displayMetrics.widthPixels;
//Make this percentage whatever you want
float movementPercentage = 0.02
//Will move the object 2 percent up the y axis
meteorite1.setY(meteorite1.getY() + maxWidth*movementPercentage);

Related

Android Button different position on other devices (using setX)

I want to move several buttons, rearranging them in a different position than the original one at a specific moment, while playing the app. I used the setX() method for this purpose, to move the button to the place I want. I know that this method takes pixels as imput (and pixels depend on the density of the device), so I took the density of the device and multiplied it by a certain number (the position in density pixels), so the output is that position in pixels for each device. I thought that would give me the same button position for all devices, but it doesn´t work. The buttons appear displaced on different devices. This is the method I used to convert density pixels to the corresponding pixels for each device:
public void Converter_Dp_to_Px(){
pxX = (int) (dpX * Resources.getSystem().getDisplayMetrics().density); //Pixels in X direction
pxY = (int) (dpY * Resources.getSystem().getDisplayMetrics().density); //Pixels in Y direction
}
Now I set values for dpX and dpY, convert them into pixels for each device, and place the button in that position with setX() and setY() methods:
dpX = 254;
Converter_Dp_to_Px();
dpY = 477;
Converter_Dp_to_Px();
button1.setX(pxX);
button1.setY(pxY);
I also tried not with absolute position, but with one with percentages, as follows:
int maxX = Resources.getSystem().getDisplayMetrics().heightPixels;
int maxY = Resources.getSystem().getDisplayMetrics().widthPixels;
mov_percenX = 0.37f;
mov_percenY = 0.63f;
button1.setX(button1.getX() + maxX * mov_percenX);
button1.setY(button1.getY() + maxY * mov_percenY);
But it doesn´t work anyway. I hope you can help me, thanks in advance.

Android - Google Maps API Heatmap with fixed radius in meters

The Heatmap Overlay for the Android Google Maps API renders the radius in pixels, which means that by zooming in and out the radius gets bigger and smaller.
I need a heatmap with fixed radius (e.g. in meters instead of pixels) that does not rescale when zooming in and out.
Is there any possibility to do so?
In googlemap api, get scale data. In UI you have the legend which states the map scale. Something like this - (check in google maps bottom right I'm talking about 2000Ft thing below)
You know cm length of the phone screen, you know the pixels in complete width,so
cm(phoneScreen) --> cm/pixel ratio --> pixel radius to cm radius.
you can deduce using some maths I suppose.
As for my opinion you can use setRadius() method to change the radius of heatmaps location in response to changed zoom. Something like this:
int DEFAULT_ZOOM_LEVEL = 10;
int ZOOM_STEP_FOR_RADIUS = 2;
mProvider = new HeatmapTileProvider.Builder()
.data(mList)
.setRadius(DEFAULT_ZOOM_LEVEL)
.build();
mOverlay = mMap.addTileOverlay(new TileOverlayOptions().tileProvider(mProvider));
mMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener({
#Override
void onCameraIdle() {
float newZoom = mMap.getCameraPosition().zoom;
mProvider.setRadius(DEFAULT_ZOOM_LEVEL + newZoom * ZOOM_STEP_FOR_RADIUS)
mOverlay.clearTileCache();
}
});

Android - Adjusting Image View Position on Top of a Zoomable Image View

I'm working on image, making a feature which allows user to place tags on images (like Facebook photo tag). I have a zoomable ImageView (using Mike Ortiz's TouchImageView) as a background image, and some ImageViews as image tags.
Each tag has X and Y position, and there is no problem in displaying both background image and image tags in correct position. However, when user triggers pinch zoom, the position of background image is changed. Which means, the position of each image tags must also be changed/updated according to the current background image's zoom level and scroll position.
I'm currently using this method:
float currentZoom = mImageView.getCurrentZoom();
float zoomedX = x / currentZoom;
float zoomedY = y / currentZoom;
But when I tried, it went totally wrong. Can anyone help me finding correct method/equation?
EDIT:
I tried using Matrix.mapPoints(), but I don't understand how this matrix could help me solving my problem. Here is an image for better explanation:
I finally get the answer (thanks to pskink)! To get the exact tag position on image, all we have to do is to use Matrix.mapPoints(). But that alone will not give the new X & Y position after zoom. We have to multiply the current X & Y position with the current zoom level to get the "after zoom position".
In other words:
Matrix matrix = mImageView.getImageMatrix();
float[] pts = {0, 0};
matrix.mapPoints(pts);
float newTagX = (tagX * currentZoom) + pts[0];
float newTagY = (tagY * currentZoom) + pts[1];

Efficient 2D Tile based lighting system

What is the most efficient way to do lighting for a tile based engine in Java?
Would it be putting a black background behind the tiles and changing the tiles' alpha?
Or putting a black foreground and changing alpha of that? Or anything else?
This is an example of the kind of lighting I want:
There are many ways to achieve this. Take some time before making your final decision. I will briefly sum up some techiques you could choose to use and provide some code in the end.
Hard Lighting
If you want to create a hard-edge lighting effect (like your example image),
some approaches come to my mind:
Quick and dirty (as you suggested)
Use a black background
Set the tiles' alpha values according to their darkness value
A problem is, that you can neither make a tile brighter than it was before (highlights) nor change the color of the light. Both of these are aspects which usually make lighting in games look good.
A second set of tiles
Use a second set of (black/colored) tiles
Lay these over the main tiles
Set the new tiles' alpha value depending on how strong the new color should be there.
This approach has the same effect as the first one with the advantage, that you now may color the overlay tile in another color than black, which allows for both colored lights and doing highlights.
Example:
Even though it is easy, a problem is, that this is indeed a very inefficent way. (Two rendered tiles per tile, constant recoloring, many render operations etc.)
More Efficient Approaches (Hard and/or Soft Lighting)
When looking at your example, I imagine the light always comes from a specific source tile (character, torch, etc.)
For every type of light (big torch, small torch, character lighting) you
create an image that represents the specific lighting behaviour relative to the source tile (light mask). Maybe something like this for a torch (white being alpha):
For every tile which is a light source, you render this image at the position of the source as an overlay.
To add a bit of light color, you can use e.g. 10% opaque orange instead of full alpha.
Results
Adding soft light
Soft light is no big deal now, just use more detail in light mask compared to the tiles. By using only 15% alpha in the usually black region you can add a low sight effect when a tile is not lit:
You may even easily achieve more complex lighting forms (cones etc.) just by changing the mask image.
Multiple light sources
When combining multiple light sources, this approach leads to a problem:
Drawing two masks, which intersect each other, might cancel themselves out:
What we want to have is that they add their lights instead of subtracting them.
Avoiding the problem:
Invert all light masks (with alpha being dark areas, opaque being light ones)
Render all these light masks into a temporary image which has the same dimensions as the viewport
Invert and render the new image (as if it was the only light mask) over the whole scenery.
This would result in something similar to this:
Code for the mask invert method
Assuming you render all the tiles in a BufferedImage first,
I'll provide some guidance code which resembles the last shown method (only grayscale support).
Multiple light masks for e.g. a torch and a player can be combined like this:
public BufferedImage combineMasks(BufferedImage[] images)
{
// create the new image, canvas size is the max. of all image sizes
int w, h;
for (BufferedImage img : images)
{
w = img.getWidth() > w ? img.getWidth() : w;
h = img.getHeight() > h ? img.getHeight() : h;
}
BufferedImage combined = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
// paint all images, preserving the alpha channels
Graphics g = combined.getGraphics();
for (BufferedImage img : images)
g.drawImage(img, 0, 0, null);
return combined;
}
The final mask is created and applied with this method:
public void applyGrayscaleMaskToAlpha(BufferedImage image, BufferedImage mask)
{
int width = image.getWidth();
int height = image.getHeight();
int[] imagePixels = image.getRGB(0, 0, width, height, null, 0, width);
int[] maskPixels = mask.getRGB(0, 0, width, height, null, 0, width);
for (int i = 0; i < imagePixels.length; i++)
{
int color = imagePixels[i] & 0x00ffffff; // Mask preexisting alpha
// get alpha from color int
// be careful, an alpha mask works the other way round, so we have to subtract this from 255
int alpha = (maskPixels[i] >> 24) & 0xff;
imagePixels[i] = color | alpha;
}
image.setRGB(0, 0, width, height, imagePixels, 0, width);
}
As noted, this is a primitive example. Implementing color blending might be a bit more work.
Raytracing might be the simpliest approach.
you can store which tiles have been seen (used for automapping, used for 'remember your map while being blinded', maybe for the minimap etc.)
you show only what you see - maybe a monster of a wall or a hill is blocking your view, then raytracing stops at that point
distant 'glowing objects' or other light sources (torches lava) can be seen, even if your own light source doesn't reach very far.
the length of your ray gives will be used to check amount light (fading light)
maybe you have a special sensor (ESP, gold/food detection) which would be used to find objects that are not in your view? raytrace might help as well ^^
how is this done easy?
draw a line from your player to every point of the border of your map (using Bresehhams Algorithm http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
walk along that line (from your character to the end) until your view is blocked; at this point stop your search (or maybe do one last final iteration to see what did top you)
for each point on your line set the lighning (maybe 100% for distance 1, 70% for distance 2 and so on) and mark you map tile as visited
maybe you won't walk along the whole map, maybe it's enough if you set your raytrace for a 20x20 view?
NOTE: you really have to walk along the borders of viewport, its NOT required to trace every point.
i'm adding the line algorithm to simplify your work:
public static ArrayList<Point> getLine(Point start, Point target) {
ArrayList<Point> ret = new ArrayList<Point>();
int x0 = start.x;
int y0 = start.y;
int x1 = target.x;
int y1 = target.y;
int sx = 0;
int sy = 0;
int dx = Math.abs(x1-x0);
sx = x0<x1 ? 1 : -1;
int dy = -1*Math.abs(y1-y0);
sy = y0<y1 ? 1 : -1;
int err = dx+dy, e2; /* error value e_xy */
for(;;){ /* loop */
ret.add( new Point(x0,y0) );
if (x0==x1 && y0==y1) break;
e2 = 2*err;
if (e2 >= dy) { err += dy; x0 += sx; } /* e_xy+e_x > 0 */
if (e2 <= dx) { err += dx; y0 += sy; } /* e_xy+e_y < 0 */
}
return ret;
}
i did this whole lightning stuff some time ago, a* pathfindin feel free to ask further questions
Appendum:
maybe i might simply add the small algorithms for raytracing ^^
to get the North & South Border Point just use this snippet:
for (int x = 0; x <map.WIDTH; x++){
Point northBorderPoint = new Point(x,0);
Point southBorderPoint = new Point(x,map.HEIGHT);
rayTrace( getLine(player.getPos(), northBorderPoint), player.getLightRadius()) );
rayTrace( getLine(player.getPos(), southBorderPoint, player.getLightRadius()) );
}
and the raytrace works like this:
private static void rayTrace(ArrayList<Point> line, WorldMap map, int radius) {
//int radius = radius from light source
for (Point p: line){
boolean doContinue = true;
float d = distance(line.get(0), p);
//caclulate light linear 100%...0%
float amountLight = (radius - d) / radius;
if (amountLight < 0 ){
amountLight = 0;
}
map.setLight( p, amountLight );
if ( ! map.isViewBlocked(p) ){ //can be blockeb dy wall, or monster
doContinue = false;
break;
}
}
}
I've been into indie game development for about three years right now. The way I would do this is first of all by using OpenGL so you can get all the benefits of the graphical computing power of the GPU (hopefully you are already doing that). Suppose we start off with all tiles in a VBO, entirely lit. Now, there are several options of achieving what you want. Depending on how complex your lighting system is, you can choose a different approach.
If your light is going to be circular around the player, no matter the fact if obstacles would block the light in real life, you could choose for a lighting algorithm implemented in the vertex shader. In the vertex shader, you could compute the distance of the vertex to the player and apply some function that defines how bright things should be in function of the computed distance. Do not use alpha, but just multiply the color of the texture/tile by the lighting value.
If you want to use a custom lightmap (which is more likely), I would suggest to add an extra vertex attribute that specifies the brightness of the tile. Update the VBO if needed. Same approach goes here: multiply the pixel of the texture by the light value. If you are filling light recursively with the player position as starting point, then you would update the VBO every time the player moves.
If your lightmap depends on where the sunlight hits your level, you could combine two sort of lighting techniques. Create one vertex attribute for the sun brightness and another vertex attribute for the light emitted by light points (like a torch held by the player). Now you can combine those two values in the vertex shader. Suppose the your sun comes up and goes down like the day and night pattern. Let's say the sun brightness is sun, which is a value between 0 and 1. This value can be passed to the vertex shader as a uniform. The vertex attribute that represents the sun brightness is s and the one for light, emitted by light points is l. Then you could compute the total light for that tile like this:
tileBrightness = max(s * sun, l + flicker);
Where flicker (also a vertex shader uniform) is some kind of waving function that represents the little variants in the brightness of your light points.
This approach makes the scene dynamic without having to recreate continuously VBO's. I implemented this approach in a proof-of-concept project. It works great. You can check out what it looks like here: http://www.youtube.com/watch?v=jTcNitp_IIo. Note how the torchlight is flickering at 0:40 in the video. That is done by what I explained here.

How to have a "Camera" only show a portion of a loaded area

I'm having a little problem with figuring something out (Obviously).
I'm creating a 2D Top-down mmorpg, and in this game I wish the player to move around a tiled map similar to the way the game Pokemon worked, if anyone has ever played it.
If you have not, picture this: I need to load various areas, constructing them from tiles which contain an image and a location (x, y) and objects (players, items) but the player can only see a portion of it at a time, namely a 20 by 15 tile-wide area, which can be 100s of tiles tall/wide. I want the "camera" to follow the player, keeping him in the center, unless the player reaches the edge of the loaded area.
I don't need code necessarily, just a design plan. I have no idea how to go about this kind of thing.
I was thinking of possibly splitting up the entire loaded area into 10x10 tile pieces, called "Blocks" and loading them, but I'm still not sure how to load pieces off screen and only show them when the player is in range.
The picture should describe it:
Any ideas?
My solution:
The way I solved this problem was through the wonderful world of JScrollPanes and JPanels.
I added a 3x3 block of JPanels inside of a JScrollPane, added a couple scrolling and "goto" methods for centering/moving the JScrollPane around, and voila, I had my camera.
While the answer I chose was a little more generic to people wanting to do 2d camera stuff, the way I did it actually helped me visualize what I was doing a little better since I actually had a physical "Camera" (JScrollPane) to move around my "World" (3x3 Grid of JPanels)
Just thought I would post this here in case anyone was googling for an answer and this came up. :)
For a 2D game, it's quite easy to figure out which tiles fall within a view rectangle, if the tiles are rectangular. Basically, picture a "viewport" rectangle inside the larger world rectangle. By dividing the view offsets by the tile sizes you can easily determine the starting tile, and then just render the tiles in that fit inside the view.
First off, you're working in three coordinate systems: view, world, and map. The view coordinates are essentially mouse offsets from the upper left corner of the view. World coordinates are pixels distances from the upper left corner of tile 0, 0. I'm assuming your world starts in the upper left corner. And map cooridnates are x, y indices into the map array.
You'll need to convert between these in order to do "fancy" things like scrolling, figuring out which tile is under the mouse, and drawing world objects at the correct coordinates in the view. So, you'll need some functions to convert between these systems:
// I haven't touched Java in years, but JavaScript should be easy enough to convey the point
var TileWidth = 40,
TileHeight = 40;
function View() {
this.viewOrigin = [0, 0]; // scroll offset
this.viewSize = [600, 400];
this.map = null;
this.worldSize = [0, 0];
}
View.prototype.viewToWorld = function(v, w) {
w[0] = v[0] + this.viewOrigin[0];
w[1] = v[1] + this.viewOrigin[1];
};
View.prototype.worldToMap = function(w, m) {
m[0] = Math.floor(w[0] / TileWidth);
m[1] = Math.floor(w[1] / TileHeight);
}
View.prototype.mapToWorld = function(m, w) {
w[0] = m[0] * TileWidth;
w[1] = m[1] * TileHeight;
};
View.prototype.worldToView = function(w, v) {
v[0] = w[0] - this.viewOrigin[0];
v[1] = w[1] - this.viewOrigin[1];
}
Armed with these functions we can now render the visible portion of the map...
View.prototype.draw = function() {
var mapStartPos = [0, 0],
worldStartPos = [0, 0],
viewStartPos = [0, 0];
mx, my, // map coordinates of current tile
vx, vy; // view coordinates of current tile
this.worldToMap(this.viewOrigin, mapStartPos); // which tile is closest to the view origin?
this.mapToWorld(mapStartPos, worldStartPos); // round world position to tile corner...
this.worldToView(worldStartPos, viewStartPos); // ... and then convert to view coordinates. this allows per-pixel scrolling
mx = mapStartPos[0];
my = mapStartPos[y];
for (vy = viewStartPos[1]; vy < this.viewSize[1]; vy += TileHeight) {
for (vx = viewStartPos[0]; vx < this.viewSize[0]; vy += TileWidth) {
var tile = this.map.get(mx++, my);
this.drawTile(tile, vx, vy);
}
mx = mapStartPos[0];
my++;
vy += TileHeight;
}
};
That should work. I didn't have time to put together a working demo webpage, but I hope you get the idea.
By changing viewOrigin you can scroll around. To get the world, and map coordinates under the mouse, use the viewToWorld and worldToMap functions.
If you're planning on an isometric view i.e. Diablo, then things get considerably trickier.
Good luck!
The way I would do such a thing is to keep a variable called cameraPosition or something. Then, in the draw method of all objects, use cameraPosition to offset the locations of everything.
For example: A rock is at [100,50], while the camera is at [75,75]. This means the rock should be drawn at [25,-25] (the result of [100,50] - [75,75]).
You might have to tweak this a bit to make it work (for example maybe you have to compensate for window size). Note that you should also do a bit of culling - if something wants to be drawn at [2460,-830], you probably don't want to bother drawing it.
One approach is along the lines of double buffering ( Java Double Buffering ) and blitting ( http://download.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html ). There is even a design pattern associated with it ( http://www.javalobby.org/forums/thread.jspa?threadID=16867&tstart=0 ).

Categories