Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Spawning actors

The goal: put actors into the level from code: projectiles, enemies, pickups, effects.

Spawning a Blueprint chosen in the editor

The usual case: the class to spawn is a Blueprint child (BP_Projectile), picked in a SubclassOf property.

#![allow(unused)]
fn main() {
use bindings::engine::{ESpawnActorCollisionHandlingMethod, GameplayStatics, KismetMathLibrary};
use bindings::prelude::*;

#[uclass(parent = Character)]
pub struct Shooter {
    #[uproperty(EditAnywhere, category = "Projectile")]
    projectile_class: SubclassOf<Projectile>,
}

impl Shooter {
    fn spawn_projectile(&self) -> RustealResult<()> {
        let me = self.as_ref().checked()?;
        let location = me.k2_get_actor_location().to_dvec3()
            + me.get_actor_forward_vector().to_dvec3() * 100.0;

        let transform = KismetMathLibrary::make_transform(
            &FVector::from_dvec3(location),
            &me.k2_get_actor_rotation(),
            &FVector::from_dvec3(DVec3::ONE),
        );

        let world = self.as_ref().get_world()?;
        world.spawn_actor_of_class(self.projectile_class(), &transform)?;

        Ok(())
    }
}
}

get_world() gives the world an object lives in, and spawn_actor_of_class spawns an actor of a class held in a SubclassOf. It returns an UObjectRef<T> to the new actor, or an error if the class is not set or the spawn failed.

Deferred spawn: setting values before BeginPlay

To set an actor up before its BeginPlay runs (an owner, an instigator, a team), spawn it deferred and finish the spawn after:

#![allow(unused)]
fn main() {
let spawned = GameplayStatics::begin_deferred_actor_spawn_from_class(
    me.as_ref().upcast_to(),
    self.projectile_class().upcast_to(),
    &transform,
    Some(ESpawnActorCollisionHandlingMethod::AlwaysSpawn),
    Some(me.as_ref().upcast_to()),
    None,
);

if let Ok(projectile) = spawned.checked() {
    projectile.set_instigator(me.as_ref().upcast_to());
}

let spawned = GameplayStatics::finish_spawning_actor(spawned, &transform, None);

if let Ok(projectile) = Projectile::from_obj(spawned) {
    projectile.set_damage(self.damage());
}
}

The fifth argument is the owner. The same, with the world and typed parameters:

#![allow(unused)]
fn main() {
let params = SpawnParams {
    owner: Some(me.as_ref().upcast_to()),
    instigator: Some(pawn),
    collision: SpawnCollisionMethod::AlwaysSpawn,
};

let projectile = world.spawn_actor_deferred_of_class(self.projectile_class(), &transform, params)?;
Projectile::from_obj(projectile)?.set_damage(self.damage());
world.finish_spawning(&projectile.upcast_to(), &transform)?;
}

spawn_actor_deferred_of_class returns the actor already typed as the class’s T; SpawnParams::default() has no owner, no instigator and the class’s own collision handling. The collision handling method decides what happens when the spawn point is blocked:

MethodWhat happens
AlwaysSpawnSpawns where asked, overlapping or not.
AdjustIfPossibleButAlwaysSpawnMoves the actor out of the way if it can, spawns anyway.
AdjustIfPossibleButDontSpawnIfCollidingMoves the actor out of the way, or does not spawn.
DontSpawnIfCollidingDoes not spawn if the point is blocked: the returned reference is not valid.

Spawning a Rust class directly

A Rust class with no Blueprint child, or an engine class, is spawned by type:

#![allow(unused)]
fn main() {
let transform = FTransform::from_transform(Transform::new(DQuat::IDENTITY, location, DVec3::ONE));
let gem: UObjectRef<Gem> = world.spawn_actor(&transform)?;
}

Without a Blueprint child, no assets are assigned: give the actor its mesh in code, or prefer a SubclassOf property and a Blueprint.

An actor class without a root component ignores the spawn transform; Rusteal logs a warning when that happens.

Destroying

#![allow(unused)]
fn main() {
me.k2_destroy_actor();
me.set_life_span(5.0);
}

k2_destroy_actor destroys the actor at the end of the frame. set_life_span destroys it after that many seconds.

Finding actors

#![allow(unused)]
fn main() {
let world_context = self.as_ref().upcast_to();

let enemies = GameplayStatics::get_all_actors_of_class(
    world_context,
    SubclassOf::<Enemy>::base().upcast_to(),
);

let starts = GameplayStatics::get_all_actors_of_class_with_tag(
    world_context,
    SubclassOf::<PlayerStart>::base().upcast_to(),
    FName::new("Player1").handle(),
);

let player_pawn = GameplayStatics::get_player_pawn(world_context, 0);
let player_controller = GameplayStatics::get_player_controller(world_context, 0);
let game_mode = GameplayStatics::get_game_mode(world_context);
}

get_all_actors_of_class walks every actor of the level: fine on a key press or at the start of a level, too slow every frame for a big level. Keep the actors you need in a property, or let them register themselves with a subsystem.

Your Rust game mode, from any actor:

#![allow(unused)]
fn main() {
if let Ok(mut game_mode) = MyGameMode::from_obj(GameplayStatics::get_game_mode(world_context)) {
    game_mode.add_score(10);
}
}