Odin does not have OOP paradigms. It’s all structs and funct… procedures. You could somewhat reconstruct a method as a pointer to a function:
MyVector :: struct {
x, y: f32
add: proc(this: ^MyVector, other: MyVector
}
myvector_add_1 :: proc(this: ^MyVector, other: MyVector) {
this.x += other.x
this.y += other.y
}
myvector_add_2 :: proc(this: ^MyVector, other: MyVector) {
this.x += other.y
this.y += other.x
}
and then when initializing your struct, you can choose any funct… procedure you like:
mv1 := MyVector{13, 37, myvector_add_1}
mv2 := MyVector{42, 69, myvecotr_add_2}
mv1->add(mv2) // calls myvector_add_1
mv2->add(mv1) // calls myvector_add_2
So in some sense, you define an “interface” for every “method” separately, which in my experience often is the thing you’d actually want in OOP, but OOP doesn’t give you.
However, this couples your data with behaviour, which leads to the OOP thinking of individual instances of your data. The Data-Oriented way of doing this to always think of arrays of data. E.g. in a game you usually don’t just have a single Entity, but hundreds, maybe even thousands of them. So instead of call a function on every Entity individually (where each method call has a cost), you just have a funct… procedure that iterates over all the Entity and applies the behaviour on it.
You could then apply different behaviour by just having entities in different lists, maybe a roaming: []Entity for NPCs walking around in the world, maybe a fighting: []Entity for all NPCs and Monsters attacking someone, maybe even a dead: []Entity for all Monsters waiting to respawn.
One option that decoupling your data from your behaviour gives you is to split your Entity data in different Components, e.g.:
Transform :: struct {
position: Vector2,
scale: Vector2,
rotation: Vector2
}
Movement :: struct {
velocity: Vector2,
acceleration: Vector2
}
Stats :: struct {
max_health: f32,
health: f32,
max_mana: f32,
mana: f32,
strength: f32,
dexterity: f32,
intelligence: f32,
}
You can then have a []Transform, a []Movement, and a []Stats, create an EntityID :: distinct int for each Entity. Some Entities may only have a Transform Component, other Entites (like the Player) may have all 3. By mixing the components you create Archetypes, where each can be collected in a simple archetype: []EntityID, so you can apply behaviour to your Entites based on their Archetype.
Using this approach, you will end up with a bunch of encapsulated Systems that all work on a subset of the same set of Components, and each Component is associated with an Entity.
For a deeper understanding you can read about the “Entity Component System” (ECS) Architecture. There are even a couple ECS implementations for Odin on github.