Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add examples to demonstrate the use of non-ECS data structures together with ECS #379

Merged
merged 2 commits into from
Feb 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ jobs:
go run ./_examples/filter
go run ./_examples/generic
go run ./_examples/locked_world
go run ./_examples/no_ecs
go run ./_examples/no_ecs_generic
go run ./_examples/parallel
go run ./_examples/random_access
go run ./_examples/random_sampling
Expand Down Expand Up @@ -186,6 +188,8 @@ jobs:
go run -tags tiny ./_examples/filter
go run -tags tiny ./_examples/generic
go run -tags tiny ./_examples/locked_world
go run -tags tiny ./_examples/no_ecs
go run -tags tiny ./_examples/no_ecs_generic
go run -tags tiny ./_examples/parallel
go run -tags tiny ./_examples/random_access
go run -tags tiny ./_examples/random_sampling
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* Adds [BENCHMARKS.md](https://github.com/mlange-42/arche/blob/main/BENCHMARKS.md) for a tabular overview of the runtime cost of typical *Arche* ECS operations (#367, #372)
* Link benchmarking code in `README.md` and benchmarking tables (#375)
* Documents build tags `tiny` and `debug` in package docs of `ecs` (#377)
* Adds examples to demonstrate the use of non-ECS data structures together with ECS (#379)

### Bugfixes

Expand Down
106 changes: 106 additions & 0 deletions _examples/no_ecs/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Demonstrates that ECS can be mixed with non-ECS data structures, as long as they store entities.
// Uses the ID-based API.
package main

import (
"fmt"
"math/rand"

"github.com/mlange-42/arche/ecs"
)

// CellCoord component.
type CellCoord struct {
X int
Y int
}

// Grid resource / data structure.
type Grid struct {
Data [][]ecs.Entity
Width int
Height int
}

// NewGrid creates a ner Grid of the given size.
func NewGrid(w, h int) Grid {
grid := make([][]ecs.Entity, w)
for i := 0; i < w; i++ {
grid[i] = make([]ecs.Entity, h)
}
return Grid{
Data: grid,
Width: w,
Height: h,
}
}

func main() {
// Create a new World.
world := ecs.NewWorld()

// Create a non-ECS grid data structure,
// and add is as a resource.
grid := NewGrid(30, 20)
ecs.AddResource(&world, &grid)

// Create enities on the grid.
createGridEntities(&world, 250)

// Run a simulation
run(&world)
}

func createGridEntities(world *ecs.World, count int) {
// Get the grid resource.
gridId := ecs.ResourceID[Grid](world)
grid := world.Resources().Get(gridId).(*Grid)

// Get the CellCoord component ID.
coordId := ecs.ComponentID[CellCoord](world)

// Put some entities into the grid.
cnt := 0
for cnt < count {
// Draw random coordinates.
x, y := rand.Intn(grid.Width), rand.Intn(grid.Height)
// Skip if there is already an entity.
if !grid.Data[x][y].IsZero() {
continue
}
// Create an entity.
entity := world.NewEntity(coordId)
// Place the entity in the grid.
grid.Data[x][y] = entity
// Initialize entity components.
coord := (*CellCoord)(world.Get(entity, coordId))
coord.X, coord.Y = x, y

cnt++
}
}

func run(world *ecs.World) {
// Get the grid resource.
gridId := ecs.ResourceID[Grid](world)
grid := world.Resources().Get(gridId).(*Grid)

// Get the CellCoord component ID.
coordId := ecs.ComponentID[CellCoord](world)

// Print random entities from the grid.
for i := 0; i < 25; i++ {
// Draw random coordinates.
x, y := rand.Intn(grid.Width), rand.Intn(grid.Height)
// Get the entity.
entity := grid.Data[x][y]
// Print zero entity
if entity.IsZero() {
fmt.Printf("(%2d,%2d): zero entity\n", x, y)
continue
}
// Print CellCoord component of non-zero entity.
coord := (*CellCoord)(world.Get(entity, coordId))
fmt.Printf("(%2d,%2d): %+v\n", x, y, coord)
}
}
107 changes: 107 additions & 0 deletions _examples/no_ecs_generic/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Demonstrates that ECS can be mixed with non-ECS data structures, as long as they store entities.
// Uses the generic API.
package main

import (
"fmt"
"math/rand"

"github.com/mlange-42/arche/ecs"
"github.com/mlange-42/arche/generic"
)

// CellCoord component.
type CellCoord struct {
X int
Y int
}

// Grid resource / data structure.
type Grid struct {
Data [][]ecs.Entity
Width int
Height int
}

// NewGrid creates a ner Grid of the given size.
func NewGrid(w, h int) Grid {
grid := make([][]ecs.Entity, w)
for i := 0; i < w; i++ {
grid[i] = make([]ecs.Entity, h)
}
return Grid{
Data: grid,
Width: w,
Height: h,
}
}

func main() {
// Create a new World.
world := ecs.NewWorld()

// Create a non-ECS grid data structure,
// and add is as a resource.
grid := NewGrid(30, 20)
ecs.AddResource(&world, &grid)

// Create enities on the grid.
createGridEntities(&world, 250)

// Run a simulation
run(&world)
}

func createGridEntities(world *ecs.World, count int) {
// Get the grid resource.
gridRes := generic.NewResource[Grid](world)
grid := gridRes.Get()

// Create a generic Map as a builder.
builder := generic.NewMap1[CellCoord](world)

// Put some entities into the grid.
cnt := 0
for cnt < count {
// Draw random coordinates.
x, y := rand.Intn(grid.Width), rand.Intn(grid.Height)
// Skip if there is already an entity.
if !grid.Data[x][y].IsZero() {
continue
}
// Create an entity.
entity := builder.New()
// Place the entity in the grid.
grid.Data[x][y] = entity
// Initialize entity components.
coord := builder.Get(entity)
coord.X, coord.Y = x, y

cnt++
}
}

func run(world *ecs.World) {
// Get the grid resource.
gridRes := generic.NewResource[Grid](world)
grid := gridRes.Get()

// Create a generic Map for component access.
mapper := generic.NewMap1[CellCoord](world)

// Print random entities from the grid.
for i := 0; i < 25; i++ {
// Draw random coordinates.
x, y := rand.Intn(grid.Width), rand.Intn(grid.Height)
// Get the entity.
entity := grid.Data[x][y]
// Print zero entity
if entity.IsZero() {
fmt.Printf("(%2d,%2d): zero entity\n", x, y)
continue
}
// Print CellCoord component of non-zero entity.
coord := mapper.Get(entity)
fmt.Printf("(%2d,%2d): %+v\n", x, y, coord)
}
}
Loading