1. Hey Guest, is it this your first time on the forums?

    Visit the Beginner's Box

    Introduce yourself, read some of the ins and outs of the community, access to useful links and information.

    Dismiss Notice

[Solved] Physics Homework meets KAG

Discussion in 'Modding Help' started by Vermilicious, Feb 6, 2016.

  1. Vermilicious

    Vermilicious Ballista Bolt Thrower

    Messages:
    232
    Hey again guys, sorry for clogging up the help section as of late. (It needed a bit of action anyway though.)
    I've spent a little time on a small side project; belt conveyors. Something I've fantasized about for quite a while. The problem is, and I've run into it a bit before in regards to controlling zombie movement, CBlob.AddForce. I can't seem to wrap my head around what this function actually does. The documentation does mention it's not actually a force, which is frustratingly enough true, but what exactly are we sending to this function? Velocity? Acceleration?
    What I want to do, is that the belt conveyor blocks push whatever blob touches it above, but assuming that blob is not moving on it's own as if it's a player-controlled entity for example, the velocity should be fixed. Whatever I do, lower weight blobs ends up with a much higher velocity. How would I go about doing that? What do I pass on to CBlob.AddForce?
    Snippet from onTick handler:
    Code:
    //Create an array of blob object references
    CBlob@[] nearbyBlobs;
    
    //Create a handle for a blob object
    CBlob@ nearbyBlob;
    
    //Check if any blobs are within radius
    if(this.getMap().getBlobsInRadius(this.getPosition(), this.getRadius(), @nearbyBlobs)) {
    
      //Determine current mode
      bool isFacingLeft = this.isFacingLeft();
     
      //Iterate through blob objects
      for(u8 i = 0; i<nearbyBlobs.length; i++) {
     
        //Keep a reference to this blob object
        @nearbyBlob = nearbyBlobs[i];
       
        //Create a vector representing the relative displacement
        Vec2f relativeDisplacement = nearbyBlob.getPosition() - this.getPosition();
       
        //Check if blob is not this blob, not tagged as conveyor and above
        if(nearbyBlob !is this && !nearbyBlob.hasTag("isConveyor") && relativeDisplacement.y < 0.5f) {
       
          //Create a movement vector
          Vec2f movement(Transports::ConveyorVariables::SLOW_VELOCITY * nearbyBlob.getMass(), 0.0f);
         
          //Check if conveyor is facing left
          if(isFacingLeft) {
         
            //Set horizontal movement in left direction
            movement.x *= -1.0f;
         
          }
         
          //Add a force to the left
          nearbyBlob.AddForce(movement);
         
        }
       
       
      }
     
    }
     
  2. Verrazano

    Verrazano Flat Chested Haggy Old Souless Witchy Witch Witch THD Team Global Moderator Forum Moderator Tester
    1. Practitioners of War Extreme Revolution - POWER

    Messages:
    477
    It's fine that you are making frequent posts in the modding help section, hopefully other people will be able to glean information from them.

    If your problem is that things move at different speeds why not use velocity instead of addforce. Adding force applies a different amount of acceleration per object being acted on dependent on it's mass.

    I assume you are referring to this bit, saying that it isn't correct.
    However looking at the actual code you can see it's applied correctly.
    So the answer is addForce simply applies force according to f = ma

    side note: you should probably figure out irc. I'm on there quite a lot, and I will always see your message if you leave it and answer as soon as you're back.
     
    Last edited: Feb 6, 2016
  3. Vermilicious

    Vermilicious Ballista Bolt Thrower

    Messages:
    232
    I think I was going about this the wrong way.

    It is acceleration (a) I need, and that's what's passed to AddForce, like you say (F=m*a). Strictly speaking, I'd rather have seen this method called accelerate, and without the confusing description, but anyway.

    What I've been doing wrong, of course, is to make this call in the onTick handler, leading to crazy high acceleration. I have to do this in the onCollision handler, and when a blob collides with the belt conveyor, I think I'll have to keep track of the other blob to make sure that I don't accelerate it further as long as it didn't leave the track (other than to compensate for friction, perhaps). To do this I'll have to set a "touching belt conveyor"-tag on the blobs when they collide with the belt conveyor. Then I'll have to store blob IDs in the onEndCollision handler when they leave (but not by movement on the y-axis), and in onCollision check if the ID of the current colliding blob recently left a neighboring belt conveyor. A little tricky!

    Edit:Actually, I might not have to store the IDs after all, if I just remove the touching flag in the onEndCollision check. Any suggestion as to how to check if the blob is moving upwards? MovementVars.keys? Somehow, .getVelocity() often returns a y-component that isn't 0, and the player character is visually jittering a bit. (code available at: https://github.com/ANybakk/kag-tran...res/Conveyor/BeltConveyor/BeltConveyorBlob.as )
     
    Last edited: Feb 7, 2016
  4. Osmal

    Osmal Bison Rider Donator
    1. [Server] Sandbox Reborn

    Messages:
    13
    I made some too, but I ended up using 'blob.setVelocity'. It's not that realistic for objects that collide the conveyor with high speed, but it's good for now.
    [​IMG]
     
    TFlippy, Noburu, makmoud98 and 2 others like this.
  5. Vermilicious

    Vermilicious Ballista Bolt Thrower

    Messages:
    232
    That looks pretty good, Osmal. You made it work with mechanisms too.

    Did you experience any kind of jitter/wobbeling? How did you actually end up setting velocity, in onCollision or onTick? What about character blobs, how does it work when you press buttons?

    Btw, I'm curious; how do people do video capture in gif format? Is this something the engine supports, or some 3rd party software?
     
  6. Osmal

    Osmal Bison Rider Donator
    1. [Server] Sandbox Reborn

    Messages:
    13
    Stuff move on it smooth enough I think. I set it in onCollision and in onEndCollision
    blob.setVelocity(Vec2f((this.isFacingLeft() ? -4.0f : 4.0f), 0.0f));
    It currently doesnt move stuff that is placed on it when its not powered, and then powered - it needs the collision.

    I use ScreenToGif program, its okay I suppose :rekt:
     
    PUNK123 likes this.
  7. Verrazano

    Verrazano Flat Chested Haggy Old Souless Witchy Witch Witch THD Team Global Moderator Forum Moderator Tester
    1. Practitioners of War Extreme Revolution - POWER

    Messages:
    477
    You still misunderstand how the function works. You add a force not an acceleration. A force is then turned into acceleration by multiplying it by 1/mass. Each time step the object takes the current force and converts it to acceleration then adds it to the velocity before updating the position. The forces acting on the object are then reset at the end of the time step.

    Also make sure you are getting these quantities straight. Acceleration is a change in velocity not position.

    Re: every object will move at a different rate when having a force applied because it's dependent on the object's mass. That is not how a conveyor belt works. Conveyor belts move objects at a constant speed. I'd suggest doing what the guy above did which is to just use setVelocity like I said in my first post.

    You can use onCollide/onEndCollide to decide when to use setVelocity.
     
    Last edited: Feb 7, 2016
  8. Geti

    Geti Please avoid PMing me (poke a mod instead) THD Team Administrator Global Moderator

    Messages:
    3,730
    The function would be better named AddImpulse, as it's not a force in the sense that it's not divided across time, but applied as an instantaneous application of that many newtons to the object.

    Pleased to see more people messing around with custom mechanisms and the like. Keep your eyes out for @Skinney's dummy tile changes which may require a bit of conversion work on your part (but will make your blob tiles act like real tiles for collision, lighting and water flow purposes if it works out)
     
    Asu likes this.
  9. Vermilicious

    Vermilicious Ballista Bolt Thrower

    Messages:
    232
    Okay, so the description is right. You add a force, but only for that tick, meaning that the change is temporary, but the effect is not. At least that's how I interpret what you're saying. If that is the "interface" available for adding force, it's just not going to work. I guess I expected the blob to keep track of whatever forces was working on it. Stuff on belt conveyors move at a constant speed because the friction keeps them there (otherwise they would flip around), When they first hit the belt, they are accelerated, and when they leave, they will decelerate. But anyway, if I can't say that a force/acceleration is going to be applied until a certain point in time, I guess I'll have to set velocity to mimic the behavior. The question then is how I'm going to manipulate a blob's velocity when that blob could be doing all kinds of things at the time, for instance being controlled by a player or if it's a rolling boulder. I'm not sure atm.
     
  10. Geti

    Geti Please avoid PMing me (poke a mod instead) THD Team Administrator Global Moderator

    Messages:
    3,730
    The reason it would be more correctly called an impulse is that it's not divided by the timestep. If you want to apply a force in the strict sense, you will have to divide it by the tick rate (30 if you're happy with that being hardcoded, getTicksASecond if not, but ensure you're doing floating division!)

    It sounds to me like you've simply misunderstood some of the more fiddly definitions. If you've ever done physics, forces result in an acceleration, which happens as an object changes direction or speed (or both) (ie, a change in velocity). By definition, a force results in an acceleration, which results in a changing velocity, not a constant one.

    Re: conveyors - they dont apply a constant force except in the upward direction, countering gravity (like any flat surface). The force on the perpendicular direction is variable and as you say, is a result of friction. The way you can model this is by subtracting the blobs velocity from the "intended" velocity (direction of the conveyor), multiplying through a few constants to tweak the force to be right, and then applying that force to the blob. This will need to be done the whole time the blob is overlapping the conveyor, and NOT done multiple times for separate conveyor blocks (or you'll get doubled forces at every seam). It would probably best to do it in a rules script for that reason.
    This is a simple modelling of friction against a moving surface and will result in a constant speed, precisely because no force will be applied if the object is going the right speed, so it will remain at a constant speed.

    Here's a simple diagram to help out.
    [​IMG]
    This will basically end up as "about the same thing" as setting the velocity directly, but with a bit of leeway for blobs giving their own movement with forces (eg from walking, bombs going off nearby, hit by arrows, etc)
     
    DatNobby and Vermilicious like this.
  11. Vermilicious

    Vermilicious Ballista Bolt Thrower

    Messages:
    232
    Thanks for more excellent input, Geti. I think that is the right way to go.

    I'm still having trouble with player characters bumping into things on the track, causing vertical movement and thus losing their "touching" flag, and makes it harder to see if it's working properly. You mentioned some "dummy" tile changes, Geti, are you referring to stuff done in LoaderUtilities.as, such as map.AddTileFlag(index, Tile::SOLID | Tile::COLLISION); ? I tried that, but I'm still experiencing this issue. My default sprite frame has some transparent pixels on the edges, because when there's only one tile, it should visually appear to have rounded edges. Does the game engine use these sprite pixels for collision checking, even though the animation or frame is changed at some point?

    A snippet from the configuration file:
    Code:
    $shape_factory                            = box2d_shape
    @$shape_scripts                           =
    f32 shape_mass                            = 200.0
    f32 shape_radius                          = 0.0
    f32 shape_friction                        = 0.1
    f32 shape_elasticity                      = 0.0
    f32 shape_buoyancy                        = 0.0
    f32 shape_drag                            = 0.2
    bool shape_collides                       = yes
    bool shape_ladder                         = no
    bool shape_platform                       = no
    @f32 verticesXY                           = 0.0;  0.0;
                                                8.0;  0.0;
                                                8.0;  8.0;
                                                0.0;  8.0;
    u8 block_support                          = 0
    bool block_background                     = no
    bool block_lightpasses                    = no
    bool block_snaptogrid                     = yes
    Edit: This actually happens on vanilla platform blocks too when a player walks/runs over them. You can see it by the red dots being drawn in debug mode, that occasionally, there's a slight deviance on the y-axis. Does not happen on regular tiles (wood, stone etc)
     
    Last edited: Feb 15, 2016
  12. Geti

    Geti Please avoid PMing me (poke a mod instead) THD Team Administrator Global Moderator

    Messages:
    3,730
    Re: dummy tiles - yes, though it wont happen for platforms as the tilemap collisions no longer support platforms, sadly. The idea is to make "solid tiles" that have a blob in place doing logic still use the tilemap for collision, water blocking, flammability, and support.

    Re: does the game collide using pixels - nope, uses whatever polygons it was given. You'll want to make sure you've got your offsets right but if it shows up as a properly aligned box in debug mode then you should be fine.

    Re: jitter from stopping touching - this is why i'd kind of suggest doing it in a rules script, but it'd be complicated - you'd have to gather all areas to check for objects (probably using map sectors or similar), gather all directions for those objects (based on the nearest conveyor facing direction), and then apply all the forces based on each object's existing velocity.
     
  13. Vermilicious

    Vermilicious Ballista Bolt Thrower

    Messages:
    232
    Adding tiles in the background seems like a hack, considering blob shapes can be set to collide and otherwise act as "tiles" in the configuration file. I'm a bit curious about why collisions for stationary blobs are any different from tiles. I'd call that a pretty big flaw. That it's clearly visible even with vanilla blob blocks like the platform, doesn't really make it borderline either. Skinney's components are basically all using this hack. I don't like it.

    Okay, I think it's safe to tag this topic as solved now. It's not working quite perfect, but it's pretty darn close now. I think I'll have to add friction into the calculations at some point, so that the belt isn't totally slippery when disabled, for instance. And materials don't seem to move anymore. Anyway. here's the general recipe:
    • onCollision: Set a "touching" flag on the other blob so that you know it's touching the belt. Also store the ID of the belt conveyor segment.
    • onEndCollision: Remove the "touching" flag if the other blob's velocity on the y-axis indicates it's leaving the belt (<0.0f).
    • onTick: Iterate over all "touching" blobs within range (one tile above) that is touching this segment. Determine the difference in desired velocity with the actual velocity of the blob, and with it calculate what acceleration, and force, to apply (keeping in mind the tick rate). and update velocity.
    • onSetStatic: Set a tile type with proper collision, and bring the sprite in front of it
    • onDie: Remove tile
    • .cfg: Disable shape collision
    (Latest commit, for those interested: https://github.com/ANybakk/kag-transports/tree/692a41c27746ec44b2c26e11b4fdc702fdf9c241)

    Update: Changed since the solution ended up quite differently later on. The touching-flag approach ended up just being a problem when it came to objects such as logs, which sometimes has vertical movement. Also not messing with forces anymore.
     
    Last edited: Feb 19, 2016
  14. Geti

    Geti Please avoid PMing me (poke a mod instead) THD Team Administrator Global Moderator

    Messages:
    3,730
    Blob collisions will never be "perfectly smooth" - its partly an issue with box2d and partially an issue with our use of it. It's definitely a big flaw, but using the tilemap for tile collisions is a good use for it, not a bad one. It basically comes from colliding with the "hidden corner" between two block edges, and there's nothing we can do to avoid box2d solving it that way. The way the tilemap collision shapes work is lines generated at the edge of solid/not solid tiles.

    The way collisions worked in classic was totally different which is why a lot more leeway was possible at that time.

    Either way, glad the issue at hand is solved.
     
    Vermilicious likes this.
  15. Vermilicious

    Vermilicious Ballista Bolt Thrower

    Messages:
    232
    One extra question:
    I quickly found out I have to take touching blobs' friction into account, but something's not right. The question is, the value set for friction (and drag) in a blob's configuration file, is that per second, per frame or per some fixed rate?

    Code:
    //Determine what acceleration to apply (a = v / t)
    Vec2f acceleration = (targetVelocity - currentVelocity) / (Transports::ConveyorVariables::TIME_FOR_TARGET_VELOCITY * getTicksASecond());
    
    //Determine blob's friction
    f32 friction = nearbyBlob.getShape().getFriction() / getTicksASecond();
    
    //Add force impulse (F = m * a)
    nearbyBlob.AddForce((acceleration + Vec2f(friction, 0.0f)) * nearbyBlob.getMass());
     
  16. Verrazano

    Verrazano Flat Chested Haggy Old Souless Witchy Witch Witch THD Team Global Moderator Forum Moderator Tester
    1. Practitioners of War Extreme Revolution - POWER

    Messages:
    477
    Friction isn't applied like that. Fs = n*us where Fs is static friction force and n is the normal force (-mg) us (mu of static friction is a factor) the Fs is always opposing a force applied to an object when the two objects are in contact. once static friction is overcome it's easier to move the objects because now it is effected by kinetic friction which has a smaller factor, same equation different factor, uk (mu of kinectic friction). That's a basic summary of how friction works in simple cases. It's just another force that applies to the object, so you do Fnet = F + Fs (static friction) = ma (if you are moving an object from rest). You can look up many tutorials for it, for a more clear explanations.

    inb4 thread renamed to "Physics Homework Discussion".
     
    Last edited: Feb 17, 2016
    FuzzyBlueBaron and Geti like this.
  17. Geti

    Geti Please avoid PMing me (poke a mod instead) THD Team Administrator Global Moderator

    Messages:
    3,730
    Your wish is my command ;)

    Note that in KAG some things end up being nonphysical too - eg drag is a simple point drag model and I'd be unsurprised if it was applied per-frame to the velocity, but its been years since I've looked at that code. I can tell you that all the drag and friction values in game are only that way because they "seemed good", not "were physically correct".

    If we were doing real drag calculations the game would be even slower :)

    I believe the friction from box2d works the same as verra has explained (ie "real physical friction"), though there's no kinetic friction coefficient in box2d that I'm aware of (and if there is, we're definitely not setting it).
     
  18. Vermilicious

    Vermilicious Ballista Bolt Thrower

    Messages:
    232
    Had to rewrite this post a couple of times.. sorry about that!

    So the friction value is a force (in Newton)?

    The problem is high-friction things such as materials. For them to reach the next belt conveyor segment before they reach a velocity of 0, I have to compensate for this friction. I added friction as an additional force (marginally larger), but I end up with different velocities again; materials go flying. If I divide that force with getTicksASecond(), the force is not strong enough to move it. So, I'm a bit puzzled (back to my question).

    Code:
    //Determine what acceleration to apply (a = v / t)
    Vec2f acceleration = (targetVelocity - currentVelocity) / (Transports::ConveyorVariables::TIME_FOR_TARGET_VELOCITY * getTicksASecond());
    
    //Obtain friction value
    f32 friction = nearbyBlob.getShape().getFriction();
    
    //Check if conveyor is facing left
    if(isFacingLeft) {
    
      //Set left direction
      friction *= -1.0f;
    
    }
    
    //Add force impulses (F = m * a + static friction)
    nearbyBlob.AddForce(acceleration * nearbyBlob.getMass() + Vec2f(friction + 0.1f, 0.0f));
    Edit: By only applying the force when velocity reaches 0, the materials don't fly, but their velocity isn't steady, and deviates slightly from other things. Hmm...

    Code:
    //Determine what acceleration to apply (a = v / t)
    Vec2f acceleration = (targetVelocity - currentVelocity) / (Transports::ConveyorVariables::TIME_FOR_TARGET_VELOCITY * getTicksASecond());
    
    //Set a friction compensation force by determining the difference in friction
    f32 friction = nearbyBlob.getShape().getFriction() - this.getShape().getFriction() + 0.1f;
    
    //Check if conveyor is facing left
    if(isFacingLeft) {
    
      //Set left direction
      friction *= -1.0f;
    
    }
    
    //Check if velocity isn't 0
    if(currentVelocity.x != 0.0f) {
    
      //Set friction to 0
      friction = 0.0f;
    
    }
    
    //Add force impulses (F = m * a + static friction)
    nearbyBlob.AddForce(acceleration * nearbyBlob.getMass() + Vec2f(friction, 0.0f));
    [​IMG]
     
    Last edited: Feb 19, 2016
  19. Verrazano

    Verrazano Flat Chested Haggy Old Souless Witchy Witch Witch THD Team Global Moderator Forum Moderator Tester
    1. Practitioners of War Extreme Revolution - POWER

    Messages:
    477
    + Vec2f(friction + 0.1f, 0.0f) the problem is that you are trying to compensate mostly real physics with pseudo maths. If you want to compensate for the force of friction. it'd be ((-1|1, depending on direction)*abs(mass * gravity) * friction factor) + intended force.

    f32 friction = nearbyBlob.getShape().getFriction() - this.getShape().getFriction() + 0.1f;
    This wont give you anything useful. The only way to find a friction value between two surfaces is empirically. Since we can't do that with box2d, it just has a system for deciding what the coefficient will be, the equation is friction factor = sqrt(frictionA * frictionB).
     
    Last edited: Feb 18, 2016
    Asu likes this.
  20. Vermilicious

    Vermilicious Ballista Bolt Thrower

    Messages:
    232
    Clearly, this is more complicated than I thought. I honestly don't remember this from my school days, but then physics wasn't exactly my favorite subject either :o)

    I tried doing as you say, Verrazano, but I'm still not seeing the result I expect. The materials are travelling really slowly (or super fast without dividing by tick rate). The belt conveyor ("this") has a friction of 0.3, while mat_wood is 0.8 and builder is 0.07. I'm not sure what else can be at work here.

    Code:
    //Determine what acceleration to apply (a = v / t)
    Vec2f acceleration = (targetVelocity - currentVelocity) / (Transports::ConveyorVariables::TIME_FOR_TARGET_VELOCITY * getTicksASecond());
    
    //Determine what force to apply (F = m * a)
    Vec2f force = acceleration * nearbyBlob.getMass();
    
    //Determine what friction countering force to apply
    Vec2f frictionForce = Vec2f(0.0f, 0.0f);
    
    //Check if velocity is 0
    if(currentVelocity.x == 0.0f) {
    
      //Determine the friction between the blob and conveyor segment
      f32 frictionFactor = Maths::Sqrt(nearbyBlob.getShape().getFriction() * this.getShape().getFriction());
    
      //Determine the force of friction that is necessary to surpass to initiate movement (Fs = Fn * us, Fn = m * g * cos(a))
      frictionForce.x = (nearbyBlob.getMass() * sv_gravity * Maths::Cos(0.0f) * frictionFactor) / getTicksASecond() + 0.1f;
    
      //Check if conveyor is facing left
      if(isFacingLeft) {
    
        //Set left direction
        frictionForce.x *= -1.0f;
    
      }
    
    }
    
    //Add force impulses
    nearbyBlob.AddForce(force + frictionForce);
     
    Last edited: Feb 18, 2016