Moving physics from Tick to HandlePhysics
#1
Bonjour!

I have recently been working on the Minecarts code, and it seems reasonably realistic. However, x-soft wants me to move the physics code from cMinecart::Tick to cMinecart::HandlePhysics.

However, I cannot get this to work. I understand that I have to do a:
Code:
virtual void HandlePhysics (float a_Dt, cChunk & a_Chunk) override;
in Minecart.h, then move the physics code to void HandlePhysics in Minecart.cpp, but the latter isn't doing anything.

Do I need a special super::Tick? super::HandlePhysics? HandlePhyics()? Do I need to copy the entire HandlePhysics code from Entity.cpp and add my own stuff because I'm overriding it?

I be confused: Huh
Reply
Thanks given by:
#2
First of all, make sure your cMinecart::HandlePhysics() is called - put a breakpoint in it and place a minecart. If the breakpoint isn't hit, then there's something wrong. Also, make sure you remove the cMinecart::Tick() completely, both from the header and the cpp file (comment it out, so that you can keep your code for later)

If it is called, then paste all the physics handling code into it. Note that the code should not call super::Tick(); it might call super::HandlePhysics() if you want it to, but it's not required.

When in doubt, put LOGD() in key places to verify that things are what you expect them to be.
Reply
Thanks given by:
#3
That worked :D

It broke stuff a little, but it works :DD

So now if I want to negate friction when on a rail, I will just do this in cMinecart::HandlePhysics?
Code:
if (
                (BlockBelow != E_BLOCK_RAIL) &&
                (BlockBelow != E_BLOCK_DETECTOR_RAIL) &&
                (BlockBelow != E_BLOCK_POWERED_RAIL) &&
                (BlockBelow != E_BLOCK_ACTIVATOR_RAIL)
                )
            {
                //Friction
                if (NextSpeed.SqrLength() > 0.0004f)
                {
                    NextSpeed.x *= 0.7f/(1+a_Dt);
                    if ( fabs(NextSpeed.x) < 0.05 ) NextSpeed.x = 0;
                    NextSpeed.z *= 0.7f/(1+a_Dt);
                    if ( fabs(NextSpeed.z) < 0.05 ) NextSpeed.z = 0;
                }
            }
Reply
Thanks given by:
#4
I don't quite understand what "negating friction" means, and your code is definitely not "when on a rail". Not to mention it's an unreadable mess of characters, without the spaces around operators it's quite difficult to human-parse.

Slightly offtopic rambling: I keep wondering, should it be BlockBelow? I think the minecart entity actually "rides" inside the rail blocks - the rail block is the entire 1m x 1m x 1m thing, although it is drawn only as the rail at the bottom, so IMHO both the minecart entity's center and pivot (XZ center, Y bottom) lie inside the rail block.
Reply
Thanks given by:
#5
(08-30-2013, 10:20 PM)xoft Wrote: I don't quite understand what "negating friction" means, and your code is definitely not "when on a rail". Not to mention it's an unreadable mess of characters, without the spaces around operators it's quite difficult to human-parse.

Slightly offtopic rambling: I keep wondering, should it be BlockBelow? I think the minecart entity actually "rides" inside the rail blocks - the rail block is the entire 1m x 1m x 1m thing, although it is drawn only as the rail at the bottom, so IMHO both the minecart entity's center and pivot (XZ center, Y bottom) lie inside the rail block.

Oh, the friction code wasn't written by me, only the checking for rails part.

Negating friction means that the physics coding thinks the cart is on the ground (even if on a rail), and so applies friction, and so breaks flat rails, and therefore I want to stop it from happening when the cart is on rails. It definitely works, but the question is how to move the "if is rail" bits to cMinecart as you suggest?

I tried with BlockIn, it doesn't work. Sad
Reply
Thanks given by:
#6
I would restructure the code this way: If the minecart is not on rails, do the same physics emulation as in cEntity (call super::HandlePhysics() ); otherwise apply rail-based physics (the new code that you wrote).

Because checking if a block is a rail is done in more places, it would make sense to make it into a function:
class cMinecart
{
  ...
  /// Returns true if the specified blocktype is any kind of a rail
  static bool IsRailBlockType(BLOCKTYPE a_BlockType);
  ...
}
Reply
Thanks given by:
#7
Okay. Just to say, I couldn't get anything to simulate without a super::HandlePhysics in Minecart::HandlePhysics.
Reply
Thanks given by:
#8
Have a look at cProjectileEntity, it does its own physics handling, too.

