Blend Trees are bad in 2d - unity3d

My project is Isometric 2d with 8 directional movement animations.
As you all know, blend trees are great for blending animation in 3d. It also works in 2d (visual aspect) but "under the hood" I have a lot of issues.
I like to bind Animation Events to different functions. But they trigger more than once, because 2 or 3 animations are being played at the same time in my blend tree (although only the dominant one displayed) if I move in a not straight direction. In other words, if my x and y axis are not equal to 1 or -1.
I found a workaround that checks which animations weight is >= 0.5f, but that doesn't work in all circumstances and i don't know why and I would actually like to know whether there are better ways to workaround this issue.
This issue is present only on the Enemy character, because the x and y axies are not always equal to a whole number, as it modifies x and y based on a direction to the player. The player, on the other hand, works fine with the keyboard, but does not with a joystick due to the same reason.
Could I somehow "clamp" the value to be a whole number of the vector.x and vector.y to be strictly 1 or -1 in this situation? That would help a lot with Animation Events and with that blend tree situation.
For now its just simply doing this:
direction = (target.position - transform.position).normalized;
anim.SetFloat("Horizontal", direction.x);
anim.SetFloat("Vertical", direction.y);
Then I tried this but it doesn't work:
direction = (target.position - transform.position).normalized;
anim.SetFloat("Horizontal", Mathf.Clamp(direction.x, -1, 1));
anim.SetFloat("Vertical", Mathf.Clamp(direction.y, -1, 1));

Related

Unity2D: How to measure the bounds of a platform for a game object standing on it?

