I recently updated my Android app (see source on Github) to use Android Studio 0.6.0, which required bumping the build-tools to v19.1.0 (from v19.0.1), the Android Gradle plugin to v0.11 (from 0.9), and the Gradle wrapper to 1.12 (from 1.11).
Before the above changes (at this commit), the app was successfully drawing squares on a canvas to show signal strength of GLONASS satellites (see IDs 87 and 88):
After updating my Android tools and launching via Android Studio or gradlew installDebug, the squares suddenly disappeared (see IDs 77, 87, 88):
The code that draws the square, with a center of (x,y), is:
canvas.drawRect(x - SAT_OFFSET, y + SAT_OFFSET, x + SAT_OFFSET, y - SAT_OFFSET, mSatelliteStrokePaint);
(see Github for full class)
The commit with all changes to update to Android Studio 0.6.0 can be seen here.
Why did canvas.drawRect() suddenly stop working after updating the Android tools?
Apparently this has something to do with Android handling invalid input values for drawRect(), triggered by Android Gradle Plugin 0.11 enabling the new manifest merging tool by default.
If I add useOldManifestMerger true to my build.gradle, then the squares return:
android {
compileSdkVersion 19
buildToolsVersion "19.1.0"
useOldManifestMerger true
...
}
The underlying problem was that Canvas.drawRect() takes in the parameters (float left, float top, float right, float bottom, Paint paint)
I had the top and bottom coordinates of the rectangle reversed, so the top value (which should have been the smallest y value) was the larger y value, and the bottom value (which should have been the largest y value) was the smallest y value (the origin (0,0) is in the upper left corner).
Before Android Studio 0.6.0 tool updates, Android was correcting invalid input and still drawing the square. After Android Studio 0.6.0 tool update started using the new manifest merging tool, Android is no longer correcting invalid input and instead it doesn't draw anything.
After changing my code to reverse the top/bottom y values of the square:
c.drawRect(x - SAT_OFFSET, y - SAT_OFFSET, x + SAT_OFFSET, y + SAT_OFFSET, mSatelliteStrokePaint);
...the squares reappeared (even when using the new manifest merger - i.e., without adding useOldManifestMerger true to build.gradle).
So, the correct way to fix this issue is to reverse the top/bottom coordinates of the square to be valid input, and still use the new manifest merger.
This is ridiculous - we have been 'drawing rectangles backwards' for over 30 years. There are valid programmatic reasons to draw backwards sometimes.
Use the following if you need this functionality.
public static void drawRectOriented(Canvas g, float x1,float y1,float x2,float y2,Paint p) {
if (x1 > x2) {
float t = x2;
x2 = x1;
x1 = t;
}
if (y1 > y2) {
float t = y2;
y2 = y1;
y1 = t;
}
g.drawRect(x1,y1,x2,y2, p);
}
Related
I'm an experienced native iOS developer making my first foray into Android through Unity. I'm trying to set up a custom shader, but I'm having some trouble with the Normal maps. I've got them working perfectly in the Unity simulator on my computer, but when I build to an actual device (Samsung Galaxy S8+), the Normal maps don't work at all.
I'm using Mars as my test case. Here's the model running in the simulator on my computer:
And here's a screenshot from my device, running exactly the same code.
I've done a LOT of research, and apparently using Normal maps on Android with Unity is not an easy thing. There are a lot of people asking about it, but almost every answer I've found has said the trick is to override the texture import settings, and force it to be "Truecolor" which seems to be "RGBA 32 Bit" according to Unity's documentation. This hasn't helped me, though.
Another thread suggested reducing the Asino Level to zero, and another suggested turning off Mip Maps. I don't know what either of those are, but neither helped.
Here's my shader code, simplified but containing all references to Normal mapping:
void surf (Input IN, inout SurfaceOutputStandard o) {
half4 d = tex2D (_MainTex , IN.uv_MainTex);
half4 n = tex2D (_BumpMap , IN.uv_BumpMap);
o.Albedo = d.rgb;
o.Normal = UnpackNormal(n);
o.Metallic = 0.0;
o.Smoothness = 0.0;
}
I've seen some threads suggesting replacements for the "UnpackNormal()" function in the shader code, indicating that it might not be the thing to do on Android or mobile in general, but none of the suggested replacements have changed anything for better or worse: the normal maps continue to work in the simulator, but not on the device.
I've even tried making my own normal maps programmatically from a grayscale heightmap, to try to circumvent any import settings I may have done wrong. Here's the code I used, and again it works in the simulator but not on the device.
public Texture2D NormalMap(Texture2D source, float strength = 10.0f) {
Texture2D normalTexture;
float xLeft;
float xRight;
float yUp;
float yDown;
float yDelta;
float xDelta;
normalTexture = new Texture2D (source.width, source.height, TextureFormat.RGBA32, false, true);
for (int y=0; y<source.height; y++) {
for (int x=0; x<source.width; x++) {
xLeft = source.GetPixel (x - 1, y).grayscale * strength;
xRight = source.GetPixel (x + 1, y).grayscale * strength;
yUp = source.GetPixel (x, y - 1).grayscale * strength;
yDown = source.GetPixel (x, y + 1).grayscale * strength;
xDelta = ((xLeft - xRight) + 1) * 0.5f;
yDelta = ((yUp - yDown) + 1) * 0.5f;
normalTexture.SetPixel(x,y,new Color(xDelta,yDelta,1.0f,yDelta));
}
}
normalTexture.Apply();
return normalTexture;
}
Lastly, in the Build Settings, I've got the Platform set to Android and I've tried it using Texture Compression set to both "Don't Override" and "ETC (default)". The former was the original setting and the latter seemed to be Unity's suggestion both by the name and in the documentation.
I'm sure there's just some flag I haven't checked or some switch I haven't flipped, but I can't for the life of me figure out what I'm doing wrong here, or why there would be such a stubborn difference between the simulator and the device.
Can anyone help a Unity newbie out, and show me how these damn Normal maps are supposed to work on Android?
Check under:
Edit -> Project Settings -> Quality
Android is usually set to Fastest.
Recently, I encountered float number corrupted problem in selected Android device. I was wondering, anyone of you had encountered the similar problem as mine, yet have a way to reproduce it with a simplified code block?
I encountered similar problem in Nexus 5 devices. The problem doesn't happen in Genymotion Emulator.
It only happen in selected for loop code block, and it is extremely difficult to re-produce in other code block.
My situation is as follow :-
float rectangleWidth2 = 0.0f;
float startX = (float) (left + xPixelsPerUnit * (xValue - minX));
float stopX = startX;
float _left = startX - rectangleWidth2;
float _right = stopX + rectangleWidth2;
// I expect "_left" and "_right" will have same value. However, at this point,
// "_right" will become an arbitary large value, something like 5.3482353354E20
// However, I expect the value range for "_left" and "_right" within [-1000,1000]
If I change the code to
float _left = startX - rectangleWidth2;
float _right = startX + rectangleWidth2;
// "_left" and "_right" will then having same value.
A "realiable" workaround for my case, is to avoid using float as suggested by reporter. I use double whenever possible, and only perform necessary float casting, when there is a need.
Same problem occur, regardless I'm using Eclipse or Android Studio. I'm going to get Nexus 4 this week, to see whether same problem occur still...
Referenced Links
https://code.google.com/p/android/issues/detail?id=58698
http://www.gamedev.net/topic/660746-problem-with-random-float-value-on-android/
suppose imgv is an ImageView
if I say float x = imgv.getPivotX(); float y = imgv.getPivotY();
this will give me the pivot point, however, this requires your app to target api 11 and above, I want to do the same for api 8, what function should I use?
Pivot is related to animations. To make them compatible with Android 1.0+ you should use NineOldAndroids
Then you can use the helper class: float x = ViewHelper.getPivotX(imgv);
I am porting some code to Android from Visual C++. The VC++ ArcTo function takes the bounding rectangle and the start and end points as parameters to define the arc. The android.graphics.Path function arcTo takes the bounding rectangle and the "start angle" and "sweep angle" as parameters.
I am not clear how to convert from the VC set of coordinates to the Android set, or what these two angles are. The arc also has direction (CW or ACW) - I am not clear how to incorporate these in a single Path, or how to switch between one and the other.
One oddity I came across is that in the Android function, angles are expressed in degrees, rather than radians which is what most calculations would use and what one would expect.
I hope my question makes some sort of sense and that someone can help!
Edit: following on from the help I got from Dr Dredel, and with much drawing of diagrams, here's how I eventually translated the VC++ call to Android:
else if (coord.isArc())
{
ptCentre = getPoint(new Coord(coord.getArcLat(), coord.getArcLong()));
nRadius = getPixels(coord.getArcRadius());
rect = new RectF(ptCentre.x - nRadius, ptCentre.y - nRadius,
ptCentre.x + nRadius, ptCentre.y + nRadius);
if (coord.isClockwise())
{
alpha = Math.atan2(ptCentre.y - ptStart.y, ptCentre.x - ptStart.x) *
Constants.k_d180Pi;
beta = Math.atan2(ptCentre.y - ptEnd.y, ptEnd.x - ptCentre.x) *
Constants.k_d180Pi;
path.arcTo(rect, (float)(alpha + 180), (float)(180 - beta - alpha));
}
else
{
}
As you can see, I haven't done the anti-clockwise arc yet, but it should be similar. My calculation wasn't perfect, as I originally had (360 - beta - alpha) instead of (180 - beta - alpha), and the original version gave some very funny results!
(Wow! this formatting mechanism is the other side of weird!)
I'm currently developing my first android app, and my first game. I've been developing on a netbook with a CliqXT (HVGA). Things are going well, it renders perfectly on the smaller screen. I knew I'd have some issues when rendering on larger screens, but the issues I'm having are not what I was expecting and I'm kind of stuck.
So basically the game consists of a main SurfaceView which I'm rendering the tiled game world on to. I followed this tutorial to get started, and my structure is still pretty similar except that it calculates the boundries based on the player location:
http://www.droidnova.com/create-a-scrollable-map-with-cells-part-i,654.html
The game also has various buildings the player can enter. Upon entering it launches another activity for that particular building. The building activities are just normal Views with Android UI stuff defined in XML (Buttons, TextViews, etc).
What I expected to happen:
So I expected the the building UIs to render correctly on the larger screen. I specified all dimensions in "dp" and fonts in "sp" in hopes that they'd scale correctly. I expected the actual game tilemap to render generally correctly, but maybe be really tiny due to the higher resolution / dpi. I'm using a very similar function to the tutorial linked above (calculateLoopBorders(), my version is pasted below) to calculate how many tiles to render based on screen height and width (getHeight() and getWidth()).
What is actually happening:
The whole game is just being rendered as if it's HVGA. The tilemap, and the building UIs are just scaled down to the smaller screen size, leaving black borders around the left, right, and bottom (see images).
If anyone can point me in the right direction it'd be greatly appreciated, thanks a lot!
(Some of you may recognize this public domain DOS classic)
Edit: Thanks Christian for fixing code formatting.
mCellHeight and mCellWidth are the width/height of the cells in pixels
mMapHeight and mMapWidth are the width/height of the total game world in number of tiles
public void calculateLoopBorders() {
mWidth = getWidth();
mHeight = getHeight();
mStartRow = (int) Math.max(0, mPlayer.mRow - ((int) (mHeight / 2) / mCellHeight));
mStartCol = (int) Math.max(0, mPlayer.mCol - ((int) (mWidth / 2) / mCellWidth));
mMaxRow = (int) Math.min(mMapHeight, mStartRow + (mHeight / mCellHeight)) + 1;
mMaxCol = (int) Math.min(mMapWidth, mStartCol + (mWidth / mCellWidth));
if (mMaxCol >= mMapWidth) {
mStartCol = mMaxCol - (mWidth / mCellWidth);
}
if (mMaxRow >= mMapHeight) {
mStartRow = mMaxRow - (mHeight / mCellHeight);
}
int x1 = mStartCol * mCellWidth;
int y1 = mStartRow * mCellHeight;
int x2 = x1 + mWidth;
int y2 = y1 + mHeight;
mBgSrcRect = new Rect(x1, y1, x2, y2);
mBgDestRect = new Rect(0,0, mWidth, mHeight);
}
I figured it out. I was targeting 1.5 in the Project so it was assuming HVGA. Targeting 2.1 fixes the issue and the bitmaps even seem to scale correctly using some kind of android magic.
I still have a question though, when I finish this game I want it to work with 1.5+ devices. Do I need to put separate builds into the market, one for each device class? This seems like a lot of trouble for something that could be handled in a line or 2 of code in the app itself... but I've never released an app so maybe it's easily handled in the process.