Skip to content

Commit

Permalink
Port C++ relationship examples
Browse files Browse the repository at this point in the history
  • Loading branch information
BeanCheeseBurrito committed Sep 7, 2023
1 parent 5c04718 commit 4658328
Show file tree
Hide file tree
Showing 6 changed files with 418 additions and 0 deletions.
57 changes: 57 additions & 0 deletions src/Flecs.NET.Examples/Cpp/Relationships/Basics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#if Cpp_Relationships_Basics

using Flecs.NET.Core;
using static Flecs.NET.Bindings.Native;

using World world = World.Create(args);

// Entity used for Grows relationship
Entity grows = world.Entity("Grows");

// Relationship objects
Entity apples = world.Entity("Apples");
Entity pears = world.Entity("Pears");

// Create an entity with 3 relationships. Relationships are like regular components,
// but combine two types/identifiers into an (relationship, object) pair.
Entity bob = world.Entity("Bob")
// Pairs can be constructed from a type and entity
.Add<Eats>(apples)
.Add<Eats>(pears)
// Pairs can also be constructed from two entity ids
.Add(grows, pears);

// Has can be used with relationships as well
Console.WriteLine($"Bob eats apples? {bob.Has<Eats>(apples)}");

// Wildcards can be used to match relationships
Console.WriteLine($"Bob grows food? {bob.Has(grows, EcsWildcard)}");

// Print the type of the entity. Should output:
// (Identifier,Name),(Eats,Apples),(Eats,Pears),(Grows,Pears)
Console.WriteLine($"Bob's type: [{bob.Type().Str()}]");

// Relationships can be iterated for an entity. This iterates (Eats, *):
bob.Each<Eats>((Entity second) =>
{
Console.WriteLine($"Bob eats {second.Name()}");
});

// Iterate by explicitly providing the pair. This iterates (*, Pears):
bob.Each(EcsWildcard, pears, (Id id) =>
{
Console.WriteLine($"Bob {id.First().Name()} pears");
});

public struct Eats { }

#endif

// Output:
// Bob eats apples? True
// Bob grows food? True
// Bob's type: [(Identifier,Name), (Eats,Apples), (Eats,Pears), (Grows,Pears)]
// Bob eats Apples
// Bob eats Pears
// Bob Eats pears
// Bob Grows pears
109 changes: 109 additions & 0 deletions src/Flecs.NET.Examples/Cpp/Relationships/EnumRelations.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// When an enumeration constant is added to an entity, it is added as a relationship
// pair where the relationship is the enum type, and the target is the constant. For
// example, this statement:
// e.Add(Color.Red)
//
// adds this relationship:
// (Color, Color.Red)
//
// Enums are registered as exclusive relationships, which means that adding an
// enum constant will replace the previous constant for that enumeration:
// e.Add(Color.Green)
//
// will replace Color.Red with Color.Green

#if Cpp_Relationships_EnumRelations

using Flecs.NET.Core;
using static Flecs.NET.Bindings.Native;

using World world = World.Create(args);

// Create an entity with (Tile, Stone) and (TileStatus, Free) relationships
Entity tile = world.Entity()
.Add(Tile.Stone)
.Add(TileStatus.Free);

// (Tile, Tile.Stone), (TileStatus, TileStatus.Free)
Console.WriteLine(tile.Type().Str());

// Replace (TileStatus, Free) with (TileStatus, Occupied)
tile.Add(TileStatus.Occupied);

// (Tile, Tile.Stone), (TileStatus, TileStatus.Occupied)
Console.WriteLine(tile.Type().Str());

// Check if the entity has the Tile relationship and the Tile::Stone pair
Console.WriteLine(tile.Has<Tile>()); // True
Console.WriteLine(tile.Has(Tile.Stone)); // True

// Get the current value of the enum
ref readonly Tile v = ref tile.GetEnum<Tile>();
Console.WriteLine(v == Tile.Stone); // True

// Create a few more entities that we can query
world.Entity().Add(Tile.Grass).Add(TileStatus.Free);
world.Entity().Add(Tile.Sand).Add(TileStatus.Occupied);

// Iterate all entities with a Tile relationship
using Filter filter1 = world.Filter(
filter: world.FilterBuilder().With<Tile>(EcsWildcard)
);

filter1.Each((Iter it, int i) =>
{
Entity tileConstant = it.Pair(1).Second();
Console.WriteLine(tileConstant.Path());
});

// Outputs:
// ::Tile::Stone
// ::Tile::Grass
// ::Tile::Sand

// Iterate only occupied tiles
using Filter filter2 = world.Filter(
filter: world.FilterBuilder()
.With<Tile>(EcsWildcard)
.With(TileStatus.Occupied)
);

filter2.Each((Iter it, int i) =>
{
Entity tileConstant = it.Pair(1).Second();
Console.WriteLine(tileConstant.Path());
});

// Outputs:
// ::Tile::Stone
// ::Tile::Sand

// Remove any instance of the TileStatus relationship
tile.Remove<TileStatus>();

public enum Tile
{
Grass,
Sand,
Stone
}

public enum TileStatus
{
Free,
Occupied,
}

#endif

// Output:
// (Tile,Tile.Stone), (TileStatus,TileStatus.Free)
// (Tile,Tile.Stone), (TileStatus,TileStatus.Occupied)
// True
// True
// True
// ::Tile::Stone
// ::Tile::Grass
// ::Tile::Sand
// ::Tile::Stone
// ::Tile::Sand
45 changes: 45 additions & 0 deletions src/Flecs.NET.Examples/Cpp/Relationships/ExclusiveRelations.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#if Cpp_Relationships_ExclusiveRelations

