Constrained knockback - unity3d

I have a playing field which is divided in 4 zones (as seen on the image below)
The player (blue) is on the second zone while there are 2 enemies (red) on the third zone. When the player throws an object and hits the enemy, the enemy is knockbacked in the next zone (4) but should stay within that zone.
If the enemy was being hit on the edge of the zone, I want him to still be in the zone 4 and not outside of it (if he got knocked back). I'm not sure if the direction of the hit matters in the calculation. The horizontal position (x) between the zone can be random, but should be constrained by the edges of the zone.
Question: When the enemy is hit, I want to calculate the knockback position while being constrained to the zone he will be knockbacked to.
Note that it can be on any shape of playing field (triangular, hexagonal, circular, ...)

New components
You can use the NavMesh systems to compute the next position of the enemy.
You can find those classes (Components) on github. They are official component, but still in development if I remember well, that's why they are on github.
Now, you can find many tutos over the internet showing you how to use those classes. Here is one good example, thanks to Brackeys.
How it will work
Once you have downloaded thoses classes, you will be able to do as the following
I created a very basic scene in which a wolf needs to go to the cross position (on right part of the image) but is not allowed to go on the lightest blue on the map (right part of the image, you can see a line in the middle). So the navs components will compute the closest point, as shown as the middle of a circle (left part). You can manually add an offset not to be exactly on the edge if you want.
You can find more informations here.
This method computes the closest point of a given position.
Compute the knockback position
Regarding your comments, to compute the knockback position, there is differents ways :
Axis-based position
If your scene is as simple as those rules :
Left to right is X axis
Top to bottom is Y axis
(or the opposite)
Then you can do as the following :
public Vector3 GetKnockbackPosition(Vector3 startPosition, float force)
{
Vector3 res = startPosition;
res.x -= force; // Could be Z axis, depends on orientation
return res;
}
Not axis-based position
You can do as the following :
public Vector3 GetKnockbackPosition(Transform transformToKnockback, float force)
{
Vector3 res = transformToKnockback.position;
res -= transformToKnockback.forward * force;
return res;
}
Here you have the forward which will help you. You can substract this value and add a force factor to control the knockback force.
So once you have computed the position, you can use SamplePosition to adjust it, whether or not it is inside a zone.
Tell me if something is not properly explained.

Related

Unity Face Object With Off-center Pivot Point?

I'm trying to create a "billboard" effect where a quad/gameObject always faces a target object (the camera) around the Y axis. This is working fine as per the code below, but I want to add an optional offset to the pivot point on the X axis.
So rather than rotating around the center point (default behaviour), I want the quad to rotate around a new point thats n units off of the center point, while still facing the target object.
This will run in Update().
Currently Working Code Without Offset
transform.LookAt(camera.transform, Vector3.up);
transform.localEulerAngles = new Vector3(0, transform.localEulerAngles.y, 0); // Only affects Y Axis.
The offset I need is calculated by the function below. It is tested to be correct by moving a child GameObject by this value.
Both the leftObj and rightObj are children of the GameObject I want to be rotating.
public float GetCenterPos()
{
Vector3 left = leftObj.transform.localPosition;
Vector3 right = rightObj.transform.localPosition;
Vector3 center = (left + right) / 2f;
return center.x;
}
Top Down View of My Problem
I have tried combinations of RotateAround, but I can't figure out how to get it to face the correct object and what the pivot should be relative to the offset.
I have also googled around, and I can't find a solution to this problem that I feel is relatively simple.
To recap: I don't need a rotational offset, and I don't want to add an extra parent to change the pivot like many other answers suggest. The offset gets calculated dynamically in Update.
Thank you for any help.
I've been tinkering with it for a while, and I came up with this solution. It's not ideal because it requires storing a reference to the starting position, which breaks some of the other movement functionality I need, but it does answer my original question.
Before beginning the code above (either in start or before a bool flag is set, whatever) store a reference to the object's localPosition (startPos)
Then before calling LookAt, adjust the position to take into account the offset.
transform.localPosition = new Vector3(startPos.x + offset, transform.localPosition.y, transform.localPosition.z);
transform.LookAt(camController.transform, Vector3.up);
transform.localEulerAngles = new Vector3(0, transform.localEulerAngles.y, 0);
To clarify, the reason why I need a reference to startPos is because otherwise I would be adding the offset every frame, resulting in the object just moving constantly, rather than using a consistent value.
I just set the startPos before and after toggling the "billboard" functionality to keep it updated. Not ideal, but it does work.

Which rotation is shown in the Inspector?