Gimme your whole HandlePhysics() function, there's no point on discussing code without the actual codeTongue
Reply
Thanks given by:
#9
Okay, please bear with me while I try to solve the newly introduced bugs with the Y speed. Smile
Reply
Thanks given by:
#10
Code:
// Minecart.cpp

// Implements the cMinecart class representing a minecart in the world
// Indiana Jones!

#include "Globals.h"
#include "Minecart.h"
#include "../World.h"
#include "../ClientHandle.h"
#include "Player.h"





cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) :
    super(etMinecart, a_X, a_Y, a_Z, 0.98, 0.7),
    m_Payload(a_Payload)
{
    SetMass(20.f);
    SetMaxHealth(6);
    SetHealth(6);
}




bool cMinecart::Initialize(cWorld * a_World)
{
    if (super::Initialize(a_World))
    {
        a_World->BroadcastSpawnEntity(*this);
        return true;
    }
    return false;
}





void cMinecart::SpawnOn(cClientHandle & a_ClientHandle)
{
    char SubType = 0;
    switch (m_Payload)
    {
        case mpNone:    SubType = 0; break;
        case mpChest:   SubType = 1; break;
        case mpFurnace: SubType = 2; break;
        case mpTNT:     SubType = 3; break;
        case mpHopper:  SubType = 5; break;
        default:
        {
            ASSERT(!"Unknown payload, cannot spawn on client");
            return;
        }
    }
    a_ClientHandle.SendSpawnVehicle(*this, 10, SubType); // 10 = Minecarts, SubType = What type of Minecart
}





void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{

    super::HandlePhysics(a_Dt, a_Chunk); // Main physics handling

    /*
    NOTE: Please bear in mind that taking away from negatives make them even more negative,
    adding to negatives make them positive, etc.
    */
    
    BLOCKTYPE BelowType;
    NIBBLETYPE BelowMeta;
    double SpeedX = GetSpeedX(), SpeedY = GetSpeedY(), SpeedZ = GetSpeedZ(); // Get current speed

    // Get block type & meta below the cart
    GetWorld()->GetBlockTypeMeta(floor(GetPosX()), floor(GetPosY() -1 ), floor(GetPosZ()), BelowType, BelowMeta);

    if ((BelowType == E_BLOCK_RAIL) || (BelowType == E_BLOCK_DETECTOR_RAIL) || (BelowType == E_BLOCK_ACTIVATOR_RAIL))
    {
        switch (BelowMeta)
        {

            case E_RAIL_ZM_ZP: // NORTHSOUTH
            {
                SpeedY = 0; // Don't move vertically as on ground
                
                // Set Y as current Y rounded up to bypass friction
                SetPosY(floor(GetPosY()));

                if (SpeedZ != 0) // Don't do anything if cart is stationary
                {
                    if (SpeedZ > 0)
                    {
                        // Going SOUTH, slow down
                        SpeedZ = SpeedZ - 0.1;
                    }
                    else
                    {
                        // Going NORTH, slow down
                        SpeedZ = SpeedZ + 0.1;
                    }
                }
                break;
            }

            case E_RAIL_XM_XP: // EASTWEST
            {

                SpeedY = 0;

                SetPosY(floor(GetPosY()));

                if (SpeedX != 0)
                {
                    if (SpeedX > 0)
                    {
                        SpeedX = SpeedX - 0.1;
                    }
                    else
                    {
                        SpeedX = SpeedX + 0.1;
                    }
                }
                break;
            }

            case E_RAIL_ASCEND_ZM: // ASCEND NORTH
            {

                SetPosY(floor(GetPosY()) + 0.3); // It seems it doesn't work without levitation :/

                if (SpeedZ >= 0)
                {
                    // SpeedZ POSITIVE, going SOUTH
                    if (SpeedZ <= 8) // Speed limit of 8 SOUTH (m/s??)
                    {
                        SpeedZ = SpeedZ + 0.5; // Speed up
                        SpeedY = (0 - SpeedZ); // Downward movement is negative (0 minus positive numbers is negative)
                    }
                    else
                    {
                        SpeedZ = 8; // Enforce speed limit
                        SpeedY = (0 - SpeedZ);
                    }
                }
                else
                {
                    // SpeedZ NEGATIVE, going NORTH
                    SpeedZ = SpeedZ + 0.4; // Slow down
                    SpeedY = (0 - SpeedZ); // Upward movement is positive (0 minus negative number is positive number)
                }
                break;
            }

            case E_RAIL_ASCEND_ZP: // ASCEND SOUTH
            {

                SetPosY(floor(GetPosY()) + 0.3);

                if (SpeedX > 0)
                {
                    // SpeedZ POSITIVE, going SOUTH
                    SpeedZ = SpeedZ - 0.4; // Slow down
                    SpeedY = SpeedZ; // Upward movement positive
                }
                else
                {
                    if (SpeedZ >= -8) // Speed limit of 8 NORTH (m/s??)
                    {
                        // SpeedZ NEGATIVE, going NORTH
                        SpeedZ = SpeedZ - 0.5; // Speed up
                        SpeedY = SpeedZ; // Downward movement negative
                    }
                    else
                    {
                        SpeedZ = -8; // Enforce speed limit
                        SpeedY = SpeedZ;
                    }
                }
                break;
            }

            case E_RAIL_ASCEND_XM: // ASCEND EAST
            {

                SetPosY(floor(GetPosY()) + 0.3);

                if (SpeedX >= 0)
                {
                    if (SpeedX <= 8)
                    {
                        SpeedX = SpeedX + 0.5;
                        SpeedY = (0 - SpeedX);
                    }
                    else
                    {
                        SpeedX = 8;
                        SpeedY = (0 - SpeedX);
                    }
                }
                else
                {
                    SpeedX = SpeedX + 0.6;
                    SpeedY = (0 - SpeedX);
                }
                break;
            }

            case E_RAIL_ASCEND_XP: // ASCEND WEST
            {

                SetPosY(floor(GetPosY()) + 0.3);

                if (SpeedX > 0)
                {
                    SpeedX = SpeedX - 0.6;
                    SpeedY = SpeedX;
                }
                else
                {
                    if (SpeedX >= -8)
                    {
                        SpeedX = SpeedX - 0.5;
                        SpeedY = SpeedX;
                    }
                    else
                    {
                        SpeedX = -8;
                        SpeedY = SpeedX;
                    }
                }
                break;
            }

            default:
            {
                ASSERT(!"Unhandled rail meta!");
                break;
            }

        }
    }

    // Set speed to speed variables
    SetSpeedX(SpeedX);
    SetSpeedY(SpeedY);
    SetSpeedZ(SpeedZ);


    // Broadcast position to client
    BroadcastMovementUpdate();
}





