Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Behavior Trees #242

Open
zackthehuman opened this issue Jul 17, 2016 · 0 comments
Open

Behavior Trees #242

zackthehuman opened this issue Jul 17, 2016 · 0 comments

Comments

@zackthehuman
Copy link
Member

zackthehuman commented Jul 17, 2016

Most of the scripting in this game, and indeed in the original games, is very basic. Most items simply have no logic except for the action they perform when picked up. Most enemies, except for bosses, do things like the following:

  • Walk forward until a wall or obstacle is hit, then turn around (Peterchy [kind of])
(REPEAT
  (SEQUENCE
    (IF-COLLIDED-HORIZONTAL WALL|OBSTACLE
      (FLIP-DIRECTION HORIZONTAL)
    )
    (MOVE FORWARD)
  )
)

Or more simply:

(REPEAT
  (SEQUENCE
    (FLIP-DIRECTION-IF-COLLIDED WALL|OBSTACLE HORIZONTAL)
    (MOVE FORWARD)
  )
)
  • Move toward the player in a zig-zag (Telly)
(REPEAT
  (SEQUENCE
    (MOVE-ON-GREATER-AXIS-TO-PLAYER)
    (WAIT 1.0)
  )
)

Where MOVE-ON-GREATER-AXIS-TO-PLAYER was implemented in Squirrel like:

local targetX = getHeroX();
local targetY = getHeroY();
local distanceX = targetX - hostX;
local distanceY = targetY - hostY;

if(abs(distanceX) > abs(distanceY)) {
  if(distanceX > 0.0) {
    host.direction = Directions.Right;
    host.velocityX = 0.2;
  } else {
    host.direction = Directions.Left;
    host.velocityX = -0.2;
  }
  host.velocityY = 0.0;
} else {
  if(distanceY > 0.0) {
    host.velocityY = 0.2;
  } else {
    host.velocityY = -0.2;
  }
  host.velocityX = 0.0;
}

I think this gives a nice balance to the "scripting", where reusable parts can be turned in to nodes on the behavior tree, and shared across all object behaviors.

The components that truly do unique things are few, and are used often by several objects, especially enemies. For example, facing the player is a common thing that several enemies do, but at different times in their update logic. By creating a FACE-PLAYER node, it can be inserted into the behavior tree of an enemy at any point.

Here are some more examples:

  • Wait, put down shield, and then jump toward player (Bikky)
(REPEAT
  (SEQUENCE
    (CHANGE-ANIMATION "idle")
    (SET-VELOCITY X 0)
    (ENABLE-SHIELD)
    (PLAY-SAMPLE "Bikky (Landing)")
    (WAIT 1.0)
    (DISABLE-SHIELD)
    (WAIT 0.1333)
    (FACE-PLAYER)
    (CHANGE-ANIMATION "jumping")
    (SET-VELOCITY Y -5.0)
    (IF-FACING-DIRECTION "left"
      (SET-VELOCITY X -1.3)
      (SET-VELOCITY X 1.3)
    )
    (PARALLEL
      (REPEAT
        (IF-FACING-DIRECTION "left"
          (SET-VELOCITY X -1.3)
          (SET-VELOCITY X 1.3)
        )
      )
      (UNTIL
        (IF-COLLIDED-VERTICAL WALL BOTTOM)
      )
    )
  )
)

It may be possible to represent the behavior tree in JSON by using arrays instead of a Lisp-like pseudocode:

["REPEAT"
  ["SEQUENCE", 
    [
      ["IF-COLLIDED-HORIZONTAL", "WALL|OBSTACLE",
        ["FLIP-DIRECTION", "HORIZONTAL"]
      ],
      ["MOVE", "FORWARD"]
    ]
  ]
]

In the above example, each node is represented by an array. The first element of the array is the type of node.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant