I'm recreating the movement system from the DS game Dragon Quest Heroes Rocket Slime in Unity. Currently I've got pretty desirable results but it's not 100%.
One of the things that are different is that my player bounces back from walls further on long distance slings than he does in the original game. (left is my recreation, right is original game). Obviously, I can't find out how the game was made and the exact maths/values they use for slinging but one of the things I got from the game is that:
The player moves at constant velocity while slinging in a direction (no slow down, they just stop at a certain point)
Which led me to believe the game doesn't use traditional velocity mechanics so I made it so:
While stretching, the players 'pVelocity' (potentialVelocity) goes up. When they let go a point create at curPlayerPosition + pVelocity is created. I then move the player at a constant velocity to that point. My idea to fix my issue of bouncing too far is by getting the distance from the position where I slung from to the end point and making the player bounce less the larger the distance and further the smaller the distance.
Problem is, I'm not really sure how to do that last part.
Here's my code:
Vector2 reflectedVelocity = Vector2.Reflect(pVelNormalized, foundHit.normal);
playerAnimator.SetFloat("VelocityX", reflectedVelocity.x);
playerAnimator.SetFloat("VelocityY", reflectedVelocity.y);
float DistanceFromOrigin = Vector3.Distance(slingOrigin, transform.position);
print(DistanceFromOrigin);
//ToDo: reflectedVelocity is lower the higher DistanceFromOrigin is.
The other solution I thought of is that maybe my idea of how the 'velocity' works in the original game is completely wrong and if so, please tell me how it actually is/looks to be actually done
reflectedVelocity is lower the higher DistanceFromOrigin is.
So reflectedVelocity should have an inverse relationship with DistanceFromOrigin
Vector2 reflectedVelocity = Vector2.Reflect(pVelNormalized, foundHit.normal);
float DistanceFromOrigin = Vector3.Distance(slingOrigin, transform.position);
if(DistanceFromOrigin == 0) DistanceFromOrigin = 0.00001f;//to avoid division by zero
reflectedVelocity *= (SomeConstant / DistanceFromOrigin);
playerAnimator.SetFloat("VelocityX", reflectedVelocity.x);
playerAnimator.SetFloat("VelocityY", reflectedVelocity.y);
You may use a linear relationship instead of non-linear:
reflectedVelocity *= (SomeConstant - DistanceFromOrigin);
Related
Story
so I'm working on making a procedural terrain generator script to spawn rocks on the ground all the big rocks don't care what rotation they are but I have some small rocks that have to be parallel to the terrain or they look weird, part of them floating off the ground other part stuck in the ground the quaternions I don't seem to understand
Problem
i am getting the terrainData.GetInterpolatedNormal and putting it into a vector3 called normals then when i am spawning my rock i rotate it towards ground
Instantiate(SmallRock, new Vector3(point.x, Heights, point.y) transform.rotation = new Quaternion(normals.x,normals.y, normals.z, 90));
my problem lies in the Quaternion(normals.x,normals.y, normals.z, 90)
I don't know what to put where like should I only put the normals.x/z there or should I put the normals.y in there too and I don't even know what 90 at the end does, and yes I know that the interpolatednormals returns a 0 to 1 so I tried multiplying it to make it rotate more than 1 but it seems to just not rotate the right way if you can't tell by now I really have no clue how quaternions work but everywhere i search I can't find anything that helps and I didn't really feel like learning about how quaternions work so thanks for saving me time
Quaternions use compound(Imaginary) numbers to represent a sequence of rotations in 3d space.
When you instantiate a new Quaternion using it's constructor you are providing it with what you think the Quaternion's real and imaginary numbers should be.
Despite the seemingly familiar x, y, and z names you should not manually modify or provided them, they are not euler angles, cartesian coordinates, or traditional vector components.
What you're currently passing it is portions of a direction instead of the real and imaginary parts of a Quaternion.
A normal is an "outwards direction" from a given position. So to get a rotation we need some other direction to compare it to in order to get a rotation.
Compare your direction with the up direction and you'll get a rotation that you can use.
Quaternion rotation = Quaternion.FromToRotation(Vector3.up, normalDirection);
I have a 2.5d platformer game. The character is using rigidbody movement on a spline (using the curvy splines asset) which curves into 3d space in all sorts of ways, while the camera stays fixed to the side so that you see the path and background turning, but maintain a 2d side scrolling perspective.
I'm essentially creating a look rotation based on the spline, then moving the player using that forward vector, and making sure to remove any velocity perpendicular to the path so that the player stays centered on the path even when curving. I'm removing the velocity on that vector instead of projecting all the velocity in the direction of the path so that the player can still jump and fall like normal.
void SetLookRotation()
{
// get nearest TF and point on spline
Vector3 p;
mTF = Spline.GetNearestPointTF(transform.localPosition, out p);
// Get forward and up vectors of point on spline
_localHorizontal = Spline.GetTangentFast(mTF);
_localVertical = Spline.GetOrientationUpFast(mTF);
// Set look rotation to path
transform.rotation = Quaternion.LookRotation(Vector3.Cross(_localHorizontal, _localVertical), _localVertical);
}
void Movement()
{
Vector3 m = transform.right * groundAcceleration * moveInput;
rb.AddForce(RemoveCrossVelocity(m));
rb.velocity = RemoveCrossVelocity(rb.velocity);
Vector3 localVelocity = transform.InverseTransformDirection(rb.velocity);
localVelocity.z = 0;
rb.velocity = transform.TransformDirection(localVelocity);
}
Vector3 RemoveCrossVelocity(Vector3 v)
{
// get magnitude going in the cross product / perpindicular of localHorizontal and localVertical vector
// (essentially the magnitude on "local Z" or to the sides of the player)
Vector3 crossVelocity = Vector3.Project(v, Vector3.Cross(transform.right, transform.up));
// and remove it from the vector
return v -= crossVelocity;
}
The first 2 functions are happening in FixedUpdate() in the order shown.
The problem is, when hitting sharp corners at high speeds, some inertia causes the player to deviate off the center of the path still just ever so slightly, and a lot of that momentum turns into upward momentum, launching the player upwards. Eventually the player can fall off the path completely (I do have a custom gravity acting towards the spline though). It works perfectly at lower speeds though, even when dealing with sharp corners. At least as far as I can tell.
I tried a bit of code from https://answers.unity.com/questions/205406/constraining-rigidbody-to-spline.html too but no luck.
Is there a way I could constrain the player rigidbody on a vector that is not one of the global x/y/z axes? I've tried a host of other solutions like setting the transform of the player towards at the center of the spline but I can't seem to get it without feeling very jerky. Using forces makes the player "rubber band" towards and past the center back and forth. Maybe there is something in my math wrong. In any case, I'm hoping someone could help me make sure that the player will always stay on the center of the spline but only on the vector to the sides of the player's face direction, so that it doesn't mess with jumping. Thank you very much in advance!
For potential future visitors, I have figured this out. There are a few components (and a lot more if you're trying to do full spline based physics, but just to start with movement...)
First we must orient our character, so that our local coordinate system can be referenced with transform.right etc. Luckily this package provides these functions which return useful vectors. I'm sure there is math beyond me to do this otherwise if you are building your own spline system.
void SetLookRotation()
{
// get nearest TF and point on spline
Vector3 p;
playerTF = currentSpline.GetNearestPointTF(transform.localPosition, out p);
// Get forward and up vectors of point on spline
_localHorizontal = currentSpline.GetTangentFast(playerTF);
_localVertical = currentSpline.GetOrientationUpFast(playerTF);
// Set look rotation to path
transform.rotation = Quaternion.LookRotation(Vector3.Cross(_localHorizontal, _localVertical), _localVertical);
}
Here I am setting a velocity directly but if you're using forces it's the same principle.
if (Mathf.Abs(localVelocityAs_X) <= maxDashSpeed * Mathf.Abs(moveInput))
{
Vector3 m = transform.right * maxDashSpeed * moveInput;
rb.velocity = RemoveCrossVelocity(m);
}
localVelocityAs_X is defined as (ran in fixedUpdate/ physics step):
float currLocalVelocityX = (playerTF - prevPositionX) / Time.deltaTime;
localVelocityAs_X = Mathf.Lerp(localVelocityAs_X, currLocalVelocityX, 0.5f);
prevPositionX = playerTF;
Where playerTF is your position on a spline (in this case, using the curvy spline package from the unity asset store. Those spline positions return very small floats so in my case I multiplied playerTF by around 10,000 to make it a more easily readable metric). This is essentially just manually calculating velocity of the player each frame by comparing current position on the spline to last frame's.
RemoveCrossVelocity is the same as above. Comment explanations should suffice.
Vector3 RemoveCrossVelocity(Vector3 v)
{
// get magnitude going in the cross product / perpendicular of local horizontal and local vertical vectors
// (essentially the magnitude on "local Z" of the player)
Vector3 crossVelocity = Vector3.Project(v, Vector3.Cross(transform.right, transform.up));
// and remove it from the vector
return v -= crossVelocity;
}
Finally the solution to the drift. My crude fix was essentially to just adjust the player to the center of the spline every frame. Horizontally, there is no change because it grabs the closest spline point which is calculated by this package to be sort of a float clamped between the start and end of the spline. Vertically, we are being set to the distance the player is from the spline in the local up direction - a fancy way of saying we're not moving vertically at all. The reason this must be done is to avoid the spline vertical position overwriting the players, and we obviously can't set this vector back to playerPos.y in our local coordinate space, so we must resort to using a direction vector * the distance from our everchanging floor. This isn't absolutely ideal at the end of the day, but it works, and there isn't any extra jitter from it (interpolate on your player's rigidbody and some camera dampening helps). All in all these together combine to make a player able to accelerate quickly around sharp corners of a spline with physics and intertia will never cause the player to fly off or drift from the center. Take that, rocket physics!
void ResetPlayerToSpline()
{
Vector3 P; //closest spline point to player
float pTf = currentSpline.GetNearestPointTF(transform.position, out P);
playerHeight = Vector3.Distance(transform.position, P);
transform.position = P + (transform.up * Vector3.Distance(transform.position, P));
}
Ultimately for those possibly looking to do some kind of implementation in the future, the biggest thing you'll run into is a lack of cardinal direction, global oriented axis-based functions and properties normally provided by a game engine. For a primer, here are a few I would use (not including gravity, which is simply opposite your up vector times whatever magnitude):
This one allows you to create a vector using x and y like normal (and z in theory) and run this function to convert it when you actually use the vector in a local space. That way, you don't have to try and think in directions without names. You can still think of things in terms of x and y:
Vector3 ConvertWorldToLocalVector(Vector3 v)
{
Vector3 c;
c = transform.right * v.x + transform.up * v.y;
return c;
}
This is basically the same as what is happening in RemoveCrossVelocity(), but it's important to reiterate this is how you set velocity in a direction to 0. The second part shows how to get velocity in a certain vector.
void Velocity_ZeroY()
{
rb.velocity -= GetLocalVerticalVelocity();
}
public Vector3 GetLocalVerticalVelocity()
{
return Vector3.Project(rb.velocity, _localVertical);
}
Getting height, since you cannot just compare y positions:
height = Vector3.Distance(transform.position, P);
I think that's all the good stuff I can think of. I noticed a severe lack of resources for created spline based physics movement in games, and I'm guessing now it's based on the fact that this was quite an undertaking. It has since been brought to my attention that the game "Pandemonium"(1996) is a curvy 3d spline based sidescrolling platformer - just like mine! The main difference seems to be that it isn't at all based on physics, and I'm not sure from what I can tell if it has pitch changes and gravity to compliment. Hope this helps someone someday, and thank you to those who contributed to the discussion.
myRB.AddForce(new Vector2(jumpRight,jumpHeight));
By using the above code, in Unity2D using c#, the jumping distance varies and goes weird.
How to hop forward keeping the distance constant everytime?
If you want to ensure that the trajectory is always the same, then you need to overwrite any velocity it already has. That can be done just by setting the velocity of the RigidBody2D equal to the same vector each time.
myRB.velocity = new Vector2(jumpRight, jumpHeight);
I have a game that effectively has two paddles in it, and I've got it working set up so that either the players or an AI can control either paddle. However, I'm trying to figure out how to get my AI to actually smoothly run after the ball and catch it, instead of simply brute-forcing it with a basic "Direction of Ball" = Opposite direction AI moves.
In order to do so, I'm trying to write some code that will allow the AI to predict where it will land, when the ball is in it's court or going to be. It's not going that well. This is a diagram of what I want to achieve:
So, I've been studying Unity3D and came up with this:
Vector3 BallMinder = BallPosition.gameObject.rigidbody.velocity;
float BallX = BallMinder.x;
float BallY = BallMinder.y * Time.deltaTime;
float GroundHitPointX = (BallY + BallX);
Vector3 BallImpactPoint = new Vector3 (GroundHitPointX, 0, 0);
Debug.Log (BallImpactPoint);
Debug.DrawRay (BallImpactPoint, Vector3.up, Color.green);
However, I feel like I've oversimplified it or forgotten something. My calculations are way off, and the ray isn't showing up where it should, if it does at all. What have I gotten wrong?
All you need is the Kinematics equations.
These equations perfectly describe a projectile in motion. Therefore, you can use them to predict where and with what components the ball will land.
Here is a graph to help you understand.
This is going to be simplified by assuming that gravity is downward, and the floor is at Y=0. It could be expanded using vector math for arbitrary floorlines and gravity directions, but for your purposes you should be fine using the two aforementioned assumptions. I'm also assuming for this explanation that BallPosition is a Transform object, but the math would be the same if it weren't. I'm also assuming no air friction on the ball, which would make the math a fair bit more complicated.
Essentially, you need to calculate the time until the ball hits the ground and then extrapolate the ball's X-position at that time.
The classic formula dictating the motion of an accelerating object is d = vt + at^2 / 2, where v is the current velocity, a is the acceleration, and t is the amount of time that has passed. To figure out the time of impact, we simply solve for t.
To figure out when the ball will hit the ground you'll want to set d = BallPosition.position.y * -1, a = Physics.gravity.y, and v = BallPosition.rigidbody.velocity.y. This will give us the number of seconds until impact.
Since gravity is assumed to be entirely downward and no other forces are acting on the ball, we can know that the X-position of the ball at impact time is BallPosition.position.x + BallPosition.rigidbody.velocity.x * impactTime. This would be the X-position your computer player should move towards.
...The formula should work any time the gravity is entirely downward, even if the ball is on the upward portion of its trajectory or moving away from your computer player. You'll probably want to come up with some strategy for what the computer should do while waiting for the human to hit the ball and set the ball's new trajectory, since you probably don't want the computer to try to run towards the human's side of the net. Depending on how your players are able to hit the ball, you might be able to predict the new velocity the ball would have after the human hits it, and then feed that data to this formula.
essentialy you apply gravity to the ball at every step to make it seem natural something like this:
ball is at your position ready to be trown
ball.x = 20;
ball.y = 10;
xVelocity = 0;
yVelocity = 0;
gravity = 0.1;
you trow the ball
When you trow the ball you set xVelocity to a constant lets say 1 and yVelocity to 5. So:
xVelocity = 1;
yVelocity = 5;
ball.x+=xVelocity;
ball.y+=yVelocity; //to start the ball moving
Now in the gameloop you calculate the ball position like this:
tick
{
if(ball.y > 10) //if the ball is off the ground (i asume 10 is ground)
{
ball.x+=xVelocity;
ball.y+=yVelocity;
yVelocity-=gravity;
}
}
with this the ball should fly in a perfect arc now to calculate the landing zone you can simply do the same thing again with dummy numbers
while(dummyBall.y > 10)
{
dummyBall.x+=xVelocity;
dummyBall.y+=yVelocity;
yVelocity-=gravity;
}
landingPosX = dummyBall.x;
You will probably need to tweak the numbers but if i remember everything correctly it should work something like this. Hope it make sense
title pretty much says it all.. say I have a ship, its worldVelocity is a Vector3 with these values:
X: 0
Y: 0
Z: 1
(assuming +Z is Forward, +Y is Up, and +X is Right)
this Vector3 is added to the ships worldTranslations Matrix which stores translation and rotation.
worldTranslations.Translation += worldVelocity
and my ship goes forward, and it stops accelerating at speed 1 (correctly) as this is its target velocity, but what if the ship has been rotated Right 180 degrees, so its now flying backwards. Automatically the ships target velocity should detect this and begin trying to fly forward again.
The problem is I don't know how to get the local velocity out of my ships velocity and localTransforms, and I cant find an answer anywhere..
so at the moment my ship uses the world velocity for checking if its reached its target speed, so long as the ship doesnt rotate, it flies properly. My target speed will be one, so my ship compares its current velocity (0,0,0) to target velocity (0,0,1) and properly accelerates till it reaches its target (0,0,1) and stops accelerating to maintain a constant speed, but if i turn (say just 90 degrees right), current velocity is now (-1,0,0) because the ship is drifting left now, so it should use that relative velocity to determine that it needs to accelerate (1,0,1) to get back to (0,0,1) but I have no idea how to get relative velocity so it currently uses world velocity which is still (0,0,1) even though its flying left and so it thinks its all good but its still drifting left..
Thanks.. again everyone :)
The answer was to transform the velocity vector by the inverse rotation matrix.
This post was made as my friend realised the desired result was achieved in Unity3D using one of its built in functions, but ultimately is the same question:
XNA equivalency of unity function Transform.InverseTransformDirection
More detailed explanation can be found on that page.
However if this does not work for you, as it didn't immediately fix my problem, try examining one component of the vector at a time. I had to multiply only the Z component of the vector by -1 to make it work. Most likely interference by other maths so I recommend you examine the result vectors components results individually.
-Aaron
The ship's world matrix has a 'Forward' property which is a unit length vector that always points in the ship's forward direction. This vector is updated anytime the ship's rotation is changed and the world matrix updated to store the change.
So you can key the velocity off that vector and it will always go forward relative to the rotation of the ship.
Velocity = shipMatrix.Forward * speed;
ShipMatrix.Translation += velocity;