void cMinecart::DoTakeDamage(TakeDamageInfo & TDI)
{
    std::cout << GetHealth() << std::endl;
    if (GetHealth() == 0)
    {
        Destroy(true);
    }
    else
    {
        m_Health = m_Health - 1;
    }
}





///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cEmptyMinecart:

cEmptyMinecart::cEmptyMinecart(double a_X, double a_Y, double a_Z) :
    super(mpNone, a_X, a_Y, a_Z)
{
}





void cEmptyMinecart::OnRightClicked(cPlayer & a_Player)
{
    if (m_Attachee != NULL)
    {
        if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID())
        {
            // This player is already sitting in, they want out.
            a_Player.Detach();
            return;
        }
        
        if (m_Attachee->IsPlayer())
        {
            // Another player is already sitting in here, cannot attach
            return;
        }
        
        // Detach whatever is sitting in this minecart now:
        m_Attachee->Detach();
    }
    
    // Attach the player to this minecart
    a_Player.AttachTo(this);
}





///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cMinecartWithChest:

cMinecartWithChest::cMinecartWithChest(double a_X, double a_Y, double a_Z) :
    super(mpChest, a_X, a_Y, a_Z)
{
}





void cMinecartWithChest::SetSlot(int a_Idx, const cItem & a_Item)
{
    ASSERT((a_Idx >= 0) && (a_Idx < ARRAYCOUNT(m_Items)));
    
    m_Items[a_Idx] = a_Item;
}





void cMinecartWithChest::OnRightClicked(cPlayer & a_Player)
{
    // Show the chest UI window to the player
    // TODO
}





///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cMinecartWithFurnace:

cMinecartWithFurnace::cMinecartWithFurnace(double a_X, double a_Y, double a_Z) :
    super(mpFurnace, a_X, a_Y, a_Z)
{
}





