:sk: Helpers Repo


#1

This is an ongoing topic that I’ll update whenever I add new things to my SkookumScript Helpers Repo. The repo itself has a decent readme with all items catalogued for quick viewing.


:sk: Output Buffer Blocks Compiling
#2

Added Boolean/_wait_cycle_return_value

//---------------------------------------------------------------------------------------
// Waits for the next rising edge of the boolean, when detected, sets out_var to value.
// 
// Params:
//   poll_secs: 
//     Number of seconds that should be waited between each test of this Boolean's value.
//     If it is 0.0 it will test every simulation update/frame. 
//
//   out_var:
//     The output variable that should be written to on success
//
//   value:
//     The value to write to out_var on success
// Notes:
//     Designed to determine which coroutine won a race.
//
// Examples:
//    !result : -1
//    race
//    [
//      @test0?._wait_cycle_return_value(0.0, result, 0)
//      @test1?._wait_cycle_return_value(0.0, result, 1)
//      @test2?._wait_cycle_return_value(0.0, result, 2)
//      @test3?._wait_cycle_return_value(0.0, result, 3)
//      @test4?._wait_cycle_return_value(0.0, result, 4)
//    ]
//    do_something(result)
//   
//---------------------------------------------------------------------------------------

(Real poll_secs: 0.1, Integer out_var, Integer value)
[
  _wait_false(poll_secs)
  _wait_true(poll_secs)
  out_var := value
]

This one is a pattern that I’ve found helpful, especially when writing high-level input handling, for instance mapping inputs to abilities and/or canceling abilities. The goal behind this code is to figure out who won a race. In this case I’m specifically waiting for the next rising edge of a boolean.

As an example of where I’m using this, let’s say my character has 2 abilities mapped to the following inputs:
Glide - A button
Melee - B button

So if A is being held and the player starts holding B, I want to cancel glide and start melee. Now both A and B are being held, I don’t want to ping-pong and cancel my Melee ability because A is still held, so this is the case for only triggering on next rising edge.

To use this I might have a _cancel_ability coroutine

(Integer new_ability) 
[
  !ability : -1
  race
  [
    @wants_glide?._wait_cycle_return_value(0.0, ability, 1)
    @wants_melee_attack?._wait_cycle_return_value(0.0, ability, 2)
  ]
  new_ability := ability
]

Then to trigger an ability I could basically do something like:

!new_ability_idx : -1
race
[
  _ability_glide
  _cancel_ability(new_ability_idx)
]
do_something(new_ability_idx)

#3

Added some wrappers for SystemLib.print_string used for specifically printing to the screen/log and avoiding the parameter craziness.

SystemLib.print_log takes 1 argument, a string.
SystemLib.print_log("Yo, I'm popping and locking to the log with only 1 parameter")

SystemLib.print_screen only needs 1 parameter for printing every frame but also the argument order was re-arranged to easily change duration.

SystemLib.print_screen("Easy for printing in a loop")
SystemLib.print_screen("Print a one-shot and leave it on for 5-seconds", 5.0)
SystemLib.print_screen("I just really need green", 5.0, Color!green)

#4

Added HitResult!fake, which is a copy of an alternate C++ constructor for HitResult. This is useful when you need to construct a HitResult by hand (outside of a trace) with a limited set of data. It’s not a true HitResult, but it’s enough to throw around and test certain things, like applying point damage:

GameLib.apply_point_damage(some_actor, 1.0,, HitResult!fake(,,some_actor.actor_location))


#5

New:

  • LevelSequenceActor.jump_to_end, handy for fast-forwarding cinematics.
  • Transform.near? for comparing transforms

#6

Added a bunch of viewport and camera helpers. I’ve always been frustrated with some of the default projection functions in :ue4: particularly the fact that they don’t have any way to do screen/world deprojection using normalized screen coordinates [-1, 1].

CameraComponent
aspect_ratio_constrained?
Helper to tell you whether the aspect ratio for a camera is constrained.

GameLib

