📓

PhysicsComponent

public class PhysicsComponent : MonoBehaviour, IEntityComponent
{

    public SVector startingPos;
    //Where the entity/thing is. Make sure you give a reference to the correct transform!
    public STransform sTransform;
    //basic phys force stuff
    public SVector velocity = new SVector();
    public SVector acceleration = new SVector();
    //lazy friction. if 1, no friction, 0 means instant stop.
    public sfloat frictionMultiplier = (sfloat)1;
    //cheating phys stuff
    //use this for instant velocity that works outside of the basic phys
    public SVector instantVelocity = new SVector();
    //FINAL VELOCITY (velocity+instantVelocity)
    public SVector finalVelocity = new SVector();

    //easy way to get around the local rollback and inputs. Forces I create for myself(not acted upon me)
    public SVector localAcceleration = new SVector();
    public SVector localVelocity = new SVector();

    public ObjectRect collisionBox = null;
    public bool isSolid = false;
    public bool bumpsIntoSolids = false;
    public bool destroyOnCollision = false;
    
    //testing 
    public ObjectRect preRollBackRect;
    public ObjectRect broadPhaseCollisionTest;


    [SerializeField]
    Vector3 velocityVec = new Vector3();
    [SerializeField]
    Vector3 accelerationVec = new Vector3();

    //NETWORK RELATED
    //==================
    //BACKUP BEFORE ROLLBACK
    public SVector positionPrevious = new SVector();
    public SVector velocityPrevious = new SVector();
    public SVector accelerationPrevious = new SVector();//impulse force?
    public SVector instantVelocityPrevious = new SVector();
    public SVector finalVelocityPrevious = new SVector();

    //lerp targets
    public SVector lerpStartPosition = new SVector();
    public SVector targetLerpPosition = new SVector();
    public int framesSinceLastPacket = 0;
    public float unityDeltasSinceLastPacket = 0;

    //just to keep track for lerp reasons
    public bool firstPacketRead = true;
    public SVector positionMostRecentFromServer = new SVector();
    public SVector velocityMostRecentFromServer = new SVector();
    public SVector accelerationMostRecentFromServer = new SVector();
    public SVector instantVelocityMostRecentFromServer = new SVector();
    public SVector finalVelocityMostRecentFromServer = new SVector();

    //lerp types
    public LerpType lerpType = LerpType.snapshotLerp;