I'm making a game where I have a skeleton as a game object:
Since this skeleton is an enemy, I want to make it walk on its own, and turn around as it reaches the edge, and so on. I managed to make it walk like so:
void Update() {
if (isWalking) {
body.velocity = new Vector2((isFacingLeft ? -1 : 1) * speed, body.velocity.y);
.
.
.
where isWalking is set to true from the beginning for testing purposes, speed is 5, and isFacingLeft is a boolean I want to flip whenever the skeleton reaches an edge, initially it is false, so the skeleton travels to the right without stopping.
So the idea I had is make use of OnCollisionEnter2D which should work since the skeleton is on top of the platform game object, and use the collision object to get different bounds:
From the picture, if the game object platform can be called L and its width y, then I'm thinking of getting the necessary bounds of it by finding Lx and Lx + y. Then, if the skeleton's width can be called x, I would want to measure the transform.position.x of the skeleton and check: if it faces right, and transform.position.x > Lx + y - x (or maybe x / 2 to approach it a bit more to the edge), then flip the isFacingLeft boolean. If it's facing left and transform.position.x < Lx + x (or x / 2), flip the boolean.
However, if I try logging the left edge (via collider.transform.position.x) and the skeleton position (via transform.position.x), I get starting values of 6.4596, 5.943441, meaning that the skeleton somehow starts before the platform. Is it possible that transform is not the right property to check for bounds in this case? How then can I ensure I'm using the desired values to check edges? If anyone knows, please comment even if you have questions about my approach, as going over the thought process really helps to get an answer.
So as suggested by pocokknight on reddit, I tried implementing ray casting, so that there is a ray projecting from a bottom corner of the skeleton sprite depending on what position it should face, achievable via SpriteRenderer.bounds.size. If the ray projected doesn't hit anything the position the skeleton would face flips. This enables the skeleton to turn around back and forth forever :)
RaycastHit2D hit = Physics2D.Raycast(new Vector2(transform.position.x + ((enemyWidth / 2) * direction), transform.position.y - enemyHeight / 2), transform.TransformDirection(new Vector2(1 * (direction), -1)), 1f);
Of course, this is a very simple execution of the function, and I could probably think of scenarios where the raycast check could fail (such as if the player object is right on its path when it should be checking for empty ground), but thus far it's a progress I'm happy with

Questions related to transform.rotate() in Unity Scripting

I have four rectangular platforms put around a square platform. All other three rectangular platforms were rotated from the '0' platform. On the end of each rectangular platforms, I set up some spawn points to generate cubes with arrows sign on its face(the image of this cube is shown in the second link). I hope to rotate the cube to change the orientation of the arrow on it. But my original codes that rotating the cube do not work for platform 1 & 3, but work for platform 0&2.
Although I have found how to change it to make it works, I still cannot understand why my original codes failed. The version I used is 2019.4.18f1.
The top view of these platforms are shown below.
enter image description here
Unworkable way
dirCubeTypes is an array that stores all my different kinds of cube objects.
dirCubePoints is an array that stores all my spawn points.
The first statement works well. The problem is in the second statement.
GameObject cube = Instantiate(dirCubeTypes[Random.Range(0, 6)], dirCubePoints[Random.Range(0, 4)]);
cube.transform.Rotate(transform.forward, 90 * Random.Range(0, 4));
The figure below shows the cube generated on the platfrom 3. I want it to rotate around z-axis, but the codes above make it rotate around its x-axis instead of z-aixs. I know the third parameter is relativeTo, and the default value is Space.self. Why doesn't the codes above make it rotate along the z-axis in its own local coordinate system?
enter image description here
Workable ways
I have tried out three workable ways, but for two of them, I still don't know why it works.
This one works well. If I use Vector3.forward in Space.self, I think it should rotate around the x-axis of the cube itself, but it rotate around its z-axis.
GameObject cube = Instantiate(dirCubeTypes[Random.Range(0, 6)], dirCubePoints[Random.Range(0, 4)]);
cube.transform.Rotate(Vector3.forward, 90 * Random.Range(0, 4));
This one also works well. But I am confused either.
GameObject cube = Instantiate(dirCubeTypes[Random.Range(0, 6)], dirCubePoints[Random.Range(0, 4)]);
cube.transform.Rotate(transform.forward, 90 * Random.Range(0, 4), Space.World
);
The last one is the only one that works as I think.
GameObject cube = Instantiate(dirCubeTypes[Random.Range(0, 6)], dirCubePoints[Random.Range(0, 4)]);
cube.transform.RotateAround(transform.position, transform.forward, 90*Random.Range(0,4));
First of all
Vector3.forward just equals new Vector3(0,0,1) so it is just a generic Z axis vector (either in the world or local depending on how it is used)
transform.forward rather is the local Z axis of your object in world space
And Rotate can either be used in world space or in local space (this is the default since the space parameter is optional and defaults to Space.Self).
When you passed in
cube.transform.Rotate(transform.forward * 90 * Random.Range(0,4));
it of course behaves strange since you treat the world space vector transform.forward like a local space one. So as soon as your object is somehow rotated in the world it breaks.
So either you want the world space then do
// Using the world space transform.forward and passing in Space.World
cube.transform.Rotate(transform.forward * 90 * Random.Range(0,4), Space.World);
or the same in local space
// Using the local space Vector3.right
// in this case - since it will use Space.Self if nothing is passed in -
// the Vector3.forward is treated as relative to the local space
cube.transform.Rotate(Vector3.forward * 90 * Random.Range(0,4));
I just saw your question update. I believe your misunderstanding stems from not knowing what Vector3.forward and Transform.forward are and their core difference. Vector3.forward is the unit vector defined by (0, 0, 1). The Transform.forward is a relative direction based on the orientation of your object.
Your first example works in some cases as it just so happens that the Transform.forward and Vector3.forward happen to be the same direction.
Your second example works as you are using Vector3.forward not the relative Transform.forward
Your third example works as you provide the Space.World, so instead of using local orientations, it is rotating the object in world space relative to the scene.
Your fourth example works as you are using RotateAround. RotateAround is different than Rotate in that it will rotate the transform about your inputted axis (transform.forward), which pass through the point (transform.position) in world coordinates by the angle (90*Random.Range(0,4)) degrees. Effectively moving the local rotation around an axis back to world space.
I hope this clears up why the first example does not work while the last three do.

[Solved - 3D movement on X and Y with mesh colliders

I am working on an Asteroids clone in 3D with top-down camera. This setup is static and will not change (so no, I do not want to transform the project to 2D game instead).
This means that I need to limit all movement to X and Y axis. I created movement for both asteroids and player and everything worked fine. All movements are done using AddForce on respective RigidBody components.
The problem is then I start dealing with collisions. I use Mesh Collider components to get a nice and precise "touch reaction". The problem is that when collision like this occurs, the new movement vector has Z value different from 0. This is a problem as the object will start moving on Z axis.
What have I tried:
Freezing constraints on RigidBody
Manully reseting Z in Update function
The first solution (freezing constraints) did not work and neither did the second one (moreover, the second one seems quite messy)
So the question is
What would be the optimal way to force physics-based movement only to X and Y axis while using precise collision with Mesh Colliders?
are you sure you used the position restriction correctly? You can check to set the restrictions with a vector as in the documentation. https://docs.unity3d.com/ScriptReference/RigidbodyConstraints.FreezePosition.html
to see how its done. If not please share the code or a screenshot of the rigidbody restrictions you tried in the editor

How do I apply rotations to parent child using Rigidbody?

I have a finger object. It is just three cubes representing the finger parts.
The 2nd cube is the child of the 1st one. And the 3rd cube is the child of the 2nd one.
This is the heirarchy: Cube1 -> Cube2 -> Cube3
My goal is to apply a rotation angle to the first cube and let the other cubes do the same locally.
Example: Apply 30 degrees Z rotation to the first cube, 30 degrees Z rotation to the 2nd cube, and also the 3rd one.
This will make a finger that look like this:
(Forgive me if it doesn't look like a finger)
In every Update() frame, I will change the angle (it's just one number) and it will rotate every cube for me.
My question is:
How do I make all these cubes collide properly with other objects?
I tried putting the Rigidbody on all of them and set isKinematic=false because I want to transform them myself. But I still cannot use transform.rotation to update my rotation because it will miss the collision with a ball very easily (especially the tip of the finger because it moves faster than other parts). Continuous detection doesn't help.
So I tried using rigidbody.MoveRotation() and rigidbody.MovePosition() instead, which is a pain because they need absolute values. They worked but the animation is so jumpy when I change the angle quickly.
I'm guessing that the animation is jumpy because there are many Rigidbodies or because the physics engine cannot interpolate the position of each box properly when I use MoveRotation() and MovePosition().
I need to use MovePosition() also because when I use only child.MoveRotation(transform.parent.rotation * originalChildLocalRotation * Quaternion.Euler(0, 0, angle)), the position doesn't move relative to the parent. So I have to compute child.MovePosition(parent.TransformPoint(originalLocalPositionOfTheChild)) every frame too.

Unity rigidbody2d velocity converges to 0 over time for 1 axis at random

I made a very simple program where it creates edgecolliders on the edges of the screen, and then launches an ellipse that has a circleCollider2d attached to it with impulse force.
I set all the colliders (edge and circle) to use the bouncy material with bounciness set at 1.0 (infinite bounce).
I am having a strange issue where over time, one of the axis of velocity converges to 0. For example
this.GetComponent<Rigidbody2D> ().AddForce (new Vector2 (100, 100), ForceMode2D.Impulse);
What happens is that the object will start moving in a diagonal fashion, but given enough bounces, the velocity goes to just alternating between (100,0) and (-100,0). This can happen either where the x axis goes to 0 or the Y axis goes to 0. It depends on which one was higher during the start.
I am not sure why this is happening. Basically visually, the object will start bouncing around, and after about 10 bounces or so, it will just be bouncing back and forth in a straight line instead of preserving the other axis as well. I have no idea why it does that. I don't have any of the constraints on (I also tried constraining Z axis rotation, but that did nothing).
bouncyness on all edge colliders as well as the circle collider are set to 1.0.
The rigid body has mass at 1, and all drag coefficients are set to 0 as well.
The intention is for it to just bounce endlessly.
I will answer my own question here as I just realized why this is happening once I observed the behavior of the rotation. It completely did not occur to me that friction would have a hand in this, especially since friction in this case was resulting in the same simulation each time.
Along with turning bounciness to a 1, you must also turn friction to 0.