Skip to content

Commit

Permalink
dci for godot
Browse files Browse the repository at this point in the history
  • Loading branch information
baiXfeng committed Jul 19, 2024
1 parent bc3ba54 commit dc0f7a7
Show file tree
Hide file tree
Showing 15 changed files with 758 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.godot
74 changes: 74 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# godot-dci
Lightweight dci framework wirtten with gdscript.

![](dci.png)

# Features

- Lightweight and non-intrusive.
- Easy to use.

# How To Use

- Copy the 'dci' directory to any location within your Godot project.
- Begin your DCI coding journey with the following code:

```
# define data
class Person extends dci_data:
var name: String = "Player"
```

```
# define role (Interactions)
class swimmer extends dci_behavior:
func swim() -> dci_behavior:
print("%s Swim." % data().name)
return self
class superman extends dci_behavior:
func fly() -> dci_behavior:
print("%s Fly." % data().name)
return self
```

```
# define context
class FlyContext extends dci_context:
# override
func _on_execute(data):
assert(sender() is superman, "type not match!")
sender().fly()
```

```
# create dci environment
var dci = dci_env.new()
# register role
dci.add_behavior("swimmer", swimmer)
dci.add_behavior("superman", superman)
# register context
dci.add_context("FlyContext", FlyContext.new())
dci.add_callable("FlyLambda", func(ctx: dci_context, data):
assert(ctx.sender() is superman, "type not match!")
ctx.sender().fly()
)
# data usage
var man: Person = Person.new().with(dci)
# cast role
man.cast("swimmer").swim() \
.cast("superman").fly()
# cast and context execute
man.cast("superman").execute("FlyContext")
man.cast("superman").execute("FlyLambda")
```
Binary file added dci.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
100 changes: 100 additions & 0 deletions project/dci-scene/scene/main.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
extends Control

var dci: dci_env = dci_env.new("my-dci")

class swimmer extends dci_behavior:
func swim() -> dci_behavior:
print("%s Swim." % data().name)
return self

class superman extends dci_behavior:
func fly() -> dci_behavior:
print("%s Fly." % data().name)
return self

class tankman extends dci_behavior:
func fire() -> dci_behavior:
print("%s Fire!" % data().name)
return self
func attack() -> dci_behavior:
print("%s Attack enemies!" % data().name)
return self

class shopper extends dci_behavior:
func buy(name: String) -> dci_behavior:
print("shopper <%s> age <%d> have gold <%d> to buy <%s>." % [data().name, data().age, get_component("gold"), name])
return self

class Person extends dci_data:
var age: int = 13
var name: String = "Player"

class FlyContext extends dci_context:
# override
func _on_execute(data):
assert(sender() is superman, "type not match!")
sender().fly()

class BuySomeContext extends dci_context:
# override
func _on_execute(data):
assert(sender() is shopper, "type not match!")
sender().buy(data)

func _ready():
#dci.debug_print = true

# register role
dci.add_behavior("swimmer", swimmer)
dci.add_behavior("superman", superman)
dci.add_behavior("tank", tankman)
dci.add_behavior("shopper", shopper)

# register context
dci.add_context("BuySomeContext", BuySomeContext.new())
dci.add_callable("attack_enemies", func(ctx: dci_context, data):
ctx.sender().cast("tank").attack()
printt("enemies:", data)
)
dci.add_callable("FlyLambda", func(ctx: dci_context, data):
assert(ctx.sender() is superman, "type not match!")
print("Here is lambda function.")
ctx.sender().fly()
)

# data group
var man: Person = Person.new().with(dci)
man.add_to_group("player")

for i in 5:
var enemy: Person = Person.new().with(dci)
enemy.add_to_group("enemy")

# cast role
man.cast("swimmer").swim() \
.cast("superman").fly() \
.cast("tank").fire()

# cast to entity
man.cast("entity") \
.add_component("hello", 100) \
.add_component("world", 200) \
.add_component("gold", 9999)

# cast to shopper
var shopper = man.cast("shopper") \
.execute("BuySomeContext", "drinks")

man.cast("superman").execute("FlyLambda")

# fetch enemies and execute context/callable
var enemies = dci.group("enemy")
man.cast("tank").execute("attack_enemies", enemies)

# destory
man.destroy()
for d in dci.group("enemy"):
d.destroy()

printt("data list:", dci.data_keys())

12 changes: 12 additions & 0 deletions project/dci-scene/scene/main.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[gd_scene load_steps=2 format=3 uid="uid://bftqdiki5eyv4"]

[ext_resource type="Script" path="res://dci-scene/scene/main.gd" id="1_uua2x"]

[node name="Main" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_uua2x")
56 changes: 56 additions & 0 deletions project/dci/dci_behavior.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
extends RefCounted
class_name dci_behavior

var _data

func _init(d):
_data = d
_data._set_behavior(self)

func data():
return _data

func cast(role: String) -> dci_behavior:
return _data.cast(role)

func execute(name: String, data = null) -> dci_behavior:
_data.env().execute(name, self, data)
return self

func add_component(name: String, component) -> dci_behavior:
_data.env().add_component(_data, name, component)
return self

func remove_component(name: String) -> dci_behavior:
_data.env().remove_component(_data, name)
return self

func remove_all_components():
_data.env().remove_all_components(_data)
return self

func has_component(name: String) -> bool:
return _data.env().has_component(_data, name)

func get_component(name: String):
return _data.env().get_component(_data, name)

func get_components() -> Array:
return _data.env().get_components(_data)

func on_enter(data):
_on_enter(data)

func on_exit(data):
_on_exit(data)

# ==============================================================================

# override
func _on_enter(data):
pass

# override
func _on_exit(data):
pass

41 changes: 41 additions & 0 deletions project/dci/dci_context.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
extends Node
class_name dci_context

func env() -> dci_env:
return _env.get_ref()

func sender():
if _sender == null:
return null
return _sender.get_ref()

func execute(data = null):
_on_execute(data)

func group(name: String) -> Array:
return env().group(name)

func get_data(id: int) -> dci_data:
return env().get_data(id)

# ==============================================================================
# override
func _on_execute(data):
pass

# ==============================================================================
# private
var _env: WeakRef
var _sender: WeakRef

func _set_env(f: dci_env):
_env = weakref(f)

func _set_sender(sender):
_sender = weakref(sender)

func _init(parent: Node = null):
# add scene tree for rpc
if parent:
parent.add_child(self)

60 changes: 60 additions & 0 deletions project/dci/dci_data.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
extends RefCounted
class_name dci_data

signal on_init(sender)
signal on_destroy(sender)

signal on_component_added(sender, component)
signal on_component_removed(sender, component)

func id() -> int:
return _id

func with(env) -> dci_data:
env.add_data(self)
return self

func destroy():
env().remove_data(self)

func env() -> dci_env:
return _env.get_ref()

func behavior():
return _behavior

func cast(role: String) -> dci_behavior:
return env().cast_behavior(self, role)

func execute(name: String, data = null) -> dci_data:
env().execute(name, self, data)
return self

func add_to_group(group_name: String) -> bool:
return env().add_data_to_group(self, group_name)

func remove_from_group(group_name: String) -> bool:
return env().remove_data_from_group(self, group_name)

func get_groups() -> Array:
return env().get_data_groups(self)

# ==============================================================================
# private
var _id: int # 唯一实例id
var _env: WeakRef
var _behavior

func _set_id(id: int):
_id = id

func _set_env(f: dci_env):
_env = weakref(f)

func _set_behavior(behavior):
if _behavior:
_behavior.on_exit(self)
_behavior = behavior
if _behavior:
_behavior.on_enter(self)

Loading

0 comments on commit dc0f7a7

Please sign in to comment.