-
-
Notifications
You must be signed in to change notification settings - Fork 14
Entities
You'll need to create your own entities for your game. If you want to use entities from Half-Life 2, you should implement the entity logic on your own.
All entities should be in @tool mode, extend from VMFEntityNode, and be placed in the folder assigned in the config (entities_folder).
- Create a script for an entity.
- Create a 3D scene with the same name and assign the script.
- Try to import your map with the entity.
Note
Inputs of entities are just methods with a single parameter that is passed from outputs.
The input method MUST have one parameter. If you don't need a parameter, just write _void = null.
func SomeInput(_void = null):
# Do somethingImportant
Outputs should be called by the trigger_output method (e.g., from a signal).
Repo with some implemented entities: https://github.com/H2xDev/GodotVMF-Entities
In case you don't want to extend your entity from VMFEntityNode, just define a 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 VMFEntityNode.
class_name SomeEntity extends Node3D
var entity: Dictionary = {};
static func setup(entity: VMFEntity, instance: SomeEntity):
instance.entity = entity.data; # This is required
instance.transform = VMFEntityNode.get_entity_transform(entity);
instance.basis *= Basis(Vector3.UP, -PI / 2);
## func_button.gd
@tool
extends VMFEntityNode
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 of _ready
func _entity_ready():
is_locked = has_flag(FLAG_STARTS_LOCKED);
# Once we trigger this signal, we trigger 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 _entity_setup(e: VMFEntity):
# 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;In case you don't want to place some entities inside the entities folder, you can use aliases:
The base of all entities. Contains I/O logic and some useful methods for entities.
All entities extending from this node will always have these inputs, so you don't need to define them.
| Input | Description |
|---|---|
| Toggle | Toggles enabled field of the entity |
| Enable | Enables the entity |
| Disable | Disables and blocks outputs for the entity |
| Kill | Removes the node from the tree |
_entity_ready_entity_setuphas_flagtrigger_outputget_meshget_entity_shapeget_entity_convex_shapeget_entity_trimesh_shapeget_entity_basisget_movement_vectorconvert_vectorconvert_directiondefine_aliasremove_aliasget_targetget_all_targetsget_separated_collisionsget_entity_transform
| Signature | Description |
|---|---|
_entity_ready() |
Called when all outputs and reparents are ready. Use this method instead of _ready. |
Example:
func _entity_ready():
# Initialize your entity logic here
if has_flag(1):
queue_free()| Signature | Description |
|---|---|
_entity_setup(e: VMFEntity) |
Called during import to setup entities (assigning brushes, generating collisions). |
Example:
func _entity_setup(e: VMFEntity):
var mesh = get_mesh()
$MeshInstance3D.mesh = mesh
$CollisionShape3D.shape = mesh.create_convex_shape()| Signature | Description |
|---|---|
has_flag(flag: int) -> bool |
Checks the spawnflags field of the entity. |
Example:
const FLAG_LOCKED = 2048
func _entity_ready():
isLocked = has_flag(FLAG_LOCKED)| Signature | Description |
|---|---|
trigger_output(outputName: String | Signal) |
Triggers outputs defined in the entity. |
Example:
if isLocked:
trigger_output("OnUseLocked")
else:
trigger_output("OnPressed")| Signature | Description |
|---|---|
get_mesh(cleanup=true, lods=true) -> ArrayMesh |
Returns entity's solids as ArrayMesh.cleanup: remove unnecessary faces.lods: generate LODs. |
Example:
$mesh.mesh = get_mesh()| Signature | Description |
|---|---|
get_entity_shape() -> Shape |
Returns optimized collision shape (trimesh or convex). Uses CSGMesh. |
Example:
$col.shape = get_entity_shape()| Signature | Description |
|---|---|
get_entity_convex_shape() -> Shape |
Returns a convex shape for the entity's brushes. |
Example:
$CollisionShape3D.shape = get_entity_convex_shape()| Signature | Description |
|---|---|
get_entity_trimesh_shape() -> Shape |
Returns optimized trimesh shape for the entity's brushes. |
Example:
$CollisionShape3D.shape = get_entity_trimesh_shape()| Signature | Description |
|---|---|
static get_entity_basis(entity: Dictionary) -> Basis |
Returns rotation state for specified entity. |
Example:
var rotation_basis = VMFEntityNode.get_entity_basis(entity_data)| Signature | Description |
|---|---|
static get_movement_vector(vec: Vector3) -> Vector3 |
Returns directional vector from property. Useful for entities like func_door or func_button. |
Example:
var dir: Vector3:
get: return get_movement_vector(entity.get("movedir", Vector3.LEFT))| Signature | Description |
|---|---|
static convert_vector(v: Vector3) -> Vector3 |
Converts Vector3 position from Z-up to Y-up. |
Example:
var godot_pos = VMFEntityNode.convert_vector(source_pos)| Signature | Description |
|---|---|
static convert_direction(v: Vector3) -> Vector3 |
Converts Vector3 rotation from Z-up to Y-up. |
Example:
var godot_rot = VMFEntityNode.convert_direction(source_rot)| Signature | Description |
|---|---|
static define_alias(name: String, value: VMFEntityNode) |
Defines global alias to node for using in I/O. |
Example:
func _entity_ready():
VMFEntityNode.define_alias('!player', self)| Signature | Description |
|---|---|
static remove_alias(name: String) |
Removes global alias. |
Example:
func _exit_tree():
VMFEntityNode.remove_alias('!player')| Signature | Description |
|---|---|
get_target(targetName: String) -> VMFEntityNode |
Returns first node by target name assigned in entity. |
Example:
var target_node = get_target(entity.target)
if target_node:
target_node.trigger_input("Use")| Signature | Description |
|---|---|
get_all_targets(targetName: String) -> Array[VMFEntityNode] |
Returns all nodes by target name assigned in entities. |
Example:
var targets = get_all_targets(entity.target)
for t in targets:
t.trigger_input("Use")| Signature | Description |
|---|---|
get_separated_collisions() -> Array[CollisionShape3D] |
Returns a list of collisions for each brush. |
Example:
for col in get_separated_collisions():
$StaticBody3D.add_child(col)| Signature | Description |
|---|---|
static get_entity_transform(entity: Dictionary) -> Transform3D |
Returns Transform3D of entity. |
Example:
transform = VMFEntityNode.get_entity_transform(entity_data)To make prop entities creation a bit easier you can just inherit your entity from prop_studio instead VMFEntityNode.
-
model_instance: MeshInstance3D- The mesh instance with the target model -
model: String- The path to the model