mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-11-03 18:39:19 +00:00 
			
		
		
		
	Add examples
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	
This commit is contained in:
		
							parent
							
								
									549fe97622
								
							
						
					
					
						commit
						af093713b4
					
				
					 6 changed files with 173 additions and 48 deletions
				
			
		| 
						 | 
				
			
			@ -1,15 +1,19 @@
 | 
			
		|||
local jecs = require("@jecs")
 | 
			
		||||
local world = jecs.World.new()
 | 
			
		||||
local world = jecs.world()
 | 
			
		||||
 | 
			
		||||
local Position = world:component()
 | 
			
		||||
local Position = world:component() :: jecs.Id<vector>
 | 
			
		||||
local Walking = world:component()
 | 
			
		||||
local Name = world:component()
 | 
			
		||||
local Name = world:component() :: jecs.Id<string>
 | 
			
		||||
 | 
			
		||||
local function name(e: jecs.Entity): string
 | 
			
		||||
	return assert(world:get(e, Name))
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Create an entity with name Bob
 | 
			
		||||
local bob = world:entity()
 | 
			
		||||
 | 
			
		||||
-- The set operation finds or creates a component, and sets it.
 | 
			
		||||
world:set(bob, Position, Vector3.new(10, 20, 30))
 | 
			
		||||
world:set(bob, Position, vector.create(10, 20, 30))
 | 
			
		||||
-- Name the entity Bob
 | 
			
		||||
world:set(bob, Name, "Bob")
 | 
			
		||||
-- The add operation adds a component without setting a value. This is
 | 
			
		||||
| 
						 | 
				
			
			@ -18,15 +22,16 @@ world:add(bob, Walking)
 | 
			
		|||
 | 
			
		||||
-- Get the value for the Position component
 | 
			
		||||
local pos = world:get(bob, Position)
 | 
			
		||||
print(`\{{pos.X}, {pos.Y}, {pos.Z}\}`)
 | 
			
		||||
assert(pos)
 | 
			
		||||
print(`\{{pos.x}, {pos.y}, {pos.z}\}`)
 | 
			
		||||
 | 
			
		||||
-- Overwrite the value of the Position component
 | 
			
		||||
world:set(bob, Position, Vector3.new(40, 50, 60))
 | 
			
		||||
world:set(bob, Position, vector.create(40, 50, 60))
 | 
			
		||||
 | 
			
		||||
local alice = world:entity()
 | 
			
		||||
-- Create another named entity
 | 
			
		||||
world:set(alice, Name, "Alice")
 | 
			
		||||
world:set(alice, Position, Vector3.new(10, 20, 30))
 | 
			
		||||
world:set(alice, Position, vector.create(10, 20, 30))
 | 
			
		||||
world:add(alice, Walking)
 | 
			
		||||
 | 
			
		||||
-- Remove tag
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +39,7 @@ world:remove(alice, Walking)
 | 
			
		|||
 | 
			
		||||
-- Iterate all entities with Position
 | 
			
		||||
for entity, p in world:query(Position) do
 | 
			
		||||
	print(`{entity}: \{{p.X}, {p.Y}, {p.Z}\}`)
 | 
			
		||||
	print(`{name(entity)}: \{{p.x}, {p.y}, {p.z}\}`)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Output:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										60
									
								
								examples/luau/queries/archetypes/targets.luau
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										60
									
								
								examples/luau/queries/archetypes/targets.luau
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,60 @@
 | 
			
		|||
-- Using world:target is the recommended way to grab the target in a wildcard
 | 
			
		||||
-- query. However the random access can add up in very hot paths. Accessing its
 | 
			
		||||
-- column can drastically improve performance, especially when there are
 | 
			
		||||
-- multiple adjacent pairs.
 | 
			
		||||
 | 
			
		||||
local jecs = require("@jecs")
 | 
			
		||||
local pair = jecs.pair
 | 
			
		||||
local __ = jecs.Wildcard
 | 
			
		||||
 | 
			
		||||
local world = jecs.world()
 | 
			
		||||
 | 
			
		||||
local Likes = world:entity()
 | 
			
		||||
local function name(e, name: string): string
 | 
			
		||||
	if name then
 | 
			
		||||
		world:set(e, jecs.Name, name)
 | 
			
		||||
		return name
 | 
			
		||||
	end
 | 
			
		||||
	return assert(world:get(e, jecs.Name))
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local e1 = world:entity()
 | 
			
		||||
name(e1, "e1")
 | 
			
		||||
local e2 = world:entity()
 | 
			
		||||
name(e2, "e2")
 | 
			
		||||
local e3 = world:entity()
 | 
			
		||||
name(e3, "e3")
 | 
			
		||||
 | 
			
		||||
world:add(e1, pair(Likes, e2))
 | 
			
		||||
