MeGodotnk... kinda

I watched https://www.youtube.com/watch?v=gnxnmw5ryhg and wanted to recreate the first part in Godot. So I did… kinda :sweat_smile::

It’s kinda scary when they are all towering on top of each other and climbing any obstacle in their path ngl :anxious_face_with_sweat:

It’s using Jolt, physics interpolation, and I had to tweak a bunch of values of the physics engine: lowering the physics ticks to 30, running the physics server in a separate thread,… I’m also using the RenderingServer and PhysicsServer3D directly so no nodes for the entities.

On my machine it can keep >60fps with 6000 entities running in the editor (until they surround you and everything dies :sweat_smile:). Each entity is also a capsule which is not the best performant shape. I tried with spheres and it runs faster but the result is less impactful.

When exported 6000 entities stay at ~90fps. 7000 entities stay at ~60fps but I think they reach some of Jolt limits and it’s not stable. I tweaked some of the limits values but I’m not sure how much they help.

Here’s the Entity script if someone want to test it:

entity.gd
class_name Entity extends RefCounted


static var mesh: Mesh = CapsuleMesh.new()
static var shape: Shape3D = CapsuleShape3D.new()

var position: Vector3: set=_set_position, get=_get_position
var speed: float = 8.0
var target: Vector3

var floor_wall_angle = deg_to_rad(85)
var direction_angle = deg_to_rad(160)

var _body_rid: RID
var _instance_rid: RID

var PS = PhysicsServer3D
var RS = RenderingServer

var _old_transform: Transform3D
var _transform_interp: Transform3D
var _transform: Transform3D


func _init(world: World3D) -> void:
	speed = randf_range(5.0, 8.0)

	_body_rid = PS.body_create()
	PS.body_set_space(_body_rid, world.space)
	PS.body_set_mode(_body_rid, PhysicsServer3D.BODY_MODE_RIGID_LINEAR)
	PS.body_set_state(_body_rid, PhysicsServer3D.BODY_STATE_CAN_SLEEP, false)
	PS.body_set_enable_continuous_collision_detection(_body_rid, false)
	PS.body_set_max_contacts_reported(_body_rid, 4)
	PS.body_set_collision_layer(_body_rid, 2)
	PS.body_set_collision_mask(_body_rid, 3)
	shape.radius = 0.5
	PS.body_add_shape(_body_rid, shape.get_rid())
	#var t = Transform3D()
	#t.origin = Vector3(0, -0.5, 0)
	#PS.body_set_shape_transform(_body_rid, 0, t)

	PS.body_reset_mass_properties(_body_rid)
	PS.body_set_param(_body_rid, PhysicsServer3D.BODY_PARAM_MASS, 1.0)
	PS.body_set_param(_body_rid, PhysicsServer3D.BODY_PARAM_FRICTION, 0.0)
	PS.body_set_param(_body_rid, PhysicsServer3D.BODY_PARAM_LINEAR_DAMP, 1.0)
	PS.body_set_param(_body_rid, PhysicsServer3D.BODY_PARAM_LINEAR_DAMP_MODE, PhysicsServer3D.BODY_DAMP_MODE_REPLACE)

	PS.body_set_state_sync_callback(_body_rid, _on_state_sync)

    _instance_rid = RS.instance_create()
    RS.instance_set_scenario(_instance_rid, world.scenario)
    RS.instance_set_base(_instance_rid, mesh.get_rid())
    RS.frame_pre_draw.connect(_interpolate_transforms)


func _notification(what: int) -> void:
	if what == NOTIFICATION_PREDELETE:
		if _body_rid.is_valid():
			PS.free_rid(_body_rid)
		if _instance_rid.is_valid():
			RS.free_rid(_instance_rid)

		if RS.frame_pre_draw.is_connected(_interpolate_transforms):
			RS.frame_pre_draw.disconnect(_interpolate_transforms)


func update(delta: float) -> void:
	var direction = position.direction_to(target)
	direction.y = 0.0
	var motion = direction.normalized() * speed

	var state = PS.body_get_direct_state(_body_rid)
	motion.y = state.linear_velocity.y
	state.linear_velocity = motion
	var collisions = state.get_contact_count()
	for i in collisions:
		var normal = state.get_contact_local_normal(i)
		var wall_dot = normal.dot(Vector3.UP)

		if acos(wall_dot) >= floor_wall_angle:
			var dir_dot = normal.dot(motion.normalized())
			if acos(dir_dot) >= direction_angle:
				state.apply_central_impulse(Vector3.UP * 1.0)
				break


func _set_position(value: Vector3) -> void:
	_transform.origin = value
	_old_transform = _transform
	PS.body_set_state(_body_rid, PhysicsServer3D.BODY_STATE_TRANSFORM, _transform)


func _get_position() -> Vector3:
	return _transform.origin


func _interpolate_transforms() -> void:
	var fraction = Engine.get_physics_interpolation_fraction()
	_transform_interp.origin = _old_transform.origin.lerp(_transform.origin, fraction)
	RS.instance_set_transform(_instance_rid, _transform_interp)


