So I'm using a script I found on here (How to enable gyroscope camera at current device orientation) BUT I only want this initial device orientation to affect the heading.
Quaternion offset;
void Awake()
{
Input.gyro.enabled = true;
}
void Start()
{
//Subtract Quaternion
offset = transform.rotation * Quaternion.Inverse(GyroToUnity(Input.gyro.attitude));
}
void Update()
{
GyroModifyCamera();
}
void GyroModifyCamera()
{
//Apply offset
transform.rotation = offset * GyroToUnity(Input.gyro.attitude);
}
private static Quaternion GyroToUnity(Quaternion q)
{
return new Quaternion(q.x, q.y, -q.z, -q.w);
}
For the backstory, I have a few interior renderings, with a gyro control to look around the space. I don't want to affect the entire world orientation, I just want them to be facing in the "correct" direction, in terms of the best view of the space. (If not, it just depends on how the device is oriented and its common to jump into a space looking at a random, uninteresting corner of the room.)
With the script above, it resets the camera on all three axes (and therefore the entire world orientation), so if the user is holding the tablet at an unusual angle, then things get really weird.
Here's a quick image to assist the explanation:
I always want the initial view to be aligned with the green arrow, not something random like the red arrow. But just the heading, it's OK to be looking at the floor or ceiling as long as the heading is right in that sweet spot of the room.
Thanks!
Related
I am using the Unity Engine with C#.
I have a 1x1 cube which moves forward on a grid of 49, 1x1 cubes (screenshot below) - when I press the start button on the controller.
The movement code for the cube is below.
void MovePlayerCube()
{
transform.Translate(direction * moveSpeed * Time.deltaTime);
}
When this cube passes over a cube with an arrow on it, the cube will change direction to where the arrow is pointing (staying on the same Y axis).
I need to detect the exact point at which the cube is directly over the cube with the arrow on it, and run the 'change direction' code at that point.
I'm currently using Vector3.Distance to check if the X and Z coordinates of the 2 cubes are close enough together (if they are less than 0.03f in distance), I can't check if they are equal due to floating point imprecision.
However this is really ineffective as half the time this code doesn't register for probably the same reason, and if I increase the 0.03f to a point where it never misses it becomes really noticeable that the cube isn't aligned with the grid anymore.
There has to be a proper solution to this and hopefully I've clarified the situation enough?
Any advice is appreciated.
You are moving your cube via
transform.Translate(direction * moveSpeed * Time.deltaTime);
which will never be exact an might overshoot your positions.
=> I would rather implement a coroutine for moving the cube exactly one field at a time, ensuring that after each iteration it fully aligns with the grid and run your checks once in that moment.
It doesn't even have to match exactly then, you only need to check if you are somewhere hitting a cube below you.
So something like e.g.
private Vector3Int direction = Vector3Int.left;
private IEnumerator MoveRoutine()
{
// depends on your needs if this runs just forever or certain steps
// or has some exit condition
while(true)
{
// calculate the next position
// optional round it to int => 1x1 grid ensured on arrival
// again depends a bit on your needs
var nextPosition = Vector3Int.RoundToInt(transform.position) + direction;
// move until reaching the target position
// Vector3 == Vector3 uses a precision of 1e-5
while(transform.position != nextPosition)
{
transform.position = Vector3.MoveTowards(transform.position, nextPosition, moveSpeed * Time.deltaTime);
yield return null;
}
// set target position in one frame just to be sure
transform.position = nextPosition;
// run your check here ONCE and adjust direction
}
}
start this routine only ONCE via
StartCoroutine(MoveRoutine());
or if you have certain exit conditions at least only run one routine at a time.
A Corouine is basically just a temporary Update routine with a little bit different writing => of course you could implement the same in Update as well if you prefer that
private Vector3Int direction = Vector3Int.left;
private Vector3 nextPosition;
private void Start()
{
nextPosition = transform.position;
}
private void Update()
{
if(transform.position != nextPosition)
{
transform.position = Vector3.MoveTowards(transform.position, nextPosition, moveSpeed * Time.deltaTime);
}
else
{
transform.position = nextPosition;
// run your check here ONCE and adjust direction
// then set next position
nextPosition = Vector3Int.RoundToInt(transform.position) + direction;
}
}
Then regarding the check you can have a simple raycast since you only run it in a specific moment:
if(Physics.Raycast(transform.position, Vector3.down, out var hit))
{
direction = Vector3Int.RountToInt(hit.transform.forward);
}
assuming of course your targets have colliders attached, your moved cube position (pivot) is above those colliders (assumed it from your image) and your targets forward actually points int the desired new diretcion
I would do it this way. First I would split the ability of certain objects to be "moving with certain speed" and "moving in a certain direction", this can be done with C# interfaces. Why? Because then your "arrow" cube could affect not only your current moving cube, but anything that implements the interfaces (maybe in the future you'll have some enemy cube, and it will also be affected by the arrow modifier).
IMovingSpeed.cs
public interface IMovementSpeed
{
float MovementSpeed{ get; set; }
}
IMovementDirection3D.cs
public interface IMovementDirection3D
{
Vector3 MovementDirection { get; set; }
}
Then you implement the logic of your cube that moves automatically in a certain direction. Put this component on your player cube.
public class MovingStraight: MonoBehaviour, IMovementSpeed, IMovementDirection3D
{
private float _movementSpeed;
Vector3 MovementSpeed
{
get { return _movementSpeed; }
set { _movementSpeed = value; }
}
private Vector3 _movementDirection;
Vector3 MovementDirection
{
get { return _movementDirection; }
set { _movementDirection= value; }
}
void Update()
{
// use MovementSpeed and MovementDirection to advance the object position
}
}
Now to implement how the arrow cube modifies other objects, I would attach a collision (trigger) component for both moving cube and the arrow cube.
In the component of the arrow cube, you can implement an action when something enters this trigger zone, in our case we check if this object "has direction that we can change", and if so, we change the direction, and make sure that the object will be aligned, by forcing the arrow cube's position and the other object's position to be the same on the grid.
public class DirectionModifier: MonoBehaviour
{
private Vector3 _newDirection;
private void OnTriggerEnter(Collider collider)
{
IMovementDirection3D objectWithDirection = collider as IMovementDirection3D ;
if (objectWithDirection !=null)
{
objectWithDirection.MovementDirection = _newDirection;
// to make sure the object will continue moving exactly
// from where the arrow cube is
collider.transform.position.x = transform.position.x;
collider.transform.position.y = transform.position.y;
}
}
}
If you made your trigger zones too large, however, then the moving cube will "jump" abruptly when it enters the arrow cube's trigger zone. You can fix it by either starting a coroutine as other answers suggested, or you could make the trigger zones pretty small, so that the jump is not noticeable (just make sure not to make them too small, or they may not intersect each other)
You could then similarly create many other modifying blocks, that would change speed or something
I think that it is enough for you to check if the X and Z coordinates are equal, since the movement occurs only along them
Example
if(_player.transfom.position.x == _gameSquare.transfom.position.x && _player.transfom.position.z == _gameSquare.transfom.position.z)
{
Debag.Log("Rotate!")
}
Current problem:
I'm using world space quaternion offsets to rotate a player model's hands to the player's true hands using:
private Quaternion GetOffsetFromAvatar(Quaternion a, Quaternion b)
{
Quaternion _offset = a * Quaternion.Inverse(b);
return _offset;
}
then applying it with:
public void OffsetTarget(Transform Target, Transform PlayerHand, Transform AvatarBone)
{
/// OffsetTarget() is to be used to auto align avatar hands to player hands.
Quaternion _offset = GetOffsetFromAvatar(PlayerHand.rotation,
AvatarBone.GetComponentInChildren<AvatarOffsetPoint>().transform.rotation);
Target.rotation = _offset * PlayerHand.rotation;
}
The expected result should allow players to change their model on the fly during runtime and the hands align correctly to the player's real hands, but when I test that, the hands face in completely different directions.
.
Solution
In the OffsetTarget() method; I needed to change the last line were the offset is finally applied to the avatar.
From:
...
Target.rotation = _offset * PlayerHand.rotation;
}
To:
...
Target.rotation = _offset;
}
Someone smarter than me, correct me if I'm wrong about the reasoning behind this answer:
I was assuming I was doing the Quaternion math in world space, but the PlayerHand.rotation; part of the line was actually applying the math relative to the player's hand, instead of world space by using Quaternion.identity;.
I really need to stop asking a question just to answer it a few minutes later...
I'm using a Texture2D to display a map, and I need to get the color of the pixel I clicked on. I used Input.mousePosition to get the float coordinates, but using GetPixel to get the color requires the coordinates to be integers.
I am having trouble with getting GetPixel to find the coordinate that I clicked on.
When using floats and clicking on say, the rightmost side of the texture, I get a number like 27.xxx, but when I cast it to an integer, it displays a coordinate 27 pixels from the leftmost side of the texture. The way floats represent pixels confuses me a great deal, maybe clarifying that would help.
public class ProvinceSelectScript : MonoBehaviour {
public Material SpriteMain;
public Color SelectedCol;
public Color NewlySelectedCol;
public Texture2D WorldColMap;
Vector2 screenPosition;
Vector2 worldPosition;
void Start()
{
WorldColMap = (Texture2D)SpriteMain.GetTexture("_MainTexture");
NewlySelectedCol = Color.blue;
}
private void OnMouseDown()
{
screenPosition = new Vector2(Input.mousePosition.x,Input.mousePosition.y);
worldPosition = Camera.main.ScreenToWorldPoint(screenPosition);
SelectedCol = WorldColMap.GetPixel(((int)(worldPosition.x)+(WorldColMap.width/2)) , (int)((worldPosition.y)+(WorldColMap.height / 2)));
SpriteMain.SetColor("_SelectedProvince", SelectedCol);
SpriteMain.SetColor("_NewlySelectedProvince", NewlySelectedCol);
}
}
The worldPosition in the question isn't calculated in a way that's useful if you're using a perspective camera or if your camera is pointed any direction but directly forward.
To find the world position of the click, the best way to go about that is to use Camera.ScreenPointToRay to calculate the position of the click when intersecting the plane made by the position of the sprite and its local forward.
Either way, a world position does not mean anything to the sprite, which could be positioned anywhere in world space. You should rather use transform.InverseTransformPoint to calculate the local position you're clicking on. At that point, you can then use the spriterenderer's bounds to convert to normalized form (0-1 originating fromt he bottom-left instead of world unit lengths originating from the center).
But, once you have the local sprite position of the click expressed in normalized form, you can try to use GetPixelBilinear to get the color at the UV of (x,y) of the click. If the sprite is super simple, this MAY work. If it is animated or nine-sliced, or anything else it probably won't, and you'll have to reverse-engineer what UV the mouse is actually hovering over.
Camera mainCam;
SpriteRenderer sr;
void Start()
{
WorldColMap = (Texture2D)SpriteMain.GetTexture("_MainTexture");
NewlySelectedCol = Color.blue;
mainCam = Camera.main; // cache for faster access
sr = GetComponent<SpriteRenderer>(); // cache for faster access;
}
private void OnMouseDown()
{
Plane spritePlane = new Plane(transform.position, transform.forward);
Ray pointerRay = Camera.main.ScreenPointToRay(Input.mousePosition);
if (spritePlane.Raycast(pointerRay, out distance))
{
Vector3 worldPositionClick = pointerRay.GetPoint(distance);
Vector3 localSpriteClick = transform.InverseTransformPoint(worldPositionClick);
// convert [(-extents,-extents),(extents,extents)] to [(0,0),(1,1)]
Vector3 localSpriteExtents = sr.sprite.bounds.extents;
localSpriteClick = localSpriteClick + localSpriteExtents ;
localSpriteClick.x /= localSpriteExtents.x * 2;
localSpriteClick.y /= localSpriteExtents.y * 2;
// You clicked on localSpriteClick, on a very simple sprite (where no uv magic is happening) this might work:
SelectedCol = WorldColMap.GetPixelBilinear(localSpriteClick.x, localSpriteClick.y);
SpriteMain.SetColor("_SelectedProvince", SelectedCol);
}
I'm learning unity and c#, and want to make my movement to be camera relative movement instead of world relative movement. How do I do that?
I'm learning unity and c#, my unity version is 2018.3.12f1. I would be happy for help.
just to let know, instead of moving the cam I'm rotating the player.
void Update()
{
float AxisY = Player.transform.eulerAngles.y;
/* Movement starts here */
Vector3 Movement = new Vector3 (Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) { //running code
Player.transform.position += Movement * running_speed * Time.deltaTime;
} else {
Player.transform.position += Movement * speed * Time.deltaTime;
}
/*Movement ends here */
/* Rotation controller starts here */
Quaternion target = Quaternion.Euler(Player.transform.eulerAngles.x, Player.transform.eulerAngles.y, Player.transform.eulerAngles.z);
/*if (Player.transform.eulerAngles.x != 0 || Player.transform.eulerAngles.z != 0 || Player.transform.eulerAngles.y != 0) {
Player.transform.rotation = Quaternion.Euler(0,0,0);
}*/
if (Input.GetKey(KeyCode.E))
{
Debug.Log("E got pressed");
//float AxisYPositive = Player.transform.eulerAngles.y;
AxisY = AxisY+1;
Player.transform.rotation = Quaternion.Euler(0, AxisY, 0);
} else if (Input.GetKey(KeyCode.Q))
{
Debug.Log("Q got pressed");
//float AxisYNegetive = Player.transform.eulerAngles.y;
AxisY=AxisY-1;
Player.transform.rotation = Quaternion.Euler(0, AxisY, 0);
}
}
}
The player's movement is world relative, how to make the movement camera relative?
If you want to make the movements relative to the gameObject, call the method Transform.Rotate() on the transform of the gameObject you want to rotate rather than modifying its Quaternion directly. Just make sure the final argument is set to Space.Self.
if (Input.GetKey(KeyCode.E))
{
Debug.Log("E got pressed");
//float AxisYPositive = Player.transform.eulerAngles.y;
AxisY = AxisY+1;
Player.transform.Rotate(Quaternion.Euler(0, AxisY, 0), Space.Self);
}
In general you don't want to directly mess with objects transform.rotation, at least not unless you at least somewhat understand quaternions (I don't!).
I can see a few issues with your code, but the common thread seems to be that you don't really understand how transforms work. Specifically, you might want to look into World/Local space.
The usual way to control a player goes roughly like this:
void DoMovement(Transform player)
{
//If you move first your controls might feel 'drifty', especially at low FPS.
Turn(player);
Move(player);
}
void Turn(Transform player)
{
float yaw = Input.GetAxis("Yaw") * time.deltaTime; //Aka turn left/right
player.Rotate(0, yaw, 0, Space.Self);
// Space.Self is the default value, but I put it here for clarity.
//That means the player will rotate relative to themselves,
//...instead of relative to the world-axis, like in your code.
}
You didn't ask about movement, but as-is your character will always move relative to the world. The below should make it move relative to the camera.
Transform _cameraTransform; //Assumes this is set druing Start()
void Move(Transform player)
{
var forwardMove = _cameraTransform.Forward; //Get whatever direction is 'forward' for camera
forwardMove.Y = 0; //Don't want movement up and down.
forwardMove = forwardMove.normalized; //Normalize sets the 'power' of the vector to 1.
//If you set Y to 0 and don't normalize you'll go slower when camera looks down
//...than when camera is flat along the plane
player.position += forwardMove * Input.GetAxis("Vertical") * time.deltaTime;
//Here you could do the same for strafe/side to side movement.
//Would be same as above, but using the transform.right and Horizontal axis
}
Now, I'm making some assumptions here since you haven't specified what kind of game it is and what kind of controls you want. I'm assuming you have a character running around on a mostly flat plane (no aircraft/spaceship controls), and that the camera is attached to the player. This might not not actually be the case.
In any case I advice you to check out the tutorials, especially the Roll-a-Ball tutorial which I have found is good for beginners to get a grasp on basic players controls that are not just world-relative. The other tutorials, too, are pretty good if you think they're interesting.
Aside from the official Unity tuts a ton of decent to amazing tutorials out there, including video tutorials, so for something like this you could just search for <game type> tutorial and pick whatever seems good to you. While getting started I advice you to avoid the shortest videos, as you will likely benefit greatly from explanation that only fits in longer videos. Of course, that doesn't mean you should pick the longest videos either.
In case someone needs to move an object and don't care about colliders, you can use transform.Translate and assign to his second parameter relativeTo your camera (or any transform) to automatically calculate the translation relative to the object assigned.
I have this code that figures out the shortest distance between my white balls and my green balls.
When it's used by my green balls, they chase the white ones and turn them into green ones.
Is there a way to do the opposite of what my green balls do so my white ones run away? The green balls code is below:
private Vector3 FindClosestHuman()
{
GameObject[] Humans = GameObject.FindGameObjectsWithTag("Human");
Transform bestTarget = null;
float closestDistanceSqr = Mathf.Infinity;
Vector3 currentPosition = this.transform.position;
foreach (GameObject human in Humans)
{
Vector3 directionToTarget = human.transform.position - currentPosition;
float dSqrToTarget = directionToTarget.sqrMagnitude;
if (dSqrToTarget < closestDistanceSqr)
{
closestDistanceSqr = dSqrToTarget;
bestTarget = human.transform;
}
}
return bestTarget.position;
}
void Update ()
{
infectionAgent.SetDestination(FindClosestHuman());
}
I've tried using transform.translate and Vector3 in the Update method but I just can't seem to get it to work...
There is no opposite of SetDestination for Unity's NavMeshAgent.
That is: how you decide which direction to take at a fork when running away from something? Randomly? The one that gives the most distance? What if you can run in circles, is that better than running into a dead end, even if you stay closer to your away-from target? Does running towards your away-from target in order to reach a branch that lets you reach a farther-away destination allowed?
You need to figure out what "run away" means and write your own algorithm for that sort of navigation.