TransformedCollision.htm 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246
  1. <html xmlns:MSHelp="http://msdn.microsoft.com/mshelp" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:xanx="http://schemas.microsoft.com/developer/xanx/2005"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta name="save" content="history" /><title>Collision Series 3: 2D Collision with Transformed Objects</title>
  2. <style><!--
  3. /***********************************************************
  4. * SCRIPT-SUPPORTING STYLES
  5. ***********************************************************/
  6. /* Defines the userData cache persistence mechanism. */
  7. .userDataStyle
  8. {
  9. behavior: url(#default#userData);
  10. }
  11. /* Used to save the scroll bar position when navigating away from a page. */
  12. div.saveHistory
  13. {
  14. behavior: url(#default#saveHistory);
  15. }
  16. /* Formats the expand/collapse images for all collapsible regions. */
  17. img.toggle
  18. {
  19. border: 0;
  20. margin-right: 5;
  21. }
  22. /* Formats the Language filter drop-down image. */
  23. img#languageFilterImage
  24. {
  25. border: 0;
  26. margin-left: 0;
  27. vertical-align: middle;
  28. }
  29. /* Formats the Members Options filter drop-down image. */
  30. img#membersOptionsFilterImage
  31. {
  32. border: 0;
  33. margin-left: 0;
  34. vertical-align: middle;
  35. }
  36. /* Formats the Collapse All/Expand All images. */
  37. img#toggleAllImage
  38. {
  39. margin-left: 0;
  40. vertical-align: middle;
  41. }
  42. /* Supports XLinks */
  43. MSHelp\:link
  44. {
  45. text-decoration: underline;
  46. color: #0000ff;
  47. hoverColor: #3366ff;
  48. filterString: ;
  49. }
  50. body
  51. {
  52. background: #FFFFFF;
  53. color: #000000;
  54. font-family: Verdana;
  55. font-size: medium;
  56. font-style: normal;
  57. font-weight: normal;
  58. margin-top: 0;
  59. margin-bottom: 0;
  60. margin-left: 0;
  61. margin-right: 0;
  62. width: 100%;
  63. /*font-size: 110%;*/
  64. }
  65. div.section
  66. {
  67. margin-left: 15px;
  68. }
  69. div.hxnx5
  70. {
  71. margin-left: 1.5em;
  72. }
  73. /* Font for all headings */
  74. h1, h2, h3, h4, h5, h6
  75. {
  76. font-family: Verdana, Arial, Helvetica, sans-serif;
  77. margin-top: 18;
  78. margin-bottom: 8;
  79. font-weight: bold;
  80. }
  81. h1
  82. {
  83. font-size: 130%;
  84. color: #003399;
  85. }
  86. div#scrollyes h1 /* Changes font size for full-scrolling topic */
  87. {
  88. font-size: 150%;
  89. }
  90. h2
  91. {
  92. font-size: 122%;
  93. }
  94. h3
  95. {
  96. font-size: 115%;
  97. margin-top: 9;
  98. margin-bottom: 4;
  99. }
  100. h4
  101. {
  102. font-size: 115%;
  103. margin-top: 9;
  104. margin-bottom: 4;
  105. }
  106. h5
  107. {
  108. font-size: 100%;
  109. margin-top: 9;
  110. margin-bottom: 4;
  111. }
  112. h6
  113. {
  114. font-size: 100%;
  115. margin-top: 9;
  116. margin-bottom: 4;
  117. }
  118. ul p, ol p, dl p
  119. {
  120. margin-left: 0em;
  121. }
  122. p
  123. {
  124. margin-top: .6em;
  125. margin-bottom: .6em;
  126. }
  127. td p
  128. {
  129. margin-top: 0.0em;
  130. margin-bottom: 0.6em;
  131. }
  132. dd p
  133. {
  134. margin-top: 0.0em;
  135. margin-bottom: 0.6em;
  136. }
  137. .image
  138. {
  139. text-align: center;
  140. }
  141. dl
  142. {
  143. margin-top: 0em;
  144. margin-bottom: 1.3em;
  145. }
  146. dd
  147. {
  148. margin-bottom: 0em;
  149. margin-left: 1.5em;
  150. }
  151. dl.glossary dd
  152. {
  153. margin-bottom: 0em;
  154. margin-left: 1.5em;
  155. }
  156. dt
  157. {
  158. margin-top: .6em;
  159. margin-bottom: 1;
  160. }
  161. ul, ol
  162. {
  163. margin-top: 0.6em;
  164. margin-bottom: 0.6em;
  165. }
  166. ol
  167. {
  168. margin-left: 2.5em;
  169. }
  170. ul
  171. {
  172. list-style-type: disc;
  173. margin-left: 1.9em;
  174. }
  175. li
  176. {
  177. margin-bottom: 0.4em;
  178. }
  179. ul ol, ol ol
  180. {
  181. list-style-type: lower-alpha;
  182. }
  183. pre
  184. {
  185. margin-top: .6em;
  186. margin-bottom: .6em;
  187. font: 105% Lucida, mono;
  188. color: #000066;
  189. }
  190. code
  191. {
  192. font-family: Monospace, Courier New, Courier;
  193. font-size: 105%;
  194. color: #000066;
  195. }
  196. table.userdata td
  197. {
  198. background: #ffffff;
  199. background-color: #F5F5F5;
  200. border-color: #ffffff;
  201. border: none;
  202. }
  203. table.clsWarning
  204. {
  205. background: #ffffff;
  206. padding: 0px;
  207. margin: 0px;
  208. border: none;
  209. }
  210. table.clsWarning td
  211. {
  212. padding: 0px;
  213. margin: 0px;
  214. background: #ffffff;
  215. vertical-align: middle;
  216. font-size: 70%;
  217. }
  218. div#mainSection table
  219. {
  220. width: 95%;
  221. background: #ffffff;
  222. margin-top: 5px;
  223. margin-bottom: 5px;
  224. }
  225. div#mainSection table th
  226. {
  227. padding: 5px 6px;
  228. background: #EFEFF7;
  229. text-align: left;
  230. font-size: 70%;
  231. vertical-align: bottom;
  232. border-bottom: 1px solid #C8CDDE;
  233. }
  234. div#mainSection table td
  235. {
  236. padding: 5px 5px;
  237. background: #F7F7FF;
  238. vertical-align: top;
  239. font-size: 70%;
  240. border-bottom: 1px solid #D5D5D3;
  241. }
  242. div#syntaxCodeBlocks table th
  243. {
  244. padding: 1px 6px;
  245. color: #000066;
  246. }
  247. div#syntaxCodeBlocks table td
  248. {
  249. padding: 1px 5px;
  250. }
  251. /* Applies to the running header text in the first row of the upper table in the
  252. non-scrolling header region. */
  253. span#runningHeaderText
  254. {
  255. color: #003399;
  256. font-size: 90%;
  257. padding-left: 13;
  258. }
  259. /* Applies to the topic title in the second row of the upper table in the
  260. non-scrolling header region. */
  261. span#nsrTitle
  262. {
  263. color: #003399;
  264. font-size: 120%;
  265. font-weight: 600;
  266. padding-left: 13;
  267. }
  268. /* Applies to everything below the non-scrolling header region. */
  269. div#mainSection
  270. {
  271. font-size: 70%;
  272. width: 100%;
  273. }
  274. /* Applies to everything below the non-scrolling header region, minus the footer. */
  275. div#mainBody
  276. {
  277. font-size: 90%;
  278. margin-left: 15;
  279. margin-top: 10;
  280. padding-bottom: 20;
  281. }
  282. /* Adds right padding for all blocks in mainBody */
  283. div#mainBody p, div#mainBody ol, div#mainBody ul, div#mainBody dl
  284. {
  285. padding-right: 5;
  286. }
  287. div#mainBody div.alert, div#mainBody div.code, div#mainBody div.tableSection
  288. {
  289. width:98.9%;
  290. }
  291. div.alert p, div.code p
  292. {
  293. margin-top:5;
  294. margin-bottom:8;
  295. }
  296. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Begin Note Styles - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  297. div#mainSection div.alert table
  298. {
  299. border: 0;
  300. }
  301. div#mainSection div.alert table th
  302. {
  303. padding-top: 0;
  304. padding-bottom: 0;
  305. padding-left: 5;
  306. padding-right: 5;
  307. }
  308. div#mainSection div.alert table td
  309. {
  310. padding-left: 5;
  311. padding-right: 5;
  312. }
  313. img.note
  314. {
  315. border: 0;
  316. margin-left: 0;
  317. margin-right: 3;
  318. }
  319. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - End Note Styles - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  320. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Begin Non-scrolling Header Region Styles - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  321. /* Applies to the entire non-scrolling header region. */
  322. div#header
  323. {
  324. background-color: #D4DFFF;
  325. padding-top: 0;
  326. padding-bottom: 0;
  327. padding-left: 0;
  328. padding-right: 0;
  329. width: 100%;
  330. }
  331. /* Applies to both tables in the non-scrolling header region. */
  332. div#header table
  333. {
  334. margin-top: 0;
  335. margin-bottom: 0;
  336. border-bottom-color: #C8CDDE;
  337. border-bottom-style: solid;
  338. border-bottom-width: 1;
  339. background: #D4DFFF;
  340. width: 100%;
  341. }
  342. /* Applies to cells in both tables in the non-scrolling header region. */
  343. div#header table td
  344. {
  345. color: #0000FF;
  346. font-size: 70%;
  347. padding-right: 20;
  348. padding-top: 1;
  349. padding-bottom: 1;
  350. border: none;
  351. background: #D4DFFF;
  352. }
  353. /* Applies to the last row in the upper table of the non-scrolling header region. Text
  354. in this row includes See Also, Constructors, Methods, and Properties. */
  355. div#header table tr#headerTableRow3 td
  356. {
  357. padding-bottom: 2;
  358. padding-top: 5;
  359. padding-left: 15;
  360. }
  361. /* Applies to the lower table in the non-scrolling header region. Text in this table
  362. includes Collapse All/Expand All, Language Filter, and Members Options. */
  363. div#header table#bottomTable
  364. {
  365. border-top-color: #FFFFFF;
  366. border-top-style: solid;
  367. border-top-width: 1;
  368. text-align: left;
  369. padding-left: 15;
  370. }
  371. blockquote
  372. {
  373. margin-left: 3.8em;
  374. margin-right: 3.8em;
  375. margin-top: .6em;
  376. margin-bottom: .6em;
  377. }
  378. sup
  379. {
  380. text-decoration: none;
  381. font-size: smaller;
  382. }
  383. a:link
  384. {
  385. color: #0000FF;
  386. /* font-weight: bold */
  387. }
  388. a:visited
  389. {
  390. color: #0000AA;
  391. /* font-weight: bold */
  392. }
  393. a:hover
  394. {
  395. color: #3366FF;
  396. /* font-weight: bold */
  397. }
  398. .label
  399. {
  400. font-weight: bold;
  401. margin-top: 1em;
  402. margin-left: -26px;
  403. }
  404. .tl
  405. {
  406. margin-bottom: .75em;
  407. }
  408. .atl
  409. {
  410. padding-left: 1.5em;
  411. padding-bottom: .75em;
  412. }
  413. .cfe
  414. {
  415. font-weight: bold;
  416. }
  417. .mini
  418. {
  419. font-size: smaller;
  420. }
  421. .dt
  422. {
  423. margin-bottom: -.6em;
  424. }
  425. .indent
  426. {
  427. margin-left: 1.9em;
  428. margin-right: 1.9em;
  429. }
  430. .product
  431. {
  432. text-align: right;
  433. color: #333333;
  434. font-size: smaller;
  435. font-style: italic;
  436. }
  437. .buttonbarshade
  438. {
  439. position: relative;
  440. margin: 0;
  441. left: 0px;
  442. top: 2;
  443. width: 50%;
  444. height: 40px;
  445. }
  446. .buttonbartable
  447. {
  448. position: absolute;
  449. margin: 0;
  450. padding:0;
  451. border:0;
  452. left:0px;
  453. top: 2;
  454. width: 100%;
  455. height: 40px;
  456. }
  457. /* background color, font for header */
  458. table.buttonbartable td, table.buttonbarshade td
  459. {
  460. background: #ffffff; /*#5177B8; #80C615;*/
  461. border-left: 0px solid #80C615;
  462. margin: 0;
  463. padding: 0px 0px 0px 0px;
  464. font-family: Impact, sans-serif;
  465. font-size: 14pt;
  466. }
  467. table.buttonbartable td.button1
  468. {
  469. background: #5177B8; /*#80C615;*/;
  470. padding: 0;
  471. font-weight: bold;
  472. text-align: center;
  473. cursor: hand;
  474. }
  475. table.buttonbartable td.button2
  476. {
  477. background: #5177B8; /*#80C615;*/;
  478. font-weight: bold;
  479. text-align: center;
  480. }
  481. table.buttonbartable td.button3
  482. {
  483. background: #5177B8; /*#80C615;*/;
  484. font-weight: bold;
  485. text-align: center;
  486. }
  487. table.buttonbartable td.runninghead
  488. {
  489. padding-left: 0px;
  490. font-style: italic;
  491. text-align: left;
  492. }
  493. .version
  494. {
  495. text-align: left;
  496. color: #000000;
  497. margin-top: 3em;
  498. margin-left: -26px;
  499. font-size: smaller;
  500. font-style: italic;
  501. }
  502. .lang, .ilang
  503. {
  504. color: #0000ff;
  505. font: normal 7pt Arial, Helvetica, sans-serif;
  506. }
  507. div.langMenu
  508. {
  509. position: absolute;
  510. z-index: 1;
  511. width: 96pt;
  512. padding: 8pt;
  513. visibility: hidden;
  514. border: 1px solid #000000;
  515. background: #ffffd0;
  516. }
  517. div.langMenu ul
  518. {
  519. padding-left: 2em;
  520. margin-left: 0;
  521. }
  522. div.filtered
  523. {
  524. margin: 4pt 0 8pt -26px;
  525. padding: 4px 4px 8px 26px;
  526. width: 100%;
  527. border: 2px solid #aaaacc;
  528. background: #ffffff;
  529. }
  530. div.filtered2
  531. {
  532. margin: 4pt 0 8pt -26px;
  533. padding: 4px 4px 8px 26px;
  534. width: 100%;
  535. border: none;
  536. background: #ffffff;
  537. }
  538. div.filtered h1, div.filtered h2, div.filtered h3, div.filtered h4
  539. {
  540. margin-left: -22px;
  541. }
  542. div.filtered span.lang
  543. {
  544. position: relative;
  545. left: -22px;
  546. }
  547. div.reftip
  548. {
  549. position: absolute;
  550. z-index: 1;
  551. padding: 8pt;
  552. visibility: hidden;
  553. border: 1px solid #000000;
  554. background: #ffffd0;
  555. }
  556. a.synParam
  557. {
  558. color: #0000FF;
  559. /*color: #3F7800;*/
  560. /*color: #8DC54F;*/
  561. text-decoration: none;
  562. font-weight: normal;
  563. }
  564. a.synParam:hover
  565. {
  566. text-decoration: underline;
  567. font-weight: normal;
  568. }
  569. div.sapop
  570. {
  571. position: absolute;
  572. z-index: 1;
  573. left: 26px;
  574. width: 100%;
  575. padding: 10px 10px 10px 36px;
  576. visibility: hidden;
  577. border: 1px solid #000000;
  578. background: #ffffd0;
  579. }
  580. div.footer
  581. {
  582. width: 100%;
  583. border: none;
  584. background: #ffffff;
  585. margin-top: 18pt;
  586. padding-bottom: 12pt;
  587. color: #0000FF;
  588. /*color: #228B22; */
  589. text-align: center;
  590. font-size: 76%;
  591. }
  592. div.preliminary
  593. {
  594. margin-top: 8pt;
  595. padding-bottom: 12pt;
  596. color: #A0A0A0;
  597. }
  598. /* A procedure section. eg. 'To create a file', 'To add a value' */
  599. div.proc
  600. {
  601. margin-left: 0.5em;
  602. }
  603. /* The title of a 'procedure' section. */
  604. div.proc h3
  605. {
  606. font-family: Verdana, Arial, Helvetica, sans-serif;
  607. font-weight: bold;
  608. font-size: 115%;
  609. margin-top: 1em;
  610. margin-bottom: 0.4em;
  611. margin-left: -0.5em;
  612. color: #003399;
  613. }
  614. div.proc ul
  615. {
  616. margin-left: 1.5em;
  617. }
  618. div.proc ol
  619. {
  620. margin-left: 2.0em;
  621. }
  622. .note
  623. {
  624. margin-left: 14pt;
  625. margin-right: 12pt;
  626. }
  627. .indent1
  628. {
  629. margin-left: 12pt;
  630. }
  631. .indent2
  632. {
  633. margin-left: 24pt;
  634. }
  635. .indent3
  636. {
  637. margin-left: 36pt;
  638. }
  639. p.proch
  640. {
  641. padding-left: 16px;
  642. }
  643. p.proch img
  644. {
  645. position: relative;
  646. vertical-align: top;
  647. left: -18px;
  648. margin-right: -14px;
  649. margin-bottom: -18px;
  650. }
  651. div.clsPlatSpec
  652. {
  653. background-color:#FFF8DC;
  654. border-style:solid;
  655. border-width:1pt 0pt 0pt 1pt;
  656. border-color:#ffE4C4;
  657. margin-top:0.6em;
  658. width:100%;
  659. }
  660. /* Applies to the language labels in the Language Filter drop-down list. */
  661. .languageFilter
  662. {
  663. color: #0000FF;
  664. cursor:hand;
  665. text-decoration:underline;
  666. padding-bottom:4;
  667. }
  668. /* Dropdown areas */
  669. #languageSpan {
  670. position: absolute;
  671. visibility: hidden;
  672. border-style: solid;
  673. border-width: 1px;
  674. border-color: #C8CDDE;
  675. background: #d4dfff;
  676. padding: 4px;
  677. font-size: 70%;
  678. }
  679. #membersOptionsSpan {
  680. position: absolute;
  681. visibility: hidden;
  682. border-style: solid;
  683. border-width: 1px;
  684. border-color: #C8CDDE;
  685. background: #d4dfff;
  686. padding: 4px;
  687. font-size: 70%;
  688. }
  689. --></style>
  690. <xml>
  691. <MSHelp:TOCTitle Title="Collision Series 3: 2D Collision with Transformed Objects" />
  692. <MSHelp:RLTitle Title="Collision Series 3: 2D Collision with Transformed Objects" />
  693. <MSHelp:Keyword Index="A" Term="O:Microsoft.Xna.TransformedCollision" />
  694. <MSHelp:Keyword Index="A" Term="0958027c-f5ee-9acb-ac54-3bbe5fccc85e" />
  695. <MSHelp:Keyword Index="K" Term="Collision Series 3: 2D Collision with Transformed Objects" />
  696. <MSHelp:Attr Name="AssetID" Value="0958027c-f5ee-9acb-ac54-3bbe5fccc85e" />
  697. <MSHelp:Attr Name="Locale" Value="en-us" />
  698. <MSHelp:Attr Name="CommunityContent" Value="1" />
  699. <MSHelp:Attr Name="TopicType" Value="kbOrient" />
  700. </xml>
  701. </head><body><div id="mainSection"><div id="mainBody">
  702. <h1>Collision Series 3: 2D Collision with Transformed Objects</h1>
  703. This article explains how to perform per-pixel collision detection on sprites by using linear transformations such as rotation or scale.
  704. <div class="alert"><table cellspacing="0" cellpadding="0"><tr><th>Note </th></tr><tr><td>This tutorial builds on the code that you wrote during the previous tutorial, "2D Collision Tutorial 2: Per-Pixel." Follow the steps in the two previous tutorials before starting this one.</td></tr></table></div>
  705. <a id="ID2EN" name="ID2EN"> </a><h1 class="heading">Introduction</h1><div id="ID2EN" class="hxnx1">
  706. <p>In the previous tutorial, you enhanced your simple obstacle-avoidance game by adding per-pixel collisions, which are more accurate than the previously existing bounding rectangle test. The per-pixel technique presented in tutorial 2 accommodates only positioned sprites, without any other transformations. For many games, this is completely sufficient. If your game requires objects that are rotated, scaled, or otherwise linearly transformed, however, you are going to need a more sophisticated per-pixel collision test.</p>
  707. </div>
  708. <a id="ID2EU" name="ID2EU"> </a><h1 class="heading">Step 1: Create Rotating Obstacles</h1><div id="ID2EU" class="hxnx1">
  709. <p>For starters, the game needs objects that rotate! The SpinnerBlock.bmp texture in the Content directory of the companion code is a more interesting object for exploring rotation than our current triangular falling block.</p>
  710. <div class="proc"><h3 class="subHeading">Add the Spinner Block Art to Your Project</h3><div class="subSection">
  711. <ol>
  712. <li>Ensure that you can see the Solution Explorer for your project. If you cannot, on the <b>View</b> menu, click <b>Solution Explorer</b>. When the Solution Explorer appears, you see files associated with your project in a tree structure.</li>
  713. <li>Right-click the <b>Content project</b>, click <b>Add</b>, and then click <b>Existing Item</b>.</li>
  714. <li>In the dialog box that appears, browse to the path where the artwork is located, and select the SpinnerBlock.bmp texture. If you cannot see the textures, change the <b>Files of type</b> selection box to read <b>Content Pipeline Files</b>.</li>
  715. <li>Click <b>OK</b>.</li>
  716. </ol>
  717. </div></div>
  718. <div class="proc"><h3 class="subHeading">Modify the LoadContent Method to Use the New Art</h3><div class="subSection">
  719. <p>Locate the <code>LoadContent</code> method in the game class. Change the <code>blockTexture</code> assignment to reference the <code>SpinnerBlock</code> texture.</p>
  720. <pre>blockTexture = Content.Load&lt;Texture2D&gt;("SpinnerBlock");</pre>
  721. <p>If you compile and run the project just like this, the game should still work perfectly with the big plus-sign shaped blocks. Because the blocks are larger, they are harder to dodge, so you may wish to change the <code>BlockFallSpeed</code> constant from 2 to 1.</p>
  722. </div></div>
  723. <div class="proc"><h3 class="subHeading">Store Rotation with Each Block</h3><div class="subSection">
  724. <p>Previously, blocks were represented by a <b>Vector2</b> for their position, but now they also have a rotation. You need to create a structure to hold both pieces of data together for each block.</p>
  725. <ol>
  726. <li>
  727. In <b>Solution Explorer</b>, right-click the <b>project icon</b>, click <b>Add</b>, and then click <b>New Item</b>.
  728. </li>
  729. <li>
  730. In the dialog box that appears, select <b>Class</b>, and then enter the name <i>Block.cs</i>.
  731. </li>
  732. <li>
  733. Click <b>OK</b>.
  734. </li>
  735. </ol>
  736. <p>This creates a <b>Block</b> class to which two fields must be added, one for position and one for rotation. As before, the position field is a Vector2. The rotation field is a single float that represents a clockwise rotation around the block's origin, measured in radians. The complete Block.cs should resemble the following:</p>
  737. <div class="code"><span codeLanguage="CSharp"><table><tr><th>C# </th></tr><tr><td><pre><pre>
  738. using System;
  739. using Microsoft.Xna.Framework;
  740. namespace TransformedCollision
  741. {
  742. /// &lt;summary&gt;
  743. /// A falling and spinning object to be avoided.
  744. /// &lt;/summary&gt;
  745. class Block
  746. {
  747. public Vector2 Position;
  748. public float Rotation;
  749. }
  750. } </pre></pre></td></tr></table></span></div>
  751. <p>The existing code now needs to be adjusted to use the new <b>Block</b> class. Locate the <b>Block</b> field at the top of the game class and replace the <b>blockPositions</b> field with a <b>Blocks</b> field. Also, add a constant for the block rotation speed.</p>
  752. <div class="code"><span codeLanguage="CSharp"><table><tr><th>C# </th></tr><tr><td><pre><pre>
  753. List&lt;Block&gt; blocks = new List&lt;Block&gt;();
  754. const float BlockRotateSpeed = 0.005f; </pre></pre></td></tr></table></span></div>
  755. <p>At this point, the code will not compile because the <b>blockPositions</b> field no longer exists. Any mention of <b>blockPositions</b> needs to be updated to refer to <b>Blocks</b>. For example, <code>blockPositions[i]</code> becomes <code>blocks[i].Position</code>, and <code>foreach (Vector2 blockPosition in blockPositions)</code> becomes <code>foreach (Block block in blocks)</code>. Construction of a block also changes slightly. Instead of simply calling <code>Vector2 blockPosition = new Vector2(x, y)</code>, a block must be constructed by calling <code>Block newBlock = new Block()</code>, and then constructing the position by using <code>newBlock.Position = new Vector2(x, y)</code>. Before proceeding to the next section, make all the necessary adjustments by searching for references to <code>blockPositions</code>. The compiler will complain if you miss any, and you can cheat by looking at the finished code sample. When the game works as expected (still without rotation), continue to the next section.</p>
  756. </div></div>
  757. <div class="proc"><h3 class="subHeading">Update and Draw with Rotation</h3><div class="subSection">
  758. <p>Although there is now a place to store rotation with each block, the value is always zero! For diversity, each block should start with a random rotation, but for simplicity, each block will rotate with a constant speed. Modify the block spawn code in the <b>Update</b> method to match the following code.</p>
  759. <div class="code"><span codeLanguage="CSharp"><table><tr><th>C# </th></tr><tr><td><pre><pre>
  760. // Spawn new falling blocks
  761. if (random.NextDouble() &lt; BlockSpawnProbability)
  762. {
  763. Block newBlock = new Block();
  764. // at a random position just above the screen
  765. float x = (float)random.NextDouble() *
  766. (Window.ClientBounds.Width - blockTexture.Width);
  767. newBlock.Position = new Vector2(x, -blockTexture.Height);
  768. // with a random rotation
  769. newBlock.Rotation = (float)random.NextDouble() * MathHelper.TwoPi;
  770. blocks.Add(newBlock);
  771. } </pre></pre></td></tr></table></span></div>
  772. <p>Farther down in the <b>Update</b> method, inside the block update loop, the rotation value needs to be animated. The new line appears in bold.</p>
  773. <div class="code"><span codeLanguage="CSharp"><table><tr><th>C# </th></tr><tr><td><pre><pre>
  774. // Animate this block falling
  775. blocks[i].Position += new Vector2(0.0f, BlockFallSpeed);
  776. <b>blocks[i].Rotation += BlockRotateSpeed;</b>
  777. </pre></pre></td></tr></table></span></div>
  778. <p>Now the blocks are rotating, but the draw code does not have any knowledge of this. In the <b>Draw</b> method, modify the draw blocks loop to match the following code.</p>
  779. <div class="code"><span codeLanguage="CSharp"><table><tr><th>C# </th></tr><tr><td><pre><pre>
  780. // Draw blocks
  781. foreach (Block block in blocks)
  782. {
  783. spriteBatch.Draw(blockTexture, block.Position, null, Color.White,
  784. block.Rotation, Vector2.Zero, 1.0f, SpriteEffects.None, 0.0f);
  785. }</pre></pre></td></tr></table></span></div>
  786. <p>If you compile and run, you will notice two problems. First, as you would expect, the collision does not work correctly. Second, the blocks are rotating around their top left corners! We will get to the first problem shortly, but we can solve the second problem quite easily, by moving the block origin from <i>Vector2.Zero</i> to the center of the block texture. Add the following field to the top of the game class.</p>
  787. <pre>Vector2 blockOrigin;</pre>
  788. <p>Immediately after loading the block texture in the <b>LoadContent</b> method, initialize <b>blockOrigin</b> to be centered.</p>
  789. <div class="code"><span codeLanguage="CSharp"><table><tr><th>C# </th></tr><tr><td><pre><pre>
  790. // Calculate the block origin
  791. blockOrigin =
  792. new Vector2(blockTexture.Width / 2, blockTexture.Height / 2);</pre></pre></td></tr></table></span></div>
  793. <p>And modify the <b>Draw</b> method to use this new field.</p>
  794. <div class="code"><span codeLanguage="CSharp"><table><tr><th>C# </th></tr><tr><td><pre><pre>
  795. // Draw blocks
  796. foreach (Block block in blocks)
  797. {
  798. spriteBatch.Draw(blockTexture, block.Position, null, Color.White,
  799. block.Rotation, <b>blockOrigin</b>, 1.0f, SpriteEffects.None, 0.0f);
  800. }</pre></pre></td></tr></table></span></div>
  801. <p>Compile and run again. The falling objects rotate correctly as they fall, but you may notice that there is yet another problem! The blocks disappear with a pop when their origin passes across the bottom edge of the window. This is also a simple fix. We need to account for the block origin when we detect removal of blocks from the screen. Modify the block update loop in the <b>Update</b> method. New code is in bold.</p>
  802. <div class="code"><span codeLanguage="CSharp"><table><tr><th>C# </th></tr><tr><td><pre><pre>
  803. // Remove this block if it has fallen off the screen
  804. if (block.Position.Y &gt; Window.ClientBounds.Height <b>+ blockOrigin.Length()</b>)
  805. </pre></pre></td></tr></table></span></div>
  806. <p>Finally, if you compile and run, everything looks perfect. Except, of course, the collision detection does not work correctly.</p>
  807. </div></div>
  808. </div>
  809. <a id="ID2EKAAC" name="ID2EKAAC"> </a><h1 class="heading">Step 2: Per-Pixel Collision with Transforms</h1><div id="ID2EKAAC" class="hxnx1">
  810. <p>Recall from the previous tutorial that a per-pixel collision test must compare every potentially colliding pixel from both sprites. Consider the following transformed sprites.</p>
  811. <img src="World.png" />
  812. <p>Both the red plus sign, called sprite A, and the blue slash, called sprite B, are transformed by more than just a simple translation. Both sprites are rotated, and sprite B is also scaled—notice how the pixel squares in sprite B are larger than in sprite A. To determine if the two sprites are colliding, we can test every pixel of A to see if it collides with a pixel of B. To do this, we need a way to take a pixel from sprite A and find the overlapping pixel in sprite B.</p>
  813. <div class="alert"><table cellspacing="0" cellpadding="0"><tr><th>Note </th></tr><tr><td>
  814. It helps to understand matrices, though such knowledge is not required. For the purposes of this tutorial, you need to know only that you can use a matrix to represent a transformation such as scaling, rotation, or translation. You can multiply transformation matrices together to perform multiple transformations in sequence. You can use a matrix to transform a vector from one linear coordinate system to another. The inverse of the matrix transforms back to the original coordinate system. We will use the XNA Framework’s <b>Vector2.Transform</b> method to apply a matrix transformation to a vector.</td></tr></table></div>
  815. <p>You can simplify the problem by working from a perspective where sprite A is completely untransformed. This view uses a coordinate system that is local to sprite A. In other words, in this view, positive X is always one pixel directly to the right, and positive Y is always one pixel directly above. In the following illustration, both sprites have the same position and orientation relative to each other, but the local coordinate system results in a much simpler algorithm.</p>
  816. <img src="LocalToA.png" />
  817. <p>To accomplish this, consider the following illustration.</p>
  818. <img src="CoordinateSystems.png" />
  819. <p>The local coordinate systems of both sprites are relative to the world coordinate system. Points that are local to sprite A can be made local to sprite B by first passing through world space and then into the local space of sprite B. The first operation is performed by the transformation matrix of sprite A. The second operation is performed by the inverse of the tranformation matrix of sprite B.</p>
  820. <pre>Matrix transformAToB = transformA * Matrix.Invert(transformB);</pre>
  821. <p>Iterating over every pixel in A and transforming into B yields a point with fractional values that can be rounded to the nearest integer. If this integer lies within the bounds of B, the pixel is compared for collision against the original pixel in A.</p>
  822. <p>The full method code follows. Add it to your game class.</p>
  823. <div class="code"><span codeLanguage="CSharp"><table><tr><th>C# </th></tr><tr><td><pre><pre>
  824. /// &lt;summary&gt;
  825. /// Determines if there is overlap of the non-transparent pixels between two
  826. /// sprites.
  827. /// &lt;/summary&gt;
  828. /// &lt;param name="transformA"&gt;World transform of the first sprite.&lt;/param&gt;
  829. /// &lt;param name="widthA"&gt;Width of the first sprite's texture.&lt;/param&gt;
  830. /// &lt;param name="heightA"&gt;Height of the first sprite's texture.&lt;/param&gt;
  831. /// &lt;param name="dataA"&gt;Pixel color data of the first sprite.&lt;/param&gt;
  832. /// &lt;param name="transformB"&gt;World transform of the second sprite.&lt;/param&gt;
  833. /// &lt;param name="widthB"&gt;Width of the second sprite's texture.&lt;/param&gt;
  834. /// &lt;param name="heightB"&gt;Height of the second sprite's texture.&lt;/param&gt;
  835. /// &lt;param name="dataB"&gt;Pixel color data of the second sprite.&lt;/param&gt;
  836. /// &lt;returns&gt;True if non-transparent pixels overlap; false otherwise&lt;/returns&gt;
  837. static bool IntersectPixels(
  838. Matrix transformA, int widthA, int heightA, Color[] dataA,
  839. Matrix transformB, int widthB, int heightB, Color[] dataB)
  840. {
  841. // Calculate a matrix which transforms from A's local space into
  842. // world space and then into B's local space
  843. Matrix transformAToB = transformA * Matrix.Invert(transformB);
  844. // For each row of pixels in A
  845. for (int yA = 0; yA &lt; heightA; yA++)
  846. {
  847. // For each pixel in this row
  848. for (int xA = 0; xA &lt; widthA; xA++)
  849. {
  850. // Calculate this pixel's location in B
  851. Vector2 positionInB =
  852. Vector2.Transform(new Vector2(xA, yA), transformAToB);
  853. // Round to the nearest pixel
  854. int xB = (int)Math.Round(positionInB.X);
  855. int yB = (int)Math.Round(positionInB.Y);
  856. // If the pixel lies within the bounds of B
  857. if (0 &lt;= xB &amp;&amp; xB &lt; widthB &amp;&amp;
  858. 0 &lt;= yB &amp;&amp; yB &lt; heightB)
  859. {
  860. // Get the colors of the overlapping pixels
  861. Color colorA = dataA[xA + yA * widthA];
  862. Color colorB = dataB[xB + yB * widthB];
  863. // If both pixels are not completely transparent,
  864. if (colorA.A != 0 &amp;&amp; colorB.A != 0)
  865. {
  866. // then an intersection has been found
  867. return true;
  868. }
  869. }
  870. }
  871. }
  872. // No intersection found
  873. return false;
  874. }</pre></pre></td></tr></table></span></div>
  875. <a id="ID2E1EAC" name="ID2E1EAC"> </a><h2 class="subHeading">Test Application</h2><div id="ID2E1EAC" class="hxnx2">
  876. <p>This algorithm is likely to be dauntingly complex the first time you see it. To wrap your head around the concept of local space, take some time to try the test application in the TransformedCollisionTest subdirectory. The test application presents two sprites, the black letters F and R (chosen because they have distinguishable orientation) in world space. The same two sprites are gray and represented in the local space of the F sprite. The dots represent the origins of the sprites. You will see that transforming the black R yields an analogous transform in the gray R, but transforming F yields an inverse transform in the gray R. The gray F never moves!</p>
  877. <a id="ID2EAFAC" name="ID2EAFAC"> </a><h4 class="subHeading">Test Application Controls</h4><div id="ID2EAFAC" class="hxnx3">
  878. <table>
  879. <tr>
  880. <th>Action</th>
  881. <th>Keyboard control</th>
  882. <th>Gamepad control</th>
  883. </tr>
  884. <tr>
  885. <td>Select F</td>
  886. <td>Hold left mouse button</td>
  887. <td>Hold left trigger</td>
  888. </tr>
  889. <tr>
  890. <td>Select R</td>
  891. <td>Hold right mouse button</td>
  892. <td>Hold right trigger</td>
  893. </tr>
  894. <tr>
  895. <td>Move selected object</td>
  896. <td>Move mouse</td>
  897. <td>Left thumb stick</td>
  898. </tr>
  899. <tr>
  900. <td>Rotate selected object</td>
  901. <td>LEFT and RIGHT ARROW keys or scroll mouse wheel</td>
  902. <td>Right and left on the right thumb stick</td>
  903. </tr>
  904. <tr>
  905. <td>Scale selected object</td>
  906. <td>UP and DOWN ARROW keys or hold CTRL and scroll mouse wheel</td>
  907. <td>Up and down on the right thumb stick</td>
  908. </tr>
  909. <tr>
  910. <td>Select origin of F</td>
  911. <td>Hold ALT and left mouse button</td>
  912. <td>Hold left shoulder button</td>
  913. </tr>
  914. <tr>
  915. <td>Select origin of R</td>
  916. <td>Hold ALT and right mouse button</td>
  917. <td>Hold right shoulder button</td>
  918. </tr>
  919. </table>
  920. </div>
  921. </div>
  922. </div>
  923. <a id="ID2ESHAC" name="ID2ESHAC"> </a><h1 class="heading">Step 3: Invoke the new Collision Test</h1><div id="ID2ESHAC" class="hxnx1">
  924. <p>To use the new intersection method, transformation matrices must be constructed for the person and for the falling blocks. The <b>Matrix</b> structure of the XNA Framework provides several static helper methods to create matrices for transformations that can be concatenated with multiplications. Add the bold lines to the <b>Update</b> method to calculate the person matrix solely from the person position.</p>
  925. <div class="code"><span codeLanguage="CSharp"><table><tr><th>C# </th></tr><tr><td><pre><pre>
  926. // Move the player left and right with arrow keys or D-pad
  927. if (keyboard.IsKeyDown(Keys.Left) ||
  928. gamePad.DPad.Left == ButtonState.Pressed)
  929. {
  930. personPosition.X -= PersonMoveSpeed;
  931. }
  932. if (keyboard.IsKeyDown(Keys.Right) ||
  933. gamePad.DPad.Right == ButtonState.Pressed)
  934. {
  935. personPosition.X += PersonMoveSpeed;
  936. }
  937. // Prevent the person from moving off of the screen
  938. personPosition.X = MathHelper.Clamp(personPosition.X,
  939. safeBounds.Left, safeBounds.Right - personTexture.Width);
  940. <b>
  941. // Update the person's transform
  942. Matrix personTransform =
  943. Matrix.CreateTranslation(new Vector3(personPosition, 0.0f));
  944. </b>
  945. </pre></pre></td></tr></table></span></div>
  946. <p>Inside the block update loop, calculate the block transformation matrix. Be sure to account for its position, origin, and rotation. New code appears in bold.</p>
  947. <div class="code"><span codeLanguage="CSharp"><table><tr><th>C# </th></tr><tr><td><pre><pre>
  948. // Build the block's transform
  949. <b>Matrix blockTransform =
  950. Matrix.CreateTranslation(new Vector3(-blockOrigin, 0.0f)) *
  951. Matrix.CreateRotationZ(blocks[i].Rotation) *
  952. Matrix.CreateTranslation(new Vector3(blocks[i].Position, 0.0f));</b>
  953. // Check collision with person
  954. if (IntersectPixels(<b>personTransform, personTexture.Width,
  955. personTexture.Height, personTextureData,
  956. blockTransform, blockTexture.Width,
  957. blockTexture.Height, blockTextureData</b>))
  958. {
  959. personHit = true;
  960. } </pre></pre></td></tr></table></span></div>
  961. <p>Compile and run the game. Everything should work exactly as expected now!</p>
  962. </div>
  963. <a id="ID2EQIAC" name="ID2EQIAC"> </a><h1 class="heading">Going Beyond: Optimize</h1><div id="ID2EQIAC" class="hxnx1">
  964. <p>Two key optimizations should be performed. The first optimization is to perform a bounding rectangle collision test before invoking the per-pixel test. If the bounding rectangles do not intersect, then it is impossible for any pixels to intersect, so do not even bother to check. The second optimization is more complex, and is discussed in a later section.</p>
  965. <a id="ID2EWIAC" name="ID2EWIAC"> </a><h2 class="subHeading">Bounding Rectangles for Transformed Sprites</h2><div id="ID2EWIAC" class="hxnx2">
  966. <p>Sprites that are only positioned have trivially calculated bounding rectangles, but transformed sprites are slightly more complex. When a sprite is transformed, so is its bounding rectangle. Unfortunately, the new rectangle is not necessarily axis-aligned. An axis-aligned rectangle can be formed by creating a rectangle that bounds the four corners of the transformed rectangle.</p>
  967. <img src="BoundingRectangles.png" />
  968. <p>Add the following method to your game class.</p>
  969. <div class="code"><span codeLanguage="CSharp"><table><tr><th>C# </th></tr><tr><td><pre><pre>
  970. /// &lt;summary&gt;
  971. /// Calculates an axis aligned rectangle which fully contains an arbitrarily
  972. /// transformed axis aligned rectangle.
  973. /// &lt;/summary&gt;
  974. /// &lt;param name="rectangle"&gt;Original bounding rectangle.&lt;/param&gt;
  975. /// &lt;param name="transform"&gt;World transform of the rectangle.&lt;/param&gt;
  976. /// &lt;returns&gt;A new rectangle which contains the trasnformed rectangle.&lt;/returns&gt;
  977. public static Rectangle CalculateBoundingRectangle(Rectangle rectangle, Matrix transform)
  978. {
  979. // Get all four corners in local space
  980. Vector2 leftTop = new Vector2(rectangle.Left, rectangle.Top);
  981. Vector2 rightTop = new Vector2(rectangle.Right, rectangle.Top);
  982. Vector2 leftBottom = new Vector2(rectangle.Left, rectangle.Bottom);
  983. Vector2 rightBottom = new Vector2(rectangle.Right, rectangle.Bottom);
  984. // Transform all four corners into work space
  985. Vector2.Transform(ref leftTop, ref transform, out leftTop);
  986. Vector2.Transform(ref rightTop, ref transform, out rightTop);
  987. Vector2.Transform(ref leftBottom, ref transform, out leftBottom);
  988. Vector2.Transform(ref rightBottom, ref transform, out rightBottom);
  989. // Find the minimum and maximum extents of the rectangle in world space
  990. Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop),
  991. Vector2.Min(leftBottom, rightBottom));
  992. Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop),
  993. Vector2.Max(leftBottom, rightBottom));
  994. // Return as a rectangle
  995. return new Rectangle((int)min.X, (int)min.Y,
  996. (int)(max.X - min.X), (int)(max.Y - min.Y));
  997. } </pre></pre></td></tr></table></span></div>
  998. <p>In the <b>Update</b> method, check for intersection of the axis-aligned block rectangle with the player rectangle by adding the bold lines.</p>
  999. <div class="code"><span codeLanguage="CSharp"><table><tr><th>C# </th></tr><tr><td><pre><pre>
  1000. <b>// Calculate the bounding rectangle of this block in world space
  1001. Rectangle blockRectangle = CalculateBoundingRectangle(
  1002. new Rectangle(0, 0, blockTexture.Width, blockTexture.Height),
  1003. blockTransform);
  1004. // The per-pixel check is expensive, so check the bounding rectangles
  1005. // first to prevent testing pixels when collisions are impossible.
  1006. if (personRectangle.Intersects(blockRectangle))</b>
  1007. {
  1008. // Check collision with person
  1009. if (IntersectPixels(personTransform, personTexture.Width,
  1010. personTexture.Height, personTextureData,
  1011. blockTransform, blockTexture.Width,
  1012. blockTexture.Height, blockTextureData))
  1013. {
  1014. personHit = true;
  1015. }
  1016. }</pre></pre></td></tr></table></span></div>
  1017. </div>
  1018. <a id="ID2EXKAC" name="ID2EXKAC"> </a><h2 class="subHeading">Eliminate the Per-Pixel Transformation</h2><div id="ID2EXKAC" class="hxnx2">
  1019. <p>Currently, the <b>IntersectPixels</b> method transforms every pixel of sprite A into the local space of sprite B. You can get a substantial speed boost by recognizing that each pixel in both sprites is a uniform displacement from the previous pixel. Only the first pixel of sprite A needs to be transformed into the coordinate space of sprite B. You can infer subsequent pixels by using only a simple translation.</p>
  1020. <p>Examine the following illustrations. Notice that advancing one pixel along the local x axis of sprite A results in an analogous movement along an arbitrary axis in the local coordinates of sprite B. This analogous movement vector can be determined by transforming the unit X vector from the local space of sprite A local space into the local space of sprite B without translation. Although these illustrations depict the unit X vector, the same can be done for the unit Y vector. Transformations without translation are performed by the <b>Vector2.TransformNormal</b> method.</p>
  1021. <img src="LocalToAWithStep.png" />
  1022. <img src="LocalToBWithStep.png" />
  1023. <p>For each iteration through the pixels of sprite A, the analogous position in sprite B is remembered. Each step in sprite A can be replicated in sprite B by simply adding the step vectors to the current position in sprite B.</p>
  1024. <p>The optimized <b>IntersectPixels</b> method follows.</p>
  1025. <div class="code"><span codeLanguage="CSharp"><table><tr><th>C# </th></tr><tr><td><pre><pre>
  1026. /// &lt;summary&gt;
  1027. /// Determines if there is overlap of the non-transparent pixels between two
  1028. /// sprites.
  1029. /// &lt;/summary&gt;
  1030. /// &lt;param name="transformA"&gt;World transform of the first sprite.&lt;/param&gt;
  1031. /// &lt;param name="widthA"&gt;Width of the first sprite's texture.&lt;/param&gt;
  1032. /// &lt;param name="heightA"&gt;Height of the first sprite's texture.&lt;/param&gt;
  1033. /// &lt;param name="dataA"&gt;Pixel color data of the first sprite.&lt;/param&gt;
  1034. /// &lt;param name="transformB"&gt;World transform of the second sprite.&lt;/param&gt;
  1035. /// &lt;param name="widthB"&gt;Width of the second sprite's texture.&lt;/param&gt;
  1036. /// &lt;param name="heightB"&gt;Height of the second sprite's texture.&lt;/param&gt;
  1037. /// &lt;param name="dataB"&gt;Pixel color data of the second sprite.&lt;/param&gt;
  1038. /// &lt;returns&gt;True if non-transparent pixels overlap; false otherwise&lt;/returns&gt;
  1039. public static bool IntersectPixels(
  1040. Matrix transformA, int widthA, int heightA, Color[] dataA,
  1041. Matrix transformB, int widthB, int heightB, Color[] dataB)
  1042. {
  1043. // Calculate a matrix which transforms from A's local space into
  1044. // world space and then into B's local space
  1045. Matrix transformAToB = transformA * Matrix.Invert(transformB);
  1046. // When a point moves in A's local space, it moves in B's local space with a
  1047. // fixed direction and distance proportional to the movement in A.
  1048. // This algorithm steps through A one pixel at a time along A's X and Y axes
  1049. // Calculate the analogous steps in B:
  1050. Vector2 stepX = Vector2.TransformNormal(Vector2.UnitX, transformAToB);
  1051. Vector2 stepY = Vector2.TransformNormal(Vector2.UnitY, transformAToB);
  1052. // Calculate the top left corner of A in B's local space
  1053. // This variable will be reused to keep track of the start of each row
  1054. Vector2 yPosInB = Vector2.Transform(Vector2.Zero, transformAToB);
  1055. // For each row of pixels in A
  1056. for (int yA = 0; yA &lt; heightA; yA++)
  1057. {
  1058. // Start at the beginning of the row
  1059. Vector2 posInB = yPosInB;
  1060. // For each pixel in this row
  1061. for (int xA = 0; xA &lt; widthA; xA++)
  1062. {
  1063. // Round to the nearest pixel
  1064. int xB = (int)Math.Round(posInB.X);
  1065. int yB = (int)Math.Round(posInB.Y);
  1066. // If the pixel lies within the bounds of B
  1067. if (0 &lt;= xB &amp;&amp; xB &lt; widthB &amp;&amp;
  1068. 0 &lt;= yB &amp;&amp; yB &lt; heightB)
  1069. {
  1070. // Get the colors of the overlapping pixels
  1071. Color colorA = dataA[xA + yA * widthA];
  1072. Color colorB = dataB[xB + yB * widthB];
  1073. // If both pixels are not completely transparent,
  1074. if (colorA.A != 0 &amp;&amp; colorB.A != 0)
  1075. {
  1076. // then an intersection has been found
  1077. return true;
  1078. }
  1079. }
  1080. // Move to the next pixel in the row
  1081. posInB += stepX;
  1082. }
  1083. // Move to the next row
  1084. yPosInB += stepY;
  1085. }
  1086. // No intersection found
  1087. return false;
  1088. }</pre></pre></td></tr></table></span></div>
  1089. </div>
  1090. </div>
  1091. <a id="ID2EAPAC" name="ID2EAPAC"> </a><h1 class="heading">Ideas to Expand</h1><div id="ID2EAPAC" class="hxnx1">
  1092. <p>Now that you have a flexible per-pixel collision detection method, try out these ideas.</p>
  1093. <ul>
  1094. <li>Make the falling blocks have varying scales.</li>
  1095. <li>Create collectable power-ups that make the person shrink or grow.</li>
  1096. </ul>
  1097. </div>
  1098. </div><div class="footer" id="footer"><p>© 2010 Microsoft Corporation. All rights reserved.<br />Send feedback to <a href="mailto:[email protected]?subject=Documentation Feedback: Collision Series 3: 2D Collision with Transformed Objects">[email protected]</a>.</p></div></div></body></html>