I'm working on an Augmented Reality app for Android without tracking images/objects. The user stands at a predefined position and virtual objects are placed into the real world. when the user turns around or moves the phone, the objects are fixed at their respective places. I do this by applying the gyroscope data to the camera.
My problem: I want the objects positions to be always fixed to the same places regardless of the users viewing direction when he starts up the app. Right now, on starting the app, the objects are positioned depending on the camera. After that, they are fixed to their places, when the user changes his viewing direction.
I drew an image of what the exact problem is to better elaborate:
I want to know which sensors are relevant to solve this problem. Since Google Maps accurately determines the viewing direction of a user, I assume there are built in sensors to find out in which direction the user is looking in order to apply this information to the camera's rotation at the start.
This is the code I use to apply the phones rotation to the camera (I'm using Unity and C#):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Gyrotransform : MonoBehaviour
{
// STATE
private float _initialYAngle = 0f;
private float _appliedGyroYAngle = 0f;
private float _calibrationYAngle = 0f;
private Transform _rawGyroRotation;
private float _tempSmoothing;
// SETTINGS
[SerializeField] private float _smoothing = 0.1f;
private IEnumerator Start()
{
Input.gyro.enabled = true;
Application.targetFrameRate = 60;
_initialYAngle = transform.eulerAngles.y;
_rawGyroRotation = new GameObject("GyroRaw").transform;
// _rawGyroRotation.parent = Core.Instance.transform;
_rawGyroRotation.position = transform.position;
_rawGyroRotation.rotation = transform.rotation;
// Wait until gyro is active, then calibrate to reset starting rotation.
yield return new WaitForSeconds(1);
StartCoroutine(CalibrateYAngle());
}
private void Update()
{
ApplyGyroRotation();
ApplyCalibration();
transform.rotation = Quaternion.Slerp(transform.rotation, _rawGyroRotation.rotation, _smoothing);
}
private IEnumerator CalibrateYAngle()
{
_tempSmoothing = _smoothing;
_smoothing = 1;
_calibrationYAngle = _appliedGyroYAngle - _initialYAngle; // Offsets the y angle in case it wasn't 0 at edit time.
yield return null;
_smoothing = _tempSmoothing;
}
private void ApplyGyroRotation()
{
_rawGyroRotation.rotation = Input.gyro.attitude;
_rawGyroRotation.Rotate(0f, 0f, 180f, Space.Self); // Swap "handedness" of quaternion from gyro.
_rawGyroRotation.Rotate(90f, 180f, 0f, Space.World); // Rotate to make sense as a camera pointing out the back of your device.
_appliedGyroYAngle = _rawGyroRotation.eulerAngles.y; // Save the angle around y axis for use in calibration.
}
private void ApplyCalibration()
{
_rawGyroRotation.Rotate(0f, -_calibrationYAngle, 0f, Space.World); // Rotates y angle back however much it deviated when calibrationYAngle was saved.
}
public void SetEnabled(bool value)
{
enabled = true;
StartCoroutine(CalibrateYAngle());
}
}
As far as I understand it the Gyroskope returns the rotational difference since it was started.
That's why your objects appear in the direction you are facing during start.
I guess what you rather want might be Compass.magneticHeading at least for setting the correct rotation once at gamestart
// Orient an object to point to magnetic north.
transform.rotation = Quaternion.Euler(0, -Input.compass.magneticHeading, 0);
You could do this once at start on the parent of all the objects you want to show in order to orient them correctly on the GPS north.
Related
I am using the new input system in my unity project. I also use Cinemachine. I use Cinemachine Input Provider to change the input from the old to the new system.
When I change max speed to Input Value Gain in speed field of virtual camera's settings (I did it because it is the most comfortable way to control camera) I face a problem.
My problem: When my character moves after some time the camera speed changes. If I start to move in the opposite direction, the camera speed returns to normal.
This is independent of the other components in the scene. My scene has only plane, cube, camera and my character.
Here's my character control code (ignore the awful calculation of movement direction):
private Rigidbody _rb;
private Vector2 _moveDirection;
private float speed = 5f;
private void Awake()
{
_rb = GetComponent<Rigidbody>();
Cursor.lockState = CursorLockMode.Locked;
}
public void OnMove(InputAction.CallbackContext context)
{
_moveDirection = context.ReadValue<Vector2>();
}
private void FixedUpdate()
{
Move(_moveDirection);
}
private void Move(Vector3 moveDirection)
{
float scaledMoveSpeed = speed * Time.deltaTime;
moveDirection = new Vector3(Camera.main.transform.forward.x, 0, Camera.main.transform.forward.z).normalized * moveDirection.y + new Vector3(Camera.main.transform.right.x, 0, Camera.main.transform.right.z).normalized * moveDirection.x;
_rb.MovePosition(transform.position + moveDirection * scaledMoveSpeed);
}
Here's a screenshot of the camera settings and Cinemachine Input Provider:
And screenshots of the Input Actions settings:
I found a solution for those who will ever face this problem! On your main camera, in the CinemachineBrain component, change the Update Method from Smart Update to Late Update. That should help
I'm fairly new to Unity, and I've begun learning how to use Fishnet networking. I've created a basic player movement script that syncs player position far faster than a Network Transform would. But I'm running into a bizarre problem I don't know how to solve.
In my scene, I have a Network Manager which, upon connection, spawns my Player prefab--a simple sprite with a player script and a network object. I haven't added a network transform, since I'll be syncing each player's position manually to reduce delay between clients. Here's the player script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using FishNet.Object;
public class Player : NetworkBehaviour
{
private void Update()
{
if (IsOwner) //only the client that owns this object will run this code
{
//get input, send it to server
float horizontalInput = Input.GetAxisRaw("Horizontal");
float verticalInput = Input.GetAxisRaw("Vertical");
RpcMoveCharacter(horizontalInput, verticalInput);
}
//since this is an observers rpc, only the server will call it
RpcSendCharacterPosition(transform.position.x, transform.position.y);
}
[ServerRpc]
public void RpcMoveCharacter(float x, float y)
{
//change the position of the server's instance of the player
transform.Translate(x * 10f * Time.deltaTime * Vector3.right);
transform.Translate(y * 10f * Time.deltaTime * Vector3.up);
}
[ObserversRpc]
public void RpcSendCharacterPosition(float x, float y)
{
if (IsClientOnly)
{
//ensure clients' instance of the player match the server's' position
transform.position = new Vector2(x, y);
}
}
}
The script works perfectly...except for one problem: the movement speed of the player isn't consistent for both players. The issues only occur when I build and run my game, then have the two versions of the game connect.
When either player is a host (server + client) their player object moves at medium speed on both screens. This is the intended speed.
When the version of my game running from my unity editor window is only a client, the player moves at fast speed on both screen--many times faster than intended.
When the version of my game I created using 'build and run' is only a client, the player moves at slow speed on both screens--many times slower than intended.
I've tested everything I can think of. One test I did was to prevent the network manager from spawning the player prefab, place the player object in scene ahead of time, and convert this:
private void Update()
{
if (IsOwner)
{
float horizontalInput = Input.GetAxisRaw("Horizontal");
float verticalInput = Input.GetAxisRaw("Vertical");
RpcMoveCharacter(horizontalInput, verticalInput);
}
RpcSendCharacterPosition(transform.position.x, transform.position.y);
}
[ServerRpc]
to this:
private void Update()
{
//now anyone can control the player object
float horizontalInput = Input.GetAxisRaw("Horizontal");
float verticalInput = Input.GetAxisRaw("Vertical");
RpcMoveCharacter(horizontalInput, verticalInput);
RpcSendCharacterPosition(transform.position.x, transform.position.y);
}
//same effect as note above
[ServerRpc (RequireOwnership = false)]
in order to see if there was something about the player spawning feature was bugged. My changes had zero effect whatsoever--nothing changed at all. If my editor was a client only it still moved the player too quickly, and if my build was a client only it still moved the player too slowly.
Another thing I tried was to make a brand new project in case I had toggled a setting weirdly or something in the last one. Once I had created a new project, all I did was import fishnet, add fishnet's default NetworkManager object to my scene, create a simple prefab called player, add a network object and the original player script to the player prefab, set the network manager to spawn the player prefab, and tried again. No luck--everything was exactly the same.
Any ideas? I'm super stuck here--I don't know what else to try, since everything in the code/scene seems to be working perfectly. I can't figure out why my build would be behaving differently than my editor's play mode, regardless of which is the server (or host) and which is the client only.
Thanks!
Creator of FishNet here. The only possible way you can update a transforms data quicker than the built-in NetworkTransform is by sending a RPC and snapping the transform every time data arrives.
Otherwise setting the interpolation and interval to 1 on NetworkTransform will send every tick, and smooth over only 1 tick. It's literally the fastest possible way to update transforms without snapping them.
If you want the entire network to move faster then add a TimeManager component to your network manager and increase the tick rate.
So I don't really know how exactly this Fishnet works.
But as said in general in any networking you can never rely on
All your devices running at the same FPS (frames per second)
Your networked messages arriving at the server/other clients immediately
Your networked messages arriving at the server/other clients in the exact same intervals as you ended them
So what I would rather do as mentioned is
First of all do not send network messages every frame but rather some fixed time intervals (like e.g. often used every 0.2 seconds)
Rather handle all local movement locally and immediately
It would be very bad for the UX if you send your user inputs to the server and have to wait until it is applied by receiving back the resulting position. This causes a 2-times network delay which would be extremely uncanny for the local user.
Instead of the delta rather synchronize the resulting position value.
This way you can be sure all players are in sync with the actual resulting positions and it works immediately also for players who joined the session later or potentially missed a few input messages due to network lag.
So I would do something like
public class Player : NetworkBehaviour
{
// Interval in seconds how often to send your position to the server/clients
[SerializeField] private float sendInterval = 0.2f;
// How fast you can move in units per second
[SerializeField] private float moveSpeed = 10f;
// Use this to adjust your input sensitivities
[SerializeField] [Min(0)] private float inputSensitivityX = 1f;
[SerializeField] [Min(0)] private float inputSensitivityY = 1f;
// Might have to play a bit with this value to make smooth interpolation faster or slower
// 5 is an arbitrary value but works quite good from experience
// depends on your sendInterval and movespeed as well
[SerializeField] privte float interpolation = 5f;
// keeps track of passed time
private float sendTimer;
private Vector2 receivedTargetPosition;
private void Start()
{
if(!IsOwner)
{
receivedTargetPosition = transform.position;
}
}
private void Update()
{
//only the client that owns this object will run this code
if (IsOwner)
{
//get input
var horizontalInput = Input.GetAxisRaw("Horizontal");
var verticalInput = Input.GetAxisRaw("Vertical");
var input = new Vector2(horizontalInput * inputSensitivityX, verticalInput * inputSensitivityY);
// Makes sure that you always have maximum 1 magnitude for the input
input = Vector2.ClampMagnitude(input, 1f);
// use the rotation to already rotate this vector from local into world space
input = trasform.rotation * input;
// Here you want the deltaTime of THIS DEVICE
var movement = moveSpeed * Time.deltaTime * input;
// Move your player LOCALLY
transform.position += (Vector3)movement;
}
// If you are not the owner you rather apply the received position
else
{
// I would e.g. smoothly interpolate somewhat like
transform.position = Vector3.Lerp(transform.position, receivedTargetPosition, interpolation * Time.deltaTime);
}
// Check if next send time interval has passed
sendTimer += Time.deltaTime;
if(sendTimer >= sendInterval)
{
sendTimer = 0;
if(IsServer)
{
RpcSendPositionToClients(transform.position.x, transform.position.y);
}
else
{
RpcSendPositionToServer(transform.position.x, transform.position.y);
}
}
}
[ServerRpc]
public void RpcSendPositionToServer(float x, float y)
{
// just in case
// the owner already gets its position in Update so nothing to do
if(IsOwner) return;
//change the position of the server's instance of the player
receivedTargetPosition = new Vector2(x, y);
}
[ClientRpc]
public void RpcSendPositionToClients(float x, float y)
{
// Owner and server already know the positions
if(IsOwner || IsServer) return;
receivedTargetPosition = new Vector2(x, y);
}
}
this question is a bit long but please bare with me.
I am working with Unity3D's newer input system, and I've been able to set up camera movement so when the user clicks the middle mouse button, they can drag the camera around along the X- & Y-axis. However, has a very parallax-y feel to it, which I am trying to avoid. From my understanding this is happening because the speed of the mouse movement & the camera movements are not the same (See, visual).
My InputController class:
public class InputController : MonoBehaviour
{
private InputControls inputControls;
[Header("Camera Movement")]
[SerializeField] CameraController cameraController;
private InputAction.CallbackContext context;
void Awake()
{
inputControls = new InputControls();
}
private void Start()
{
inputControls.Mouse.MiddleButton.started += MiddleButton_started;
}
public void MiddleButton_started(InputAction.CallbackContext ctx)
{
context = ctx;
}
private void Update()
{
bool mouseIsDown = context.performed;
if (mouseIsDown)
{
Vector2 delta = inputControls.Mouse.DeltaPosition.ReadValue<Vector2>();
cameraController.Move(delta, false);
}
}
}
My CameraController class:
public class CameraController : MonoBehaviour
{
[SerializeField] Vector2 minPosition;
[SerializeField] Vector2 maxPosition;
[SerializeField] float speed = 3;
[SerializeField] float zPosition = -10f;
private Camera mainCam;
private void Start()
{
mainCam = GetComponent<Camera>();
}
public void Move(Vector2 deltaPosition, bool convertToViewportPoint = true)
{
if (convertToViewportPoint) deltaPosition = mainCam.ScreenToViewportPoint(deltaPosition);
Vector3 moveTo = new Vector3(deltaPosition.x, deltaPosition.y, 0);
Vector3 target = transform.position - moveTo;
float clampedX = Mathf.Clamp(target.x, minPosition.x, maxPosition.x);
float clampedY = Mathf.Clamp(target.y, minPosition.y, maxPosition.y);
transform.position = Vector3.Lerp(transform.position, new Vector3(clampedX, clampedY, -10), speed * Time.deltaTime);
}
}
Now, in this version of the CameraController, the parallax-y feel might be coming from the fact that the Lerp speed is constant, whereas the speed of the mouse movement is not.
However, I've tested it with various magnitudes (ie., the magnitudes of the delta mouse position, the current mouse position, the current mouse position subtracted from the delta mouse position, etc...) with generally the same results -- either the camera moves too fast for the mouse or vice versa.
From my limited but growing understanding of trig, magnitudes represents the speed of a vector but I CANNOT get the speed of the camera movement and the speed of the mouse movement to match up. I would like for this to happen so that when the user clicks a certain point, that point generally stays under the cursor when in movement (See other visual).
Can someone help me understand how I might achieve this?
Thanks kindly.
What I did to solve this, in case anyone comes across this, is get the position of the cursor when the mouse is first clicked. We can call that dragOrigin. Then we can get the change in position as the mouse moves (polling the input through Unity's input system can be done through an Update function). Then we subtract the new mouse position from dragOrigin. Once we have that difference -- lets call it deltaPosition -- we just add it to the camera's current position.
To ensure that the speed matches that of the mouse, run the deltaPosition and the currentMousePosition through SmoothDamp to get the velocity of the change. Its magnitude will be the speed you want the camera to move.
So for example:
// Call this when the user first clicks the mouse.
// Lets assume this is part of a class that controls the camera.
public void SetDragOrigin(Vector2 mousePosition)
{
// could probably cache Camera.main for efficiency
dragOrigin = Camera.main.ScreenToWorldPoint(mousePosition);
}
// This is called in an update function of another class that deals with input.
// (Or perhaps you can simply pass the input controls to the class controlling the camera, but that's not the case here).
public void Move(Vector2 newMousePosition)
{
mousePosition = mainCam.ScreenToWorldPoint(mousePosition);
Vector2 deltaPosition = dragOrigin - mousePosition;
Vector3.SmoothDamp(deltaPosition, mousePosition, ref dragVelocity, 0.1f, 250f, Time.deltaTime);
cameraPosition += new Vector3(deltaPosition.x, deltaPosition.y, 0) * dragVelocity.magnitude * Time.deltaTime;
// anything else you want to do like clamping camera position
// ....
}
One thing to note here, is that the 5th argument of SmoothDamp is the maxSpeed. I hardcoded it in this example, but you might want to play around with it. When I did not set the maxSpeed, there was some wonky things happening to the dragVelocity over time, so it may be better to set the maxSpeed to what suits you best.
First, I want you to understand my English.
I manually change the camera's projection to orthographic using the source code.
Please refer to the code below.
using UnityEngine;
using System.Collections;
public class CameraOrthoController : MonoBehaviour
{
private Matrix4x4 ortho;
private Matrix4x4 perspective;
public float near = 0.001f;
public float far = 1000f;
private float aspect;
public static CameraOrthoController Instance
{
get
{
return instance;
}
set { }
}
//-----------------------------------------------------
private static CameraOrthoController instance = null;
//---------------------------------------------------
// Use this for initialization
void Awake()
{
if (instance)
{
DestroyImmediate(gameObject);
return;
}
// 이 인스턴스를 유효한 유일 오브젝트로 만든다
instance = this;
}
private void Start()
{
perspective = Camera.main.projectionMatrix;
}
public void StartMatrixBlender(float OrthoSize)
{
aspect = (Screen.width + 0.0f) / (Screen.height + 0.0f);
if (OrthoSize != 0f)
{
float vertical = OrthoSize;
float horizontal = (vertical * 16f) / 9f;
ortho = Matrix4x4.Ortho(-horizontal, horizontal, -vertical, vertical, near, far);
BlendToMatrix(ortho, 1f);
}
else
{
BlendToMatrix(perspective, 1f);
}
}
//---------------------------------------
private Matrix4x4 MatrixLerp(Matrix4x4 from, Matrix4x4 to, float time)
{
Matrix4x4 ret = new Matrix4x4();
int i;
for (i = 0; i < 16; i++)
ret[i] = Mathf.Lerp(from[i], to[i], time);
return ret;
}
IEnumerator LerpFromTo(Matrix4x4 src, Matrix4x4 dest, float duration)
{
float startTime = Time.time;
while (Time.time - startTime < duration)
{
Camera.main.projectionMatrix = MatrixLerp(src, dest, (Time.time - startTime) / duration);
yield return new WaitForSeconds(0f);
}
Camera.main.projectionMatrix = dest;
}
//-------------------------------------------------
private Coroutine BlendToMatrix(Matrix4x4 targetMatrix, float duration)
{
StopAllCoroutines();
return StartCoroutine(LerpFromTo(Camera.main.projectionMatrix, targetMatrix, duration));
}
//-------------------------------------------------
public void OnEvent(EVENT_TYPE Event_Type, Component Sender, object Param = null, object Param2 = null)
{
switch (Event_Type)
{
}
}
}
I use code this way.
CameraOrthoController.Instance.StartMatrixBlender(OrthographicSize);
This has worked well so far.
However, the problem occurred when i added particle system for effect.
The screen where the problem is occurring
In a normal state, the effect appears in front of the gameobject, as shown on the scene screen at the bottom of the picture above.
But if I use the code I wrote above to manipulate the camera, the effect will always be obscured by all gameobject, as if it were on the game screen at the top of the picture. Despite the fact that the effects are located in front of the game object.
At first, I thought it would be possible to solve it with layer sorting, but I don't think it's a layer problem because it's visible under normal camera conditions.
I want to know where the problem is with the above codes because I have to use them.
Please let me know if you know how to solve it.
Thank you.
When you modify Camera.projectionMatrix, the camera will no longer update its rendering based on the field of view. The particle will remain behind the GameObject until you call Camera.ResetProjectionMatrix() which ends the effect off setting the Camera.projectionMatrix property.
If this doesn't work, use multiple cameras to make the particle system always appear on top of the 3D object. Basically, you render the 3D Object and other objects with the main camera then render the Particle System with another camera.
Layer:
1.Create new layer and name it "Particle"
2.Change the Particle System layer to Particle
Main Camera:
1.Make sure that the main camera's Clear Flags is set to Skybox.
2.Change the Culling Mask to "Everything". Click on Everything which is a setting of Culling Mask and de-select/uncheck Particle.
3.Make sure that its Depth is set to 0.
The camera should not render the Particle System at this point.
New Camera:
1.Create new Camera. Make sure it's at the-same position/rotation as the main camera. Also remove the AudioListener that is attached to it.
2.Change Clear Flags to Depth only.
3.Change the Culling Mask of the camera to be Particle and make sure that nothing else is selected in the "Culling Mask"
4.Change Depth to 1.
This will make the Particle System to always display on top of every object rendered with the first or main camera.
If you want the Particle System to appear on top of a Sprite/2d Object instead of Mesh/3D Object, change the sortingOrder of the particle's Renderer to be bigger than the SpriteRenderer's sortingOrder. The default is 0 so changing the Particle's sortingOrder to 1 or 2 should be fine.
particle.GetComponent<Renderer>().sortingOrder = 2;
I need to have a game object point north AND I want to combine this with gyro.attitude input. I have tried, unsuccessfully, to do this in one step. That is, I couldn't make any gyro script, which I found on the net, work with the additional requirement of always pointing north. Trust me, I have tried every script I could find on the subject. I deduced that it's impossible and probably was stupid to think it could be done; at least not this way (i.e. all-in-one). I guess you could say I surmised that you can't do two things at once. Then I thought possibly I could get the same effect by breaking-up the duties. That is, a game object that always points north via the Y axis. Great, got that done like this:
_parentDummyRotationObject.transform.rotation = Quaternion.Slerp(_parentDummyRotationObject.transform.rotation, Quaternion.Euler(0, 360 - Input.compass.trueHeading, 0), Time.deltaTime * 5f);
And with the game object pointing north on the Y, I wanted to add the second game-object, a camera in this case, with rotation using gyro input on the X and Z axis. The reason I have to eliminate the Y axes on the camera is because I get double rotation. With two things rotating at once (i.e. camera and game-object), a 180 degree rotation yielded 360 in the scene. Remember I need the game object to always point north (IRL) based on the device compass. If my device is pointing towards the East, then my game-object would be rotated 90 degrees in the unity scene as it points (rotation) towards the north.
I have read a lot about gyro camera controllers and one thing I see mentioned a lot is you shouldn't try to do this (limit it) on just 1 or 2 axis, when using Quaternions it's impossible when you don't know what you're doing, which I clearly do not.
I have tried all 3 solutions from this solved question: Unity - Gyroscope - Rotation Around One Axis Only and each has failed to rotate my camera on 1 axis to satisfy my rotational needs. Figured I'd try getting 1 axis working before muddying the waters with the 2nd axis. BTW, my requirements are simply that the camera should only rotate on 1 axis (in any orientation) based on the X axis of my device. If I could solve for X, then I thought it'd be great to get Z gyro input to control the camera as well. So far I cannot get the camera controlled on just 1 axis (X). Anyway, here are my findings...
The first solution, which used Input.gyro.rotationRateUnbiased, was totally inaccurate. That is, if I rotated my device around a few times and then put my phone/device down on my desk, the camera would be in a different rotation/location each time. There was no consistency. Here's my code for the first attempt/solution:
<code>
private void Update()
{
Vector3 previousEulerAngles = transform.eulerAngles;
Vector3 gyroInput = Input.gyro.rotationRateUnbiased;
Vector3 targetEulerAngles = previousEulerAngles + gyroInput * Time.deltaTime * Mathf.Rad2Deg;
targetEulerAngles.y = 0.0f;
targetEulerAngles.z = 0.0f;
transform.eulerAngles = targetEulerAngles;
}
</code>
The second solution was very consistent in that I could rotate my device around and then put it down on the desk and the unity camera always ended up in the same location/rotation/state so-to-speak. The problem I had was the camera would rotate on the one axis (X in this case), but it did so when I rotated my device on either the y or x axis. Either type of rotation/movement of my phone caused the unity camera to move on the X. I don't understand why the y rotation of my phone caused the camera to rotate on X. Here is my code for solution #2:
private void Start()
{
Input.gyro.enabled = true;
startEulerAngles = transform.eulerAngles;
startGyroAttitudeToEuler = Input.gyro.attitude.eulerAngles;
}
private void Update()
{
Vector3 deltaEulerAngles = Input.gyro.attitude.eulerAngles - startGyroAttitudeToEuler;
deltaEulerAngles.y = 0.0f;
deltaEulerAngles.z = 0.0f;
transform.eulerAngles = startEulerAngles - deltaEulerAngles;
}
The 3rd solution: I wasn't sure how to complete this last solution, so it never really worked. With the 2 axis zeroed-out, the camera just flipped from facing left to right and back, or top to bottom and back; depending on which axis were commented out. If none of the axis were commented-out (like the original solution) the camera would gyro around on all axis. Here's my code for attempt #3:
private void Start()
{
_upVec = Vector3.zero;
Input.gyro.enabled = true;
startEulerAngles = transform.eulerAngles;
}
private void Update()
{
Vector3 gyroEuler = Input.gyro.attitude.eulerAngles;
phoneDummy.transform.eulerAngles = new Vector3(-1.0f * gyroEuler.x, -1.0f * gyroEuler.y, gyroEuler.z);
_upVec = phoneDummy.transform.InverseTransformDirection(-1f * Vector3.forward);
_upVec.z = 0;
// _upVec.x = 0;
_upVec.y = 0;
transform.LookAt(_upVec);
// transform.eulerAngles = _upVec;
}
Originally I thought it was my skills, but after spending a month on this I'm beginning to think that this is impossible to do. But that just can't be. I know it's a lot to absorb, but it's such a simple concept.
Any ideas?
EDIT: Thought I'd add my hierarchy:
CameraRotator (parent with script) -> MainCamera (child)
CompassRotator (parent) -> Compass (child with script which rotates parent)
I'd do this in the following way:
Camara with default 0, 0, 0 rotation
Screenshot
Object placed at the center of the default position of the camera.
Script for the Camera:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour
{
Camera m_MainCamera;
// Start is called before the first frame update
void Start()
{
// Disable the sleep timeout during gameplay.
// You can re-enable the timeout when menu screens are displayed as necessary.
Screen.sleepTimeout = SleepTimeout.NeverSleep;
// Enable the gyroscope.
if (SystemInfo.supportsGyroscope)
{
Input.gyro.enabled = true;
}
m_MainCamera = Camera.main;
m_MainCamera.enabled = true;
}
// Update is called once per frame
void Update()
{
if (m_MainCamera.enabled)
{
// First - Grab the Gyro's orientation.
Quaternion tAttitude = Input.gyro.attitude;
// The Device uses a 'left-hand' orientation, we need to transform it to 'right-hand'
Quaternion tGyro = new Quaternion(tAttitude.x, tAttitude.y, -tAttitude.z, -tAttitude.w);
// the gyro attitude is tilted towards the floor and upside-down reletive to what we want in unity.
// First Rotate the orientation up 90deg on the X Axis, then 180Deg on the Z to flip it right-side up.
Quaternion tRotation = Quaternion.Euler(-90f, 0, 0) * tGyro;
tRotation = Quaternion.Euler(0, 0, 180f) * tRotation;
// You can now apply this rotation to any unity camera!
m_MainCamera.transform.localRotation = tRotation;
}
}
}
With this script my Object always face SOUTH no matter what.
If you want the object to face NORTH you just have to turn the view 180º on the Y axis as a last rotation:
Quaternion tRotation = Quaternion.Euler(-90f, 0, 0) * tGyro;
tRotation = Quaternion.Euler(0, 0, 180f) * tRotation;
//Face NORTH:
tRotation = Quaternion.Euler(0,180f, 0) * tRotation;
Hope this might help ;)