Support multiple aspect ratio in Unity - android

I've been trying to create a Unity 2D game that supports each and every aspect ratios of devices for both android and tablets. Is there a way to do so that's been provided or recommended by Unity?

There are a few things that should be considered. The first is what elements should be allowed to scale? There are two categories, namely UI and Game Elements.
The Game Elements portion can mean a lot of things. If the game space is limited, the key is typically, including a generous portion of "negative space", or parts of the image that don't affect the game play significantly. For instance, the below image could be cropped from the left and right without affecting the image significantly. Put the center part of the image as the key element, or one side.
One could also stretch the elements, although that might lead to undesirable effects. Having a surplus of image and testing with different aspect rations is the best way typically for such background elements. These background elements can be placed in the background, with the canvas being set to "Scale With Screen Size", and setting the "Screen Match Mode" to the effect that works best for your image. See "Canvas Scaler" for more information.
As for the other UI elements, the key is to use anchor points. You can tell a UI element to take either a number of pixels, or fill a portion of the screen, when you place it. Look at the "Rect Transform" component included with each such UI object. You can adjust these on the screen as well.
Lastly, you could do it programmatically. There exists Screen.height and Screen.width. You could adjust the objects as desired in run time to make it work. I suggest you don't do this for everything, but it might help in some cases.

In my case, I do work by create all of it as a scale
So, it could support no matter screen are
//Find Screen resolution at the splash or loading screen
float scalex = DataFactory.SCREEN_WIDTH / (float)DataFactory.OUR_FIXED_GAME_SCREEN;
float scaley = DataFactory.SCREEN_HEIGHT / (float)DataFactory.OUR_FIXED_GAME_SCREEN;
if (scalex >= scaley)
DataFactory.SCALE = scalex;
else
DataFactory.SCALE = scaley;
//Set all size in game at the start
private int gameWidth = (int) (1400 * DataFactory.SCALE);
private int gameHeight = (int) (800 * DataFactory.SCALE);
private int startGameX = (int) (300 * DataFactory.SCALE);
private int startGameY = (int) (280 * DataFactory.SCALE);
private int objectX = (int) (410 * DataFactory.SCALE) + DataFactory.BEGIN_X;
private int objectY = (int) (979 * DataFactory.SCALE) + DataFactory.BEGIN_Y;
private int objectGapX = (int) (400 * DataFactory.SCALE);
private int objectGapY = (int) (180 * DataFactory.SCALE);
private int objectWidth = (int) (560 * DataFactory.SCALE);
private int objectHeight = (int) (400 * DataFactory.SCALE);
private int xRing = (int) (1005 * DataFactory.SCALE) + DataFactory.BEGIN_X;
private int yRing = (int) (1020 * DataFactory.SCALE) + DataFactory.BEGIN_Y;
private int radiusOutside = (int) (740 * DataFactory.SCALE);
private int radiusInside = (int) (480 * DataFactory.SCALE);
private int radiusObject = (int) (600 * DataFactory.SCALE);
private int yObjectRing = (int) (920 * DataFactory.SCALE) + DataFactory.BEGIN_Y;
* ALL FIXED VALUED IS THE VALUED THAT I CREATE BY BASE ON SINGLE SCREEN *
This is some sample of 3D Game that I made, however, I still use the same concept at GUI part
This is some sample of 2D Game that I used this concept

I know it’s an old post, wanted to show an alternative for this. You could try to define towards which axis you would like to scale your game (ex. all width should be always visible, height should scale respectively to the width): store all scene objects in a parent and scale the parent.
Ex. (my width was fixed and the height got cut off for the width )
bottomRightPosition = Camera.main.ScreenToWorldPoint(new Vector3(0, 0, - Camera.main.transform.position.z));
topLeftPosition = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, -Camera.main.transform.position.z));
float Width = topLeftPosition.x -bottomRightPosition.x
float scale = width / optimizedWorldDistance
gameHolder.transform.localScale = new Vector3(scale,scale,1); // for 2D
Note: my gameHolder is initially of scale (1,1,1);

You should put everything in a main game object and scale it with difference ratio using a simple script (camera 'something' could help you to detect the screen ratio).
That's my idea.
Sorry for my bad English.

Try the new unity3d ui with anchoring.

For all the UI elements you must use the unit UI system, which is the best way to support multi platforms and aspect ratios.
The following content is based on this article:
This article say basically the same things that I'm saying:
1)
Regarding design ONLY in high resolution the article said:
"Another approach is to use higher resolution graphics (in fact the
one with the highest resolution of the device you want to target) and
scale it down on all devices. However, this is not a good idea because
you effectively need much more memory and will lose performance on
low-end devices."
So design in high resolution and then scale down it's not the good approach.
So as the article said the best thing it's to have different images for different resolution (SD, HD UD) and load the right image when the game it's loading: the article said:
"The best approach is to use a different image with the higher resolution and use this image version on the iPhone 4 and the low-res version on an iPhone 3GS, which is effectively what Apple is doing by using images with a #2x suffix for the file name.
In a similar way, you can create all your graphics in ultra-high-resolution needed for the iPad 3 for instance and append another suffix, and load the right image based on the screen resolution the device has. This is called content scaling, as the game was written only for a single "logical" scene size, and all the images & fonts are scaled to the device resolution."
So by using this approach we solved the problem of target devices with differnt RESOLUTIONS.
No like the articole said there is another problem which is target the devices with different ASPECT RATIO:
From the artichle:
"However, this approach is not sufficient when you want to target devices with different aspect ratios"
To do that I usually choose an aspect ratio that can fit the design of my game and use the following script to maintain the same aspect ratio on different devices:
/* The MIT License (MIT)
Copyright (c) 2014, Marcel Căşvan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
using System;
using System.Collections;
using UnityEngine;
[ExecuteInEditMode]
[RequireComponent (typeof (Camera))]
public class CameraFit : MonoBehaviour
{
#region FIELDS
public float UnitsForWidth = 1; // width of your scene in unity units
public static CameraFit Instance;
private float _width;
private float _height;
//*** bottom screen
private Vector3 _bl;
private Vector3 _bc;
private Vector3 _br;
//*** middle screen
private Vector3 _ml;
private Vector3 _mc;
private Vector3 _mr;
//*** top screen
private Vector3 _tl;
private Vector3 _tc;
private Vector3 _tr;
#endregion
#region PROPERTIES
public float Width {
get {
return _width;
}
}
public float Height {
get {
return _height;
}
}
// helper points:
public Vector3 BottomLeft {
get {
return _bl;
}
}
public Vector3 BottomCenter {
get {
return _bc;
}
}
public Vector3 BottomRight {
get {
return _br;
}
}
public Vector3 MiddleLeft {
get {
return _ml;
}
}
public Vector3 MiddleCenter {
get {
return _mc;
}
}
public Vector3 MiddleRight {
get {
return _mr;
}
}
public Vector3 TopLeft {
get {
return _tl;
}
}
public Vector3 TopCenter {
get {
return _tc;
}
}
public Vector3 TopRight {
get {
return _tr;
}
}
#endregion
#region METHODS
private void Awake()
{
try{
if((bool)GetComponent<Camera>()){
if (GetComponent<Camera>().orthographic) {
ComputeResolution();
}
}
}catch (Exception e){
Debug.LogException(e, this);
}
}
private void ComputeResolution()
{
float deviceWidth;
float deviceHeight;
float leftX, rightX, topY, bottomY;
#if UNITY_EDITOR
deviceWidth = GetGameView().x;
deviceHeight = GetGameView().y;
#else
deviceWidth = Screen.width;
deviceHeight = Screen.height;
#endif
//Debug.Log("Aspect Ratio " + GetComponent<Camera>().aspect);
if (GetComponent<Camera>().aspect >= 0.7f)
{
UnitsForWidth = 2.2f;
}
else
{
UnitsForWidth = 2f;
}
/* Set the ortograpish size (shich is half of the vertical size) when we change the ortosize of the camera the item will be scaled
* autoamtically to fit the size frame of the camera
*/
GetComponent<Camera>().orthographicSize = 1f / GetComponent<Camera>().aspect * UnitsForWidth / 2f;
//Get the new height and Widht based on the new orthographicSize
_height = 2f * GetComponent<Camera>().orthographicSize;
_width = _height * GetComponent<Camera>().aspect;
float cameraX, cameraY;
cameraX = GetComponent<Camera>().transform.position.x;
cameraY = GetComponent<Camera>().transform.position.y;
leftX = cameraX - _width / 2;
rightX = cameraX + _width / 2;
topY = cameraY + _height / 2;
bottomY = cameraY - _height / 2;
//*** bottom
_bl = new Vector3(leftX, bottomY, 0);
_bc = new Vector3(cameraX, bottomY, 0);
_br = new Vector3(rightX, bottomY, 0);
//*** middle
_ml = new Vector3(leftX, cameraY, 0);
_mc = new Vector3(cameraX, cameraY, 0);
_mr = new Vector3(rightX, cameraY, 0);
//*** top
_tl = new Vector3(leftX, topY, 0);
_tc = new Vector3(cameraX, topY , 0);
_tr = new Vector3(rightX, topY, 0);
Instance = this;
}
private void Update()
{
#if UNITY_EDITOR
ComputeResolution();
#endif
}
private void OnDrawGizmos()
{
if (GetComponent<Camera>().orthographic) {
DrawGizmos();
}
}
private void DrawGizmos()
{
//*** bottom
Gizmos.DrawIcon(_bl, "point.png", false);
Gizmos.DrawIcon(_bc, "point.png", false);
Gizmos.DrawIcon(_br, "point.png", false);
//*** middle
Gizmos.DrawIcon(_ml, "point.png", false);
Gizmos.DrawIcon(_mc, "point.png", false);
Gizmos.DrawIcon(_mr, "point.png", false);
//*** top
Gizmos.DrawIcon(_tl, "point.png", false);
Gizmos.DrawIcon(_tc, "point.png", false);
Gizmos.DrawIcon(_tr, "point.png", false);
Gizmos.color = Color.green;
Gizmos.DrawLine(_bl, _br);
Gizmos.DrawLine(_br, _tr);
Gizmos.DrawLine(_tr, _tl);
Gizmos.DrawLine(_tl, _bl);
}
private Vector2 GetGameView()
{
System.Type T = System.Type.GetType("UnityEditor.GameView,UnityEditor");
System.Reflection.MethodInfo getSizeOfMainGameView =
T.GetMethod("GetSizeOfMainGameView",System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
System.Object resolution = getSizeOfMainGameView.Invoke(null, null);
return (Vector2)resolution;
}
#endregion
}
[1]: http://v-play.net/doc/vplay-different-screen-sizes/
This should sole the different aspect ratios problem. Now if you want anchor some game object the be always in a fixed position event if the game is resized on devices with different aspect ratios, you can use the following script:
/***
* This script will anchor a GameObject to a relative screen position.
* This script is intended to be used with CameraFit.cs by Marcel Căşvan, available here: http://gamedev.stackexchange.com/a/89973/50623
*
* Note: For performance reasons it's currently assumed that the game resolution will not change after the game starts.
* You could not make this assumption by periodically calling UpdateAnchor() in the Update() function or a coroutine, but is left as an exercise to the reader.
*/
/* The MIT License (MIT)
Copyright (c) 2015, Eliot Lash
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class CameraAnchor : MonoBehaviour {
public enum AnchorType {
BottomLeft,
BottomCenter,
BottomRight,
MiddleLeft,
MiddleCenter,
MiddleRight,
TopLeft,
TopCenter,
TopRight,
};
public AnchorType anchorType;
public Vector3 anchorOffset;
// Use this for initialization
void Start () {
UpdateAnchor();
}
void UpdateAnchor() {
switch(anchorType) {
case AnchorType.BottomLeft:
SetAnchor(CameraFit.Instance.BottomLeft);
break;
case AnchorType.BottomCenter:
SetAnchor(CameraFit.Instance.BottomCenter);
break;
case AnchorType.BottomRight:
SetAnchor(CameraFit.Instance.BottomRight);
break;
case AnchorType.MiddleLeft:
SetAnchor(CameraFit.Instance.MiddleLeft);
break;
case AnchorType.MiddleCenter:
SetAnchor(CameraFit.Instance.MiddleCenter);
break;
case AnchorType.MiddleRight:
SetAnchor(CameraFit.Instance.MiddleRight);
break;
case AnchorType.TopLeft:
SetAnchor(CameraFit.Instance.TopLeft);
break;
case AnchorType.TopCenter:
SetAnchor(CameraFit.Instance.TopCenter);
break;
case AnchorType.TopRight:
SetAnchor(CameraFit.Instance.TopRight);
break;
}
}
void SetAnchor(Vector3 anchor) {
Vector3 newPos = anchor + anchorOffset;
if (!transform.position.Equals(newPos)) {
transform.position = newPos;
}
}
// Update is called once per frame
#if UNITY_EDITOR
void Update () {
UpdateAnchor();
}
#endif
}
Hope this can help, for more info please read the article that I've linked above.

Related

How to adjust the screen for portrait and landscape mode such that all UI elements can be seen?

I am building an app.I have kept my reference resolution game view setting as (800 x 1200) .Settings for canvas shown below
When I check the it Iphone6(750 x 1334) resolution I can see the contents but certain components are cropped with respect to width.The Game view is shown below. I use panels,vertical layout group,horizontal layout group many UI components in my application.
Now I changed my Game view resolution to ipad pro(2224 x 1668).Now I cannot see my images.The Game view is shown below
I dont know how to adjust the UI elements according to different screen sizes.Some of the answers I observed was I should keep settings as Scale with screen size,match width or height and Match slider to 0.5. I have kept these settings(see first image).Still it is not changing according to different screen sizes.
I tried Aspect ratio fitter inside the parent element(Showdata) but of no use.What should I do to change screen according to different screen sizes?Is it because I am not anchoring UI elements in a proper way?
How will you anchor something like this(Canvas>Panel(Stretched along XY)>Panel(with Vertical layout)>Many UI elements)
I have seen many tutorials on how to use anchor for images and buttons but how to use for nested elements or when we use vertical or horizontal layout and it contains many UI elements.
The Rect Transform for each UI element is given below.
Here is an abbreviated list of tips, from most to least urgent:
Set your canvas scaling to Constant Physical Size. On iPhone X devices, the scaling factor is 3x. Find other physical scale factors here. The purpose of doing this is to make your Canvas's layout coordinates correspond to device points while rendering at the native resolution of the device. This is the most urgent source of your agony.
For RectTransform components corresponding to a full screen, set its anchors to (0, 0), (1, 1), i.e., fill the parent. Play with the Top, Right, etc. properties. But it seems like you already know that.
Child elements of vertical / horizontal layout groups should have a LayoutElement and should generally be of fixed size in the non-scrolling axis, i.e., do not use ContentSizeFitter on rows for now, get it to work for a fixed height. This is almost always the correct behavior on mobile devices anyhow.
Experiment with LayoutElement's Ignore Layout flag.
Correctly set the Pixels Per Unit setting on your textures.
On mobile devices, you will miss issues like cutouts and bevels in corners. Use NotchSolution for Unity 2019.2 and earlier, and the new Device Simulator for 2019.3 and above, to visualize and adapt to these issues.
Use a recycling scroll view component, like Optimized ScrollView Adapter. This will also include thoughtful tutorials about how to lay things out. If you need content fit height elements in a recycling scroll view, you will need this asset.
To generally trigger changes based on screen layout, use OnRectTransformDimensionsChanged(). Nothing else, don't check for changes in Screen! You should familiarize yourself with the EventSystem generally.
Familiarize yourself with the aspect ratios of various devices, and use Camera.main.aspect to correctly toggle between hand-tuned layouts for these aspect ratios. There are 4 in iOS.
Here is an example of a platform canvas scaler:
using UnityEngine;
using UnityEngine.EventSystems;
#if UNITY_IPHONE
using UnityEngine.iOS;
#endif
using UnityEngine.UI;
namespace StackOverflow
{
[ExecuteInEditMode]
public sealed class PlatformCanvasScaler : UIBehaviour
{
public static PlatformCanvasScaler instance { get; private set; }
public float scaleFactor
{
get
{
if (m_CanvasScaler != null)
{
return m_CanvasScaler.scaleFactor;
}
return 1.0f;
}
}
[SerializeField] private CanvasScaler m_CanvasScaler;
[SerializeField] private float m_PhoneScaleFactor = 1.15f;
private PlatformCanvasScaler()
{
instance = this;
}
protected override void Awake()
{
base.Awake();
if (!enabled)
{
return;
}
Refresh();
}
private void Refresh()
{
var scaleFactor = 1f;
var isMobile = PlatformCanvasScaler.isMobile;
if (isMobile)
{
#if UNITY_IPHONE
switch (Device.generation)
{
case DeviceGeneration.iPhoneX:
case DeviceGeneration.iPhoneXSMax:
case DeviceGeneration.Unknown:
case DeviceGeneration.iPadUnknown:
case DeviceGeneration.iPodTouchUnknown:
scaleFactor = 3f;
break;
case DeviceGeneration.iPhone5:
case DeviceGeneration.iPhoneSE1Gen:
case DeviceGeneration.iPhone5C:
case DeviceGeneration.iPhone5S:
case DeviceGeneration.iPhone6:
case DeviceGeneration.iPhone6S:
case DeviceGeneration.iPhone7:
case DeviceGeneration.iPhone8:
case DeviceGeneration.iPhoneXR:
case DeviceGeneration.iPhoneUnknown:
scaleFactor = 2f;
break;
case DeviceGeneration.iPhone6Plus:
case DeviceGeneration.iPhone7Plus:
case DeviceGeneration.iPhone8Plus:
case DeviceGeneration.iPhone6SPlus:
scaleFactor = 3f / 1.15f;
break;
case DeviceGeneration.iPhone:
case DeviceGeneration.iPhone3G:
case DeviceGeneration.iPhone3GS:
case DeviceGeneration.iPad1Gen:
case DeviceGeneration.iPad2Gen:
case DeviceGeneration.iPad3Gen:
scaleFactor = 1f;
break;
default:
scaleFactor = 2f;
break;
}
scaleFactor *= m_PhoneScaleFactor;
#else
scaleFactor = 2f * Screen.dpi / 326f;
#endif
}
else
{
if (Screen.dpi > 140)
{
scaleFactor = 2.0f;
}
else
{
scaleFactor = 1.0f;
}
}
m_CanvasScaler.scaleFactor = scaleFactor;
}
public static bool isMobile
{
get
{
var isPhone = false;
#if UNITY_IPHONE
isPhone = true;
#endif
var isMobile = Application.platform == RuntimePlatform.IPhonePlayer ||
(Application.isEditor && isPhone);
return isMobile;
}
}
#if UNITY_EDITOR
private void OnValidate()
{
Refresh();
}
#endif
// Update is called once per frame
private void Update()
{
}
}
}
Observe I make some decisions about what is considered a HiDPI device.
Unfortunately, my favorite UI asset for screen management, MaterialUI, is deprecated. Hopefully someone can comment on their experience with others.

Use x/y coordinates to set bounds of a moving particle

I have two bitmaps that I draw onto the center of a canvas:
One is only a background, it's a spirit level in top view which doesnt move. The second one is a bitmap looking like a "air bubble". When the user tilts the phone, the sensors read the tilt and the air bubble moves according to the sensor values along the x-axis. However, I need to make sure that the air bubble doesnt move too far, e.g out of the background-bitmap.
So I tried to which x coordinate the bubble can travel to,
before I have to set xPos = xPos -1 using trial and error
This works fine on my device.
To clarify: On my phone, the air bubble could move to the coordinate x = 50 from the middle of the screen. This would be the point, where the bitmap is at the very left of the background spirit level.
On a larger phone, the position x = 50 is too far left, and therefore looking like the air bubble travelled out of the water level.
Now I've tried following:
I calculated the area in % in which the air bubble can move. Let's say that
is 70% of the entire width of the bitmap. So I tried to calculate the two x boundary values:
leftBoundary = XmiddlePoint - (backgroundBitmap.getWidth() * 0.35);
rightBoundary = XmiddlePoint + (backgroundBitmap.getWidth() * 0.35);
...which doesnt work when testing with different screen sizes :(
Is it possible to compensate for different screen sizes and densities using absolute coordinates or do I have to rethink my idea?
If you need any information that I forgot about, please let me know. If this question has already been answered, I would appreciate a link :) Thanks in advance!
Edit:
I load my bitmaps like this:
private Bitmap backgroundBitmap;
private static final int BITMAP_WIDTH = 1898;
private static final int BITMAP_HEIGHT = 438;
public class SimulationView extends View implements SensorEventListener{
public SimulationView(Context context){
Bitmap map = BitmapFactory.decodeResource(getResources, R.mipmap.backgroundImage);
backgroundBitmap = Bitmap.createScaledBitmap(map, BITMAP_WIDTH, BITMAP_HEIGHT, true;
}
and draw it like this:
protected void onDraw(Canvas canvas){
canvas.drawBitmap(backgroundBitmap, XmiddlePoint - BITMAP_WIDTH / 2, YmiddlePont - BITMAP_HEIGHT / 2, null);
}
backgroundBitmap.getWidth() and getHeight() prints out the correct sizes.
Calculating like mentioned above would return following boundaries:
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int width = displayMetrics.widthPixels;
//which prints out width = 2392
xMiddlePoint = width / 2;
// = 1196
leftBoundary = xMiddlePoint - (BITMAP.getWidth()* 0.35);
// = 531,7
However, when I use trial and error, the right x coordinate seems to be at around 700.
I've come across a great explanation on how to fix my issue here.
As user AgentKnopf explained, you have to scale coordinates or bitmaps like this:
X = (targetScreenWidth / defaultScreenWidth) * defaultXCoordinate
Y = (targetScreenHeight / defaultScreenHeight) * defaultYCoordinate
which, in my case, translates to:
int defaultScreenWidth = 1920;
int defaultXCoordinate = 333;
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
displayWidth = displayMetrics.widthPixels;
leftBoundary = (displayWidth / defaultScreenWidth) * defaultXCoordinates

360 panorama viewer with rajawali + VR howto change FieldOfView

I am trying to create a 360 image viewer using rajawali + vr (Cardboard toolkit).
When i disable the VR-Mode on the CardboardView the changes i made on Field of View property in the renderer has no effect.
in the Google Cardboard docs i found the the view will ignore it
For monocular rendering, the implementor should feel free to ignore the FieldOfView and instead create a perspective matrix with whatever field of view is desired for monocular rendering
My Question is how can i do this?
and where should i implement it, neither the renderer nor the CardboardView has a method to set an perspectiveMatrix (float[])?
updating the device param seems to always get overwritten by the gvr view
but if you decompile the FieldOfView class, you get this:
public void toPerspectiveMatrix(float near, float far, float[] perspective, int offset) {
if(offset + 16 > perspective.length) {
throw new IllegalArgumentException("Not enough space to write the result");
} else {
float l = (float)(-Math.tan(Math.toRadians((double)this.left))) * near;
float r = (float)Math.tan(Math.toRadians((double)this.right)) * near;
float b = (float)(-Math.tan(Math.toRadians((double)this.bottom))) * near;
float t = (float)Math.tan(Math.toRadians((double)this.top)) * near;
Matrix.frustumM(perspective, offset, l, r, b, t, near, far);
}
}

Vertical orientation degree - Android

Anyone knows how to get smooth vertical orientation degree in Android?
I already tried OrientationEventListener as shown below but it's very noisy. already tried all rates, Normal, Delay, Game and Fastest, all shown the same result.
myOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {
#Override
public void onOrientationChanged(int arg0) {
orientaion = arg0;
Log.i("orientaion", "orientaion:" + orientaion);
}
};
So there are two things going on that can affect what you need.
Sensor delay. Android provides four different sensor delay modes: SENSOR_DELAY_UI, SENSOR_DELAY_NORMAL, SENSOR_DELAY_GAME, and SENSOR_DELAY_FASTEST, where SENSOR_DELAY_UI has the longest interval between two data points and SENSOR_DELAY_FASTEST has the shortest. The shorter the interval the higher data sampling rate (number of samples per second). Higher sampling rate gives you more "responsive" data, but comes with greater noise, while lower sampling rate gives you more "laggy" data, but more smooth.
Noise filtering. With the above in mind, you need to decide which route you want to take. Does your application need fast response? If it does, you probably want to choose a higher sampling rate. Does your application need smooth data? I guess this is obviously YES given the context of the question, which means you need noise filtering. For sensor data, noise is mostly high frequency in nature (noise value oscillates very fast with time). So a low pass filter (LPF) is generally adequate.
A simple way to implement LPF is exponential smoothing. To integrate with your code:
int orientation = <init value>;
float update_rate = <value between 0 to 1>;
myOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {
#Override
public void onOrientationChanged(int arg0) {
orientation = (int)(orientation * (1f - update_rate) + arg0 * update_rate);
Log.i("orientation", "orientation:" + orientation);
}
};
Larger update_value means the resulting data is less smooth, which should be intuitive: if update_value == 1f, it falls back to your original code. Another note about update_value is it depends on the time interval between updates (related to sensor delay modes). You probably can tune this value to find one works for you, but if you want to know exactly how it works, check the alpha value definition under Electronic low-pass filters -> Discrete-time realization.
I had a similar problem showing an artificial horizon on my device. The low pass filter (LPF) solved this issue.
However you need to consider when you use the orientation angle in degrees and apply the LPF on it blindly, the result is faulty when the device is in portrait mode and turned from left to ride or opposite. The reason for this is the shift between 359 and 0 degree. Therefore I recommend to convert the degree into radians and apply the LPF on the sin and cos values of the orientation angle.
Further I recommend to use a dynamic alpha or update rate for the LPF. A static value for the alpha might be perfect on your device but not on any other.
The following class filters based on radians and uses a dynamic alpha as described above:
import static java.lang.Math.*;
Filter {
private static final float TIME_CONSTANT = .297f;
private static final float NANOS = 1000000000.0f;
private static final int MAX = 360;
private double alpha;
private float timestamp;
private float timestampOld;
private int count;
private int values[];
Filter() {
timestamp = System.nanoTime();
timestampOld = System.nanoTime();
values = new int[0];
}
int filter(int input) {
//there is no need to filter if we have only one
if(values.length == 0) {
values = new int[] {0, input};
return input;
}
//filter based on last element from array and input
int filtered = filter(values[1], input);
//new array based on previous result and filter
values = new int[] {values[1], filtered};
return filtered;
}
private int filter(int previous, int current) {
calculateAlpha();
//convert to radians
double radPrev = toRadians(previous);
double radCurrent = toRadians(current);
//filter based on sin & cos
double sumSin = filter(sin(radPrev), sin(radCurrent));
double sumCos = filter(cos(radPrev), cos(radCurrent));
//calculate result angle
double radRes = atan2(sumSin, sumCos);
//convert radians to degree, round it and normalize (modulo of 360)
long round = round(toDegrees(radRes));
return (int) ((MAX + round) % MAX);
}
//dynamic alpha
private void calculateAlpha() {
timestamp = System.nanoTime();
float diff = timestamp - timestampOld;
double dt = 1 / (count / (diff / NANOS));
count++;
alpha = dt/(TIME_CONSTANT + dt);
}
private double filter(double previous, double current) {
return (previous + alpha * (current - previous));
}
}
For further readings see this discussion.

(UNITY3d android game) When i don't touch the screen, my fps are lower than when i touch

I've had a problem for a long time with smooth camera on mobile phone (platformer game), but I reported that my game works well when my fps don't drop below 60. I notice that my fps are fine when i touch a screen, but when i don't do it, fps drop to approximately 58, 59 and after that my camera don't follow my player smoothly. For testing i create new scene with only FPSCounter script and the effects are the same. Could someone help me with it? I think that it is engine settings reasons, but i can't handle with it.emphasized text
//---------------------------------------------
// VARIABLES
//---------------------------------------------
private float deltaTime = 0.0f;
//---------------------------------------------
// METHODS FROM SUPERCLASS
//---------------------------------------------
void Update()
{
deltaTime += (Time.deltaTime - deltaTime) * 0.1f;
}
void OnGUI()
{
GUIStyle style = new GUIStyle();
float x = Screen.width - 110;
float fps = 1.0f / deltaTime;
Rect rect = new Rect(x, 90, 100, 50);
style.fontSize = 18;
style.normal.textColor = getColor(fps);
string text = string.Format("{0:0,0.0000 FPS}",fps);
GUI.Label(rect, text, style);
}
//---------------------------------------------
// CLASS LOGIC
//---------------------------------------------
private Color getColor(float fps)
{
if (fps >= 60)
{
return Color.yellow;
}
return Color.red;
}
Have you tried using the new UI system introduced in Unity 4.6? Maybe that fixes your issues.
https://www.youtube.com/watch?v=EOX6itCuKOc
As previously mentioned, you should really switch to the new UI system as the old GUI system was always a nightmare.
However, if you aren't wanting to switch, try setting more of your variables outside of OnGUI.
OnGUI is called multiple times per frame and it is expensive to set up a GUI style etc. so frequently - especially on an already poorly performing behaviour.

Categories

Resources