draco 0.2.0 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fabf6d184411f5d8dc5782499dfd2e7d07174333f6927566fb3fe51d59ae0df3
4
- data.tar.gz: 5a647308729162793c8d4841b86f954298640d319dbfa836f8cb072f491c04cb
3
+ metadata.gz: 363ccf9c3649a752219bca392bdc13b88defbd864a63afb5615969986e59c070
4
+ data.tar.gz: 81dcbc8225646cec6efc357ecaacfd89334e9a183caad035cf4bd2ece9a01470
5
5
  SHA512:
6
- metadata.gz: 6e754598051acec8e35c62d40d52c255bf2187fa68b6e2e18112de90661af7faa53b9bb7fc402b8425df99bac3fb6d9d6a3cf15dabb0f956004dd43a63d04ae7
7
- data.tar.gz: a558139ee8f227e9307c406453b18a5280ceac36ee0386fbd3998eebfe975cb0fc6e21effff52292aab4b594fbef56498a69623aa75586d6229a0b34740613a2
6
+ metadata.gz: aa089a7a6abe9b615d07e8def023fad13fedcbca605e78bc1acf181431b1659961811074a9238f79702b7e1e9ddd76782a173fd079ed5f7ae04a2ee83905c660
7
+ data.tar.gz: e6a70346719b8cf5e5269924158bcbad7ed4ea66440686c57931b7dc6232d467adf6abeac5740970e6d16a573d61a66af27c5626599ed115a7a87624d9ac9bdf
@@ -22,8 +22,7 @@ jobs:
22
22
  - name: Set up Ruby
23
23
  # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
24
24
  # change this to (see https://github.com/ruby/setup-ruby#versioning):
25
- # uses: ruby/setup-ruby@v1
26
- uses: ruby/setup-ruby@ec106b438a1ff6ff109590de34ddc62c540232e0
25
+ uses: ruby/setup-ruby@v1
27
26
  with:
28
27
  ruby-version: 2.6
29
28
  - name: Install dependencies
@@ -7,6 +7,7 @@ AllCops:
7
7
  NewCops: enable
8
8
  Exclude:
9
9
  - "benchmark.rb"
10
+ - "samples/**/*.rb"
10
11
 
11
12
  Style/StringLiterals:
12
13
  EnforcedStyle: double_quotes