world:add(e1, pair(Likes, e3))
 | 
			
		||||
 | 
			
		||||
local likes = jecs.component_record(world, pair(Likes, __))
 | 
			
		||||
assert(likes)
 | 
			
		||||
 | 
			
		||||
local likes_cr = likes.records
 | 
			
		||||
local likes_counts = likes.counts
 | 
			
		||||
 | 
			
		||||
local archetypes = world:query(pair(Likes, __)):archetypes()
 | 
			
		||||
 | 
			
		||||
for _, archetype in archetypes do
 | 
			
		||||
	local types = archetype.types
 | 
			
		||||
 | 
			
		||||
	-- Get the starting index which is what the (R, *) alias is at
 | 
			
		||||
	local wc = likes_cr[archetype.id]
 | 
			
		||||
	local count = likes_counts[archetype.id]
 | 
			
		||||
 | 
			
		||||
	local entities = archetype.entities
 | 
			
		||||
	for i = #entities, 1, -1 do
 | 
			
		||||
		-- It is generally a good idea to iterate backwards on arrays if you
 | 
			
		||||
		-- need to delete the iterated entity to prevent iterator invalidation
 | 
			
		||||
		local entity = entities[i]
 | 
			
		||||
		for cr = wc, wc + count - 1 do
 | 
			
		||||
			local person = jecs.pair_second(world, types[cr])
 | 
			
		||||
			print(`entity ${entity} likes ${person}`)
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Output:
 | 
			
		||||
--  entity $273 likes $275
 | 
			
		||||
--  entity $273 likes $274
 | 
			
		||||
							
								
								
									
										44
									
								
								examples/luau/queries/archetypes/visibility_cascades.luau
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										44
									
								
								examples/luau/queries/archetypes/visibility_cascades.luau
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
-- To get the most out of performance, you can lift the inner loop of queries to
 | 
			
		||||
-- the system in which you can do archetype-specific optimizations like finding
 | 
			
		||||
-- the parent once per archetype rather than per entity.
 | 
			
		||||
 | 
			
		||||
local jecs = require("@jecs")
 | 
			
		||||
local pair = jecs.pair
 | 
			
		||||
local ChildOf = jecs.ChildOf
 | 
			
		||||
local __ = jecs.Wildcard
 | 
			
		||||
 | 
			
		||||
local world = jecs.world()
 | 
			
		||||
 | 
			
		||||
local Position = world:component() :: jecs.Id<vector>
 | 
			
		||||
local Visible = world:entity()
 | 
			
		||||
 | 
			
		||||
local parent = world:entity()
 | 
			
		||||
world:set(parent, Position, vector.zero)
 | 
			
		||||
world:add(parent, Visible)
 | 
			
		||||
 | 
			
		||||
local child = world:entity()
 | 
			
		||||
world:set(child, Position, vector.one)
 | 
			
		||||
world:add(child, pair(ChildOf, parent))
 | 
			
		||||
 | 
			
		||||
local parents = jecs.component_record(world, pair(ChildOf, __))
 | 
			
		||||
assert(parents)
 | 
			
		||||
 | 
			
		||||
local parent_cr = parents.records
 | 
			
		||||
 | 
			
		||||
local archetypes = world:query(Position, pair(ChildOf, __)):archetypes()
 | 
			
		||||
 | 
			
		||||
for _, archetype in archetypes do
 | 
			
		||||
	local types = archetype.types
 | 
			
		||||
	local p = jecs.pair_second(world, types[parent_cr[archetype.id]])
 | 
			
		||||
	if world:has(p, Visible) then
 | 
			
		||||
		local columns = archetype.columns_map
 | 
			
		||||
		local positions = columns[Position]
 | 
			
		||||
	  	for row, entity in archetype.entities do
 | 
			
		||||
			local pos = positions[row]
 | 
			
		||||
			print(`Child ${entity} of ${p} is visible at {pos}`)
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Output:
 | 
			
		||||
--  Child $274 of $273 is visibile at 1,1,1
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
local jecs = require("@jecs")
 | 
			
		||||
local world = jecs.World.new()
 | 
			
		||||
local world = jecs.world()
 | 
			
		||||
 | 
			
		||||
local Position = world:component()
 | 
			
		||||
local Velocity = world:component()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,19 +1,16 @@
 | 
			
		|||
local jecs = require("@jecs")
 | 
			
		||||
local pair = jecs.pair
 | 
			
		||||
 | 
			
		||||
local world = jecs.World.new()
 | 
			
		||||
local Name = world:component()
 | 
			
		||||
local world = jecs.world()
 | 
			
		||||
local Name = world:component() :: jecs.Id<string>
 | 
			
		||||
 | 
			
		||||
