-
Notifications
You must be signed in to change notification settings - Fork 2
/
boid.lua
187 lines (159 loc) · 6 KB
/
boid.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
require 'vector.lua'
Boid = {
identity = "Boid class",
radius = 15
}
Boid.MAX_SPEED = 175
Boid.MIN_SPEED = 100
Boid.ATTRACTION_RADIUS = Boid.radius * 8
Boid.ATTRACTION_DAMPER = 10
Boid.AVOID_RADIUS = Boid.radius * 3
Boid.AVOID_AMPLIFIER = 6
Boid.ALIGNMENT_RADIUS = Boid.radius * 3
Boid.ALIGNMENT_DAMPER = 8
Boid.HUNTING_RADIUS = Boid.radius * 10
Boid.HUNTING_DAMPER = 5
Boid.STAY_VISIBLE_DAMPER = 40
function Boid:new(x, y, vx, vy)
local instance = {}
setmetatable(instance, self)
self.__index = self
instance.velocity = Vector:new(vx, vy)
instance.position = Vector:new(x, y)
instance.velocity_delta = Vector:new(0, 0)
instance.left_sprite = love.graphics.newImage("images/left_chicken.png")
instance.right_sprite = love.graphics.newImage("images/right_chicken.png")
instance.left_anim = love.graphics.newAnimation(instance.left_sprite, 18, 18,
instance:flap_rate())
instance.right_anim = love.graphics.newAnimation(instance.right_sprite, 18, 18,
instance:flap_rate())
instance.anim = left_anim
return instance
end
function Boid:flap_rate()
return 0.1--self.velocity:r() / Boid.MAX_SPEED / 4
end
function Boid:calculate_avoidance_delta(boids)
for _, other in ipairs(boids) do
if self.position:isNearby(Boid.AVOID_RADIUS, other.position) then
local avoid_vector = (self.position - other.position)
local unit_avoid_accel = avoid_vector:norm()
local avoid_multiplier = Boid.AVOID_RADIUS * Boid.AVOID_AMPLIFIER / avoid_vector:r()
local avoid_accel = unit_avoid_accel * avoid_multiplier
self.velocity_delta = self.velocity_delta + avoid_accel
end
end
end
function Boid:calculate_attraction_delta(boids)
local average_position = Vector:new(0, 0)
local visible_boids = 0
for _, other in ipairs(boids) do
if self.position:isNearby(Boid.ATTRACTION_RADIUS, other.position) then
average_position = average_position + other.position
visible_boids = visible_boids + 1
end
end
average_position = average_position / visible_boids
self.velocity_delta = self.velocity_delta +
((average_position - self.position) / Boid.ATTRACTION_DAMPER)
end
function Boid:calculate_alignment_delta(boids)
local alignment_delta = Vector:new(0, 0)
local visible_boids = 0
for _, other in ipairs(boids) do
if self.position:isNearby(Boid.ALIGNMENT_RADIUS, other.position) then
alignment_delta = alignment_delta + other.velocity
visible_boids = visible_boids + 1
end
end
alignment_delta = alignment_delta / visible_boids
self.velocity_delta = self.velocity_delta +
((alignment_delta / Boid.ALIGNMENT_DAMPER))
end
function Boid:calculate_hunting_delta(foodstuffs)
for _, food in ipairs(foodstuffs) do
if self.position:isNearby(Boid.HUNTING_RADIUS, food.position) then
self.velocity_delta = self.velocity_delta +
((food.position - self.position) / Boid.HUNTING_DAMPER)
end
end
end
function Boid:calculate_stay_visible_delta(boids)
local mid_x = love.graphics.getWidth() / 2
local mid_y = love.graphics.getHeight() / 2
local center_vector = Vector:new(mid_x, mid_y)
self.velocity_delta = self.velocity_delta -
((self.position - center_vector) / Boid.STAY_VISIBLE_DAMPER)
end
function Boid:apply_deltas()
self.velocity = self.velocity + self.velocity_delta
self.velocity_delta = Vector:new(0, 0)
end
function Boid:limit_speed()
if self.velocity:r() > Boid.MAX_SPEED then
self.velocity = self.velocity / self.velocity:r() * Boid.MAX_SPEED
end
if self.velocity:r() < Boid.MIN_SPEED then
self.velocity = self.velocity / self.velocity:r() * Boid.MIN_SPEED
end
end
function Boid:navigate(boids, foodstuffs)
self:calculate_avoidance_delta(boids)
self:calculate_attraction_delta(boids)
self:calculate_alignment_delta(boids)
self:calculate_hunting_delta(foodstuffs)
-- self:calculate_stay_visible_delta(boids)
end
function Boid:move(dt)
self:apply_deltas()
self:limit_speed()
self.position = self.position + self.velocity * dt
end
function Boid:animate(dt)
if self.velocity.x <= 0 then
self.anim = self.left_anim
else
self.anim = self.right_anim
end
self.anim:update(dt)
end
function Boid:draw()
love.graphics.draw(self.anim, self.position.x, self.position.y,
math.deg(self.velocity:ang()), 1.5)
end
function Boid:draw_debug()
love.graphics.setColor(255, 165, 0)
love.graphics.circle(love.draw_line, self.position.x, self.position.y, 10)
love.graphics.draw("("..math.floor(self.position.x)..", "..math.floor(self.position.y)..")", self.position.x + 5, self.position.y)
love.graphics.setColor(255, 255, 255)
end
-- draw the physical radius
function Boid:draw_physical()
love.graphics.setColor(255, 255, 255, 128)
love.graphics.circle(love.draw_line, self.position.x, self.position.y, Boid.radius, 10)
love.graphics.setColor(255, 255, 255, 255)
end
-- draw the attraction radius
function Boid:draw_attraction()
love.graphics.setColor(255, 0, 0, 128)
love.graphics.circle(love.draw_line, self.position.x, self.position.y, Boid.ATTRACTION_RADIUS, 10)
love.graphics.setColor(255, 255, 255, 255)
end
-- draw the avoidance radius
function Boid:draw_avoidance()
love.graphics.setColor(0, 255, 0, 128)
love.graphics.circle(love.draw_line, self.position.x, self.position.y, Boid.AVOID_RADIUS, 10)
love.graphics.setColor(255, 255, 255, 255)
end
-- draw the alignment radius
function Boid:draw_alignment()
love.graphics.setColor(0, 0, 255, 128)
love.graphics.circle(love.draw_line, self.position.x, self.position.y, Boid.ALIGNMENT_RADIUS, 10)
love.graphics.setColor(255, 255, 255, 255)
end
-- draw the hunting radius
function Boid:draw_hunting()
love.graphics.setColor(255, 225, 132, 128)
love.graphics.circle(love.draw_line, self.position.x, self.position.y, Boid.HUNTING_RADIUS, 10)
love.graphics.setColor(255, 255, 255, 255)
end