Butterflies are skookum!

(or at least one of them :slight_smile: )

Hi everybody!

For a long time we wanted to show you a side-by-side comparison between Blueprint graphs and SkookumScript, and here it is. We thought the butterflies in the Blueprints` demo project were cute and have converted their behavior to SkookumScript. This example is a faithful replication of the original Blueprint behavior for ease of comparison.

I’d like to illustrate the differences in approach between Blueprint event graphs and SkookumScript coroutines by comparing the flight control loop in both versions of the butterfly.

1. Blueprint Graph for butterfly flight

Here’s the original event graph that comes with the Blueprints demo project provided by Epic. The Blueprint event graph is a network of functional nodes that are connected by links.

This is the portion of the event graph controlling movement, rotation and collision detection:

Here is how the wings are animated:

This portion picks a new random target every few seconds:

2. SkookumScript Coroutine for butterfly flight

In SkookumScript, this same functionality is textually represented via a coroutine plus four helper methods.

While potentially less visually attractive than the event graph, the textual representation can be diffed, merged, easily shared via e-mail/company chat/web, worked on by multiple scripters at once, and provides a different, hierarchical view of the behavior broken down into components. Read more about strengths of text over graphs here: http://skookumscript.com/about/faq/#qst-txt-vs-vis

Also, as a reminder, you can of course affect the live running SkookumScript code from the Skookum IDE console, for example by setting the butterfly’s wing color to yellow in mid-flight:

BP_Butterfly_Sk.instances_first.@wing_material.vector_parameter_value_set("Wing Color".Name Color.@@yellow)

So let’s have a look at the SkookumScript butterfly’s main loop controlling its flight (inside the coroutine _fly). There are three update loops running in parallel, controlled by a race block (a race block contains concurrent statements (three loops in this case) and it terminates when any of the contained statements terminates).

  // 1. Pick a new flight target once in a while
    pick_new_flight_target                // Call the pick_new_flight_target method
    _wait(@@random.uniform_range(1 2.5))  // Wait a random time between 1.0 and 2.5 seconds
    ]                                     // Repeat
  // 2. Flight movement
    move_flight(100 0) // Move and rotate        
    animate_flight     // Animate wing/body components        
    _wait              // Wait one frame
    ]                  // Repeat
  // 3. Collision detection
    if check_for_landing_spot(landing_spot) [exit]   // Check for a landing spot and, if found, exit loop (and enclosing race block)
    _wait(0.4)                                       // Wait 0.4 seconds
    ]                                                // Repeat

There are four helper methods that look like this:

move_flight - move butterfly forward and rotate to face the flight target

(Real forward_speed Real upward_speed)
  !delta_time: GameLib.world_delta_seconds(@@world)
  // Move butterfly forward based on speed variables
  @base.add_local_offset(Vector3!xyz(forward_speed 0 upward_speed) * delta_time)
  // Smoothly ease butterfly rotation to face the flight target
  !target_rotation: MathLib.find_look_at_rotation(@base.component_location @flight_target.component_location)
  @base.world_rotation_set(MathLib.rinterp_to(@base.component_rotation target_rotation delta_time 1.2))

animate_flight - animate the wing/body components for butterfly flight

  // Wings flap
  !wing_angle: MathLib.lerp(0 80 @flight_curve_flapping_motion)
  @left_wing.relative_rotation_set(RotationAngles!yaw_pitch_roll(0 0 wing_angle))
  @right_wing.relative_rotation_set(RotationAngles!yaw_pitch_roll(0 0 -wing_angle))  
  // Body bobs up and down
  @body.relative_location_set(Vector3!xyz(0 0 MathLib.lerp(2 -2 @flight_curve_flapping_motion)))
  @body.relative_rotation_set(RotationAngles!yaw_pitch_roll(0 MathLib.lerp(4 -2 @flight_curve_flapping_motion) 0))  

pick_new_flight_target - pick new location to flutter towards

  // Keep track of how many times we pick a new target
  // Choose new target nearby the attractor location
    if @time_in_flight >= 6
      // Got tired - head back to our trusted landing spot
      // Pick random flight targets as long as we aren't tired
      !target_offset_h: MathLib.random_float_in_range(-0.7 * @max_flight_range @max_flight_range)
      !target_offset_v: MathLib.random_float_in_range(-0.15 * @max_flight_range @max_flight_range)
      @attractor_location + Vector3!xyz(target_offset_h target_offset_h target_offset_v)
  @flight_target.world_location_set(new_target_location false HitResult! false)  

check_for_landing_spot - line trace to check for surfaces to land on

(HitResult landing_spot) Boolean
  !seg_start: @base.component_location
  !seg_end:   seg_start + [@base.forward_vector * 50] - Vector3!xyz(0 0 15)
  SystemLib.line_trace_single_for_objects(@@world seg_start seg_end {EObjectTypeQuery.@@object_type_query_1} true List{Actor}! EDrawDebugTrace.@@none landing_spot true)  

Play with it!

If you’d like to inspect this example in more detail and see it in action, please download the demo project using the link below. Note that these will only work if you have the latest release of the SkookumScript UE4 Plugin installed). The SkookumScript version of the butterfly is inside Blueprint named BP_Butterfly_Sk (and its coroutines and methods are located in the SkookumScript class with the same name). The new SkookumScript code controls the purple butterfly and the original blueprint still controls the other two.

Get it here!

1 Like

No compilation errors as far as I can tell, just the “blueprint bad” icon on the BP_Butterfly_Sk in the level. Is that of no consequence?

That happens occasionally, we have not quite figured out why. Is the purple butterfly working fine otherwise? I.e. can you select the BP_Butterfly_Sk actor in the level outliner and see it moving around? If so, then everything is working as intended.

Ah I see. Yep works fine.

Maybe Unreal Engine is just finding it to be broken because it’s unable to properly read it as working, but still compiles it through and works because of on Skookums side. It’s probably on the Unreal compilers side when trying to read and recompile what Skookum already is able to, just a thought. That probably didn’t make any sense at all, I’m actually terrible at explaining things and it’s only a theory. :grin:

When I fire up this project, I get all kinds of overlay/path errors. The SkookumScript code does not work. I know that this post is old but I was wanting to show this off to others. Does this still work for you fellows? Please let me know, thanks!

Did you get the latest version of the Blyueprints_Sk project from here? We recently changed the Project-Generated-BP file format, and you might be be experiencing trouble with that.

I downloaded the 4.16 Blueprints_SK and I am using the marketplace version of SkookumScript.

Epic has not yet updated the Marketplace with our latest plugin even though we submitted it two weeks ago. We are on their case with that. In the meantime, you can get the zipped version of the 4.16 plugin from the same page. Delete your Engine/Plugins/Marketplace/SkookumScript folder and unzip the 4.16 plugin into the Engine/Plugins/Marketplace folder. Then, when Epic posts the latest plugin on the Marketplace you can simply update to it.

Ah, I see. I thought I had the latest version because I had not updated for a while… then I ran an update. Epic sure can be slow about those updates.

It works great, thanks!

1 Like