Search the Community
Showing results for tags 'Physics'.
The search index is currently processing. Current results may not be complete.
-
I love the physics in games like Amnesia and the Half-Life series. Realistic interactive physics add a degree of interactivity that immerses the player in the world like nothing else, and I plan on physics being a key feature in our SCP game. In this article, I will walk you through the steps I took to make a breakable crate. The Breakable Model As usual, I like to start with the artwork, because it's key to everything. I usually learn so much just by using actual game-ready artwork, rather than blocky "programmer art". Plus, at the end I get something I am proud of showing off. @reepblue created this model and provided it for use with Leadwerks, and @Rich D textured it and broke it up into fragments, for your destructive enjoyment. In fact, breakable objects are primarily an art problem. The basic concept is very simple: When an object is damaged, hide it and replace it with a series of fragments that represent the object broken into pieces. This is not a complicated programming problem at all, but let's dig into the details and see what happens. Colliders The first question is how the colliders for the model fragments should be made. If your colliders fit too perfectly, the broken object will form a strong interlocking structure, and won't fall apart unless a large force is applied to it. Even worse, if your fragment colliders intersect the moment they are enabled, the object will form explosive forces that could send the crate or the player flying! We definitely don't want this happening. The main question is, should fragments of the same object even intersect at all? I was curious how Half-Life 2 handled this, so I fired it up, broke a crate, picked up one of the pieces, and saw that fragments of a broken object don't collide with each other at all! Here I am holding one piece, and it freely intersects the other fragments with no collision, even if I drop it. I have 36 hours of play time in this game, and I never noticed this until now! Well, if this solution is good enough for Valve, then I don't feel a need to make it more complicated than it needs to be. This simplifies our goals because we can just set the collision type of the fragments to "Debris", which will collide with the Prop and Scene collision types, but not with the player, and not with other broken pieces. That makes it perfect for this use, and we don't have to worry too much about the exact shape of the colliders for each piece of our broken object. If the fragment colliders overlap, it won't cause explosive forces or lock the pieces into place like a jigsaw puzzle. The next question is how should the content be structured? I have a single-mesh unbroken version of the model, as well as a version that contains all the fragments separated into child limbs. I could save each limb as a separate model and maybe use some naming scheme to identify them, like crate_frag1.mdl, crate_frag2.mdl, etc. However, I think the simplest way to handle this is to make the unbroken version of the model the top-level entity in the hierarchy, and then just add the fragments as children in the model file. The script will just make the assumption that all children in the model are fragments, and hide them when the game starts: Unbroken mesh Fragment 1 Fragment 2 Fragment 3 I think this design accounts for the most likely use case, and making it more complicated would be more trouble than its worth. I noticed that in the current build of Leadwerks Editor, the model editor only allows assigning a collider to the whole object. Let's fix that, so we can assign a separate collider to any object in the hierarchy! I performed that for each limb in the broken crate model. I then opened the unbroken crate model, and assigned a box collider to it, and saved it: Merging the Models Now how to we combine these two models? An artist might want to export the two models to glTF and combine them in Blender or another program, but since I am a programmer, the easiest solution for me is to just write a simple script. local mdl1 = LoadModel(nil, "Models/Crate/crate01.mdl") local mdl2 = LoadModel(nil, "Models/Crate/cratebroken01.mdl") while #mdl2.kids > 0 do local child = mdl2.kids[1] child:SetParent(mdl1) end mdl1:Save("Models/Crate/Crate01_Combined.mdl") The only thing to watch out for is that when were call SetParent we are modifying the list of objects we are iterating through. Therefore, it's best to use a while loop and just get the first child each time. This tripped me up for a few minutes because I started with a simple for loop! Here's the wrong way that I tried first: --Don't do this! for n = 1, #mdl2.kids do local child = mdl2.kids[n] child:SetParent(mdl1) end I just copied and pasted my script code into the editor console, pressed enter, and it saves the model with the unbroken single mesh as the top-level limb, and all the fragments as children. Separate colliders for each limb are retained correctly: Awesome! Now that the artwork is correctly prepared, the actual code this requires will be very easy. Breakable Object Script Let's create a new entity script called "Breakable" in the Physics folder. We'll add a property for health, and another for the speed of a collision that will cause the object to break: Breakable = {} Breakable.health = 10 --"Health" Breakable.breakspeed = 10 --"Break speed" In the Start function, we'll just hide all the child limbs, since we have decided already that those will form the fragments of the object when it breaks: function Breakable:Start() local n for n = 1, #self.kids do --No problems using a for loop here since we are not modifying the entity limbs ;) self.kids[n]:SetMass(0)--This should already be zero, but if not let's set it automatically self.kids[n]:SetCollisionType(COLLISION_NONE) self.kids[n]:SetHidden(true) --self.kids[n]:SetColor(0,4,0,1) --optional visual hint for testing end end We will add a function so that the object will accept damage if something hits it. Most likely this will be caused by the player shooting or hitting object with a melee weapon, but if an enemy attacks the crate for any reason, it will also work. function Breakable:Damage(amount) if self.health > 0 then self.health = self.health - amount if self.health <= 0 then self:Break() end end end In the collide function, we can add a check to see if the break speed was hit, and break the object if it was: function Breakable:Collide(entity, position, normal, speed) if speed > self.breakspeed then self:Break() end end Finally, we add our Break function, where the object finally gets broken apart into pieces: function Breakable:Break() if self:GetHidden() then return end --Make sure this only gets broken once if #self.kids == 0 then return end --If there are no kids then don't do anything --Hide the object self:SetHidden(true) --Calculate simple mass for each piece local mass = self:GetMass() if mass == 0 then mass = 5 else mass = mass / #self.kids end --Create a table to store the fragments in self.fragments = {} while #self.kids > 0 do local child = self.kids[1] child:SetParent(nil) child:SetMass(mass) child:SetHidden(false) child:SetCollisionType(COLLISION_DEBRIS) table.insert(self.fragments, child) end end Running the Game I was very excited when I ran the game for the first time, in a level with a crate hooked up to this script. What would happen if I shot the object? Would it break into pieces and look natural, like I hoped it would? Here is the result: Wow, on the first try it looks great! Adding sounds for collision and breaking would enhance the feel of it even more, and I plan to add this next. I also plan to try adding some velocity to the broken fragments, so the crate has more of an explosive appearance, instead of just falling apart in place. Here is the complete script, with the accompanying properties definition file. The script requires a game created with the current "beta-e" branch on Steam: Breakable.luaBreakable.json And here are the model files I used. Crate01_Combined.mdl is the one that is ready-to-use with this script: Crate01.zip I even find the code itself for this very beautiful, for some reason. The weird comments with the @ signs are annotations that give Lua Language Server a hint about what type each parameter is, for nice auto-completion in our script editor. These comments are automatically generated when the script is first created. ---@class Breakable : Entity Breakable = {} Breakable.health = 10 --"Health" Breakable.breakspeed = 10 --"Break speed" ---@param self Breakable function Breakable:Start() local n for n = 1, #self.kids do --No problems using a for loop here since we are not modifying the entity limbs ;) self.kids[n]:SetMass(0)--This should already be zero, but if not let's set it automatically self.kids[n]:SetCollisionType(COLLISION_NONE) self.kids[n]:SetHidden(true) --self.kids[n]:SetColor(0,4,0,1) --optional visual hint for testing end end ---@param self Breakable ---@param entity Entity ---@param position Vec3 ---@param normal Vec3 ---@param speed number function Breakable:Collide(entity, position, normal, speed) if speed > self.breakspeed then self:Break() end end ---@param self Breakable ---@param amount number function Breakable:Damage(amount) if self.health > 0 then self.health = self.health - amount if self.health <= 0 then self:Break() end end end ---@param self Breakable function Breakable:Break() if self:GetHidden() then return end --Make sure this only gets broken once if #self.kids == 0 then return end --If there are no kids then don't do anything --Hide the object self:SetHidden(true) --Calculate simple mass for each piece local mass = self:GetMass() if mass == 0 then mass = 5 else mass = mass / #self.kids end --Create a table to store the fragments in self.fragments = {} while #self.kids > 0 do local child = self.kids[1] child:SetParent(nil) child:SetMass(mass) child:SetHidden(false) child:SetCollisionType(COLLISION_DEBRIS) table.insert(self.fragments, child) end end If you are looking for custom artwork for your game, I can wholeheartedly recommend Rich DiGiovanni's services. I have worked with Rich on many projects, and I always get great results at a reasonable price: https://richdigiovanni.com/
-
I love the physics interactions in Half-Life 2, Penumbra, and similar games, and I want this to be a key part of gameplay in our upcoming SCP game. Physics interactions should should be everywhere, all around the player, so that the world feels alive and interactive. Furniture is a great way to add physics interactions to your game, because each object can be set up one, and many instances can of the object can be placed in your levels. Cabinets and doors can swivel open and closed using a hinge joint. Drawers can slide in and out using a slider joint. This simple interactivity can turn a search for an important item into a mini-game, and encourages the player to explore the world in hopes of finding a valuable item. I decided to start with a filing cabinet that @Rich created. Each drawer is a separate limb in the model, so they can move independently. Colliders This item will require five different oddly-shape colliders. One for the cabinet, and another for each of the four drawers. I want to be able to place items in the drawers and close the drawers, while still having collision with the exterior of the cabinet, so the colliders need to be pretty detailed. The first thing I did was open the model in the model editor, delete everything except the bottom drawer, collapse it, then save that model as a separate drawer model. Because the drawers are dynamic objects, they could not use polygon mesh colliders. There isn't a single collider shape that is appropriate for drawers, and a convex hull would not give me the abililty to place items in the drawer. What I needed was a multi-part collider made up of convex hulls. I added the drawer model to a new scene, positioned it as (0,0,0), and started building a collision shape out of brushes. I saved this as a map file and will keep it in case I want to modify the collider a bit more in the future: My minimum thickness here is four centimeters, a number I will keep in mind in the future and revise if it turns out this is needed after play testing. I selected all the brushes, right clicked in the scene editor, and selected the "Save as Model" popup menu item, and saved a MDL file. Now I had a model file with separate limbs for the different convex hulls I wanted to make the shape out of. I did the same thing for the cabinet exterior. Entities that are connected to each other with a joint will not collide with each other, fortunately, so we don't have to worry about making the drawer and cabinet colliders match exactly. If the drawers intersect the file cabinet collider a little bit, it's not a problem. The individual drawers will collide with each other, since they are not connected to the same joint, so take care that they do not interpenetrate. I am not sure yet if I want the file cabinets to be immovable or dynamic. Since it's not difficult, I will make the collider out of separate convex pieces so I can make it dynamic in the future if I decide I want to. Now we are ready to add the colliders to the models. I opened the drawer model in the model editor and selected the new File > Load Dynamic Collider menu item. This will load a model and turn each limb into a separate convex hull, for multi-part colliders. It worked perfectly! For this to work as expected, you usually need the root limb in your visual model to be positioned at (0,0,0), with rotation (0,0,0), and scale (1,1,1). You may get weirdly rotated, offset, or scaled results if this is not the case. Fortunately, you can reset the model's transformation matrix just by selecting the Tools > Reset Transform menu item. You might need to do this to the visual model, the collider model, or both! Now that I had my individual models, with colliders, I added the file cabinet to the scene, added four drawers, positioned them each in the right place, and parented them to the cabinet. Positioning drawers was actually pretty easy and their placement does not have to be exact, so don't get obssessive about making them perfect. I saved the assembly as a prefab, and now I can place the cabinet anywhere I like. I could even save this assembly as a new model file, and it would retain the colliders for each limb, but I think I am going to leave it in its separate pieces, referencing the file cabinet and draw models. This way I can easily modify those models in the future, if I am not happy with how their colliders are shaped. Since I ended up just using one repeating drawer model, I recommend all furniture in the future be exported from the modeling program this way: the cabinet object with no drawers, doors, or whatever moving parts there are, and then one copy of the drawer or door. Since it's pretty easy to line the drawers up in the right place, you don't even need any nodes to hint where they are supposed to be placed. Joints The next step was to add a slider joint for each drawer. I added a new class that just creates a slider joint. There are two pieces of information I am interested in, the joint pin and the limits. The pin defines the vector the joint slides along, and the limits determine where the joint starts and stops. I want the drawer to be locked to the cabinet so it can't be pulled out all the way, and the joint limits will help me achieve that behavior. I could add more properties in the future, but for now let's just keep it simple. SliderJoint.zip In the Start method, a slider joint is created between the entity and its own parent. If there is no parent, this is actually okay. The joint will just be created as if a non-movable parent object is used. void SliderJoint::Start() { auto entity = GetEntity(); if (entity->GetMass() == 0.0f) { Print("Error: Slider joint requires entity to have non-zero mass"); return; } auto gpin = TransformVector(pin, entity, NULL); joint = CreateSliderJoint(entity->GetPosition(true), gpin, entity->GetParent(), entity); if (limits[1] > limits[0]) joint->SetLimits(limits[0], limits[1]); } I created a simple scene with a floor, a pivot for the player start position, and our filing cabinet. I selected one drawer, pulled it out a bit, set the pass to 2.0, and added the slider joint to it. If it works, I should be able to walk into the drawer and push it back into the cabinet. When I run the game, I am able to push the drawer in a bit, but it seems like it is hitting something when it gets close to the filing cabinet. Since the drawer does not collide with the cabinet, it must be hitting the other drawers. I went back to the editor and moved the drawer above and below out of the way, to make sure that was actually the problem: When I ran the game I could push the drawer in all the way, and then some. Because there is no collision between the drawer and its parent, the cabinet itself, there is nothing to stop the drawer unless I set the joint limits. I'm not worried about this, so it's fine for now. I'm more worried about these collidring drawers! If I pull the other drawers out all the way, the problem becomes can be easily seen. The front panel overlaps with the drawer above it. How should I resolve this? I already said I want 4 centimeters to be my minimum thickness, since very thin objects could potentially have problems with small objects falling through them. Fortunately, I still have my map file where I created the drawer collider shape. I decided to just lower the height of the front piece so that it matched the sides and the back. Then I saved the brush objects as a model, and loaded it back into the fiel cabinet drawer model as a dynamic collider. We might change this design in the future, but for now I just want to get it working! Here is the drawer with the new collider loaded: When I load my scene back up, it looks like the drawers have plenty of clearance. The game now works as expected. I can push the drawer in all the way without hitting anything. I added the slider joint to the other drawers and set their mass to 2.0, and all the drawers could now be pushed freely. I discovered an interesting problem while doing this! It's easy for the drawers to form a stair case shape. When this happens, the player will step almost instantly up all the drawers, to the top of the cabinet! In the future, maybe we will need a per-entity setting that makes it so an entity is non-steppable. For right now, I am not too concerned about it because I want to manually push and pull these drawers, not lean into them. Tactile Physics I love the player physics in Amnesia: The Dark Descent, and I wanted to bring this type of interaction to our game. It seemed like it would be achievable, since we are using the same excellent physics library, Newton Game Dynamics, that is in use by Frictional Games. We already have logic in our player class for picking up objects, and to my delight this mostly worked with the drawers. When a new object is picked up, a kinematic joint is created. The kinematic joint will then exert force and torque on the object the player is grabbing. if (pickedentity) { float mass = pickedentity->GetMass(); if (mass > 0.0f and mass < maxcarrymass + maxcarrytolerance) { carryjoint = CreateKinematicJoint(pickedentity->GetPosition(true), pickedentity); carryjoint->SetMaxForce(1000); carryjoint->SetMaxTorque(1000); carrycollisiontype = pickedentity->GetCollisionType(); pickedentity->SetCollisionType(COLLISION_DEBRIS); carryposition = TransformPoint(pickedentity->GetPosition(true), NULL, camera); carryposition.z -= 0.1f; carryrotation = TransformRotation(pickedentity->GetQuaternion(true), NULL, camera); pickedobject.reset(); } } I made a small improvement to disable torque if the picked up object has any joints attached to it. This makes sense, because you don't want to rotate the drawer, just move it on one axis: if (not pickedentity->GetJoints().empty()) carryjoint->SetMaxTorque(0); When the game is played, the physics basically work the way I wanted. Of course there are no joint limits here, so there is nothing from stopping the drawers from going through the cabinet, or being thrown very far away. I set the joint limits property to [-40, 0], since the drawer starts closed by default. When I ran the game again, here was the result. You can see the joint limits are working as intended. The behavior is not perfect. It's jumpy and glitchy. I might need to treat jointed objects a little differently than objects you pick up. But it's basically working, and I am confident with more testing and refinement it will turn into a polished and fun game mechanic. The last step was to save my assembly as a new prefab, now that I am pretty sure it's done. Now I can place an infinite number of file cabinets, with an infinite number of interactive drawers, and force the player to search through them all! The true horror of the SCP beauracracy awaits you! Bwahahahahahaha!
-
I'm not sure what's exact conditions when it does not work but manage to reproduce it when model with detection collider is a green in debug before target brush moves beside its area and in a release is a red (not detecting) 90% of time in same conditions when it should be a green. btw i don't know why model is not transparent. Somehow it's still working in my game but not in example. #include "UltraEngine.h" #include "ComponentSystem.h" using namespace UltraEngine; int main(int argc, const char* argv[]) { //Get the displays auto displays = GetDisplays(); //Create a window auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR); //Create a framebuffer auto framebuffer = CreateFramebuffer(window); //Create a world auto world = CreateWorld(); //Create a camera auto camera = CreateCamera(world); camera->SetClearColor(0.8); camera->Turn(35, 0, 0); camera->Move(2, 0, -2); camera->SetRefraction(true); //Create light auto light = CreateBoxLight(world); light->SetRange(-20, 20); light->SetArea(20, 20); light->SetRotation(35, 35, 0); auto unlitMaterial = CreateMaterial(); auto unlitShader = LoadShaderFamily("Shaders/Unlit.json"); unlitMaterial->SetShaderFamily(unlitShader); int width = 2, height = 1, length = 3; auto model = CreateModel(world); auto mesh = model->AddMesh(); mesh->AddVertex(0, 0, 0); //S mesh->AddVertex(-width * 0.5, -height * 0.5, length);//NW mesh->AddVertex(width * 0.5, -height * 0.5, length);//NE mesh->AddPrimitive(2, 1, 0);//S , NW, NE mesh->AddVertex(-width * 0.5, height * 0.5, length);//NW h mesh->AddVertex(width * 0.5, height * 0.5, length);//NE h mesh->AddPrimitive(0, 3, 4);//S , NW h, NE h mesh->AddPrimitive(0, 1, 3);//left mesh->AddPrimitive(4, 3, 1); //"face" mesh->AddPrimitive(2, 4, 1); //"face" mesh->AddPrimitive(0, 4, 2); //"right" auto& mat = unlitMaterial; mat->SetTransparent(true); model->SetMaterial(mat); model->SetColor(0.5f, 0.8f, 0, 0.25f); model->SetPosition(0, 0, 0); auto collider = CreateConvexHullCollider(mesh); model->SetCollider(collider); Vec3 targetPos(0, 0, 0); auto box = CreateBox(world, 0.1f); box->SetPosition(targetPos); auto mover = box->AddComponent<Mover>(); mover->movementspeed.x = 1.5f; model->Turn(0, 90, 0); //Main loop while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false) { targetPos = box->GetPosition(); auto newTargetPos = TransformPoint(targetPos, NULL, model); bool isInside = model->GetCollider()->IntersectsPoint(newTargetPos); if (isInside) { model->SetColor(0, 1, 0, 1); } else { model->SetColor(1, 0, 0, 1); } world->Update(); world->Render(framebuffer); } return 0; }
-
The code below gives 3 different error messages (if you press ignore each time). Here's the first one; Only get the errors in debug mode. In release mode the object is shot away at speed. #include "UltraEngine.h" using namespace UltraEngine; int main(int argc, const char* argv[]) { auto displays = GetDisplays(); auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR); auto framebuffer = CreateFramebuffer(window); auto world = CreateWorld(); auto camera = CreateCamera(world); camera->SetClearColor(0.125); camera->SetPosition(0, 0, -8); camera->SetDebugPhysicsMode(true); auto light = CreateDirectionalLight(world); light->SetRotation(35, 35, 0); auto box = CreateCylinder(world, 0.5f, 1.8f); box->SetMass(1.0f); box->SetColor(0, 1, 0); auto floor = CreateBox(world, 10, .1, 10); floor->SetPosition(0, -2, 0); auto joint = CreateKinematicJoint(box->position, box); joint->SetMaxForce(100); joint->SetMaxTorque(100); auto pivot = CreatePivot(nullptr); float a = 0, y = 0; while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false) { pivot->AlignToVector(Vec3(0, 0, -1)); auto quat = pivot->GetQuaternion(); joint->SetPose(Vec3(0, y, 0), quat); world->Update(); world->Render(framebuffer); } return 0; }
-
Here's a small example on how physics on a moving object can be achieved. The physics objects are different to their visual counter parts so that they can be calculated without any rotation. Then using matrix magic the physics position and rotation can be transformed to any moving target. #include "UltraEngine.h" using namespace UltraEngine; Vec3 mousepos = Vec3(0.0f); float move_adjustment = 0.1f; float move_speed = 1.0f; float lookspeed = 0.1f; float looksmoothing = 0.5f; bool enable_camera = true; void UpdatePhysics(shared_ptr<Entity> source, shared_ptr<Entity> target, shared_ptr<Entity> platform = nullptr) { auto pos = source->GetPosition(); auto rot = source->GetRotation(); if (platform != nullptr) { auto matrix = platform->GetMatrix(); matrix = matrix.Inverse(); matrix.t = Vec4(0, 0, 0, 1); pos = TransformPoint(pos, Mat4(), matrix); rot = TransformRotation(rot, Mat4(), matrix); } target->SetPosition(pos); target->SetRotation(rot); } int main(int argc, const char* argv[]) { auto displays = GetDisplays(); auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR); auto world = CreateWorld(); auto framebuffer = CreateFramebuffer(window); auto camera = CreateCamera(world); camera->SetClearColor(0.125); camera->SetFov(70); camera->SetPosition(0, 10, -5); auto light = CreateDirectionalLight(world); light->SetRotation(35, 45, 0); auto floor = CreateBox(world, 10.0f, 0.1f, 10.0f); floor->SetCollider(nullptr); floor->SetPosition(0.0f, -3.0f, 0.0f); floor->SetMaterial(LoadMaterial("Materials\\Developer\\grid01.mat")); auto box = CreateBox(world); box->SetCollider(nullptr); box->SetPosition(2.0f, 2.0f, 0.0f); auto ref = CreateBox(world); ref->SetCollider(nullptr); ref->SetPosition(11, 0, 0); auto player = CreateCylinder(world); player->SetCollider(nullptr); player->SetPosition(0.0f, 5.0f, 0.0f); auto cam_pivot = CreatePivot(world); cam_pivot->SetParent(player); cam_pivot->SetPosition(0.0f, 1.8f, 0.0f); camera->SetParent(cam_pivot); camera->SetPosition(0.0f, 0.0f, -3.0f); camera->SetDebugPhysicsMode(true); //Create physics objects auto f2 = CreatePivot(world); auto ff2 = CreateBoxCollider(10.0f, 0.1f, 10.0f); f2->SetCollider(ff2); f2->SetPosition(0, -3, 0); f2->SetCollisionType(COLLISION_SCENE); auto b2 = CreatePivot(world); auto bb2 = CreateBoxCollider(1.0f, 1.0f, 1.0f); b2->SetCollider(bb2); b2->SetMass(1.0f); b2->SetPosition(2.0f, 2.0f, 0.0f); b2->SetCollisionType(COLLISION_PROP); //player physics object auto player_phys = CreatePivot(world); auto cyl = CreateCylinderCollider(0.5f, 1.0f); player_phys->SetCollider(cyl); player_phys->SetPhysicsMode(PHYSICS_PLAYER); player_phys->SetCollisionType(COLLISION_PLAYER); player_phys->SetMass(1.0f); player_phys->SetPosition(0, 5, 0); bool stage = 0; while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false) { if (window->KeyHit(KEY_F2) == true) { camera->SetWireframe(!camera->GetWireframe()); } if (window->KeyHit(KEY_F3) == true) { camera->SetDebugPhysicsMode(!camera->GetDebugPhysicsMode()); } if (window->KeyHit(KEY_F4) == true) { enable_camera = !enable_camera; } if (enable_camera) { auto _displaySize = window->GetSize(); float cx = Round((float)_displaySize.x / 2.0f); float cy = Round((float)_displaySize.y / 2.0f); auto mpos = Vec3(window->GetMousePosition().x, window->GetMousePosition().y, window->GetMousePosition().z); window->SetMousePosition(cx, cy); mpos = mpos * looksmoothing + mousepos * (1 - looksmoothing); auto dx = (mpos.x - cx) * lookspeed; auto dy = (mpos.y - cy) * lookspeed; auto camrot = cam_pivot->GetRotation(); camrot.x += dy; camrot.y += dx; cam_pivot->SetRotation(camrot); mousepos = mpos; } floor->Turn(0.0f, 0.1f, 0.0f); auto y_angle = cam_pivot->GetRotation().y; auto move = 0.0f, strafe = 0.0f; if (window->KeyDown(KEY_W)) { move = 1.0f; } else if (window->KeyDown(KEY_S)) { move = -1.0f; } if (window->KeyDown(KEY_A)) { strafe = -1.0f; } else if (window->KeyDown(KEY_D)) { strafe = 1.0f; } player_phys->SetInput(y_angle, move, strafe); if (window->KeyHit(KEY_P)) { b2->AddForce(0,200,0); } UpdatePhysics(b2, box, floor); UpdatePhysics(player_phys, player, floor); world->Update(); world->Render(framebuffer); } return 0; }
-
I want to be able have physics working on various different platforms that each have there own velocity and rotations happening. (In this example I'm just turning the floor object, so that might be the wrong way to do that.) On each platform I'd want to have physics working normally as though each platform is it's own little physics world, oblivious to the fact that it's platform is moving about. Just like we can walk on Earth and not feel like were hurtling around the sun and around the Earths axis. I guess this is similar to parenting a player controller to an elevator that moves left and right so the player doesn't slide off the platform as it moves. As the GIF shows, simply parenting the box to the floor breaks the physics sim I think. But the concept is what I'm trying to achieve. The same goes for parenting the player controller to the floor. It looks like it wants to work the way I intend, it just keeps bouncing back and forth. Any one know if this might be possible? #include "UltraEngine.h" using namespace UltraEngine; Vec3 mousepos = Vec3(0.0f); float move_adjustment = 0.1f; float move_speed = 1.0f; float lookspeed = 0.1f; float looksmoothing = 0.5f; bool enable_camera = true; int main(int argc, const char* argv[]) { auto displays = GetDisplays(); auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR); auto world = CreateWorld(); auto framebuffer = CreateFramebuffer(window); auto camera = CreateCamera(world); camera->SetClearColor(0.125); camera->SetFov(70); camera->SetPosition(0, 10, -5); auto light = CreateDirectionalLight(world); light->SetRotation(35, 45, 0); auto floor = CreateBox(world, 10.0f, 0.1f, 10.0f); floor->SetPosition(0.0f, -3.0f, 0.0f); floor->SetMaterial(LoadMaterial("Materials\\Developer\\bluegrid.mat")); auto box = CreateBox(world); box->SetMass(1.0f); box->SetPosition(2.0f, 2.0f, 0.0f); box->SetParent(floor); auto player = CreateCylinder(world); player->SetPhysicsMode(PHYSICS_PLAYER); player->SetPosition(0.0f, 5.0f, 0.0f); player->SetMass(1.0f); auto cam_pivot = CreatePivot(world); cam_pivot->SetParent(player); cam_pivot->SetPosition(0.0f, 1.8f, 0.0f); camera->SetParent(cam_pivot); camera->SetPosition(0.0f, 0.0f, -3.0f); camera->SetDebugPhysicsMode(true); bool stage = 0; while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false) { if (window->KeyHit(KEY_F2) == true) { camera->SetWireframe(!camera->GetWireframe()); } if (window->KeyHit(KEY_F3) == true) { camera->SetDebugPhysicsMode(!camera->GetDebugPhysicsMode()); } if (window->KeyHit(KEY_F4) == true) { enable_camera = !enable_camera; } if (enable_camera) { auto _displaySize = window->GetSize(); float cx = Round((float)_displaySize.x / 2.0f); float cy = Round((float)_displaySize.y / 2.0f); auto mpos = Vec3(window->GetMousePosition().x, window->GetMousePosition().y, window->GetMousePosition().z); window->SetMousePosition(cx, cy); mpos = mpos * looksmoothing + mousepos * (1 - looksmoothing); auto dx = (mpos.x - cx) * lookspeed; auto dy = (mpos.y - cy) * lookspeed; auto camrot = cam_pivot->GetRotation(); camrot.x += dy; camrot.y += dx; cam_pivot->SetRotation(camrot); mousepos = mpos; } floor->Turn(0.0f, 0.1f, 0.0f); auto y_angle = cam_pivot->GetRotation().y; auto move = 0.0f, strafe = 0.0f; if (window->KeyDown(KEY_W)) { move = 1.0f; } else if (window->KeyDown(KEY_S)) { move = -1.0f; } if (window->KeyDown(KEY_A)) { strafe = -1.0f; } else if (window->KeyDown(KEY_D)) { strafe = 1.0f; } player->SetInput(y_angle, move, strafe); world->Update(); world->Render(framebuffer); } return 0; }
-
> This is defintely the best result I obtained with Leadwerks physics to build a car; I think this car is good enough so that it could be used in a game. > The car reacts depending on terrain, feeling different if climbing or driving down. The car allows speed driving until 140-160 kmh > The dampers became an upgrade that give me the possibility to construct car over 200 kg. That means simply that the cars drive now with much more stability and give for the player heavier feeling. > It approachs the feeling you get in games klike GTA 4.. Even if the car doesn't really get out of control like sometime it happens ingame when cars are drifting. > Car balancing and transmission could get more fine tuning, but this should be no problem. Here is the demo:
-
This is a demo for a future offroad game. It demonstrates what a typical offroad drive could be in this game. It is a challenge for the driver to find a way on this broken road. The player has to follow an old forgotten road, the nature has slowly begun to cover. In the future, it supposed to let you deliver things to NPCs, win points on the road... I plan to add mud, water physics and visual effect, sounds, music, buildings, NPCs, cars upgrades, repairing tools for broken pieces... There will be no minimap, maybe a reduced map to get maximal immersion.
-
-
Heyas, So I'm making this simple pachinko game. The basic gameplay is that balls will fall down hitting pins and occasionally triggering scoring areas that award more balls. All the balls and pins are CSGs with, Physics Mode: Rigid Body and Collision type: Prop. Right now the balls fall, hit the pins and simply roll off. Is there a way to adjust the physics properties of the pins and/or balls to get the balls to bounce a bit more when they hit the pins? Or perhaps the change the gravity of the balls? Thanks in advance! -can
-
Here's my attempt so far at making my own character controller. The reason for this is because I'm using multiple gravity directions in the same world and the current controller doesn't like gravity in any direction other than down the y axis. The problem I'm having is getting the controller to stay constrained to the up axis, when you move it with speed (shift key) you'll see that it drags across the terrain and bounces around. I'm not sure how to correctly use the joints settings to stop this kind of movement. Any help is appreciated. #include "App.h" using namespace Leadwerks; App::App() : window(NULL), context(NULL), world(NULL), camera(NULL) {} App::~App() { delete world; delete window; } float lookspeed = 0.1, looksmoothing = 0.5; Vec3 mousepos; float jointpos = 1; bool wireframe = false; Joint* joint; Entity* parent; Joint* k = nullptr; Entity* child; bool App::Start() { window = Leadwerks::Window::Create(); context = Context::Create(window); world = World::Create(); camera = Camera::Create(); camera->Move(0, 0, -4); Light* light = DirectionalLight::Create(); light->SetRotation(35, 35, 0); parent = Model::Box(); parent->SetColor(0.0, 0.0, 1.0); parent->SetMass(1); parent->SetGravityMode(false); child = Model::Box(); child->SetColor(1.0, 0.0, 0.0); child->SetShape(Shape::Box()); child->SetMass(1); child->SetGravityMode(true); k = Joint::Kinematic(0, 0, 0, parent); joint = Joint::Slider(0, 0, 0, 0, 1, 0, child, parent); joint->EnableLimits(); joint->SetLimits(-5, 5); joint->SetMotorSpeed(100); Model* mdl = Model::Load("Models\\Terrain.mdl"); mdl->SetScale(20, 20, 20); mdl->SetPosition(0, -3, 0); Shape* shp = Shape::PolyMesh(mdl->GetSurface(0)); mdl->SetShape(shp); mousepos = window->GetMousePosition(); window->SetMousePosition(context->GetWidth() / 2, context->GetHeight() / 2); return true; } bool App::Loop() { if (window->Closed() || window->KeyDown(Key::Escape)) return false; if (window->KeyHit(Key::F3) == true) { camera->GetDebugPhysicsMode() == true ? camera->SetDebugPhysicsMode(false) : camera->SetDebugPhysicsMode(true); } if (window->KeyHit(Key::F4) == true) { camera->GetDebugEntityBoxesMode() == true ? camera->SetDebugEntityBoxesMode(false) : camera->SetDebugEntityBoxesMode(true); } if (window->KeyHit(Key::F2) == true) { if (wireframe == true) { camera->SetDrawMode(0); wireframe = false; } else { camera->SetDrawMode(2); wireframe = true; } } float cx = Math::Round(context->GetWidth() / 2); float cy = Math::Round(context->GetHeight() / 2); Vec3 mpos = window->GetMousePosition(); window->SetMousePosition(cx, cy); mpos = mpos * looksmoothing + mousepos * (1 - looksmoothing); float dx = (mpos.x - cx) * lookspeed; float dy = (mpos.y - cy) * lookspeed; Vec3 camrot = camera->GetRotation(); camrot.x += dy; camrot.y += dx; camera->SetRotation(camrot); mousepos = mpos; float _time = Time::GetSpeed(); float camspeed = 0.2f * _time; if (window->KeyDown(Key::Shift) == true) { camspeed = camspeed * 5.0f; } if (window->KeyDown(Key::W) == true) { camera->Move(0, 0, camspeed); } else if (window->KeyDown(Key::S) == true) { camera->Move(0, 0, -camspeed); } if (window->KeyDown(Key::A) == true) { camera->Move(-camspeed, 0, 0); } else if (window->KeyDown(Key::D) == true) { camera->Move(camspeed, 0, 0); } if (window->KeyDown(Key::T) == true) { camera->Move(0, camspeed, 0); } else if (window->KeyDown(Key::G) == true) { camera->Move(0, -camspeed, 0); } Vec3 p = parent->GetPosition(true); Vec3 r = parent->GetRotation(true); float speed = 0.1f; if (window->KeyDown(Key::Shift) == true) { speed = 1.0f; } if (window->KeyDown(Key::Up)) { k->SetTargetPosition(p.x, p.y, p.z + speed); } if (window->KeyDown(Key::Down)) { k->SetTargetPosition(p.x, p.y, p.z - speed); } if (window->KeyDown(Key::Left)) { k->SetTargetPosition(p.x - speed, p.y, p.z); } if (window->KeyDown(Key::Right)) { k->SetTargetPosition(p.x + speed, p.y, p.z); } //Jumping...? joint->SetAngle(jointpos); if (joint->MotorEnabled() == true) { joint->DisableMotor(); } if (window->KeyHit(Key::Space)) { joint->EnableMotor(); } Leadwerks::Time::Update(); world->Update(); world->Render(); context->SetBlendMode(Blend::Alpha); context->DrawText("Target position: " + String(jointpos), 0, 0); context->DrawText("Current position: " + String(joint->GetAngle()), 0, 20); context->DrawText("Motor enabled: " + String(joint->MotorEnabled()), 0, 40); context->SetBlendMode(Blend::Solid); context->Sync(); return true; } Other links; https://www.leadwerks.com/community/topic/17437-physics-constraints/ Project.zip
-
What's the best way to add physics constraints to a custom player controller? For example, the cylinder shape is pulled toward gravity (which is not always straight down) then collides with an object, how do I stop little movements like sliding and twisting once it collides? As far as I can tell, the inbuilt character controller only slides if the slope is greater than max slope. And when it moves around by key press it doesn't rotate left or right or jitter as it goes over various sloped polygons. Gravity is added like so in a different class to the controller; float _force = -9.8f; entity->AddForce(_gravity->gravityDirection.x * _force, _gravity->gravityDirection.y * _force, _gravity->gravityDirection.z * _force); //gravityDirection is a normalized vector And so far for the controller I've been doing this to make it move; Vec3 _velocity = entity->GetVelocity(true); //Get the current velocity Vec3 _gravityVelocity = _velocity.Multiply(upDirection); //Find how much of the velocity is along the gravity vector Vec3 _otherVelocity = _velocity.Subtract(_gravityVelocity); //Any remaining velocity other than gravity Vec3 _forwardVelocity = _forwardVector * move; //A vector in front of the player and perpindicular to gravity Vec3 _strafeVelocity = _strafeVector * strafe; //A vector that is the cross of gravity and the forward vector (out to the side) Vec3 _jumpVelocity = upDirection * jump; //GravityDirection * -1.0f movementVector = _forwardVelocity + _strafeVelocity + _jumpVelocity; //Find total movement vector (don't normalize, magnitude is speed) entity->SetVelocity((_velocity - _otherVelocity) + movementVector, true); //Subtract othervelocity to get only what gravity is, then add any movemnt entity->SetOmega(0.0f, 0.0f, 0.0f, true); //Been using this to reduce angular rotation, not perfect though The above code has issues still, but it's my attempt at constraining it along gravity unless needed to move. This code isn't done in a physics hook, which I was wondering if that might be better? And here's a quick video showing the physic shape jittering. It doesn't translate to the camera much at the moment but it does when the jitter is larger. Also I can't move the shape along it's local axis because of the jitter and the random rotation that happens as a result of moving. https://youtu.be/ZA_UNsYCEQs Any thoughts on more accurate ways of constraining are appreciated
-
Now, I wrote a single script that creates suspension, steer and traction You have to create the chassis, 4 wheels, set this script to each one of the wheels and then paly with the configurable parameters. Enjoy: --[[ Autor Juan Ignacio Odriozola (charrua) Purpose: A script that facilitates the making of a simple car all you need is a chassis and 4 wheels assing this scrip to each wheel and set the object chassis then adjust some of the configurable parameters Parent: chassis entity : wheel 3 joints and 2 auxiliary entities are created the chain is: Parent Slider Pivot Hinge Pivot Hinge chassis -suspensionJoint- suspensionPivot -steerJoint- steerPivot -wheelJoint- wheel suspension uses pin 010 (Y axis) steer uses pin 010 (Y axis) wheel pin (must be set depends of wheel orientation) up/down keys are defaults for forward and backward left/right keys are defaults for steer left/right space key is default for brakes steer velocity and start/end angle must be set suspension lenght must be set wheel friction must be set steerAngle set both limits to +/- steerAngle/2 if no key (left/right) is pressed then, target angle is 0 : straight suspensionLenght set both limits to +/- suspensionLength/2 and target distance is set to 0 suspension strength defaults to 1000 which is too much strenght for a light weight car (20 of mass) and not to much for a 200 car of mass each joint is created with a mass of 1, which should be taking into accoung (so for a 4 wheels car, you have a mass of 8 on the 8 joints). there are so many other parameters that may be adjusted: Spring, Strength, Stiffness ... not too much documented :) ]]-- Script.currspeed = 0 Script.chassis = nil--Entity "chassis" Script.pin = Vec3(0,0,1) --Vec3 "wheel Pin" Script.motorspeed=500--float "max motor speed" Script.velcontrolled=false--bool "velControl" Script.suspensionLength=0.2--float "suspension" Script.steerAngle=90--float "steer angle" Script.steerSpeed=100--float "steer velocity" Script.friction=1--float "wheel friction" Script.steerPivot=nil Script.suspensionPivot=nil Script.steerJoint=nil Script.suspensionJoint=nil Script.wheelJoint=nil function Script:Start() local pos = self.entity:GetPosition(false) --true for global if self.chassis ~= nil then self.suspensionPivot = Pivot:Create() self.suspensionPivot:SetPosition(pos) self.suspensionPivot:SetMass(1) self.suspensionPivot:SetCollisionType(Collision.None) self.steerPivot = Pivot:Create() self.steerPivot:SetPosition(pos) self.steerPivot:SetMass(1) self.steerPivot:SetCollisionType(Collision.None) --joints creation self.suspensionJoint = Joint:Slider(pos.x, pos.y, pos.z, 0, 1, 0, self.chassis, self.suspensionPivot) self.steerJoint = Joint:Hinge(pos.x, pos.y, pos.z, 0, -1, 0, self.suspensionPivot, self.steerPivot) self.wheelJoint = Joint:Hinge(pos.x, pos.y, pos.z, self.pin.x, self.pin.y, self.pin.z, self.steerPivot, self.entity) --suspension self.suspensionJoint:EnableLimits() self.suspensionJoint:SetLimits(-self.suspensionLength/2,self.suspensionLength/2) --steerAngle=0 means no steer self.suspensionJoint:SetTargetAngle(0) --at the middle self.suspensionJoint:SetMotorSpeed(1) -- 1 m/s self.suspensionJoint:SetStrength(100) --defatul is 1000 self.suspensionJoint:EnableMotor() --steer self.steerJoint:EnableLimits() self.steerJoint:SetLimits(-self.steerAngle/2,self.steerAngle/2) --steerAngle=0 means no steer self.steerJoint:SetMotorSpeed(self.steerSpeed) self.steerJoint:EnableMotor() --wheel self.entity:SetFriction(self.friction, self.friction) else Debug:Error("no chassis assigned") end end function Script:setMotorSpeed(speed) if self.velcontrolled then --System:Print("setMotorSpeed: "..speed) self.currspeed = speed if speed~=0 then self.wheelJoint:EnableMotor() end self.wheelJoint:SetMotorSpeed(self.currspeed) end end function Script:UpdateWorld() if self.motorspeed>0 then self.wheelJoint:SetAngle(self.wheelJoint:GetAngle()+100) else self.wheelJoint:SetAngle(self.wheelJoint:GetAngle()-100) end if App.window:KeyDown(Key.Space) then self:setMotorSpeed(0) end if self.velcontrolled then if App.window:KeyDown(Key.Up) then self.currspeed = self.currspeed + 10 if self.currspeed>self.motorspeed then self.currspeed=self.motorspeed end if self.currspeed == 10 then self.wheelJoint:EnableMotor() end self.wheelJoint:SetMotorSpeed(self.currspeed) end if App.window:KeyDown(Key.Down) then self.currspeed = self.currspeed - 10 if self.currspeed<-self.motorspeed then self.currspeed=-self.motorspeed end self.wheelJoint:SetMotorSpeed(self.currspeed) end end if self.steerAngle>0 then local direction=0 if App.window:KeyDown(Key.Left) then direction=-self.steerAngle/2 end if App.window:KeyDown(Key.Right) then direction=self.steerAngle/2 end self.steerJoint:SetAngle(direction) else self.steerJoint:SetAngle(0) end end In the other maps i was using a box as a floor to which I set the desired friction, testing this new script i use a terrain and have to figure it out how to set the friction to the terrain... Did some searches and ended with: local n for n=0,self.world:CountEntities()-1 do local entity = self.world:GetEntity(n) if entity:GetClassName()=="Terrain" then terrain = entity System:Print("terrain found!") terrain:SetFriction(10,10) break end end insert this in the app.lua (after load map) and then you can play with terrain friction, the video shows how the car behaves with the defaul terrain friction and then whit a friction of 10,10 Always learning something new A word about some parameters: If you are using a hinge, when you specity speed (SetMotorSpeed) the number means degrees per second. So if you use 3600 as max speed you get 10 revoluions per second. If your tire has, 64cm then d*pi aprox 2 meters per revolution, 10 revolutions per secon aprox 20 meters per second... and if you are lucky 72Km/h If you are using a slider, then speed is un meters per second. Other parameter which is very important is the hinge/slider "pin" which is a vector that tells the direction of movement of the slider or over which plane de hinges open/close For a common door, we need to use the Y axis, so the pin is 0,1,0 I use this pin for the suspension and for the steer but for this script, you must tell the traction pin, which sould be 1,0,0 or 0,0,1 depending on the orientation of your tires If your tires are not facing X nor Z axis, then you have to do some math to get the proper x,z component of the pin In the script I use a Strenght of 100, instead of the 1000 which is default, my car is light weight : 5 + 4*3 = 17 of Mass chassis has 5, each wheel has 1 and the 2 auxiliary pivots has 1 each one whith a friction of 10 on each tire and with a friction of 10 on the terrain looks ok for me (better than I spected at first). Juan
-
Recently i posted a simple car made with joints without too much explanations... so What is a joint? This video shows joints in action. At the end, the stand alone executable and the complete project. The following text and figure is from the "ode-latest-userguide", figures are also form the JV-ODE documentation: In real life a joint is something like a hinge, that is used to connect two objects. It is a relationship that is enforced between two bodies so that they can only have certain positions and orientations relative to each other. This relationship is called a constraint, the words joint and constraint are often used interchangeably. The figure shows three different constraint types. The first is a ball and socket joint that constraints the “ball” of one body to be in the same location as the “socket” of another body. The second is a hinge joint that constraints the two parts of the hinge to be in the same location and to line up along the hinge axle. The third is a slider joint that constraints the “piston” and “socket” to line up, and additionally constraints the two bodies to have the same orientation. Each time the integrator takes a step all the joints are allowed to apply constraint forces to the bodies they affect. These forces are calculated such that the bodies move in such a way to preserve all the joint relationships. Each joint has a number of parameters controlling its geometry. An example is the position of the balland- socket point for a ball-and-socket joint. The functions to set joint parameters all take global coordinates, not body-relative coordinates. A consequence of this is that the rigid bodies that a joint connects must be positioned correctly before the joint is attached. Figure: types of joints The "Integrator step" is the part of the physics engine that does all the calculations. Based on the current state, force applied, constrain parameters, collisions etc... recalculates the next position and rotation of every affected physical obejct. Leadwerks then, does the render of the objects acordingly to it's new positions, or that is what is supposed to do... (Josh should correct me if I'm wrong). A joint normally is placed between two objects, one is called Parent and the other Child. The child should be NULL, and in this case the joint is between one body and the world or scene. In the case of a door, you need a Door and a Frame, the Frame should be the Parent body, the Door the child body. In real life you have to decide where the Hinge (or more than one) will be placed and use some screws to attach both parts of the hinge to the frame and the door. In software, things are practically the same In other words, we have to have 3 things clear: Who is the parent and where it should be positioned and oriented. Who is the child and where it should be poitioned and oriented. Which type of joint you need, where it should be positioned and oriented. Normally we create and place the two bodies first and then, at joint creation we say where the joint will be and how it should be oriented, who the parent is and who child is, if any. After that, joints normally has some other properties that should be adjusted, used, controlled, limited, enabled... etc, depending on the joint type. Joints have two "limits": If the joint is a Hinge, the limits are the Start Angle and End Angle "the door should turn". If the joint is a Slider, the limits are the Start Position and End Position the piston should extend. If the joint is a Ball, the limits are the cone angle and twist or torsion... keep reading Joints (hinge and slider) should have a "motor" if not, as a normal door, we need external forces to "open the door", if we place a door on a frame attached with hinges and the door has no lock at all, then the wind should open/close it, or some body that collides with the door should move it. As we use a hinge, the door do not fall, do not take an upward motion, the motion is well known and constrainded tho the clasic movement we all know. But if, for instance we have a "motorized door" then the open/close operation is started in some way (press of a button?) and a motor does the job. Normally it stops automatically, because there are end-run switches or the like installed and working for us. A sofware motorized joint is exactly that, we set where we want the joint go (setTargetAngle), and start a motor (enableMotor). Ball Joint A ball is used when we need a "cone" movement freedom, think that you have a rope attached at the roof of your room and you hang a box on it. If the rope is made of a rigid material, the box should made a pendulum movement not so large and probably the box orientations should not change too much. How much we let the rope to move is dictated by the first limit and how much the object attached should change orientation (twist/torsion) is dictated by the second limit. A rope is a nice looking example, all you have to do is place N cubes (shape is not important) and place ball joints in the middle of each pair. You may have the first box joined to the world (attached to a fixed 3d position) and then a link of bodies and ball joints: Joint N has body N as parent and body N+1 as child Leadwerks came with a simple ball.lua script: function Script:Start() local pos = self.entity:GetPosition(true) self.joint = Joint:Ball(pos.x, pos.y, pos.z, self.entity, self.entity:GetParent()) self.joint:SetFriction(10) self.entity:SetDamping(0,0) end If you have a chain of parented objetcts, then you may set this script and you have a rope build with a few clicks of your mouse on the editor. Procedure: Create some boxes, set prop collision and a mass not zero (1 perhaps), place then in a vertical row somewhat separated. In the editor drag the second box to the first, the third to the second building a hierachy (parent/child relationship) Select all (one by one with ctrl click on each box) and then apply the ball script. Voila! You have a rope with no limits: cone angle is the maximum the engine lets you to be and the same for torsion. Collide the rope with some object or, place the boxes horizontally (instead of vetically) and let gravity do it's job. I made another ball script: ballEnhaced: Script.parent = nil--Entity "Parent" Script.child = nil--Entity "Child" Script.useLimits=false--bool "Use limits" Script.coneAngle = 45--Float "Cone Angle" Script.torsion = 45--Float "Torsion" Script.hide = true--bool "Hide entity" function Script:Start() local pos = self.entity:GetPosition(true) if self.parent~=nil then self.joint = Joint:Ball(pos.x, pos.y, pos.z, self.parent, self.child) self.joint:SetLimits(self.coneAngle, self.torsion) if self.useLimits then self.joint:EnableLimits() System:Print("limits:"..self.coneAngle..", "..self.torsion) end if self.hide then self.entity:Hide() end end end if using this script, you have to create a pivot or box (collision type: none, and no mass) and tell the script ho the parent is and ho the child is, if no child, then the parent body will be attached to the environement, fixed at this 3d point. In the map included in this blog there are two ropes, one made with the leadwerks ball.lua and one with the ballEnhaced.lua script, in this script you may tell how to constrain the ball cone anlgle and trosion. Look the video, and you will see the difference on how the cubes behave when hitted by the car. A rope suspension bridge should be made with both ends attached to the world and a double chain of joints... doing two unions between each part of the brige. It's nice so see the car passing over it! The attached video shows at first the car passing over the bridge... the mass of the car is very little, if not, the joints get broken.. as always there are some other things to consider. Hinje Joint As I used a hinge for some explanation, i guess there is not to much to say here, I use some hinges for doors in the map attached and also used them for the steer and wheels of the car. One door have a motor and the other not, so the second one moves as the car collides it. I wrote two scripts: hingeEnhaced.lua Script.parent = nil --entity "parent" Script.child = nil --entity "hild" Script.hide = true--bool "Hide entity" Script.pin = Vec3(0,0,1) --Vec3 "Hinge Pin" Script.limitsenabled=false--bool "Enable limits" Script.limits = Vec2(-45,45) --Vec2 "Limits" Script.friction = 0--float "Friction" function Script:Start() local pos = self.entity:GetPosition(true) if self.parent~=nil then self.joint = Joint:Hinge(pos.x, pos.y, pos.z, self.pin.x, self.pin.y, self.pin.z, self.parent, self.child) if self.limitsenabled then self.joint:EnableLimits() end self.joint:SetLimits(self.limits.x,self.limits.y) self.joint:SetFriction(self.friction) self.entity:SetDamping(0,0) if self.hide then self.entity:Hide() end end end As in the case of a ball joint, with this script you may set parent, child and limits hingeMotorized.lua Script.parent = nil --entity "parent" Script.child = nil --entity "hild" Script.hide = true--bool "Hide entity" Script.pin = Vec3(0,0,1) --Vec3 "Hinge Pin" Script.limitsenabled=false--bool "Enable limits" Script.limits = Vec2(-45,45) --Vec2 "Limits" Script.tOpenClose = 15 --Int "Open/Close time" Script.movespeed = 60 --Int "Speed" Script.startTime = 0 Script.action = 1 --1 open, 0 close function Script:Start() local pos = self.entity:GetPosition(true) if self.parent~=nil then self.joint = Joint:Hinge(pos.x, pos.y, pos.z, self.pin.x, self.pin.y, self.pin.z, self.parent, self.child) if self.limitsenabled then self.joint:EnableLimits() end self.joint:SetLimits(self.limits.x,self.limits.y) self.joint:SetTargetAngle(self.limits.x) self.joint:EnableMotor() self.startTime = Time:GetCurrent() self.joint:SetMotorSpeed(self.movespeed) if self.hide then self.entity:Hide() end end end function Script:UpdatePhysics() local time_ = Time:GetCurrent() if time_ - self.startTime > self.tOpenClose*1000 then self.action = 1-self.action --toggle action if self.action == 1 then self.joint:SetTargetAngle(self.limits.x) else self.joint:SetTargetAngle(self.limits.y) end self.startTime = Time:GetCurrent() end end This scripts creates the joint, then set a target angle, enables the motor and apply a velocity, so the "door" starts going to the first limit. After the tOpenClose time, the variable action is toggled and based on it the TargetAngle will be first limit or second limit, in this way, the door continously open and then close after tOpenClose time Slider Joint For a slider joint, in this map i wrote one script: sliderMotorized.lua Script.child = nil --entity "hild" Script.hide = true--bool "Hide entity" Script.pin = Vec3(0,0,1) --Vec3 "Hinge Pin" ScrScript.parent = nil --entity "parent" ipt.limitsenabled=false--bool "Enable limits" Script.limits = Vec2(-45,45) --Vec2 "Limits" Script.tOpenClose = 15 --Int "Open/Close time" Script.movespeed = 60 --Int "Speed" Script.startTime = 0 Script.action = 1 --1 open, 0 close function Script:Start() local pos = self.entity:GetPosition(true) if self.parent~=nil then self.joint = Joint:Slider(pos.x, pos.y, pos.z, self.pin.x, self.pin.y, self.pin.z, self.parent, self.child) if self.limitsenabled then self.joint:EnableLimits() end self.joint:SetLimits(self.limits.x,self.limits.y) self.joint:SetTargetAngle(self.limits.x) self.joint:EnableMotor() self.startTime = Time:GetCurrent() self.joint:SetMotorSpeed(self.movespeed) if self.hide then self.entity:Hide() end end end function Script:UpdatePhysics() local time_ = Time:GetCurrent() if time_ - self.startTime > self.tOpenClose*1000 then self.action = 1-self.action --toggle action if self.action == 1 then self.joint:SetTargetAngle(self.limits.x) else self.joint:SetTargetAngle(self.limits.y) end self.startTime = Time:GetCurrent() end end This script is 99% the same as the hingeMotorized.lua, the only difference is that a slider joint is created instead of a hinge joint. Note that the limis are not angles, they are offsets from the initial position of the plataform. Stand alone executable: jointsworldDistro.zip Project: jointsworldProject.zip Enjoy Juan
-
Here there is a way of making a simple car based on hinges This is not a tutorial, is just simply a stating point for the ones that want/like to play arround physics and hinges... I included the entire project and the distribution executable to test the scripts so, you have as I say a starting point and just that, hpe it helps someone This is the editor view and the initial placement of the parts needed Basically I made 3 scripts physicsProperties.lua wheel.lua steer.lua First you have to create and place the car body or chassis, 4 wheels and 6 auxiliary pivots/ or any brush you like (a cube is fine) for the hinges 4 of the auxiliary entities are for the wheels hinges and 2 for the wheels steer. Place the wheel hinge and steer centered with the wheel. After that you may set some script parameters: Wheel scritp: Basically, the position of the entity script holder is used to create a hinge between the "parent" and the "child" you choose (in the picture above: between the auxiliary entity SteerFL and the WheelFL) If vel control is checked then, a motor is enabled for that hinge and keys Up/Down are used to increase/decrease speed If vel control is not cheched, no motor is enabled and the wheel is free to run Be carefull whit the Hinge Pin, which dictates the axis over which the wheel will rotate, in this case I used X axis, but if you use other pieces direction/alignement you should adjust this values. Steer script: The steer hinge is used to turn the wheel to handle car heading, so the pin is the Y axis Limits and Motor are needed to control the steer Limits is for how much the steer will turn right/left using the default keys left/right arrow When you press left or ritght key, the right limit will be set as the hinge angle and the hinge will try to reach this angle at the "steer speed", the same, but whit the left limit happen if you press the left key. physicsProperties just let you adjust the friction of the wheels and or the floor Script.sfrict=0--float "static friction" Script.kfrict=0--float "kinetic friction" function Script:Start() System:Print("phy properties start") self.entity:SetFriction(self.sfrict, self.kfrict) end so simple, and in the editor it looks: Here is a hand drawing of how scripts, objects, parent/child are connected Here is the wheel script Script.currspeed = 0 Script.parent = nil--Entity "Parent" Script.child = nil--Entity "Child" Script.pin = Vec3(0,0,1) --Vec3 "Hinge Pin" Script.motorspeed=500--float "Motor speed" Script.velcontrolled=false--bool "velControl" function Script:Start() System:Print("wheel start") self.entity:Hide() local pos = self.entity:GetPosition(false) --true for global if self.child ~= nil then if self.parent ~= nil then self.joint = Joint:Hinge(pos.x, pos.y, pos.z, self.pin.x, self.pin.y, self.pin.z, self.child, self.parent) else Debug:Error("no parent assigned") end else Debug:Error("no child assigned") end end function Script:setMotorSpeed(speed) if self.velcontrolled then System:Print("setMotorSpeed: "..speed) self.currspeed = speed if speed~=0 then self.joint:EnableMotor() end self.joint:SetMotorSpeed(self.currspeed) end end function Script:UpdateWorld() if self.motorspeed>0 then self.joint:SetAngle(self.joint:GetAngle()+100) else self.joint:SetAngle(self.joint:GetAngle()-100) end if App.window:KeyDown(Key.Space) then self:setMotorSpeed(0) end if self.velcontrolled then if App.window:KeyDown(Key.Up) then self.currspeed = self.currspeed + 10 if self.currspeed>self.motorspeed then self.currspeed=self.motorspeed end if self.currspeed == 10 then self.joint:EnableMotor() end self.joint:SetMotorSpeed(self.currspeed) end if App.window:KeyDown(Key.Down) then self.currspeed = self.currspeed - 10 if self.currspeed<-self.motorspeed then self.currspeed=-self.motorspeed end self.joint:SetMotorSpeed(self.currspeed) end end end Here is the steer scritp Script.parent = nil--Entity "Parent" Script.child = nil--Entity "Child" Script.pin = Vec3(0,1,0) --Vec3 "Hinge Pin" Script.useLimits=false--bool "use limits" Script.limits = Vec2(-45,45) --Vec2 "Limits" Script.useMotor=flase--bool "use motor" Script.motorspeed=50--float "Steer speed" function Script:Start() System:Print("steer start") if self.child == nil then Debug:Error("No child assigned.") end if self.parent == nil then Debug:Error("No parent assigned.") end self.entity:Hide() local pos = self.entity:GetPosition() self.joint = Joint:Hinge(pos.x, pos.y, pos.z, self.pin.x, self.pin.y, self.pin.z, self.child, self.parent) if self.useLimits then self.joint:EnableLimits() self.joint:SetLimits(self.limits.x,self.limits.y) end if self.useMotor then self.joint:SetMotorSpeed(self.motorspeed) self.joint:EnableMotor() end end function Script:UpdateWorld() local direction=0 if App.window:KeyDown(Key.Left) then direction=self.limits.x end if App.window:KeyDown(Key.Right) then direction=self.limits.y end self.joint:SetAngle(direction) end here the distro: simpleCarDistro.zip here the project: simpleCar.zip and here a video... enjoy Juan
-
I just want to verify the way the physics commands should work. I'm assuming that Newton Game Dynamics uses real world equations to calculate the final force and velocity of objects. So if that's the case, what are the units of measurements that should be passed to each of the physics commands? If I set an entities mass to 100.0f, and say this is 100 kg, what should input into into "AddForce()"? m/s² or Newtons? It would make sense to be newtons because that's the measurement of force. And also "SetFriction()". Is this the friction coefficient or the force in newtons that has to be over come? Physics are not behaving the way I think they should in my game and I just want to make sure I'm feeding the commands the right values.
-
Hi! I was wondering if someone can point me a basic tutorial or example on how to create simple vehicle with 4.4? I haven't loaded workshop cars because, if I understood correctly, they were made for 3.x and vehicle physics have changed since. I also read that they were disabled at some point, is that still the case? I guess not when watching this cool video I would appreciate any tips, thanks!
-
Hi everyone,can anyone give me a hand? I'm doing some port models, and my crane is getting very heavy physics. I've tried another but none of my taste, can anyone give me a hint ?? I've already used "PolyHedron" "ConvexHull" and "Convex Decomposition" but the best is "PolyMesh" but it gets very heavy! Thank you, follow an image of the model in the engine!
-
Hi guys I am trying Leadwerks and it's good! I can't find nothing about how to join togheter different physical objects (for example to make a car with wheels). Obviously that car must be handled by physic engine. Is this feature available? Thank you!
-
I am asking myself how to make a sphere that intersects with an other object notice that it did ? The problem with that is that, - I need no character physics shape, i want to keep the physics shape of the sphere. - Also I want no collision sice it would make no sense in the game I am currently making. It works with A character physics shape and a trigger, but the cylinder that is the only option with the character mode is not usable as said. To make it clearer, heres a screenshot of my game. It is an endless procedual generated game, the sphere is made visible on purpose. (to see if it should intersect) The Sphere is controlled by the mouse, and when it hits the white object, it has a flickering effect and a life less but not change the movement when colliding.
-
I am trying to make a script for physics based doors (like Amnesia) where you can pull and push the doors. Right now I have slam open and slam closed, but the pull and push are super glitchy, so I think I'm approaching that the wrong way. Any suggestions on how to approach this?
-
Hello I want to make an elevator-like capsule which has the movingplatform script attached. the problem is that it has some physics objects attached that are colliding with the capsule So how do you make a script or adjust things like that in the editor? The Objects that are attached to the moving platform should only collide with the player (or just not with the platform)
-
It would be nice to be able to set the stiffness of joints. Right now, things like ropes and ragdolls aren't really possible because everything looks like a rubber band.
-
- 3
-