deproject_nscreen_to_world
The :ue4: deprojection methods take screen coordinates (like, how are you supposed to easily reference the top right corner of the screen?). The :ue4: deprojection methods are also ignorant of aspect ratio, so if aspect ratios are constrained and you want to get the top right of the screen, it’ll just happily do your deprojection into the blackness of the letterbox. Gee, thanks! This method not only takes normalized screen coordinates (resolution invariance :tada:), but also adds an option to respect AR so that the deprojection only occurs inside the letterboxed portion of the viewport.

deproject_point_to_plane
This lets you deproject a point in normalized screen coordinates to a plane whose normal faces the camera at a specified distance in front of the camera. This is super useful if you’re say making a 2D in 3D type game and want to get the game boundaries in world space at a specific distance from the camera. Think space invaders, you have a fixed camera and want to know where the four corners of the screen are in world space at a distance D in front of the camera. It also can respect AR.

viewport_coordinates_norm_to_screen
viewport_coordinates_screen_to_norm
viewport_size

Some helpers to convert between normalized and regular screen coordinates, as well as one to get the viewport size (which is required internally to convert between normalized and regular coordinates).

PlayerController
active_camera
A helper to get the active CameraComponent for a player controller.


#7

Added
_wait_frames - Handy for those times when you want to wait based on number of ticks/frames.

Integer
even? True if even
odd? True if odd


#8

Added constructor Vector3!one for parity with FVector::OneVector.
Added Actor.furthest_in_dir that finds the actor that is furthest in a given direction from a list of actors.


#9

Added some Boolean helpers:
_wait_true_false Does exactly what you think
_wait_false_true Also does exactly what you think

map To map the value of a Boolean to objects. Still a little undecided about this one. Was trying to make this cleaner:
!dir : Vector3!right * if right?[1.0] else [-1.0]

But it’s actually longer due to the forced cast (returning an object):
!dir : Vector3!right * right?.map(1.0, -1.0)<>Real


#10

A suggestion, do not name it map, that name is used often in other languages with a different meaning.

Historic use:

{1 2 3}.map[sq(item)] => {1 4 9}


#11

Any name suggestions? I was half-basing the name on the existing MapRange from KismetMathLibrary.


#12

Good names are the hardest part of programming for me. How about either?


#13

What do you think of ternary? Technically there are only 2 parameters passed to it while the 3rd is an implied this… so still meets the criteria for the name with a stretch.

I was also thinking that having variants for Real and Integer would be helpful although I’m undecided on whether to copy the :ue4: fceil fclamp or use an r prefix for Real instead. Naming is hard :cold_sweat:.


#14

After some thought, I’ve renamed Boolean.map, it now has variants:

  • as_objs
  • as_reals
  • as_ints
  • as_vec3s

Also added yesterday:
destroy_sk_actor

This one is pretty great. You may have noticed that when destroying actors that have running coroutines that you often have to protect yourself against the actor going null. The reasons behind this are that destruction/cleanup in :ue4: is latent, so while the coroutines eventually get killed off for any actor that is destroyed, it can often not happen until the next frame. Which means all of your running coroutines will get one more tick :clock1:.

Typically to protect against this you would sprinkle a lot of if valid? checks anywhere you might be looping. With destroy_sk_actor, the actor gets destroyed AND aborts all coroutines in the same frame. This prevents you from having to do these null checks and results in much cleaner code without the worry of getting the rug yanked out from under you after destruction or for instance a stray overlap firing after you destroyed your actor (if you’re capturing overlaps in a coroutine).


#15

Added helpers for Controller class that are pretty self-explanatory.

_wait_pawn_null
_wait_pawn_valid

#16

Added
Random.vector3_between finds a random vector that is between 2 given vectors.
Color.near? Returns true when the given color is near the color of this.

Also updated SoundCue play & _play so that null checks are no longer needed.


#17

Just found this topic. Great stuff. Would be helpful if there was perhaps an SK wiki we could keep building on to that could reference these kinds of helpers and other topics as important.