MMF2 SPRINGCHAIN21F:\Web\UltimateWalrus\uwexamples\springchain2.mfa4(@0%%%%%%%%%%%%%%%%%%%%%%%%%%ATNFJArialAPMSASUMAGMI """)))UUUMMMBBB999|PP3f333f3333f3ffffff3f̙3ff333f333333333f33333333f33f3ff3f3f3f3333f33̙33333f333333f3333f3ffffff3f33ff3f3f3f3fff3ffffffffff3ffff̙fff3fffff3fff333f3f3ff3ff33f̙̙3̙ff̙̙̙3f̙3f333f3333f3ffffff3f̙3f3f3f333f3333f3ffffff3f̙3f3ffffffffff!___www*B00 h hpppph`@qxaxaxExpp`xÀI,0Qqqiph0$À$(mq4U444Uep`$IŽqUUuq$pHiÀ0YYYY888 xXDZmu8YY8U444UuuYyyyya`eÀm88Uqq4u8Y4ahÀDZ840Q4uY4aP,UMMIzizzzz {,{M{U<}}<yx8ax0uUQ ,m``E)}}Y $#W 51uO ~% ,;@= /&{m?S 5#P +*S}D 1U4_ Mk"8X )Th{> ){~L3jخvQ) 8WeO1  $&''&"  xAxAxAx㐂pppX$Ү0QqMAxhHÀiUUuUax`〆4YY8u$hu8U488puUQӎ0Ӽ<<$`axMUm)s͞c  `[  q= P2pO-AhppqmEumx4QӮq$#@#"B"!D!!D! F HJLNNPRTVXXZ\\^^`bbddfhhhjj l l n n p p r r r t t vvvxxxzzz||||~~~~~~~~||||zzzxxxvvv t t r r r p p n n l l jjhhhfddbb`^^\\ZXXVTRPNNLJH F !D!!D!"B"#@#$>$%<%%<%&:&(6()4)*2*+0+,.,.*./(/0&01$12"24455667788::;;= =??@@BBD}DE{EHuHJqJLmLNiNPePS_SU[UXUX[O[^I^bAbf9fk/kq#q~ ~{)KGKGKGKGKG )ˁˁˁˁˁ)#####)kkkkkkkkkkM )#'#'#'#'#'  ˁ  ˁˁˁˁˁˁˁ˃ ˅˃ˁˁˁˁˁˁˁˁˁˁˁˁˁ  ˁ ' 333 333333 333 333333333333333333333333333333 333333333333333333333333333333333333333333333333333333333333333333333 333 333 333333P)SSSSSSSSSS)ccccc)xx#' #' #'#'#'#' #'#'#'#'#'#'#'#' #'#' #' #'#'#'#'#' #' #'#'#'#'#'#'#' #'#' #' #'#'#'#'#'#' #'#'#'#'#'#'#' #'#'#'#'#'#'#'#'#' #'#'#' #'#'#'#' #'#'#'#'#'#'#'#'#'#'#'#'#' #'#'#'#'#'#'#'#'#' #'#'#' #'#'#'#'#'#'#'#'#' #'#'#'#'#'#'#'#'#' #'#' #'#'#'#'#'#'#'#'#' #' #'#'#'#'#'#'#' #'#' #'#'#'#'#'#'#'#'#'#'#'#'#'#'#'#'#' #'#' #'#'#'#'#'#'#'#' #' #'#'#'#'#'#'#'#' #'#' #' #' #'#'#'#'#'#' #'#'#' #'#'#'#'#'#'#' #'#'#' #'#'#'#'#'#'#'#' #'#'#'#'#'#' #'#'#'#'#' #'#'#'#' #'#'#'#'#'#'#' #'#'#'#'#'#'#'#'#'#' #'#'#'#'#' #'#'#'#'#'#'#'#'#'#'#'#'#'#'#'#' #'#'#'#' #'#'#'#'#'#'#'#'#'#'#'#'#' #'#'#'#'#'#'#'#'#'#' #'#'#'#'#'#'#'#' #'#' #'#' #'#'#'#'#' #' #'#'#'#'#'#'#'#' #'#'#'#' #'#'#'#'#'#' #'#'#'#' #'#'#'#' #'#'            SPRINGCHAIN2Sebastian Janisz20072&(%' &(%' &(%' &(%' &H&File&New F2Pass&word&Pause Ctrl+PPla&yers Ctrl+Y&Quit Alt+F4&OptionsPlay &samples Ctrl+SPlay &musics Ctrl+M&Hide the menu F8&Full Screen Alt+Enter&Help&Contents F1&About...  P Y S MpqswRKLBEDAEHSTVE6  !DNE  'YK)|`׮Frame 1DB{׻kϻ[ǻwwwcccSSSCCC333oSKC;{3k+Ww#G_7K'3 kWCw;k3_+S#C7g+K 3ssccSSGG;;33s++_##K7#ۧϏ{gWG7{+osccW WGK;;//ssKK##kS;#ۻÓoG#gc[WO;/s_K ;CϳÛscSwGg7W+K#ߧϏwwggSWCG3;w'/g#[K ; +ӃkgSK73# o_SC7'{cK7'o _##'#'#'#'##skkSS??ۧןϓLJsgߏ[ӃSsKgCW;K3?+{3#o+_OC3 '{k_OC3' w,??Active4IDFxFybigXbigYxvelyvelnewAngle Movement #1 Behavior #1KLBEDAEHSTVE"(  2($  SJBOActiveSpriteOIC0!DNE22 Chain 4INVERSE_AIR_FRICTION NODE_MASS CHAIN_GRAVITY prevXprevYnextXnextYWALL_BOUNCE_PERCENTAGE2 xCompPrev yCompPrevcurAngleSPRING_EQUILIBRIUM xCompNext yCompNext Movement #122String 2 Movement #1=Keeping the rope from retracting (Method 1): These nodes are given a "spring equilibrium" so that they push eachother apart if they get too close (in this case, 20 pixels). Click the mouse to see the springs. I realize this might not be the desired effect for most applications. Just wait till the next frame.String Movement #1*By Sebastian Janisz (UltimateWalrus), 2007Backdrop*pp ppp([J)|  Evtsx6" 2 j< 2$  d< 2$  dh.  2 . 2 . 2  :-chnfrc (-chnfrcN  24.chnfrc . 2 . 2  (-chnfrcN  24.chnfrc . 2 . 2 0(-chnfrc>  2$.chnfrc0  2 , 2  Z0(-chnfrc>  2$.chnfrc0  2 , 2   (-chnfrc>  2$.chnfrc0  2  2  h"??     0 (-chnfrc>  2$.chnfrc0  2 ,  2   (-chnfrcv 2^ ??     v 2 ^ ??     0 (-chnfrc>  2$.chnfrc0  2 , 2  Z0 (-chnfrc>  2$.chnfrc0  2 , 2   (-chnfrc>  2$.chnfrc0  2  2  h"??     0(-chnfrc>  2$.chnfrc0  2 ,  2  (-chnfrcv 2 ^ ??     v 2 ^ ??     L(-chnfrc>  2$.chnfrc 2|        2          h|l%"Arial$(-chnfrc>  2$.chnfrc4&  d4&  dT(-chnfrc>  2$.chnfrc8X   (-chnfrc>  2$.chnfrc$  ^V<    d   d   d   d @@ (-chnfrc>  2$.chnfrc$  ^V<    d   d   d   d >@A  @  2(  @  2(  .  2 .  2 T.  2 8 2  d8 2  d,.  2 , 2 l 2T    d:  2  d8 2  dl 2T    d:  2  d8 2  dl 2T    dRemsEvObActiveSpriteChainSprite String 2TextStringTextEvEd EvTs EvLsEvCs,!DNE!#% Frame 1DB{׻kϻ[ǻwwwcccSSSCCC333oSKC;{3k+Ww#G_7K'3 kWCw;k3_+S#C7g+K 3ssccSSGG;;33s++_##K7#ۧϏ{gWG7{+osccW WGK;;//ssKK##kS;#ۻÓoG#gc[WO;/s_K ;CϳÛscSwGg7W+K#ߧϏwwggSWCG3;w'/g#[K ; +ӃkgSK73# o_SC7'{cK7'o _##'#'#'#'##skkSS??ۧןϓLJsgߏ[ӃSsKgCW;K3?+{3#o+_OC3 '{k_OC3' w ??Active4IDFxFybigXbigYxvelyvelnewAngle Movement #1 Behavior #1KLBEDAEHSTVE"(  2($  SJBOActiveSpriteOIC0!DNE22  Chain 4INVERSE_AIR_FRICTION NODE_MASS CHAIN_GRAVITY prevXprevYnextXnextYWALL_BOUNCE_PERCENTAGE2 xCompPrev yCompPrevcurAngleSPRING_EQUILIBRIUM xCompNext yCompNextprevDistnextDist Movement #122String 2 Movement #1VKeeping the rope from retracting (Method 2): This uses the same "spring equilibrium" concept from the first method. However, force is only ever applied to a node if the length of the spring is _greater_ than the spring equilbrium --- not if it's lower. This is similar in effect to an elastic band. Click the mouse to see the "elastic."pp p J)  Evts 6" 2 j< 2$  d< 2$  dh.  2 . 2 . 2  :-chnfrc (-chnfrcN  24.chnfrc . 2 . 2  (-chnfrcN  24.chnfrc . 2 . 2 0(-chnfrc>  2$.chnfrc0  2 , 2  Z0(-chnfrc>  2$.chnfrc0  2 , 2   (-chnfrc>  2$.chnfrc0  2  2  h"??     0 (-chnfrc>  2$.chnfrc0  2 ,  2   (-chnfrcv 2^ ??     v 2 ^ ??     0 (-chnfrc>  2$.chnfrc0  2 , 2  Z0 (-chnfrc>  2$.chnfrc0  2 , 2   (-chnfrc>  2$.chnfrc0  2  2  h"??     0(-chnfrc>  2$.chnfrc0  2 ,  2  (-chnfrcv 2 ^ ??     v 2 ^ ??     h|l%"Arialhp|l%"Arialh|l%"Arial (-chnfrc>  2$.chnfrcP 28   t 2\     h0|l%"Arial (-chnfrc>  2$.chnfrc 2          2         h|l%"Arial (-chnfrc>  2$.chnfrc0  2  @  2(  @  2(   (-chnfrc>  2$.chnfrc0  2  @  2(   @  2(   h|l%"Arial$(-chnfrc>  2$.chnfrc4&  d4&  dT(-chnfrc>  2$.chnfrc8X   (-chnfrc>  2$.chnfrc$  ^V<    d   d   d   d @@ (-chnfrc>  2$.chnfrc$  ^V<    d   d   d   d >@A  @  2(  @  2(  .  2 .  2 T .  2 8 2  d8 2  d,! .  2 , 2 l 2T    d"!:  2  d8 2  dl 2T    d#":  2  d8 2  dl 2T    dRemsNow that we've got the spring equilibrium thing set up, it's simpler to implement the "elastic" method. Basically, what we want is this: if the spring goes past it's equilbrium, we want it to pull. However, if it gets behind its equilibrium (i.e. too short) then we don't want it to do anything --- the node can just move freely within that space. We do this by looking at how far away the other node is from the current one, and deciding whether we want to apply the force or not. Simple enough. >These are forces we know for sure we want acting on all nodes. Find the previous and next distance using the Pythagorean theorem. Note that this is a "big" distance, i.e. 100 times what it actually should be. ]Now, we just apply "prev" and "next" node forces based on whether the distance is far enough.EvObActiveSpriteChainSprite String 2TextEvEd EvTsEvLsEvCs,!DNE!#% Frame 1D{׻kϻ[ǻwwwcccSSSCCC333oSKC;{3k+Ww#G_7K'3 kWCw;k3_+S#C7g+K 3ssccSSGG;;33s++_##K7#ۧϏ{gWG7{+osccW WGK;;//ssKK##kS;#ۻÓoG#gc[WO;/s_K ;CϳÛscSwGg7W+K#ߧϏwwggSWCG3;w'/g#[K ; +ӃkgSK73# o_SC7'{cK7'o _##'#'#'#'##skkSS??ۧןϓLJsgߏ[ӃSsKgCW;K3?+{3#o+_OC3 '{k_OC3' w ??Active 4IDFxFybigXbigYxvelyvelnewAngle Movement #1 Behavior #1KLBEDAEHSTVE"(  2($  SJBOActiveSpriteOIC0!DNE22  Chain 4INVERSE_AIR_FRICTION NODE_MASS CHAIN_GRAVITY prevXprevYnextXnextYWALL_BOUNCE_PERCENTAGE2 xCompPrev yCompPrevcurAngleSPRING_EQUILIBRIUM xCompNext yCompNextprevDistnextDistdragID oldXMouse oldYMouse Movement #122String 2 Movement #1Try using the mouse button to hook the chain onto the "anchors." Drag the mouse over an anchor when you aren't holding the chain to release the anchor. Right click to see the "elastic" in the chain.Anchor 4IDnodeID Movement #1 Behavior #1KLBEDAEHSTVE "( .  2 "( SJBOAnchorSpriteOIC0!DNE22pp pp!QS  ! q Rak, 1Evtsr$6" 2 j< 2$  d< 2$  dh.  2 . 2 . 2  :-chnfrc (-chnfrcN  24.chnfrc . 2 . 2  (-chnfrcN  24.chnfrc . 2 . 2 0(-chnfrc>  2$.chnfrc0  2 , 2  Z0(-chnfrc>  2$.chnfrc0  2 , 2   (-chnfrc>  2$.chnfrc0  2  2  h"??     0 (-chnfrc>  2$.chnfrc0  2 ,  2   (-chnfrcv 2^ ??     v 2 ^ ??     0 (-chnfrc>  2$.chnfrc0  2 , 2  Z0 (-chnfrc>  2$.chnfrc0  2 , 2   (-chnfrc>  2$.chnfrc0  2  2  h"??     0(-chnfrc>  2$.chnfrc0  2 ,  2  (-chnfrcv 2 ^ ??     v 2 ^ ??     (-chnfrc>  2$.chnfrcP 28   t 2\     (-chnfrc>  2$.chnfrc 2          2         (-chnfrc>  2$.chnfrc0  2  @  2(  @  2(   (-chnfrc>  2$.chnfrc0  2  @  2(   @  2(   $(-chnfrc>  2$.chnfrc4&  d4&  dT(-chnfrc>  2$.chnfrc8X   (-chnfrc>  2$.chnfrc$  ^V<    d   d   d   d @@ (-chnfrc>  2$.chnfrc$  ^V<    d   d   d   d >@A & . 2  ,, 2 0  2 8 2  d8 2  dV 2>  dV 2>  d ( 2( 2 @  2(  @  2(  .  2 .  2 ,.  2 , 2 l 2T    d:  2  d8 2  dl 2T    d V  2<  dT 2<  dl 2T    dh! |l%"Arialh"!|l%"Arial#"6" 2 h0$#|l%"Arial6%$ ,  .  2 . 2 , 2 &% :-anchors h '&|l%"Arial('(-anchors>  2$.anchors0  2 < 2$  d< 2$  d, 2 , 2 Rems8Give each anchor an ID so they are easy to loop through:9If the user drags the mouse over an anchor, then just set the anchor's nodeID value to the dragID value. This is stored so that the anchor can grab the node every cycle. If the dragID is -1 (i.e., the user isn't dragging the chain), then the nodeID will be set to -1, meaning that the anchor will be turned off.First, grab the anchor we want. Then, grab the node that corresponds to its nodeID value. Then, set its bigX and bigY values (and set its xvel and yvel to 0, so that they won't build up over time).EvObActiveSpriteChainSprite String 2TextAnchorSpriteEvEdEvTsEvLsEvCs!DNE!#% Frame 1D]{׻kϻ[ǻwwwcccSSSCCC333oSKC;{3k+Ww#G_7K'3 kWCw;k3_+S#C7g+K 3ssccSSGG;;33s++_##K7#ۧϏ{gWG7{+osccW WGK;;//ssKK##kS;#ۻÓoG#gc[WO;/s_K ;CϳÛscSwGg7W+K#ߧϏwwggSWCG3;w'/g#[K ; +ӃkgSK73# o_SC7'{cK7'o _##'#'#'#'##skkSS??ۧןϓLJsgߏ[ӃSsKgCW;K3?+{3#o+_OC3 '{k_OC3' w??Active$4IDFxFybigXbigYxvelyvelnewAngle Movement #1 Behavior #1KLBEDAEHSTVE"(  2($  SJBOActiveSpriteOIC0!DNE22  Chain 4INVERSE_AIR_FRICTION NODE_MASS CHAIN_GRAVITY prevXprevYnextXnextYWALL_BOUNCE_PERCENTAGE2 xCompPrev yCompPrevcurAngleSPRING_EQUILIBRIUM xCompNext yCompNextprevDistnextDistdragID oldXMouse oldYMouse Movement #122String 2 Movement #1RThis frame doesn't introduce anything new, there's just something I want you to notice. If you've ever taken a highschool physics class, you probably learned about "wave physics." The basics of this is that, if two waves pass eachother, their amplitude is added to eachother; in the case that they are opposites, they cancel eachother out temporarily. Furthermore, if a wave hits a wall, it switches direction as well as switching amplitude. Finally (and this is more rope physics than wave physics), if you hang a rope between two points it will settle into a caternary. What amazes me is that all of these real-life physical laws are seen in this engine. You can try them out using these two anchor points (and holding enter to stop gravity). I didn't build them in --- they're simply a natural consequence of the way our universe works, even in an emulation. Just thought some of you might appreciate that :]Anchor 4IDnodeID Movement #1 Behavior #1KLBEDAEHSTVE "( .  2 "( SJBOAnchorSpriteOIC0!DNE22pp pp  ! ,)Evts#6" 2 j< 2$  d< 2$  dh.  2 . 2 . 2  :-chnfrc (-chnfrcN  24.chnfrc . 2 . 2  (-chnfrcN  24.chnfrc . 2 . 2 0(-chnfrc>  2$.chnfrc0  2 , 2  Z0(-chnfrc>  2$.chnfrc0  2 , 2   (-chnfrc>  2$.chnfrc0  2  2  h"??     0 (-chnfrc>  2$.chnfrc0  2 ,  2   (-chnfrcv 2^ ??     v 2 ^ ??     0 (-chnfrc>  2$.chnfrc0  2 , 2  Z0 (-chnfrc>  2$.chnfrc0  2 , 2   (-chnfrc>  2$.chnfrc0  2  2  h"??     0(-chnfrc>  2$.chnfrc0  2 ,  2  (-chnfrcv 2 ^ ??     v 2 ^ ??     h|l%"Arial(-chnfrc>  2$.chnfrc  P 28   t 2\     (-chnfrc>  2$.chnfrc  P 28   P 28   (-chnfrc>  2$.chnfrc 2          2         (-chnfrc>  2$.chnfrc0  2  @  2(  @  2(   (-chnfrc>  2$.chnfrc0  2  @  2(   @  2(   $(-chnfrc>  2$.chnfrc4&  d4&  dT(-chnfrc>  2$.chnfrc8X   (-chnfrc>  2$.chnfrc$  ^V<    d   d   d   d @@ (-chnfrc>  2$.chnfrc$  ^V<    d   d   d   d >@A & . 2  ,, 2 0  2 8 2  d8 2  dV 2>  dV 2>  d ( 2( 2 @  2(  @  2(  .  2 .  2 , .  2 , 2 l 2T    d! :  2  d8 2  dl 2T    d"!V  2<  dT 2<  dl 2T    d#"6" 2 6$# ,  .  2 . 2 , 2 %$ :-anchors &%(-anchors>  2$.anchors0  2 < 2$  d< 2$  d, 2 , 2 Rems,My super-lazy way of getting rid of gravityEvObActiveSpriteChainSprite String 2TextAnchorSpriteEvEd EvTsEvLsEvCs,!DNE!#% Frame 1{{׻kϻ[ǻwwwcccSSSCCC333oSKC;{3k+Ww#G_7K'3 kWCw;k3_+S#C7g+K 3ssccSSGG;;33s++_##K7#ۧϏ{gWG7{+osccW WGK;;//ssKK##kS;#ۻÓoG#gc[WO;/s_K ;CϳÛscSwGg7W+K#ߧϏwwggSWCG3;w'/g#[K ; +ӃkgSK73# o_SC7'{cK7'o _##'#'#'#'##skkSS??ۧןϓLJsgߏ[ӃSsKgCW;K3?+{3#o+_OC3 '{k_OC3' w??Active4 IDFxFybigXbigYxvelyvelnewAnglexOldyOldxNewyNew Movement #1 Behavior #1HKLBEDAEHSTVESJBOActiveSpriteOIC0!DNE22 Chain 4INVERSE_AIR_FRICTION NODE_MASS CHAIN_GRAVITY prevXprevYnextXnextYWALL_BOUNCE_PERCENTAGE2 xCompPrev yCompPrevcurAngleSPRING_EQUILIBRIUM xCompNext yCompNextprevDistnextDist collisionSide Movement #122String 2 Movement #1fThis frame demonstrates collision detection. Each individual node detects if it has collided with a backdrop, and reacts accordingly. The problem with this is that there is no collision detection inbetween the nodes, so obstacles can "stab" inbetween nodes, as is easily demonstrated by the long, thin backdrop. I can't think of a clean way of solving this problem in MMF without putting a heavy load on the CPU. If you are making a game where this would be a problem then I suggest you keep all your backdrop objects big and fat.Quick BackdropQuick Backdrop 2:Backdrop pp ppppg|\Ju  EvtsF-6" 2 j< 2$  d< 2$  dh.  2 . 2 . 2  :-chnfrc (-chnfrcN  24.chnfrc . 2 . 2  (-chnfrcN  24.chnfrc . 2 . 2 0(-chnfrc>  2$.chnfrc0  2 , 2  Z0(-chnfrc>  2$.chnfrc0  2 , 2   (-chnfrc>  2$.chnfrc0  2  2  h"??     0 (-chnfrc>  2$.chnfrc0  2 ,  2   (-chnfrcv 2^ ??     v 2 ^ ??     0 (-chnfrc>  2$.chnfrc0  2 , 2  Z0 (-chnfrc>  2$.chnfrc0  2 , 2   (-chnfrc>  2$.chnfrc0  2  2  h"??     0(-chnfrc>  2$.chnfrc0  2 ,  2  (-chnfrcv 2 ^ ??     v 2 ^ ??     (-chnfrc>  2$.chnfrcP 28   t 2\     (-chnfrc>  2$.chnfrc 2          2         (-chnfrc>  2$.chnfrc0  2  @  2(  @  2(   (-chnfrc>  2$.chnfrc0  2  @  2(   @  2(   h|l%"Arialh|l%"Arialh|l%"Arial(-chnfrc>  2$.chnfrc4&  d4&  d, 2 h|l%"ArialB(-chnfrc>  2$.chnfrc :-moveOut dh |l%"Arialh|l%"Arialh |l%"Arial(-moveOut>  2$.chnfrc RD .moveOut , 2 (-moveOut>  2$.chnfrc n`  .moveOut , 2  (-moveOut>  2$.chnfrc RD .moveOut RD .moveOut , 2 ! (-moveOut>  2$.chnfrc n`  .moveOut , 2 h"!|l%"Arial *#"(-moveOut>  2$.chnfrc RD .moveOut h$#|l%"Arial V%$(-moveOut>  2$.chnfrc &-moveOuth &%|l%"Arialh0'&|l%"Arial ('(-chnfrc>  2$.chnfrc.  2 .  2 l 2T    d)((-chnfrc>  2$.chnfrc.  2 .  2 l 2T    d*)(-chnfrc>  2$.chnfrc.  2 .  2 l 2T    d+*(-chnfrc>  2$.chnfrc.  2 .  2 l 2T    dh0,+|l%"Arial-,(-chnfrc>  2$.chnfrc.  2 < 2$  d< 2$  dh.-|l%"Arial/. @  2(  @  2(  .  2 .  2 T0/.  2 8 2  d8 2  d,10.  2 , 2 l 2T    d21:  2  d8 2  dl 2T    d32:  2  d8 2  dl 2T    dRems wThere are more accurate ways of doing this collision detection, but taking performance into account, I think this way works good enough. Here's how this is going to work: After the node is moved to its new location, we detect if it is overlapping a backdrop. If it is, we start a loop that will move it out of the backdrop. This loop will find a way out by looking right, left, down, and up in increasing magnitude. We will save a value to tell us which direction led to the resolution --- this lets us know what side the collision is on. Then, we can modify the velocity based on the side on which the collision occurred.gI set collisionSide to -1, so that I have an easy way to know if the "moveOut" loop ever even executed.$If it's overlapping, start the loop.-Loop "moveOut" ----------------------------------------------------------- Loop "moveOut"Here's an example of how this algorithm would help a node find its way out of a backdrop: Try 1 pixel to the right Try 1 pixel to the left Try 1 pixel down Try 1 pixel up Try 2 pixels right Try 2 pixels left Try 2 pixels down Try 2 pixels up Try 3 pixels right Try 3 pixels left No longer overlapping backdrop! Loop stops! Note that in this case, the last value that collisionSide is set to is 0.rcollisionSide is used to determine what side the node collided on. 0 is right, 1 is up, 2 is left, and 3 is down. Put it back where it was Check if it's done Now we look at what side the node collided on, and adjust its velocity accordingly. Note that this won't work well on slopes, etc. However, making "perfect" collision detection here is out of the scope of an MMF program, I think. Remember, we've been goofing around with the node's actual X and Y positions --- not the alterable values which store more accurate positions. We have to re-set these values if we've changed the X and Y (in which case the CollisionSide value would have been changed).EvObActiveSpriteChainSprite String 2TextEvEd EvTs EvLsEvCs,!DNE!#% Frame 1){׻kϻ[ǻwwwcccSSSCCC333oSKC;{3k+Ww#G_7K'3 kWCw;k3_+S#C7g+K 3ssccSSGG;;33s++_##K7#ۧϏ{gWG7{+osccW WGK;;//ssKK##kS;#ۻÓoG#gc[WO;/s_K ;CϳÛscSwGg7W+K#ߧϏwwggSWCG3;w'/g#[K ; +ӃkgSK73# o_SC7'{cK7'o _##'#'#'#'##skkSS??ۧןϓLJsgߏ[ӃSsKgCW;K3?+{3#o+_OC3 '{k_OC3' w??Active4 IDFxFybigXbigYxvelyvelnewAnglexOldyOldxNewyNew Movement #1 Behavior #1HKLBEDAEHSTVESJBOActiveSpriteOIC0!DNE22 Chain 4INVERSE_AIR_FRICTION NODE_MASS CHAIN_GRAVITY prevXprevYnextXnextYWALL_BOUNCE_PERCENTAGE2 xCompPrev yCompPrevcurAngleSPRING_EQUILIBRIUM xCompNext yCompNextprevDistnextDist collisionSide Movement #122String 2 Movement #1L%This frame shows collision detection with the sprites stretched and rotated, as usual. This works pretty nicely on big, fat backdrops like these. However, if you use the arrow keys, you can scroll over and see how embarrassingly badly it works on small objects. I don't plan on fixing this!Quick Backdrop 2  Backdrop 2 Quick Backdrop 3 Quick Backdrop 4  Quick Backdrop 5pp pppppp 6 ^u> 9?wFQC$L88 8 8 888888 EvtsL36" 2 j< 2$  d< 2$  dh.  2 . 2 . 2  :-chnfrc (-chnfrcN  24.chnfrc . 2 . 2  (-chnfrcN  24.chnfrc . 2 . 2 0(-chnfrc>  2$.chnfrc0  2 , 2  Z0(-chnfrc>  2$.chnfrc0  2 , 2   (-chnfrc>  2$.chnfrc0  2  2  h"??     0 (-chnfrc>  2$.chnfrc0  2 ,  2   (-chnfrcv 2^ ??     v 2 ^ ??     0 (-chnfrc>  2$.chnfrc0  2 , 2  Z0 (-chnfrc>  2$.chnfrc0  2 , 2   (-chnfrc>  2$.chnfrc0  2  2  h"??     0(-chnfrc>  2$.chnfrc0  2 ,  2  (-chnfrcv 2 ^ ??     v 2 ^ ??     (-chnfrc>  2$.chnfrcP 28   t 2\     (-chnfrc>  2$.chnfrc 2          2         (-chnfrc>  2$.chnfrc0  2  @  2(  @  2(   (-chnfrc>  2$.chnfrc0  2  @  2(   @  2(   h|l%"Arial(-chnfrc>  2$.chnfrc4&  d4&  d, 2 (-chnfrc>  2$.chnfrc8   :-moveOut d(-chnfrc>  2$.chnfrc.  2 .  2 l 2T    d(-chnfrc>  2$.chnfrc.  2 .  2 l 2T    d(-chnfrc>  2$.chnfrc.  2 .  2 l 2T    d(-chnfrc>  2$.chnfrc.  2 .  2 l 2T    d(-chnfrc>  2$.chnfrc.  2 < 2$  d< 2$  dh |l%"Arial(-moveOut>  2$.chnfrc8   RD .moveOut , 2 (-moveOut>  2$.chnfrc8   n`  .moveOut , 2  (-moveOut>  2$.chnfrc8   RD .moveOut RD .moveOut , 2 ! (-moveOut>  2$.chnfrc8   n`  .moveOut , 2 "!(-moveOut>  2$.chnfrc8   RD .moveOut .#"(-moveOut>  2$.chnfrc8   &-moveOuth $#|l%"Arialh%$|l%"Arialh &%|l%"Arial$'&(-chnfrc>  2$.chnfrc4&  d4&  dh ('|l%"Arial*)((-chnfrc>  2$.chnfrc6 &  d, 2 Z**)(-chnfrc>  2$.chnfrc6 &  d, 2 z+*(-chnfrc>  2$.chnfrc6 &  d 2 h"??  Y@B  Y@B*,+(-chnfrc>  2$.chnfrc6 &  d,  2 h0-,|l%"Arial.-(-chnfrc>  2$.chnfrc8X  ^V<    d   d   d   d @@ h/.|l%"Arial0/ @  2(  @  2(  .  2 .  2 T10.  2 8 2  d8 2  d,21.  2 , 2 l 2T    d32:  2  d8 2  dl 2T    d43:  2  d8 2  dl 2T    d54 'VH  65 %VH  Rems------------------------------ Loop "moveOut"-Loop "moveOut" -----------------------------For the sake of easier calculations, I set the X and Y position here. This doesn't really change anything, since the BigX and BigY values are what affect the main events.These next events find the angle from the node to the next node, so that it can be "stretched" to cover that distance. I'm not gonna go into detail about how this angle is found.Here's where I set the angle and X Scale of the object. I find the distance between the current node and the next node, and divide by 5 (since the object is 5 pixels wide). I also added 4 to the distance so that the objects would "fit together" better.EvObActiveSpriteChainSprite String 2TextEvEd EvTsEvLsEvCs,!DNE!#% Frame 1~P{׻kϻ[ǻwwwcccSSSCCC333oSKC;{3k+Ww#G_7K'3 kWCw;k3_+S#C7g+K 3ssccSSGG;;33s++_##K7#ۧϏ{gWG7{+osccW WGK;;//ssKK##kS;#ۻÓoG#gc[WO;/s_K ;CϳÛscSwGg7W+K#ߧϏwwggSWCG3;w'/g#[K ; +ӃkgSK73# o_SC7'{cK7'o _##'#'#'#'##skkSS??ۧןϓLJsgߏ[ӃSsKgCW;K3?+{3#o+_OC3 '{k_OC3' w ??Active4 IDFxFybigXbigYxvelyvelnewAnglexOldyOldxNewyNew Movement #1 Behavior #1HKLBEDAEHSTVESJBOActiveSpriteOIC0!DNE22  Chain 4INVERSE_AIR_FRICTION NODE_MASS CHAIN_GRAVITY prevXprevYnextXnextYWALL_BOUNCE_PERCENTAGE2 xCompPrev yCompPrevcurAngleSPRING_EQUILIBRIUM xCompNext yCompNextprevDistnextDist collisionSide chainNumberdragID oldXMouse oldYMouse Movement #122String 2 Movement #1&LHere's an example of multiple chains. The nodes are all part of the same logical "chain," but I put "breaks" in the chain by setting an internal flag.Chain splitter"4 Movement #122pp pp"Cp" "E"*F"9G"HH"WI"fJ"uK"L"M"N"O"P"Q"R"S"T" U"V")W"8X"GY"VZ"e["t\"]"^"_"`"a #&#*Evtsp:6" 2 j< 2$  d< 2$  dh.  2 . 2 . 2 h|l%"Arial  "# h0|l%"Arial>-chainColor `,-chainColorB  2(.chainColor$( 4 ,-chainColorB  2(.chainColor$  ,  2   :-chnfrc h |l%"Arialh |l%"Arial0 (-chnfrc>  2$.chnfrc. 2 . 2 h  |l%"Arial(-chnfrcN  24.chnfrc $  . 2 . 2 h|l%"Arial (-chnfrcN  24.chnfrc . 2 . 2 h |l%"Arial (-chnfrc>  2$.chnfrc$  . 2 . 2 h|l%"Arial 0(-chnfrc>  2$.chnfrc0  2 , 2  Z0(-chnfrc>  2$.chnfrc0  2 , 2  (-chnfrc>  2$.chnfrc0  2  2  h"??     0(-chnfrc>  2$.chnfrc0  2 ,  2  (-chnfrcv 2^ ??     v 2 ^ ??     0(-chnfrc>  2$.chnfrc0  2 , 2  Z0(-chnfrc>  2$.chnfrc0  2 , 2  (-chnfrc>  2$.chnfrc0  2  2  h"??     0(-chnfrc>  2$.chnfrc0  2 ,  2  (-chnfrcv 2 ^ ??     v 2 ^ ??     (-chnfrc>  2$.chnfrcP 28   t 2\      (-chnfrc>  2$.chnfrc 2          2         ! (-chnfrc>  2$.chnfrc0  2  @  2(  @  2(   "!(-chnfrc>  2$.chnfrc0  2  @  2(   @  2(   #"(-chnfrc>  2$.chnfrc4&  d4&  d, 2 $#(-chnfrc>  2$.chnfrc8   :-moveOut d%$(-chnfrc>  2$.chnfrc.  2 .  2 l 2T    d&%(-chnfrc>  2$.chnfrc.  2 .  2 l 2T    d'&(-chnfrc>  2$.chnfrc.  2 .  2 l 2T    d('(-chnfrc>  2$.chnfrc.  2 .  2 l 2T    d)((-chnfrc>  2$.chnfrc.  2 < 2$  d< 2$  dh *)|l%"Arial+*(-moveOut>  2$.chnfrc8   RD .moveOut , 2 ,+(-moveOut>  2$.chnfrc8   n`  .moveOut , 2 -,(-moveOut>  2$.chnfrc8   RD .moveOut RD .moveOut , 2 .-(-moveOut>  2$.chnfrc8   n`  .moveOut , 2 /.(-moveOut>  2$.chnfrc8   RD .moveOut .0/(-moveOut>  2$.chnfrc8   &-moveOuth 10|l%"Arial$21(-chnfrc>  2$.chnfrc4&  d4&  d*32(-chnfrc>  2$.chnfrc6 &  d, 2 Z*43(-chnfrc>  2$.chnfrc6 &  d, 2 z54(-chnfrc>  2$.chnfrc6 &  d 2 h"??  Y@B  Y@B*65(-chnfrc>  2$.chnfrc6 &  d,  2 76(-chnfrc>  2$.chnfrc$  8X  ^V<    d   d   d   d @@ Z87$  6X  >V?? 98 @  2(  @  2(  .  2 .  2 ,:9.  2 , 2 l 2T    d;::  2  d8 2  dl 2T    d<;V  2<  dT 2<  dl 2T    d=<& . 2 >= ,, 2 ?>0  2 8 2  d8 2  dV 2>  dV 2>  d@? ( 2( 2Rems ------------------------------ Loop "moveOut"-Loop "moveOut" ----------------------------- lFirst, we set the prev coordinates to that of the current node, in case the previous node has its Flag 0 on.If it turns out that the previous node didn't have its flag 0 on, then we reset the prev coordinates to what they are supposed to be.AHere, we set the next coordinates to what they're supposed to be.If it turns out that the current node has its flag 0 on, then we don't really want it to be affected by the next node --- so we fix it.>At the start of the frame, we launch a loop to color the individual chains. This works pretty simply: We just loop through every node, setting its frame the the "chainNumber" value. When we find a node that has its Flag 0 set on, we increment the "chainNumber" value so that the next node will be a different color.When a node has its flag 0 set, that means that it's detached from everything below it. This means two things: 1.) The node itself is not affected by the node below it. 2.) The node below it is not affected by it. It's easy to make a node not be affected by another node --- just set the prev/next X and Y values to the X and Y coordinate of the current node, and no force will be applied.EvObActiveSpriteChainSprite String 2TextChain splitterSpriteEvEd EvTs EvLsEvCs,!DNE!#% Frame 1%{׻kϻ[ǻwwwcccSSSCCC333oSKC;{3k+Ww#G_7K'3 kWCw;k3_+S#C7g+K 3ssccSSGG;;33s++_##K7#ۧϏ{gWG7{+osccW WGK;;//ssKK##kS;#ۻÓoG#gc[WO;/s_K ;CϳÛscSwGg7W+K#ߧϏwwggSWCG3;w'/g#[K ; +ӃkgSK73# o_SC7'{cK7'o _##'#'#'#'##skkSS??ۧןϓLJsgߏ[ӃSsKgCW;K3?+{3#o+_OC3 '{k_OC3' w&??Active!4 IDFxFybigXbigYxvelyvelnewAnglexOldyOldxNewyNew Movement #1 Behavior #1HKLBEDAEHSTVESJBOActiveSpriteOIC0!DNE22 Chain 4INVERSE_AIR_FRICTION NODE_MASS CHAIN_GRAVITY prevXprevYnextXnextYWALL_BOUNCE_PERCENTAGE2 xCompPrev yCompPrevcurAngleSPRING_EQUILIBRIUM xCompNext yCompNextprevDistnextDist collisionSide chainNumberdragID oldXMouse oldYMouse TEAR_DISTANCE Movement #122String 2 Movement #1&LThis chain will rip if the distance between any two nodes exceeds 150 pixels. I also gave you some anchors and backdrops to play with :)Anchor 4IDnodeID Movement #1 Behavior #1KLBEDAEHSTVE "( .  2 "( SJBOAnchorSpriteOIC0!DNE22 Quick Backdrop 5%pp ppp )  &" "E"*F"9G"HH"WI"fJ"uK"L"M"N"O"P"Q"R"S"T" U"V")W"8X"GY"VZ"e["t\"]"^"_"`"a )s(Dj! { 5 Evts96" 2 j< 2$  d< 2$  dh.  2 . 2 . 2  :-chnfrc 0(-chnfrc>  2$.chnfrc. 2 . 2 (-chnfrcN  24.chnfrc $  . 2 . 2  (-chnfrcN  24.chnfrc . 2 . 2  (-chnfrc>  2$.chnfrc$  . 2 . 2 0 (-chnfrc>  2$.chnfrc0  2 , 2  Z0 (-chnfrc>  2$.chnfrc0  2 , 2   (-chnfrc>  2$.chnfrc0  2  2  h"??     0 (-chnfrc>  2$.chnfrc0  2 ,  2   (-chnfrcv 2^ ??     v 2 ^ ??     0 (-chnfrc>  2$.chnfrc0  2 , 2  Z0(-chnfrc>  2$.chnfrc0  2 , 2  (-chnfrc>  2$.chnfrc0  2  2  h"??     0(-chnfrc>  2$.chnfrc0  2 ,  2  (-chnfrcv 2 ^ ??     v 2 ^ ??     (-chnfrc>  2$.chnfrcP 28   t 2\     (-chnfrc>  2$.chnfrc 2          2         (-chnfrc>  2$.chnfrc0  2  @  2(  @  2(   (-chnfrc>  2$.chnfrc0  2  @  2(   @  2(   (-chnfrc>  2$.chnfrc4&  d4&  d, 2 (-chnfrc>  2$.chnfrc8   :-moveOut d(-chnfrc>  2$.chnfrc.  2 .  2 l 2T    d(-chnfrc>  2$.chnfrc.  2 .  2 l 2T    d(-chnfrc>  2$.chnfrc.  2 .  2 l 2T    d(-chnfrc>  2$.chnfrc.  2 .  2 l 2T    d(-chnfrc>  2$.chnfrc.  2 < 2$  d< 2$  dh |l%"Arial(-moveOut>  2$.chnfrc8   RD .moveOut , 2  (-moveOut>  2$.chnfrc8   n`  .moveOut , 2 ! (-moveOut>  2$.chnfrc8   RD .moveOut RD .moveOut , 2 "!(-moveOut>  2$.chnfrc8   n`  .moveOut , 2 #"(-moveOut>  2$.chnfrc8   RD .moveOut .$#(-moveOut>  2$.chnfrc8   &-moveOuth %$|l%"Arial$&%(-chnfrc>  2$.chnfrc4&  d4&  d*'&(-chnfrc>  2$.chnfrc6 &  d, 2 Z*('(-chnfrc>  2$.chnfrc6 &  d, 2 z)((-chnfrc>  2$.chnfrc6 &  d 2 h"??  Y@B  Y@B**)(-chnfrc>  2$.chnfrc6 &  d,  2 h+*|l%"Arial hP,+|l%"ArialP-,(-chnfrc>  2$.chnfrc< 2$    d   d   d   d :.-(-chnfrc0  2 >  2$.chnfrc"# /.(-chnfrc>  2$.chnfrc$  8X  PV.  @@ Z0/$  6X  >V?? h10|l%"Arial 21 @  2(  @  2(  .  2 .  2 ,32.  2 , 2 l 2T    d43:  2  d8 2  dl 2T    d54V  2<  dT 2<  dl 2T    d65& . 2 76 ,, 2 870  2 8 2  d8 2  dV 2>  dV 2>  d98 ( 2( 2:96" 2 6;: ,  .  2 . 2 , 2 <; :-anchors =<(-anchors>  2$.anchors0  2 < 2$  d< 2$  d, 2 , 2 Rems------------------------------ Loop "moveOut"-Loop "moveOut" ----------------------------- This is actually very simple. Just see how far the next node is from the current one (we already do this to stretch the sprite). If this distance is greater than a certain value, set the node's Flag 0 on. That's it! ...Boy, this engine sure got complicated didn't it? :)EvObActiveSpriteChainSprite String 2TextAnchorSpriteEvEd EvTs EvLsEvCs,!DNE!#% Frame 1D{׻kϻ[ǻwwwcccSSSCCC333oSKC;{3k+Ww#G_7K'3 kWCw;k3_+S#C7g+K 3ssccSSGG;;33s++_##K7#ۧϏ{gWG7{+osccW WGK;;//ssKK##kS;#ۻÓoG#gc[WO;/s_K ;CϳÛscSwGg7W+K#ߧϏwwggSWCG3;w'/g#[K ; +ӃkgSK73# o_SC7'{cK7'o _##'#'#'#'##skkSS??ۧןϓLJsgߏ[ӃSsKgCW;K3?+{3#o+_OC3 '{k_OC3' w)??Active(4 IDFxFybigXbigYxvelyvelnewAngle addedMass Movement #1 Behavior #1KLBEDAEHSTVE"(  2($  SJBOActiveSpriteOIC0!DNE22 Chain 4INVERSE_AIR_FRICTION NODE_MASS CHAIN_GRAVITY prevXprevYnextXnextYWALL_BOUNCE_PERCENTAGE2 xCompPrev yCompPrevcurAngleSPRING_EQUILIBRIUM xCompNext yCompNextprevDistnextDistdragID oldXMouse oldYMouse Movement #122String 2 Movement #1Drag the chain onto the weights to attach it. Press enter to detach all the weights. This uses a similar method to the "anchors."Anchor'4IDnodeIDMASSxvelyvelbigXbigY Movement #1 Behavior #1G KLBEDAEHSTVEf "( .  2 "( j< 2$  d< 2$  d .  2  .  2 .  2 ,.  2 , 2 l 2T    d:  2  d8 2  dl 2T    dV  2<  dT 2<  dl 2T    dz  4&  d4&  dSJBOAnchorSpriteOIC0ChainSpriteOIC2vGMICLR ]4Mm4MQmEQm4MQmu4MQmu4MQmueEQ mueEq0 Q mueEq0 Q mueEq0 QmueEq0 QmueEq0 QmueEq 0 Q4MUU4MUU4MUU  2$.chnfrc0  2 , 2  Z0(-chnfrc>  2$.chnfrc0  2 , 2   (-chnfrc>  2$.chnfrc0  2  2  h"??     0 (-chnfrc>  2$.chnfrc0  2 ,  2   (-chnfrcv 2^ ??     v 2 ^ ??     0 (-chnfrc>  2$.chnfrc0  2 , 2  Z0 (-chnfrc>  2$.chnfrc0  2 , 2   (-chnfrc>  2$.chnfrc0  2  2  h"??     0(-chnfrc>  2$.chnfrc0  2 ,  2  (-chnfrcv 2 ^ ??     v 2 ^ ??     h|l%"Arialh |l%"Arial (-chnfrc>  2$.chnfrcP 28    2z      h|l%"Arial(-chnfrc>  2$.chnfrc 2          2         (-chnfrc>  2$.chnfrc0  2  @  2(  @  2(   (-chnfrc>  2$.chnfrc0  2  @  2(   @  2(   $(-chnfrc>  2$.chnfrc4&  d4&  dT(-chnfrc>  2$.chnfrc8X   (-chnfrc>  2$.chnfrc$  ^V<    d   d   d   d @@ (-chnfrc>  2$.chnfrc$  ^V<    d   d   d   d >@A & . 2  ,, 2 0  2 8 2  d8 2  dV 2>  dV 2>  d ( 2( 2h |l%"Arialh ! |l%"Arial"! ^  2F   ^  2F   .  2 .  2 h#"|l%"Arial,$#.  2 , 2 l 2T    d%$:  2  d8 2  dl 2T    d&%V  2<  dT 2<  dl 2T    d'&6" 2 (' ,  .  2 .  2 . 2 , 2 )( ,  .  2 .  2 . 2 , 2 h*)|l%"Arialh@+*|l%"Arial,+ , 2 -, :-anchors v.-(-anchors>  2$.anchors0  2 .  2 . 2 . 2 . 2 . 2 /.  , 2 h0/|l%"ArialRems  gI changed it from NODE_MASS( "Chain" ) to (NODE_MASS( "Chain" )+addedMass( "Active" )). Simple enough.pI did the same thing here: changed it from NODE_MASS( "Chain" ) to (NODE_MASS( "Chain" )+addedMass( "Active" )).I changed two things here: 1.) The value addedMass is used to indicate how much extra weight each individual node is carrying. 2.) The anchor sets its position to the node, instead of the other way around.EvObActiveSpriteChainSprite String 2TextAnchorSpriteEvEd EvTs+ -EvLsEvCs,!DNE!#% 08 ACHK f)9<@D