I'm a doctor, not a mechanic
Karma is the new physics engine included in UnrealEngine2. It runs alongside the regular physics engine, which works essentially the same as the physics in the original UnrealEngine1. In addition to Karma Rigid Body Simulation, Karma supports a more complex form of simulation called Karma Ragdoll Simulation. Actors of both types interact with one another in the world but they have different uses in practice.
- 1 Karma Rigid Body Simulation
- 2 Karma Ragdoll Simulation
- 3 Karma Constraints
- 4 Karma Actors Syntax and Variables
- 5 Karma Constraints Syntax and Variables
- 6 Karma Functions and Events
- 7 KSimParams
- 8 Assorted Tidbits
- 9 Related Topics
- 10 External links
- 11 Comments
Karma Rigid Body Simulation
Karma Rigid Bodies are simply static meshes that are simulated using the Karma engine. Their mass distribution and inertia is determined in real-time, unless you specify otherwise in their Karma Properties. Note that you can only use Rigid Body simulation on static meshes. However, that does not mean that your actor must have DT_StaticMesh set. You can still run a Karma simulation on an actor with DT_Mesh or DT_Sprite or any other drawtype. In that instance, the appropriate drawtype will be displayed, but the physics will still be determined by the static mesh.
Karma Ragdoll Simulation
Karma Constraints are Karma physics interactions between objects. They cause some physical phenomenon between two actors, or an actor and the world. This may be a point around which an actor may pivot but is otherwise fixed to the world, or a springy tether that limits the ability of two objects to move apart, or a myriad of other configurations. Note that the world is assumed to have infinite mass, while other objects will both experience any force that the constraint generates. You can use more than one constraint between the same two actors to create more complex activities. There are 4 main classes of constraint. This page provides only an overview and explanation of the various constraint classes, more information can be found on the individual class pages for those constraints:
This is a "ball and socket" joint. Actors joined by this type of joint cannot change their distance from each other. They can be rotated by outside stimuli and will "swing" around the point at which they are fixed to the other actor. The point at which they are fixed is the point in the world where the KBSJoint is located. (I believe.)
This is a special type of constraint because its location in the world does not mean anything. Its function is to limit rotation angles of objects to KHalfAngle and to do so with specified KStiffness and KDamping. The angle you set will limit rotation of the affected KConstraintActors to 0.5*KHalfAngle in either direction of the starting orientation. The important thing to note is that this constraint limits angle movements from its starting orientation. In other words, when the level starts, the actors (or more accurately their coordinate system) can only increase rotation by 0.5*KHalfAngle or decrease rotation by 0.5*KHalfAngle. If this angle is exceeded, the KStiffness will return it to its limits. If you set the KStiffness very strong objects will be stopped dead (more or less) upon reaching the limit. A very light force will cause a "springy" return to the limits.
This type of constraint allows rotation around one axis only, set by the orientation of the constraint itself. There are 4 types you can set this to, through the KHingeType parameter: HT_Normal, HT_Springy, HT_Motor, and HT_Controlled.
- HT_Normal has no special properties. It will allow the objects to spin freely around the axis of the constraint.
- HT_Springy will apply a torsional force to return the object to a set orientation around the constraints axis.
- HT_Motor applies a constant torsional force to spin the second object around the first, or vice versa.
- HT_Controlled is between an HT_Springy and HT_Motor - it attempts to achieve a certain angle through the use of a motorized hinge. These are similar in their aim but act somewhat differently.
This is a special type of constraint, used for attaching wheels to cars. It combines several types of constraint into one. It saves on constraint usage and runs faster than using several separate constraints. It contains an "HT_Springy" style suspension, "HT_Controlled" style steering, and "HT_Motor" style drive system. You will need one for each wheel, though each wheel will not always have all three of these systems. If a wheel has no drive, the drive settings for that wheel can be left at the default, zero. If it has no steering you can leave the steering as it is if you'd like, but it is recommended that you toggle a special flag that indicates that the wheel is fixed and has no ability to steer. This will protect it from outside interference.
Karma Actors Syntax and Variables
All actors have a variable called KParams. This variable is a pointer to a class of type KarmaParams or one of its subclasses. To set up the Karma physics of an object, set up a KarmaParams class or a subclass of it and assign it to the KParams variable. You must also set the Movement -> Physics to PHYS_Karma. The way that I do this is to put something like the following in the default properties of the object. The specific variables for KarmaParams and it's subclasses can be found on their respective pages, indexed below. You can add or omit properties as required, obviously:
Physics=PHYS_Karma Begin Object Class=KarmaParams Name=KParams0 KMass=8 KStartEnabled=True KFriction=1.5 KLinearDamping=0 KAngularDamping=0 Name="KParams0" End Object KParams=KarmaParams'KParams0'
Subclasses of KarmaParams
Karma Constraints Syntax and Variables
Karma Constraints are spawned and treated as actors in all respects. A few variables must be set, however, in order for them to have any effect on the game world. These parameters are all you should need for setting up Karma constraints in the editor. If you would like to generate them in real-time or use them in other more exotic ways, please refer to the specific class pages for more detail on all of the settings.
- This is the first actor that this constraint should affect. It defaults to none. If KConstraintActor1==none, the constraint will have no effect or behave strangely when KConstraintActor2 != none ... therefore always set this variable first :D .
- This is the second actor that this constraint should affect. It defaults to none. If it is not set to an actor, the constraint will be enforced between KConstraintActor1 and the world – i.e. the constraint will not move location (only rotation). This is a better solution than attaching to a PHYS_None actor because sometimes such actors will cause a crash when attached to a constraint.
- If KConstraintActor1 has skeletal data associated with it, this is the name of the bone that the constraint should affect. This defaults to a blank name. If left blank, the bone closest to the constraint will be used.
- If KConstraintActor2 has skeletal data associated with it, this is the name of the bone that the constraint should affect. This defaults to a blank name. If left blank, the bone closest to the constraint will be used.
- If this is set to true, the two actors joined by the constraint will not collide with each other. They will collide as per their normal collision settings with any other actor. Conversely, if this is set to false, the two actors will collide as they would with any other actor. This property defaults to true.
- This setting dictates the point at which the KForceExceed event should be triggered. This event is triggered on the constraint when the linear force being applied to the constraint exceeds this value. No event is called on the actors involved; only the constraint itself receives the event. If set to 0, the event will never be triggered. This property defaults to 0.
- Forces the constraint to update its local reference frame the next time Karma is updated. (between the end of this tick and the start of the next) This is usually false for things created in the editor, because the editor calculates the local reference frame automatically whenever you change a value in the constraint. It is helpful, however, if the constraint is generated through script, as it automatically calculates the relative positions and angles of the karma actors involved, based on their positions at it's first Karma update. It is a constant variable, so it can only really be set in the default properties. (barring the use of SetPropertyText - behaviour in this scenario is unknown.)
KBSJoint has no additional parameters to set.
- This is the amount of rotation permitted by the limiter from the orientation of the limiter itself. This is set in Unreal units, not Karma units.
- The amount of force applied to stop an object that has exceeded it's limit. The greater this value is, the more force will be exerted to return the constraints' participants to the limit.
- The amount of force that the limiter will absorb from an actor that has exceeded the limit. Without any damping, an actor will 'ricochet' off of the limit when it exceeds it, while a large amount of damping will cause the actor to bounce gently from the limit.
KHinge has a number of properties to set, however some may not be used depending on the setting of HingeType.
- HT_Normal, HT_Springy, HT_Motor, or HT_Controlled. Details of what each is and does are provided above in the general information about KHinges.
- This is the same as KStiffness in KConeLimit. This is how much force will be applied to return the hinge to it's original rotation. This is only used if the hinge is of KHingeType HT_Springy. It defaults to 50.
- This is the amount of damping applied to external forces acting on the hinge. (any force other than that applied by KStiffness) If your spring has no damping, it will swing back and forth endlessly bouncing off objects. The higher this is set, the less outside forces will be able to interfere with the hinge. This applies only to hinges of KHingeType HT_Springy. The default value for this property is 0.
- The desired rate of hinge rotation. This is in Karma units, not Unreal units. If the hinge is of KHingeType HT_Motor or HT_Controlled, the hinge will increase it's rate of rotation as much as possible until it reaches this speed or is stopped by reaching it's desired angle in the case of HT_Controlled, or is shutdown by a trigger or from within code. By default, this is set to 0.
- The maximum amount of torque that the hinge can output, if it is of KHingeType HT_Motor or HT_Controlled. This primarily impacts the acceleration of the hinge up to KDesiredAngVel. This is set to 0 by default.
- If the hinge is of KHingeType HT_Springy or HT_Controlled, this is the angle that it will attempt to achieve, either through spring tension or the application of torque, as applicable. This is in Unreal Units, not Karma units. It's default value is 0.
- This is the same as KDesiredAngle, and is used in lieu of it if KUseAltDesired is set to true. If KUseAltDesired is set to false, the KDesiredAngle is the angle that the hinge will attempt to attain. This variable defaults to 0.
- This is a toggle that selects between KDesiredAngle and KAltDesiredAngle. If it is false, the desired angle is KDesiredAngle. If it is true, the desired angle is KAltDesiredAngle. It defaults to false.
- This is a value used to make smoother movements with KHingeType HT_Controlled. When the difference between the desired angle and actual angle is less than this value, the KDesiredAngVel that is used for the hinge will be interpolated between the actual setting of the variable KDesiredAngVel, and 0. This means that the closer the angle is to what it is desired to be, the slower the joint will move. This value defaults to 8200. If it is too low, the hinge may overshoot the desired angle before it can stop itself. Even if it does not it will result in a very abrupt halt if this value is set too low. Conversely if it is set to high the hinge will behave sluggishly. It is best to experiment with this value to get the desired effect.
- This is a constant variable that indicates the current rotation of the hinge around its axis. When a KHinge starts in a map, its angle always starts at zero. When the hinge rotates, the amount of rotation (in Unreal units - i.e. 65535 = 360 degrees) is added to KCurrentAngle. Think of it as just another coordinate system, but where the hinge is at the origin and the rotation is 0. When the coordinate system rotates, all the KConstraintActors in it rotate as well. However, the cool thing is that these KConstraintActors are actually rotating the coordinate system using physics which then causes an update to KCurrentAngle. Because KCurrentAngle is constant, you can only read this value to find the rotation of the hinge. Attempting to change the value of this variable cannot rotate the hinge.
You can set the state of the hinge to further control its behavior. In it's default state it acts as described above. There are four special states that will respond to being triggered by UnrealScript or something in a map. They are ToggleMotor, ControlMotor, ToggleDesired, and ControlDesired.
- When triggered, KHingeType will be changed to HT_Motor, if it is not already. If it is set to HT_Motor already, it will change to HT_Controlled and KDesiredAngle will be set to its current rotation. KUseAltDesired will be set to false as well. Generally you would use this to toggle between a motor rotating and being locked into position. It will set the KHingeType to HT_Controlled when it is first created, though it will not set it's own KDesiredAngle. It will set KUseAltDesired to false when it is first spawned/the map is loaded.
- This is similar to ToggleMotor, but works slightly differently. When triggered, it will set KHingeType to HT_Motor. When untriggered, it will set KHingeType to HT_Controlled, KDesiredAngle to the current angle, and KUseAltDesired to false. However, it will only do so if the current type is HT_Motor. Usually it would be as it was set that way when it was triggered, but in the case that some other code has changed it before it was untriggered, it will not change any settings when untriggered if it is not an HT_Motor at the time.
- This is a simple type, when triggered it will toggle KUseAltDesired. It does not modify KHingeType or KDesiredAngle, nor does it do any setup when spawned/the map is loaded.
- Similar to ToggleDesired, when triggered, KUseAltDesired will be set to true. When untriggered, KUseAltDesired will be set to false. It does not modify KHingeType or KDesiredAngle, nor does it do any setup when spawned/the map is loaded.
Note also that HT_Springy and HT_Controlled will "sleep" the simulation once they have settled at their desired location. Subsequent changes to the KDesiredAngle will have no effect until one or both of the constrained actors is awakened through KAddImpulse, collision with another Karma actor, or KWake. If you are using one of the "states" listed above to control the hinge it will be done for you, but if, for instance, you wanted a hinge to track something and thus it constantly changed it's KDesiredAngle, you will need to awaken the actors it constrains as appropriate.
Karma Functions and Events
There are a great deal of functions and events associated with Karma. Some are commonly used while others are of questionable
value. Due to the sheer number of functions they have been given their own page: Karma Functions and Events.
Avoid screwing with these. These are internal parameters used by the Karma Engine. You can modify them but be aware that although they are declared in Actor, they are global and will persist even after you exit the level. You must restart UT2003 in order to reset these to the defaults. These affect things like leeway in collision and when a Karma simulated object is deemed to have suffered a SimError - which generally results in it's destruction. KGetSimParams and KSetSimParams can be used to get and set the current SimParams if you are so inclined. Most of these should not be changed, because doing so generally only causes bad things to happen. However, some can be tweaked.. but note that Epic has of course already tweaked them extensively so unless you have a specific application that requires the tweaking, you will probrably only make things worse by changing them. Regardless, here goes..
- Supposedly this makes things push apart harder when they penetrate, though no indication is given as to whether a larger or smaller value causes this behaviour more. It's referred to as the relaxation constant, which implies the latter. Defaults to 6. I don't believe negative values are valid.
- This is the "constraint compliance." The comment in the script says it makes joints softer or harder, but don't be fooled! This is actually the amount of "slop" allowed in the contraint simulation. EG, a KHinge is supposed to allow rotation around one axis, and one axis only. This controls the amount of rotation allowed around other axes. By default, this is set to 0.001. No slop is really noticeable under normal conditions with this value, but can be observed by making a very large Karma simulated actor and applying a strong impulse to it, perpindicular to the axis it's "allowed" to rotate around by the KHinge. 0.005 results in noticeable slop for even small actors, while 0.0002 appears to be very precise. I highly doubt negative values are valid. Changing this value does not appear to impact performance, however lower (more precise) tends to cause jiggling in ragdolls. If it's too low, (lower than about 0.000025) Karma objects will not simulate at all. You shouldn't normally need to change this, unless you are dealing with very large Karma actors and strong impulses or collisions with other large Karma actors.
- This appears to be added to the actual penetration for all intensive purposes when the object is consider "at rest." IE, when it's not moving, it's assumed to be penetrating the world by this much, meaning it has to be "dislodged" by some amount of force in order to move it. This helps to keep actors at rest resting, instead of the simulation being restarted from the slightest thing. Default is 0.01. Obviously, larger values make actors more dificult to "dislodge" from rest. Negative values might not be allowed, but if they are, they would probrably make things appear to vibrate when "at rest."
- Penetration is multiplied by this amount. So, if two actors are interpenetrating by 1 UU, the Karma system will consider them interpenetrating by 2 UUs. Since interpenetration is caused by collision, this causes objects to appear to have impacted each other much harder (or softer, if the value is lower) than they really did. The default is 1. Negative values are probrably not possible, but if they are, would they create a black hole effect?
- Supposedly, this is the softness of contact constraints. However, with no inforation as to what a contact constraint is, I can't say much about this. The default is 0.01.
- The maximum amount of penetration allowed. Since penetration is generally the result of collision, penetration is assumed to be the result of collision if it is below this amount, and the objects will "bounce" off each other with stiffness and such determined by their KParams. Above this amount, the two objects are assumed to in fact not be colliding, but instead one is impaled on the other and should not bounce off but instead remain within the other actor. The default is 0.1. (This is in Karma units I think, so it translates to about 5 UUs.) Note that impalation in this manner does not guarantee that the actors will stay together - they still simulate separately. The penetration is merely assumed to be not the result of a collision, so the actors won't "push off" each other.
- This is the maximum "tick" allowed to the Karma system. So, if it was 100 ms, and you were getting 1 fps, the simulation would update 10 times per frame with a "deltatime" of 100ms each time. This does not actually affect the Tick in script, but the analogy was provided for illustrative purposes. If your framerate is sufficiently high, then the Karma simulation will just update once every frame. The default is 0.04, which is probrably 40ms, so if you are getting more than 25 fps, Karma updates once per frame. Lower values will give a more precise simulation, but are likely directly proportional to the performance of the Karma system. Note that there appears to be a maximum number of updates per tick, so if you set this too low (too precise of simulation) the simulation will be slowed down to fit within the maximum updates per tick.
O-GL: Are you sure that the Karma physics can be simulated more than 2 times per frame? When I was getting really low fps (ie 1-4), the karma simulation was running like in slo-mo....
Foxpaw: Karma seems to run properly on my machine at low fps, but I suppose there's no way to know for SURE how many times it's simulating per second. Or is there? :P Maybe KUpdateState or some function like that gets caled every time the simulation is updated. Then again, it might just get called once a tick regardless.
Foxpaw: I got curious so I played around with more extreme values of MaxTimeStep. It appears that there is a limit on the number of times it gets updated per tick. I tried setting MaxTimeStep to 0.004 which resulted in approximately 2 and a half times the number of cycles expended by Karma. The simulation appeared to be slowed to approximately a third of it's usual rate. So, it appears there is a cap on the number of times it can get updated per tick, and the lowest admissable MaxTimeStep seems to be about 0.015 or so, at a fast framerate. That wouldn't leave much leeway for slower machines though - or so I would think. By your comment below it appears much lower values may be possible without slowdown if you have a faster machine or less Karma to simulate.
O-GL Nah man, when I set it to 0.001, that means I would need to be getting 1000 fps to run Karma at normal speed (with slomo=1). I was getting about 40fps and it too friggin ages to move anywhere ^^. When I set MaxTimeStep to 0.005, I should theoretically need 200fps to run smoothly. When running smoothly, I should move along at 400UU/sec due to my acceleration/deceleration code. At 50fps, I move along at 100UU/sec and at 30fps I move along at about 60UU/sec. This was done with a custom speed measurer because, for some reason RBstate.LinVel still says I'm moving along at 400 (there must be something wrong with LinVel cos it should be using MaxTimeStep if DeltaTime>MaxTimeStep). Thanks to this MaxTimeStep, I can (hopefully) get a good basis for my lockstep multiplayer code (I owe you big-time Foxpaw) ... but thats a story for my journal. I should also state that if I slow the game speed to say "slomo 0", the Karma will have a chance to meet the low TimeStep because time is stepping alot slower per frame, and therefore, at slow times, you can actually move more UU per "second".
- This is fairly straightforward, and is the maximum allowed speed of any Karma actor. It's a bit low by default. The default is 2500. Making it higher will make the simulation more accurate, but if a Karma actor hits something going too fast, it could get lodged in it. (By exceeding MaxPenetration in a single update.) In theory, you should be able to avoid all kinds of Karma craziness by limiting your maximum MaxKarmaSpeed. Using the equation MaxPenetration per second = MaxPenetration/MaxTimeStep = 5/0.04 = 125 UU per second, you could set MaxKarmaSpeed = 125 and possibly stop some items from being lodged in other items. MaxKarmaSpeed has an interesting condition associated with it: if a fast moving Karma actor is revolving around another fast moving Karma actor and the two are joined by a constraint, the constraint can "explode." (That's my term for it) The two pieces are sent flying away from each other at MaxKarmaSpeed for no apparent reason. I would hazard that perhaps some int or float is rolling over in some of the Karma systems internal math, and the velocities/rotation rates required appear to be directly related to framerate. At any rate, lower MaxKarmaSpeeds prevent objects from travelling at extreme velocities, limiting this behaviour.
O-GL: That?s too slow for me considering my pawn travels at around 400 UU per second. I?m gonna try changing MaxPenetration and see if I don?t get stuck in walls :D. Thanks for doing KSimParams FoxPaw, I never would've been bothered with it otherwise :cheesy:.
Foxpaw: Yes, 125 is barely moving.. I bumped mine up to 25000 and have yet to have any lodging problems. Granted, though, collisions usually call KImpact, which in my case usually destroys the actor (later, in tick, to prevent crashing) so that may contribute to the lack of lodging as well.
O-GL: Unfortunately, I can still walk right through a few of the buggy walls in Antalus :'(. Even with MaxTimestep=0.001 and MaxPenetration=0.1. I think it's just the levels's fault or something because I walk slowly inside the wall and can sorta step out of the world to where my screen freezes. I'm just gonna have to make a hack up to where I use cylinder collision to see if I'm inside a wall, and if so, re-spawn out of it.
Foxpaw: I'm not sure which walls these are that are causing the problem, but if they're terrain, it's probrably because terrain is infinitely thin so I'm not sure how the penetration is figured in that case.
Baz: The MaxKarmaSpeed and MaxRagdollSpeed properties have been removed in 2004. I assume this means there's no way of changing the max speed now?
Foxpaw: MaxKarmaSpeed appears to have been changed to KMaxSpeed in UT2004.
- Similar to MaxKarmaSpeed, this one's for Ragdolls. The comments suggest that if this was higher than MaxKarmaSpeed, the MaxKarmaSpeed would still stop the ragdoll from going any faster, but I haven't tested that. It defaults to 800, which is rediculously slow, since you can knock a ragdoll off a high building and then jump after it and you will rocket past it. The default speed of 800 is a bit floaty, but there again, you may encounter penetration issues if you make it too high without dropping the MaxTimeStep, which would cause a drop in performace.
- This is a setting in Actor - it defines whether the actor will block Karma simulated objects. Usually this should be the same as bBlockActors, though it is not automatically set as such.
IMPORTANT when using constraints in UnrealED for UT2003 (with patch 2225)
When you move or rotate a KConstraintActor of a constraint in UnrealED, the constraint WILL NOT recognize its new location or rotation. If you play your map, you will probably get unexpected results like KConeLimit using different angles or KHinge moving the KConstraintActors back to an older position you set in UnrealED. What I have found updates the constraint is to simply change and save a variable in each of the contraints using the KConstraintActor. For example, find a redundant variable such as "Buoyancy" in your constraint or another variable that will not affect much, change it, save, and then run the map. You will find that the constraint and KContraintActor are updated and now functioning correctly.
- Joints Coding
- Karma Authoring Tool
- Karma Ragdoll
- Linking Karma Ragdolls
- Karma Ragdoll Injury System
- Mod Ideas/KarmaCrossfire
- Modifying The Bulldog
- Skeletal Mesh
- Collision Detection
Tarquin: You might want to think about splitting this page up, as it's getting pretty long. It seems to me that you could have this page as a general intro, and then have Ragdoll Simulation and Rigid Body Simulation.