Cleanup on API plans.

This commit is contained in:
2026-11-04 09:07:20 +00:00
parent cbb77ecf49
commit cd8ecb09c0
+47 -27
View File
@@ -39,22 +39,27 @@ world = ElementWorld.new
# this is a custom component # this is a custom component
world.component :health do |c| world.component :health do |c|
field :health, int: 100 # one of int or float or boolean which use c stuff and generally faster
# also allow for bulk operations to be done in hotloops.
allow_redefine [:health] # can use `any` which allows for any ruby value but uses runtime gc etc and so is worse to use performance wise for each entity.
field :health, :int, 100 # stored for each entity it is attached to.
field :max_health, :int, 100, global: true # stored only once per component
end end
world.spawn do |e| # Name is optional world.spawn do |e| # Name is optional
e.tags = [:enemy] e.tags = [:enemy]
e.z = 0.5
e.position = {x: 100, y: 100} # entities always have a position, even if they don't have a body component, this is useful for rendering and for entities that don't need physics but still need to be positioned in the world e.position = {x: 100, y: 100} # entities always have a position, even if they don't have
# a body component, this is useful for rendering and for entities
# that don't need physics but still need to be positioned in the world
e.add_component :body do |c| e.add_component :body do |c|
c.shape = :rectangle c.shape = :rectangle
c.size = {width: 50, height: 50} c.size = {width: 50, height: 50}
c.angle = 0 c.angle = 0
c.mass = 1 c.mass = 1
c.velocity = {x: 0, y: 0} c.velocity = {x: 0, y: 0}
c.collision = true c.collision_layer = 1
end end
e.add_component :sprite do |c| e.add_component :sprite do |c|
tiles = Tiles.from "assets/sprite.png", tile_width: 50, tile_height: 50 tiles = Tiles.from "assets/sprite.png", tile_width: 50, tile_height: 50
@@ -76,6 +81,7 @@ end
world.spawn "Trap Preview" do |e| world.spawn "Trap Preview" do |e|
e.position = {x: 0, y: 0} e.position = {x: 0, y: 0}
e.z = 0
e.tags = [:trap_preview] e.tags = [:trap_preview]
e.active = false e.active = false
e.add_component :sprite do |c| e.add_component :sprite do |c|
@@ -84,10 +90,12 @@ world.spawn "Trap Preview" do |e|
c.animations = {idle: animation1} c.animations = {idle: animation1}
c.current_animation = :idle c.current_animation = :idle
end end
# e.remove_component :sprite # compoenents can also be removed
end end
# systems are called on the world every update frame, some systems are built into the engine for things like physics, rendering, etc, but you can also create your own custom systems for things like trap placement, enemy spawning, etc # systems are called on the world every update frame, some systems
# are built into the engine for things like physics, rendering, etc,
# but you can also create your own custom systems for things like
# trap placement, enemy spawning, etc
# systems can be created on queries # systems can be created on queries
# the query can be for compoenents or tags or both # the query can be for compoenents or tags or both
@@ -96,24 +104,27 @@ end
# for entities it does similarly # for entities it does similarly
# mouse state can be read in systems always # mouse state can be read in systems always
query = Query.from(tag(:enemy) & component(:health, true) & anti(name("Trap Preview", true))) query = Query.from(tag(:enemy) & component(:health, capture: true) & anti(name("Trap Preview")))
# in the match name will return just the one entity # in the match name will return just the one entity
# tag and componet return list-like tables but they will be parallel and in order # tag and componet return list-like tables but they will be parallel and in order
# empty columns will be filled with nil (not possible if only & is used) # empty columns will be filled with nil (not possible if only & is used)
# the last part is to show how anti() works # the last part is to show how anti() works
# it doesnt actually do anything as tag(:enemy) and component(:health) will never match name("Trap Preview") but it shows how you can use anti() to negate a query # it doesnt actually do anything as tag(:enemy) and component(:health) will
# never match name("Trap Preview") but it shows how you can use anti() to negate a query
world.system :health_regen, for: query do |match, dt| # no trigger means always world.system :health_regen, for: query do |match, dt| # no trigger means always
# example of a simple health regeneration system that regenerates health for all entities with a health component # example of a simple health regeneration system that regenerates health for all entities with a health component
healths = query.get[0] # get the first component in the query, in this case health, this will be a list of all health components for entities that match the query healths = match[0] # get the first component in the query, in this case health
healths.add_scalar! :health, 1 * dt # , this will be a list of all health components for
healths.clamp! :health, 0, 100 # entities that match the query
healths[:health].add! 1 * dt
healths[:health].clamp! 0, :max_health
end end
trigger = [:place] trigger = Trigger.from(key(:place))
world.system :trap_placement, trigger: trigger do |dt| world.system :trap_placement, trigger: trigger do |dt|
return unless world.holding_trap? return unless world.globals.holding_trap?
world.spawn do |e| world.spawn do |e|
e.tags = [:trap] e.tags = [:trap]
@@ -126,39 +137,44 @@ world.system :trap_placement, trigger: trigger do |dt|
c.current_animation = :idle c.current_animation = :idle
end end
e.add_component :body do |c| e.add_component :body do |c|
c.shape = :rectangle
c.size = {width: 50, height: 50} c.size = {width: 50, height: 50}
c.angle = 0 c.angle = 0
c.mass = 1 c.mass = 1
c.velocity = {x: 0, y: 0} c.velocity = {x: 0, y: 0}
c.collision = true c.collision_layer = 0 # layer 0 doesnt collide.
end end
end end
end end
name_query = Query.from(name("Trap Preview", true) & component(:sprite, true)) name_query = Query.from(name("Trap Preview") & component(:sprite, capture: true))
# could be done as so or # could be done as so or
world.system :trap_preview do |match, dt| # example from teh game mist trying to be made with this engine world.system :trap_preview, for: name_query do |dt, matches| # example from the game mist trying to be made with this engine
# do trap placement logic when a trap is held # do trap placement logic when a trap is held
return unless world.holding_trap? return unless world.globals.holding_trap?
# show a preview of the trap at the mouse position, you can use the mouse position relative to the world for this # show a preview of the trap at the mouse position, you can use the mouse position relative to the world for this
e = name_query.get[0] # get the first entity that matches the name query, in this case the trap preview entity, this will be nil if no entity matches e = matches[0] # get the first entity that matches the name query, in
# this case the trap preview entity, this will be nil if no entity matches
e.position = world.mouse_position_world e.position = world.mouse_position_world
e.active = true # enable the trap preview entity to show it at the mouse position e.active = true # enable the trap preview entity to show it at the mouse position
end end
# OR, this one isnt the exact same but shows that .active works on both entites and components # OR, this one isnt the exact same but shows that .active works on both entites and components
world.system :trap_preview, for: name("Trap Preview", true) & component(:sprite, true) do |match, dt| world.system :trap_preview, for: name("Trap Preview", capture: true) & component(:sprite, capture: true) do |dt, matches|
return unless world.holding_trap? return unless world.globals.holding_trap?
# if | was used between teh queries then the arrays would still be parallel but empty rows fit with nil # if | was used between teh queries then the arrays would still be parallel but empty rows fit with nil
# for & between they will always match and so the arrays will always be the same length and so you can just use the first one to set the position of the entity and then use the second one to set the active of the sprite component, etc (without nil check) # for & between they will always match and so the arrays
match.second.each_with_index do |s, i| # will always be the same length and so you can just use
match.first[i].position = world.mouse_position_world # the first one to set the position of the entity and then
# use the second one to set the active of the sprite component,
# etc (without nil check)
matches.second.each_with_index do |s, i|
matches.first[i].position = world.mouse_position_world
s.active = true s.active = true
end end
end end
## ------ ## ## ------ ##
world.shader :fog do |shader| world.shader :fog do |shader|
@@ -174,12 +190,16 @@ world.shader :fog do |shader|
} }
frame do |shader| frame do |shader|
# example of custom shader code for this shader, this is called every frame and is where you would update the shader data based on the current state of the world or whatever # example of custom shader code for this shader, this is called every
# frame and is where you would update the shader data based on the
# current state of the world or whatever
shader.set_data :time, Time.now.to_f shader.set_data :time, Time.now.to_f
shader.set_data :resolution, {x: width, y: height} shader.set_data :resolution, {x: width, y: height}
end end
z = 100 # shaders always appear above normal sprites but z is used to sort them relative to each other, so a shader with z 100 will appear above a shader with z 50, but both will appear above normal sprites z = 100 # shaders always appear above normal sprites but z is used to
# sort them relative to each other, so a shader with z 100 will
# appear above a shader with z 50, but both will appear above normal sprites
end end
App.start menu_scene # first scene to show when the game starts, stops the loading bg App.start menu_scene # first scene to show when the game starts, stops the loading bg