local function named(ctr, name)
 | 
			
		||||
	local e = ctr(world)
 | 
			
		||||
	world:set(e, Name, name)
 | 
			
		||||
	return e
 | 
			
		||||
end
 | 
			
		||||
local function name(e)
 | 
			
		||||
	return world:get(e, Name)
 | 
			
		||||
 | 
			
		||||
local function name(e: jecs.Entity): string
 | 
			
		||||
	return assert(world:get(e, Name))
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local Position = named(world.component, "Position") :: jecs.Entity<vector>
 | 
			
		||||
local Position = world:component() :: jecs.Id<vector>
 | 
			
		||||
world:set(Position, Name, "Position")
 | 
			
		||||
local Previous = jecs.Rest
 | 
			
		||||
 | 
			
		||||
local added = world
 | 
			
		||||
| 
						 | 
				
			
			@ -29,10 +26,14 @@ local removed = world
 | 
			
		|||
	:cached()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
local e1 = named(world.entity, "e1")
 | 
			
		||||
local e1 = world:entity()
 | 
			
		||||
world:set(e1, Name, "e1")
 | 
			
		||||
world:set(e1, Position, vector.create(10, 20, 30))
 | 
			
		||||
local e2 = named(world.entity, "e2")
 | 
			
		||||
 | 
			
		||||
local e2 = world:entity()
 | 
			
		||||
world:set(e2, Name, "e2")
 | 
			
		||||
world:set(e2, Position, vector.create(10, 20, 30))
 | 
			
		||||
 | 
			
		||||
for entity, p in added do
 | 
			
		||||
	print(`Added {name(entity)}: \{{p.x}, {p.y}, {p.z}}`)
 | 
			
		||||
	world:set(entity, pair(Previous, Position), p)
 | 
			
		||||
| 
						 | 
				
			
			@ -40,10 +41,10 @@ end
 | 
			
		|||
 | 
			
		||||
world:set(e1, Position, vector.create(999, 999, 1998))
 | 
			
		||||
 | 
			
		||||
for _, archetype in changed:archetypes() do
 | 
			
		||||
for entity, new, old in changed do
 | 
			
		||||
	if new ~= old then
 | 
			
		||||
		print(`{name(e)}'s Position changed from \{{old.x}, {old.y}, {old.z}\} to \{{new.x}, {new.y}, {new.z}\}`)
 | 
			
		||||
		world:set(e, pair(Previous, Position), new)
 | 
			
		||||
		print(`{name(entity)}'s Position changed from \{{old.x}, {old.y}, {old.z}\} to \{{new.x}, {new.y}, {new.z}\}`)
 | 
			
		||||
		world:set(entity, pair(Previous, Position), new)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,32 +3,47 @@ local pair = jecs.pair
 | 
			
		|||
local ChildOf = jecs.ChildOf
 | 
			
		||||
local __ = jecs.Wildcard
 | 
			
		||||
local Name = jecs.Name
 | 
			
		||||
local world = jecs.World.new()
 | 
			
		||||
local world = jecs.world()
 | 
			
		||||
 | 
			
		||||
type Id<T = nil> = number & { __T: T }
 | 
			
		||||
local Voxel = world:component() :: Id
 | 
			
		||||
local Position = world:component() :: Id<Vector3>
 | 
			
		||||
