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 ;)
Related
My bat stays still in the air.
How can i check that the player, that is moving right below on x axis, comes from the left or right side relative to the bat?
public class Bat : MonoBehaviour
{
Player player;
void Start()
{
player = FindObjectOfType<Player>();
}
void Update()
{
if (transform.InverseTransformPoint(player.transform.position).x >= 0)
transform.rotation = new Quaternion(transform.rotation.x, 0f, transform.rotation.z, 0f);
else
transform.rotation = new Quaternion(transform.rotation.x, -180f, transform.rotation.z, 0f);
}
}
As you may notice i try to flip the the Bat so it actually looks at the player.
First, don't use new Quaternion(...); unless you absolutely, 100% know quaternions inside and out. For instance, having the w component be 0 results in zero effective rotation. Also, quaternions use unitless figures and not degrees for their components. See here for a nice visualization of what different quaternion values might look like, under Quaternion).
Second, your logic is weird because if the bat has zero (identity) rotation, and the player is on the left of the bat, then your logic tries to* flip the bat 180 degrees, so that the player is now on the right side of the bat.
Then, the next frame assuming the bat and player are still in the same positions, the logic says oh the player is on the right side, set the rotation to zero (identity) rotation, which is of course what it was in the first place. So basically, you would have the bat rotate 180 degrees again so that the player is once again on the left side of the bat.
And so, you could get into a situation where every frame, the bat would flip a complete 180. Definitely not desired.
* I'm assuming if you had used Quaternion.Euler(transform.eulerAngles.x, 0f, transform.eulerAngles.z) etc.
Instead of concerning with any of that, use Vector3.Cross to find the forward the bat should point so its right faces the player's position. Then, use Quaternion.LookRotation to set the rotation of the bat to point in that forward direction:
void Update()
{
Vector3 batRightDir = player.transform.position - transform.position;
Vector3 batForwardDir = Vector3.Cross(batRightDir, Vector3.up);
if (batForwardDir.sqrMagnitude == Vector3.zero)
{
// player above or below bat. do nothing?
return;
}
transform.rotation = Quaternion.LookRotation(batForwardDir);
}
If you have the player on a different z as the bat, this will cause the bat to rotate around the y axis accordingly, which is what you would want from a 3d game, and could be a neat effect for a 2d game if you want that kind of effect.
If you want it to ignore the z position of the player, you could zero out the z component of batRightDir...
void Update()
{
Vector3 batRightDir = player.transform.position - transform.position;
batRightDir.z = 0; // ignore Z differences between bat and player
Vector3 batForwardDir = Vector3.Cross(batRightDir, Vector3.up);
if (batForwardDir.sqrMagnitude == Vector3.zero)
{
// player above or below bat. do nothing?
return;
}
transform.rotation = Quaternion.LookRotation(batForwardDir);
}
or it may be more intuitive to branch as you were previously:
void Update()
{
Vector3 batRightDir = player.transform.position - transform.position;
if (batRightDir.x > 0)
{
transform.rotation = Quaternion.identity;
}
else if (batRightDir.x < 0)
{
transform.rotation = Quaternion.LookRotation(Vector3.back);
}
else
{
// player above or below bat. do nothing?
}
}
You can get the access to his Rigidbody and than call it.
If Rigidbody.velocity.x > 0 he is moving to the right. If it's <0 it's moving to the left. All is related to X axis of course.
I'm currently working on a 2D platforming game using Unity. When the player jumps, I prevent the camera from following them in order to allow the player to see what is beneath them. However, I have a mechanic implemented that allows the player to perform a double jump when they grab an enemy, and this double jump makes the player jump past the boundary of the screen. I was wondering how I would go about getting the camera to smoothy follow the player only when they exit certain boundaries. I have basic code written that does this in a choppy way. I will include gifs to show how my game currently behaves, and an example of how I want it to behave.
Here is the code I have written in my camera controller:
transform.position = new Vector3(player.position.x, 50, -100);
if (player.position.y > 50)
{
transform.position = player.position - transform.forward * camDist + Vector3.up * playerHeight;
}
Below is an embeded imgur gif of what my code does now:
<blockquote class="imgur-embed-pub" lang="en" data-id="a/houedOV" data-context="false" ></blockquote><script async src="//s.imgur.com/min/embed.js" charset="utf-8"></script>
and below here is an example of what I am trying to achieve:
<blockquote class="imgur-embed-pub" lang="en" data-id="a/deg5yeq"></blockquote><script async src="//s.imgur.com/min/embed.js" charset="utf-8"></script>
You should moving camera from origin to destination instead of set camera to destination immediately (1 frame or 1 update called)
Try to set camera position with this code
void Update(){
// ...
Vector2 destination = Vector2.zero; // your camera destination you expected
float maxMoveDistance = 1; // maximum distance to move camera in each frame
camera.transform.position = Vector2.MoveTowards(camera.transform.position, destination, maxMoveDistance); // move camera to destination
}
ok, I figured out the answer to my own question!
It was as simple as this:
void Update()
{
if (player.position.y > 50)
{
transform.position = new Vector3(player.position.x, player.position.y, -100);
}
else
{
transform.position = new Vector3(player.position.x, 50, -100);
}
I'm working on a prototype of a tower defense game and I've encountered a problem with the rotation of a turret. I made it so that every turret must have a rotator part which rotates horizontally and holds the main turret body with the cannon which rotates vertically. I came up with a simple script to this but it only seems to work for the rotator and not for the cannon, at least not the way it should.
Here is the code from the script:
void Update () {
if (target != null) {
Vector3 tempRotatorRotation = rotator.transform.localEulerAngles;
rotator.transform.LookAt (target.transform);
rotator.transform.localEulerAngles = new Vector3 (tempRotatorRotation.x, rotator.transform.localEulerAngles.y, tempRotatorRotation.z);
Vector3 tempCannonRotation = cannon.transform.localEulerAngles;
cannon.transform.LookAt (target.transform);
cannon.transform.localEulerAngles = new Vector3 (cannon.transform.localEulerAngles.x, tempCannonRotation.y, tempCannonRotation.z);
}
}
And here is an image of how this turns out. The rotator is rotated perfectly, but as you can see the cannon is looking down for some reason.
(Blue is the pedestal which doesn't move. Green is rotator. Red is turret body. Light blue is cannon)
The origin of the cannon 3D model is set almost at the start of it.
Here is the screenshot of the canon selected showing it's axis and transform data
forward to unity is the blue line, which in your diagram is facing up. try this
crate empty, attach to turret so it rotates, make sure blue line(z axis) is facing your forward direction, you can do this manualy by rotating. then place your barrel as a child of that object, and point that object at target.
ive had to do this several times with blender models, since blender uses the z axis as its vertical axis not its depth axis like unity.
-turret_test
-turret_test_pedestal
-turret_test_rotater
-turret_test_turret
-AIM(new empty, orient the proper direction then add child)
-turret_test_cannon
Your cannon's barrel points in its forward direction, so all you need to use is cannon.transform.LookAt (target.transform, cannon.transform.up);
void Update () {
if (target != null) {
/* rotator code here */
// Remember which way the top of the cannon faces.
Vector3 cannonUpDirection = cannon.transform.up;
// point the cannon directly at the target
cannon.transform.LookAt (target.transform, cannonUpDirection);
}
}
If the rotator isn't pointing at/above/below the target, then you have to figure out how much to rotate the canon upwards/downwards from the horizontal, then point it in the same direction as the rotator and then do that:
void Update () {
if (target != null) {
/* rotator code here */
// Remember which way the top of the cannon faces.
Vector3 cannonUpDirection = cannon.transform.up;
// point the cannon directly at the target
cannon.transform.LookAt (target.transform, cannonUpDirection);
// Find global direction for looking straight ahead above/below the target
Vector3 sameYDirection = Vector3.Scale(
cannon.transform.forward,
new Vector3(1f,0f,1f)
);
// Convert that to local
Vector3 sameYDirectionLocal = cannon.transform
.InverseTransformDirection(sameYDirection);
// get rotation for moving from looking ahead to looking direct
Quaternion lookTowards = Quaternion.FromToRotation(
sameYDirectionLocal,
Vector3.forward
);
// lookTowards is a locally-vertical rotation from horizontal to the target,
// given some top side of the cannon.
// Clamp lookTowards here if you have a max rotation speed.
// Face cannon in same direction of rotator;
cannon.transform.rotation = Quaternion.LookRotation(
rotator.forward, cannonUpDirection);
// Use lookTowards to look down from that position.
cannon.transform.rotation *= lookTowards;
}
}
Turns out Unity is broken and when I re-imported the model all the axis have changed and instead of the canon turning in the X axis up and down now it was in the Y axis while everything else rotates horizontally in Y.
Also in the calculation I had to add + 90 to both equations. This will make no sense now since it shows both are changing in Y axis but one is rotating horizontally and the other vertically.
if (target != null) {
Vector3 tempRotatorRotation = rotator.transform.localEulerAngles;
rotator.transform.LookAt (target.transform);
rotator.transform.localEulerAngles = new Vector3 (tempRotatorRotation.x, rotator.transform.localEulerAngles.y + 90, tempRotatorRotation.z);
Vector3 tempCanonRotation = canon.transform.localEulerAngles;
canon.transform.LookAt (target.transform);
canon.transform.localEulerAngles = new Vector3 (tempCanonRotation.x, canon.transform.localEulerAngles.y + 90, tempCanonRotation.z);
}
I have a simple waypoint system. It uses a transform of arrays that act as the bucket what holds the waypoint values.
I use this waypoint system to move a camera throughout a scene by moving towards one point to another. The scene is not big - so everything is close to each other.
The camera moves from one position to another by button click/press. This works fine.
void Start()
{
//Sets the Camera to the first point
Camera = GameObject.Find("Main Camera");
Camera.transform.position = patrolPoints[0].position
currentPoint = 0;
}
//Fixed Update seems to work better for LookAt
void FixedUpdate()
{
//Looks at initial Target
Camera.transform.LookAt(TargetPoints[0]);
if (click == true)
{
Camera.transform.position = Vector3.MoveTowards(Camera.transform.position, patrolPoints[currentPoint].position, moveSpeed * Time.deltaTime);
//Camera.transform.rotation = Quaternion.Slerp(Camera.transform.rotation, patrolPoints[currentPoint].transform.rotation, Time.deltaTime);
Camera.transform.LookAt(TargetPoints[currentPoint]);
}
}
public void onNextClick()
{
if (currentPoint >= patrolPoints.Length)
{
currentPoint = 0;
}
if (Camera.transform.position == patrolPoints[currentPoint].position)
{
currentPoint++;
click = true;
}
}
I am having difficulty with one aspect of the transform that I haven't talked about yet. That is the rotation.
I have used lookAt for setting up the target of the first look at point and that works. However when it runs to the next target in the look at array - the change is sudden.
I have tried an Slerp in the shot as well - and this works when the waypoint has been placed in the appropriate rotation value - and the value Slerps from one position to the next. However, the timing isn't quite aligning up yet. Some position transitions get there quicker - meaning the rotation is trying to get caught up / while others are lagging behind.
I have tried getting the distance between the current waypoint and the next waypoint and treating that as an overall percentage in the update relative to the current position of the camera - I believe this could help work out how far the rotation should be orientated given the position update.
However, I am somewhat lost on it. Many examples suggest looking at iTween - and I wouldn't imagine this would work - however, I want to get into the math a bit.
Can anyone put me in the right direction?
Looks like the Lerp for Position and a Slerp for Rotation done the trick.
MoveTowards wasn't playing ball with a Slerp on rotation - so I believe the timings weren't aligning.
if (click == true)
{
Camera.transform.position = Vector3.MoveTowards(Camera.transform.position, patrolPoints[currentPoint].position, moveSpeed * Time.deltaTime);
Camera.transform.rotation = Quaternion.Lerp(Camera.transform.rotation, patrolPoints[currentPoint].rotation, moveSpeed * Time.deltaTime);
I've been led to believe the lerp values work like a percentage of such - so the same input value works for it.
Finally I used a range on the distance between current position and update on the click - this helped speed up the button click.
if (Vector3.Distance(Camera.transform.position, patrolPoints[currentPoint].position) < PositionThreshold)
{
currentPoint++;
click = true;
}
Thank you for your time.
ok well i am working on a top down shooter i have made my character look at the mouse using screentoworld view and i made my camera follow my players x, and z axis but anytime my character moves parallel from the mouse it starts shaking I think its because it looks at the mouse an the mouse real world position is dependent on the cameras position which is, but i am not sure.
here is the playerscript the many backslashes showed other things I've tried
gameObject.GetComponent<Rigidbody>().velocity = new Vector3(Input.GetAxisRaw("Horizontal")*movementspeed,0,Input.GetAxisRaw("Vertical")*movementspeed);
if (gameObject.GetComponent<Rigidbody> ().velocity.x != 0 && gameObject.GetComponent<Rigidbody> ().velocity.z != 0) {
gameObject.GetComponent<Rigidbody>().velocity = new Vector3(Input.GetAxisRaw("Horizontal")*movementspeeddiag ,0,Input.GetAxisRaw("Vertical")*movementspeeddiag);
}
// makes vector 3 type target equall to mouse position on pixial cordinates converted in to real world coordniates
target = camera.ScreenToWorldPoint (new Vector3(Input.mousePosition.x,Input.mousePosition.y,camera.transform.position.y - transform.position.y));
//Vector3 newtarget = target - transform.position;
//lerptarget = Vector3.Lerp (transform.eulerAngles,newtarget, 100);
// states the vector 3 values of target
// makes object local z face target and iniziates up axis
//Quaternion rotation = Quaternion.LookRotation (newtarget, Vector3.up);
//transform.eulerAngles = Vector3.up * Mathf.MoveTowardsAngle (transform.eulerAngles.y, rotation.eulerAngles.y, rotatespeed * Time.deltaTime);
transform.LookAt (target,Vector3.up);
and camera script
void LateUpdate(){
position = new Vector3 (player.transform.position.x, 10, player.transform.position.z);
transform.position = position;
//transform.localPosition = Vector3.Lerp (transform.position, position, speed *Time.deltaTime);
transform.eulerAngles = new Vector3 (90,0,0);
}
Probably, the problem is that you change the velocity of rigidbody. Becouse velocity changes applied each fixed update time interval, it doesnt match with frame updating time, and may lead to shaking. According to unity docs you should try to avoid directly changes of velocity. (http://docs.unity3d.com/ScriptReference/Rigidbody-velocity.html)
I'd suggest you to you use MovePosition alongside with Lerp function to implement player movement.
See this:
http://docs.unity3d.com/ScriptReference/Rigidbody.MovePosition.html
http://docs.unity3d.com/ScriptReference/Vector3.Lerp.html