mirror of
https://github.com/cyberarm/cyberarm_engine.git
synced 2025-12-17 13:32:34 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0850336e55 | |||
| 8b6d7b6eb2 | |||
| 3226eb2bda | |||
| a98bb4ec82 | |||
| da5d740c6e | |||
| 4055f645f3 | |||
| 185ab000d6 | |||
| ed061c8408 | |||
| d8551c7428 | |||
| 0017c841dd | |||
| 8cedb40283 | |||
| 93a4e9a2b8 | |||
| a0c0180411 | |||
| 7f25cd49fe | |||
| 7e6a17fe9f | |||
| 0ea1e5c2ff |
36
README.md
36
README.md
@@ -1,8 +1,13 @@
|
||||
# CyberarmEngine
|
||||
|
||||
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/cyberarm_engine`. To experiment with that code, run `bin/console` for an interactive prompt.
|
||||
Yet Another Game Engine On Top Of Gosu
|
||||
|
||||
TODO: Delete this and the text above, and describe your gem
|
||||
## Features
|
||||
* [Shoes-like](http://shoesrb.com) GUI support
|
||||
* OpenGL Shader support (requires [opengl-bindings](https://github.com/vaiorabbit/ruby-opengl) gem)
|
||||
* Includes classes for handling Vectors, Rays, Bounding Boxes, and Transforms
|
||||
* GameState system
|
||||
* Monolithic GameObjects
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -22,7 +27,32 @@ Or install it yourself as:
|
||||
|
||||
## Usage
|
||||
|
||||
TODO: Write usage instructions here
|
||||
```ruby
|
||||
require "cyberarm_engine"
|
||||
|
||||
class Hello < CyberarmEngine::GuiState
|
||||
def setup
|
||||
stack do
|
||||
label "Hello World!"
|
||||
|
||||
button "close" do
|
||||
window.close
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Window < CyberarmEngine::Window
|
||||
def initialize
|
||||
super
|
||||
self.show_cursor = true
|
||||
|
||||
push_state(Hello)
|
||||
end
|
||||
end
|
||||
|
||||
Window.new.show
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
@@ -29,8 +29,9 @@ Gem::Specification.new do |spec|
|
||||
spec.require_paths = ["lib"]
|
||||
|
||||
spec.add_dependency "gosu", "~> 0.15.0"
|
||||
spec.add_dependency "gosu_more_drawables", "~> 0.3"
|
||||
|
||||
spec.add_development_dependency "bundler", "~> 1.16"
|
||||
spec.add_development_dependency "rake", "~> 10.0"
|
||||
spec.add_development_dependency "rake", "~> 13.0"
|
||||
spec.add_development_dependency "minitest", "~> 5.0"
|
||||
end
|
||||
|
||||
@@ -4,15 +4,16 @@ rescue LoadError => e
|
||||
pp e
|
||||
require "gosu"
|
||||
end
|
||||
require "json"
|
||||
require "gosu_more_drawables"
|
||||
|
||||
require_relative "cyberarm_engine/version"
|
||||
require_relative "cyberarm_engine/stats"
|
||||
|
||||
require_relative "cyberarm_engine/common"
|
||||
|
||||
require_relative "cyberarm_engine/gosu_ext/circle"
|
||||
|
||||
require_relative "cyberarm_engine/game_object"
|
||||
require_relative "cyberarm_engine/engine"
|
||||
require_relative "cyberarm_engine/window"
|
||||
|
||||
require_relative "cyberarm_engine/bounding_box"
|
||||
require_relative "cyberarm_engine/vector"
|
||||
@@ -24,6 +25,7 @@ require_relative "cyberarm_engine/animator"
|
||||
|
||||
require_relative "cyberarm_engine/text"
|
||||
require_relative "cyberarm_engine/timer"
|
||||
require_relative "cyberarm_engine/config_file"
|
||||
|
||||
require_relative "cyberarm_engine/ui/dsl"
|
||||
|
||||
@@ -33,15 +35,19 @@ require_relative "cyberarm_engine/ui/style"
|
||||
require_relative "cyberarm_engine/ui/border_canvas"
|
||||
require_relative "cyberarm_engine/ui/element"
|
||||
require_relative "cyberarm_engine/ui/elements/label"
|
||||
require_relative "cyberarm_engine/ui/elements/list_box"
|
||||
require_relative "cyberarm_engine/ui/elements/button"
|
||||
require_relative "cyberarm_engine/ui/elements/toggle_button"
|
||||
require_relative "cyberarm_engine/ui/elements/edit_line"
|
||||
require_relative "cyberarm_engine/ui/elements/edit_box"
|
||||
require_relative "cyberarm_engine/ui/elements/image"
|
||||
require_relative "cyberarm_engine/ui/elements/container"
|
||||
require_relative "cyberarm_engine/ui/elements/flow"
|
||||
require_relative "cyberarm_engine/ui/elements/stack"
|
||||
require_relative "cyberarm_engine/ui/elements/check_box"
|
||||
require_relative "cyberarm_engine/ui/elements/radio"
|
||||
require_relative "cyberarm_engine/ui/elements/progress"
|
||||
require_relative "cyberarm_engine/ui/elements/slider"
|
||||
|
||||
require_relative "cyberarm_engine/game_state"
|
||||
require_relative "cyberarm_engine/ui/gui_state"
|
||||
|
||||
@@ -78,15 +78,15 @@ module CyberarmEngine
|
||||
end
|
||||
|
||||
def get_image(path, retro: false, tileable: false)
|
||||
get_asset(path, Engine::IMAGES, Gosu::Image, retro, tileable)
|
||||
get_asset(path, Window::IMAGES, Gosu::Image, retro, tileable)
|
||||
end
|
||||
|
||||
def get_sample(path)
|
||||
get_asset(path, Engine::SAMPLES, Gosu::Sample)
|
||||
get_asset(path, Window::SAMPLES, Gosu::Sample)
|
||||
end
|
||||
|
||||
def get_song(path)
|
||||
get_asset(path, Engine::SONGS, Gosu::Song)
|
||||
get_asset(path, Window::SONGS, Gosu::Song)
|
||||
end
|
||||
|
||||
def window
|
||||
|
||||
46
lib/cyberarm_engine/config_file.rb
Normal file
46
lib/cyberarm_engine/config_file.rb
Normal file
@@ -0,0 +1,46 @@
|
||||
module CyberarmEngine
|
||||
class ConfigFile
|
||||
def initialize(file:)
|
||||
@file = file
|
||||
|
||||
if File.exist?(@file)
|
||||
deserialize
|
||||
else
|
||||
@data = {}
|
||||
end
|
||||
end
|
||||
|
||||
def []= *keys, value
|
||||
last_key = keys.last
|
||||
|
||||
if keys.size == 1
|
||||
hash = @data
|
||||
else
|
||||
keys.pop
|
||||
hash = @data[keys.shift] ||= {}
|
||||
|
||||
keys.each do |key|
|
||||
hash[key] ||= {}
|
||||
end
|
||||
end
|
||||
|
||||
hash[last_key] = value
|
||||
end
|
||||
|
||||
def get(*keys)
|
||||
@data.dig(*keys)
|
||||
end
|
||||
|
||||
def serialize
|
||||
JSON.dump(@data)
|
||||
end
|
||||
|
||||
def deserialize
|
||||
@data = JSON.parse(File.read(@file), symbolize_names: true)
|
||||
end
|
||||
|
||||
def save!
|
||||
File.open(@file, "w") { |f| f.write(serialize) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3,7 +3,7 @@ module CyberarmEngine
|
||||
include Common
|
||||
|
||||
attr_accessor :options, :global_pause
|
||||
attr_reader :game_objects, :containers
|
||||
attr_reader :game_objects
|
||||
|
||||
def initialize(options={})
|
||||
@options = options
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
module Gosu
|
||||
# Sourced from https://gist.github.com/ippa/662583
|
||||
def self.draw_circle(cx,cy,r, z = 9999,color = Gosu::Color::GREEN, step = 10)
|
||||
0.step(360, step) do |a1|
|
||||
a2 = a1 + step
|
||||
draw_line(cx + Gosu.offset_x(a1, r), cy + Gosu.offset_y(a1, r), color, cx + Gosu.offset_x(a2, r), cy + Gosu.offset_y(a2, r), color, z)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2,13 +2,44 @@ module CyberarmEngine
|
||||
# Ref: https://github.com/vaiorabbit/ruby-opengl/blob/master/sample/OrangeBook/brick.rb
|
||||
class Shader
|
||||
include OpenGL
|
||||
@@shaders = {}
|
||||
PREPROCESSOR_CHARACTER = "@"
|
||||
@@shaders = {} # Cache for {Shader} instances
|
||||
PREPROCESSOR_CHARACTER = "@".freeze # magic character for preprocessor phase of {Shader} compilation
|
||||
|
||||
# add instance of {Shader} to cache
|
||||
#
|
||||
# @param name [String]
|
||||
# @param instance [Shader]
|
||||
def self.add(name, instance)
|
||||
@@shaders[name] = instance
|
||||
end
|
||||
|
||||
# removes {Shader} from cache and cleans up
|
||||
#
|
||||
# @param name [String]
|
||||
def self.delete(name)
|
||||
shader = @@shaders.dig(name)
|
||||
|
||||
if shader
|
||||
@@shaders.delete(name)
|
||||
|
||||
if shader.compiled?
|
||||
glDeleteProgram(shader.program)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# runs _block_ using {Shader} with _name_
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# CyberarmEngine::Shader.use("blur") do |shader|
|
||||
# shader.uniform_float("radius", 20.0)
|
||||
# # OpenGL Code that uses shader
|
||||
# end
|
||||
#
|
||||
# @param name [String] name of {Shader} to use
|
||||
# @return [void]
|
||||
def self.use(name, &block)
|
||||
shader = @@shaders.dig(name)
|
||||
if shader
|
||||
@@ -18,22 +49,37 @@ module CyberarmEngine
|
||||
end
|
||||
end
|
||||
|
||||
# returns whether {Shader} with _name_ is in cache
|
||||
#
|
||||
# @param name [String]
|
||||
# @return [Boolean]
|
||||
def self.available?(name)
|
||||
@@shaders.dig(name).is_a?(Shader)
|
||||
end
|
||||
|
||||
# returns instance of {Shader}, if it exists
|
||||
#
|
||||
# @param name [String]
|
||||
# @return [Shader?]
|
||||
def self.get(name)
|
||||
@@shaders.dig(name)
|
||||
end
|
||||
|
||||
# returns currently active {Shader}, if one is active
|
||||
#
|
||||
# @return [Shader?]
|
||||
def self.active_shader
|
||||
@active_shader
|
||||
end
|
||||
|
||||
# sets currently active {Shader}
|
||||
#
|
||||
# @param instance [Shader] instance of {Shader} to set as active
|
||||
def self.active_shader=(instance)
|
||||
@active_shader = instance
|
||||
end
|
||||
|
||||
# stops using currently active {Shader}
|
||||
def self.stop
|
||||
shader = Shader.active_shader
|
||||
|
||||
@@ -44,11 +90,18 @@ module CyberarmEngine
|
||||
end
|
||||
end
|
||||
|
||||
# returns location of OpenGL Shader uniform
|
||||
#
|
||||
# @param variable [String]
|
||||
def self.attribute_location(variable)
|
||||
raise RuntimeError, "No active shader!" unless Shader.active_shader
|
||||
Shader.active_shader.attribute_location(variable)
|
||||
end
|
||||
|
||||
# sets _variable_ to _value_
|
||||
#
|
||||
# @param variable [String]
|
||||
# @param value
|
||||
def self.set_uniform(variable, value)
|
||||
raise RuntimeError, "No active shader!" unless Shader.active_shader
|
||||
Shader.active_shader.set_uniform(variable, value)
|
||||
@@ -64,7 +117,7 @@ module CyberarmEngine
|
||||
|
||||
@program = nil
|
||||
|
||||
@error_buffer_size = 1024
|
||||
@error_buffer_size = 1024 * 8
|
||||
@variable_missing = {}
|
||||
|
||||
@data = {shaders: {}}
|
||||
@@ -80,20 +133,30 @@ module CyberarmEngine
|
||||
compile_shader(type: :fragment)
|
||||
link_shaders
|
||||
|
||||
@data[:shaders].each { |key, id| glDeleteShader(id) }
|
||||
|
||||
# Only add shader if it successfully compiles
|
||||
if @compiled
|
||||
puts "compiled!"
|
||||
puts "Compiled shader: #{@name}"
|
||||
Shader.add(@name, self)
|
||||
else
|
||||
glDeleteProgram(@program)
|
||||
warn "FAILED to compile shader: #{@name}", ""
|
||||
end
|
||||
end
|
||||
|
||||
# whether vertex and fragment files exist on disk
|
||||
#
|
||||
# @return [Boolean]
|
||||
def shader_files_exist?(vertex:, fragment:)
|
||||
File.exist?(vertex) && File.exist?(fragment)
|
||||
end
|
||||
|
||||
# creates an OpenGL Shader of _type_ using _source_
|
||||
#
|
||||
# @param type [Symbol] valid values are: :vertex, :fragment
|
||||
# @param source [String] source code for shader
|
||||
def create_shader(type:, source:)
|
||||
_shader = nil
|
||||
|
||||
@@ -103,7 +166,7 @@ module CyberarmEngine
|
||||
when :fragment
|
||||
_shader = glCreateShader(GL_FRAGMENT_SHADER)
|
||||
else
|
||||
warn "Unsupported shader type: #{type.inspect}"
|
||||
raise ArgumentError, "Unsupported shader type: #{type.inspect}"
|
||||
end
|
||||
|
||||
processed_source = preprocess_source(source: source)
|
||||
@@ -115,6 +178,23 @@ module CyberarmEngine
|
||||
@data[:shaders][type] =_shader
|
||||
end
|
||||
|
||||
# evaluates shader preprocessors
|
||||
#
|
||||
# currently supported preprocessors:
|
||||
#
|
||||
# @include "file/path" "another/file/path" # => Replace line with contents of file; Shader includes_dir must be specified in constructor
|
||||
#
|
||||
# @example
|
||||
# # Example Vertex Shader #
|
||||
# # #version 330 core
|
||||
# # @include "material_struct"
|
||||
# # void main() {
|
||||
# # gl_Position = vec4(1, 1, 1, 1);
|
||||
# # }
|
||||
#
|
||||
# Shader.new(name: "model_renderer", includes_dir: "path/to/includes", vertex: "path/to/vertex_shader.glsl")
|
||||
#
|
||||
# @param source shader source code
|
||||
def preprocess_source(source:)
|
||||
lines = source.lines
|
||||
|
||||
@@ -141,6 +221,9 @@ module CyberarmEngine
|
||||
lines.join
|
||||
end
|
||||
|
||||
# compile OpenGL Shader of _type_
|
||||
#
|
||||
# @return [Boolean] whether compilation succeeded
|
||||
def compile_shader(type:)
|
||||
_compiled = false
|
||||
_shader = @data[:shaders][type]
|
||||
@@ -166,6 +249,11 @@ module CyberarmEngine
|
||||
return _compiled
|
||||
end
|
||||
|
||||
# link compiled OpenGL Shaders in to a OpenGL Program
|
||||
#
|
||||
# @note linking must succeed or shader cannot be used
|
||||
#
|
||||
# @return [Boolean] whether linking succeeded
|
||||
def link_shaders
|
||||
@program = glCreateProgram
|
||||
@data[:shaders].values.each do |_shader|
|
||||
@@ -187,7 +275,10 @@ module CyberarmEngine
|
||||
@compiled = linked == 0 ? false : true
|
||||
end
|
||||
|
||||
# Returns the location of a uniform variable
|
||||
# Returns the location of a uniform _variable_
|
||||
#
|
||||
# @param variable [String]
|
||||
# @return [Integer] location of uniform
|
||||
def variable(variable)
|
||||
loc = glGetUniformLocation(@program, variable)
|
||||
if (loc == -1)
|
||||
@@ -197,6 +288,7 @@ module CyberarmEngine
|
||||
return loc
|
||||
end
|
||||
|
||||
# @see Shader.use Shader.use
|
||||
def use(&block)
|
||||
return unless compiled?
|
||||
raise "Another shader is already in use! #{Shader.active_shader.name.inspect}" if Shader.active_shader
|
||||
@@ -210,49 +302,93 @@ module CyberarmEngine
|
||||
end
|
||||
end
|
||||
|
||||
# stop using shader, if shader is active
|
||||
def stop
|
||||
Shader.active_shader = nil if Shader.active_shader == self
|
||||
glUseProgram(0)
|
||||
end
|
||||
|
||||
# @return [Boolean] whether {Shader} successfully compiled
|
||||
def compiled?
|
||||
@compiled
|
||||
end
|
||||
|
||||
# returns location of a uniform _variable_
|
||||
#
|
||||
# @note Use {#variable} for friendly error handling
|
||||
# @see #variable Shader#variable
|
||||
#
|
||||
# @param variable [String]
|
||||
# @return [Integer]
|
||||
def attribute_location(variable)
|
||||
glGetUniformLocation(@program, variable)
|
||||
end
|
||||
|
||||
# send {Transform} to {Shader}
|
||||
#
|
||||
# @param variable [String]
|
||||
# @param value [Transform]
|
||||
# @param location [Integer]
|
||||
# @return [void]
|
||||
def uniform_transform(variable, value, location = nil)
|
||||
attr_loc = location ? location : attribute_location(variable)
|
||||
|
||||
glUniformMatrix4fv(attr_loc, 1, GL_FALSE, value.to_gl.pack("F16"))
|
||||
end
|
||||
|
||||
# send Boolean to {Shader}
|
||||
#
|
||||
# @param variable [String]
|
||||
# @param value [Boolean]
|
||||
# @param location [Integer]
|
||||
# @return [void]
|
||||
def uniform_boolean(variable, value, location = nil)
|
||||
attr_loc = location ? location : attribute_location(variable)
|
||||
|
||||
glUniform1i(attr_loc, value ? 1 : 0)
|
||||
end
|
||||
|
||||
# send Integer to {Shader}
|
||||
# @param variable [String]
|
||||
# @param value [Integer]
|
||||
# @param location [Integer]
|
||||
# @return [void]
|
||||
def uniform_integer(variable, value, location = nil)
|
||||
attr_loc = location ? location : attribute_location(variable)
|
||||
|
||||
glUniform1i(attr_loc, value)
|
||||
end
|
||||
|
||||
# send Float to {Shader}
|
||||
#
|
||||
# @param variable [String]
|
||||
# @param value [Float]
|
||||
# @param location [Integer]
|
||||
# @return [void]
|
||||
def uniform_float(variable, value, location = nil)
|
||||
attr_loc = location ? location : attribute_location(variable)
|
||||
|
||||
glUniform1f(attr_loc, value)
|
||||
end
|
||||
|
||||
# send {Vector} (x, y, z) to {Shader}
|
||||
#
|
||||
# @param variable [String]
|
||||
# @param value [Vector]
|
||||
# @param location [Integer]
|
||||
# @return [void]
|
||||
def uniform_vec3(variable, value, location = nil)
|
||||
attr_loc = location ? location : attribute_location(variable)
|
||||
|
||||
glUniform3f(attr_loc, *value.to_a[0..2])
|
||||
end
|
||||
|
||||
# send {Vector} to {Shader}
|
||||
#
|
||||
# @param variable [String]
|
||||
# @param value [Vector]
|
||||
# @param location [Integer]
|
||||
# @return [void]
|
||||
def uniform_vec4(variable, value, location = nil)
|
||||
attr_loc = location ? location : attribute_location(variable)
|
||||
|
||||
|
||||
21
lib/cyberarm_engine/stats.rb
Normal file
21
lib/cyberarm_engine/stats.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
module CyberarmEngine
|
||||
class Stats
|
||||
@@hash = {
|
||||
gui_recalculations_last_frame: 0
|
||||
}
|
||||
|
||||
def self.get(key)
|
||||
@@hash.dig(key)
|
||||
end
|
||||
|
||||
def self.increment(key, n)
|
||||
@@hash[key] += n
|
||||
end
|
||||
|
||||
def self.clear
|
||||
@@hash.each do |key, value|
|
||||
@@hash[key] = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -9,7 +9,7 @@ module CyberarmEngine
|
||||
@text = text.to_s || ""
|
||||
@options = options
|
||||
@size = options[:size] || 18
|
||||
@font = options[:font] || "sans-serif"#Gosu.default_font_name
|
||||
@font = options[:font] || Gosu.default_font_name
|
||||
@x = options[:x] || 0
|
||||
@y = options[:y] || 0
|
||||
@z = options[:z] || 1025
|
||||
@@ -98,12 +98,12 @@ module CyberarmEngine
|
||||
@shadow_color = n
|
||||
end
|
||||
|
||||
def width
|
||||
textobject.text_width(@text)
|
||||
def width(text = @text)
|
||||
textobject.text_width(text)
|
||||
end
|
||||
|
||||
def height
|
||||
@text.lines.count > 0 ? (@text.lines.count) * textobject.height : @textobject.height
|
||||
def height(text = @text)
|
||||
text.lines.count > 0 ? (text.lines.count) * textobject.height : @textobject.height
|
||||
end
|
||||
|
||||
def draw
|
||||
|
||||
@@ -57,6 +57,13 @@ module CyberarmEngine
|
||||
add_element( Element::Progress.new(options, block) )
|
||||
end
|
||||
|
||||
def slider(options = {}, &block)
|
||||
options[:parent] = element_parent
|
||||
options[:theme] = current_theme
|
||||
|
||||
add_element( Element::Slider.new(options, block) )
|
||||
end
|
||||
|
||||
def background(color = Gosu::Color::NONE)
|
||||
element_parent.style.background = color
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@ module CyberarmEngine
|
||||
include Event
|
||||
include Common
|
||||
|
||||
attr_accessor :x, :y, :z, :enabled
|
||||
attr_accessor :x, :y, :z, :enabled, :tip
|
||||
attr_reader :parent, :options, :style, :event_handler, :background_canvas, :border_canvas
|
||||
|
||||
def initialize(options = {}, block = nil)
|
||||
@@ -16,6 +16,7 @@ module CyberarmEngine
|
||||
@focus = false
|
||||
@enabled = true
|
||||
@visible = true
|
||||
@tip = @options[:tip] ? @options[:tip] : ""
|
||||
|
||||
@style = Style.new(options)
|
||||
|
||||
@@ -110,6 +111,8 @@ module CyberarmEngine
|
||||
event(:leave)
|
||||
|
||||
event(:blur)
|
||||
|
||||
event(:changed)
|
||||
end
|
||||
|
||||
def enabled?
|
||||
@@ -152,6 +155,10 @@ module CyberarmEngine
|
||||
def button_up(id)
|
||||
end
|
||||
|
||||
def draggable?(button)
|
||||
false
|
||||
end
|
||||
|
||||
def render
|
||||
end
|
||||
|
||||
@@ -212,7 +219,7 @@ module CyberarmEngine
|
||||
raise "dimension must be either :width or :height" unless dimension == :width || dimension == :height
|
||||
if size && size.is_a?(Numeric)
|
||||
if size.between?(0.0, 1.0)
|
||||
((@parent.send(:"content_#{dimension}") - self.send(:"noncontent_#{dimension}") - 1) * size).round
|
||||
((@parent.send(:"content_#{dimension}") - self.send(:"noncontent_#{dimension}")) * size).round
|
||||
else
|
||||
size
|
||||
end
|
||||
@@ -239,6 +246,8 @@ module CyberarmEngine
|
||||
end
|
||||
|
||||
def root
|
||||
return self if is_root?
|
||||
|
||||
unless @root && @root.parent.nil?
|
||||
@root = parent
|
||||
|
||||
@@ -272,5 +281,9 @@ module CyberarmEngine
|
||||
def value=(value)
|
||||
raise "#{self.class}#value= was not overridden!"
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{self.class} x=#{x} y=#{y} width=#{width} height=#{height} value=#{ value.is_a?(String) ? "\"#{value}\"" : value }"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2,13 +2,32 @@ module CyberarmEngine
|
||||
class Element
|
||||
class CheckBox < Flow
|
||||
def initialize(text, options, block = nil)
|
||||
super({}, block = nil)
|
||||
super(options, block)
|
||||
options[:toggled] = options[:checked]
|
||||
|
||||
@toggle_button = ToggleButton.new(options)
|
||||
@label = Label.new(text, options)
|
||||
|
||||
define_label_singletons
|
||||
@label.subscribe(:holding_left_mouse_button) do |sender, x, y|
|
||||
@toggle_button.left_mouse_button(sender, x, y)
|
||||
end
|
||||
|
||||
@label.subscribe(:released_left_mouse_button) do |sender, x, y|
|
||||
@toggle_button.released_left_mouse_button(sender, x, y)
|
||||
end
|
||||
|
||||
@label.subscribe(:clicked_left_mouse_button) do |sender, x, y|
|
||||
@toggle_button.clicked_left_mouse_button(sender, x, y)
|
||||
publish(:changed, @toggle_button.value)
|
||||
end
|
||||
|
||||
@label.subscribe(:enter) do |sender|
|
||||
@toggle_button.enter(sender)
|
||||
end
|
||||
|
||||
@label.subscribe(:leave) do |sender|
|
||||
@toggle_button.leave(sender)
|
||||
end
|
||||
|
||||
add(@toggle_button)
|
||||
add(@label)
|
||||
@@ -24,35 +43,8 @@ module CyberarmEngine
|
||||
end
|
||||
|
||||
def value=(bool)
|
||||
@toggle_button.vlaue = bool
|
||||
end
|
||||
|
||||
def define_label_singletons
|
||||
@label.define_singleton_method(:_toggle_button) do |button|
|
||||
@_toggle_button = button
|
||||
end
|
||||
|
||||
@label._toggle_button(@toggle_button)
|
||||
|
||||
@label.define_singleton_method(:holding_left_mouse_button) do |sender, x, y|
|
||||
@_toggle_button.left_mouse_button(sender, x, y)
|
||||
end
|
||||
|
||||
@label.define_singleton_method(:released_left_mouse_button) do |sender, x, y|
|
||||
@_toggle_button.released_left_mouse_button(sender, x, y)
|
||||
end
|
||||
|
||||
@label.define_singleton_method(:clicked_left_mouse_button) do |sender, x, y|
|
||||
@_toggle_button.clicked_left_mouse_button(sender, x, y)
|
||||
end
|
||||
|
||||
@label.define_singleton_method(:enter) do |sender|
|
||||
@_toggle_button.enter(sender)
|
||||
end
|
||||
|
||||
@label.define_singleton_method(:leave) do |sender|
|
||||
@_toggle_button.leave(sender)
|
||||
end
|
||||
@toggle_button.value = bool
|
||||
publish(:changed, @toggle_button.value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,13 +22,13 @@ module CyberarmEngine
|
||||
def build
|
||||
@block.call(self) if @block
|
||||
|
||||
recalculate
|
||||
root.gui_state.request_recalculate
|
||||
end
|
||||
|
||||
def add(element)
|
||||
@children << element
|
||||
|
||||
recalculate
|
||||
root.gui_state.request_recalculate
|
||||
end
|
||||
|
||||
def clear(&block)
|
||||
@@ -41,7 +41,6 @@ module CyberarmEngine
|
||||
|
||||
$__current_container__ = old_container
|
||||
|
||||
recalculate
|
||||
root.gui_state.request_recalculate
|
||||
end
|
||||
|
||||
@@ -57,6 +56,8 @@ module CyberarmEngine
|
||||
|
||||
def hit_element?(x, y)
|
||||
@children.reverse_each do |child|
|
||||
next unless child.visible?
|
||||
|
||||
case child
|
||||
when Container
|
||||
if element = child.hit_element?(x, y)
|
||||
@@ -73,6 +74,9 @@ module CyberarmEngine
|
||||
def recalculate
|
||||
@current_position = Vector.new(@style.margin_left + @style.padding_left, @style.margin_top + @style.padding_top)
|
||||
return unless visible?
|
||||
|
||||
Stats.increment(:gui_recalculations_last_frame, 1)
|
||||
|
||||
stylize
|
||||
|
||||
layout
|
||||
@@ -90,15 +94,16 @@ module CyberarmEngine
|
||||
@height = _height ? _height : (@children.map {|c| c.y + c.outer_height}.max || 0).round
|
||||
end
|
||||
|
||||
|
||||
# Move child to parent after positioning
|
||||
@children.each do |child|
|
||||
child.x += @x
|
||||
child.y += @y
|
||||
child.x += (@x + @style.border_thickness_left) - style.margin_left
|
||||
child.y += (@y + @style.border_thickness_top) - style.margin_top
|
||||
|
||||
child.stylize
|
||||
child.recalculate
|
||||
child.reposition # TODO: Implement top,bottom,left,center, and right positioning
|
||||
|
||||
Stats.increment(:gui_recalculations_last_frame, 1)
|
||||
end
|
||||
|
||||
update_background
|
||||
@@ -109,10 +114,12 @@ module CyberarmEngine
|
||||
end
|
||||
|
||||
def max_width
|
||||
@max_width ? @max_width : window.width - (@parent ? @parent.style.margin_right + @style.margin_right : @style.margin_right)
|
||||
_width = dimensional_size(@style.width, :width)
|
||||
_width ? outer_width : window.width - (@parent ? @parent.style.margin_right + @style.margin_right : @style.margin_right)
|
||||
end
|
||||
|
||||
def fits_on_line?(element) # Flow
|
||||
p [@options[:id], @width] if @options[:id]
|
||||
@current_position.x + element.outer_width <= max_width &&
|
||||
@current_position.x + element.outer_width <= window.width
|
||||
end
|
||||
@@ -171,6 +178,25 @@ module CyberarmEngine
|
||||
def value
|
||||
@children.map {|c| c.class}.join(", ")
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{self.class} x=#{x} y=#{y} width=#{width} height=#{height} children=#{@children.size}"
|
||||
end
|
||||
|
||||
def write_tree(indent = "", index = 0)
|
||||
puts self
|
||||
|
||||
indent = indent + " "
|
||||
@children.each_with_index do |child, i|
|
||||
print "#{indent}#{i}: "
|
||||
|
||||
if child.is_a?(Container)
|
||||
child.write_tree(indent)
|
||||
else
|
||||
puts child
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
6
lib/cyberarm_engine/ui/elements/edit_box.rb
Normal file
6
lib/cyberarm_engine/ui/elements/edit_box.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
module CyberarmEngine
|
||||
class Element
|
||||
class EditBox < Element
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -100,6 +100,22 @@ module CyberarmEngine
|
||||
end
|
||||
end
|
||||
|
||||
def caret_position
|
||||
text_input_position_for(:caret_pos)
|
||||
end
|
||||
|
||||
def selection_start_position
|
||||
text_input_position_for(:selection_start)
|
||||
end
|
||||
|
||||
def text_input_position_for(method)
|
||||
if @type == :password
|
||||
@text.x + @text.width(default(:password_character) * @text_input.text[0..@text_input.send(method)].length)
|
||||
else
|
||||
@text.x + @text.width(@text_input.text[0..@text_input.send(method)])
|
||||
end
|
||||
end
|
||||
|
||||
def left_mouse_button(sender, x, y)
|
||||
super
|
||||
window.text_input = @text_input
|
||||
@@ -141,22 +157,6 @@ module CyberarmEngine
|
||||
return :handled
|
||||
end
|
||||
|
||||
def caret_position
|
||||
text_input_position_for(:caret_pos)
|
||||
end
|
||||
|
||||
def selection_start_position
|
||||
text_input_position_for(:selection_start)
|
||||
end
|
||||
|
||||
def text_input_position_for(method)
|
||||
if @type == :password
|
||||
@text.x + @text.textobject.text_width(default(:password_character) * @text_input.text[0..@text_input.send(method)].length)
|
||||
else
|
||||
@text.x + @text.textobject.text_width(@text_input.text[0..@text_input.send(method)])
|
||||
end
|
||||
end
|
||||
|
||||
def recalculate
|
||||
super
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
module CyberarmEngine
|
||||
class Element
|
||||
class Flow < Container
|
||||
include Common
|
||||
|
||||
def layout
|
||||
@children.each do |child|
|
||||
if fits_on_line?(child)
|
||||
|
||||
@@ -14,7 +14,7 @@ module CyberarmEngine
|
||||
def clicked_left_mouse_button(sender, x, y)
|
||||
@block.call(self) if @block
|
||||
|
||||
return :handled
|
||||
# return :handled
|
||||
end
|
||||
|
||||
def recalculate
|
||||
@@ -44,6 +44,8 @@ module CyberarmEngine
|
||||
recalculate
|
||||
|
||||
root.gui_state.request_recalculate if old_width != width || old_height != height
|
||||
|
||||
publish(:changed, self.value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
6
lib/cyberarm_engine/ui/elements/list_box.rb
Normal file
6
lib/cyberarm_engine/ui/elements/list_box.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
module CyberarmEngine
|
||||
class Element
|
||||
class ListBox < Element
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -43,6 +43,7 @@ module CyberarmEngine
|
||||
@fraction = decimal.clamp(0.0, 1.0)
|
||||
update_background
|
||||
|
||||
publish(:changed, @fraction)
|
||||
return @fraction
|
||||
end
|
||||
end
|
||||
|
||||
6
lib/cyberarm_engine/ui/elements/radio.rb
Normal file
6
lib/cyberarm_engine/ui/elements/radio.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
module CyberarmEngine
|
||||
class Element
|
||||
class Radio < Element
|
||||
end
|
||||
end
|
||||
end
|
||||
107
lib/cyberarm_engine/ui/elements/slider.rb
Normal file
107
lib/cyberarm_engine/ui/elements/slider.rb
Normal file
@@ -0,0 +1,107 @@
|
||||
module CyberarmEngine
|
||||
class Element
|
||||
class Slider < Container
|
||||
class Handle < Button
|
||||
def initialize(*args)
|
||||
super(*args)
|
||||
|
||||
event(:begin_drag)
|
||||
event(:drag_update)
|
||||
event(:end_drag)
|
||||
|
||||
subscribe :begin_drag do |sender, x, y, button|
|
||||
@drag_start_pos = Vector.new(x, y)
|
||||
|
||||
:handled
|
||||
end
|
||||
|
||||
subscribe :drag_update do |sender, x, y, button|
|
||||
@parent.handle_dragged_to(x, y)
|
||||
|
||||
:handled
|
||||
end
|
||||
|
||||
subscribe :end_drag do
|
||||
@drag_start_pos = nil
|
||||
|
||||
:handled
|
||||
end
|
||||
end
|
||||
|
||||
def draggable?(button)
|
||||
button == :left
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :range, :step_size
|
||||
def initialize(options = {}, block = nil)
|
||||
super(options, block)
|
||||
|
||||
@range = @options[:range] ? @options[:range] : 0.0..1.0
|
||||
@step_size = @options[:step] ? @options[:step] : 0.1
|
||||
@value = @options[:value] ? @options[:value] : (@range.first + @range.last) / 2
|
||||
|
||||
@handle = Handle.new("", parent: self, width: 8, height: 1.0) { close }
|
||||
self.add(@handle)
|
||||
end
|
||||
|
||||
def recalculate
|
||||
_width = dimensional_size(@style.width, :width)
|
||||
_height= dimensional_size(@style.height,:height)
|
||||
|
||||
@width = _width
|
||||
@height = _height
|
||||
|
||||
position_handle
|
||||
@handle.recalculate
|
||||
@handle.update_background
|
||||
|
||||
update_background
|
||||
end
|
||||
|
||||
def position_handle
|
||||
@handle.x = @x + @style.padding_left + @style.border_thickness_left +
|
||||
((content_width - @handle.outer_width) * (@value - @range.min) / (@range.max - @range.min).to_f)
|
||||
|
||||
@handle.y = @y + @style.border_thickness_top + @style.padding_top
|
||||
end
|
||||
|
||||
def draw
|
||||
super
|
||||
|
||||
@handle.draw
|
||||
end
|
||||
|
||||
def update
|
||||
super
|
||||
|
||||
@tip = value.to_s
|
||||
@handle.tip = @tip
|
||||
end
|
||||
|
||||
def holding_left_mouse_button(sender, x, y)
|
||||
handle_dragged_to(x, y)
|
||||
|
||||
:handled
|
||||
end
|
||||
|
||||
def handle_dragged_to(x, y)
|
||||
@ratio = ((x - @handle.width / 2) - @x) / (content_width - @handle.outer_width)
|
||||
|
||||
self.value = @ratio.clamp(0.0, 1.0) * (@range.max - @range.min) + @range.min
|
||||
end
|
||||
|
||||
def value
|
||||
@value
|
||||
end
|
||||
|
||||
def value=(n)
|
||||
@value = n
|
||||
position_handle
|
||||
@handle.recalculate
|
||||
|
||||
publish(:changed, @value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,8 +1,6 @@
|
||||
module CyberarmEngine
|
||||
class Element
|
||||
class Stack < Container
|
||||
include Common
|
||||
|
||||
def layout
|
||||
@children.each do |child|
|
||||
move_to_next_line(child)
|
||||
|
||||
@@ -5,8 +5,8 @@ module CyberarmEngine
|
||||
|
||||
def initialize(options, block = nil)
|
||||
super(options[:checkmark], options, block)
|
||||
@toggled = options[:toggled] || false
|
||||
if @toggled
|
||||
@value = options[:checked] || false
|
||||
if @value
|
||||
@text.text = @options[:checkmark]
|
||||
else
|
||||
@text.text = ""
|
||||
@@ -15,41 +15,42 @@ module CyberarmEngine
|
||||
return self
|
||||
end
|
||||
|
||||
def toggled=(boolean)
|
||||
@toggled = !boolean
|
||||
toggle
|
||||
end
|
||||
|
||||
def clicked_left_mouse_button(sender, x, y)
|
||||
toggle
|
||||
self.value = !@value
|
||||
|
||||
@block.call(self) if @block
|
||||
|
||||
return :handled
|
||||
end
|
||||
|
||||
def toggle
|
||||
if @toggled
|
||||
@toggled = false
|
||||
@text.text = ""
|
||||
else
|
||||
@toggled = true
|
||||
@text.text = @options[:checkmark]
|
||||
end
|
||||
end
|
||||
|
||||
def recalculate
|
||||
super
|
||||
|
||||
_width = dimensional_size(@style.width, :width)
|
||||
_height= dimensional_size(@style.height,:height)
|
||||
|
||||
@width = _width ? _width : @text.textobject.text_width(@options[:checkmark])
|
||||
@height = _height ? _height : @text.height
|
||||
|
||||
update_background
|
||||
end
|
||||
|
||||
def value
|
||||
@toggled
|
||||
@value
|
||||
end
|
||||
|
||||
def value=(boolean)
|
||||
@value = boolean
|
||||
|
||||
if boolean
|
||||
@text.text = @options[:checkmark]
|
||||
else
|
||||
@text.text = ""
|
||||
end
|
||||
|
||||
recalculate
|
||||
|
||||
publish(:changed, @value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,6 +22,11 @@ module CyberarmEngine
|
||||
@mouse_down_on = {}
|
||||
@mouse_down_position = {}
|
||||
@pending_recalculate_request = false
|
||||
|
||||
@tip = CyberarmEngine::Text.new("", size: 22, z: Float::INFINITY)
|
||||
@menu = nil
|
||||
@min_drag_distance = 0
|
||||
@mouse_pos = Vector.new
|
||||
end
|
||||
|
||||
# throws :blur event to focused element and sets GuiState focused element
|
||||
@@ -35,8 +40,18 @@ module CyberarmEngine
|
||||
@focus
|
||||
end
|
||||
|
||||
def draw
|
||||
super
|
||||
|
||||
if @tip.text.length > 0
|
||||
Gosu.draw_rect(@tip.x - 2, @tip.y - 2, @tip.width + 4, @tip.height + 4, 0xff020202, Float::INFINITY)
|
||||
@tip.draw
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
if @pending_recalculate_request
|
||||
@root_container.recalculate
|
||||
@root_container.recalculate
|
||||
@pending_recalculate_request = false
|
||||
end
|
||||
@@ -56,12 +71,30 @@ module CyberarmEngine
|
||||
redirect_holding_mouse_button(:middle) if @mouse_over && Gosu.button_down?(Gosu::MsMiddle)
|
||||
redirect_holding_mouse_button(:right) if @mouse_over && Gosu.button_down?(Gosu::MsRight)
|
||||
|
||||
if Vector.new(window.mouse_x, window.mouse_y) == @last_mouse_pos
|
||||
if @mouse_over && (Gosu.milliseconds - @mouse_moved_at) > tool_tip_delay
|
||||
@tip.text = @mouse_over.tip if @mouse_over
|
||||
@tip.x, @tip.y = window.mouse_x - @tip.width / 2, window.mouse_y - @tip.height - 4
|
||||
else
|
||||
@tip.text = ""
|
||||
end
|
||||
else
|
||||
@mouse_moved_at = Gosu.milliseconds
|
||||
end
|
||||
|
||||
@last_mouse_pos = Vector.new(window.mouse_x, window.mouse_y)
|
||||
@mouse_pos = @last_mouse_pos.clone
|
||||
|
||||
request_recalculate if @active_width != window.width || @active_height != window.height
|
||||
|
||||
@active_width = window.width
|
||||
@active_height = window.height
|
||||
end
|
||||
|
||||
def tool_tip_delay
|
||||
500 # ms
|
||||
end
|
||||
|
||||
def button_down(id)
|
||||
super
|
||||
|
||||
@@ -72,6 +105,8 @@ module CyberarmEngine
|
||||
redirect_mouse_button(:middle)
|
||||
when Gosu::MsRight
|
||||
redirect_mouse_button(:right)
|
||||
when Gosu::KbF5
|
||||
request_recalculate
|
||||
end
|
||||
end
|
||||
|
||||
@@ -115,12 +150,26 @@ module CyberarmEngine
|
||||
@mouse_over.publish(:"clicked_#{button}_mouse_button", window.mouse_x, window.mouse_y) if @mouse_over == @mouse_down_on[button]
|
||||
end
|
||||
|
||||
if @dragging_element
|
||||
@dragging_element.publish(:end_drag, window.mouse_x, window.mouse_y, button)
|
||||
@dragging_element = nil
|
||||
end
|
||||
|
||||
@mouse_down_position[button] = nil
|
||||
@mouse_down_on[button] = nil
|
||||
end
|
||||
|
||||
def redirect_holding_mouse_button(button)
|
||||
@mouse_over.publish(:"holding_#{button}_mouse_button", window.mouse_x, window.mouse_y) if @mouse_over
|
||||
if !@dragging_element && @mouse_down_on[button] && @mouse_down_on[button].draggable?(button) && @mouse_pos.distance(@mouse_down_position[button]) > @min_drag_distance
|
||||
@dragging_element = @mouse_down_on[button]
|
||||
@dragging_element.publish(:"begin_drag", window.mouse_x, window.mouse_y, button)
|
||||
end
|
||||
|
||||
if @dragging_element
|
||||
@dragging_element.publish(:"drag_update", window.mouse_x, window.mouse_y, button) if @dragging_element
|
||||
else
|
||||
@mouse_over.publish(:"holding_#{button}_mouse_button", window.mouse_x, window.mouse_y) if @mouse_over
|
||||
end
|
||||
end
|
||||
|
||||
def redirect_mouse_wheel(button)
|
||||
|
||||
@@ -88,7 +88,7 @@ module CyberarmEngine
|
||||
caret_width: 2,
|
||||
caret_color: Gosu::Color::WHITE,
|
||||
caret_interval: 500,
|
||||
selection_color: Gosu::Color::GREEN,
|
||||
selection_color: Gosu::Color.rgba(255, 128, 50, 200),
|
||||
},
|
||||
|
||||
Image: { # < Element
|
||||
@@ -114,6 +114,15 @@ module CyberarmEngine
|
||||
fraction_background: [0xffc75e61, 0xffe26623],
|
||||
border_thickness: 1,
|
||||
border_color: [0xffd59674, 0xffff8746]
|
||||
},
|
||||
|
||||
Slider: { # < Element
|
||||
width: 250,
|
||||
height: 36,
|
||||
background: 0xff111111,
|
||||
fraction_background: [0xffc75e61, 0xffe26623],
|
||||
border_thickness: 1,
|
||||
border_color: [0xffd59674, 0xffff8746]
|
||||
}
|
||||
}.freeze
|
||||
end
|
||||
|
||||
@@ -1,25 +1,61 @@
|
||||
module CyberarmEngine
|
||||
class Vector
|
||||
##
|
||||
# Creates a up vector
|
||||
#
|
||||
# Vector.new(0, 1, 0)
|
||||
#
|
||||
# @return [CyberarmEngine::Vector]
|
||||
def self.up
|
||||
Vector.new(0, 1, 0)
|
||||
end
|
||||
|
||||
##
|
||||
# Creates a down vector
|
||||
#
|
||||
# Vector.new(0, -1, 0)
|
||||
#
|
||||
# @return [CyberarmEngine::Vector]
|
||||
def self.down
|
||||
Vector.new(0, -1, 0)
|
||||
end
|
||||
|
||||
##
|
||||
# Creates a left vector
|
||||
#
|
||||
# Vector.new(-1, 0, 0)
|
||||
#
|
||||
# @return [CyberarmEngine::Vector]
|
||||
def self.left
|
||||
Vector.new(-1, 0, 0)
|
||||
end
|
||||
|
||||
##
|
||||
# Creates a right vector
|
||||
#
|
||||
# Vector.new(1, 0, 0)
|
||||
#
|
||||
# @return [CyberarmEngine::Vector]
|
||||
def self.right
|
||||
Vector.new(1, 0, 0)
|
||||
end
|
||||
|
||||
##
|
||||
# Creates a forward vector
|
||||
#
|
||||
# Vector.new(0, 0, 1)
|
||||
#
|
||||
# @return [CyberarmEngine::Vector]
|
||||
def self.forward
|
||||
Vector.new(0, 0, 1)
|
||||
end
|
||||
|
||||
##
|
||||
# Creates a backward vector
|
||||
#
|
||||
# Vector.new(0, 0, -1)
|
||||
#
|
||||
# @return [CyberarmEngine::Vector]
|
||||
def self.backward
|
||||
Vector.new(0, 0, -1)
|
||||
end
|
||||
@@ -43,25 +79,30 @@ module CyberarmEngine
|
||||
alias w weight
|
||||
alias w= weight=
|
||||
|
||||
# @return [Boolean]
|
||||
def ==(other)
|
||||
if other.is_a?(Numeric)
|
||||
@x == other &&
|
||||
@y == other &&
|
||||
@z == other &&
|
||||
@weight == other
|
||||
else
|
||||
elsif other.is_a?(Vector)
|
||||
@x == other.x &&
|
||||
@y == other.y &&
|
||||
@z == other.z &&
|
||||
@weight == other.weight
|
||||
else
|
||||
other == self
|
||||
end
|
||||
end
|
||||
|
||||
# Create a new vector using {x} and {y} values
|
||||
# @return [CyberarmEngine::Vector]
|
||||
def xy
|
||||
Vector.new(@x, @y)
|
||||
end
|
||||
|
||||
# Performs math operation, excluding @weight
|
||||
# Performs math operation, excluding {weight}
|
||||
private def operator(function, other)
|
||||
if other.is_a?(Numeric)
|
||||
Vector.new(
|
||||
@@ -78,22 +119,26 @@ module CyberarmEngine
|
||||
end
|
||||
end
|
||||
|
||||
# Adds Vector and Numberic or Vector and Vector, excluding @weight
|
||||
# Adds Vector and Numeric or Vector and Vector, excluding {weight}
|
||||
# @return [CyberarmEngine::Vector]
|
||||
def +(other)
|
||||
operator("+", other)
|
||||
end
|
||||
|
||||
# Subtracts Vector and Numberic or Vector and Vector, excluding @weight
|
||||
# Subtracts Vector and Numeric or Vector and Vector, excluding {weight}
|
||||
# @return [CyberarmEngine::Vector]
|
||||
def -(other)
|
||||
operator("-", other)
|
||||
end
|
||||
|
||||
# Multiplies Vector and Numberic or Vector and Vector, excluding @weight
|
||||
# Multiplies Vector and Numeric or Vector and Vector, excluding {weight}
|
||||
# @return [CyberarmEngine::Vector]
|
||||
def *(other)
|
||||
operator("*", other)
|
||||
end
|
||||
|
||||
# Divides Vector and Numberic or Vector and Vector, excluding @weight
|
||||
# Divides Vector and Numeric or Vector and Vector, excluding {weight}
|
||||
# @return [CyberarmEngine::Vector]
|
||||
def /(other)
|
||||
# Duplicated to protect from DivideByZero
|
||||
if other.is_a?(Numeric)
|
||||
@@ -111,6 +156,8 @@ module CyberarmEngine
|
||||
end
|
||||
end
|
||||
|
||||
# dot product of {Vector}
|
||||
# @return [Integer|Float]
|
||||
def dot(other)
|
||||
product = 0
|
||||
|
||||
@@ -124,6 +171,8 @@ module CyberarmEngine
|
||||
return product
|
||||
end
|
||||
|
||||
# cross product of {Vector}
|
||||
# @return [CyberarmEngine::Vector]
|
||||
def cross(other)
|
||||
a = self.to_a
|
||||
b = other.to_a
|
||||
@@ -136,24 +185,40 @@ module CyberarmEngine
|
||||
end
|
||||
|
||||
# returns degrees
|
||||
# @return [Float]
|
||||
def angle(other)
|
||||
Math.acos( self.normalized.dot(other.normalized) ) * 180 / Math::PI
|
||||
end
|
||||
|
||||
# returns magnitude of Vector, ignoring #weight
|
||||
# @return [Float]
|
||||
def magnitude
|
||||
Math.sqrt((@x * @x) + (@y * @y) + (@z * @z))
|
||||
end
|
||||
|
||||
##
|
||||
# returns normalized {Vector}
|
||||
#
|
||||
# @example
|
||||
# CyberarmEngine::Vector.new(50, 21.2, 45).normalized
|
||||
# # => <CyberarmEngine::Vector:0x001 @x=0.7089... @y=0.3005... @z=0.6380... @weight=0>
|
||||
#
|
||||
# @return [CyberarmEngine::Vector]
|
||||
def normalized
|
||||
mag = magnitude
|
||||
self / Vector.new(mag, mag, mag)
|
||||
end
|
||||
|
||||
|
||||
# returns a direction {Vector}
|
||||
#
|
||||
# z is pitch
|
||||
#
|
||||
# y is yaw
|
||||
#
|
||||
# x is roll
|
||||
# @return [CyberarmEngine::Vector]
|
||||
def direction
|
||||
# z is pitch
|
||||
# y is yaw
|
||||
# x is roll
|
||||
_x = -Math.sin(@y.degrees_to_radians) * Math.cos(@z.degrees_to_radians)
|
||||
_y = Math.sin(@z.degrees_to_radians)
|
||||
_z = Math.cos(@y.degrees_to_radians) * Math.cos(@z.degrees_to_radians)
|
||||
@@ -161,41 +226,63 @@ module CyberarmEngine
|
||||
Vector.new(_x, _y, _z)
|
||||
end
|
||||
|
||||
# returns an inverse {Vector}
|
||||
# @return [CyberarmEngine::Vector]
|
||||
def inverse
|
||||
Vector.new(1.0 / @x, 1.0 / @y, 1.0 / @z)
|
||||
end
|
||||
|
||||
# Adds up values of {x}, {y}, and {z}
|
||||
# @return [Integer|Float]
|
||||
def sum
|
||||
@x + @y + @z
|
||||
end
|
||||
|
||||
##
|
||||
# Linear interpolation: smoothly transition between two {Vector}
|
||||
#
|
||||
# CyberarmEngine::Vector.new(100, 100, 100).lerp( CyberarmEngine::Vector.new(0, 0, 0), 0.75 )
|
||||
# # => <CyberarmEngine::Vector:0x0001 @x=75.0, @y=75.0, @z=75.0, @weight=0>
|
||||
#
|
||||
# @param other [CyberarmEngine::Vector | Integer | Float] value to subtract from
|
||||
# @param factor [Float] how complete transition to _other_ is, in range [0.0..1.0]
|
||||
# @return [CyberarmEngine::Vector]
|
||||
def lerp(other, factor)
|
||||
(self - other) * factor.clamp(0.0, 1.0)
|
||||
end
|
||||
|
||||
# 2D distance using X and Y
|
||||
# @return [Float]
|
||||
def distance(other)
|
||||
Math.sqrt((@x-other.x)**2 + (@y-other.y)**2)
|
||||
end
|
||||
|
||||
# 2D distance using X and Z
|
||||
# @return [Float]
|
||||
def gl_distance2d(other)
|
||||
Math.sqrt((@x-other.x)**2 + (@z-other.z)**2)
|
||||
end
|
||||
|
||||
# 3D distance using X, Y, and Z
|
||||
# @return [Float]
|
||||
def distance3d(other)
|
||||
Math.sqrt((@x-other.x)**2 + (@y-other.y)**2 + (@z-other.z)**2)
|
||||
end
|
||||
|
||||
# Converts {Vector} to Array
|
||||
# @return [Array]
|
||||
def to_a
|
||||
[@x, @y, @z, @weight]
|
||||
end
|
||||
|
||||
# Converts {Vector} to String
|
||||
# @return [String]
|
||||
def to_s
|
||||
"X: #{@x}, Y: #{@y}, Z: #{@z}, Weight: #{@weight}"
|
||||
end
|
||||
|
||||
# Converts {Vector} to Hash
|
||||
# @return [Hash]
|
||||
def to_h
|
||||
{x: @x, y: @y, z: @z, weight: @weight}
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module CyberarmEngine
|
||||
NAME = "InDev"
|
||||
VERSION = "0.13.0"
|
||||
VERSION = "0.14.0"
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module CyberarmEngine
|
||||
class Engine < Gosu::Window
|
||||
class Window < Gosu::Window
|
||||
IMAGES = {}
|
||||
SAMPLES= {}
|
||||
SONGS = {}
|
||||
@@ -34,6 +34,8 @@ module CyberarmEngine
|
||||
end
|
||||
|
||||
def update
|
||||
Stats.clear
|
||||
|
||||
current_state.update if current_state
|
||||
@last_frame_time = Gosu.milliseconds-@current_frame_time
|
||||
@current_frame_time = Gosu.milliseconds
|
||||
Reference in New Issue
Block a user