Walk around square platform. Unity2D - unity3d

I'm trying to make a character that walks around a platform and if the character reach a corner, it rotates and continue walking on the side of the platform, same with the bottom part.
This is a visual representation of what I'm trying to achive.
Movement
The specific problem is when the character reach the corners, the rotation just go crazy. I'm trayng to achive this using a raycast from the character to the platform and if the raycast doesn't find floor, I start the rotation like this:
times++;
transform.rotation = Quaternion.Slerp(transform.rotation,Quaternion.AngleAxis(times*-89.9f,Vector3.forward),Time.deltaTime * RotationSpeed);
_characterGravity.SetGravityAngle(transform.localEulerAngles.z);
I'm using a characterGravity script that allows me to change the gravity direction for the character in order to not fall when is walking upside down or on the sides. But this is not working propperly. Is there a better way to do this?

Assuming your 2D view is aligned on the X-Y plane, with the Z-axis aimed into the screen (the same direction the camera is facing), I suggest using Transform.Rotate() instead of trying to linearly interpolate the rotation between two values:
if (ShouldRotateAroundCorner()) {
transform.Rotate(Vector3.forward, RotationSpeed * Time.deltaTime);
}
You'll just need to make sure your ShouldRotateAroundCorner() knows when to start and stop the 90 degree turn, which will take a little bit of additional code to keep track of when it's in this state change.

Related

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.

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.

How to prevent characters to overlap

I'm really struggling at something that I had imagined to be pretty simple: I have two meshes (fighters) instantiated from the same prefab and I want them to not overlap. The prefab is set to have a box collider. Ideally, I let the animator handles the position. I've tried several approaches:
Configure the animators to "Animate Physics". No collision is detected
Uncheck root motion and move characters using game object's transform's position. No collision is detected
Uncheck root motion, add a rigid body to each character and move character using game object's position. Collision is detected, but the reaction is governed by physics which makes it look unnatural since all I want is to just avoid the characters to pass through each other i.e. just want characters to be pushed back until they don't collide anymore
I really would like to avoid going with a manual approach to this. How would I prevent this overlapping?
Edit: isTrigger is unset on box colliders
A rigidbody is highly configurable and you want a limited set of the physics functionality it can offer. Go with the third solution you enumerated and freeze the rotation in the rigidbody. See the rigidbody page for more details.
As a side note, you might consider using a capsule collider, so that characters "slide" around each other more easily.
Although it's not necessarily preventing overlap, you could create a perimeter for characters. It would be rather simple depending on what you're doing.
If you want to set up a distance perimeter to prevent characters from getting too close to one another you could use the following equation
Vector3 focalPOne = focalPointOfCharacterOne();
Vector3 focalPTwo = focalPointOfCharacterTwo();
float dx = focalPOne.x - focalPTwo.x;
float dy = focalPOne.y - focalPTwo.y;
float dz = focalPOne.z - focalPTwo.z;
// calc distance along z plane
if (Math.Sqrt((dx * dx) + (dy * dy)) < minDistance)
{
// person is too close to the others head.
if (Math.abs(dz) < minHeightAbove)
{
// go to previous position.
}
// if there is no vertical elivation, you can go
// straight to moving them back to previous position
}
enter code here
If this wasn't what you're looking for, please let me know so I can edit or remove it.
The perimeter created would look something like this.
In order to have your characters with their respective box colliders register a collision, both need to have a RigidBody component. That's the only way to make sure the characters do not go through each other considering they share a prefab.
You don't have to handle the separation/rejection of the characters via the physics engine though. Simply make the box collider trigger, and then handle the separation via code in a C# script using onTiggerEnter.
There's a collision detection matrix that could help you, found here: https://docs.unity3d.com/Manual/CollidersOverview.html. Also this image summarizes it beautifully (in the documentation it's in two separate tables for some reason):

Need Unity character controller to make sharp 90 degree turns and not slide when turning

I'm using the first person controller for my characters movement. On a left arrow keypress, I'd like the character to instantly rotate 90 degrees and keep moving forward. Currently, when I hit the arrow key, the character makes the sharp 90 degree turn, but the forward momentum the character previously had takes a second to wear off so the character ends up sliding in the direction he was previously moving a short bit.
The closest example I can think of to visually explain what I'm trying to do is how the character turns sharp in Temple Run. How my game is currently working, if I had the character on a ledge make a sharp left turn, he'd likely keep the original momentum and slide off the edge right after he turns.
Since my character is running on the x/z axis, I'm wondering if there would just be some way to maybe swap the directional velocity/momentum? The speed the character had on the x axis would instantly be switched to the z when it turns and the other would be set to zero. I'm obviously open to any solution that accomplishes what I'm looking for.
I dug into the CharacterMotor class in the first person controller, but have yet to find what part I can tweak to accomplish this.
I'd greatly appreciate any help.
Thank you.
You can try to stop the velocity of the Rigidbody before turning.
this.rigidbody.velocity = Vector3.zero;
this.rigidbody.angularVelocity = Vector3.zero;
If you want the object to continue like it did, you can try playing around with it by saving the current velocity in a variable, setting it to 0, rotate it and then putting back the old velocity (still forward).
If it works with global vectors (so from the point of view of the world, not the object), then you can try negativing the velocity, actually causing it to go 'backwards'. I can't test it for now but either way I think you need to set the velocity to zero first before turning the character.

Camera movement using WASD on x/z plane only

I've been wearing my enter key down on google searches - I have a camera script based on the MouseOrbit.js asset. That's all working fine, but in addition to the basic orbiting and the zooming that I've added, I would like to use the WASD keys to move the camera around the world.
The W key would move the camera straight forward, however it would ignore the y axis. For example, using
transform.Translate(Vector3.Forward*Time.Delta*20);
moves the camera forward relative to the camera. This results in you quickly hitting the ground. Moving back oibviously does the opposite. The desired effect is sliding across the world without getting any closer/farther to it, regardless of the angle the camera is at.
The closest I can get is using the Space.World parameter of Translate(), but this does not take into account the rotation of my camera. I think if I could take that into account, this would be solved but I'm not clear on how to do that.
Thanks,
Chris
(From Tetrad on http://Gamedev.stackecxchange.com)
You don't need to use transform.Translate. Just calculate how much the camera should move forward for a given frame (something like if the W key is held down do deltaPos = transform.forward * Time.deltaTime * 20), set the Y value of that Vector3 to zero, then add that delta vector to the original position by adding it to the current position transform.position += deltaPos;