I have been working on programming a graph in Unity that rotates based on head movement. I have been having multiple issues with the rotation aspect of it.
A sample of the Autowalk class, which I am using to find the angle that the graph needs to rotate based on where the user is facing:
public class AutoWalk : MonoBehaviour {
//VR head
private Transform vrHead;
//angular displacement from normal
public float xAng, yAng, zAng;
//previous values
public float xOrig, yOrig, zOrig;
// Use this for initialization
void Start () {
//Find the VR head
vrHead = Camera.main.transform;
//finding the initial direction
Vector3 orig = vrHead.TransformDirection(Vector3.forward);
xOrig = orig.x;
yOrig = orig.y;
zOrig = orig.z;
}
// Update is called once per frame
void Update () {
//find the forward direction
Vector3 forward = vrHead.TransformDirection(Vector3.forward);
float xForward = forward.x;
float yForward = forward.y;
float zForward = forward.z;
//find the angle between the initial and current direction
xAng = Vector3.Angle(new Vector3(xOrig, 0, 0), new Vector3(xForward, 0, 0));
yAng = Vector3.Angle(new Vector3(0, yOrig, 0), new Vector3(0, yForward, 0));
zAng = Vector3.Angle(new Vector3(0, 0, zOrig), new Vector3(0, 0, zForward));
//set new original angle
xOrig = xAng;
yOrig = yAng;
zOrig = zAng;
}
From there I go to the ReadingPoints class, which contains all of the points on the graphs (spheres) and axes (stretched out cubes):
public class ReadingPoints : MonoBehaviour {
// Update is called once per frame
void Update () {
float xAngl = GameObject.Find("GvrMain").GetComponent<AutoWalk>().xAng;
float yAngl = GameObject.Find("GvrMain").GetComponent<AutoWalk>().yAng;
float zAngl = GameObject.Find("GvrMain").GetComponent<AutoWalk>().zAng;
if ((xAngl != prevXAngl) || (yAngl!=prevYAngl) || (zAngl!=prevZAngl))
{
//rotate depending on the angle from normal
foreach (GameObject o in allObjects)
{
o.transform.Rotate(new Vector3(xAngl, yAngl, zAngl), Space.World);
prevXAngl = xAngl;
prevYAngl = yAngl;
prevZAngl = zAngl;
}
}
allObjects, as the name implies, contains the points and the axes.
Anyway, the first issue that is upon running the program is that the graph appears to be torn apart.
How the graph is supposed to look (this is what it looks like when o.transform.Rotate(...) is commented out)
Here is how it actually looks :( Also, the graph does not rotate when I move, and I have no idea why (I thought I might be using the Rotate function improperly but perhaps I also didn't find the correct angles?). Any ideas of what went wrong are much appreciated, thank you!
First of all I think you should start using Quaternions (https://docs.unity3d.com/ScriptReference/Quaternion.html)
Secondly; while you rotate a object with a Camera attached its line of view will change and eventually the object(Graph) will be out of view. If you want as much of the full graph as possible to remain in view of camera as long as possible. You should give the Quaternion of the graph the opposite rotation of that applied to your Camera or the object it is attached to.
Make sure your all elements of your graph are children of a central gameobject in your graph, and only manipulate that point.
Related
I have one problem. I want my prefabs to spawn every time my player picks them up. I did research on Google and YouTube and I tried to use the random function and instantiate. I don't know how to use them. I wrote this code I saw on YouTube and my prefab Sphere moves like 1cm to z position. I want to every time when I pick up object or my player go to spawn more of this on the z position. How do I do this?
My smaller script:
public GameObject Sphere;
public float zrange;
// Use this for initialization
void Start () {
RandomPosition();
}
void RandomPosition()
{
zrange = Random.Range(0f, 2f);
this.transform.position = new Vector3(0, 0, zrange);
}
You achieve that by not messing with the x and y values (your code sets them both to 0).
Vector3 p = transform.position;
p.z = zrange;
transform.position = p;
This assumes that your code to instantiate the object is already correctly placing the object. If not, more information is needed.
I have created a game in which you can control X characters at the same time in the same form and they can die at any time. My problem is when I want the game camera to include all these gameobjects.
I thought that a good option is to calculate the central point between the gameobjects in the scene and make the camera follow that point at a certain distance.
I already have the camera code, but I still need to know how to get that central point or another way of doing it. In addition, the camera does not follow any of the axes (X, Y, Z) linearly, since it is placed in such a way that is the view is isometric (the game is in 3D).
As a last important fact, it is that all gameobjects that are running in the game (that are alive), are stored in a public static List <GameObject> to be able to access the components of these gameobjects at any time. Also, if a character (gameobject) dies or is born, the list is updated without problems.
I leave you a graphic example with three different cases, being the black points, the characters that are in the scene (gameobjects) and the red points, the central point (vector) that I would like to find.
Also, I leave the camera code so you can test if you have any solution:
public class Camera_Movement : MonoBehaviour {
Vector3 newPos;
public static List<GameObject> playersInGame = new List<GameObject>();
void Update() {
// Get Central Vector
// Replace playersInGame[0].transform.position with central vector
//newPos = Vector3.Lerp(gameObject.transform.position, "central vector", Time.deltaTime);
newPos = Vector3.Lerp(gameObject.transform.position, playersInGame[0].transform.position, Time.deltaTime);
gameObject.transform.position = new Vector3(newPos.x, newPos.y, newPos.z);
}
}
Thank you very much in advance!
You need to take the average x and and average y.
That would look like the following:
var totalX = 0f;
var totalY = 0f;
foreach(var player in playersInGame)
{
totalX += player.transform.position.x;
totalY += player.transform.position.y;
}
var centerX = totalX / playersInGame.Count;
var centerY = totalY / playersInGame.Count;
Let me know if this works for you (don't have access to Unity at the moment), but I put together an example here: https://dotnetfiddle.net/jGd99I
To have a solution that your camera can be best positioned to see all of your objects, then try this:
public Vector3 FindCenterOfTransforms(List<Transform> transforms)
{
var bound = new Bounds(transforms[0].position, Vector3.zero);
for(int i = 1; i < transforms.Count; i++)
{
bound.Encapsulate(transforms[i].position);
}
return bound.center;
}
Well, I am new to unity 3d and C sharp. I was trying a script to rotate my spehere object . But it's not working.
I was following a youtube video. This code worked for him. But in my case it is not working.
I added the transform object.
using UnityEngine;
using System.Collections;
public class cubescript : MonoBehaviour {
public Transform sphereTransform;
// Use this for initialization
void Start () {
sphereTransform.parent = transform;
}
// Update is called once per frame
void Update () {
transform.eulerAngles = new Vector3 (0, 180*Time.deltaTime, 0);
}
}
It's kind of working but stuck at 2.981877-3 Y rotation .. And not rotating around the cube..
The problem is that you are trying to rotate, but eulerAngles only sets to ABSOLUTE angles (if you want to add angles to the current frame angle, you will use Rotate).
So, if you use transform.eulerAngles you will be all the frames setting the angle change to what 180 * Time.deltaTime returns, that will depend on how many FPS you are running, thats why you get constant number.
If you use transform.Rotate it will add the new angle change to the current angle frame. Say that you want to increment by 10 degress, so frame 1 = (0,0,0), frame 2 = (0,10,0), frame 3 = (0,20,0).
In eulerAngles you will get all the time (0,10,0), because it sets ABSOLUTE angle, Rotate adds to the current angle what you want.
Change this
transform.eulerAngles = new Vector3 (0, 180*Time.deltaTime, 0);
To this
transform.Rotate(new Vector3 (0, 180*Time.deltaTime, 0));
This is the official Unity Documentation for eulerAngle and Rotate
As said in another answer, when you set transform.eulerAngles, you are setting an absolute rotation. You can use transform.Rotate() but you can also use Time.time to ensure that you get linear rotation: transform.eulerAngles = new Vector3(0, 180*Time.time, 0);
I am not sure how to approach this problem or whether there are any built in Unity functions that can help with this problem so any advice is appreciated.
Here is an image that'll help describe what I want to do:
I want to spawn Game Objects around a given point within the limits of a set radius. However their position in this radius should be randomly selected. This position should have the same Y axis as the origin point (which is on the ground). The next main problem is that each object should not clash and overlap another game object and should not enter their personal space (the orange circle).
My code so far isn't great:
public class Spawner : MonoBehaviour {
public int spawnRadius = 30; // not sure how large this is yet..
public int agentRadius = 5; // agent's personal space
public GameObject agent; // added in Unity GUI
Vector3 originPoint;
void CreateGroup() {
GameObject spawner = GetRandomSpawnPoint ();
originPoint = spawner.gameObject.transform.position;
for (int i = 0; i < groupSize; i++) {
CreateAgent ();
}
}
public void CreateAgent() {
float directionFacing = Random.Range (0f, 360f);
// need to pick a random position around originPoint but inside spawnRadius
// must not be too close to another agent inside spawnRadius
Instantiate (agent, originPoint, Quaternion.Euler (new Vector3 (0f, directionFacing, 0f)));
}
}
Thank you for any advice you can offer!
For personal space you can use colliders to avoid overlapping.
For spawning in circle you can use Random.insideUnitSphere. You can modify your method as,
public void CreateAgent() {
float directionFacing = Random.Range (0f, 360f);
// need to pick a random position around originPoint but inside spawnRadius
// must not be too close to another agent inside spawnRadius
Vector3 point = (Random.insideUnitSphere * spawnRadius) + originPoint;
Instantiate (agent, point, Quaternion.Euler (new Vector3 (0f, directionFacing, 0f)));
}
Hope this helps you.
For spawning the object within the circle, you could define the radius of your spawn circle and just add random numbers between -radius and radius to the position of the spawner like this:
float radius = 5f;
originPoint = spawner.gameObject.transform.position;
originPoint.x += Random.Range(-radius, radius);
originPoint.z += Random.Range(-radius, radius);
For detecting if the spawn point is to close to another game object, how about checking the distance between them like so:
if(Vector3.Distance(originPoint, otherGameObject.transform.position < personalSpaceRadius)
{
// pick new origin Point
}
I'm not that skilled in unity3d, so sry for maybe not the best answer^^
Also:
To check which gameobjects are in the spawn area in the first place, you could use the Physics.OverlapSphere Function defined here:
http://docs.unity3d.com/ScriptReference/Physics.OverlapSphere.html
So before I explain my problem. I will first tell what I am really doing.
I am working on a click to move/zoom camera script. There are 3 planes in front of my Main Camera. Now what I am doing is, creating a script which says " The camera will zoom on the plane which gets clicked. I made several attempts to come up with a working script but it didn't worked well. Every time I come across new bugs , errors and what not. :|
I got frustrated and deleted the buggy script. Now I want to start from scratch. I am doing it in C#
Since I am not a professional, Can anyone explain me in detail to get it done?
I am confused how to deal with the planes I placed. I want to know what is missing in my script.
Here is a screenshot of how I placed those planes.
Edit. - I managed to work on it. Now I need advice, how to target the planes I placed in front of the camera.
using UnityEngine;
using System.Collections;
public class CameraZoom : MonoBehaviour
{
public int zoomLevelSelected = 0;
public float[] ZoomLevels = new float[] { 60, 40, 20 };
void Update()
{
int zoomChange = 0;
if (Input.GetMouseButtonDown(0)) { zoomChange = +1; } // back
else if (Input.GetMouseButtonDown(1)) { zoomChange = -1; } // forward
if (zoomChange != 0)
{
zoomLevelSelected = Mathf.Clamp(zoomLevelSelected + zoomChange, 0, ZoomLevels.Length - 1);
camera.fieldOfView = ZoomLevels[zoomLevelSelected];
}
}
}
Heck with it, here is one way to create a click zoom. The gist is that you create a ray from your camera in to the scene through the mouse cursor. When that ray intersects an object create a second ray from the point of intersection back out along the intersecting face's normal.
void Update () {
if(Input.GetMouseButtonDown(0)){
// get ray from camera in to scene at the mouse position
Ray ray = Camera.mainCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
// hardcoded "zoom" distance.
float zoomDist = 15.0f;
// Raycast from camera to mouse cursor, if object hit, zoom.
if (Physics.Raycast(ray,out hit,Mathf.Infinity)){
// Create a second ray from the hit object back out, zoom the camera along this ray.
Ray r = new Ray(hit.point,hit.normal);
Camera.mainCamera.transform.position = r.GetPoint(zoomDist);
}
}
}
Things to keep in mind:
Physics.Raycast, as written, will return true for any GameObject with a collider. Use layers if you only want to zoom when selecting specific GameObjects.
The camera won't directly center on the GameObject you click. I use the exact point of intersection to create the position where the camera will zoom to.
zoomDist is the distance away from the object.
This code only works with perspective cameras, if you use orthographic you'll need to modify the size value of the camera to zoom.
The problem with your script is that your var int zoomChange is getting set to zero every frame so move that variable to class level.
using UnityEngine;
using System.Collections;
public class CameraZoom : MonoBehaviour
{
public int zoomLevelSelected = 0;
public float[] ZoomLevels = new float[] { 60, 40, 20 };
int zoomChange = 0; //<<<<<<<<<<<<<
void Update()
{
if (Input.GetMouseButtonDown(0)) { zoomChange = +1; } // back
else if (Input.GetMouseButtonDown(1)) { zoomChange = -1; } // forward
if (zoomChange != 0)
{
zoomLevelSelected = Mathf.Clamp(zoomLevelSelected + zoomChange, 0, ZoomLevels.Length - 1);
camera.fieldOfView = ZoomLevels[zoomLevelSelected];
}
}
}