I have a 3d model prefab in unity that I'm trying to add physics to. The issue is that the prefab was imported from auto desk and when that happened the pivot/center of gravity for all the child mesh objects was not where you would necessarily expect them. I've tried attaching a rigid body to the various child objects and setting the center of mass with a script but it doesn't seem to be working as I would expect. My method for setting the center of mass involved creating two child game objects at either end of the highlighted object and using the midpoint formula to a 'good enough' center of mass. Is there any way around this beyond rebuilding/re-importing the object?
Showing the off center pivot
Showing the weird center of mass
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CenterOfGravity : MonoBehaviour
{
// Start is called before the first frame update
public Transform[] centerPoints;
public Rigidbody rb;
void Start()
{
centerPoints = gameObject.GetComponentsInChildren<Transform>();
Transform center1 = Array.Find(centerPoints, elem => elem.name.Equals("Center1"));
Transform center2 = Array.Find(centerPoints, elem => elem.name.Equals("Center2"));
Vector3 center = (center1.localPosition + center2.localPosition)/2.0f;
rb = GetComponent<Rigidbody>();
rb.ResetCenterOfMass();
rb.centerOfMass = center;
}
// Update is called once per frame
void Update(){}
}
Every time I have to change the center off mass of an object, I create an empty child Game Object in the place where I want to be the center of mass.
After that, I set the center off mass of the object, to the local position of the new child object, something like that:
public GameObject childObj, myObj;
void Start()
{
myObj.GetComponent<Rigidbody>().centerOfMass = childObj.transform.localPosition;
}
Edit:
If you want to change the pivot of a GameObject, you need to do those things:
1-Create an empty game object and name it as "Pivot".
2-Place the pivot where you want the center of mass to be.
3-Asign your initial game object that you want to change the pivot as child of your new object called "Pivot".
Now the pivot of the initial game object should be the empty game object called pivot.
Some disclaimers first, AFAIK there's no way to do that in unity BUT! there is a workaround I came across when I had a similar issue. The thing is that I'm not quite sure what your work pipeline is, and this fix might be even worst than just modifying the meshes in auto desk and reimporting it.
The idea is to move your object temporarily to another game object, then modifying the broken pivot and bringing it back to the original object. The idea comes from this video. It may work for you too!
Related
I would like to use a characters tongue so instead of shooting a bullet, it goes toward the enemy, licks it, and comes back. I got this wording from this question: Unity shooting with Tongue 2d game (hasn't been answered and is 4+ years old). The only difference is my character moves.
I have this code from looking at a shooting tutorial so when you click the tongue prefab generates and is at the correct angle. I need it to grow on click and shrink back.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LineController : MonoBehaviour
{
public GameObject player;
private Vector3 target;
public GameObject crosshairs;
public GameObject tonguePrefab;
// Start is called before the first frame update
void Start()
{
Cursor.visible = false;
}
// Update is called once per frame
void Update()
{
target = transform.GetComponent<Camera>().ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, transform.position.z));
crosshairs.transform.position = new Vector2(target.x, target.y);
Vector3 difference = target - player.transform.position;
float rotationZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
if (Input.GetMouseButtonDown(0))
{
shootTongue(rotationZ);
}
}
IEnumerator shootTongue(float rotationZ)
{
GameObject t = Instantiate(tonguePrefab) as GameObject;
t.transform.position = new Vector2(target.x, target.y);
t.transform.rotation = Quaternion.Euler(0.0f, 0.0f, rotationZ);
yield return new WaitForSeconds(2000);
t.SetActive(false);
}
}
I was also trying to make it disappear after whatever time and with that it doesn't work at all?
If the prefab is just a line with a simple texture I suggest using a line renderer:
Create an empty transform that starts on the mouth and then it move untill it reaches the crosshair. The movement is done using Vector2.MoveTowards.
Your tongue will no be a line renderer component. This line renderer will have 2 points, the start which is static, and the end which you have to update on the Update() for example, to correspond to the positions of the empty transform in item 1.
If you really wish to expand a tongue gameobject instead, then you are going to need a bit of math, this answer has what you need but in 3D:
https://answers.unity.com/questions/473076/scaling-in-the-forward-direction.html
1) I suggest creating sprite/sprite sheet animations with Mecanim or relatively new Skeletal Animation with Anima2D. With these systems you can create nice animations, transitions, even animate the sphere collider to trigger collisions and actions, change active state of your objects, etc. and control the animation with very little code. This way you can get best effects in my opinion.
As tongue is not a bullet... :) You only need one. I don't think you want to Instantiate/Create prefabs every time you press the lick button. Instead you should just turn your tongue object on/off (gameObject.SetActive). You can also change your object active state within the animation. So if you don't know how to code it you can do most of it in the Animation window and use very simple code to play the animation when you press the lick button. Whenever a sphere collider touches something you can tell the Animator Controller to play a 'roll back' animation and it will transition nicely to the start position.
There are many tutorials about Mecanim, Animations, 2D Animations, Anima2D, Animator Controller out there.
2) If you need very good control over the tongue you could create a custom mesh and control it via script but this is far more difficult.
3) The reason why your object is not turning off is probably because you wrote WaitForSeconds(2000) so it will turn off after 2000 seconds - more than half an hour. You should also call it with StartCoroutine(shootTongue()) as it is a Coroutine. Again if you want to turn off the object don't create new ones every time. If you want to keep creating new objects you should Destroy the objects instead. Otherwise you will end up with a lot of deactivated tongues in your scene and I don't think you needs that many tongues.
Currently I am working on a game for people with accessibility restrictions. I am having the issue of locking the player model in a sitting position. If the user does not center themselves in the room the player model will be pulled to a certain direction. I would like to lock the player model in a seat and only allow for arm movements and head rotations, no leaning or moving in the game using the HMD.
Since I am using the Final VR IK asset I have tried using their demo for sitting position in VR and cannot get the player to stay seated naturally. I am not sure how to program this or set restrictions to be able to do this.
Edit: To simplify my question. How do you lock the oculus rift HMD to only allow for rotation and not position tracking.
I figured it out how to lock the HMD into to only allow for rotation and not position tracking. To add a sitting position just use an body animation that's sitting. There are 2 things I did. First I added a line of code to the OVRCameraRig script:
trackingSpace.localPosition = -1 * centerEyeAnchor.localPosition;
This was done right before the RaiseUpdatedAnchorsEvent(); call around line 260 in the UpdateAnchors() method. What this does is it locks the head position and only allows for head rotation.
The second thing I did was write a head relocation script based on #derHugo answer to one of my other questions. What this does is when the space bar is pressed it will move the entire OVRCameraRig position. There must be a parent on OVRCameraRig for this to work In the screen shot below you can see the CameraParent object as the parent. I used a sphere as the relocation object that was placed in the middle of the head of my player model. The reason I had to add this was sometimes when you hit play the player would start at a weird position depending on where the headset was at the beginning. In the screen shot you can see use position tracking as not being checked in the inspector, that is an error. Please keep it selected to prevent screen tearing in the headset
Here is the code for the changing position of player when in game:
public class VRPositionChange : MonoBehaviour
{
public Transform resetPos;
private Transform parent;
private void Awake()
{
// create a new object and make it parent of this object
parent = gameObject.GetComponentInParent<Transform>();
}
// Update is called once per frame
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
// reset parent objects position
parent.position = resetPos.position;
}
}
}
Title pretty much sums it up. I am trying to adjust the scale of cloned prefabs during runtime, through a UI slider. However on first run the slider doesn't seem to have any effect on them, and on second run the Objects just keep one value from the slider and appear already taller or shorter. I need the slider to adjust the height of the objects dynamically, just as it does on a normal, not cloned, object. Any help will be greatly appreciated. Thanks!
The script works on normal objects, not clones.
Here is my slider method :
public void slideScale(float textNumber)
{
Vector3 scale = Wall.transform.localScale;
scale.y = textNumber;
Wall.transform.localScale = scale;
}
I expected to be able to change the height, while moving the slider.
A value is being stored and just takes place on second run while the slider still seems not to be affecting anything.
Looks like you're modifying the reference to the Prefab instead of the instance of a prefab (clone, in your words).
Here's how you store the reference to an instance of a prefab:
// Spawn an object based on a prefab "Wall"
// and store the spawned object in variable "instance"
GameObject instance = Instantiate(Wall);
// Modify the instance properties
instance.transform.localScale = scale;
I have 2 gameobjects within my character say right_obj and left_obj, I want them to ignore parent rotation, like if player change its direction (left, right), these 2 gameobjects stay on their position and rotation, so far I use 2 approaches but fail. here is my code, I use this script on my two gameobjects, but they ignour this lines
// first one approch
Quaternion rotation;
void Awake()
{
rotation = transform.rotation;
}
void LateUpdate ()
{
transform.rotation = rotation;
}
// 2nd approch
void LateUpdate ()
{
transform.rotation = Quaternion.identity;;
}
Do you really need this type of relationship?
If you don't really need the father/child relationship you can spawn an empty GameObject at 0,0,0 and spawn the actual parent and child as childs of the GameObject in the middle of the scene, this way they would still be grouped in some sort of relationship but instead of a father/child you'd get a brother/brother relationship, making the first independent from the second.
If you need to make two objects dependent from each other but not in every single way as it's supposed to be in father/child try using Parent Constraints
This way you'll obtain this type of hierarchy:
- Father GO (at 0,0,0)
-- Child1 (your actual father)
-- Child2 (your actual child)
Child1 and Child2 are syncing position by their ParentConstraint but not their rotation.
Else there is no other way than applying the opposite rotation to the child.
If you really don't want destory parent-child hierarchy, you could rotate child every frame to counteract parent rotation.
Let's say we have the follow scene,
Add the follow RestoreOriginRotation.cs to child object, we can get a stable child rotation.
What we can get.
****If you don't concern the underlying math, you chould stop here. ***
LeviathanCode's "Parent Constraints" solution only works with Unity 2018.1+ because it's a new addition.
My suggestion isn't a "pretty" solution but it'll also work with older versions if you want to prevent imitating rotations while still copying movement:
Get rid of the parent/child relationship, add a new parent GameObject (as LeviathanCode suggested) and a script to it that simply copies the player's movement each frame:
public GameObject player; //Drag the "player" GO here in the Inspector
public void LateUpdate() {
transform.position = player.transform.position;
}
This way your "right_obj" and "left_obj" won't rotate like the player but still move "with" it.
You could also use a Rotation Constraint component. This is similar to the Parent Constraint mentioned above, but it's simpler to understand in my opinion - it just uses the Rotation and only the rotation of whichever GameObjects you slot in.
You just have to make an empty game object and set the rotation to 0,0,0 , then set this empty game object as the source of the rotation constraint. I have this exact setup in my game, and it overrides the rotation of the actual parent, which is what I want.
Perhaps not the absolute cleanest solution, but it's something to consider.
Here is the docs link - https://docs.unity3d.com/Manual/class-RotationConstraint.html
As #aaronrums stated, a Rotation Constraint is an easy way to achieve this.
Here, I have 3 game objects. The main parent object container ("Player Seat") which holds all the children will always be set to a rotation of 0 on the Z axis. The GO I'm trying to keep positioned vertically (so the text and avatar doesn't rotate) is "DrewPlayer." I have it offset on the X-axis by 100 in its parent "Player Position." I added a rotation constraint on the item I didn't want to rotate. (a.k.a "DrewPlayer") Once the constraint was on it, I dragged over the "Player Seat" to the source list and when I rotated "Player Seat," I was able to achieve the desired effect in the gif below.
Thanks #aaronrums! Saved me some effort here. :)
I am a total beginner at Unity3d. I have some background in android programming, but no C# experience whatsoever. The first thing I am trying to do is to create a clone of flappy bird game, called flappy plane, according to this tutorial
http://anwell.me/articles/unity3d-flappy-bird/
The problem is, when I tried to write a script that allows player to move (player.cs) with the code
using UnityEngine;
using System.Collections;
public class player: MonoBehaviour {
public Vector2 jumpForce = new Vector2(0,300);
public Vector2 jumpForce2 = new Vector2(0,-300);
// Use this for initialization
// Update is called once per frame
void Update () {
if (Input.GetKeyUp("space")){
Rigidbody2D.velocity = Vector2.zero;
Rigidbody2D.AddForce(jumpForce);
}
}
}
I get an error "An Object reference is required to access non-static member 'UnityEngine.Rigidbody2D.velocity'". I have googled that and it is suggested to access Rigidbody2d with GetComponent().velocity,
so I changed
Rigidbody2D.velocity = Vector2.zero;
Rigidbody2D.AddForce(jumpForce);
with
GetComponent<Rigidbody2D>().velocity = Vector2.zero;
GetComponent<Rigidbody2D>().AddForce(jumpForce);
The error is gone and I am able to add the script to the object, still I don`t get the desired action - after I hit play the object turns invisible and just falls down, does not react to spacebar button. What am I doing wrong?
Thanks for the answer.
It's possible that you're not adding enough force to have the object move upwards.
There's technically nothing wrong with your code. (Although you do have somethings mixed up in your question). The problem is in the fact that you're not adding ANY force upwards every single frame.
Essentially, at the moment, your player object is in free-fall the instant you hit the play button, and you're adding a minuscule force to the player only on the frames that the space bar is pressed.
To solve this, here's what you should be doing
Add an upward force to counter-act the force of gravity every frame. You can do this in two ways.
a. Set the rigidbody's velocity.y to 0 BEFORE detecting the space bar (this is really a hacky way, but it'll suffice and doesn't need any more code)
b. Add an upward force to the player which will nullify the effect of gravity. Just use F = mg to get the value of force you'd need to add.
You could, alternatively set the isKinematic property to true by default on the Player's rigidbody, set it to false on pressing the space bar, and back to true after a few frames (5 - 6 frames)
make sure your player object and the ground both have BoxCollider2D colliders to keep above ground.
you could keep a reference stored for the rigidBody like Rigidbody2D myRigidbody;
then in start put myRigidbody = GetComponent<Rigidbody2D>(); then you would use like myRigidbody.AddForce(jumpForce); your jumpForce2 though is shooting your player downward you should not need it in a jump as the physics and gravity will apply with the rigidbody.
incase your input is not set up in the project settings try to fire the jump with
Input.GetKeyDown(KeyCode.Space);