ang 9.19.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|