Skip to content
Open
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
49 changes: 49 additions & 0 deletions guides/scaffolds_guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Scaffold Guides

Some component and system pairs are often developed together.

A good example of these pairs are position components and a movement system. In a 2D space, you will have an `XPosition`, `YPosition`,`Xvelocity` and `YVelocity` component and a movement system that updates these components. Other examples would be health/damage systems, where your entity has health/damage point components and a health system updates their values.

By using a scaffold, you will now be able to generate these component system pairs. For instance;

```
mix ecsx.gen.scaffold movement2d
```

would generate these components and systems for movement on a 2d plane.


## Existing scaffolds
### Movement2d
```
mix ecsx.gen.scaffold movement2d
```

- This creates 4 components, all with the `float` data type
```
- x_position
- y_position
- x_velocity
- y_velocity
```

and a single system, `movement2d`

The movement system will come with 2 private functions, `move_x` and `move_y` that can be used in the run function to update the entitiy's position.

### Health
```
mix ecsx.gen.scaffold health
```

This creates a `health_points` component, with an `integer` data type and a single system, `health`

The health system has 2 private functions, `take_damage/2` and `add_health/2` which can be used to reduce health and increase health of an entity.

## Scaffold Systems in Progress
### Collision
```
mix ecsx.gen.scaffold collision
```

These allow collisions to be added to entities. It also implements the collision force calculations for you.
79 changes: 79 additions & 0 deletions lib/mix/tasks/ecsx.gen.scaffold.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
defmodule Mix.Tasks.Ecsx.Gen.Scaffold do
@shortdoc "Generates a scaffold for common system and component combinations like movement systems"

@moduledoc """
Scaffolds a system and component pair for an ECSx appliaction

$ mix ecxs.gen.scaffold movement2d

The argument is the type of scaffold to be generated

Valid scaffolds are:
* movement2d
* health
* collision
"""

use Mix.Task

alias Mix.Tasks.Ecsx.Gen.System
alias Mix.Tasks.Ecsx.Gen.Component

@valid_scaffold_types ~w(movement2d health collision)

@doc false
def run([]) do
"Invalid arguments - must provide a valid scaffold type"
|> message_with_help()
|> Mix.raise()
end

def run([scaffold_type | _opts] = _args) do
scaffold_type = validate(scaffold_type)
# {opts, _, _} = OptionParser.parse(opts, strict: [namespace: :string])
create_scaffold(scaffold_type)
end

defp message_with_help(message) do
"""
#{message}

mix ecsx.gen.sacffold expects a scaffold type.

For example:

mix ecsx.gen.scaffold movement2d

"""
end

defp validate(scaffold_type) when scaffold_type in @valid_scaffold_types,
do: String.to_atom(scaffold_type)

defp validate(_),
do:
Mix.raise(
"Invalid scaffold type. Possible scaffold types are: #{inspect(@valid_scaffold_types)}}"
)

defp create_scaffold(scaffold_type) do
create_associated_scaffold_system(scaffold_type)
create_associated_scaffold_components(scaffold_type)
end

# create systems
defp create_associated_scaffold_system(:movement2d), do: System.run(["Movement2d"])

defp create_associated_scaffold_system(:health), do: System.run(["Health"])

# create components
defp create_associated_scaffold_components(:movement2d) do
["XPosition", "YPosition", "XVelocity", "YVelocity"]
|> Enum.each(fn component -> Component.run([component, "float"]) end)
end

defp create_associated_scaffold_components(:health) do
["HealthPoints", "DamagePoints"]
|> Enum.each(fn component -> Component.run([component, "integer"]) end)
end
end
14 changes: 13 additions & 1 deletion lib/mix/tasks/ecsx.gen.system.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,19 @@ defmodule Mix.Tasks.Ecsx.Gen.System do
defp create_system_file(system_name) do
filename = Macro.underscore(system_name)
target = "lib/#{Helpers.otp_app()}/systems/#{filename}.ex"
source = Application.app_dir(:ecsx, "/priv/templates/system.ex")

source =
case system_name do
"Movement2d" ->
Application.app_dir(:ecsx, "/priv/templates/scaffold_templates/movement2d.ex")

"Health" ->
Application.app_dir(:ecsx, "/priv/templates/scaffold_templates/health.ex")

_ ->
Application.app_dir(:ecsx, "/priv/templates/system.ex")
end

binding = [app_name: Helpers.root_module(), system_name: system_name]

Mix.Generator.create_file(target, EEx.eval_file(source, binding))
Expand Down
2 changes: 1 addition & 1 deletion mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"mix_test_watch": {:hex, :mix_test_watch, "1.1.0", "330bb91c8ed271fe408c42d07e0773340a7938d8a0d281d57a14243eae9dc8c3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "52b6b1c476cbb70fd899ca5394506482f12e5f6b0d6acff9df95c7f1e0812ec3"},
"nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
Expand Down
24 changes: 24 additions & 0 deletions priv/templates/scaffold_templates/health.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
defmodule <%= app_name %>.Systems.Health do
@moduledoc """
Documentation for <%= system_name %> system.
"""
@behaviour ECSx.System

@impl ECSx.System
def run do
# System logic
:ok
end

# These functions can be used to update the health of entities.

defp take_damage(entity, damage_points) do
health = <%= app_name %>.Components.HealthPoints.get(entity)
<%= app_name %>.Components.HealthPoints.update(health - damage_points)
end

defp add_health(entity, extra_health_points) do
health = <%= app_name %>.Components.HealthPoints.get(entity)
<%= app_name %>.Components.HealthPoints.update(health + extra_health_points)
end
end
26 changes: 26 additions & 0 deletions priv/templates/scaffold_templates/movement2d.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
defmodule <%= app_name %>.Systems.Movement2d do
@moduledoc """
Documentation for <%= system_name %> system.
"""
@behaviour ECSx.System

@impl ECSx.System
def run do
# System logic
:ok
end

# These functions can be used to update the position of entities.

defp move_x(entity) do
x_pos = <%= app_name %>.Components.XPosition.get(entity)
x_velocity = <%= app_name %>.Components.XVelocity.get(entity)
<%= app_name %>.Components.XPosition.update(x_pos + x_velocity)
end

defp move_x(entity) do
y_pos = <%= app_name %>.Components.YPosition.get(entity)
y_velocity = <%= app_name %>.Components.YVelocity.get(entity)
<%= app_name %>.Components.YPosition.update(x_pos + x_velocity)
end
end
Loading