Skip to content

Entities

Radik edited this page Aug 13, 2025 · 16 revisions

You'll need to create your own entities for your game. If you want to use entities from Half-Life 2 then you should implement entities logic on your own. All entities should be in @tool mode, extended from ValveIONode and placed in the folder that you assigned in the config (entities_folder).

  1. Create script of an entity.
  2. Create a 3d scene with the same name and assign the script.
  3. Try to import your map with entity.

Note

Inputs of entities are just methods with a single parameter that passed from outputs.
The input method MUST have one parameter. In case you don't need a parameter just write _void = null

func SomeInput(_void = null): 
  # Do something

Important

Outputs should be called by method trigger_output (from signal for example)

Repo with some implemented entities: https://github.com/H2xDev/GodotVMF-Entities

In case you don't want to extend your entity from ValveIONode, just define static function called setup. In this case it's not necessary to make the script in @tool mode. Also you won't be able to use flags and other features from ValveIONode.

class_name SomeEntity extends Node3D

var entity: Dictionary = {};

static func setup(entity_structure: Dictionary, instance: SomeEntity):
	instance.entity = entity_structure; # This is required
	instance.transform = ValveIONode.get_entity_transform(entity_structure);
	instance.basis *= Basis(Vector3.UP, -PI / 2);

Example

## func_button.gd
@tool
extends ValveIONode

signal interact();
signal OnUseLocked();
signal OnPressed();


const FLAG_ONCE = 2;
const FLAG_STARTS_LOCKED = 2048;

var is_locked = false;
var is_used = false;

var sound = null;
var locked_sound = null;

## Use this instead _ready
func _entity_ready():
	is_locked = has_flag(FLAG_STARTS_LOCKED);

	# Once we trigger this signal we triggering the entity's outputs.
	interact.connect(_on_interact);

	if "sound" in entity:
		sound = load("res://Assets/Sounds/" + entity.sound);

	if "locked_sound" in entity:
		locked_sound = load("res://Assets/Sounds/" + entity.locked_sound);

# This method will be called during import
func _apply_entity(entity_structure: Dictionary):
	super._apply_entity(entity_structure);

	# Getting entity's brush geometry and assigning it
	var mesh = get_mesh();
	$MeshInstance3D.set_mesh(mesh);

	# Generating collision for the assigned mesh.
	$MeshInstance3D/StaticBody3D/CollisionShape3D.shape = mesh.create_convex_shape();

func _on_interact():
	if is_locked:
		if locked_sound: SoundManager.PlaySound(global_position, locked_sound, 0.05);
		trigger_output(OnUseLocked)
		return;

	if has_flag(FLAG_ONCE) and is_used:
		return;

	if sound: SoundManager.PlaySound(global_position, sound, 0.05);
	trigger_output(OnPressed)
	is_used = true


func Lock(_param = null):
	is_locked = true;

func Unlock(_param = null):
	is_locked = false;

Entity aliases

In case you don't want to place some entities inside the entities folder you can use aliases:

// vmf.config.json
{
  "import": {
    "entity_aliases": {
      "npc_ghost": "res://objects/npc_ghost/npc_ghost.tscn"
    }
  }
}

ValveIONode

The Base of all entities. Contains I/O logic and some useful methods for entities.

All entities extends from this node will always have these inputs so you don't need to define it.

  • Toggle - toggles enabled field of the entity
  • Enable - enable the entity
  • Disable - disables and blocks outputs for the entity
  • Kill - removes the node from tree

Reference:

_entity_ready()

Means that all outputs and reparents are ready to use. Use this method instead of _ready.

_apply_entity(entity_structure: Dictionary)

This method called by VMFNode during import entities and needs to make some setup for entities, such as assigning brushes, generation collisions and so on.

Don't forget to call super._apply_entity before making any changes in the node.

Example

func _apply_entity(entity_structure: Dictionary):
	super._apply_entity(entity_structure);

	# Getting a mesh from the solid data of the entity and assigning
	var mesh = get_mesh();
	$MeshInstance3D.set_mesh(mesh);

	# Generating a collision shape for the mesh
	$MeshInstance3D/StaticBody3D/CollisionShape3D.shape = mesh.create_convex_shape();

has_flag(flag: int) -> bool

Checks the spawnflags field of the entity.

Example

const FLAG_STARTS_LOCKED = 2048;
var isLocked = false;

func _entity_ready():
	isLocked = has_flag(FLAG_STARTS_LOCKED);

trigger_output(outputName: string):

Triggers outputs that defined in the entity.

Example

interact.connect(func():
	if isLocked:
		trigger_output("OnUseLocked");
	else:
		trigger_output("OnPressed"));

get_mesh(cleanup = true, lods = true) -> ArrayMesh

Returns entity's solids as ArrayMesh. cleanup means that the mesh will be cleaned from unnecessary faces. lods means that the mesh will have generated LODs.

Example

func _apply_entity(entity_structure: Dictionary):
	super._apply_entity(entity_structure);
	$mesh_instance.mesh = get_mesh(); # Will return ready-to-use mesh with removed ignored faces and generated LODs

get_entity_shape() -> Shape:

Returns optimized collision shape for the entity's solids (trimesh or convex. Depends of brushes count inside of entity). Uses CSGMesh for shape generation

Example

## $body is a StaticBody3D
func _apply_entity(entity_structure: Dictionary):
	super._apply_entity(entity_structure);
	$body/mesh.mesh = get_mesh();
	$body/collision_shape.shape = get_entity_shape();

get_entity_convex_shape() -> Shape

Returns a convex shape for the entity's brushes

get_entity_trimesh_shape() -> Shape

Returns optimized trimesh shape for the entity's brushes

get_entity_basis(entity: Dictionary) -> Basis [static]

Returns rotation state for specified entity.

get_movement_vector(vec: Vector3) -> Vector3 [static]

Returns directional vector from specified vector. If an entity has movement direction property (i.e. func_door, func_button) use this function to convert the direction.

Example

## func_door.gd
var movement_direction: Vector3:
	get: return get_movement_vector(entity.get("movedir", Vector3.LEFT));

convert_vector(v: Vector3) -> Vector3 [static]

Converts Vector3 of position from Z-up to Y-up.

convert_direction(v: Vector3) -> Vector3 [static]

Converts Vector3 of rotation from Z-up to Y-up.

define_alias(name: string, value: ValveIONode) [static]

Defines global alias to node for using in I/O.

Example

# player.gd

func _entity_ready():
	ValveIONode.define_alias('!player', self);

get_target(targetName: string) -> ValveIONode

Returns first node by target name assigned in entity.

get_all_targets(targetName: string) -> ValveIONode[]

Returns all nodes by target name assigned in entities.

get_separated_collisions() -> Array[CollisionShape3D]

Returns a list of collisions for each brush.

get_entity_transform(entity_structure: Dictionary) -> Transform3D [static]

Returns Transform3D of entity

get_entity_basis(entity_structure: Dictionary) -> Basis [static]

Returns Basis of entity

Prop entities

To make prop entities creation a bit easier you can just inherit your entity from prop_studio instead ValveIONode.

Properties

  • model_instance: MeshInstance3D - The mesh instance with the target model
  • model: String - The path to the model
Clone this wiki locally