I have two objects (target and player), both have Position (Vector3) and Rotation (Quaternion). I want the target to rotate and be facing right at the player. The target, when it shoots something should shoot right at the player.
I've seen plenty of examples of slerping to the player, but I don't want incremental rotation, well, I suppose that would be ok as long as I can make the slerp be 100%, and as long as it actually worked.
FYI - Im able to use the position and rotation to do plenty of other things and it all works great except this last piece I cant figure out.
EDIT
Code samples run in the Target's class, Position = the targets position, Avatar = the player.
Using the value of 1 for the Slerp isn't work. This code below rotates some, but I think something is way off becuase when it's drawn the target scales up and then down as the player gets closer.
var A = new Vector3(Position.X, Position.Y, Position.Z);
var B = new Vector3(GameState.Avatar.Position.X, GameState.Avatar.Position.Y, GameState.Avatar.Position.Z);
A.Normalize();
B.Normalize();
var angle = Math.Acos(Vector3.Dot(A, B));
var axis = Vector3.Normalize(Vector3.Cross(A, B));
var rotOnAngle = new Quaternion(axis, (float)angle);
var newRot = Quaternion.Slerp(Quaternion.Identity, rotOnAngle, 1f);
Rotation = newRot;
Cannon.Shoot(Position, Rotation, this);
I tried using this and it doesn't quite work either...the target does rotate, but not to face the player. But at least the scaling problem goes away.
Quaternion q = new Quaternion();
var pos = Vector3.Normalize(Position);
var pos2 = Vector3.Normalize(GameState.Avatar.Position);%
var a = Vector3.Cross(Position, GameState.Avatar.Position);
q.X = a.X; q.Y = a.Y; q.Z = a.Z;
q.W = (float)Math.Sqrt(((int)Position.Length() ^ 2) * ((int)GameState.Avatar.Position.Length() ^ 2)) + Vector3.Dot(Position, GameState.Avatar.Position);
q.Normalize();
Rotation = q;
Cannon.Shoot(Position, Rotation, this);
It's been a while since I did this sort of math, but I would have guessed that the 3rd parameter there would simply be 1.
Edit: To qualify that, the last time I did this, it was called Managed directX, not XNA!
I happen to ask the same question over in Game Dev stack exchange and someone answered over there. Make sure to read the comments in the answer, regardless, the answer/solution works great! Thanks, and sorry about asking here as well.
https://gamedev.stackexchange.com/questions/15070/orienting-a-model-to-face-a-target
Related
Quick summation:
I am attempting to create an ocean comprised of planes that can be easily loaded and unloaded based on distance. On this ocean I want a boat to sail with a player onboard in the first person, where I want them to experience the buoyancy of their boat relative to the surrounding waves.
I am new to shadergraph and have been following several tutorials to try and create the desired effect.
These tutorials include
Catlikecoding's Wave shader
https://catlikecoding.com/unity/tutorials/flow/waves/
Zicore's Gertsner wave
https://www.youtube.com/watch?v=Awd1hRpLSoI&ab_channel=Zicore
Tom Weiland's dynamic water physics
https://www.youtube.com/watch?v=eL_zHQEju8s&ab_channel=TomWeiland
These resources have gotten me a good chunk of the way there, but I've run into some issues regarding the boat physics specifically.
I understand the math behind simulating Gertsner waves, and have tried to set up a WaveManager that calculates the y-value of a "floater" at position (x,z).
Floater.cs
public Rigidbody rigidBody;
public float depthBeforeSubmerged = 1f;
public float displacementAmount = 3f;
public int floaterCount = 1;
public float waterDrag = 0.99f;
public float waterAngularDrag = 0.5f;
private void FixedUpdate()
{
rigidBody.AddForceAtPosition(Physics.gravity / floaterCount, transform.position, ForceMode.Acceleration);
float waveHeight = WaveManager.instance.GetWaveHeight(transform.position.x,transform.position.z);
if(transform.position.y < waveHeight)
{
float displacementMultiplier = Mathf.Clamp01((waveHeight-transform.position.y) / depthBeforeSubmerged) * displacementAmount;
rigidBody.AddForceAtPosition(new Vector3(0f, Mathf.Abs(Physics.gravity.y) * displacementMultiplier, 0f),transform.position, ForceMode.Acceleration);
rigidBody.AddForce(displacementMultiplier * -rigidBody.velocity * waterDrag * Time.fixedDeltaTime, ForceMode.VelocityChange);
rigidBody.AddTorque(displacementMultiplier * -rigidBody.angularVelocity * waterAngularDrag * Time.fixedDeltaTime, ForceMode.VelocityChange);
}
}
This is pretty much lifted directly from Tom Weiland's video. Basically, when my floatpoint dips below the calculated wave, it applies force to make it travel upwards. Following his instructions carefully yielded decent results, but the problem arose when I started using Shadergraph to create my ocean.
The main issue is I wanted the waves to be tileable across multiple planes, so I used the object position and transformed it to world position to do calculations, and then added it back to the object position before manipulating the vertices of the ocean plane.
I've tried to show it below here:
This makes the ocean plane tileable and looks great, but also enlarges it in the scene quite a bit. I've put a regular plane on top to show the difference. Both are 1x1 units in the inspector.
So this is the first problem. The calculations I do in my WaveManager aren't lining up properly with the actual visual representation of the waves.
The second problem is that I can't seem to make the calculations done in WaveManager give me the correct y-coordinates.
In the shader, the waves are animated using the Time-component.
I've found the documentation to be a bit sparse on Shadergraph components, probably because I'm self taught and have a hard time wrapping my head around some of these concepts.
I've had a hard time working out how to calculate the change in y-coordinates over time in the wavemanager-script. The different solutions I've tried have just made the y-coordinate slowly grow larger into the negative range. I just have no idea how to make my calculations match up with the ones done on the GPU.
It's no important that it be super accurate, just good enough to sell the effect with small waves.
The Wavemanager code, finally.
private void Start()
{
waveLengthA = waves.GetFloat("_WaveLengthA");
waveLengthB = waves.GetFloat("_WaveLengthB");
waveLengthC = waves.GetFloat("_WaveLengthC");
waveLengthD = waves.GetFloat("_WaveLengthD");
steepnessA = waves.GetFloat("_SteepnessA");
steepnessB = waves.GetFloat("_SteepnessB");
steepnessC = waves.GetFloat("_SteepnessC");
steepnessD = waves.GetFloat("_SteepnessD");
directionA = waves.GetVector("_DirectionA");
directionB = waves.GetVector("_DirectionB");
directionC = waves.GetVector("_DirectionC");
directionD = waves.GetVector("_DirectionD");
kA = (2 * Mathf.PI) / waveLengthA;
kB = (2 * Mathf.PI) / waveLengthB;
kC = (2 * Mathf.PI) / waveLengthC;
kD = (2 * Mathf.PI) / waveLengthD;
cA = Mathf.Sqrt(Mathf.Abs(Physics.gravity.y)/ kA);
cB = Mathf.Sqrt(Mathf.Abs(Physics.gravity.y) / kB);
cC = Mathf.Sqrt(Mathf.Abs(Physics.gravity.y) / kC);
cD = Mathf.Sqrt(Mathf.Abs(Physics.gravity.y) / kD);
}
private void Update()
{
offset += Time.deltaTime;
}
public float GetWaveHeight(float x,float z)
{
fA = kA*(directionA.x * x + directionA.y * z - cA * offset);
fB = kB * (directionB.x * x + directionB.y * z - cB * offset);
fC = kC * (directionC.x * x + directionC.y * z - cC * offset);
fD = kD * (directionD.x * x + directionD.y * z - cD * offset);
position += new Vector3(x + directionA.x * steepnessA / kA * Mathf.Cos(fA),steepnessA/kA*Mathf.Sin(fA),z+directionA.y*steepnessA/kA*Mathf.Cos(fA));
position += new Vector3(x + directionB.x * steepnessB / kB * Mathf.Cos(fB),steepnessB/kB*Mathf.Sin(fB),z+directionB.y*steepnessB/kB*Mathf.Cos(fB));
position += new Vector3(x + directionC.x * steepnessC / kC * Mathf.Cos(fC),steepnessC/kC*Mathf.Sin(fC),z+directionC.y*steepnessC/kC*Mathf.Cos(fC));
position += new Vector3(x + directionD.x * steepnessD / kD * Mathf.Cos(fD),steepnessC/kD*Mathf.Sin(fD),z+directionD.y*steepnessD/kD*Mathf.Cos(fD));
return position.y;
}
The code above is quite ugly with a lot of repetition, but my plan is to make a constructor at some point to make it easier to read.
I grab all the values used in my shader, to make sure they match even if I change the look of the waves. Then I do the calculations from Catlikecoding and plot in the x- and z-coordinates of my floating object.
As far as I can understand, it should work if I just combine alle the calculated vectors, but obviously I'm missing something.
From what I've seen others do, they often opt to create custom planes with more vertices, that can cover their entire gameworld and avoid the problem, but I'm making a larger world and am worried about performance. (Though I don't know if I should be even.) I really like the fact that my ocean planes are tileable.
Does anyone here know of any solutions or help me solve the issue of worldspace vs objectspace, or how to accurately recreate the time-progression as seen in the shader?
Any help would be much appreciated.
So, for anyone struggling with this, I found the answer.
When combining multiple waves together, the manipulated plane grows in size for every wave added.
In my above question, I had somehow messed up the formulas for calculating the waves. I redid them and got the correct result.
Now, the trick is to simply divide the resultant wave, by the number of waves that you are combining. This will make sure that the actual size of the plane won't change.
You of course need to do this in your waveManager code as well. It's important to keep in mind that you only need the y-coordinate, so you only have to calculate that. For each wave, calculate the y-coordinate and then divide the combined height by the number of waves. This will make the floatation code work as it should!
Hope this helps someone out there who struggled like me.
i'm new with Arkit and Xamarin enviroment.
I need help about the translation of SCNNode in scene using PanGesture.
I have used this guide to start my fist approach with PanGesture. Guide
After that....
I used the help code, but I noticed that, as in the example, when I move an object in the scene it ONLY follows the X, Y axes.
In short, if the Cartesian axes of the ARkit scene are framed, with the Z of the camera pointing at the observer, everything works.
If the camera position is changed (the phone moves), how can I obtain the translation delta within the 3D space?
if (sender.State == UIGestureRecognizerState.Changed)
{
var translate = sender.TranslationInView(areaPanned);
// Only allow movement vertically or horizontally [OK, but how can i obtain the XYZ value of delta in scene from XY of Viewport?]
node.LocalTranslate(new SCNVector3((float)translate.X / 10000f, (float)-translate.Y / 10000, 0.0f));
}
Following the opengl standard I thought of such a solution:
[Pseudo code]
scale/offset from 0...1 to -1...1 coordinate space
var vS = new Coordinate(this.Scene.CurrentViewport.X, this.Scene.CurrentViewport.Y, 1.0);
var vWH = new Coordinate(this.Scene.CurrentViewport.Width, this.Scene.CurrentViewport.Height, 1.0);
var scrrenpos = new Coordinate(translate.X, -translate.Y, 1.0);
var normalized = (scrrenpos - vS) / vWH;
After that i need matrix:
var inversePM = (projection * modelView).inverse
where:
=> projection from ARCAmera.ProjectionMatrix
=> modelView from ARCamera.Transform
To finish:
var result = normalized * inversePM;
if I set the SCNNode position with this value nothing works :(
Thanks
Problem solved!
here Swift code to translate in c#... works fine!
I have two identical objects and two cameras. What I'd like to achieve is the following:
1) Capture the position and rotation of Cube A relative to Camera A
2) Transfer that to a Cube B, so that in Camera B (which I cannot move), Cube B looks exactly as Cube A looks in Camera A
I was successful doing that with position with the following code:
positionDifference = CubeA.InverseTransformPoint(CameraA.transform.position);
To transfer it onto Cube B, I do:
cubeBpos = transform.InverseTransformPoint(CubeB.transform.localPosition);
while ( Mathf.Abs (cubeBpos.x + positionDifference.x) > 0.01f ) {
if (cubeBpos.x + positionDifference.x > 0) {
CubeB.transform.position += new Vector3(-0.01f, 0, 0);
}
else if (cubeBpos.x + positionDifference.x < 0) {
CubeB.transform.position += new Vector3(+0.01f, 0, 0);
}
cubeBpos = transform.InverseTransformPoint(CubeB.transform.position);
}
That's clunky, but works. However, when I try to transfer rotations, Cube B starts to pivot around the origin. Interestingly, when I move Cube A in world coordinates, Cube B moves in local, and vice versa. I suspect local-to-world coordinate translation is an issue, but I also think my rotation code is naive. I tried to capture rotation in two ways, first like this:
rotationDifference = Quaternion.Inverse(CubeA.transform.rotation) * CameraA.transform.rotation;
CubeB.transform.rotation = Quaternion.Inverse(rotationDifference);
Second attempt:
rotationDifference = new Vector3(transform.eulerAngles.x, transform.eulerAngles.y, transform.eulerAngles.z);
CubeB.transform.eulerAngles = rotationDifference;
Both approaches resulted in weird rotational offsets. I tried using localPosition and localEulerAngles, didn't help.
I'm hoping there's a smarter way to do this :)
EDIT:
Here's a Dropbox link to the project
The problem is that you are treating position and rotation separately although they influence each other. Let's put both together and say we have model transforms for the two cameras and the two cubes (represented as homogeneous matrices; assuming column vectors). Then, we want to find the transform for cube B TCubeB, such that:
TCameraA^-1 * TCubeA = TCameraB^-1 * TCubeB
Note that TCamera is the model transform of the camera, not the view matrix. If you have the view matrix, simply leave the inverse away.
We can immediately solve for TCubeB:
TCameraB * TCameraA^-1 * TCubeA = TCubeB
I'm not too familiar with the Unity API but it seems like you cannot use transformation matrices directly. So let's split the transformation matrix T in a rotational part R and a translational part Tr:
TrCameraB * RCameraB * (TrCameraA * RCameraA)^-1 * TrCubeA * RCubeA = TrCubeB * RCubeB
TrCameraB * RCameraB * RCameraA^-1 * TrCameraA^-1 * TrCubeA * RCubeA = TrCubeB * RCubeB
If we care only about the rotation, we can calculate the respective quaternion by simply doing:
QCameraB * QCameraA^-1 * QCubeA = QCubeB
The translation becomes a bit more difficult. We need to find the translation transform, such that
TrCameraB * RCameraB * RCameraA^-1 * TrCameraA^-1 * TrCubeA * RCubeA * RCubeB^-1 = TrCubeB
To find the translation vector, simply multiply the origin to the left-hand side:
TrCameraB * RCameraB * RCameraA^-1 * TrCameraA^-1 * TrCubeA * RCubeA * RCubeB^-1 * (0, 0, 0, 1)
In pseudo-code, this boils down to (the matrices that appear stand for the respective translation vectors):
Vector4 translation = (0, 0, 0, 1)
translation += TrCubeA
translation -= TrCameraA
translation = RCameraA.Inverse().Transform(translation)
translation = RCameraB.Transform(translation)
translation += TrCameraB
Again, I barely know the Unity API and it might use some different conventions than I did (conventions in transformation math are especially tricky). But I am sure you can adapt the above derivations if something is not correct.
Nico's great answer solved my issue, but since code-formatting in the comment section wasn't built for this, here's the code that I wrote for Unity based on Nico's answer:
Vector3 translation = new Vector3(0,0,0);
translation += cubeA.transform.position;
translation -= cameraA.transform.position;
translation = cameraA.transform.InverseTransformPoint(translation);
translation = cameraB.transform.TransformPoint(translation/2);
cubeB.transform.position = translation;
Quaternion rotation = Quaternion.identity;
rotation = cameraB.transform.rotation * Quaternion.Inverse(cameraA.transform.rotation) * cubeA.transform.rotation;
cubeB.transform.rotation = rotation;
It's not one-to-one, but it achieves what I hoped: If Cube A is stationary and Camera A moves around it, then Cube B moves with respect to Camera B in such a way that Cube A and Cube B always have the exact same position and rotation in relation to their respective cameras.
I just want to know if I'm using vector3.slerp correctly as I have some issues.
I have set up a cube in a scene and I want it move smoothly from its current position to one being passed into by the program. Before I was trying to use the slerp I simply had my cube moving from its current point to its new point like this (as I said, the math all works):
cubeFour.transform.localPosition = new Vector3((B2*C1 - B1*C2)/delta,0,(A1*C2 - A2*C1)/delta);
But when I put it in a slerp call, my cube is no longer on the screen. This is how I'm calling it:
Vector3 targetPosition = new Vector3(lerpX, lerpY, lerpZ);
cubeFour.transform.localPosition = Vector3.Slerp(cubeFour.transform.localPosition, targetPosition, Time.deltaTime);
LerpX, LerpY & LerpZ are local variables I've set to contain the X, Y & Z of the first Vector3 I created in my first attempt.
Have I set up the slerp correctly or have I made a kerfuffle somewhere?
Slerp is best for directions, Lerp is best for positions. You should probably be using Lerp. And Time.deltaTime is almost certainly the wrong choice for t. You should be giving it a number that moves from 0 to 1 over the time that you want the cube to move, e.g.
float moveTimeInSeconds = 2;
cubeFour.transform.localPosition = Vector3.Lerp(cubeStartingPosition, targetPosition, (Time.time - startTime) / moveTimeInSeconds);
Or use MoveTowards if it makes more sense to define the speed of motion, and to move towards a target regardless of a starting point, instead of via starting and ending positions and times.
float step = speed * Time.deltaTime;
cubeFour.transform.localPosition = Vector3.MoveTowards(cubeFour.transform.localPosition, targetPosition, step);
Lerp uses a percentage to animate!
This means you must plug in a percentage, not a value near the same thing each frame.
Example Update():
var animationSpeedInSeconds=1;
var animationCounter:float=0;
function Update(){
if(animationCounter>=0 && animationCounter<=1){
animationCounter+=Time.deltaTime/animationSpeedInSeconds;
cubeFour.transform.localPosition = Vector3.MoveTowards(cubeFour.transform.localPosition, targetPosition, animationCounter);
}
}
I hope this helps!
I am encountering an issue when updating a bullet's position when I rotate the ship that shoots it. Currently the code almost works except that at most angles the bullet is not fired from where i want it to. Sometimes it fires slightly upward than the centre where it should be, and sometimes downwards. I'm pretty sure it has something to do with the trigonometry but I'm having a hard time finding the problem.
This code is in the constructor; it sets the bullet's initial position and rotation based on the ship's position (sp.position) and its rotation (Controls.rotation):
this.backgroundRect = new RotatedRectangle(Rectangle.Empty, Controls.rotation);
//get centre of ship
this.position = new Vector2(sp.getPosition().X + sp.getTexture().Width/2,
(sp.getPosition().Y + sp.getTexture().Height / 2));
//get blast pos
this.position = new Vector2((float)(this.position.X + (sp.getTexture().Width / 2 *
Math.Cos(this.backgroundRect.Rotation))), (float)(this.position.Y +
(sp.getTexture().Width / 2 * Math.Sin(this.backgroundRect.Rotation))));
//get blast pos + texture height / 2
this.position = new Vector2((float)(this.position.X + (this.texture.Height / 2 *
Math.Cos(this.backgroundRect.Rotation))), (float)(this.position.Y +
(this.texture.Height / 2 * Math.Sin(this.backgroundRect.Rotation))));
this.backgroundRect = new RotatedRectangle(new Rectangle((int)this.position.X,
(int)this.position.Y, this.texture.Width, this.texture.Height), Controls.rotation);
speed = defSpeed;
Then in the update method I use the following code; I'm pretty sure this is working fine though:
this.position.X = this.position.X + defSpeed *
(float)Math.Cos(this.getRectangle().Rotation);
this.position.Y = this.position.Y + defSpeed *
(float)Math.Sin(this.getRectangle().Rotation);
this.getRectangle().CollisionRectangle.X = (int)(position.X + defSpeed *
(float)Math.Cos(this.getRectangle().Rotation));
this.getRectangle().CollisionRectangle.Y = (int)(position.Y + defSpeed *
(float)Math.Sin(this.getRectangle().Rotation));
Also I should mention that my ship had not been working correctly when I rotated it since the origin (center of rotation) was (0,0). To remedy this I changed the origin to the center of the ship (width/2,height/2) and also added those values to the drawing rectangle so that the sprite would draw correctly. I imagine this could be the problem and suppose there's a better way of going around this. The game is in 2D by the way.
The problem is that the sprites are not drawn on the positions they are supposed to be. When calculating the center position, you assume that the sprite is not rotated. This causes some trouble. Furthermore, rotating an arbitrary vector is not as easy as you did. You can only rotate a vector by multiplying its components with sin/cos when it is on the x-axis.
Here is how you can rotate arbitrary vectors:
Vector2 vecRotate(Vector2 vec, double phi)
{
double length = vec.Length(); //Save length of vector
vec.Normalize();
double alpha = Math.Acos(vec.X); //Angle of vector against x-axis
if(vec.Y < 0)
alpha = 2 * Math.PI - alpha
alpha += phi;
vec.X = Math.Cos(alpha) * length;
vec.Y = Math.Sin(alpha) * length;
return vec;
}
Make sure, the angle is in radians.
With that you can calculate the correct position of the ship:
Vector2 ShipCenter = sp.Position() + vecRotate(new Vector2(sp.getTexture().Width/2, sp.getTexture().Height/2), Controls.Rotation);
You can use the same function to determine the position of the bullet's center. I don't exactly know, where you want to place the bullet. I will assume, it is at the center of the right edge:
Vector2 offset = new Vector2(sp.getTexture.Width/2, 0);
this.position = ShipCenter + vecRotate(offset, Controls.Rotation);
If you then need the position of the upper left corner of the bullet, you can go further:
this.position -= vecRotate(new Vector2(this.texture.Width/2, this.texture.Height/2), Controls.Rotation)
However, as I stated, it is probably easier to handle the ship's and bullets' positions as central positions. Doing so, you need far less trigonometry.