simplephysics.bmx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. ' Simple Physics Engine - V1.0
  2. ' --------------------------------------------------------
  3. ' Written By: Rob Hutchinson 2004
  4. Strict
  5. Type TRectangle
  6. '#Region Declarations
  7. Field X:Int,Y:Int,Width:Int,Height:Int
  8. '#End Region
  9. '#Region Method: Bottom
  10. Method Bottom:Int()
  11. Return Y + Height
  12. End Method
  13. '#End Region
  14. '#Region Method: Right
  15. Method Right:Int()
  16. Return X + Width
  17. End Method
  18. '#End Region
  19. '#Region Method: Create
  20. Function Create:TRectangle(X:Int,Y:Int,Width:Int,Height:Int)
  21. Local Out:TRectangle = New TRectangle
  22. Out.X = X
  23. Out.Y = Y
  24. Out.Width = Width
  25. Out.Height = Height
  26. Return Out
  27. End Function
  28. '#End Region
  29. '#Region Method: IntersectsWith
  30. Method IntersectsWith:Int(Rectangle:TRectangle)
  31. If (((Rectangle.X < (Self.X + Self.Width)) And (Self.X < (Rectangle.X + Rectangle.Width))) And (Rectangle.Y < (Self.Y + Self.Height))) Then
  32. Return (Self.Y < (Rectangle.Y + Rectangle.Height))
  33. End If
  34. Return False
  35. End Method
  36. '#End Region
  37. End Type
  38. Type TPhysicsUtility
  39. '#Region Method: DistanceBetweenPoints
  40. Function DistanceBetweenPoints:Double(X1:Int, Y1:Int, X2:Int, Y2:Int)
  41. Local DeltaX:Int = X2 - X1
  42. Local DeltaY:Int = Y2 - Y1
  43. Local Calculation:Int = ((DeltaX * DeltaX) + (DeltaY * DeltaY))
  44. Return Sqr(Double(Calculation))
  45. End Function
  46. '#End Region
  47. Function DegreesBetweenPoints(X1:Double, Y1:Double, X2:Double, Y2:Double)
  48. Local Out:Double = ATan2(X2 - X1,-(Y2 - Y1))
  49. Return 180.0 - Out
  50. End Function
  51. End Type
  52. Type TPointD
  53. '#Region Declarations
  54. Field X:Double = 0
  55. Field Y:Double = 0
  56. '#End Region
  57. End Type
  58. Type TMagnetCollection Extends TList
  59. '#Region Method: Draw
  60. Method Draw()
  61. Local Item:TMagnet
  62. For Item=EachIn Self
  63. Item.Draw()
  64. Next
  65. End Method
  66. '#End Region
  67. End Type
  68. Type TPhysicsProviderCollection Extends TList
  69. '#Region Method: Draw
  70. Method Draw()
  71. Local Item:TPhysicsProvider
  72. For Item=EachIn Self
  73. Item.Draw()
  74. Next
  75. End Method
  76. '#End Region
  77. '#Region Method: ApplyPhysics
  78. Method ApplyPhysics:Int()
  79. Local Item:TPhysicsProvider
  80. Local Count:Int
  81. For Item=EachIn Self
  82. Item.ApplyPhysics()
  83. Count:+1
  84. Next
  85. Return Count
  86. End Method
  87. '#End Region
  88. '#Region Method: ApplyPhysicsAndFriction
  89. Method ApplyPhysicsAndFriction:Int(Axis:Int)
  90. Local Item:TPhysicsProvider
  91. Local Count:Int
  92. For Item=EachIn Self
  93. Item.ApplyFriction(Axis)
  94. Item.ApplyPhysics()
  95. Item.Draw()
  96. Count:+1
  97. Next
  98. Return Count
  99. End Method
  100. '#End Region
  101. '#Region Method: ApplyPhysicsAndDraw
  102. Method ApplyPhysicsAndDraw()
  103. Local Item:TPhysicsProvider
  104. For Item=EachIn Self
  105. Item.ApplyPhysics()
  106. Item.Draw()
  107. Next
  108. End Method
  109. '#End Region
  110. End Type
  111. Type TMagnet
  112. '#Region Declarations
  113. Field X:Int, Y:Int
  114. Field Radius:Double
  115. Const NegativePolarity = -1
  116. Const PositivePolarity = 1
  117. Field Strength:Double
  118. Field Polarity:Int = 1
  119. '#End Region
  120. '#Region Method: GetStrengthOfPull
  121. Method GetStrengthOfPull:Double(Orbit:Double)
  122. If Orbit > Self.Radius Then Return 0
  123. ' First work out the percentage...
  124. Local PercentOfPull:Double = (Orbit / Self.Radius) * 100
  125. Return Self.Strength - ((PercentOfPull / 100) * Self.Strength)
  126. End Method
  127. '#End Region
  128. '#Region Method: GetForces
  129. Method GetForces:TPointD(X:Int, Y:Int)
  130. Local Out:TPointD=New TPointD
  131. Out.X = 0
  132. Out.Y = 0
  133. ' Get Distance Between Points...
  134. Local Distance:Double = TPhysicsUtility.DistanceBetweenPoints(X, Y, Self.X, Self.Y)
  135. Local Strength:Double = Self.GetStrengthOfPull(Distance)
  136. If Strength = 0 Then Return Out
  137. ' Get the degrees between points..
  138. Local Angle:Double = TPhysicsUtility.DegreesBetweenPoints(X, Y,Self.X, Self.Y)
  139. ' Reverse strength if using negative polarity.
  140. If Self.Polarity = NegativePolarity Then Strength = -Strength
  141. Out.X = Sin(Angle) * Strength
  142. Out.Y = Cos(Angle) * Strength
  143. Return Out
  144. End Method
  145. '#End Region
  146. '#Region Method: Draw
  147. Method Draw()
  148. Local Degrees
  149. SetColor 255,255,255
  150. For Degrees = 0 To 360
  151. DrawRect Float(Self.X + (Sin(Degrees) * Self.Radius)), Float(Self.Y + (Cos(Degrees) * Self.Radius)),1,1
  152. Next
  153. End Method
  154. '#End Region
  155. '#Region Method: Create
  156. Function Create:TMagnet(X:Int,Y:Int,Radius:Double,Polarity:Int,Strength:Double)
  157. Local Out:TMagnet = New TMagnet
  158. Out.X = X
  159. Out.Y = Y
  160. Out.Radius = Radius
  161. Out.Polarity = Polarity
  162. Out.Strength = Strength
  163. Return Out
  164. End Function
  165. '#End Region
  166. '#Region Method: SetPosition
  167. Method SetPosition(X:Int,Y:Int)
  168. Self.X = X
  169. Self.Y = Y
  170. End Method
  171. '#End Region
  172. End Type
  173. Type TWorldPhysicsProvider
  174. Field Loc = 0
  175. '#Region Declarations
  176. Field Gravity:Double
  177. Field Wind:Double
  178. Field Drag:Double
  179. Field ApplyMagnets:Int = True
  180. Field Magnets:TMagnetCollection = New TMagnetCollection
  181. '#End Region
  182. '#Region Method: Initialize
  183. Method Initialize(Gravity:Double, Wind:Double = 0.0, Drag:Double = 0.0, ApplyMagnets:Int = False)
  184. Self.Gravity = Gravity
  185. Self.Wind = Wind
  186. Self.Drag = Drag
  187. Self.ApplyMagnets = ApplyMagnets
  188. End Method
  189. '#End Region
  190. '#Region Method: Create
  191. Function Create:TWorldPhysicsProvider(Gravity:Double, Wind:Double = 0.0, Drag:Double = 0.0, ApplyMagnets:Int = False)
  192. Local Out:TWorldPhysicsProvider = New TWorldPhysicsProvider
  193. Out.Gravity = Gravity
  194. Out.Wind = Wind
  195. Out.Drag = Drag
  196. Out.ApplyMagnets = ApplyMagnets
  197. Return Out
  198. End Function
  199. '#End Region
  200. End Type
  201. Type TPhysicsProvider Extends TLink
  202. '#Region Declarations
  203. ' Location.
  204. Field X:Double, Y:Double
  205. Field World:TWorldPhysicsProvider = New TWorldPhysicsProvider
  206. ' Terminal Velocity
  207. Field TerminalVelocityX:Double = 40.0, TerminalVelocityY:Double = 40.0
  208. ' Velocity
  209. Field VelocityX:Double, VelocityY:Double
  210. ' Weight
  211. Field Weight:Double = 10.0
  212. Field SurfaceArea:Double = 10.0
  213. ' Bounce Values.
  214. Field BounceCoefficientX:Double = 1.0
  215. Field BounceCoefficientY:Double = 0.75
  216. ' The friction being applied to this object.
  217. Field Friction:Double = 1.0
  218. ' Whether or not to apply magnets to the individual object.
  219. Field ApplyMagnets:Int = True
  220. Const AxisX:Int = 1
  221. Const AxisY:Int = 2
  222. Const VerticalPlane = 1
  223. Const HorizontalPlane = 2
  224. '#End Region
  225. ' Method remove()
  226. ' World.remove Self
  227. ' End Method
  228. '#Region Method: Double
  229. Method Speed:Double()
  230. Return Sqr((Self.VelocityX * Self.VelocityX) + (Self.VelocityY * Self.VelocityY))
  231. End Method
  232. '#End Region
  233. '#Region Method: Momentum
  234. Method Momentum:Double()
  235. Return Self.Speed() * Self.Weight
  236. End Method
  237. '#End Region
  238. '#Region Method: ReverseVelocityX
  239. Method ReverseVelocityX()
  240. Self.VelocityX = -Self.VelocityX
  241. End Method
  242. '#End Region
  243. '#Region Method: ReverseVelocityY
  244. Method ReverseVelocityY()
  245. Self.VelocityY = -Self.VelocityY
  246. End Method
  247. '#End Region
  248. '#Region Method: SnapWithinRectangle
  249. Method SnapWithinRectangle(Area:TRectangle)
  250. If Self.X < Area.X Then Self.X = Area.X
  251. If Self.Y < Area.Y Then Self.Y = Area.Y
  252. Local Right:Int = Area.X + Area.Width
  253. Local Bottom:Int = Area.Y + Area.Height
  254. If Self.X > Right Then Self.X = Right
  255. If Self.Y > Bottom Then Self.Y = Bottom
  256. End Method
  257. '#End Region
  258. '#Region Method: SnapWithinRectangleAndReverseVelocity
  259. Method SnapWithinRectangleAndReverseVelocity:Int(Area:TRectangle)
  260. Local Out:Int = False
  261. If Self.X < Area.X Then
  262. Self.X = Area.X
  263. Self.ReverseVelocityX()
  264. Out = True
  265. End If
  266. If Self.Y < Area.Y Then
  267. Self.Y = Area.Y
  268. Self.ReverseVelocityY()
  269. Out = True
  270. End If
  271. Local Right:Int = Area.X + Area.Width
  272. Local Bottom:Int = Area.Y + Area.Height
  273. If Self.X > Right Then
  274. Self.X = Right
  275. Self.VelocityX = -Self.VelocityX
  276. Out = True
  277. End If
  278. If Self.Y > Bottom Then
  279. Self.Y = Bottom
  280. Self.VelocityY = -Self.VelocityY
  281. Out = True
  282. End If
  283. Return Out
  284. End Method
  285. '#End Region
  286. '#Region Method: ApplyFriction
  287. Method ApplyFriction(Axis:Int)
  288. If ((Axis & AxisX) = AxisX) Then Self.VelocityX:*Self.Friction
  289. If ((Axis & AxisY) = AxisY) Then Self.VelocityY:*Self.Friction
  290. End Method
  291. '#End Region
  292. '#Region Method: ApplyFrictionX
  293. Method ApplyFrictionX()
  294. Self.VelocityX:*Self.Friction
  295. End Method
  296. '#End Region
  297. '#Region Method: ApplyFrictionY
  298. Method ApplyFrictionY()
  299. Self.VelocityY:*Self.Friction
  300. End Method
  301. '#End Region
  302. '#Region Method: Bounce
  303. Method Bounce(Planes:Int)
  304. If ((Planes & HorizontalPlane) = HorizontalPlane)
  305. Self.VelocityY = -Self.VelocityY
  306. Self.VelocityY:*Self.BounceCoefficientY
  307. End If
  308. If ((Planes & VerticalPlane) = VerticalPlane)
  309. Self.VelocityX = -Self.VelocityX
  310. Self.VelocityX:*Self.BounceCoefficientX
  311. End If
  312. End Method
  313. '#End Region
  314. '#Region Method: Draw
  315. Method Draw() Abstract
  316. '#End Region
  317. '#Region Method: PhysicsApplied
  318. Method PhysicsApplied() Abstract
  319. '#End Region
  320. '#Region Method: ApplyPhysics
  321. Method ApplyPhysics()
  322. ' Gravity
  323. Self.VelocityY:+Self.World.Gravity - (Self.World.Drag * Self.SurfaceArea)
  324. ' Wind
  325. Self.VelocityX:+(Self.World.Wind / Self.Weight) * Self.SurfaceArea
  326. ' Apply the magnets?
  327. If (Self.World.ApplyMagnets = True) And (Self.ApplyMagnets = True) Then
  328. Local Magnet:TMagnet
  329. For Magnet=EachIn Self.World.Magnets
  330. Local Pull:TPointD = Magnet.GetForces(Int(Self.X),Int(Self.Y))
  331. Self.VelocityX = Self.VelocityX + Pull.X
  332. Self.VelocityY = Self.VelocityY + Pull.Y
  333. Next
  334. End If
  335. ' Terminal Velocity
  336. If Self.VelocityX > Self.TerminalVelocityX Then Self.VelocityX = Self.TerminalVelocityX
  337. If Self.VelocityY > Self.TerminalVelocityY Then Self.VelocityY = Self.TerminalVelocityY
  338. If Self.VelocityX < -Self.TerminalVelocityX Then Self.VelocityX = -Self.TerminalVelocityX
  339. If Self.VelocityY < -Self.TerminalVelocityY Then Self.VelocityY = -Self.TerminalVelocityY
  340. ' Update
  341. Self.Y:+Self.VelocityY
  342. Self.X:+Self.VelocityX
  343. ' Raise the event...
  344. Self.PhysicsApplied()
  345. End Method
  346. '#End Region
  347. '#Region Method: SetVelocityFromAngle
  348. Method SetVelocityFromAngle(Angle!, Speed!)
  349. Self.VelocityX = Sin(Angle) * Speed
  350. Self.VelocityY = Cos(Angle) * Speed
  351. End Method
  352. '#End Region
  353. '#Region Method: IncreaseVelocityFromAngle
  354. Method IncreaseVelocityFromAngle(Angle!, Speed!)
  355. Self.VelocityX :+ Sin(Angle) * Speed
  356. Self.VelocityY :+ Cos(Angle) * Speed
  357. End Method
  358. '#End Region
  359. '#Region Method: Angle
  360. Method Angle!()
  361. Return TPhysicsUtility.DegreesBetweenPoints(Self.X, Self.Y, Self.X + Self.VelocityX, Self.Y + Self.VelocityY)
  362. End Method
  363. '#End Region
  364. End Type
  365. ' Particle Engine.
  366. Type TParticleEmitter Extends TLink
  367. '#Region Declarations
  368. Field World:TWorldPhysicsProvider
  369. Field X:Int, Y:Int
  370. Field Particles:TPhysicsProviderCollection = New TPhysicsProviderCollection
  371. '#End Region
  372. '#Region Method: Update
  373. Method Update()
  374. Self.Particles.ApplyPhysics()
  375. End Method
  376. '#End Region
  377. '#Region Method: UpdateWithFriction
  378. Method UpdateWithFriction(Axis:Int)
  379. Self.Particles.ApplyPhysicsAndFriction(Axis)
  380. End Method
  381. '#End Region
  382. '#Region Method: Draw
  383. Method Draw()
  384. Self.Particles.Draw()
  385. End Method
  386. '#End Region
  387. '#Region Method: UpdateAndDraw
  388. Method UpdateAndDraw()
  389. Self.Particles.ApplyPhysics()
  390. Self.Particles.Draw()
  391. End Method
  392. '#End Region
  393. '#Region Method: AddParticle
  394. Method AddParticle(Particle:IParticle)
  395. Self.Particles.AddLast(Particle)
  396. End Method
  397. '#End Region
  398. '#Region Method: RemoveParticle
  399. Method RemoveParticle(Particle:IParticle)
  400. Self.Particles.Remove(Particle)
  401. End Method
  402. '#End Region
  403. End Type
  404. Type IParticle Extends TPhysicsProvider
  405. Field Angle:Float
  406. Field Speed:Float
  407. Field Region:TRectangle
  408. Method Initialize() Abstract
  409. End Type
  410. Type TStaticParticle Extends IParticle
  411. '#Region Declarations
  412. Field LastX:Int
  413. Field LastY:Int
  414. Field Emitter:TParticleEmitter
  415. Field LifeTime:Int = -1
  416. Field LifeAmount:Int
  417. Field FadeSpeed:Float = -1.0
  418. Field Color[] = [255,255,255]
  419. Field FadeAmount:Float
  420. Field Size:Float = 1.0
  421. Field ActualRotation:Float = 0.0
  422. Field Rotation:Float = 0.0
  423. Field Graphic:TImage
  424. '#End Region
  425. '#Region Constructor
  426. Function Create:TStaticParticle(World:TWorldPhysicsProvider, Graphic:TImage, Region:TRectangle)
  427. Local Out:TStaticParticle = New TStaticParticle
  428. Out.Graphic = Graphic
  429. Out.Region = Region
  430. Return Out
  431. End Function
  432. '#End Region
  433. '#Region Method: Draw
  434. Method Draw()
  435. SetColor(Self.Color[0],Self.Color[1],Self.Color[2])
  436. SetScale(Self.Size, Self.Size)
  437. SetAlpha(1.0 - Self.FadeAmount)
  438. Self.ActualRotation :+ Self.Rotation
  439. SetRotation(Self.ActualRotation)
  440. DrawImage(Self.Graphic, Int(Self.X), Int(Self.Y))
  441. End Method
  442. '#End Region
  443. '#Region Method: Initialize
  444. Method Initialize()
  445. Self.SetVelocityFromAngle(Self.Angle, Self.Speed)
  446. End Method
  447. '#End Region
  448. '#Region Override: PhysicsApplied
  449. Method PhysicsApplied()
  450. Local ImageRect:TRectangle = New TRectangle
  451. ImageRect.Width = Self.Graphic.Width
  452. ImageRect.Height = Self.Graphic.Height
  453. ImageRect.X = Int(Self.X - Self.Graphic.handle_x)
  454. ImageRect.Y = Int(Self.Y - Self.Graphic.handle_y)
  455. Local RemoveThis:Int = False
  456. If Not Self.Region.IntersectsWith(ImageRect) Then
  457. RemoveThis = True
  458. Else
  459. If Self.LifeTime > -1 Then
  460. If Self.LifeAmount >= Self.LifeTime Then
  461. ' Start fade out?
  462. If Self.FadeSpeed > -1 Then
  463. Self.FadeAmount :+ FadeSpeed
  464. If Self.FadeAmount >= 1.0 Then
  465. RemoveThis = True
  466. EndIf
  467. Else
  468. RemoveThis = True
  469. EndIf
  470. Else
  471. Self.LifeAmount :+ 1 ' its lived a bit longer now.
  472. EndIf
  473. Else
  474. RemoveThis = True
  475. EndIf
  476. EndIf
  477. If RemoveThis Then
  478. Self.Emitter.RemoveParticle(Self)
  479. Self.Emitter = Null
  480. EndIf
  481. End Method
  482. '#End Region
  483. End Type
  484. Type TFountain Extends TParticleEmitter
  485. '#Region Declarations
  486. Field Region:TRectangle
  487. '#End Region
  488. '#Region Constructor
  489. Function Create:TFountain(World:TWorldPhysicsProvider, Region:TRectangle)
  490. Local Out:TFountain = New TFountain
  491. Out.World = World
  492. Out.Region = Region
  493. Return Out
  494. End Function
  495. '#End Region
  496. '#Region Method: AddStaticParticle
  497. Method AddStaticParticle:TStaticParticle(Image:TImage, Angle:Float, Velocity:Float, LifeTime:Int, FadeSpeed:Float, Color[])
  498. Local Particle:TStaticParticle = TStaticParticle.Create(Self.World, Image, Self.Region)
  499. Particle.X = Self.X
  500. Particle.Y = Self.Y
  501. Particle.Angle = Angle
  502. Particle.Speed = Velocity
  503. Particle.Emitter = Self
  504. Particle.FadeSpeed = FadeSpeed
  505. Particle.LifeTime = LifeTime
  506. Particle.Color = Color
  507. Particle.Initialize()
  508. Self.AddParticle(Particle)
  509. Return Particle
  510. End Method
  511. '#End Region
  512. End Type