Excluding a struct field from being using'd

I recently ran into a collision between using’d struct fields. There doesn’t really seem to be any mechanism to resolve that, besides renaming the fields or using #subtype instead of using.

I think it would be useful to be able to exclude specific fields on a struct from getting pulled in when the struct gets using’d, as these conflicts often happen with fields that I don’t even really want to pull in anyway. The two scenarios where I can see this being most useful are:

  1. Having a struct that’s using multiple component structs, that hold using pointers back to some base struct.
  2. Having a struct that’s using multiple structs with vtable fields (which preferably would just be named ‘vtable’)

Syntax wise, I could imagine it being something like a #hidden directive before a a struct field.
Example: (scenario 1)

Player:: struct {
   using entity: Base_Entity,
   using component_a: Component_A,
   using component_b: Component_B, 
   some_entity_field: u8,
   // No collision between component_a.entity and component_b.entity
   // Those fields must be accessed explicitly
}

Component_A :: struct {
   #hidden using entity: ^Base_Entity,
   some_component_field: u8,
}

Component_B :: struct {
   #hidden using entity: ^Base_Entity,
   some_other_component_field: u8,
}

This seems like something that might’ve already been considered, though I couldn’t find any earlier discussion about it. Curious what people think.

(I had also made a Github Discussion about this topic here.)

Not sure what your Base_Entity looks like, but you can do the following to ignore the name of a subset entity and still bring it in to the top level context. Note the underscore used to “ignore”.

Player :: struct {
   using entity: Base_Entity, //this is the only "entity" name at top level
   using component_a: Component_A,
   using component_b: Component_B, 
   some_entity_field: u8,
}

Component_A :: struct {
   using _: ^Base_Entity,
   some_component_field: u8,
}

Component_B :: struct {
   using _: ^Base_Entity,
   some_other_component_field: u8,
}

…then I would imagine it used like:

player: Player
player.entity.some_base_entity_field = value
player.component_a.some_base_entity_field = value

I might even consider not using the “entity” field name at all so there is symmetry with the references, i.e.

Player :: struct {
  using _: Base_Entity,
  using component_a: Component_A,
  using component_b: Component_B, 
  some_entity_field: u8,
}

Component_A :: struct {
  using _: ^Base_Entity,
  some_component_field: u8,
}

Component_B :: struct {
  using _: ^Base_Entity,
  some_other_component_field: u8,
}

main :: proc() {
  player: Player
  player.some_base_entity_field = value
  player.component_a.some_base_entity_field = value
}

Oh wow I didn’t know about using the underscore for field names, that’s very useful! How exactly would you set the value of those fields though? I figure only with an ordered struct literal or reflection?

I feel like this would work in most situations you’d want #hidden though I still think a directive like that could be more elegant.

I suppose it depends on what your trying to do. Seems this would be more complex a structure than you might want, but you could do the following. Component_A and Component_B can start off with the same initialized values, but their pointers are different once assigned to Player. Then the values are changed independently after that. Also changed the below example from the above example to avoid name collision on Base_Entity.

If you’re trying to keep all values parallel, then I’m a little confused with the point since you’d end up with a circular reference inside a reference, which would be redundant, so I’m guessing that is not the goal.

Base_Entity :: struct {
  field1: u8,
  field2: u8,
}

Player :: struct {
  using _: Base_Entity,
  component_a: Component_A,
  component_b: Component_B,
  some_entity_field: u8,
}

Component_A :: struct {
  using _: ^Base_Entity,
  some_component_field: u8,
}

Component_B :: struct {
  using _: ^Base_Entity,
  some_other_component_field: u8,
}

main :: proc() {

  base_entity := Base_Entity {
    field1 = 1,
    field2 = 2,
  }
  
  //cannot use combination of field names and no field names in literal, so do it like this
  comp_a := Component_A {
    &base_entity,
    3,
  }

  //cannot use combination of field names and no field names in literal, so do it like this
  comp_b := Component_B {
    &base_entity,
    4,
  }

  player := Player {
    field1 = 1,
    field2 = 2,
    component_a = comp_a,
    component_b = comp_b,
    some_entity_field = 3,
  }

  player.field1 = 1
  player.component_a.field1 = 5

  fmt.println(player.field1)
  fmt.println(player.component_a.field1)
}