using Flecs.NET.Core;
using static Flecs.NET.Bindings.Native;

using World world = World.Create(args);

// Register Platoon as exclusive relationship. This ensures that an entity
// can only belong to a single Platoon.
world.Component<Platoon>().Entity.Add(EcsExclusive);

// Create two platoons
Entity platoon1 = world.Entity();
Entity platoon2 = world.Entity();

// Create a unit
Entity unit = world.Entity();

// Add unit to platoon 1
unit.Add<Platoon>(platoon1);

// Log platoon of unit
Console.WriteLine($"Unit in platoon 1: {unit.Has<Platoon>(platoon1)}");
Console.WriteLine($"Unit in platoon 2: {unit.Has<Platoon>(platoon2)}");
Console.WriteLine();

// Add unit to platoon 2. Because Platoon is an exclusive relationship, this
// both removes (Platoon, platoon_1) and adds (Platoon, platoon_2) in a
// single operation.
unit.Add<Platoon>(platoon2);

Console.WriteLine($"Unit in platoon 1: {unit.Has<Platoon>(platoon1)}");
Console.WriteLine($"Unit in platoon 2: {unit.Has<Platoon>(platoon2)}");

// Type for Platoon relationship
public struct Platoon { }

#endif

// Output:
// Unit in platoon 1: True
// Unit in platoon 2: False
//
// Unit in platoon 1: False
// Unit in platoon 2: True
88 changes: 88 additions & 0 deletions src/Flecs.NET.Examples/Cpp/Relationships/RelationComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// This example shows how relationships can be combined with components to attach
// data to a relationship.

#if Cpp_Relationships_RelationComponent

using Flecs.NET.Core;
using static Flecs.NET.Bindings.Native;

using World world = World.Create(args);

// When one element of a pair is a component and the other element is a tag,
// the pair assumes the type of the component.
Entity e1 = world.Entity().SetFirst<Requires, Gigawatts>(new Requires { Amount = 1.21 });
ref readonly Requires r = ref e1.GetFirst<Requires, Gigawatts>();
Console.WriteLine($"requires: {r.Amount}");

// The component can be either the first or second part of a pair:
Entity e2 = world.Entity().SetSecond<Gigawatts, Requires>(new Requires { Amount = 1.21 });
r = ref e2.GetSecond<Gigawatts, Requires>();
Console.WriteLine($"requires: {r.Amount}");

// Note that <Requires, Gigawatts> and <Gigawatts, Requires> are two
// different pairs, and can be added to an entity at the same time.

// If both parts of a pair are components, the pair assumes the type of
// the first element:
Entity e3 = world.Entity().SetFirst<Expires, Position>(new Expires { Timeout = 0.5 });
ref readonly Expires e = ref e3.GetFirst<Expires, Position>();
Console.WriteLine($"expires: {e.Timeout}");

// You can prevent a pair from assuming the type of a component by adding
// the Tag property to a relationship:
world.Component<MustHave>().Entity.Add(EcsTag);

// Even though Position is a component, <MustHave, Position> contains no
// data because MustHave has the Tag property.
world.Entity().Add<MustHave, Position>();

// The id::type_id method can be used to find the component type for a pair:
Console.WriteLine(world.Pair<Requires, Gigawatts>().TypeId().Path());
Console.WriteLine(world.Pair<Gigawatts, Requires>().TypeId().Path());
Console.WriteLine(world.Pair<Expires, Position>().TypeId().Path());
Console.WriteLine(world.Pair<MustHave, Position>().TypeId().Path());

// When querying for a relationship component, add the pair type as template
// argument to the builder:
Query q = world.Query(
filter: world.FilterBuilder()
.Term<Requires>().Second<Gigawatts>() // set second part of pair for first term
);

// When iterating, always use the pair type:
q.Each((Iter it, int i) =>
{
Column<Requires> rq = it.Field<Requires>(1);
Console.WriteLine($"requires {rq[i].Amount} gigawatts");
});

public struct Requires
{
public double Amount { get; set; }
}

public struct Expires
{
public double Timeout { get; set; }
}

public struct Position
{
public double X { get; set; }
public double Y { get; set; }
}

public struct Gigawatts { }
public struct MustHave { }

#endif

// Output:
// requires: 1.21
// requires: 1.21
// expires: 0.5
// ::Requires
// ::Requires
// ::Expires
// 0
// requires 1.21 gigawatts
31 changes: 31 additions & 0 deletions src/Flecs.NET.Examples/Cpp/Relationships/SymmetricRelations.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#if Cpp_Relationships_SymmetricRelations

using Flecs.NET.Core;
using static Flecs.NET.Bindings.Native;

using World world = World.Create(args);

// Register TradesWith as symmetric relationship. Symmetric relationships
// go both ways, adding (R, B) to A will also add (R, A) to B.
world.Component<TradesWith>().Entity.Add(EcsSymmetric);

// Create two players
Entity player1 = world.Entity();
Entity player2 = world.Entity();

// Add (TradesWith, player_2) to player_1. This also adds
// (TradesWith, player_1) to player_2.
player1.Add<TradesWith>(player2);

// Log platoon of unit
Console.WriteLine($"Player 1 trades with Player 2: {player1.Has<TradesWith>(player2)}");
Console.WriteLine($"Player 2 trades with Player 1: {player2.Has<TradesWith>(player1)}");

// Type for TradesWith relationship
public struct TradesWith { }

#endif

// Output:
// Player 1 trades with Player 2: True
// Player 2 trades with Player 1: True
Loading

0 comments on commit 4658328

Please sign in to comment.