void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player)
{
    // Try to power the furnace with whatever the player is holding
    // TODO
}





///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cMinecartWithTNT:

cMinecartWithTNT::cMinecartWithTNT(double a_X, double a_Y, double a_Z) :
    super(mpTNT, a_X, a_Y, a_Z)
{
}

// TODO: Make it activate when passing over activator rail





///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cMinecartWithHopper:

cMinecartWithHopper::cMinecartWithHopper(double a_X, double a_Y, double a_Z) :
    super(mpHopper, a_X, a_Y, a_Z)
{
}

// TODO: Make it suck up blocks and travel further than any other cart and physics and put and take blocks
// AND AVARYTHING!!

And in the header.

Code:
// Minecart.h

// Declares the cMinecart class representing a minecart in the world





#pragma once

#include "Entity.h"
#include "../Item.h"





class cMinecart :
    public cEntity
{
    typedef cEntity super;
    
public:
    CLASS_PROTODEF(cMinecart);
    
    enum ePayload
    {
        mpNone,     // Empty minecart, ridable by player or mobs
        mpChest,    // Minecart-with-chest, can store a grid of 3*8 items
        mpFurnace,  // Minecart-with-furnace, can be powered
        mpTNT,      // Minecart-with-TNT, can be blown up with activator rail
        mpHopper,   // Minecart-with-hopper, can be hopper
        // TODO: Spawner minecarts, (and possibly any block in a minecart with NBT editing)
    } ;
    
    // cEntity overrides:
    virtual bool Initialize(cWorld * a_World) override;
    virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
    virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
    virtual void DoTakeDamage(TakeDamageInfo & TDI) override;
    
    ePayload GetPayload(void) const { return m_Payload; }
    
protected:
    ePayload m_Payload;
    
    cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z);
} ;





class cEmptyMinecart :
    public cMinecart
{
    typedef cMinecart super;
    
public:
    CLASS_PROTODEF(cEmptyMinecart);
    
    cEmptyMinecart(double a_X, double a_Y, double a_Z);

    // cEntity overrides:
    virtual void OnRightClicked(cPlayer & a_Player) override;
} ;





class cMinecartWithChest :
    public cMinecart
{
    typedef cMinecart super;
    
public:
    CLASS_PROTODEF(cMinecartWithChest);
    
    /// Number of item slots in the chest
    static const int NumSlots = 9 * 3;
    
    cMinecartWithChest(double a_X, double a_Y, double a_Z);
    
    const cItem & GetSlot(int a_Idx) const { return m_Items[a_Idx]; }
    cItem &       GetSlot(int a_Idx)       { return m_Items[a_Idx]; }
    
    void SetSlot(int a_Idx, const cItem & a_Item);

protected:

    /// The chest contents:
    cItem m_Items[NumSlots];
    
    // cEntity overrides:
    virtual void OnRightClicked(cPlayer & a_Player) override;
} ;





class cMinecartWithFurnace :
    public cMinecart
{
    typedef cMinecart super;
    
public:
    CLASS_PROTODEF(cMinecartWithFurnace);
    
    cMinecartWithFurnace(double a_X, double a_Y, double a_Z);
    
    // cEntity overrides:
    virtual void OnRightClicked(cPlayer & a_Player) override;
} ;





class cMinecartWithTNT :
    public cMinecart
{
    typedef cMinecart super;
    
public:
    CLASS_PROTODEF(cMinecartWithTNT);
    
    cMinecartWithTNT(double a_X, double a_Y, double a_Z);
} ;





class cMinecartWithHopper :
    public cMinecart
{
    typedef cMinecart super;
    
public:
    CLASS_PROTODEF(cMinecartWithHopper);
    
    cMinecartWithHopper(double a_X, double a_Y, double a_Z);
} ;

Broken stuff fixed, dang Ys.

Next: move friction code into cMinecart because x-soft thinks creepers on rails are a bad idea. (If there is ruby on rails, why not creepers?Tongue)
Nextnext: make carthurting work using the confusing entity metadata packet. Temporary solution achieved, need what mobs have - damage based on weaponry, etc, as well as creative instant break.
Nextnextnext: powered rails and curved rails.

You should test the minecarts out to see if there are any bugs!
Reply
Thanks given by:




Users browsing this thread: 9 Guest(s)