func _on_state_sync(state: PhysicsDirectBodyState3D) -> void:
	_old_transform = _transform
	_transform = state.transform
7 Likes

I love it, the effect is terrifying indeed! It’s like the collection of enemies create its own megastructure. It would definitely be a cool mechanic if you could only optimize or better control it.

I wonder why it drops so significantly once they surround the player. I would expect it to be more or less the same scenario as them marching towards the player from the distance, the amount of collisions seems like should be about the same (which is approximately “a shit ton” :smiley: ).

Did you try with the default physics engine to see what’s the difference in performance?

2 Likes

I looked at your code and didn’t see this, you might want to try (if you haven’t already) taking down your radial segments:

mesh.radial_segments = 16
mesh.rings = 2

I’ve needed to do this when I’m using a buttload of meshes with a GPUParticles3D object to keep framerate zippy on the quest. The capsules look a little more boxy, but meh.

3 Likes

After some testing I’m not sure either. The fps go to single digits but they go back to normal after a while and I’m not sure why… maybe because then every entity is climbing on top of each other which skyrockets the collision pairs and after the tower stabilizes itself Jolt applies some optimizations? I’m not sure. Jolt doesn’t report any information like the collision pairs so it’s hard to tell.

:clown_face:

I did but it’s less stable and the performance goes down to the bottom when they start colliding between them :sweat_smile: The default GodotPhysics does report collision pairs.

On my machine I’m not GPU-bounded but CPU-bounded. I can have ~25000 capsules before the framerate goes lower than 60fps:

(it’s actually more than the the physics bodies I’ve configured in ProjectSettings.physics/jolt_physics_3d/limits/max_bodies :sweat_smile: )

:rofl:

5 Likes

It’s giving me phobias rn…

lol,It’s sick!

1 Like

That gave me an idea and… Uuuuuuuuuuuuuggggh…

I HATE IT!!!

I swapped the bean for a spider model and implemented a basic vertex animation texture with a shader

:warning: ARACHNOPHOBIA TRIGGER WARNING :warning:
open under your own risk: https://www.youtube.com/watch?v=zSsB9MhL1wA
:warning: ARACHNOPHOBIA TRIGGER WARNING :warning:

I forgot to tweak the parameters of the bodies so they fly a bit in that video…

Here’s another one with the parameters tweaked:

:warning: ARACHNOPHOBIA TRIGGER WARNING :warning:
open under your own risk: https://www.youtube.com/watch?v=vtg-S7a5rBU
:warning: ARACHNOPHOBIA TRIGGER WARNING :warning:

3 Likes

LoL that is not scary at all… Am I an alien?
I have megalophobia ( phobia of big objs ) and cetaphobia ( phobia of big whales)

Would u try that XD

who is afraid of spiders?

Lucky you! I had to walk away a couple times while implementing it :laughing:

1 Like

The only good bug is a dead bug… you’ll have to add some guns or a flamethrower :stuck_out_tongue:

This is a really cool post, thanks for sharing!

1 Like

Oh, I’d love to see this mountain of spiders melting down, please @mrcdk

1 Like

I don’t think fire is working guys…

:warning: ARACHNOPHOBIA TRIGGER WARNING :warning:
open under your own risk: https://www.youtube.com/watch?v=bEKJdwSrRwQ
:warning: ARACHNOPHOBIA TRIGGER WARNING :warning:

My computer is the only thing melting here :sweat_smile:

EDIT:

I did some optimizations and I can get 10k between 30~70fps depending on how many collisions there are. Not with the fire effect! With fire I can get it to 1200 or so :laughing: but I didn’t optimize it either so who knows

:warning: ARACHNOPHOBIA TRIGGER WARNING :warning:
open under your own risk: https://www.youtube.com/watch?v=9mz2T_fYt1I
:warning: ARACHNOPHOBIA TRIGGER WARNING :warning:

3 Likes

Now you need to release a spider swarm Tech demo with capsule wielding a boomstick, and blasting away spider chunks!

So we can play it :grin:

Trippy! :+1:t5::mechanical_arm::man_dancing:t5::mechanical_arm::+1:t5:

1 Like

I agree with @OleNic this seems like a fantastic 3D survival shooter. Very simple.

Fire spiders was definitely an “oh shit go back” moment :stuck_out_tongue:

I love that it was set to spread between them too, looked like it was running pretty good too!

Strong EDF vibes for sure, would love to see this in a game.

That is amazing! I just want to start blasting them all right now! Someone has to make a game out of this!

EDIT: Of course it seems someone is as in the OP. This is such an impressive effect though, it is genuinely exciting to see it! It made me think of a mutant zombie hoard!

1 Like

I HAVE AN AMAZING IDEA: HOW ABOUT WHEN YOU TAKE DOWN ENEMIES, THEY EXPLODE IN A weird WAY?! BOOM! :collision: IT’S GONNA BE EPIC