Yeah, it seems like you may be on the right track. Without more info, I can’t really provide better advice. I would need to know what was supposed to be happening at a higher level to say how I might decompose things.
I’m afraid I’m still stuck. Okay, I have one movement system (let’s imagine) that controls all the movements. Some movements are straight (i.e. bullet fly), some are conditional (i.e. homing missile that has to avoid obstacles that are not its designated target). Since I have just one system, I have to distinguish what type of movement it is. Still, I have to check if I have some component (I can store this information in some inner field, of course, but isn’t that effectively the same?) or don’t. Even if I move all the obstacle detection code to the system, I still have to decide should the evasion happen or no, somehow. How else could I make a system being one common and distinguish between different object types?
It sounds like you are mixing physics and steering… that’s probably not a great idea.
p = p + v * t
…that’s one system.
There is another system that controls what v is. Some of the entities may have a fixed ‘v’ and don’t need this system. Others need occasional updating to avoid obstacles, move to waypoints, whatever. That’s steering.
Ok, this is suitable for point objects (or, say, uniform spheres), but I have to consider rotations as well. I mean I can’t change movement direction by PI/2 in one frame (well, I can technically, but I want some realism) - but I don’t want to implement full scale physics with mass, forces, inertia tensor etc. And some movements are strafe (or, consider a constant rotation w/o linear movement at all), so one base “v” is not enough. Shouldn’t it be implemented in a separate system? Now rotations and linear movements are separated (1st step - fwd looking linear integration if speed (scalar value) >0, 2nd step - rotation with respect to some rotation speed if there’s order to rotate).
Frankly, I’m not even sure what kind of game you are writing so I can’t speculate how you are controlling rotation.
velocity is enough to model strafing or any movement because you literally can’t move anything but straight in one frame. Aside from some kind of transdimensional paradox.
The point is that there should be a single system handling movement. If that also includes rotation, then fine. I don’t know how you are controlling rotation so I can’t say. Is it set absolutely? Is it set through rotational accelerations? Apparently my guesses are wrong so I’ll wait to know more info.
There should be a separate system controlling the accelerations or velocities or whatever controls the movement. That’s steering. Some objects won’t be steered at all.
The point is to give your systems one job when possible. Not two jobs and not conditionally this job or this job or maybe this other job if the sun is setting, the components are aligned right, and it’s the second thursday in May on a leap year.
It is set absolutely, I’m generating target quaternion (where it shall look in the end of maneuver), then I slerp from current orientation to target quaternion with respect to fps and rotation speed (constant for particular object). Like
Quaternion q = new Quaternion();
q.lookAt(...);
float angleDEG = FastMath.RAD_TO_DEG * getAngleRadians(currentRot, q);
float timeToComplete = angleDEG / rotSpeed; // getting seconds needed
float donePart = Math.min(1f, tpf / timeToComplete);
// interpolation
Quaternion resultQuat = new Quaternion();
resultQuat.slerp(currentRot, q, donePart);
// setting orientation
PhysicalObject newObj = obj.newFacing(resultQuat);
ed.setComponent(eId, newObj);
where
private float getAngleRadians(Quaternion q1, Quaternion q2) {
float angle = 2 * FastMath.acos( FastMath.abs( q1.dot(q2) ) );
return angle;
}
So far if there’s obstacle - I give it one lookat quat, if there’s no obstacle - I give it another lookat quat, but obstacle check itself happens not for every object type, which is where distinction problem appears. I mean it is not necessarily in movement system, but anywhere it is.
update: what I want to achieve - well, recall Descent capsule. I want my movement being controlled likewise for both player and NPC. I mean propulsion system takes uniform orders like “accelerate to the speed of”, “decelerate by this value”, “rotate to look here”, “strafe this direction”…
There is some movement system that will move objects. Maybe it will also make them face a certain way. That’s all it does. It doesn’t decide to avoid things. It doesn’t make pancakes. All objects that move are moved by this system.
There is some other system that decides what to set velocity to. What to set “facing” to… or what to set “I’d like to face in this direction please” to. Velocity doesn’t necessarily have to coincide with facing direction, by the way. By locking those together you’ve limited yourself to specific types of movement… or you end up having to have a separate velocity for movement that isn’t in the facing direction. Or adding the idea of forces.
…anyway, it feels like you are doing steering the hard way. By hard-coding your steering I think there is a lot of dynamic behavior that you will miss out on. But maybe that’s ok.
How I might do general movement:
1)really simple physics system:
v = v + a * tpf
p = p + v * tpf
…similar lines for angular acceleration. This is simple. No need to worry about momentum and stuff I think.
For me I ended up hooking control drivers to the objects that have external input. It doesn’t have to be done that way but it’s the easiest way to have conditional forces. Just now saw your “descent style” movement so maybe it’s not totally necessary but it does make the feedback loop tighter. I did this in sim-eth-es. (Which by the way moves exactly like Descent… just saying.)
- a steering system. It’s job is to set the velocity (linear and rotation) or acceleration (linear and rotation) depending on how simple your physics integration is. Some objects won’t need to be steered at all (like a bullet). The NPC ships will steer based on way points, ship avoidance, AI, whatever. You build up some few steering primitives: go here, chase this, avoid this. This starts to get into how the AI works.
Every AI book I’ve read so far has at least one chapter on steering primitives.
I had assumptions
Thanks, now I need to work all this out.
I really wanted to add a boids style set of swarming objects to the sim-eth-es example… I just never got around to it. It would have required a few simple steering primitives that get mixed together, I think.
I want to make a free-form tower defense style game for my son and I to script objects for… I’ll have to work some of this out for that too. Unfortunately, I have no time these days except for answering forum posts.
Edit: for reference: Boids (Flocks, Herds, and Schools: a Distributed Behavioral Model)
Somewhere I knew I’d written a 2D boids implementation before and I couldn’t remember where. This is now completely off topic but I found this kids game I started:
http://progeeks.com/pspeed/download/bubbles-0.1.0-SNAPSHOT-exec.jar
It was for my son when he was a toddler++… he had trouble concentrating so I started making a game that would try to help him focus through distractions. I never completed it, though. Once you completed a module with a bubble you were supposed to be able to link them into an underwater city. I never go that far.
Highlights: random sea floor and boids-style schools of fish.
Funny contrast of that distant “space” voice and cartoon environment
p.s. I’ll have a look into boids as soon as I’m done with more basic stuff.
Update: Trying to get sim-eth-es to run in IDEA. Getting this:
Exception in thread "main" java.lang.NoClassDefFoundError: com/simsilica/util/LogAdapter
at example.Main.main(Main.java:76)
Strange thing is, the class is there, I can see the source, and the project builds… and it runs from outside IDEA via ‘gradle run’.
Update2: in JME 3.1 works out of the box, just as expected. Just thought that it would be quicker since I had IDEA installed already. Not really
Weird. Not sure what’s confusing IDEA.
I’d ask to clarify this point a little bit, if possible. If I take this approach, I have to have e.g. Vec4d with axis and value, and integrate on the base of that. Then, if I want to replicate my current behavior (=when ship rotates from current orientation to target one), I have my lookat (target) quat, and I have to use .toAngleAxis() to pass what I get to the new movement system. But in your Quatd you don’t have toAngleAxis() nor even lookAt() which kinda concerns me… how would you do such rotation then, not being setting quats directly as I did before?
Angular acceleration and velocity have to be tracked as euler angles. There’s really no easy way around it. Trying to combine some arbitrary angle+axis with some other gets ugly. And you can’t use Quaternion to do it because Quaternion can never represent angular changes more than a certain amount.
My recollection was that Descent used “mouse look is ship’s direction” but I admit that may be filtered through years of playing mouse look games.
If you want to use angular acceleration and/or velocity… then the math is something like:
rotVelocity.addScaledVectorLocal(rotAccel, t);
orientation.addScaledVectorLocal(rotVelocity, t);
…where rotVelocity is a Vec3d and orientation is a Quatd.
Usually you want your rotational acceleration transformed into local space first and so have to do an inverse world rotation transform on it. If I’d released my mphys library yet then I could just point to it. Suffice it so say that as written, the rotational acceleration vector can be treated like any other vector. So translating from world space to local space is straight forward if you’ve ever done quaternion stuff before.
This is something “between the lines” of Quatd implementation I wanted to hear (and toAngles() is there, right)
You helped me with that for quats before, so I hope It won’t be much harder for vectors; it’s up to some tests anyway. Thanks for your points!
toAngles() isn’t relevant here as it will give you the most compact representation of an orientation. This is not the same as angular rotation or acceleration.
Quaternions cannot be used to represent angular acceleration or rotation in any way. They are compact form of ORIENTATION not ROTATION. Where here ORIENTATION is the way an object is pointing in space… and ROTATION is some amount of angular movement.
I know it seems like splitting hairs but take a simple example of trying to represent rotational velocity of 1.5 PI per second around some axis. Already you can see that a Quaternion would have normalized that to 0.5 PI… and that’s only predictable if there are no other axes of rotation. If you have rotation on some of the other axes then the “angles” (if you were crazy enough to suck them back out) would quite likely all be changed… and the original angle gone or some strange value.
To track angular acceleration and/or velocity you need to use a Vec3d that represents rotation around the three axes.
Quatd’s addScaledVectorLocal() is magic enough to properly add that to an orientation. As it turns out, it’s not very much magic though. Quaternions are the real magic here.
I got this, but if I need to know what angles I need in the end, knowing target orientation, I have to get them somehow… which is what toAngles() is for, isn’t it? I speak only “rotating to face target shortest way” thing now, not arbitrary-possibly-many-turns rotations.
Rotating to face target is a steering problem. Usually you decide x and y rotational axis acceleration based on dot products with the left and up vectors. (You can even calculate this in local space already if you like. Just worldToLocal() the position of the thing you are steering towards.)
You don’t want arbitrary angles representing some orientation delta. They could do strange things like induce roll when you don’t want that and so on. x, y, 0 into a quaternion may give you some completely different x, y, AND z out.
dot products are almost as magic as quaternions and have a 1001 uses. They are one of my very favorite things. (Which by the way doesn’t make for good conversation at a party if someone asks what your favorite thing is…)
Yeah, I’m sorry for mixing concerns, it happens exactly because I’m trying to split the mix I have now - and immediate movement (integration) system is just one half, so I’ll study what you said
and about deltas!
Update:
finally looks like I got something moving like I wanted it to (spinning around local Y at regular speed in this case):
// test spinning steering
Pose pose = ed.getComponent(eId, Pose.class);
Quatd facing = pose.getFacing();
Vec3d dir = facing.mult(Vec3d.UNIT_Z);
Vec3d localY = facing.mult(Vec3d.UNIT_Y);
Quaternion rotQ = new Quaternion();
rotQ.fromAngleAxis(FastMath.HALF_PI, localY.toVector3f());
Vec3d tdir = new Vec3d(rotQ.mult(dir.toVector3f()));
Angular angular = ed.getComponent(eId, Angular.class);
Vec3d newRotVel = new Vec3d(tdir.dot(Vec3d.UNIT_X), tdir.dot(Vec3d.UNIT_Y), tdir.dot(Vec3d.UNIT_Z)).mult(12);
Angular newangular = angular.changeVelocity(newRotVel);
ed.setComponent(eId, newangular);
// EOF test spinning
(I know, the way I’m getting tdir
is kinda weird, this is just test, I can’t switch everything onto mathd at once, that’d break too much - but at least I feel some control back now). Thanks again!
So you just want a 90 degree rotation in local space? Actually I’m not sure I really understand what that’s for.
You said it looks weird but to me it looks unnecessary. Why isn’t it just facing.mult(Vec3d.UNIT_X)?
Your rotational velocity calculation looks strange also. I’m not sure why you are doing dot products against the x,y,z axes. Conceptually, what is this meant to be doing?