Game Programming

Lerp Smoothing

Advantage: Nice smoothing of animations in-place, without having to know/store the start of an animation (like you need for easing functions).

Standard version:

// a - current pos
// b - target pos
// t - factor to move a towards b in each step (0..1)
float lerp(float a,b,t) {
	return (1-t)*a + t*b;
}
void update() {
	a = lerp(a,b,0.01); // move a 1% closer to b each frame
}

-> This is framerate dependent and stops working when framerate is not constant.
-> Cannot simply multiply t by dt, since 2*dt is not the same as calling lerp 2 times (kinda works, but is unstable and can explode if t*dt > 1)

Continuous (non recursive) version:

// F = fraction to move after one frame
// frame based version (n = frame counter)
f(n) = (a-b)*(1-F)^n+b
// time based version
f(t) = (a-b)*(1-F)^(t/dt)+b

-> still framerate dependent

Framerate independent version:

// dt = delta time for last frame in s (Unity: Time.deltaTime)
float lerp_r(float a,b,r,dt) { // r = remainder (in %) after dt
	return b + (a-b)*pow(r,dt);
	// NOTE: pow() is about 8x slower than exp()
}
float lerp_hl(float a,b,hl,dt) { // hl = half life time in s
	return b + (a-b)*exp2(-dt/hl); // exp2(n) = 2^n
}

// Generally use this one (fastest, easy to use):
float lerp_decay(float a,b,l,dt) { // l = decay constant
	return b + (a-b)*exp(-l*dt);
	// useful range for l: 1..25 (slow to fast decay)
}

Source: https://www.youtube.com/watch?v=LSNQuFEDOyQ
Better article with detailed explanations (and also springs!): https://theorangeduck.com/page/spring-roll-call

Engine

https://youtu.be/drCnFueS4og?feature=shared&t=1740

Entity systems

General approaches to a unit/entity system.
Preferred way for moderately complex games: #Preferred Fat/Flat struct (behaviors)
For simple games this can also work well (MathTD): #Naive fat/flat struct (entity types)

OOP

class UnitBase {
	Vec2 pos;
	Vec2 vel;
	int health;
	virtual void simulate(float dt)=0;
}
class UnitGoblin : public UnitBase {
	int damage;
	void calcDmg();
}

Discriminated struct/union

// Approach 1:
enum EntityType {
	etNone=-1,
	etPlayer,
	etGoblin,
	etChest,
};
struct PlayerData {
	int holdingItemId;
	int potionCount;
};
struct GoblinData {
	int damage;
};
struct Entity {
	EntityType type;
	union {
		struct PlayerData;
		struct GoblinData;
	};
};
// Approach 2:
enum DataType {
	dtNone=-1,
	dtPosition,
	dtHealthAndDamage,
};
struct PositionData {
	int x,y;
};
struct HealthAndDamage {
	int hp, dmg;
};
struct Data {
	DataType type;
	union {
		struct PositionData;
		struct HealthAndDamage;
	}
};
struct Entity {
	Data *data; // array
	int size;
};

Naive fat/flat struct (entity types)

struct RenderSprite {
	uint8_t *data;
	int len;
	int w, h;
};
struct ControlledByUser {
	Input in;
};
struct Hostile {
	int damage;
	int aggroRange;
};
struct Player { 
	struct RenderSprite, 
	struct ControlledByUser, 
	struct HasPhysicsShape,
};
struct Tree { 
	struct RenderSprite, 
	struct HasPhysicsShape, 
	struct IsStatic, 
};
struct Jelly { 
	struct RenderSprite, 
	struct ChasePlayer, 
	struct HasPhysicsShape, 
	struct Hostile 
};

Fat/Flat struct (behaviors)

// but instead of Player, Tree, etc.:
enum EntityProperties {
	epRenderSprite = 1<<0,
	epControlledByPlayer = 1<<1,
	epHasPhysicsShape = 1<<2,
	epIsStatic = 1<<3,
	// ...
};
struct Entity {
	uint64_t props; // bitmask for active props
	// Sprite
	struct Texture sprite;
	Vec2 pos;
	// Movement
	Vec2 vel;
	Vec2 acc;
	// Control
	int controllerId;
	int health;
	int damage;
	// etc.
};
// Helper functions for entity "types":
Entity *MakePlayer() {
	Entity *entity = AllocateEntity();
	SetEntProp(entity, epRenderSprite);
	entity->sprite = ...;
	SetEntProp(entity, epControlledByPlayer);
	entity->controllerId = ...;
	//...
	return entity;
}
// Property: epTrigger
enum TriggerKind { ... };
struct Trigger
{
    TriggerKind kind;
    Input input;
    Shape shape;
    EntityHandle source_entity;
    // for type tkChest:
    ItemKind collect_item_kind;
    Item *collect_item;
    // for type tkHazard:
    f32 seconds_to_allow_source_entity;
    AttackFlags attack_flags;
    // can pack the variables associated with
    // types into an union: more compression, but
    // can only access data after type check
};
struct Entity
{
  Entity *first_child;
  Entity *last_child;
  Entity *next;
  Entity *prev;
  Entity *parent;
  // other data ...
};