Skip to content

Instantly share code, notes, and snippets.

@quietsamurai98
Created September 23, 2020 05:09
Show Gist options
  • Save quietsamurai98/c1a10ff9d9ddbdd2d6fc5baed6225489 to your computer and use it in GitHub Desktop.
Save quietsamurai98/c1a10ff9d9ddbdd2d6fc5baed6225489 to your computer and use it in GitHub Desktop.
DragonRuby Boids
# l to toggle lines
# space to reset
# left click to attract
# right click to repel
$TIME_FACTOR = 1.0
$X_WRAP = true
$Y_WRAP = true
# @param [GTK::Args] args
def tick(args)
#noinspection RubyResolve
if args.inputs.keyboard.key_down.space || !args.state.boids
args.state.boids = (1..40).map { |_| Boid.new }
args.outputs.static_sprites.clear
args.outputs.static_sprites << args.state.boids
args.state.show_lines ||= false
end
if args.inputs.keyboard.key_down.l
args.state.show_lines = !args.state.show_lines
end
#@type [Array<Boid>]
boids = args.state.boids
update_neighbors(boids)
boids.each do |b|
b.separate
b.cohere
b.align
b.repel
end
boids.each do |b|
b.tick
end
if args.state.show_lines
args.outputs.lines << lines(boids)
end
# args.outputs.primitives << boids
args.outputs.labels << {x: 10, y: 30, text: "FPS: #{$gtk.current_framerate.to_s.to_i}", r: 255, g: 0, b: 0}
args.outputs.background_color = [0,0,0]
end
def lines(boids)
lines = []
line_factor = 1
# edges = {}
# boids.each do |a|
# edges[a] = {}
# end
boids.each do |a|
a.neighbors.each do |d|
b = a
if b != d # && !edges[b][d] && !edges[d][b]
#edges[b][d] = true
#edges[d][b] = true
dx, dy = *(b.offset(d))
if dx == d.x - b.x || dy == d.y - b.y
if dx * dx + dy * dy < (b.coh_dist * line_factor) ** 2
[1280, 0].product([720, 0]).each do |xy|
lines << {
x: d.x + 0.5 * b.w - xy[0],
y: d.y + 0.5 * b.h - xy[1],
x2: d.x + dx + 0.5 * b.w - xy[0],
y2: d.y + dy + 0.5 * b.h - xy[1],
r: 255,
g: 255,
b: 255,
a: (((b.coh_dist * line_factor) - Math.sqrt(dx * dx + dy * dy)) * (255 / (b.coh_dist * line_factor))).clamp(50, 255)
}
end
end
else
lines << {
x: b.x + 0.5 * b.w,
y: b.y + 0.5 * b.h,
x2: b.x - dx + 0.5 * b.w,
y2: b.y - dy + 0.5 * b.h,
r: 255,
g: 255,
b: 255,
a: (((b.coh_dist * line_factor) - Math.sqrt(dx * dx + dy * dy)) * (255 / (b.coh_dist * line_factor))).clamp(50, 255)
}
end
end
end
end
lines
end
# @param [Array<Boid>] boids
def update_neighbors(boids)
boids.each do |b|
b.neighbors = []
end
boids.each_with_index do |a, ai|
boids.each_with_index do |b, bi|
if ai != bi
dx, dy = *(a.offset(b))
dist_sq = dx * dx + dy * dy
if dist_sq <= a.coh_dist * a.coh_dist
angle = ((Math.atan2(dy, dx) - a.angle.to_radians + Math::PI) % (2 * Math::PI)) - Math::PI
if (Math::PI - angle.abs) < a.fov.to_radians
a.neighbors.push(b)
end
end
end
end
end
end
class Boid
attr_reader :x, :y, :vel_x, :vel_y, :coh_dist, :angle, :fov
attr_accessor :neighbors
attr_sprite
def initialize
@min_speed = 1.00
@max_speed = 2.50
@coh_dist = 60
@sep_dist = 30
@rep_dist = 100
@fov = 95
@x = rand(1281)
@y = rand(721)
@w = 10
@h = 10
@anchor_x = -0.5
@anchor_y = -0.5
@angle = 0
@vel_x = (rand * 2.0 - 1.0) * @max_speed
@vel_y = (rand * 2.0 - 1.0) * @max_speed
@angle = Math.atan2(@vel_y, @vel_x).to_degrees
@acc_x = 0
@acc_y = 0
@path = "sprites/boid.png"
@neighbors = []
@age = 0
@noises = (1..3).map { |_| (rand * 2.0 - 1.0) / 10.0 }.product((1..3).map { |_| rand * 2 * Math::PI })
@deviation = 2
end
def tick
@age += 1
@vel_x += @acc_x
@vel_y += @acc_y
speed = Math.sqrt(@vel_x * @vel_x + @vel_y * @vel_y).clamp(@min_speed, @max_speed)
@angle = Math.atan2(@vel_y, @vel_x).to_degrees
@angle += @deviation * (@noises.map { |n| Math.sin(n[0] * @age * $TIME_FACTOR + n[1]) }.reduce(&:plus) / @noises.length) / (1 + @neighbors.length)
@vel_x = Math.cos(@angle.to_radians) * speed
@vel_y = Math.sin(@angle.to_radians) * speed
@x = (@x + @vel_x * $TIME_FACTOR) % 1280
@y = (@y + @vel_y * $TIME_FACTOR) % 720
@acc_x = 0
@acc_y = 0
# @r = 255 / (1 + @neighbors.length)
@g = (256 - 255 / (1 + @neighbors.length))
@b = @g
end
# @param [Boid] other
def offset(other)
point_offset(other.x, other.y)
end
# @param [Numeric] in_x
# @param [Numeric] in_y
def point_offset(in_x, in_y)
dx = if (@x - in_x).abs < 640 || !$X_WRAP
@x - in_x
elsif @x > in_x
@x - (in_x + 1280)
else
@x + 1280 - in_x
end
dy = if (@y - in_y).abs < 360 || !$Y_WRAP
@y - in_y
elsif @y > in_y
@y - (in_y + 720)
else
@y + 720 - in_y
end
[dx, dy]
end
def separate
sep_factor = 0.1
move_x = 0.0
move_y = 0.0
n = 0
@neighbors.each do |b|
if b != self
dx, dy = *(self.offset(b))
if (dx * dx + dy * dy) < (@sep_dist * @sep_dist)
repulsion = 1 - (Math.sqrt(dx * dx + dy * dy) / @sep_dist)
move_x += dx * repulsion
move_y += dy * repulsion
n += 1
end
end
end
if $args.inputs.mouse.button_right
dx, dy = *(self.point_offset($args.inputs.mouse.x, $args.inputs.mouse.y))
if (dx * dx + dy * dy) < ((@sep_dist * 2) * (@sep_dist * 2))
repulsion = 1 - (Math.sqrt(dx * dx + dy * dy) / (@sep_dist * 2))
move_x += dx * repulsion
move_y += dy * repulsion
n += 1
end
end
if n > 0
move_x /= n
move_y /= n
@acc_x += move_x * sep_factor
@acc_y += move_y * sep_factor
end
end
def cohere
cohere_factor = 0.01
c_x = 0
c_y = 0
n = 0
@neighbors.each do |b|
if b != self
dx, dy = *self.offset(b)
if (dx * dx + dy * dy) < (@coh_dist * @coh_dist)
# dist = Math.sqrt(dx * dx + dy * dy)
dist = dx * dx + dy * dy
c_x += dx / dist
c_y += dy / dist
n += 1
end
end
end
if $args.inputs.mouse.button_left
dx, dy = *(self.point_offset($args.inputs.mouse.x, $args.inputs.mouse.y))
if (dx * dx + dy * dy) < ((@coh_dist * 2) * (@coh_dist * 2))
c_x += dx * 2
c_y += dy * 2
n += 2
end
end
if n > 0
c_x /= n
c_y /= n
@acc_x -= c_x * cohere_factor
@acc_y -= c_y * cohere_factor
end
end
def align
align_factor = 0.1
v_x = 0
v_y = 0
n = 0
@neighbors.each do |b|
if b != self
dx, dy = *self.offset(b)
if (dx * dx + dy * dy) < (@coh_dist * @coh_dist)
dist = @coh_dist / Math.sqrt(dx * dx + dy * dy)
#dist = 1
v_x += b.vel_x / dist
v_y += b.vel_y / dist
n += 1
end
end
end
if n > 0
v_x /= n
v_y /= n
v_x -= @vel_x * align_factor
v_y -= @vel_y * align_factor
@acc_x += v_x * align_factor
@acc_y += v_y * align_factor
end
end
def repel
repel_factor = 0.05
r_x = 0
r_y = 0
if @x < @rep_dist
r_x += (@rep_dist / @x)
elsif (1280 - @x) < @rep_dist
r_x -= (@rep_dist / (1280 - @x))
end
if @y < @rep_dist
r_y += (@rep_dist / @y)
elsif (720 - @y) < @rep_dist
r_y -= (@rep_dist / (720 - @y))
end
@acc_x += r_x * repel_factor unless $X_WRAP
@acc_y += r_y * repel_factor unless $Y_WRAP
end
end
@quietsamurai98
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment