kiwi-ecs 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +145 -0
- data/lib/arch_store.rb +7 -0
- data/lib/archetype.rb +82 -78
- data/lib/bitmap.rb +6 -0
- data/lib/entity.rb +6 -0
- data/lib/query.rb +4 -0
- data/lib/world.rb +50 -48
- metadata +12 -145
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3c28c74f941f35abc3fe5ae1d565f8942f7676b01341e4c3efeef7969dcb1512
|
4
|
+
data.tar.gz: 6ac603e29b87be9c27da787dc330291f8c6bc8ce28e8807e4b8d36294609638e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db0833836918141ec6f231b943c9c2fb6455e232c6d451cb46b80ff949eb245a164d037dfd984054af69019bf717ebe380734c6a74a1493db429623ed22825d1
|
7
|
+
data.tar.gz: a1d9e6d195d65505ea25f21f5cc98224c4bf4729adaca7fb4a0fa9725a74e3748956853d7fda3e22d6108eab4d98651a612a10b847784f8bee3c99f47f3bd7b3
|
data/README.md
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
# Kiwi
|
2
|
+
|
3
|
+
Kiwi is a versatile entity component system focussing on fast iteration and a nice api.
|
4
|
+
|
5
|
+
To get started, read the [usage guide](#usage) below.
|
6
|
+
|
7
|
+
[](https://github.com/Jomy10/kiwi-ecs-ruby/actions/workflows/tests.yml)
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
The library is available from [ruby gems](https://rubygems.org/gems/kiwi-ecs):
|
12
|
+
|
13
|
+
```sh
|
14
|
+
gem install kiwi-ecs
|
15
|
+
```
|
16
|
+
|
17
|
+
To use it in your ruby source files:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
require 'kiwi-ecs'
|
21
|
+
```
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
### The world
|
26
|
+
|
27
|
+
The world is the main object that controls the ecs.
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
world = Kiwi::World.new
|
31
|
+
```
|
32
|
+
|
33
|
+
### Components
|
34
|
+
|
35
|
+
Creating a component is as simple as declaring a struct:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
Position = Struct.new :x, :y
|
39
|
+
```
|
40
|
+
|
41
|
+
Classes can also be used instead of structs
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
class Velocity
|
45
|
+
attr_accessor :x
|
46
|
+
attr_accessor :y
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
### Entities
|
51
|
+
|
52
|
+
An entity is spawned with a set of components:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
entityId = world.spawn(Position.new(10, 10))
|
56
|
+
|
57
|
+
world.spawn(Position.new(3, 5), Velocity.new(1.5, 0.0))
|
58
|
+
```
|
59
|
+
|
60
|
+
The `world.spawn(*components)` function will return the id of the spawned entity.
|
61
|
+
|
62
|
+
Killing an entity can be done using `world.kill(entityId)`:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
world.kill(entityId)
|
66
|
+
```
|
67
|
+
|
68
|
+
### Systems
|
69
|
+
|
70
|
+
#### Queries
|
71
|
+
|
72
|
+
Queries can be constructed as follows:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
# Query all position componentss
|
76
|
+
world.query(Position) do |pos|
|
77
|
+
puts pos
|
78
|
+
end
|
79
|
+
|
80
|
+
# Query all entities having a position and a velocity component, and their entity ids
|
81
|
+
world.query_with_ids(Position, Velocity) do |id, pos, vel|
|
82
|
+
# ...
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
86
|
+
### Flags
|
87
|
+
|
88
|
+
Entities can be tagged using flags
|
89
|
+
|
90
|
+
#### Defining flags
|
91
|
+
|
92
|
+
A flag is an integer
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
module Flags
|
96
|
+
Player = 0
|
97
|
+
Enemy = 1
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
#### Setting flags
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
id = world.spawn
|
105
|
+
|
106
|
+
world.set_flag(id, Flags::Player)
|
107
|
+
```
|
108
|
+
|
109
|
+
#### Removing a flag
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
world.remove_flag(id, Flags::Player)
|
113
|
+
```
|
114
|
+
|
115
|
+
#### Checking wether an entity has a flag
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
world.has_flag(id, Flags::Player)
|
119
|
+
```
|
120
|
+
|
121
|
+
#### Filtering queries with flags
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
world.query_with_ids(Pos)
|
125
|
+
.filter do |id, pos|
|
126
|
+
world.has_flag(id, Flags::Player)
|
127
|
+
end
|
128
|
+
.each do |id, pos|
|
129
|
+
# Do something with the filtered query
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
The `hasFlags` function is also available for when you want to check multiple flags.
|
134
|
+
|
135
|
+
## Road map
|
136
|
+
|
137
|
+
- [ ] System groups
|
138
|
+
|
139
|
+
## Contributing
|
140
|
+
|
141
|
+
Contributors are welcome to open an issue requesting new features or fixes or opening a pull request for them.
|
142
|
+
|
143
|
+
## License
|
144
|
+
|
145
|
+
The library is licensed under LGPLv3.
|
data/lib/arch_store.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
require_relative 'archetype'
|
2
2
|
|
3
|
+
module Kiwi
|
4
|
+
# Internal methods not meant for outside use
|
5
|
+
module Internal
|
6
|
+
|
3
7
|
class ArchStore
|
4
8
|
# [[Integer]:Integer]
|
5
9
|
attr_accessor :compMap
|
@@ -28,3 +32,6 @@ class ArchStore
|
|
28
32
|
end
|
29
33
|
end
|
30
34
|
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
data/lib/archetype.rb
CHANGED
@@ -1,93 +1,97 @@
|
|
1
1
|
ComponentColumn = Struct.new(:components)
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
def new_arch_row_id(entityId)
|
16
|
-
id = @availableEntityRows.pop
|
17
|
-
if id != nil
|
18
|
-
@entities[id] = entityId
|
19
|
-
return id
|
20
|
-
else
|
21
|
-
id = @entities.size
|
22
|
-
@entities.push entityId
|
23
|
-
return id
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def set_component(archRowId, component)
|
28
|
-
compCol = @components[component.class.object_id]
|
29
|
-
if compCol.components.size <= archRowId
|
30
|
-
compCol.components.push component
|
31
|
-
else
|
32
|
-
compCol.components[archRowId] = component
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def get_component(archRowId, componentId)
|
37
|
-
return @components[componentId].components[archRowId]
|
38
|
-
end
|
39
|
-
|
40
|
-
def has_component(componentId)
|
41
|
-
return @components[componentId] != nil
|
42
|
-
end
|
3
|
+
module Kiwi
|
4
|
+
module Internal
|
5
|
+
class Archetype
|
6
|
+
# @params [Array<Integer>] componentIds
|
7
|
+
def initialize(componentIds)
|
8
|
+
# [Integer(componentId): ComponentColumn]
|
9
|
+
@components = componentIds.map do |compId|
|
10
|
+
[compId, ComponentColumn.new([])]
|
11
|
+
end.to_h
|
12
|
+
@availableEntityRows = []
|
13
|
+
@entities = []
|
14
|
+
end
|
43
15
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
16
|
+
# @return [int]
|
17
|
+
def new_arch_row_id(entityId)
|
18
|
+
id = @availableEntityRows.pop
|
19
|
+
if id != nil
|
20
|
+
@entities[id] = entityId
|
21
|
+
return id
|
22
|
+
else
|
23
|
+
id = @entities.size
|
24
|
+
@entities.push entityId
|
25
|
+
return id
|
26
|
+
end
|
27
|
+
end
|
48
28
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
@components[compId].components
|
29
|
+
def set_component(archRowId, component)
|
30
|
+
compCol = @components[component.class.object_id]
|
31
|
+
if compCol.components.size <= archRowId
|
32
|
+
compCol.components.push component
|
33
|
+
else
|
34
|
+
compCol.components[archRowId] = component
|
35
|
+
end
|
57
36
|
end
|
58
|
-
compCount = componentIds.size
|
59
37
|
|
60
|
-
|
61
|
-
|
62
|
-
.filter do |entId, _|
|
63
|
-
entId != nil
|
38
|
+
def get_component(archRowId, componentId)
|
39
|
+
return @components[componentId].components[archRowId]
|
64
40
|
end
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
end
|
41
|
+
|
42
|
+
def has_component(componentId)
|
43
|
+
return @components[componentId] != nil
|
69
44
|
end
|
70
|
-
end
|
71
45
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
# ...
|
76
|
-
components = componentIds
|
77
|
-
.map do |compId|
|
78
|
-
@components[compId].components
|
46
|
+
def remove_entity(archRowId)
|
47
|
+
@availableEntityRows.push(archRowId)
|
48
|
+
@entities[archRowId] = nil
|
79
49
|
end
|
80
|
-
compCount = componentIds.size
|
81
50
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
51
|
+
# @return [[Component, EntityId]]
|
52
|
+
def active_components(componentIds)
|
53
|
+
# [comp1a, comp1b]
|
54
|
+
# [comp2a, comp2b]
|
55
|
+
# ...
|
56
|
+
components = componentIds
|
57
|
+
.map do |compId|
|
58
|
+
@components[compId].components
|
59
|
+
end
|
60
|
+
compCount = componentIds.size
|
61
|
+
|
62
|
+
@entities
|
63
|
+
.each_with_index
|
64
|
+
.filter do |entId, _|
|
65
|
+
entId != nil
|
66
|
+
end
|
67
|
+
.map do |_, rowIdx|
|
68
|
+
(0...compCount).map do |compRowId|
|
69
|
+
components[compRowId][rowIdx]
|
70
|
+
end
|
71
|
+
end
|
86
72
|
end
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
73
|
+
|
74
|
+
def components_and_ids(componentIds)
|
75
|
+
# [comp1a, comp1b]
|
76
|
+
# [comp2a, comp2b]
|
77
|
+
# ...
|
78
|
+
components = componentIds
|
79
|
+
.map do |compId|
|
80
|
+
@components[compId].components
|
81
|
+
end
|
82
|
+
compCount = componentIds.size
|
83
|
+
|
84
|
+
@entities
|
85
|
+
.each_with_index
|
86
|
+
.filter do |entId, _|
|
87
|
+
entId != nil
|
88
|
+
end
|
89
|
+
.map do |entId, rowIdx|
|
90
|
+
[entId, *(0...compCount).map do |compRowId|
|
91
|
+
components[compRowId][rowIdx]
|
92
|
+
end]
|
93
|
+
end
|
91
94
|
end
|
95
|
+
end
|
92
96
|
end
|
93
97
|
end
|
data/lib/bitmap.rb
CHANGED
data/lib/entity.rb
CHANGED
data/lib/query.rb
CHANGED
data/lib/world.rb
CHANGED
@@ -2,63 +2,65 @@ require_relative 'entity'
|
|
2
2
|
require_relative 'arch_store'
|
3
3
|
require_relative 'query'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
module Kiwi
|
6
|
+
class World
|
7
|
+
def initialize
|
8
|
+
@entityStore = Kiwi::Internal::EntityStore.new
|
9
|
+
@archStore = Kiwi::Internal::ArchStore.new
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
12
|
+
def spawn(*components)
|
13
|
+
compIds = components.map { |c| c.class.object_id }.sort
|
14
|
+
archId = @archStore.get_archetype_id(compIds)
|
15
|
+
entId = @entityStore.new_id
|
16
|
+
archetype = @archStore.get(archId)
|
17
|
+
archRowId = archetype.new_arch_row_id(entId)
|
18
|
+
@entityStore.spawn(entId, archId, archRowId)
|
19
|
+
for component in components
|
20
|
+
archetype.set_component(archRowId, component)
|
21
|
+
end
|
22
|
+
return entId
|
20
23
|
end
|
21
|
-
return entId
|
22
|
-
end
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
def get_component(entityId, componentType)
|
26
|
+
entity = @entityStore.get(entityId)
|
27
|
+
archetype = @archStore.get(entity.archId)
|
28
|
+
archetype.get_component(entity.archRow, componentType.object_id)
|
29
|
+
end
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
def kill(entityId)
|
32
|
+
entity = @entityStore.get entityId
|
33
|
+
@entityStore.kill(entityId)
|
34
|
+
archetype = @archStore.get(entity.archId)
|
35
|
+
archetype.remove_entity(entity.archRow)
|
36
|
+
end
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
def entity_count
|
39
|
+
return @entityStore.entity_count
|
40
|
+
end
|
40
41
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
42
|
+
def has_component(entityId, componentType)
|
43
|
+
entity = @entityStore.get(entityId)
|
44
|
+
archetype = @archStore.get(entity.archId)
|
45
|
+
return archetype.has_component(componentType.object_id)
|
46
|
+
end
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
|
48
|
+
def has_flag(entityId, flagId)
|
49
|
+
return @entityStore.has_flag entityId, flagId
|
50
|
+
end
|
50
51
|
|
51
|
-
|
52
|
-
|
53
|
-
|
52
|
+
def has_flags(entityId, *flagIds)
|
53
|
+
return flagIds.filter { |flagId| !@entityStore.has_flag entityId, flagId }.count == 0
|
54
|
+
end
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
56
|
+
def set_flag(entityId, flagId)
|
57
|
+
@entityStore.set_flag(entityId, flagId)
|
58
|
+
end
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
|
60
|
+
def remove_flag(entityId, flagId)
|
61
|
+
@entityStore.remove_flag(entityId, flagId)
|
62
|
+
end
|
62
63
|
|
63
|
-
|
64
|
+
include Kiwi::Internal::Query
|
65
|
+
end
|
64
66
|
end
|
metadata
CHANGED
@@ -1,158 +1,25 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kiwi-ecs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonas Everaert
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
date: 2023-08-11 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description:
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
To get started, read the [usage guide](#usage) below.
|
19
|
-
|
20
|
-
[](https://github.com/Jomy10/kiwi-ecs-ruby/actions/workflows/tests.yml)
|
21
|
-
|
22
|
-
## Installation
|
23
|
-
|
24
|
-
This library is currently not on ruby gems, but will arrive shortly.
|
25
|
-
|
26
|
-
To use it now, simple `git clone https://github.com/jomy10/kiwi-ecs-ruby`.
|
27
|
-
|
28
|
-
Then incude the world.rb file in your ruby files like so:
|
29
|
-
|
30
|
-
```ruby
|
31
|
-
require_relative 'kiwi-ecs-ruby/src/world.rb'
|
32
|
-
```
|
33
|
-
|
34
|
-
## Usage
|
35
|
-
|
36
|
-
### The world
|
37
|
-
|
38
|
-
The world is the main object that controls the ecs.
|
39
|
-
|
40
|
-
```ruby
|
41
|
-
world = World.new
|
42
|
-
```
|
43
|
-
|
44
|
-
### Components
|
45
|
-
|
46
|
-
Creating a component is as simple as declaring a struct:
|
47
|
-
|
48
|
-
```ruby
|
49
|
-
Position = Struct.new :x, :y
|
50
|
-
```
|
51
|
-
|
52
|
-
Classes can also be used instead of structs
|
53
|
-
|
54
|
-
```ruby
|
55
|
-
class Velocity
|
56
|
-
attr_accessor :x
|
57
|
-
attr_accessor :y
|
58
|
-
end
|
59
|
-
```
|
60
|
-
|
61
|
-
### Entities
|
62
|
-
|
63
|
-
An entity is spawned with a set of components:
|
64
|
-
|
65
|
-
```ruby
|
66
|
-
entityId = world.spawn(Position.new(10, 10))
|
67
|
-
|
68
|
-
world.spawn(Position.new(3, 5), Velocity.new(1.5, 0.0))
|
69
|
-
```
|
70
|
-
|
71
|
-
The `world.spawn(*components)` function will return the id of the spawned entity.
|
72
|
-
|
73
|
-
Killing an entity can be done using `world.kill(entityId)`:
|
74
|
-
|
75
|
-
```ruby
|
76
|
-
world.kill(entityId)
|
77
|
-
```
|
78
|
-
|
79
|
-
### Systems
|
80
|
-
|
81
|
-
#### Queries
|
82
|
-
|
83
|
-
Queries can be constructed as follows:
|
84
|
-
|
85
|
-
```ruby
|
86
|
-
# Query all position componentss
|
87
|
-
world.query(Position) do |pos|
|
88
|
-
puts pos
|
89
|
-
end
|
90
|
-
|
91
|
-
# Query all entities having a position and a velocity component, and their entity ids
|
92
|
-
world.query_with_ids(Position, Velocity) do |id, pos, vel|
|
93
|
-
# ...
|
94
|
-
end
|
95
|
-
```
|
96
|
-
|
97
|
-
### Flags
|
98
|
-
|
99
|
-
Entities can be tagged using flags
|
100
|
-
|
101
|
-
#### Defining flags
|
102
|
-
|
103
|
-
A flag is an integer
|
104
|
-
|
105
|
-
```ruby
|
106
|
-
module Flags
|
107
|
-
Player = 0
|
108
|
-
Enemy = 1
|
109
|
-
end
|
110
|
-
```
|
111
|
-
|
112
|
-
#### Setting flags
|
113
|
-
|
114
|
-
```ruby
|
115
|
-
id = world.spawn
|
116
|
-
|
117
|
-
world.set_flag(id, Flags::Player)
|
118
|
-
```
|
119
|
-
|
120
|
-
#### Removing a flag
|
121
|
-
|
122
|
-
```ruby
|
123
|
-
world.remove_flag(id, Flags::Player)
|
124
|
-
```
|
125
|
-
|
126
|
-
#### Checking wether an entity has a flag
|
127
|
-
|
128
|
-
```ruby
|
129
|
-
world.has_flag(id, Flags::Player)
|
130
|
-
```
|
131
|
-
|
132
|
-
#### Filtering queries with flags
|
133
|
-
|
134
|
-
```ruby
|
135
|
-
# TODO
|
136
|
-
```
|
137
|
-
|
138
|
-
The `hasFlags` function is also available for when you want to check multiple flags.
|
139
|
-
|
140
|
-
## Road map
|
141
|
-
|
142
|
-
- [ ] System groups
|
143
|
-
|
144
|
-
## Contributing
|
145
|
-
|
146
|
-
Contributors are welcome to open an issue requesting new features or fixes or opening a pull request for them.
|
147
|
-
|
148
|
-
## License
|
149
|
-
|
150
|
-
The library is licensed under LGPLv3.
|
151
|
-
email:
|
13
|
+
description: |-
|
14
|
+
== Description
|
15
|
+
Kiwi is a versatile entity component system that is focussed on fast iteration and a nice api
|
16
|
+
email:
|
152
17
|
executables: []
|
153
18
|
extensions: []
|
154
|
-
extra_rdoc_files:
|
19
|
+
extra_rdoc_files:
|
20
|
+
- README.md
|
155
21
|
files:
|
22
|
+
- README.md
|
156
23
|
- lib/arch_store.rb
|
157
24
|
- lib/archetype.rb
|
158
25
|
- lib/bitmap.rb
|
@@ -164,7 +31,7 @@ homepage: https://github.com/jomy10/kiwi-ecs-ruby
|
|
164
31
|
licenses:
|
165
32
|
- LGPL-3.0-or-later
|
166
33
|
metadata: {}
|
167
|
-
post_install_message:
|
34
|
+
post_install_message:
|
168
35
|
rdoc_options: []
|
169
36
|
require_paths:
|
170
37
|
- lib
|
@@ -179,8 +46,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
179
46
|
- !ruby/object:Gem::Version
|
180
47
|
version: '0'
|
181
48
|
requirements: []
|
182
|
-
rubygems_version: 3.3.
|
183
|
-
signing_key:
|
49
|
+
rubygems_version: 3.3.26
|
50
|
+
signing_key:
|
184
51
|
specification_version: 4
|
185
52
|
summary: An entity component system with a nice api, fit for a variety of use cases
|
186
53
|
test_files: []
|