After noodling around with this, I personally would lean towards making the Base_Entity the top level instead, with Player and components as the subsets who contain their own unique copy of a Component type. Might be less complex, and more extensible, but I’d have to play with it some more and also have a better idea of the end-game. It’s harder to imagine the desired path when it’s not your own project.

I found this could be simplified and made easier to reference in more traditional ways by removing the use of using. I saw your comment “Those fields must be accessed explicitly”, so really removing the using statements forces explicit access. So I came up with this, highlighting two ways to use the structures, player1 and player2. Note that the Base_Entity pointer must be initialized to something and not allowed to be zero, or there is a segmentation fault. Otherwise, you can have Componant_A or B come from anywhere, and be applied to Player.

Base_Entity :: struct {
  field1: u8,
  field2: u8,
}

Player :: struct {
  entity: Base_Entity,
  component_a: Component_A,
  component_b: Component_B,
  some_entity_field: u8,
}

Component_A :: struct {
  entity: ^Base_Entity,
  some_component_field: u8,
}

Component_B :: struct {
  entity: ^Base_Entity,
  some_other_component_field: u8,
}

main :: proc() {

  base_entity := Base_Entity {
    field1 = 1,
    field2 = 2,
  }

  player1 := Player {
    entity = base_entity,
    component_a = {
      entity = &base_entity,
      some_component_field = 3,
    },
    component_b = {
      entity = &base_entity,
      some_other_component_field = 4,
    },
    some_entity_field = 5,
  }

  player2 := Player {
    entity = base_entity,
    component_a = {
      entity = &{
        field1 = 6,
        field2 = 7,
      },
      some_component_field = 8,
    },
    component_b = {
      entity = &{
        field1 = 9,
        field2 = 10,
      },
      some_other_component_field = 11,
    },
    some_entity_field = 12,
  }
  
  player1.entity.field1 = 100
  player1.component_a.entity.field1 = 200
  player2.entity.field1 = 110
  player2.component_a.entity.field1 = 210
  fmt.println(player1.entity.field1)
  fmt.println(player1.component_a.entity.field1)
  fmt.println(player2.entity.field1)
  fmt.println(player2.component_a.entity.field1)
}

Or made even simpler, and I’d say more “elegant” too, by removing the Base_Entity pointers, which is ambiguous to the compiler when “using” is used given that structs are implicit reference-able pointers in themselves.

Base_Entity :: struct {
  field1: u8,
  field2: u8,
}

Player :: struct {
  using _: Base_Entity,
  component_a: Component_A,
  component_b: Component_B,
  some_entity_field: u8,
}

Component_A :: struct {
  using _: Base_Entity,
  some_component_field: u8,
}

Component_B :: struct {
  using _: Base_Entity,
  some_other_component_field: u8,
}

main :: proc() {

  player := Player {
    field1 = 1,
    field2 = 2,
    component_a = {
      field1 = 6,
      field2 = 7,
      some_component_field = 8,
    },
    component_b = {
      field1 = 9,
      field2 = 10,
      some_other_component_field = 11,
    },
    some_entity_field = 12,
  }

  player.field1 = 50
  player.component_a.field1 = 60
  player.component_b.field1 = 70
  fmt.printfln("%v %v %v", player.field1, player.component_a.field1, player.component_b.field1)
}

I’m liking this last one over the other possibilities myself :smiley:

Thanks for the thorough examples, I agree that this is definitely more elegant. The idea with the components all holding pointers back to the base entity is so a Player could be ‘up-cast’ to some component, let’s say Controllable, and then procs could take pointers to a Controllable as a param and treat it like an extension of Base_Entity, no matter what entity type it belongs to. While at the same time Player can also seamlessly be treated like a Base_Entity and as any of its components. A bit like interfaces in OOP, though traits are probably a closer comparison, or just ‘flat multiple inheritance’. Doing this with a bit of extra explicit field accessing is definitely not the end of the world.

My ultimate goal is pretty convoluted and probably not super relevant, but in case you’re interested:

[Expand]

The idea was that these component traits would all also provide their own virtual functions to the implementing entity type. Components could require other components, and modify the entity type’s vtable to do things like replace or extend virtual functions from other components. It’s convoluted, but it could lead to very reusable gameplay code where components can reason about and extend each other without having to manage loads of gameplay state.
My favourite example is player movement, where you might have ‘player movement’ as a component separate from the ‘sprinting’ component. Instead of having something like a movement speed field, you would have a get_movement_speed() virtual function on the movement components. The sprinting component would replace that with its own version, which takes the result of the original function and multiplies it by 2 if shift is held, or something like that. You could then tack on more like a crouching component or a broken leg component which do their own extra modifications to the movement speed function. It’s all very unnecessary if you’re making one game, but if you were to make an engine with this system it could be used to offer lots of reusable gameplay logic and systems out of the box. Maybe the engine has a really good default ‘player movement’ component, and you just add your own extra things on top of that, or pick other components from the engine to use with it.

I’ll probably end up doing my own language for this to really see if the idea has merit, but I was curious to what extend this could be done in Odin.

In the end it’s probably not something I should want to be doing in Odin, but I do still wonder if there could be space for a #hidden-esque feature in Odin. I don’t think it really conflicts with anything else and it can be used to limit the craziness of using in structs.

Ok, sounds pretty cool. I understand your goal, I think. Got a few ideas I’ll shoot your way, but don’t have time right now to flesh them out. I’ll try to post later tonight.

So focusing on the player movement thing, I thought of using a proc pointer in the Player struct as the “flag”. This way a move procedure does not need any flag or speed, just execute what ever the pointer is pointing to. This could be extended with several predefined move functions with their own associated Config_Move, or allow a developer to create their own, and assign it dynamically.

I may have included too much detail below, but it’s how I work out the flow and make sure of no errors. Plus it’s nice to copy and paste an idea, and then modify it from there. Also, maybe you already thought of this, but I provide this just in case it helps spark some ideas.

Edit: I’m not happy with this. It’s too far away from your goal. I know there’s something in using the proc pointers. Need to add parametric polymorphism I think, but I could not work it out. Maybe it’ll come to me.

// base entity holding all the configs
Base_Entity :: struct {
  using _: Config_Move,
}

// speed configured either by file or other process
Config_Move :: struct {
  config_walk_speed: u8,
  config_sprint_speed: u8,
  move: Player_Move_Proc_Type,
}

Player :: struct {
  using _: Base_Entity,
}

// set defaults since odin initializes everything to zero.
// move proc pointer should not be zero - default to walk
create_player :: proc(base: Base_Entity) -> Player {
  return Player {
    config_walk_speed = base.config_walk_speed,
    config_sprint_speed = base.config_sprint_speed,
    move = base.move,
  }
}

// move proc type
Player_Move_Proc_Type :: #type proc (u8)

player_walk :Player_Move_Proc_Type: proc(speed: u8) {
  // do some special conditions for walking, then use configured walk speed
  fmt.printfln("Player walking like and Egyptian at %v speed", speed)
}

player_sprint :Player_Move_Proc_Type: proc(speed: u8) {
  // do some special conditions for sprinting, then use configured sprint speed
  fmt.printfln("Player sprinting from a terror dog at %v speed", speed)
}

set_player_move :: proc(player: ^Player, move_proc: Player_Move_Proc_Type) {
  player.move = move_proc
}

move_player :: proc(player: ^Player) {
  switch player.move {
    case player_walk:   player.move(player.config_walk_speed)
    case player_sprint: player.move(player.config_sprint_speed)
  }
}

// engine
main :: proc() {

  //base defaults set by file or some other process
  base_entity1 := Base_Entity {
    config_walk_speed   = 5,
    config_sprint_speed = 11,
    move = player_walk,
  }

  player1 := create_player(base_entity1)

  //some engine move player at default speed when move key pressed
  move_player(&player1)

  //if a different key is pressed, sprint
  set_player_move(&player1, player_sprint)
  move_player(&player1)

  //some other condition, go back to default player_walk
  set_player_move(&player1, player_walk)
  move_player(&player1)
}
1 Like

Doesn’t it kinda sound similar to what you would accomplish with ECS? Here the components (shared state) acts as interfaces and the systems are the effects you want to chain:

Movement :: struct {
	speed: f32
}