The chest bone of my player can be rotated while aiming.
Now I wanted to evaluate how much (minimum and maximum rotation) I should let the chest be rotatable.
To do that, I allowed all degrees of rotation and took a look at the Inspector.
For example, the minimum value that the chest should be rotatable to the left should be Y=-15.
At Y=-15 (seen in the Inspector), it still looked natural.
Now I wanted to code this.
To my surprise, chest.localRotation.Y was a completely different value than what the Inspector is showing.
I have then taken a look at the chest variable and extended the view.
I just can't see the rotation value that the Inspector is showing.
How should I go on in this case, please?
I'm using this to rotate the bone:
Chest.LookAt(ChestLookTarget.position);
Chest.rotation = Chest.rotation * Quaternion.Euler(Offset);
Thank you!
The reason why it doesn't work:
Quaternion is not a human readable value.
One Quaternion is allways unique but can have multiple (infinite?) different representations in Euler space! The other way round one Euler represents allways exactly one Quaternion value.
If you look at the docs it explicitly says
Don't modify this directly unless you know quaternions inside out.
Than as said what you see in the inspector is the localRotation in relation to the parent Transform.
Better said it is one of the many possible Euler inputs that result in the Quaternion. What you see in the debug at localEulerAngles is another possible Euler representation. Unity usually in localEulerAngles also gives you only values > 0.
It seems that the chest anyway will only rotate around the Y axis, right?
If this is the case you can simply get the Angle between the chest's original forward vector and the target. It is way easier to handle Vector3 values than Quaternions ;)
It seems to be the same use case as in this post
// get the target direction
Vector3 targetDir = ChestLookTarget.position - Chest.position;
// Reset any difference in the Y axis
// since it would change the angle as well if there was a difference I the height
// between the two objects
targetDir.y = 0;
// however you currently rotate
// instead rotate only the Vector3 variable without applying it to the transform yet
Vector3 newDir = Vector3.RotateTowards(Chest.forward, targetDir, RotationSpeed * Time.deltaTime, 0.0f);
// Compare the target direction to the parents forward vector
float newAngle = Vector3.Angle(Chest.parent.transform.forward, newDir);
if (newAngle > MaxRotationAngle)
{
// What should happen if angle gets bigger?
return;
}
// If angle still okey set the new direction
Chest.rotation = Quaternion.LookRotation(newDir);

Unity3D angle between vectors/directions on specific axis

I have two directions and i am trying to calculate the angle on a specific axis. The object from which the directions are derived is not a static object so i'm struggling to get my head round the maths to work out what i need to do.
FYI, I have the start and end points that i have used to calculate the directions if they are needed.
Here's a diagram to show what i am looking for:
The above image is from a top-down view in unity and it shows the angle i want.
The problem can be seen from the above image, which is that the directions are not on the same height so i can't use the vector3.angle function as it won't give me the correct angle value.
In essence i want to know how much would i have to rotate the red line to the left (top view) so that it would line up with the blue (top-view).
The reason i need this is as i am trying to find a way of getting the side-to-side angles of fingers from my leap motion sensor.
This a generic version of my other question:
Leap Motion - Angle of proximal bone to metacarpal (side to side movement)
It will provide more specific information as to the problem if you need it and it has more specific screenshots.
**UPDATE:
After re-reading my question i can see it wasn't particularly clear so here i will hopefully make it clearer. I am trying to calculate the angle of a finger from the leap motion tracking data. Specifically the angle of the finger relative to the metacarpal bone (bone is back of hand). An easy way to demonstrate what i mean would be for you to move your index finger side-to-side (i.e. towards your thumb and then far away from your thumb).
I have put two diagrams below to hopefully illustrate this.
The blue line follows the metacarpal bone which your finger would line up with in a resting position. What i want to calculate is the angle between the blue and red lines (marked with a green line). I am unable to use Vector3.Angle as this value also takes into account the bending of the finger. I need someway of 'flattening' the finger direction out, thus essentially ignoring the bending and just looking at the side to side angle. The second diagram will hopefully show what i mean.
In this diagram:
The blue line represents the actual direction of the finger (taken from the proximal bone - knuckle to first joint)
The green line represents the metacarpal bone direction (the direction to compare to)
The red line represents what i would like to 'convert' the blue line to, whilst keeping it's side to side angle (as seen in the first hand diagram).
It is also worth mentioning that i can't just always look at the x and z axis as this hand will be moving at rotating.
I hope this helps clear things up and truly appreciate the help received thus far.
If I understand your problem correctly, you need to project your two vectors onto a plane. The vectors might not be in that plane currently (your "bent finger" problem) and thus you need to "project" them onto the plane (think of a tree casting a shadow onto the ground; the tree is the vector and the shadow is the projection onto the "plane" of the ground).
Luckily Unity3D provides a method for projection (though the math is not that hard). Vector3.ProjectOnPlane https://docs.unity3d.com/ScriptReference/Vector3.ProjectOnPlane.html
Vector3 a = ...;
Vector3 b = ...;
Vector3 planeNormal = ...;
Vector3 projectionA = Vector3.ProjectOnPlane(a, planeNormal);
Vector3 projectionB = Vector3.ProjectOnPlane(b, planeNormal);
float angle = Vector3.Angle(projectionA, projectionB);
What is unclear in your problem description is what plane you need to project onto? The horizontal plane? If so planeNormal is simply the vertical. But if it is in reference to some other transform, you will need to define that first.

