I am building a mobile app in Unity that allows the user to scale objects in all three dimensions (currently using LeanTouch). This all works fine, but obviously when you scale the parent gameobject everything scales in that dimension. I would prefer to only allow certain meshes contained within the gameobject to actually scale, based on the dimension that is scaling.
For instance, if I have a table, and the user wants to increase the width of the object on the fly (i.e. scale in the X direction), I want the top of the unit to scale, but the thickness of the individual legs remains the same. Right now if you scale the width, the legs themselves increase in thickness.
This image illustrates the issue. As the table is scaled wider, so too are the legs. I would like for the leg B width to stay the same as Leg A, despite the table top "stretching" in the X direction.
These models are made with separate meshes for each component, so I'm not trying to do this to a single mesh.
Is there a way to indicate (or within scripts) that a mesh or gameobject can only scale in two dimensions (for instance the table legs can scale in Y and Z, but not X)? I believe this is called "Plane Locking" in Blender.
Right now I'm using Blender to import a DXF, which I'm converting to .obj or .fbx, however I'm a complete noob at it.
Thanks for any suggestions
I suggest you writing a simple script that anchors one object to another in a specific position, like this:
using UnityEngine;
using UnityEditor;
[ExecuteInEditMode, DisallowMultipleComponent]
public class Anchor : MonoBehaviour
{
public Transform target;
public Vector3 localPosition;
void Update()
{
if (target == null) return;
transform.position = target.position - transform.TransformVector(localPosition);
}
void OnDrawGizmos()
{
if (!enabled) return;
var pos = transform.TransformPoint(localPosition);
Gizmos.color = Color.red;
Gizmos.DrawSphere(pos, HandleUtility.GetHandleSize(pos) * .05f);
}
}
I also suggest you to do an object hierarchy similar to this:
It is advisable that the stretchable object and the anchored object to be siblings with a common parent.
With this, you can transform the table top as much as you want the the legs will remain attached to the position of the anchored objects (that should be childs of the strechable object). If you want a transformation to be applied to both the objects (scaling in the z direction, for example), just transform a common parent. The example is in 2D but it works for 3D too.
Two easy approaches to this problem are : unparent your gameobjects before you scale the parent (move them to a temporary parent, scale the parent and move tham back). Alternatively you can apply an inverse scale to all the children. The two are roughly equivalent. Have in mind in no case the side 'legs' of your combined object will stick to the sides, in 2D you could use RectTransform class for that, but in 3D I think you'll need some extra steps to achieve that.
Related
I am trying to create shader through amplify shader for a cube to cut through plane or any mesh when cross section. I know that I should be using size, rotation and position for that but what exactly to do with them that I don't know. Yup by that it means that I am new to amplify shader and also in shader programming so please don't provide shader code as I need to make it customizable for future so please help me out in amplify shader nodes.
Currently I have this effect but I want to make it more box bounding specific not plane normals based.
I want not this effect but the box effect shown below. This was achieved through ray marching concept but this I want to achieve with Amplify Shader. Kindly guide me through this.
This is what I have done so far with the amplify nodes
Result:
Here is the result of doing the shader using "Amplify Shader":
Solution:
First we'll call the green cube the "intersector" and the red cube the "intersectee".
So as you've done with the plane, the cutout works because the back face of the intersector is shown when inside the intersectee and the intersectee front face is show when it is inside the intersector.
Create a shader (which is used by both cubes) and put them into two seperate materials - apply individual materials to each cube. After this we can get into the actually shader node stuff.
First we need to make sure "Cull Mode" is off (Output Node > Cull Mode > off). This will ensure the back face is actually rendered (This can be optimized by decided depending on where the cube is in the intersector).
Next we need to get the surface point in object space:
Most of the variables will be defined in script. The rotation matrix is used to rotate a point. However, it is inversed as the rotation matrix rotates the cube into world space, therefore, inversing this would rotate the world space point into object space. We also get a "_Cubepos" which is the position of the cube to intersect with (E.g it would be the intersector if shader is on the intersectee). This is subracted by the world pos as the rotation matrix rotates around the origin. After this it is added back to be in the correct position.
This leads to the next section where the extents are added and subtracted to the "_Cubepos" and "_CubeExtent" to find the minimum and maximum extents.
Unfortunately, Amplify shader has no good way to check if a vector lies within two vectors. So we have to break it into components. (I encourage you to learn how to write shaders). Each compare with range returns 1 if the point in object space is within the extents for each axis. If one returns 0 we use the last multiply node to make sure the final output will be 0.
Finally, we get to the last part of the shader. The "IsIntersector" is set in script to be 1 or 0 depending on whether the cube we are refering to is used to intersect or is an intersectee. Depending on the scenario, here we set the opacity mask to 1 or 0.
After this we have to define the script to attach to each object. Add a new script and type the following in:
[ExecuteInEditMode]
public class SetVar : MonoBehaviour
{
//Transform of opposite cube
public Transform intersectingCube;
//Is this an intersector or intersectee
public bool isIntersector;
//Material of object
public Material mat;
// Start is called before the first frame update
void Start()
{
//Get material
mat = GetComponent<Renderer>().material;
}
// Update is called once per frame
void OnRenderObject()
{
//Calculate rotation matrix
Matrix4x4 m = Matrix4x4.TRS(-intersectingCube.position, intersectingCube.rotation, Vector3.one);
//Set shader variables
mat.SetMatrix("RotationMatrix", m);
mat.SetVector("_Cubepos", intersectingCube.position);
mat.SetVector("_CubeExtent", intersectingCube.localScale / 2.0f);
mat.SetFloat("_IsIntersector", (isIntersector) ? 0 : 1);
}
}
Then we can set the correct inspector values depending if the cube is an intersector or intersectee. Here is an example for the intersector cube:
Make sure to have the IsIntersector ticked depending if the cube is an intersector or not.
Here is a link to the shader: http://paste.amplify.pt/view/raw/4b248bc3. Also to do this for any mesh is a very complicated operation - too complicated for nodes. Learn about shader code and use a raycasting algorithm to determine if the point is inside the cube.
Also, alternatively for any convex shape. You could calculate each plane and then using your method already used, can check if the world position point works for every plane. For a cube there would be 6 planes, however, its a bit slower than the above method (as it is optimized for a cube).
I have a pane with one cube on it with a spawner which will spawn cubes out when the game is played. The pane can be rotated and the cubes should stick to it and move forward.
The part i am stuck on is making the cubes that come from the spawner to stick.
I managed to make the original cube stick using
Object1.transform.parent = Object2.transform;
I also managed to make the spawner stick to the pane using the same method.
However when i use this the first cube will stick fine and the cubes from the spawner are not showing up. From what i have researched it could be a scaling issue. Also the spawned cubes do appear under the cube as "Cube(clone)" but their scale is diffrent to the original cube. I have also noticed the scale of the original Cube changes when i press play. It goes from - (10,10,10) to float numbers.
A solution i tried was to set the scale but when i done this the objects did not stick on to the pane at all.
However they did spawn normally.Using this method.
void Update () {
Vector3 scale = new Vector3(10, 10, 10);
Object1.transform.localScale = scale;
Object1.transform.parent = Object2.transform;
}
If anyone has any ideas it will be much appreciated.
In situations like this, it's quite often best to parent an object first, then deal with the local transformations. Local transformations such as transform.localPosition, transform.localRotation and transform.localScale deal with the object's local values in relation to the parent.
There's a paragraph at the end of this page that describes it well:
Note that the parent transform's world rotation and scale are applied
to the local position when calculating the world position. This means
that while 1 unit in Transform.position is always 1 unit, 1 unit in
Transform.localPosition will get scaled by the scale of all ancestors.
I've already tried depthmask shaders and examined some other ideas, but it seems like it doesn't suit me at all.
I'm making an AR game and I have a scene with a house and trees. All these objects are animated and do something like falling from the sky, but not all at once, but in sequence. For example, the house first, then trees, then fence etc.
(Plz, look at my picture for details) http://f2.s.qip.ru/bVqSAgcy.png
If user moves camera too far, he will see all these objects stucking in the air and waiting for their order to start falling, and it is not good. I want to hide this area from all sides (because in AR camera can move around freely) and make all parts visible only when each will start moving (falling down).
(One more screen) http://f3.s.qip.ru/bVqSAgcz.png
I thought about animation events, but there are too many objects (bricks, for example) and I can't handle all of them manually.
I look forward to your great advice ;)
P.S. Sorry for my bad english.
You can disable their(the objects that are gonna fall) mesh renderers and re active them when they are ready to fall.
See here for more details about mesh renderer.
Deactivate your Object. You might use the camera viewport coordinates to get a y position outside the viewport. They start on the bottom left of the screen (0,0) and go to the top right of the screen (1,1). Convert them to worldspace coordinates. Camera.ViewportToWorldPoint
Vector3 outsideCamera = Camera.main.ViewportToWorldPoint(new Vector3(0.5f, 1.2f, 10.0f));
Now you can use the intended x and z positions of your object. Activate it when you want to drop it.
myObject.transform.position = new Vector3(myObject.transform.position.x, outsideCamera.y, myObject.transform.position.z);
Another thing you could additionally do is scaling the object from very small to its intended size when it is falling. This would prevent the object being visible before falling when the users point the camera upwards.
1- Maybe you can use the Camera far clipping plane property.
Or you can even use 2 Cameras if you need to display let's say the landscape on one (which will not render the house + trees + ...) with a "big" far clipping plane and use a second one with Depth only clear flags rendering only the items (this one can have a smaller far clipping plane from what I understand).
2- Other suggestion I'd give you is adding the scale to your animation:
set the scale to 0 on the beginning of animation
wait for the item to be needed to fall down
set the scale to 1 (with a transition if needed)
make the item fall down
EDIT: the workaround you found is quite just fine too! But tracking only world position should be enough I think (saving a tiny amount of memory).
Hope this helps,
Finally, the solution I chose. I've added this script to each object in composition. It stores object's position (in my case both world and local) at Start() and listening if it changes in Update(). So, if true, stop monitoring and set MeshRenderer in on state.
[RequireComponent(typeof(MeshRenderer))]
public class RenderScript : MonoBehaviour
{
private MeshRenderer mr;
private bool monitoring = true;
private Vector3 posLocal;
private Vector3 posWorld;
// Use this for initialization
void Start()
{
mr = GetComponent<MeshRenderer>();
mr.enabled = false;
posLocal = transform.localPosition;
posWorld = transform.position;
}
// Update is called once per frame
void Update()
{
if (monitoring)
{
if (transform.localPosition != posLocal || transform.position != posWorld)
{
monitoring = false;
mr.enabled = true;
}
}
}
}
Even my funny cheap сhinese smartphone is alive after this, so, I guess, it's OK.
I'm currently working on an object that involves bubble-like movement. This object has a rigidbody and a sphere collider.
I use the AddForce method every 1-3 seconds to make it move continuously and slowly.
Now I'd like to know how to make the rigidbody move in the opposite direction (a.k.a bounce off) when they reach the screen edge already. I have already computed the screen edges using ViewportToWorldPoint method.
One ineffective solution that I thought of is to put empty game objects with collider at the edges but that won't work in my case since I'm building for mobile devices which have different screen resolutions/sizes.
Anyone know a good solution for this?
I'm not sure i got the idea. But i think i had the same problem when i was writing an old mobile game.
I had the same idea you did, use empty game objects with box collider on the edges, but then i thought, this isn't responsive, so i wrote this code:
public class Walls_c : MonoBehaviour {
public Transform righttop;
public Transform rightbottom;
public Transform lefttop;
public Transform leftbottom;
// Use this for initialization
void Start () {
righttop.transform.position = Camera.main.ViewportToWorldPoint(new Vector3(1,1,0));
rightbottom.transform.position = Camera.main.ViewportToWorldPoint(new Vector3(1,0,0));
lefttop.transform.position = Camera.main.ViewportToWorldPoint(new Vector3(0,1,0));
leftbottom.transform.position = Camera.main.ViewportToWorldPoint(new Vector3(0,0,0));
}
}
With this, i always get the corners of the screen. It's no fancy... but it works.
Let me now if it works.
In order to get a reflection effect, you need Material. Attach a bouncy material to the edges(gameObject) and let the physics calculate what should be the reaction of collision.
Create a Physics 2D material and set the bounceness to some appropriate value say 0.2.
Regarding your issue:
One ineffective solution that I thought of is to put empty game
objects with collider at the edges but that won't work in my case
since I'm building for mobile devices which have different screen
resolutions/sizes.
If you are working with UI component then dealing with boundaries should not be a problem (since it anchors with different resolution) but if it is not the case you can create a script CameraResizer and an enum Anchor (Left, Right, Top, Bottom) and using the same way ViewportToWorldPoint you can align your empty gameObject (boundary) to any screen size by attaching it the gameObject.
Hope it helps!
I'm developing a game that you drag and drop objects into boxes and I have no idea what's the best and most efficient way to detect whether my objects are in a box.
I'm well aware of colliders and I'm using BoxColliders and triggers to find out whether my object is touching a box but I want to detect the moment when my object (which we can assume to be sphere for sake of simplicity but later will be a mesh) is completely inside my box trigger/collider.
I read about "Contains" method of colliders but IIRC they just check if one single point inside that collider but I'm interested to know if the whole object is inside the collider.
Thanks in advance folks.
Short answer: If you want 100% accuracy, your algorithm will never be better than O(|V|) (worst case) where V = {all vertices in mesh}, meaning you'd run .Collides() over every vertex and break if you find one that is outside your container.
Longer answer: Several methods exist to subdivide mesh surfaces, examples include: KD-Trees, OcTrees. These are beyond the scope of an SO answer to provide a full implementation, I suggest you see the wiki pages for details.
You could use these methods to divide your mesh up in to smaller sets of vertices. To speed up your algorithm you would start at the root of your subdivision tree and test if that node is contained in your container box. Keep working through the tree until you find a node that is not contained by your box. This will allow your "contains" test to fail faster but ultimately you'll wind up testing every vertex if your box contains your mesh.
Caveat: This solution does not work if your mesh is animated. In that case your best bet is to use the bounds around things like arms, feet, legs, etc and use that information to cull your Contains() tests. Again, you'll end up having to test every vertex if the mesh is fully inside your box.
All boxes have a boxCollider. If an object touches the second box, the object must be inside the first box.
This is not a good solution but, maybe it will be usefull.
Use Renderer.bounds property to get bounding box of your object.
Depending on what object you have and how accurate you want to check it to be inside a collider you might than use one of the simple ways to determine that.
for a more refined and cleaner approach. this would be perfect for what you are doing .
you can check the distance between the object you are dragging and the box .
the box has a x,y,z value which represents its position in space.
so when you drag your gameobject it may be just 0.2 on the x,y, or z away from the centere of your box. so just use this method to calculate the distance between your dragged object and the box.
var other :Transform;
function Update()
{
var dist = Vector3.Distance(other.position, transform.position);
// while dragging
if(dist <10)// 10 being on all 3 axiz .
{
//dragged object.position = box position
}
if (dist == 0)
{
print("i am in the centre of the box");
}
}
thus your gameobject will be in the box .
The box within a box solution above is a good option, but if that won't work (due to variably sized/shaped objects) you might be able to accomplish something with Physics.Raycast or Collider.Raycast. I had a similar problem where I needed to test if arbitrary points were contained inside colliders (many of which were unusual blobby concave objects).
The basic idea is a "bed of nails" approach where I cast rays toward the point from multiple directions. If I hit the outside collider on all of the rays, then I can be pretty confident that the point is contained inside the collider (but still not completely certain). Here's a pic:
In this picture, we're trying to see if the blue point is inside the yellow collider. The GREEN arrows represent successful raycasts (the yellow collider is hit), and the PINK one is unsuccessful (the yellow collider is NOT hit).
Here is a code snippet illustrating this:
public static class CollisionUtils {
private static readonly Vector3[] raycastDirections;
// These are the directions that we shoot rays from to check the collider.
static UltrasoundCollisionUtils() {
raycastDirections = new Vector3[5];
raycastDirections[0] = new Vector3(0,1,0);
raycastDirections[1] = new Vector3(0,-1,-0);
raycastDirections[2] = new Vector3(0,0,1);
raycastDirections[3] = new Vector3(-1.41f, 0, -0.5f);
raycastDirections[4] = new Vector3(1.41f, 0, -0.5f);
}
public static bool IsContained (Vector3 targetPoint, Collider collider) {
// A quick check - if the bounds doesn't contain targetPoint, then it definitely can't be contained in the collider
if (!collider.bounds.Contains(targetPoint)) {
return false;
}
// The "100f * direction" is a magic number so that we
// start far enough from the point.
foreach (Vector3 direction in raycastDirections) {
Ray ray = new Ray(targetPoint - 100f * direction, direction);
RaycastHit dummyHit = new RaycastHit();
// dummyHit because collider.Raycast requires a RaycastHit
if (!collider.Raycast(ray, out dummyHit, 100f)) {
return false;
}
}
return true;
}
}
One way you could adapt this algorithm is rather than use Collider.Raycast, do a Physics.Raycast. If the rays hit anything other than your collider, then you know that your target object isn't entirely in the collider.
Add a BoxCollider which surrounds the whole of the object you are testing for and check its bounds min and max are contained within the BoxCollider its entering ... this may not suit for complex mesh objects but you may be able to get away with it and its cheap
Simply,
You can place a box Collider at the bottom of your container.. If your sphere or whatever object touches that then it is completely inside the container..
There is one method though....not that accurate but will work :
Make four empties with collider(Circle / Box) like four wheels of a car and make the object parent to these empty objects with collider.
Then assign every collider a bool whether it is touched or not....
Count the number of colliders touched along with the actual mesh collider...Roughly estimates the orientation of the overlap. If it is 2 that means touched or overlaped from sideways.
Ex: If two collider + the mesh collider is colliding which means it is overlapped sideways...
I solved it using bounds.contains(vector 3 point).
I placed a single point on the object to be detected, and once it was inside the checking limits, I was able to acknowledge its presence.
public class PointInsideBound : MonoBehaviour
{
public Collider BoundCollider;
private Transform pointToCheck;
private void OnTriggerEnter(Collider other)
{
if (pointToCheck == null)
{
//There is a point on top of the collided object. Getting the reference here.
pointToCheck = other.transform.GetChild(0);
}
}
private void OnTriggerStay(Collider other)
{
if (pointToCheck == null)
{
return;
}
if (BoundCollider.bounds.Contains(pointToCheck.position))
{
Debug.Log(" Point Inside the boxcollider");
BoundCollider.enabled = false;
}
}
}
You should use a collider. This is description. http://docs.unity3d.com/Documentation/ScriptReference/Collider.html