Sprint :: struct {
	factor: f32
}

Crouch :: struct {
	factor: f32
}

Entity :: struct {
	movement: ^Movement,
	sprint: ^Sprint,
	crouch: ^Crouch,
}

has :: proc(ptrs: ..rawptr) -> bool {
	for p in ptrs do if p == nil do return false
	return true
}

without :: proc(ptrs: ..rawptr) -> bool {
	for p in ptrs do if p != nil do return false
	return true
}

// very contrived example but you get the idea
update :: proc(using e: ^Entity) {

	if has(movement, sprint) {
		movement.speed *= sprint.factor
	}

	if has(movement, sprint, crouch) {
		movement.speed /= sprint.factor
		sprint = nil
	}

	if has(movement, crouch) && without(sprint) {
		movement.speed *= crouch.factor
	}

}
1 Like

I somehow thought you were trying to avoid ECS. If you are interested in that, I’ve been really enjoying the usefulness of bit sets combined with enums. Makes for much cleaner flag definitions and also provides context for the ols server/client to give me the correct suggestions as I type, which I find very handy so I don’t have to memorize all the names I chose.

Still focusing on just the move part of the entity, here’s one of my very convoluted examples. I hope these are not too annoying with how I do my examples.

Player :: struct {
  using _: Move,
}

Move :: struct {
  using _: Move_Speed,
  move_flag: bit_set[Move_Flag],
  move_modifier_flag: bit_set[Move_Modifier_Flag],
}

Move_Speed :: struct {
  move_speed: [Move_Flag]u8,
}

Move_Flag :: enum {
  CROUCH,
  WALK,
  SPRINT,
}

Move_Modifier_Flag :: enum u8 {
  NONE = 0,
  CLUBFOOT = 10,
  LEGBROKEN = 20,
  ONELEG = 30,
}

move_player ::proc(p: ^Player) {
  speed: u8
  switch p.move_flag {
    case {.CROUCH}:
      speed = p.move_speed[.CROUCH]
      for mod in p.move_modifier_flag {
        speed -= u8(mod)
      }
      fmt.printfln("player move at %v speed, flag: %v, mods: %v", speed, p.move_flag, p.move_modifier_flag)
    case {.WALK}:
      speed = p.move_speed[.WALK]
      for mod in p.move_modifier_flag {
        speed -= u8(mod)
      }
      fmt.printfln("player move at %v speed, flag: %v, mods: %v", speed, p.move_flag, p.move_modifier_flag)
    case {.SPRINT}:
      speed = p.move_speed[.SPRINT]
      for mod in p.move_modifier_flag {
        speed -= u8(mod)
      }
      fmt.printfln("player move at %v speed, flag: %v, mods: %v", speed, p.move_flag, p.move_modifier_flag)
  }
}

create_player :: proc() -> (player: Player) {
  player = {
    move_speed = {
      .CROUCH = 100,
      .WALK = 150,
      .SPRINT = 200,
    },
    move_flag = {.CROUCH},
    move_modifier_flag = {.NONE}, // default to none
  }
  return player
}

// ensures only one of these flags are set
set_move_flag :: proc(player: ^Player, flag: Move_Flag) {
  player.move_flag = {flag}
}

// can set more than one of these or set to none
set_modifier_flag :: proc(p: ^Player, flags: bit_set[Move_Modifier_Flag]) {
  if .NONE in flags { p.move_modifier_flag = {.NONE} } //removes existing flags and sets only .NONE
  else {
    p.move_modifier_flag -= {.NONE} // remove .NONE if we are setting something real
  }
  for f in flags {
    p.move_modifier_flag += {f}
  }
}

// engine
main :: proc() {
  player1 := create_player()
  move_player(&player1)

  set_modifier_flag(&player1, {.CLUBFOOT, .ONELEG})
  move_player(&player1)

  set_move_flag(&player1, .SPRINT)
  move_player(&player1)

  //reset mods
  set_modifier_flag(&player1, {.NONE})
  move_player(&player1)
}
1 Like

Um, are you perhaps confusing me with the OP? I like ECS but that’s a minority opinion on this forum probably.

Yeah, guess I did. Sorry about that.

I like ECS too. The code seems to just fall into place once the pattern is found.

1 Like