    //for collision testing
    public bool collisionThisFrame = false;
    public bool destroyNextFrame = false;
//...

This component deals with a lot of the 2d physics in the game. Using basic acceleration, velocity, position we have a simple system that can handle basic 2d game scenarios. If you want full box2d type of physics, you would need to fully remodel this class.

Currently the physics part of this engine is only working with axis aligned bounding boxes (rectangles). Damage can work with circles too, but here the physics works with rectangles that cannot be rotated(axis aligned). To represent this box is the attribute called collisionBox.

Near collisionBox are some other attributes worth mentioning.

IsSolid: do things bump into this entity. E.g some games have it so players can walk through each other so they cant box each other in. They can bump into solids, but they are not solids themselves

bumpsIntoSolids : does this entity bump into solids(the level solids or other entities). Player probably doesn’t walk through walls, but ghosts might

destroyOnCollision: kill this entity when it collides with a solid. Handy for bullets hitting walls

Another attribute of note is lerpType. The other attributes mostly help with prediction, lerping and other fun networking problems

LerpType: When snapshots come in from the server, how do we deal with that data? For example our own player uses lerp type of None, we don’t want to interpolate between snapshots, we want to do client side prediction. However when the players and NPC’s around us get data from the server, to keep things looking smooth we’ll use another lerp type to smoothly move them from last server position to the current position

NOTE: All Entities have a PhysicsComponent reference attribute. However it can be set to null. A good deal of the networking challenges are dealt within this component and to make that easier to access and predict, this component is directly accessible that way. However, thanks to polymorphism, you can extend this class and pass the sub class to an entity instead if you want to modify how just a few entities do physics compared to everything else.

Methods

public virtual void Init(STransform sTransform)

Pass the entity transform reference to the physics component as soon as you can. That way when this component updates it will be updating the entities position

public virtual void ResetPhysics()

Jump position to starting position, and velocity and acceleration to zero

public virtual void ApplyForces()

Calculate finalVelocity for this frame based on acceleration and velocity

public void ApplyLocalForces()

Local forces represent forces the entity applies to itself. For example a player pressing input to control where they are walking. The goal here is to have your other forces act as those being created from external sources (e.g wind, gravity, being smacked around) and your local ones generated by the entity itself, usually their own movement. That way in code we can see the two fight against each other to help calculate that frames finalVelocity. For example a player is smacked into the air(external acceleration and velocity) and the player is using a dash ability(local acceleration and velocity) to try counter that motion

public virtual void UpdateComponent()

Apply forces and try move the entity with help of the CollisionManager. May also setup data for interpolating the entity

public virtual void UpdateUnityPositions()

All the maths done here is with SoftFloat library, this converts the calculations to floats and updates the Unity transform. If we don’t do this, all the math will be fine in the background, but you wont see those positional changes visually in the game. We still need to update the GameObjects for Unity’s rendering

public SRect GetCollisionBoxWorldRect()

CollisionBox represents how this entity interacts with solids in this world or how the world interacts with this entity. CollisionBox x,y are in relative/local coordinates which helps for placement around the entity, but for maths we need it in world space. This creates a SRect with coordinates the rest of the world can understand

public virtual void Update()

This is Unity’s MonoBehaviour update and is called out of sync to the rest of the networking codes tick time. We’re using this to help interpolate/lerp physicsComponents with a LerpType of snapshotLerp or predictedLerp. Helps for smooth movement. Likely used for all entities in the world that are not your current player. Do not do any important networked logic in here.

public void BackUpBeforeSnapshotReading()

Backs up all physics data before reading a server snapshot

public virtual void FixAnyNaNPhysics()

NaN - Not a Number error. When a float or softFloat math is used incorrectly it can be set to the value of NaN. This will cause all sorts of problems and crash the game, so to avoid this if any our softFloat values are NaN(e.g velocity, acceleration etc) they will be set to zero

public virtual void MoveWithCollisionData(sfloat shortestCollisionTime, sfloat normalx, sfloat normaly)

When calling UpdateComponent and trying to move using the CollisionManager, we may collide with something. In that event, this function is called to deal with the result.

public virtual void DoLerp()

Smoothly interpolate the entity between its last position and newest position received from a server snapshot

private void DoFinerLerp()

Similar to the above but smoother. Its called from this components Update function

public virtual void WriteToPacket(NetDataWriter writer)

This writes the recommended data about a physics component to a NetDataWriter which will get used to fill in data to a networked packet. This function is optional, to use it call it from your entity’s WriteToPacket method. Alternatively you could write the parts you want from the component within Entity directly, but this is a good starting point

public virtual void ReadFromPacket(NetDataReader reader)

The reverse of the previous method, reads the recommended data about a physics component out from a network packet. Optional method again, you can Read the parts you had wrote in the previous step. As long as you mirror what’s written to what’s read you wont run into any issues with packets

public void SetupLerpFromPacketData(SVector previousPosition)

This method works with next one as a combo. When a packet comes in and an entity needs to lerp from its previous position to the new one, this method does some basic setup and calls SetupLerpForNextFrame

private void SetupLerpForNextFrame(SVector previousPosition)

This method works out the start and end positions of an entity carrying this physics component for it to lerp between.

public void StoreMostRecentDataFromServer()

Backs up some recent data from the server into some attributes so we can compare them to the next incoming packet when that happens

public void UpdateCollisionBoxColour()

This is mostly for testing and only visible within the Unity Editor when running the game. If the collision box for this physics component has collided with something this frame, it will be red. If not, green