draco 0.2.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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