Rotate model to face direction it's traveling in 3d with scenekit and swift - Boids implementation - swift

I am implementing a boids simulation using Swift and Scenekit. Implementing the simulation itself has been fairly straight forward however I have been unable to make my models faces the direction they are flying (at least all the time and correctly) To see the full project, you can get it here (https://github.com/kingreza/Swift-Boids)
Here is what I am doing to rotate the models to face the direction they are going:
func rotateShipToFaceForward(ship: Ship, positionToBe: SCNVector3)
{
var source = (ship.node.position - ship.velocity).normalized();
// positionToBe is ship.node.position + ship.velocity which is assigned to ship.position at the end of this call
var destination = (positionToBe - ship.node.position).normalized();
var dot = source.dot(destination)
var rotAngle = GLKMathDegreesToRadians(acos(dot));
var rotAxis = source.cross(destination);
rotAxis.normalize();
var q = GLKQuaternionMakeWithAngleAndAxis(Float(rotAngle), Float(rotAxis.x), Float(rotAxis.y), Float(rotAxis.z))
ship.node.rotation = SCNVector4(x: CGFloat(q.x), y: CGFloat(q.y), z: CGFloat(q.z), w: CGFloat(q.w))
}
Here is how they are behaving right now
https://youtu.be/9k07wxod3yI

Three years too late to help the original questioner, and the original YouTube video is gone, but you can see one at the project's GitHub page.
The original Boids code stored orientation as the three basis vectors of the boid’s local coordinate space, which can be thought of as the columns of a 3x3 rotation matrix. Each frame a behavioral “steering force” would act on the current velocity to produce a new velocity. Assuming “velocity alignment” this new velocity is parallel to the new forward (z) vector. It did a cross product of the old up (y) vector and the new forward vector to produce a new side vector. Then it crossed the new side and forward to get a new up vector. FYI, here is the code for that in OpenSteer
Since it looks like you want orientation as a quaternion, there is probably a constructor for your quaternion class that takes a rotation matrix as an argument.

Related

Unintended player movement from transform.InverseTransformDirection

this is my first time posting on here. I'm working on a game using the new Unity multiplayer networking solution.
In summary, the issue is that the player is not moving as intended.
I am trying to take player input as follows:
Vector3 worldSpaceDir = new Vector3(Input.GetAxisRaw("Vertical"), 0, Input.GetAxisRaw("Horizontal"));
then convert it to the object space coordinates of the player character:
_inputDirection = transform.InverseTransformDirection(worldSpaceDir);
The issue I'm having is with a rotation of 0 or 180 the player moves as expected with the WASD inputs, however, at 90 or 270 all the inputs are flipped(A = right, D = left, W = backward, S = forward).
I found a question that is exactly my question but no one responded with an answer. The question is quite old now so I wanted to ask it again for more visibility.
Here's a link to the original question.
Firstly, you are taking the worldSpaceDir wrong, it should be as follow
Vector3 worldSpaceDir = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
here we take horizontal input as X and vertical input as Z, because in Unity Forward is pointed as Z and not Y.
Secondly, we do not need to use InverseTransformDirection() we just need TransformDirection() something like following
Vector3 inputDirection = transform.TransformDirection(worldSpaceDir);
here we are telling unity to convert the worldSpaceDir that is relative to transform (local direction) into a world space direction, so we might actually give a proper name to worldSpaceDir.
The following would work for you.
private void Update() {
Move();
}
private void Move() {
Vector3 directionToMove = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
Vector3 inputDirection = transform.TransformDirection(directionToMove);
transform.position += inputDirection * Time.deltaTime;
}
I think you want to go the other way round actually!
Transform.InverseTransformDirection converts a vector from world space into local space.
What you get as input however is a local vector on the XZ plane. You want to apply this direction according to your player objects orientation, if e.g. pressing right (your input is 1,0,0) the object shall move towards its transform.right vector.
So you rather want to convert in the opposite direction into world space to move the object in the Unity world space.
You should rather be using Transform.TransformDirection!
var worldMove = transform.TransformDirection(input);
Or alternatively you can also just multiply by the rotation like
var worldMove = transform.rotation * input;
Note that if you are also using a Rigidbody like the question you linked there is also Rigidbody.AddRelativeForce which basically works the same way and expects a local space vector which is then internally converted into a world space force.

Unity: Detecting taps on particles in a particle system

I am making a scientific visualization app of the Galaxy. Among other things, it displays where certain deep sky objects (stars, star clusters, nebulae, etc) are located in and around the Galaxy.
There are 6 or 7 classes of object types (stars, star clusters, nebulae, globular clusters, etc). Each object within a class looks the same (i.e. using the same image).
I've tried creating GameObjects for each deep sky object, but the system can get bogged down with many objects (~10,000). So instead I create a particle system for each class of deep sky object, setting the specific image to display for each class.
Each particle (i.e. deep sky object) is created at the appropriate location and then I do a SetParticles() to add them to that class's particle system. This works really well and I can have 100,000 objects (particles) with decent performance.
However, I need to allow the user to click/tap on an object to select it. I have not found any examples of how to do hit testing on individual particles in the particle system. Is this possible in Unity?
Thanks,
Bill
You'll have to do the raycasting yourself.
Just implement a custom raycasting algorithm using a simple line rectangle intersection. Simply assume a small rectangle at each particle's position. Since you do not rely on Unity's built in methods you can do this async. For performance optimization you can also cluster the possible targets at simulation start, allowing the elimination of whole clusters when their bounding box is not hit by your ray.
Note: Imho you should choose a completely different approach for your data rendering.
Take a look at unity's entity component system. This allows for large amounts of data, but comes with some disadvantages (e.g. when using Unity's physics engine) (which will not be of relevance for your case I suppose).
I ended up rolling my own solution.
In Update(), upon detecting a click, I iterate through all the particles. For each particle, I calculate its size on the screen based on the particle's size and its distance from the camera.
Then I take the particle's position and translate that into screen coordinates. I use the screen size to generate a bounding rectangle and then test to see if the mouse point is inside it.
As I iterate through the particles I keep track of which is the closest hit. At the end, that is my answer.
if (Input.GetMouseButtonDown(0))
{
Particle? closestHitParticle = null;
var closestHitDist = float.MaxValue;
foreach (var particle in gcParticles)
{
var pos = particle.position;
var size = particle.GetCurrentSize(gcParticleSystem);
var distance = Vector3.Distance(pos, camera.transform.position);
var screenSize = Utility.angularSizeOnScreen(size, distance, camera);
var screenPos = camera.WorldToScreenPoint(pos);
var screenRect = new Rect(screenPos.x - screenSize / 2, screenPos.y - screenSize / 2, screenSize, screenSize);
if (screenRect.Contains(Input.mousePosition) && distance < closestHitDist)
{
closestHitParticle = particle;
closestHitDist = distance;
}
}
if (closestHitDist < float.MaxValue)
{
Debug.Log($"Hit particle at {closestHitParticle?.position}");
}
Here is the angularSizeOnScreen method:
public static float angularSizeOnScreen (float diam, float dist, Camera cam)
{
var aSize = (diam / dist) * Mathf.Rad2Deg;
var pSize = ((aSize * Screen.height) / cam.fieldOfView);
return pSize;
}

Euler angles for a direction respect to rotated local axis system in unity

I want a determined angle in a local rotated axis system. Basically I want to achieve the angle in a plane of a determined rotated axis system. The best way to explain it is graphically.
I can do that projecting the direction from origin to target in my plane, and then use Vector3.Angle(origin forward dir, Projected direction in plane).
Is there is a way to obtain this in a similar way like Quaternion.FromToRotation(from, to).eulerAngles; but, with the Euler angles, with respect to a coordinate system that is not the world's one, but the local rotated one (the one represented by the rotated plane in the picture above)?
So that the desired angle would be, for the rotation in the local y axis: Quaternion.FromToRotation(from, to).localEulerAngles.y, as the locan Euler angles would be (0, -desiredAngle, 0), based on this approach.
Or is there a more direct way than the way I achieved it?
If I understand you correct there are probably many possible ways to go.
I think you could e.g. use Quaternion.ToAngleAxis which returns an Angle and the axis aroun and which was rotated. This axis you can then convert into the local space of your object
public Vector3 GetLocalEulerAngles(Transform obj, Vector3 vector)
{
// As you had it already, still in worldspace
var rotation = Quaternion.FromToRotation(obj.forward, vector);
rotation.ToAngleAxis(out var angle, out var axis);
// Now convert the axis from currently world space into the local space
// Afaik localAxis should already be normalized
var localAxis = obj.InverseTransformDirection(axis);
// Or make it float and only return the angle if you don't need the rest anyway
return localAxis * angle;
}
As alternative as mentioned I guess yes, you could also simply convert the other vector into local space first, then Quaternion.FromToRotation should already be in local space
public Vector3 GetLocalEulerAngles(Transform obj, Vector3 vector)
{
var localVector = obj.InverseTransformDirection(vector);
// Now this already is a rotation in local space
var rotation = Quaternion.FromToRotation(Vector3.forward, localVector);
return rotation.eulerAngles;
}

Circular orbit formula is not working in unity 3D

I am working on a game where I have to implement some space physics like orbits and gravity....
My main problem is that I'am not able to make a GameObject orbit correctly around a planet (or another object ) , I am trying to do that using an old famous formula from physics textbooks :
v = sqrt( G * ((m1 + m2)/r ))
Where v is the velocity of the orbiting object, G is a constant, m1 and m2 are the masses of the the orbiting object and the planet and r is the distance from the center of m1 and m2 .
Here's my implementation :
void circularOrbit(){
// Get the distance between the two object centers .
dist = new Vector2 (Planet.renderer.bounds.center.x - renderer.bounds.center.x, Planet.renderer.bounds.center.y - renderer.bounds.center.y);
r = dist.magnitude;
tdist = new Vector2 (dist.x, -dist.y).normalized; // 2D vector prependicular to the dist vector .
float force = Mathf.Sqrt (G * ((mBody + mPlanet) / r)); // Calculate the velocity .
rigidbody2D.velocity = tdist * force;
}
The circularOrbit Function is called each frame inside FixedUpdate that unity provide for physics scripting .
Now here's a small gif that is enough to explain my problem :
http://makeagif.com/i/-JxBG8
Here are also some information about my settings :
Monster mass is 1 unit and planet mass is 5 units.
The gravity for the monster is off .
G have a value of 1 (I've tested many values without fixing the problem )
Finally, I know that i'am not the first one to do that, but i've searched a lot this week on how to do this, mainly my code is Box2D port with some tweaking and changes, I've posted a question on GameDev.net here but any suggestion worked .
Please help me by explaining why it's not working in physics beginner way, I'am stuck in this thing for the whole week .
The perpendicular vectors to (x,y) are multiples of (y,-x), but you have (x,-y). You can check when two vectors are perpendicular by the fact that their dot product x1*x2 + y1*y2 is 0. (1,2).(1,-2) = -3. (1,2).(2,-1) = 2-2 = 0.
By the way, I don't recommend mixing force and velocity. I would rename what you currently call force to speed.

finding the force needed based on distances between objects

Trying to programatically set the force based on the distance of the selected object, and another static object.
To understand this better I am making a golf game, I have forced being applied to ball already done but I am manually setting the force at this point. Right now I am trying to build some pseudo-code for how I can accomplish this. I do not have a lot of experience with physics so anything will help here. But basically I have a golfer who selects an area that he would like to hit it to. So once selected, I will grab the location of the selection, obviously find the difference via (selected.transform.position - golfer.transform.position) but now I am a little stuck on what to do next. How can I find the force needed to get to the selected location?
here's some current code in case it helps
var target : Transform;
var speed : float;
var force : float;
var angle = 50;
var i = 1;
function FixedUpdate()
{
if (target == null)
target = GameObject.FindWithTag("Hole").transform;
}
function Hit()
{
var torque = Vector3(Input.GetAxis("Vertical"), 0, -Input.GetAxis("Horizontal"));
var targetRotation = Quaternion.LookRotation (target.position-transform.position,Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation,targetRotation,8);
transform.eulerAngles.x = -angle;
rigidbody.AddTorque(torque.normalized*speed);
rigidbody.AddForceAtPosition (transform.forward * speed * force,target.position, ForceMode.VelocityChange );
}
You will need a few things.
Angle of Force theta
Mass of object m
On the x-axis:
Acceleration a
Initial velocity vi
Final velocity vf
Distance d
Time to travel t
On the y-axis:
Acceleration a = gravity
Initial velocity vi_y
Final velocity vf_y
Height traveled h
Then use the equations for Kinematics and F=ma to find the force.
Your goal is to combine what you know, mass of object, distance, etc. and apply the equations of kinematics to find the acceleration needed on the x-axis. Then you will plug in that acceleration and the mass to F=ma to find the Force.