ang 9.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.txt +88 -0
- data/README.md +86 -0
- data/Rakefile.rb +228 -0
- data/Star.bmp +0 -0
- data/Star.png +0 -0
- data/Starfighter.bmp +0 -0
- data/Starfighter2.bmp +0 -0
- data/VERSION +1 -0
- data/ang.gemspec +48 -0
- data/bin/ang +5 -0
- data/bin/angm +5 -0
- data/bin/angm_net_test +3 -0
- data/commit.rb +11 -0
- data/main.rb +379 -0
- data/main_screencast.rb +324 -0
- data/net/Star.bmp +0 -0
- data/net/Starfighter.bmp +0 -0
- data/net/agent.rb +339 -0
- data/net/essai.rb +10 -0
- data/net/missile.rb +51 -0
- data/net/multicast_test.rb +74 -0
- data/net/net.rb +199 -0
- data/net/player.rb +166 -0
- data/net/star.rb +76 -0
- data/net/tools.rb +58 -0
- metadata +93 -0
data/net/net.rb
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
# Creative Commons BY-SA : Regis d'Aubarede <regis.aubarede@gmail.com>
|
2
|
+
###########################################################################
|
3
|
+
# reseau sharing for ANG
|
4
|
+
# NetClient: singleton inteface main <=> net
|
5
|
+
# MCast : multicasting encapsulation
|
6
|
+
###########################################################################
|
7
|
+
require "thread"
|
8
|
+
require "socket"
|
9
|
+
require "ipaddr"
|
10
|
+
require "zlib" # if message is too big for a datagram, zip it
|
11
|
+
(puts "ruby version must be 1.9.3 or best (patched for multicast)";exit(1)) if RUBY_VERSION < "1.9.3"
|
12
|
+
|
13
|
+
################ Net manager : multicast member, send to all, receive from any ##############
|
14
|
+
class NetClient
|
15
|
+
class << self
|
16
|
+
def init(game)
|
17
|
+
@is_master=true
|
18
|
+
@trace=false
|
19
|
+
@event_stack = Queue.new
|
20
|
+
@players,@players_ip,@game={},{},game
|
21
|
+
@queue= Queue.new
|
22
|
+
@mcast=MCast.new(self)
|
23
|
+
Thread.new { dispatch() }
|
24
|
+
end
|
25
|
+
def set_trace(on)
|
26
|
+
@trace=on
|
27
|
+
end
|
28
|
+
def is_master() @is_master end
|
29
|
+
def reinit_master(lid)
|
30
|
+
@is_master=true unless lid.any? {|id| id<$id}
|
31
|
+
end
|
32
|
+
# invoked by MCast, data is string (ruby literal)
|
33
|
+
# ["move",id,time,[x,y],[vx,vy],[a,va]]
|
34
|
+
def receive_data(data)
|
35
|
+
return if !(data && data[0,1]=="[")
|
36
|
+
bdata=eval( data )
|
37
|
+
log("Received: #{bdata.inspect[0..100]} / length=#{data.size} bytes") if @trace
|
38
|
+
if Array===bdata && bdata.length>1
|
39
|
+
code,id,*bdata=bdata
|
40
|
+
if id==$id
|
41
|
+
log(" This message is from myself ! so, multicast seem ok")if @trace
|
42
|
+
return
|
43
|
+
end
|
44
|
+
@is_master=false if id<$id
|
45
|
+
@event_stack << [code,id,bdata]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
# event recived by multicast, are evaluated here,
|
49
|
+
# event_invoke() must be called in main thread loop
|
50
|
+
def event_invoke()
|
51
|
+
while @event_stack.size>0
|
52
|
+
code,id,bdata=*(@event_stack.pop)
|
53
|
+
invoke(code,id,bdata)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
def wait_and_invoke()
|
57
|
+
code,id,bdata=*(@event_stack.pop)
|
58
|
+
invoke(code,id,bdata)
|
59
|
+
end
|
60
|
+
def invoke(code,id,bdata)
|
61
|
+
case code
|
62
|
+
when "move"
|
63
|
+
@game.update_payers(id,bdata)
|
64
|
+
when "connect"
|
65
|
+
@game.init_player(id)
|
66
|
+
@game.send_positions(id)
|
67
|
+
when "success"
|
68
|
+
@game.receive_success(id)
|
69
|
+
when "echec"
|
70
|
+
@game.receive_echec(id)
|
71
|
+
when "positions"
|
72
|
+
@game.get_positions(id,bdata) if id < $id
|
73
|
+
when "star_delete"
|
74
|
+
@game.star_deleted(bdata[0])
|
75
|
+
when "nmissile"
|
76
|
+
@game.new_missile(bdata)
|
77
|
+
when "emissile"
|
78
|
+
@game.end_missile(bdata[0])
|
79
|
+
when "comment"
|
80
|
+
@game.display_comment(bdata[0])
|
81
|
+
when "dead-pl"
|
82
|
+
@game.receive_echec(bdata[0])
|
83
|
+
when "echo"
|
84
|
+
recho(bdata)
|
85
|
+
when "recho"
|
86
|
+
@game.recho(id,*bdata) rescue p $!
|
87
|
+
when "quit"
|
88
|
+
@game.recho(id,*bdata) rescue nil
|
89
|
+
else
|
90
|
+
puts "recieved unknown message #{[code,id,data].join(", ")}"
|
91
|
+
end rescue (puts $!.to_s + "\n "+ $!.backtrace.join("\n "))
|
92
|
+
end
|
93
|
+
def dispatch()
|
94
|
+
@mcast.send_message(1,["connect",$id])
|
95
|
+
loop do
|
96
|
+
begin
|
97
|
+
m=@queue.pop
|
98
|
+
#log "#{$id} send to serveur : ",m
|
99
|
+
@mcast.send_message(1,m)
|
100
|
+
rescue Exception => e
|
101
|
+
puts e.to_s + "\n "+ e.backtrace.join("\n ")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
def connect() @mcast.send_message(1,["connect",$id]) end
|
106
|
+
def player_is_moving(data) @queue.push(["move",$id,*data]) end
|
107
|
+
def send_success() @queue.push(["success",$id]) end
|
108
|
+
def send_echec() @queue.push(["echec",$id]) end
|
109
|
+
def send_position(data) @queue.push(["positions",$id,*data]) end
|
110
|
+
def star_deleted(index) @queue.push(["star_delete",$id,index]) end
|
111
|
+
def comment(text) @queue.push(["comment",$id,text]) end
|
112
|
+
def new_missile(data) @queue.push(["nmissile",$id,*data]) end
|
113
|
+
def end_missile(data) @queue.push(["emissile",$id,*data]) end
|
114
|
+
def dead_player(data) @queue.push(["dead-pl",$id,*data]) end
|
115
|
+
def echo(data) @queue.push(["echo",$id,*data]) end
|
116
|
+
def recho(data) @queue.push(["recho",$id,*data]) end
|
117
|
+
def is_stoping()
|
118
|
+
@queue.pop while (@queue.size>2)
|
119
|
+
@queue.push(["quit",$id])
|
120
|
+
Thread.new { sleep(1) ; exit! }
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
class MCast
|
127
|
+
MULTICAST_ADDR = "224.6.1.89"
|
128
|
+
BIND_ADDR = "0.0.0.0"
|
129
|
+
PORT = 6811
|
130
|
+
|
131
|
+
def initialize(client)
|
132
|
+
@client = client
|
133
|
+
socket
|
134
|
+
listen()
|
135
|
+
end
|
136
|
+
|
137
|
+
def send_message(n,content)
|
138
|
+
message = content.inspect
|
139
|
+
if message.size>1024
|
140
|
+
message= "#" + Zlib::Deflate.new(9).deflate(message, Zlib::FINISH)
|
141
|
+
if message.size>1024
|
142
|
+
puts "message size too big for a datagram !"
|
143
|
+
return
|
144
|
+
end
|
145
|
+
end
|
146
|
+
socket.send(message, 0, MULTICAST_ADDR, PORT)
|
147
|
+
(n-1).times { sleep(0.02); socket.send(message, 0, MULTICAST_ADDR, PORT) }
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def listen
|
153
|
+
socket.bind(BIND_ADDR, PORT) rescue nil
|
154
|
+
|
155
|
+
Thread.new do
|
156
|
+
loop do
|
157
|
+
message, x = socket.recvfrom(1024)
|
158
|
+
if message[0,1]=="#"
|
159
|
+
zstream = Zlib::Inflate.new
|
160
|
+
message = zstream.inflate(message[1..-1])
|
161
|
+
zstream.finish
|
162
|
+
zstream.close
|
163
|
+
end
|
164
|
+
@client.receive_data(message)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
def socket
|
171
|
+
@socket ||= UDPSocket.open.tap do |socket|
|
172
|
+
begin
|
173
|
+
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1) rescue nil
|
174
|
+
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEPORT, 1) rescue nil
|
175
|
+
|
176
|
+
ctx="bind()"
|
177
|
+
socket.bind(BIND_ADDR, PORT) if RUBY_PLATFORM =~ /(win|w)32$/ # for winxp
|
178
|
+
|
179
|
+
ctx="IP_ADD_MEMBERSHIP"
|
180
|
+
socket.setsockopt(Socket::IPPROTO_IP, Socket::IP_ADD_MEMBERSHIP, bind_address)
|
181
|
+
ctx="IP_MULTICAST_TTL"
|
182
|
+
socket.setsockopt(Socket::IPPROTO_IP, Socket::IP_MULTICAST_TTL, 1)
|
183
|
+
rescue Exception => e
|
184
|
+
puts "******************************************"
|
185
|
+
puts "Multicast seem problematic on your system :"
|
186
|
+
puts " in #{ctx} : #{$!.to_s}"
|
187
|
+
puts "Did you have installed last version of Ruby (1.9.3p362 is ok)"
|
188
|
+
puts "Or your network is deconnected..."
|
189
|
+
puts "******************************************"
|
190
|
+
sleep 5
|
191
|
+
exit(1)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def bind_address
|
197
|
+
IPAddr.new(MULTICAST_ADDR).hton + IPAddr.new(BIND_ADDR).hton
|
198
|
+
end
|
199
|
+
end
|
data/net/player.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
# Creative Commons BY-SA : Regis d'Aubarede <regis.aubarede@gmail.com>
|
2
|
+
###########################################################################
|
3
|
+
# P l a y e r
|
4
|
+
###########################################################################
|
5
|
+
# move by arrow keyboard acceleration commande,
|
6
|
+
# eat star, move with current speed, and attractive planets
|
7
|
+
class Player
|
8
|
+
attr_accessor :x,:y,:r,:local,:score,:id
|
9
|
+
def initialize(window,animation,local)
|
10
|
+
@local = local
|
11
|
+
@top=0
|
12
|
+
@pos=1
|
13
|
+
@animation = animation
|
14
|
+
@app=window
|
15
|
+
clear()
|
16
|
+
@id=(Time.now.to_f*1000).to_i*1000+rand(1000)
|
17
|
+
@r=15
|
18
|
+
self.restart()
|
19
|
+
end
|
20
|
+
def restart
|
21
|
+
@vel_x = @vel_y = @angle = @vangle = 0.0
|
22
|
+
@x = SX/2
|
23
|
+
@y = SY/2
|
24
|
+
@vx,@vy=[0,0]
|
25
|
+
@score = $INITIALE_SCORE
|
26
|
+
@lxy=[]
|
27
|
+
@now=Time.now.to_f * 1000
|
28
|
+
@top=0
|
29
|
+
end
|
30
|
+
def clear() @pos=1 end
|
31
|
+
def dead
|
32
|
+
@vangle=1000
|
33
|
+
NetClient.dead_player([id])
|
34
|
+
end
|
35
|
+
def fire_missile
|
36
|
+
v=Math.sqrt(@vel_x*@vel_x+@vel_y*@vel_y)
|
37
|
+
v=6 if v.abs<6
|
38
|
+
vx=Gosu::offset_x(@angle,v)
|
39
|
+
vy=Gosu::offset_y(@angle,v)
|
40
|
+
Missile.new(@app,@animation,@local,nil,@x+10*vx,@y+10*vy,vx*1.2,vy*1.2,@r)
|
41
|
+
end
|
42
|
+
def warp(x, y) @x, @y = x, y ; end
|
43
|
+
def turn_left() @pos=3 ; @vangle -= 0.3 ; end
|
44
|
+
def turn_right() @pos=4 ; @vangle += 0.3 ; end
|
45
|
+
def accelerate(s)
|
46
|
+
@pos= s ? 0 : 2
|
47
|
+
@score-=3
|
48
|
+
@vel_x += Gosu::offset_x(@angle, s ? 0.1 : -0.1)
|
49
|
+
@vel_y += Gosu::offset_y(@angle, s ? 0.1 : -0.1)
|
50
|
+
end
|
51
|
+
def receive_button(command)
|
52
|
+
@top=5
|
53
|
+
@command=command
|
54
|
+
end
|
55
|
+
|
56
|
+
def move(stars,now)
|
57
|
+
return if @x==-1000 && @y==-1000
|
58
|
+
if @local
|
59
|
+
a=false
|
60
|
+
(a=true;@vel_x *= -1) if @x >= SX || @x <= 0
|
61
|
+
(a=true;@vel_y *= -1) if @y >= SY || @y <= 0
|
62
|
+
@x = SX-10 if @x >= SX && @vel_x.abs < 0.01
|
63
|
+
@y = SY-10 if @y >= SY && @vel_y.abs < 0.01
|
64
|
+
@x = 10 if @x <= 0 && @vel_x.abs < 0.01
|
65
|
+
@y = 10 if @y <= 0 && @vel_y.abs < 0.01
|
66
|
+
|
67
|
+
@angle+=@vangle
|
68
|
+
@vangle=@vangle*95.0/100
|
69
|
+
@lxy << [@x,@y] if @vel_x!=0 && @vel_y!=0
|
70
|
+
vx,vy=newton(stars)
|
71
|
+
@vel_x+=vx
|
72
|
+
@vel_y+=vy
|
73
|
+
@vel_x.minmax(-50,+50)
|
74
|
+
@vel_y.minmax(-50,+50)
|
75
|
+
@x += @vel_x
|
76
|
+
@y += @vel_y
|
77
|
+
n=now*1000
|
78
|
+
delta=n-@now
|
79
|
+
if delta >= $NET_TRANSMIT
|
80
|
+
@top+=1
|
81
|
+
NetClient.player_is_moving([@top,n,[@x,@y],[@vel_x,@vel_y],[vx,vy],[@angle,@vangle],@score])
|
82
|
+
@now=n
|
83
|
+
end
|
84
|
+
else
|
85
|
+
@vel_x+=@vx
|
86
|
+
@vel_y+=@vy
|
87
|
+
@vx,@vy=[@vx/2,@vy/2]
|
88
|
+
@x += @vel_x
|
89
|
+
@y += @vel_y
|
90
|
+
@angle+=@vangle
|
91
|
+
@vangle=@vangle*95.0/100
|
92
|
+
end
|
93
|
+
end
|
94
|
+
def update_by_net(data)
|
95
|
+
top,t,pos,vel,acc,ang,@score=*data
|
96
|
+
return if top<=@top
|
97
|
+
@top=top
|
98
|
+
|
99
|
+
#puts "delta #{Time.now.to_f*1000-t} ms"
|
100
|
+
|
101
|
+
@x,@y=(pos[0]+@x)/2,(pos[1]+@y)/2
|
102
|
+
@vel_x,@vel_y=vel[0]/2,vel[1]/2
|
103
|
+
@vx,@vy=acc
|
104
|
+
@angle,@vangle=ang
|
105
|
+
end
|
106
|
+
|
107
|
+
def newton(stars)
|
108
|
+
vx = vy = 0.0
|
109
|
+
stars.each do |star|
|
110
|
+
next if star.type
|
111
|
+
d=Gosu::distance(@x,@y,star.x,star.y)-15-star.r
|
112
|
+
dx,dy=*newton_xy1(15*15,star.r*star.r,self,star)
|
113
|
+
vx+=dx
|
114
|
+
vy+=dy
|
115
|
+
end
|
116
|
+
[vx,vy]
|
117
|
+
end
|
118
|
+
def draw(app,stars)
|
119
|
+
img = @animation[@pos]
|
120
|
+
img.draw_rot(@x, @y, ZOrder::Player, @angle)
|
121
|
+
x,y=newton(stars) ; app.draw_line(@x,@y, 0xffffffff,@x+x*1000,@y+y*1000,0xffffffff) # debug: mark gravity force
|
122
|
+
if app.pending?
|
123
|
+
@lxy.each_cons(2) { |p0,p1| app.draw_line(p0[0],p0[1], 0xffffff00 ,p1[0],p1[1], 0xffffff00 ) if p1} rescue nil
|
124
|
+
elsif @lxy.size>100
|
125
|
+
@lxy[(-1*[800,@lxy.size].min)..-1].each_cons(2) { |p0,p1|
|
126
|
+
app.draw_line(p0[0],p0[1], 0x33ffff00 ,p1[0],p1[1], 0x33ffff00 ) if p1
|
127
|
+
} rescue nil
|
128
|
+
end
|
129
|
+
@lxy=@lxy[-5000..-1] if @lxy.size>10000
|
130
|
+
end
|
131
|
+
|
132
|
+
def collect_stars(stars)
|
133
|
+
stars.reject! do |star|
|
134
|
+
next(false) if star.x-@x > 200
|
135
|
+
next(false) if star.x-@x < -200
|
136
|
+
next(false) if star.y-@y > 200
|
137
|
+
next(false) if star.y-@y < -200
|
138
|
+
|
139
|
+
if Gosu::distance(@x, @y, star.x, star.y) < (15+star.r) then
|
140
|
+
if star.type
|
141
|
+
@score += 120
|
142
|
+
NetClient.star_deleted(star.index)
|
143
|
+
true
|
144
|
+
else
|
145
|
+
if @vel_x !=0 || @vel_y!=0
|
146
|
+
@score -= 10
|
147
|
+
@x -= 15*@vel_x
|
148
|
+
@y -= 15*@vel_y
|
149
|
+
@x -= -2*10*@vel_x if @x >= SX && @vel_x == 0
|
150
|
+
@y -= -2*10*@vel_y if @y >= SY && @vel_y == 0
|
151
|
+
else
|
152
|
+
@x += (-10..+10).rand
|
153
|
+
@y += (-10..+10).rand
|
154
|
+
end
|
155
|
+
@vel_x=0
|
156
|
+
@vel_y=0
|
157
|
+
false
|
158
|
+
end
|
159
|
+
else
|
160
|
+
false
|
161
|
+
end
|
162
|
+
end
|
163
|
+
@app.finish if @score<=0
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
data/net/star.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# Creative Commons BY-SA : Regis d'Aubarede <regis.aubarede@gmail.com>
|
2
|
+
###########################################################################
|
3
|
+
# S t a r
|
4
|
+
###########################################################################
|
5
|
+
class Star
|
6
|
+
attr_accessor :x, :y, :type,:r
|
7
|
+
|
8
|
+
def initialize(ls,type,animation)
|
9
|
+
@animation = animation
|
10
|
+
@index=ls.size
|
11
|
+
@ls=ls
|
12
|
+
@type=type
|
13
|
+
@r=@type ? 10 : $RANGE_STAR_SIZE.rand()
|
14
|
+
@no_img= type ? 1 : (rand()>0.5 && @r>35) ? 0 : (rand(3)+2)
|
15
|
+
@rot=rand(180)
|
16
|
+
ok=(@type || rand()<0.4)
|
17
|
+
p "nok" unless ok
|
18
|
+
@show= ok ? 1000 : 0
|
19
|
+
@color = Gosu::Color.new(0xff000000 )
|
20
|
+
@color.red = type ? 255 : 200
|
21
|
+
@color.green = type ? 0 : 200
|
22
|
+
@color.blue = type ? 0 : 200
|
23
|
+
@x = (SX/7..(SX-SX/7)).rand
|
24
|
+
@y = (SY/7..(SY-SY/7)).rand
|
25
|
+
#@x = (index % 10) * (SX/10)
|
26
|
+
#@y = (index / 10) * (SY/10)
|
27
|
+
end
|
28
|
+
def index() @index end
|
29
|
+
|
30
|
+
########### Net
|
31
|
+
|
32
|
+
def self.create(anim,no_img,r,rot)
|
33
|
+
Star.new([],10,anim).reinit(no_img,r,rot)
|
34
|
+
end
|
35
|
+
def reinit(no_img,r,rot,colors)
|
36
|
+
@no_img,@r,@rot = no_img,r,rot
|
37
|
+
@color.red,@color.green,@color.blue=*colors
|
38
|
+
self
|
39
|
+
end
|
40
|
+
def serialize(i)
|
41
|
+
[i,[@no_img,@r,@rot,[@color.red,@color.green,@color.blue]],[@x,@y]]
|
42
|
+
end
|
43
|
+
|
44
|
+
def update(x,y) @x,@y=x,y end
|
45
|
+
|
46
|
+
def get_pos()
|
47
|
+
[@x.to_i,@y.to_i,@type,@index,@r,@no_img]
|
48
|
+
end
|
49
|
+
def set_pos(pos)
|
50
|
+
@x,@y,@type,@index,@r,@no_img=*pos
|
51
|
+
end
|
52
|
+
####### serveur side behavior
|
53
|
+
def move(game,ls)
|
54
|
+
ox,oy=@x,@y
|
55
|
+
expand(game,ls)
|
56
|
+
@x=ox if @x>(SX-40) || @x<40
|
57
|
+
@y=oy if @y>(SY-40) || @y<40
|
58
|
+
|
59
|
+
end
|
60
|
+
def expand(game,ls)
|
61
|
+
return unless game.pending?(40)
|
62
|
+
motion(ls,self,-100.0,110) { |o| ! o.type} if type # Star <-> Planet
|
63
|
+
motion(ls,self,-10.0,50) { |o| o.type} # * <-> Star
|
64
|
+
motion(ls,self,-10.0,500) { |o| ! o.type } if !type # Planet <-> Planet
|
65
|
+
end
|
66
|
+
|
67
|
+
####### draw client+server side
|
68
|
+
def draw()
|
69
|
+
@show+=0.3 if @show<1000
|
70
|
+
return unless @show>600
|
71
|
+
img = @animation[@no_img]
|
72
|
+
fx=(@r/40.0)*(@show/1000.0)
|
73
|
+
img.draw_rot(@x, @y, ZOrder::Stars, @rot, 0.5,0.5 ,fx,fx,@color)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
data/net/tools.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# Creative Commons BY-SA : Regis d'Aubarede <regis.aubarede@gmail.com>
|
2
|
+
|
3
|
+
module ZOrder
|
4
|
+
Background, Stars, Player, UI = [0,1,2,3]
|
5
|
+
end
|
6
|
+
class Numeric
|
7
|
+
def minmax(min,max=nil)
|
8
|
+
(max=min;min=-min) if !max
|
9
|
+
return self if self>=min && self<=max
|
10
|
+
return(min) if self<min
|
11
|
+
return(max)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
class Range ; def rand() self.begin+Kernel.rand((self.end-self.begin).abs) end ; end
|
15
|
+
|
16
|
+
# pseudo newton
|
17
|
+
def newton_xy1(p1,p2,a,b)
|
18
|
+
k1=1.0/40
|
19
|
+
k2=1.0/40000
|
20
|
+
dx,dy=[a.x-b.x,a.y-b.y]
|
21
|
+
d=Math::sqrt(dx ** 2 + dy ** 2)
|
22
|
+
f1=(k1*p1*p2/(d*d+100)).minmax(100)
|
23
|
+
f2=(k2*p1*p2/(d)).minmax(100)
|
24
|
+
f=0.8*[f1,f2].max
|
25
|
+
#p [f,f1,f2]
|
26
|
+
teta=Math.atan2(dy,dx)
|
27
|
+
r=[-f*Math.cos(teta),-f*Math.sin(teta)]
|
28
|
+
r
|
29
|
+
end
|
30
|
+
|
31
|
+
# newton, with 'amrtissement'
|
32
|
+
def newton_xy(p1,p2,a,b,k=1.0/300,dmin=10,dmax=10000)
|
33
|
+
dx,dy=[a.x-b.x,a.y-b.y]
|
34
|
+
d=dmin+Math::sqrt(dx ** 2 + dy ** 2)
|
35
|
+
return [0,0] if d>dmax
|
36
|
+
#f=(k*p1*p2/(d*d)).minmax(100) : k/d**2 not good for gameplay
|
37
|
+
f=(k*p1*p2/(d*d-dmin*dmin)).minmax(10)
|
38
|
+
teta=Math.atan2(dy,dx)
|
39
|
+
[-f*Math.cos(teta),-f*Math.sin(teta)]
|
40
|
+
end
|
41
|
+
|
42
|
+
# apply gravity between obj and a list l of planet
|
43
|
+
# k=coef gr&avity, >0 attraction, <0 repulsion
|
44
|
+
# dmaw : distance max, no attaction if distance>dmax
|
45
|
+
def motion(l,obj,k,dmax)
|
46
|
+
dx,dy=0,0
|
47
|
+
l.each do |o|
|
48
|
+
next if o==obj
|
49
|
+
next if block_given? && ! yield(o)
|
50
|
+
dx1,dy1= newton_xy(obj.r,o.r,obj,o,k,0,dmax)
|
51
|
+
dx+=dx1
|
52
|
+
dy+=dy1
|
53
|
+
end
|
54
|
+
obj.x+=dx
|
55
|
+
obj.y+=dy
|
56
|
+
return Math.sqrt(dx*dx+dy*dy)
|
57
|
+
end
|
58
|
+
|