Camera: "Linear" zoom

I want to move a camera closer to an object in Unity, so that the zooming is linear. Take a look at the zooming in the Scene window in the Unity Editor. The camera isn't moving with a constant speed. The more zoomed in you are, the slower the camera moves. And I do want to move the camera, I don't want to change the FOV.
First off, yes do not use the FOV to do that, it would be a massive pain to control the movement. Being in low FOV, would mean you are focus on a tiny part and movement would be hard to control since one unit would be a large movement in small FOV. Also, a large FOV creates fish eye effect. So nope.
One way you could achieve this fast to slow effect is to use a distance to define the amount of movement
Let's consider you use the mouse wheel to zoom in and out, you listens to the value and create a movement based on that.
float movement = Input.GetAxis("Mouse ScrollWheel");
if(movement == 0)
{
return;
}
Transform camTr = Camera.main.transform;
float distance = Vector3.Distance(camTr.position, origin.position) + 1;
camTr.Translate(camTr.forward * distance * movement * Time.deltaTime * speed);
speed variable is meant to control how fast regardless of the distance. Distance gets a plus 1 so you can still move while real close to the origin.
The origin position needs to be defined, it could be based on a raycast going forward from the camera and hitting at y = 0. Just an idea. It could also be a focused object, that all depends on what you are doing.
The whole point here is that your camera will move faster if you are far from the focus point. Also, you may want to clamp the movement so that begin far away does not result in gigantic movement.
i propose you to use FOV (because player feel good)
1- use Transform.localPosition to change distance of camera to near or far - you should change z axis to changing distance of camera
2- use camera rendertotexture and in GUI change scale of it ( isn't good because need pro unity license and get allot of memory);
but can't know whats reason for you want forget changing FOV
One step of perspective projection of a point is the division by distance of that point. This means that the projected size of any object does not change linearly with its distance.
projectedSize = originalSize / distance
So if you want to approach an object in a way that its projected size grows linearly, you'll have to change your distance non-linearly.
We do this by also dividing the distance by some value f. The exact value of f depends on the stepSize and (I think) the FoV of your camera.
By using FixedUpdate() instead of Update() we make sure we have a constant stepSize which allows us to assume f as being constant as well.
void FixedUpdate()
{
newDistance = oldDistance / f;
}
For the definition of f you can use any value bigger than, but close to 1. For example f = 1.01f. This will approximate the desired behaviour quite well. If you want it to be perfectly linear, you'll have to dig deeper into the calculations behind perspective projection and calculate the exact value of f yourself: https://en.wikipedia.org/wiki/3D_projection#Perspective_projection

How to make Gravity for multiple planets in Unity3d

What i'm looking to do is something similar to our solar system, where you are gravitically drawn to one planet till you leave it's pull, and then once you're on another planet be drawn to that gravity.
I have found many entries online about how to create gravity for a single planet, but none i've found work for multiple sources.
Any help would be GREATLY appreciated.
Projects I've already looked at were mainly for single planet gravity.
unity 3.3, create local gravity on object
http://answers.unity3d.com/questions/13639/how-do-i-make-a-small-planet-with-gravitational-pu.html
http://answers.unity3d.com/questions/701618/how-could-i-simulate-planetary-gravity-that-has-an.html
This could be done easily by adding a normal force relative to the planet on the surrounding objects.
According to physics of Universal gravitational force you can calculate the gravitational force. Then calculated the normal force at the moment and add the force.
void FixedUpdate(){
// Do the Force calculation (refer universal gravitation for more info)
// Use numbers to adjust force, distance will be changing over time!
forceSun = G x (massPlanet x massSun)/d^2;
// Find the Normal direction
Vector3 normalDirectionSun = (planet.position - sun.position).normalized;
// calculate the force on the object from the planet
Vector3 normalForceSun = normalDirection * forceSun;
// Calculate for the other systems on your solar system similarly
// Apply all these forces on current planet's rigidbody
// Apply the force on the rigid body of the surrounding object/s
rigidbody.AddForce(normalForceSun);
// .... add forces of other objects.
}
With various m1, m2 values you will be able to make the system more realistic. As an example make the object move/accelerate towards the planets with higher mass.