@@ -0,0 +1,5 @@
1
+ ## 0.5.0 (December 31, 2020)
2
+
3
+ Features:
4
+
5
+ - Add `Draco::Tag` to easily define tag components [#6](https://github.com/guitsaru/draco/pull/6)
data/README.md CHANGED
@@ -6,12 +6,36 @@ An Entity Component System is an architectural framework that decouples game obj
6
6
  build game objects through composition. This allows you to easily share small logic components between different game
7
7
  objects.
8
8
 
9
+ ## Sample Application
10
+
11
+ This repository includes a sample application in `samples/teeny-tiny`.
12
+
13
+ ### Running the sample application
14
+
15
+ 1. Download the [latest release](https://github.com/guitsaru/draco/archive/main.zip) of this repository.
16
+ 2. Create a copy of DragonRuby GTK in a new folder.
17
+ 3. Copy the teeny-tiny directory from draco into your new DragonRuby GTK folder. `cp -r draco/samples/teeny-tiny dragonruby/teeny-tiny`.
18
+ 4. Run it using `./dragonruby teeny-tiny`.
19
+
9
20
  ## Installation
10
21
 
11
22
  1. Create a `lib` directory inside your game's `app` directory.
12
23
  2. Copy `lib/draco.rb` into your new `lib` directory.
13
24
  3. In your `main.rb` file, require `app/lib/draco.rb`.
14
25
 
26
+ ## Support
27
+
28
+ - Find a bug? [Open an issue](https://github.com/guitsaru/draco/issues).
29
+ - Need help? [Start a Discussion](https://github.com/guitsaru/draco/discussions) or [Join us in Discord: Channel #oss-draco](https://discord.gg/vPUNtwfm).
30
+
31
+ ## Versioning
32
+
33
+ Draco uses [https://semver.org/].
34
+
35
+ * Major (X.y.z) - Incremented for any backwards incompatible public API changes.
36
+ * Minor (x.Y.z) - Incremented for new, backwards compatible, public API enhancements/fixes.
37
+ * Patch (x.y.Z) - Incremented for small, backwards compatible, bug fixes.
38
+
15
39
  ## Usage
16
40
 
17
41
  ### Components
@@ -23,7 +47,7 @@ These can be shared across many different types of game objects.
23
47
  class Visible < Draco::Component; end
24
48
  ```
25
49
 
26
- `Visible` is an example of a label component. An entity either has it, or it doesn't. We can also associate data with our
50
+ `Visible` is an example of a tag component. An entity either has it, or it doesn't. We can also associate data with our
27
51
  components.
28
52
 
29
53
  ```ruby
@@ -44,6 +68,16 @@ component.x
44
68
  # => 110
45
69
  ```
46
70
 
71
+ #### Tag Components
72
+
73
+ The `Visible` class above is an example of a tag component. These are common enough that we don't necessarily want to
74
+ define a bunch of empty component classes. Draco provides a way to generate these classes at runtime.
75
+
76
+ ```ruby
77
+ Draco::Tag(:visible)
78
+ # => Visible
79
+ ```
80
+
47
81
  ### Entities
48
82
 
49
83
  Entities are independant game objects. They consist of a unique id and a list of components.
@@ -129,6 +163,40 @@ world.systems << RenderSpriteSystem
129
163
  world.tick(args)
130
164
  ```
131
165
 
166
+ Just like with entities, we can define a subclassed template for our world.
167
+
168
+ ```ruby
169
+ class Overworld < Draco::World
170
+ entity Goblin
171
+ entity Player, position: { x: 50, y: 50 }, as: :player
172
+ systems RenderSpriteSystem, InputSystem
173
+ end
174
+
175
+ world = Overworld.new
176
+ ```
177
+
178
+ ### Named Entities
179
+
180
+ If there are entities that are frequently accessed in our systems, we can give these a name. In the above example, our
181
+ player entity has been given the name `player`. We can now access this directly from our world:
182
+
183
+ ```ruby
184
+ world.player
185
+ ```
186
+
187
+ ### Fetching entities by id
188
+
189
+ In some cases you'll want to keep track of entities by their id, such as when you want to keep track of another entity in a component.
190
+
191
+ ```ruby
192
+ entity = Player.new
193
+ entity.id
194
+ # => 12
195
+
196
+ world.entities[12] == entity
197
+ # => true
198
+ ```
199
+
132
200
  ## Learn More
133
201
 
134
202
  Here are some good resources to learn about Entity Component Systems
@@ -140,6 +208,8 @@ Here are some good resources to learn about Entity Component Systems
140
208
 
141
209
  Draco is licensed under AGPL. You can purchase the right to use Draco under the [commercial license](https://github.com/guitsaru/draco/blob/master/COMM-LICENSE).
142
210
 
211
+ Each purchase comes with free upgrades for the current major version of Draco.
212
+
143
213
  <a class="gumroad-button" href="https://guitsaru.itch.io/draco" target="_blank">Purchase Commercial License</a>
144
214
 
145
215
  ## Development
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  # Specify which files should be added to the gem when it is released.
20
20
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
21
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|samples)/}) }
23
23
  end
24
24
  spec.bindir = "exe"
25
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
@@ -5,7 +5,7 @@
5
5
  # An Entity Component System is an architectural pattern used in game development to decouple behavior from objects.
6
6
  module Draco
7
7
  # Public: The version of the library. Draco uses semver to version releases.
8
- VERSION = "0.2.0"
8
+ VERSION = "0.5.0"
9
9
 
10
10
  # Public: A general purpose game object that consists of a unique id and a collection of Components.
11
11
  class Entity
@@ -39,8 +39,8 @@ module Draco
39
39
  @default_components[component] = defaults
40
40
  end
41
41
 
42
- # Internal: Returns the default components for the class.
43
42
  class << self
43
+ # Internal: Returns the default components for the class.
44
44
  attr_reader :default_components
45
45
  end
46
46
 
@@ -300,6 +300,23 @@ module Draco
300
300
  end
301
301
  end
302
302
 
303
+ # Internal: Empty module to enable Tag() method.
304
+ module Tag; end
305
+
306
+ # Public: Creates a new empty component at runtime. If the given Class already exists, it reuses the existing Class.
307
+ #
308
+ # name - The symbol or string name of the component. It can be either camelcase or underscored.
309
+ #
310
+ # Returns a Class with superclass of Draco::Component.
311
+ def self.Tag(name) # rubocop:disable Naming/MethodName
312
+ klass_name = camelize(name)
313
+
314
+ return Object.const_get(klass_name) if Object.const_defined?(klass_name)
315
+
316
+ klass = Class.new(Component)
317
+ Object.const_set(klass_name, klass)
318
+ end
319
+
303
320
  # Public: Systems contain the logic of the game.
304
321
  # The System runs on each tick and manipulates the Entities in the World.
305
322
  class System
@@ -373,6 +390,62 @@ module Draco
373
390
 
374
391
  # Public: The container for current Entities and Systems.
375
392
  class World
393
+ @default_entities = []
394
+ @default_systems = []
395
+
396
+ # Internal: Resets the default components for each class that inherites Entity.
397
+ #
398
+ # sub - The class that is inheriting Entity.
399
+ #
400
+ # Returns nothing.
401
+ def self.inherited(sub)
402
+ super
403
+ sub.instance_variable_set(:@default_entities, [])
404
+ sub.instance_variable_set(:@default_systems, [])
405
+ end
406
+
407
+ # Public: Adds a default Entity to the World.
408
+ #
409
+ # entity - The class of the Entity to add by default.
410
+ # defaults - The Hash of default values for the Entity. (default: {})
411
+ #
412
+ # Examples
413
+ #
414
+ # entity(Player)
415
+ #
416
+ # entity(Player, position: { x: 0, y: 0 })
417
+ #
418
+ # Returns nothing.
419
+ def self.entity(entity, defaults = {})
420
+ name = defaults[:as]
421
+ @default_entities.push([entity, defaults])
422
+
423
+ attr_reader(name.to_sym) if name
424
+ end
425
+
426
+ # Public: Adds default Systems to the World.
427
+ #
428
+ # systems - The System or Array list of System classes to add to the World.
429
+ #
430
+ # Examples
431
+ #
432
+ # systems(RenderSprites)
433
+ #
434
+ # systems(RenderSprites, RenderLabels)
435
+ #
436
+ # Returns nothing.
437
+ def self.systems(*systems)
438
+ @default_systems += Array(systems).flatten
439
+ end
440
+
441
+ class << self
442
+ # Internal: Returns the default Entities for the class.
443
+ attr_reader :default_entities
444
+
445
+ # Internal: Returns the default Systems for the class.
446
+ attr_reader :default_systems
447
+ end
448
+
376
449
  # Public: Returns the Array of Systems.
377
450
  attr_reader :systems
378
451
 
@@ -384,8 +457,17 @@ module Draco
384
457
  # entities - The Array of Entities for the World (default: []).
385
458
  # systems - The Array of System Classes for the World (default: []).
386
459
  def initialize(entities: [], systems: [])
387
- @entities = EntityStore.new(entities)
388
- @systems = systems
460
+ default_entities = self.class.default_entities.map do |default|
461
+ klass, attributes = default
462
+ name = attributes[:as]
463
+ entity = klass.new(attributes)
464
+ instance_variable_set("@#{name}", entity) if name
465
+
466
+ entity
467
+ end
468
+
469
+ @entities = EntityStore.new(default_entities + entities)
470
+ @systems = self.class.default_systems + systems
389
471
  end
390
472
 
391
473
  # Public: Runs all of the Systems every tick.
@@ -440,22 +522,36 @@ module Draco
440
522
  def initialize(*entities)
441
523
  @entity_to_components = Hash.new { |hash, key| hash[key] = Set.new }
442
524
  @component_to_entities = Hash.new { |hash, key| hash[key] = Set.new }
525
+ @entity_ids = {}
443
526
 
444
527
  self << entities
445
528
  end
446
529
 
447
- # Internal: Gets all Entities that implement all of the given Components
530
+ # Internal: Gets all Entities that implement all of the given Components or that match the given entity ids.
448
531
  #
449
- # components - The Component Classes to filter by
532
+ # components_or_ids - The Component Classes to filter by
450
533
  #
451
534
  # Returns a Set list of Entities
452
- def [](*components)
453
- components
535
+ def [](*components_or_ids)
536
+ components_or_ids
454
537
  .flatten
455
- .map { |component| @component_to_entities[component] }
538
+ .map { |component_or_id| select_entities(component_or_id) }
456
539
  .reduce { |acc, i| i & acc }
457
540
  end
458
541
 
542
+ # Internal: Gets entities by component or id.
543
+ #
544
+ # component_or_id - The Component Class or entity id to select.
545
+ #
546
+ # Returns an Array of Entities.
547
+ def select_entities(component_or_id)
548
+ if component_or_id.is_a?(Numeric)
549
+ Array(@entity_ids[component_or_id])
550
+ else
551
+ @component_to_entities[component_or_id]
552
+ end
553
+ end
554
+
459
555
  # Internal: Adds Entities to the EntityStore
460
556
  #
461
557
  # entities - The Entity or Array list of Entities to add to the EntityStore.
@@ -474,6 +570,8 @@ module Draco
474
570
  def add(entity)
475
571
  entity.subscribe(self)
476
572
 
573
+ @entity_ids[entity.id] = entity
574
+
477
575
  components = entity.components.map(&:class)
478
576
  @entity_to_components[entity].merge(components)
479
577
 
@@ -490,9 +588,10 @@ module Draco
490
588
  #
491
589
  # Returns the EntityStore
492
590
  def delete(entity)
493
- components = @entity_to_components.delete(entity)
591
+ @entity_ids.delete(entity.id)
592
+ components = Array(@entity_to_components.delete(entity))
494
593
 
495
- components.map(&:class).each do |component|
594
+ components.each do |component|
496
595
  @component_to_entities[component].delete(entity)
497
596
  end
498
597
  end
@@ -642,13 +741,36 @@ module Draco
642
741
  #
643
742
  # Returns a String.
644
743
  def self.underscore(string)
645
- string.split("::").last.bytes.map.with_index do |byte, i|
744
+ string.to_s.split("::").last.bytes.map.with_index do |byte, i|
646
745
  if byte > 64 && byte < 97
647
- downcased = byte + 32 # gemspec
746
+ downcased = byte + 32
648
747
  i.zero? ? downcased.chr : "_#{downcased.chr}"
649
748
  else
650
749
  byte.chr
651
750
  end
652
751
  end.join
653
752
  end
753
+
754
+ # Internal: Converts an underscored string into a camel case string.
755
+ #
756
+ # Examples
757
+ #
758
+ # camlize("camel_case")
759
+ # # => "CamelCase"
760
+ #
761
+ # Returns a string.
762
+ def self.camelize(string) # rubocop:disable Metrics/MethodLength
763
+ modifier = -32
764
+
765
+ string.to_s.bytes.map do |byte|
766
+ if byte == 95
767
+ modifier = -32
768
+ nil
769
+ else
770
+ char = (byte + modifier).chr
771
+ modifier = 0
772
+ char
773
+ end
774
+ end.compact.join
775
+ end
654
776
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: draco
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Pruitt
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-08 00:00:00.000000000 Z
11
+ date: 2020-12-31 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A library for Entities, Components, and Systems in games.
14
14
  email:
@@ -22,6 +22,7 @@ files:
22
22
  - ".gitignore"
23
23
  - ".rspec"
24
24
  - ".rubocop.yml"
25
+ - CHANGELOG.md
25
26
  - CODE_OF_CONDUCT.md
26
27
  - COMM-LICENSE
27
28
  - Gemfile