local Perception = world:component() :: Id<{
 | 
			
		||||
local Voxel = world:component() :: jecs.Id
 | 
			
		||||
local Position = world:component() :: jecs.Id<vector>
 | 
			
		||||
local Perception = world:component() :: jecs.Id<{
 | 
			
		||||
	range: number,
 | 
			
		||||
	fov: number,
 | 
			
		||||
	dir: Vector3,
 | 
			
		||||
	dir: vector,
 | 
			
		||||
}>
 | 
			
		||||
local PrimaryPart = world:component() :: Id<Part>
 | 
			
		||||
type part = {
 | 
			
		||||
	Position: vector
 | 
			
		||||
}
 | 
			
		||||
local PrimaryPart = world:component() :: jecs.Id<part>
 | 
			
		||||
 | 
			
		||||
local local_player = game:GetService("Players").LocalPlayer
 | 
			
		||||
local local_player = {
 | 
			
		||||
	Character = {
 | 
			
		||||
		PrimaryPart = {
 | 
			
		||||
			Position = vector.create(50, 0, 30)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
local workspace = {
 | 
			
		||||
	CurrentCamera = {
 | 
			
		||||
		CFrame = {
 | 
			
		||||
			LookVector = vector.create(0, 0, -1)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
local function distance(a: Vector3, b: Vector3)
 | 
			
		||||
	return (b - a).Magnitude
 | 
			
		||||
local function distance(a: vector, b: vector)
 | 
			
		||||
	return vector.magnitude((b - a))
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function is_in_fov(a: Vector3, b: Vector3, forward_dir: Vector3, fov_angle: number)
 | 
			
		||||
local function is_in_fov(a: vector, b: vector, forward_dir: vector, fov_angle: number)
 | 
			
		||||
	local to_target = b - a
 | 
			
		||||
 | 
			
		||||
	local forward_xz = Vector3.new(forward_dir.X, 0, forward_dir.Z).Unit
 | 
			
		||||
	local to_target_xz = Vector3.new(to_target.X, 0, to_target.Z).Unit
 | 
			
		||||
	local forward_xz = vector.normalize(vector.create(forward_dir.x, 0, forward_dir.z))
 | 
			
		||||
	local to_target_xz = vector.normalize(vector.create(to_target.x, 0, to_target.z))
 | 
			
		||||
 | 
			
		||||
	local angle_to_target = math.deg(math.atan2(to_target_xz.Z, to_target_xz.X))
 | 
			
		||||
	local forward_angle = math.deg(math.atan2(forward_xz.Z, forward_xz.X))
 | 
			
		||||
	local angle_to_target = math.deg(math.atan2(to_target_xz.z, to_target_xz.x))
 | 
			
		||||
	local forward_angle = math.deg(math.atan2(forward_xz.z, forward_xz.z))
 | 
			
		||||
 | 
			
		||||
	local angle_difference = math.abs(forward_angle - angle_to_target)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -42,7 +57,7 @@ end
 | 
			
		|||
local map = {}
 | 
			
		||||
local grid = 50
 | 
			
		||||
 | 
			
		||||
local function add_to_voxel(source: number, position: Vector3, prev_voxel_id: number?)
 | 
			
		||||
local function add_to_voxel(source: jecs.Entity, position: vector, prev_voxel_id: jecs.Entity?)
 | 
			
		||||
	local hash = position // grid
 | 
			
		||||
	local voxel_id = map[hash]
 | 
			
		||||
	if not voxel_id then
 | 
			
		||||
| 
						 | 
				
			
			@ -79,7 +94,7 @@ local function update_camera_direction(dt: number)
 | 
			
		|||
end
 | 
			
		||||
 | 
			
		||||
local function perceive_enemies(dt: number)
 | 
			
		||||
	local it = world:query(Perception, Position, PrimaryPart)
 | 
			
		||||
	local it = world:query(Perception, Position, PrimaryPart):iter()
 | 
			
		||||
	-- There is only going to be one entity matching the query
 | 
			
		||||
	local e, self_perception, self_position, self_primary_part = it()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -93,28 +108,28 @@ local function perceive_enemies(dt: number)
 | 
			
		|||
 | 
			
		||||
		if is_in_fov(self_position, target_position, self_perception.dir, self_perception.fov) then
 | 
			
		||||
			local p = target_position
 | 
			
		||||
			print(`Entity {world:get(e, Name)} can see target {world:get(enemy, Name)} at ({p.X}, {p.Y}, {p.Z})`)
 | 
			
		||||
			print(`Entity {world:get(e, Name)} can see target {world:get(enemy, Name)} at ({p.x}, {p.y}, {p.z})`)
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local player = world:entity()
 | 
			
		||||
world:set(player, Perception, {
 | 
			
		||||
	range = 100,
 | 
			
		||||
	range = 200,
 | 
			
		||||
	fov = 90,
 | 
			
		||||
	dir = Vector3.new(1, 0, 0),
 | 
			
		||||
	dir = vector.create(1, 0, 0),
 | 
			
		||||
})
 | 
			
		||||
world:set(player, Name, "LocalPlayer")
 | 
			
		||||
local primary_part = (local_player.Character :: Model).PrimaryPart :: Part
 | 
			
		||||
local primary_part = local_player.Character.PrimaryPart
 | 
			
		||||
world:set(player, PrimaryPart, primary_part)
 | 
			
		||||
world:set(player, Position, Vector3.zero)
 | 
			
		||||
world:set(player, Position, vector.zero)
 | 
			
		||||
 | 
			
		||||
local enemy = world:entity()
 | 
			
		||||
world:set(enemy, Name, "Enemy $1")
 | 
			
		||||
world:set(enemy, Position, Vector3.new(50, 0, 20))
 | 
			
		||||
world:set(enemy, Position, vector.create(50, 0, 20))
 | 
			
		||||
 | 
			
		||||
add_to_voxel(player, primary_part.Position)
 | 
			
		||||
add_to_voxel(enemy, world)
 | 
			
		||||
add_to_voxel(enemy, assert(world:get(enemy, Position)))
 | 
			
		||||
 | 
			
		||||
local dt = 1 / 60
 | 
			
		||||
reconcile_client_owned_assembly_to_voxel(dt)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue