Jump to content

Josh

Staff
  • Posts

    23,086
  • Joined

  • Last visited

Blog Comments posted by Josh

  1. I see. What is I added an "extra" parameter to be passed back to the callback, like this?:

    class Thing
    {
    Model* model;
    virtual void OnCollision(Entity* entity, Vec3 position, Vec3 normal, float speed);
    };
    
    //Extra paramter gets passed back to callback:
    void CollisionCallback(Entity* entity, Vec3 position, Vec3 normal, float speed, char* extra)
    {
    Thing* thing = (Thing*)extra;
    
    //Call the class function:
    thing->OnCollsiion(entity,position,normal,speed)
    }
    
    Thing* thing = new Thing;
    thing->model = Model::CreateBox();
    thing->model->SetCallback(CALLBACK_COLLISION,CollisionCallback,thing);

    This would do the same thing you are describing, just one global callback function has to be declared, and it will work with all languages.

  2. You can't store them in a list which will almost always be what you want to do.

    You actually can if you use pointers, which I always do for complex objects like entities. You can store Models or whatever in a list of type Entity*, and cast them back to Model* later.

     

    The problem is the user can't be responsible for the creation of every single entity. When you load a model, it has children from the file that are automatically created. When you load a map, entities will be automatically created.

  3. Another thing I should point out about collision callbacks is you avoid the issue of accessing deleted entities. If collisions are stored, you could delete an entity and then when you are iterating through collisions, come across a collision that occurred with an entity that no longer exists. I experienced this problem a long time ago with Blitz3D.

     

    I suppose you could delete an entity inside the callback, if you are a crazy person, but the problem is much less likely to come up.

  4. Yes, that is what I was describing.

     

    Now you have a model that you want to add your own collision function for. You load the model from a file and then... :) Oh wait, it's a Model, not a MyEntity.

     

    Okay, so I completely rewrite the Model class so that you can create a blank model, then load a file with it. You have your own MyEntity class derived from the Model class, and you load a file:

    MyEntity* entity = new MyEntity;
    entity->Load("model.mdl");

     

    That works, except the engine actually has the GraphicsDriver class create a model, containing special functions for rendering::

    Model* OpenGL3GraphicsDriver::CreateModel()
    {
    return new OpenGL3GraphicsModel;
    }

     

    Let's just ignore that problem for now. :)

     

    What if you want a limb in a model you load to have a special collision function? Now we have to do something like this:

    ModelGenerator* mymodelgenerator = new MyModelGenerator;
    world->SetModelGenerator(mymodelgenerator );
    MyEntity* entity = new MyEntity;
    entity->Load("model.mdl");

     

    That wouldn't be confusing, at all. :o

     

    Ideally, you would want to just replace an object's class function with your own, or turn a base object into a derived class, but you can't do those things with C++.

     

    We could have a "collisionhandler" object as a member of the Entity class, but that is even more abstract and confusing, and you would still be using the "entity" prefix inside the function to access the entity's members, so it's just like a callback and defeats the purpose.

  5. A virtual function is just a class function that can be overridden by a derived class' function. I make all my class functions virtual, always. The fact you even have to specify it probably indicates this was tacked on at some point after class functions were originally implemented in C++.

     

    So like if you have Entity::SetPosition and Model::SetPosition, the Model class' SetPosition function will override the entity's, if they are both virtual.

  6. I appreciate the engine taking care of collision declarations and other assumable information.I'm hoping that with this version of the engine, you can make it so we can do something like this, any time we want:AreEntitiesColliding(entity1,entity2);

    The problem is that at any given time, you may have thousands of collisions occurring, but you are only interested in a few. If we want to save all that information, we're going to have a ton of dynamically allocated collision objects each physics update, which makes things slow. Okay, we can get around that by writing the collision data to big memory buffers that only get expanded, never made smaller. Now we have to consider that while the user is accessing this memory, the physics are already processing the next step and writing to these buffers, so now we need to use two copies of the data, and alternate the read and write buffers each frame.

     

    If each collision is 20 bytes (position, normal, speed, entity0, entity1), 1000 collisions will use 19 kb of memory, times two. Okay, that isn't bad.

     

    So that gives us a system where you call CountCollisions() and GetCollision(int index) and iterate through all collisions that occurred, but it still doesn't do what you want. To find a particular collision, you would have to iterate through n*n collisions, which is always the wrong way to do things in programming. If you had 1000 collisions, you would have to iterate through 1000*1000=1,000,000 combinations to find the one you are interested in.

     

    Another way to do this would be to add a list of collisions to each entity, again with a read and write buffer that alternate each frame. That would allow faster focusing on the entities you are interested in, but again we are storing tons of data when we're really only interested in a small percentage of that data.

     

    When we use collision callbacks, all collisions are dealt with immediately, and no collision data has to be written and saved. (Not entirely true...the engine stores an array of collisions for each thread, but only entities with a collision callback get written into the array, so that cuts out 95% of your entities.)

     

    For C++, I think the ideal situation would be if you could write a class with a virtual function that overrides an Entity class function called OnCollision(). However, since C++ can't turn an object of one class into one with a derived class, that makes it very difficult. If you loaded a model, retrieved one of its limbs, and tried to recreate it with a derived class... :) Not pretty. This is a major shortcoming of C++, but to my knowledge, no other language deals with it either.

     

    So we have this situation where there's one approach that's simpler if you only have a few objects on the screen, but can potentially cause a lot of slowdown if you have a heavy simulation running.

     

    The best approach is probably to use a Lua script with a Collision() function declared. The support for Lua in Leadwerks3D is a lot better, with debugging and code stepping.

  7. By the way, the collision type pair is stored in an iVec2, a new class in Leadwerks3D, which is just a Vec2 with integers instead of floats. An operation overloader allows this class to be used as the key in an std::map, and the collision response is stored as a value for that key:

    void PhysicsDriver::SetCollisionResponse(const int& collisiontype0, const int& collisiontype1, const int& response)
    {
    if (collisiontype0>collisiontype1)
    {
    	collisionresponse[iVec2(collisiontype1,collisiontype0)] = response;
    }
    else
    {
    	collisionresponse[iVec2(collisiontype0,collisiontype1)] = response;
    }
    }

    The C++ programmers here might find that interesting.

  8. CSG brushes will eventually have real breakage. No pre-made pieces have to be made. Scripted breakage of models can still be done the same in LE2, but typically no one bothers with something that requires more work in the art pipeline. I know there are some third-party tools coming out that will create pre-broken pieces for any triangle mesh, and that's something I'll look at down the road, but it's low-priority at the moment.

     

    Yes, I was missing one collision response I wanted.

  9. I don't know what the previous constants were, off the top of my head.

     

    Debris means small fragments of things you don't want the player to affect, but will still collide with the scene and most dynamic objects. If you play around with Half-Life 2: Deathmatch you'll get a good feel for this. For example, if a piece of wood breaks in half, you don't want the player kicking small fragments around, but they would still need to collide with the scene and large physical objects.

  10. Some interesting observations here. It seems like for premade models, it's all or nothing. If a single character has a value of n, a complete pack of characters has a value greater than 10n. So it would be best to focus on producing large sets of complete game assets instead of individual items.

  11. I may call the AI class an "Actor" and switch our Actor class to something else, like "Modifier" or something. Maybe the class functions would look something like this:

     

    Actor::SetTargetEntity(Entity* entity, int mode, float distance)

     

    Makes an actor follow an entity.

     

    distance

    When the actor gets this close, it stops following.

     

    mode:

    0 - always follow

    1 - move to last visible point

    2 - stop when line of sight is lost

    3 - stop when a line of sight is acquired (for projectile enemies)

     

     

    Actor::SetTargetPosition(Vec3 position, float distance)

     

    distance

    When the actor gets this close, it stops following.

     

    You could do all this manually in script, but my guess is there are ways I can make it more optimal if it's built into the engine, like staggering the updates or running it on a separate thread.

     

    Maybe the actor would have Lua functions like this:

     

    function actor:TargetReached()

    function actor:TargetLost()

    function actor:TargetAcquired()

    function actor:GotBoredAndTookANap()??? :P

  12. Regarding the AI following:

     

    Normally the player moves through a map and alerts enemies when they come within a certain range and have a direct line of sight. You don't normally have a mob of enemies chasing a player through a map.

     

    Enemies should be faster than the player, so that they are forced to deal with them as they are encountered.

     

    So what would normally happen is you would move to an area, encounter some enemies, be forced to fight them there, then move on. Since enemies would be placed around the map in different locations, they normally would not all be coming at you from one direction.

  13. Here's what I'm thinking:

     

    -Melee enemies stop when they reach a certain distance. Zombies, anything with a sword, anything that bites.

     

    -Enemies with projectile weapons should stop on two conditions.

    1. A clear line of site to their target exists.

    2. They are within firing range (consider a crossbow or something with a high arc).

     

    The second type of enemy might retrace their steps to back away from the player when not firing, stopping when the line of sight is broken. This might be a good approximation for running for cover, then coming out to shoot, then hiding again.

  14. That's a function of the player radius. It chooses the shortest path, so the player will come very close to corners when they go around them. The scale of the obstacles in this test scene is quite big, so the player radius is very small in comparison.

  15. There's basically no API to worry about. You just have a command to find a path, and don't have to worry about anything else. As far as controller movement goes, I think I'll just leave that to the user, and have some example scripts. It's very easy to just point it to the next node and go.

×
×
  • Create New...