Commit ba9a3ff1 authored by Nathan Bean's avatar Nathan Bean

Added first technique, particle engines

parent 7572b241
baseURL = "https://ksucs-hugo.russfeld.me" baseURL = "https://cs.ksu.edu/~nhbean/cis580/"
languageCode = "en-us" languageCode = "en-us"
title = "K-State CS Hugo Framework" title = "K-State CIS 580 Textbook"
# Change the default theme to be use when building the site with Hugo # Change the default theme to be use when building the site with Hugo
theme = "ksucs-hugo-theme" theme = "ksucs-hugo-theme"
...@@ -12,9 +12,9 @@ page = [ "HTML", "Iframeable", "Teleprompter"] ...@@ -12,9 +12,9 @@ page = [ "HTML", "Iframeable", "Teleprompter"]
section = [ "HTML", "Iframeable", "Teleprompter"] section = [ "HTML", "Iframeable", "Teleprompter"]
[params] [params]
editURL = "https://gitlab.cs.ksu.edu/russfeld/ksucs-hugo/tree/master/content/" editURL = "https://gitlab.cs.ksu.edu/nhbean/cis580-textbook/tree/master/content/"
description = "K-State CS Hugo Framework" description = "K-State CIS 580 Textbook"
author = "Russell Feldhausen" author = "Nathan H. Bean"
showVisitedLinks = false showVisitedLinks = false
themeVariant = "purple" themeVariant = "purple"
disableLanguageSwitchingButton = true disableLanguageSwitchingButton = true
......
---
title: "Chapter 0 Introduction"
pre: "1."
weight: 10
date: 2018-08-24T10:53:26-05:00
---
Lorem Ipsum
+++
title = "Chapter 0 Title"
date = 2018-08-24T10:53:05-05:00
weight = 5
chapter = true
pre = "<b>0. </b>"
+++
### Chapter 0
# Chapter Title Here
Chapter 0 tagline.
---
type: "reveal"
hidden: true
---
<section>
<h2>CIS 123</h2><br><br><p>Title Slide</p>
</section>
<section>
<h3>Slide Title</h3>
<ul>
<li class="fragment">Content</li>
<li class="fragment">Content</li>
<li class="fragment">Content</li>
<li class="fragment">Content</li>
</ul>
</section>
---
title: "First Page"
pre: "1. "
weight: 10
date: 2018-08-24T10:53:26-05:00
---
Lorem Ipsum
[Slides]({{< relref "01-first-page-slides.md" >}})
---
title: "Decryption Example"
weight: 4
pre: "B. "
---
{{% encrypt encrypted="true" %}}
IntcIml2XCI6XCIzOC83eEpudnVXWHd4NkhYV01rSE53PT1cIixcInZcIjoxLFwiaXRlclwiOjEwMDAwLFwia3NcIjoxMjgsXCJ0c1wiOjY0LFwibW9kZVwiOlwiY2NtXCIsXCJhZGF0YVwiOlwiXCIsXCJjaXBoZXJcIjpcImFlc1wiLFwic2FsdFwiOlwiQUVIWHcraU1uQzQ9XCIsXCJjdFwiOlwiOEZweEtkWi8yL3Z6UU9sSWhEMy9acnd5NWs5Tm43RzFlb0UxRlJKU1BlcjFDVTc4Q1QxWGJaWWdrd2Y5Mk43STdCeGxZdzd6UGZJWVk3VGVISDM0OUwvL1FhUjBWRkpUcU5vTHpibVlPWUlOREdldmhla1U5TFBZVWpTUXdkNHVTWHkvSkcyR1hTNnllVk4xTk1KY1JVMGROM09uMjNFTXFYdFFZckFSMDVUcTZMMnNGK1pMZ2pUSGhwODhtcEJ6TGxMZjliTncxZndQTHZGU3lSSy9jczRlZ084dW9naGZLUno0eG8xV3pkMGZHNm1qRkZGMUJpVkpwL3d0dU5SY0t2TDhBNVNDWFZ2dXdvbnlxd3Q2ME1xUVZoWnVlRHhYSEo0ZkpzUzdPUkhNckdPUSs5MUlxMjl2Z3I4UTVPUktuWmtxdlpFNG1PcWoyWTZ6OFZQdHFWSXliK3hxQnVMRU9zSmpjbHV3eGRCMGNVdDlreE1ieGVKOTJyMFMvYXQ3SUhsVUVkV1RBMXRJYmlwbllIMWNLOUhDellPM1Q3OHlac3V1WmJyYVZydjNQK0JIM2xCZmNZWlBib28ySkFqQmQ1b1FrdEMvZjRrMWkzVHJ1cDF3cGtBd0ZQVVlQZVErY1E3dVNxUXhRVjZVVkRRVGNsK2ZacnV4RFdaSUMyTVB5WXhwcEc4a2IrZHd0UlQwUENBL0Z1VkN4aGw2UjJNMEtHTWdQenVJSlBHRnZ6RDU2ck1uaDR0QlNzMU9XdDNxRVVNN1VNR2RCNWkvelVNYWZLNlNIeGVwc1RST0tXcTAvcXVobThqYVVzYmV2bWlFZHVYc1dkTXZDL3p4Wis0QVpFY3c0L2QvWHRJbUMrZDRvUUxEN09TcnpINzB6U1dadk5zb1V5dEVkL2NiZXBKUXVMZjN2S1VISmVGWDFlR3puOHlpU0p1bWROZUJaVHd5dXM2Q0lrSEkvaEZSZURKcTc2aTJranFxRkNBTDlCQi9aMmtWOERSdmE4ODkxK2FsQlVBTEJDemNGTUpWTHZ3amR5LzJ3ZG90MGZZOVkvc0tTcWsrVUZDRVcvUTBNNFlkMGx2eGN1bmE1WkxwOUtNZ0hySFB2cDAySmNUL0VoM0dWRkUxMkpYdWFtY0ZDVktucEU3ckJZdHUwbTZZTzJBVWEvdmEzb21VTzVOU0t5VUhmREh4VTZEQ3phN1Z6NkZ3ei9VRi85QXFxYjVBTU9nTWFWQjZDRUZjbzRkRWp5TWtNQzJpOEcvcGxmOVFJUnN4MjVxek5QWWpIdm5ZYXdRTHhrWjZqUHcrVGNjeWp3R1I3enkzZFZWcDFQVDNDQWovMlVYanc1ZHUwV0xlTTZXTzk1Z3NOdGxOQ2hyT3NJeUFOUmFhbVRsY2xBendoc0hCaUlIaVdpNHdDUmJkUjB4aFdrOFJ3dlYzdmtzUzJqSmtJa2szZ1JLOTNyVUxQM3Y3dVkwcDkrT3JaT1A5S2JIQm50cEtVVVhlUkRqRUNDNHkrWmV2bmZSRzRQcG1uRUdocjdDQ0dhL29kelVnTy9yWlp1QXJWb21LVlNTWlFBdUFrVTdWZUxLTEpWVE16RmZvNE9ZTURaR1RJSjkySjRLQkljM0RIZTFNM3dKMFpWOUpIKzQyb1VHNnJBOHNaNGlSSFJLeEVUNndTb1dhdGxLYzA4bUMxeVc5MkovcFpaeS9wZDE4Mk1zM0dzTmgxaEg1K0xrL2tQT0dDN1VNSFc1R2ZJLzJiNjZBcUZnVyt4T1VjK0p5bWtSRTVsWjd6eUhsd0M3Q3MwcC9TS0ttMUZ1MTBHTm1yWmZ5cDlHUnROR0Z0T2xBQ084WG1Fem5zZHV4eHNJbHZPcENDWXZoREx2Y3Z2dVJ6OFBDU1Bpazh4R2lKeVNWU0c3ellWNEtoZ2sxWE1zb00vVFN1V3RqRGdLRDJzSGFGMzI5dTdYa29jRkhnd2pSZHVhYWNNUG90b0k1dzg5cHozakRzUlVxa0RGdHJGTFVzdE9KcUhpWHR0UzhKNHFhdjZoSXlJTjhtSm5CUENlbGtvZUNLRThjcnRzUXlqZEorVzVlcXg0ZjNDOFd0dlBaVERnS2lIZzc3U1Rwc05EUjZudmlVbnd0Vjh0VElqaE92MW4rMWE5WkNkYVk4VVhZSXpwREUyb3lPMmJmdFFxdTBqcC9YWFJvTXd1LzFPR01kV0VhZWtmVy9ZY3laUEZHbFppSldyZThiaUdKc1NuTDJDdFJMQ1hhTGRKN3VIRmttdHhhcFZsU3dJd0RibWNkVVRTQVUrKzhza1lRNUhjYWZRZjE0aDdMS0tjNkJJRVpxU054UGJtaXQ1RlcwS0VtbUtHdWlGVkRDcitKYnJwWUJEcTh4RHJZTllpdkk5UlFyMWdiN0JidnFDSjdtbFFUVzhuVGMxUDB0UFpkdXFvb2VhalZnOXc5RTBQSFBWTm5YVEZiMjU2UlA3NVZIQ0NtU1VoZlhpb1c1YmFIM3FxbG5tVm9iclB6OXNyWWp3cVpHNGNpMExOQkZGTDEvbGo5OG9XR0tEMloxQ1BlUlB5cGU2UkxnaGkxQlk4UmNxaERyTDg3ckpmV3NrTDc3MWxubkxlWGczMkRvOHU3MHY1VzB1NGl1dUdxWTJteWZ1RnVwUm14cTZ6c3FrSW9rTmFBQWRjdjgyWkpwei9ZT0NhTXpUdThWSjhyR1RKNkZYbEk3Qmxhczg2NDhiTnZPVDNlYUNBTUh6dHBJSkw5QzhTUldhNmlKN3d4UTV4b3BiVlhyVE5jOEl4bCtiVGNsaUhWQ2R2R2QrelRBRk9YU2pjNDlxOGp2WXJSSEcwanJHVmsrenRHYmlKQktJTmFDdlk0MGlna0RnaVJ5czkyR0h3djN2bUJMS0Fic0Z0ODkwcVdTRDhSQWlVWlNJM3p1SnZWZ3M1ZFlXd2Ivb3M0d2tLcXExSXkwbTh5YktpZGJQTjRLeUNLdmN0L0cxdWRmRm9MVVgycVpRZmVtUVl3L0d3YVFoSUVWL09wUGg0YWw5Sk5uNWtWa1E2ZmIyOU8velUrMGVqMUQ3Yk5lSEhuV0tUcERueW12MzJCSkcybklkUFEzU1JSTnBoYndNZG9VTVJaTzVLT0RFZDZPakNnNm13RjJIRksxMUpoTTJuQ1FjUVlZQ2JGb3ZqVjNFekFCM3lGVk03S2I5Wko4cHU1dUxTZSswR2JQVFZIejd1MmRpOXhDMUdJSUdnK2IycXN6Wld3bnovb3luZWZyellQamJjSTJ4eUxseFR0enV1K2ZQMlZMZmpZUEJ0RDlQWlI0eTVIYkNXV2hOajQzM0ZwWXBucEl0eVo0UWRpSVhTN3VHdXdnQlhyTk1BSXp6UGM1STdIbjRrc2dvay9KeWpZaGIxNUo5anlCUk02cCtqMkRZS1dBZFZNM3Vub1dybS9kQlYxZzIwd2VLb0dUNjJiV0cwdVlYTEkvVk5xZEZvbWtJTldoQXpyOWtuaXIwUEFoaEVLcDFLV2dHUFk2RUpOZEV0QXN6R2NEcVhGeEFNTXY1ZFFnOVlpRE1jeHRiMkxrd0ZoRjc3dnFpOFF1Qk00L3drdmpmREJlS0tBblM0aE1hWGZqdE9IL2h1ZldLRWxBYVZDQUJiRk1aZytTa01sbWFJNTUrczZzU0V2QjVLWmtxL1FkVTNPVXZRTlYxL0FpNEhaY3NPNkN2bDd6MXFYMGdDb1luK2x3cXBqcE4rbEN4MzFjVlkwRWpGZTJzbGI0YkZrbmxmTVc0Y1VrZHREUkJPSGtNVWpkblBQb3B6R3dtNVNtdEJ5OU5icU5VTW5DZGtBTkIycnRWdHFFbHVXRVlHNXZTOVBrWU0rRSsvUi95MkxIUkMzRDFkbjQ2TmcrTXhKMzRRMitTbXZZa2Z3bUlUeE15NlpicEk5NENPWHp3QXpnVDhZMW1MaWxQL216eXBGaTVscFF5TlNjaEhoaHlscndDWjZwSExpOWpVTWVFWC9FTTZuYUFxQ01oVnZ1Y2JjWnpVaDlDM3VRVE5laS85akRwN0xkU0JudC9aYldNNkpaenVsYVFMZytxQUFlTzlWY2FYQ09iSXQxTnhlbXRrR3crWjNoTFVLS0Jjcy93aFgwMFVMRUNPM3RvMHBQUnkzRVozWTI2aFh1cmNMdW5jOTA5eDZrVGprbVBkU2JubkZaNGNpWjQyckFQR2h0MkVvTHd5UU5MSDBkM1EvOGNHREpCZkxLMjN0Y0dObTBNcGw5R2RNUFhPdStLblBNaGp5ZVdaQlNZeXlhaURiZDdWOG84NUpxajFua2V6ZnZSaHY0bjBEREJHM2t5NzFKN1dmVUp5VGZkY0F5eGw2V3k4Zi90NVhvbHArVlNvNnFoRGNxb09sN1V6eGxDTGZMbEE0YnJsem9QcFJzQkk3dUt2SmVxRldPWnhJSFo1UVoyL2s1MVZ6K091Y3h1cVI1czVkK0RDZG55MmRISVdYcEY5aC80NE13QUFZV0gvSXVtblM4NHR3N2NZdnZzb0FYTENVZG5WQTY0MTU4TzZGeld1dERNcWtsQ3BHdlAwKzM0Z1dxK3NKSjhxTXZRL0VwaC8vRW5DVVpYY0gwNFZzVm4yWXJUb1p2NlM1YzgvS0xYb3dOeEE3dlIzc2NrVFBSak9pVUY5ZklYM0xZZEZmY2wvNG5SUnhBcVlRZlI4ZXdaajZNdFpMNHdGUWM4Z25BZ3d3cm5QOEh0RFkrOHNtRjhiM3JoUm1STHBzN0RXTDJJV1pXUFJReFhSWkZjcUt2Ri9IRzR5WTJOVGFlRVBScEcvV01iZzYyZUN3UzlHcHRGN1NNWmxRTHNoK1dlaXVtUE1NekZSM1l4ck5ZUHVHMWJWYkxpOUd4Nk9QZ09TZmk2VXVkS3k5cDFxQUFLa214RjZTNkV0cVVjVlMvSFZpMWo3RXBLamxnUGlYUGNGWU5QN3FHVllXaStmbG5aSlNjbWNOMGN1L09zdktRSnhGQ05DK21FRlpncGlrSy9Ld2tPeEpmbEdDM3dKZXR5WXlUS0dCU2xaenpOeEJ1dGh6VTd0M1RqemwxMTYxZlRQTG1Rc3J1elVXa1pHcFdIWWZ1Z1lQd2dqZnVuZStOMzl2ZkIxZnBaN3poaFVBWjBTMXJLU2xPMVBFY0lWS3VDSjRMZzBuRWVzaStsSkNNS3huQjZvRHJGR0NOczIxMnZvb0dYSVlzTGtiSmhCSW5jZlZBcHlZakdBVkoxN25oOGR6OVc1YnRXeGVRR0pOL0wrb3FXTGtHQ1E2cUtSM0FyK3Jpcmptd0x4akZMMU1oNks2VHBCOUEvNlRvaWZTRUxOTStUVTVRSGY5c21tNGtJcFdteHJGdktnVUs1dEd5MFBrRlhtcTgwTnI5Smw5MnQxTlZjMXoyWjUvM2lXbjcvRkxhVlV6Szd0NHVBeWwrZ2lDWXduNVVHMnMrb001QVRrTG5CMHlzRUJadkpKT0d5NmhTTjlPQUNzLzVnV0R6blNDVHpBR0Y1QUVmQXVFYW5uNVk0WXFOb0EyY1FuVTluL2NYRllXTEMwbFN0Uk43bjFFZTlaWVlrQUEyT3NUK2V0YlNaYTdmQ1JXNGxXWWZ3SkcwSFFTTmRvdWk5SjJHUGErbyt5Yk5heTlsTDdKSVhzOWY2Y1c3N3ZSL0ltMXFrMFZsWHBHUkdsM0hPTmpWVnBOeHBHV2toYld5VHpLdC9TMGhYbEwwaXg0OHdyNGxzR2g2ZUtBQTBHMVkyS3RJQzlHbjBUdE9Kbmx3c3FQSUY2OXk2eHVobXFwcW1zY2Z3UXRFY1ZVZjQrNUhWSzhxVThkQnVMYjM0ZFZsR1orUDdqcEFvM3BnWk93TWtyejNSNzNKeVpJb1hMaUtoYjF5c3I4NWp3MVlRMnRBbEZWUmZRVlZxbmUxK3k1ZXhYU25MbDYrSXRCeElIakY5dEZFSENUUXZFdkd1dVE5dVBEaDA4MEYzcXBHRFJ3ckpoQVhCd0ZrK3dBcUErR2ZEWVBiTThES1ZVUlVJSkloZHovQmZ4dzlGVTdGV085dUFjcHJDVkVGL2tiN0NZZm9Ed0FHOWNGYnZ2UFJUenZnVXhMOThpMklFWkdTMVM0dTE2eDZqdFZ6S1EwUXdxanlWVllQTmR6OEVCQlhXU1A0SUZUNW9NeGxFMGprU0JhVCtpL3AxZWxmelhTS3NiTVJ2QzFRYm1oRzVJVXY5aVBOWmJRb2RBTTI1WjNsTTc2VEZ0a2lyd29XKzJnbHA5SzY5VTVxNFdPKzdneVwifSI=
{{% /encrypt %}}
---
title: "Encryption Example"
weight: 3
pre: "A. "
---
{{% encrypt encrypted %}}
### Example File to be Encrypted
**Password:** testpassword
### Sample Text
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sit amet placerat risus. In hac habitasse platea dictumst. Etiam risus massa, finibus vitae felis non, hendrerit auctor nibh. Morbi ut odio posuere, pharetra metus vitae, venenatis turpis. Nullam interdum imperdiet orci, ut ultrices magna. Donec a odio eu tellus commodo venenatis a nec dolor. Nam dictum auctor enim ut consequat. Phasellus sit amet sapien ipsum. Aenean scelerisque mi orci, ut aliquam eros volutpat id. Proin interdum convallis nunc, vel mollis leo pellentesque interdum.
Donec mollis egestas lacus vitae suscipit. Vestibulum in varius massa. Nam quis velit ut dolor pellentesque molestie vel non massa. Morbi hendrerit consequat mollis. Cras ligula massa, mollis eu urna non, eleifend scelerisque nulla. Mauris vel magna aliquam arcu lobortis sollicitudin in aliquam tortor. Curabitur nec sapien felis. Etiam quis mattis mi. Phasellus leo tortor, rhoncus at viverra at, porta viverra turpis. Ut elementum tortor sit amet ex volutpat pellentesque. Integer posuere enim tortor, eget finibus dolor sodales eleifend. Phasellus at rutrum sapien, in faucibus enim. Praesent vel convallis orci.
**Bold**
_Italics_
`some code`
```
a longer code block
```
### Source File (for Editing)
{{< norender >}}
### Example File to be Encrypted
**Password:** testpassword
### Sample Text
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sit amet placerat risus. In hac habitasse platea dictumst. Etiam risus massa, finibus vitae felis non, hendrerit auctor nibh. Morbi ut odio posuere, pharetra metus vitae, venenatis turpis. Nullam interdum imperdiet orci, ut ultrices magna. Donec a odio eu tellus commodo venenatis a nec dolor. Nam dictum auctor enim ut consequat. Phasellus sit amet sapien ipsum. Aenean scelerisque mi orci, ut aliquam eros volutpat id. Proin interdum convallis nunc, vel mollis leo pellentesque interdum.
Donec mollis egestas lacus vitae suscipit. Vestibulum in varius massa. Nam quis velit ut dolor pellentesque molestie vel non massa. Morbi hendrerit consequat mollis. Cras ligula massa, mollis eu urna non, eleifend scelerisque nulla. Mauris vel magna aliquam arcu lobortis sollicitudin in aliquam tortor. Curabitur nec sapien felis. Etiam quis mattis mi. Phasellus leo tortor, rhoncus at viverra at, porta viverra turpis. Ut elementum tortor sit amet ex volutpat pellentesque. Integer posuere enim tortor, eget finibus dolor sodales eleifend. Phasellus at rutrum sapien, in faucibus enim. Praesent vel convallis orci.
**Bold**
_Italics_
`some code`
```
a longer code block
```
{{< /norender >}}
{{% /encrypt %}}
This diff is collapsed.
+++
title = "Chapter X Title"
date = 2018-08-24T10:53:05-05:00
weight = 100
chapter = true
pre = "<b>X. </b>"
+++
### Chapter X
# Chapter X Title
Chapter X tagline.
+++ +++
title = "Homepage" title = "CIS 580 Textbook"
date = 2018-08-24T10:53:05-05:00 date = 2020-03-21T10:53:05-05:00
+++ +++
# K-State CS Hugo Theme # K-State CIS 580 - Foundations of game Programming Textbook
This is an adaptation of [Hugo Theme Learn](https://learn.netlify.com/en/) that has been customized for use by K-State Computer Science to build online textbooks. It contains some featuers that are unique to our use case, but they may be useful to others. This is an open textbook written for the <b>Kansas State University CIS 580 - Foundations of Game Programming</b> course.
More information can be found on [GitHub](https://www.github.com/russfeld/ksucs-hugo-theme) ## Companion Textbook
It is intended to be used in conjunction with Robert Nystrom's excellent [Game Programming Patterns](https://gameprogrammingpatterns.com/) textbook (available free online).
Some unique features to this theme: ## Platform
* Fonts and layouts customized to match K-State's websites and color scheme, as well as [Instructure Canvas](https://canvas.instructure.com/). This text focuses on programming using [Microsoft's C# Language](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/) and the open-source [MonoGame](http://www.monogame.net/) framework.
* Each page generates an embeddable version that strips menus, headers and footers (add /embed.html to almost any URL). This is meant to be embedded in an iFrame within another page, such as an HTML wiki page in Canvas.
* By doing so, we can embed course content in Canvas while editing it via Hugo, taking advantage of tools such as git for versioning. In addition, by updating the source website, all versions of the course in Canvas are updated immediately.
* Each page also generates a teleprompter version to allow creation of course videos (add /tele.html to almost any URL). Many pages are used as a video script for multi-modal learning.
* The teleprompter pages include auto-scroll capabilities. It is compatible with an [IKAN Teleprompter Remote](https://ikancorp.com/shop/teleprompters/tablet-teleprompters-accessories/ikan-elite-remote-bluetooth-teleprompter-remote-for-pt-elite-prompters/), but can be controlled using the number keys or easily customized. See /static/js/tele-scroll.js for details.
---
title: "Introduction"
pre: "1. "
weight: 1
date: 2018-08-24T10:53:26-05:00
---
Particle engines leverage thousands of very small sprites to create the illusion of fire, smoke, explosions, precipitation, waterfalls, and many other interesting effects. They are a staple in modern game engines both to create ambience (i.e. fires and smoke) and to enhance gameplay (glowing sparkles around objects that can be interacted with). In this section, we'll discuss the basics of how particle engines work, and iteratively build a particle engine in MonoGame that can be used in your own games.
I've prepared a starter project in which we will build this engine. Feel free to clone it from GithHub, here:
// TODO: GitHub link
\ No newline at end of file
---
title: "The Particle Structure"
pre: "2. "
weight: 2
date: 2018-08-24T10:53:26-05:00
---
At the heart of a particle engine is a collection of particles - tiny sprites that move independently of one another, but when rendered together, create the interesting effects we are after. To draw each individual particle, we need to know where on the screen it should appear, as well as the texture we should be rendering, and any color effects we might want to apply. Morever, each frame our particles will be moving, so we'll also want to be able to track information to make that process easier, like velocity, acceleration, and how long a particle has been "alive".
With thousands of particles in a system, it behooves us to think on efficiency as we write this representation. The [flyweight pattern](https://gameprogrammingpatterns.com/flyweight.html) is a great fit here - each particle in the system can be implemented as a flyweight. Moreover, since we know we'll be iterating over the collection of particles each frame to both update and draw the particles, using the [data locality pattern](https://gameprogrammingpatterns.com/data-locality.html) to maximize the use of our cache makes sense as well. To fulfil these two requirements, we'll want to define a `struct` to represent our particles.
>**Why a struct?** Remember, in C# a struct is a _value_ type, so if we create an array of `struct` instances, each struct will be stored sequentially in memory. If we instead used a class, a _reference_ type, and create an array of `class` instances (objects), that array will hold references pointing to other locations in memory where those objects are located. So using a struct in this instance is a performance optimization.
Go ahead and create a file named _particle.cs_ and define a particle structure in it:
```csharp
/// <summary>
/// A struct representing a single particle in a particle system
/// </summary>
public struct Particle
{
/// <summary>
/// The current position of the particle
/// </summary>
public Vector2 Position;
/// <summary>
/// The current velocity of the particle
/// </summary>
public Vector2 Velocity;
/// <summary>
/// The current acceleration of the particle
/// </summary>
public Vector2 Acceleration;
/// <summary>
/// The current scale of the particle
/// </summary>
public float Scale;
/// <summary>
/// The current life of the particle
/// </summary>
public float Life;
/// <summary>
/// The current color of the particle
/// </summary>
public Color Color;
}
```
Here we've created fields to hold all information unique to the particle, both for updating and drawing it. Feel free to add or remove fields specific to your needs; this sampling represents only some of the most commonly used options.
Note that we don't define a texture here - all the particles in a particle system typically share a single texture (per the flyweight pattern), and that texture is maintained by the particle system itself. We'll turn our attention to that next.
\ No newline at end of file
---
title: "The Particle System Class"
pre: "3. "
weight: 3
date: 2018-08-24T10:53:26-05:00
---
The next part of the particle system is the class representing the particle system itself. Go ahead and create a new class file, _ParticleSystem.cs_ and define a `ParticleSystem` class in it:
```csharp
/// <summary>
/// A class representing a particle system
/// </summary>
public class ParticleSystem {
}
```
## Private Fields
This class needs to hold our collection of particles, and in keeping with the [data locality pattern](https://gameprogrammingpatterns.com/data-locality.html), we'd like these to be stored sequentially. An array is therefore a great fit. Add it as a private field to the class:
```csharp
/// <summary>
/// The collection of particles
/// </summary>
Particle[] particles;
```
As we said in the discussion of the `Particle` struct, in using the [Flyweight Pattern](https://gameprogrammingpatterns.com/flyweight.html) the actual texture will be held by our `ParticleSystem`, so let's add a private variable to hold that:
```csharp
/// <summary>
/// The texture this particle system uses
/// </summary>
Texture2D texture;
```
We need a `SpriteBatch` instance to render our particles with. With other sprites, we've used the `SpriteBatch` owned by our `Game` instance, but in this case we'll probably want to be using a different rendering mode (usually additive blending), so it makes sense to have our own, separate instance. Go ahead and declare it:
```csharp
/// <summary>
/// The SpriteBatch this particle system uses
/// </summary>
SpriteBatch spriteBatch;
```
We also will likely need to generate random numbers when spawning our particles. So it makes sense to create a `Random` instance and keep it around as a private field:
```csharp
/// <summary>
/// A random number generator used by the system
/// </summary>
Random random = new Random();
```
## Public Properties
Let's turn our attention to public properties we might want with our `ParticleSystem`. We typically want to spawn our particles at a particular location in the game (the center of an explosion, a powerup's location, etc). We call this location the _emitter_, and we'll want it to be public as we may move around the scene (say, for example, it's location is the tip of a torch the player is carrying). Let's use a `Vector2` to represent this location:
```csharp
/// <summary>
/// The emitter location for this particle system
/// </summary>
public Vector2 Emitter { get; set; }
```
Similarly, we'll often want new sprites to be created at a set rate. Let's create a property to hold this value:
```csharp
/// <summary>
/// The rate of particle spawning
/// </summary>
public int SpawnPerFrame { get; set; }
```
## Constructor
Now let's turn our attention to our constructor. In order to create our own `SpriteBatch`, we need to have access to the `GraphicsDevice` instance of the game. We'll also need the size of the particle system (the number of particles) to initialize our `Particle` array. Finally, we'll need our texture as well. Use these as parameters to the constructor, and initialize the corresponding fields:
```csharp
/// <summary>
/// Constructs a new particle engine
/// </summary>
/// <param name="graphicsDevice">The graphics device</param>
/// <param name="size">The maximum number of particles in the system</param>
/// <param name="texture">The texture of the particles</param>
public ParticleEngine(GraphicsDevice graphicsDevice, int size, Texture2D texture)
{
this.particles = new Particles[size];
this.spriteBatch = new SpriteBatch(graphicsDevice);
this.Texture = texture;
}
```
## Update Method
Now we can turn our attention to the update method. It needs to accomplish two tasks: 1) spawning new particles, and 2) updating particles by moving them around the screen. Let's start by definining the skeleton of the method:
```csharp
/// <summary>
/// Updates the particle system, spawining new particles and
/// moving all live particles around the screen
/// </summary>
/// <param name="gameTime">A structure representing time in the game</param>
public void Update(GameTime gameTime) {
// Part 1: Spawn Particles
// Part 2: Update Particles
}
```
### Spawning Particles
Before we start spawning our particles, we need to discuss where we're going to put them. We have an array of `Particle` instances, and obviously they will go into that array. The first few are easy, we put the first at index 0, the second at 1, the third at 2, and so on... but the next frame we'll want to start where we left off... and eventually we'll reach the end of the array, and need to start back at the beginning. For now, let's adopt a simple approach of keeping track of the next index to use. Add another private variable to the top of our class:
```csharp
/// <summary>
/// The next index in the particles array to use when spawning a particle
/// </summary>
int nextIndex = 0;
```
Now, back in our `Update()` method, we'll want to spawn a number of particles matching our `SpawnPerFrame` rate, just after the `// Part 1: Spawn new particles` placeholder:
```csharp
// Part 1: Spawn new particles
for(int i = 0; i < SpawnPerFrame; i++)
{
// TODO: Spawn Particle at nextIndex
// Advance the index
nextIndex++;
if(nextIndex > particles.Length - 1) nextIndex = 0;
}
```
After we've spawned each particle, we need to move `nextIndex` to the next index in the `particles` array. If we hit the end of the array, we want to loop back to the beginning. When this happens, we'll overwrite old particles with new particles. As particles typically have a short lifespan, this usually isn't a problem.
Now let's tackle the actual spawning. When we spawn new particles, we need to define their properties. This includes where they appear on screen (`Position`), how they move (`Velocity` and `Acceleration`), and how they are rendered (`Scale` and `Color`). In addition, particles usually only stay on screen for a specific duration of time (`Life`). For now, lets' use a random initial `Velocity` and `Acceleration`, a `Scale` of 1, the `Color` as white, and a 3 second `Life`. The Position should be where the `Emitter` is currently located (add the following code at the `TODO:// Spawn Particle at nextIndex placeholder`):
```csharp
// Create the particle
particles[nextIndex].Position = Emitter;
particles[nextIndex].Velocity = 100 * new Vector2((float)random.NextDouble(), (float)random.NextDouble());
particles[nextIndex].Acceleration = 0.1f * new Vector2((float)random.NextDouble(), (float)random.NextDouble());
particles[nextIndex].Color = Color.White;
particles[nextIndex].Scale = 1f;
particles[nextIndex].Life = 3.0f;
```
### Updating Particles
The second half of our `Update()` method is updating the living particles. Before we do so, it is helpful to have the elapsed time as a `float` ready to go in its own variable:
```csharp
float deltaT = (float)gameTime.ElapsedGameTime.TotalSeconds;
```
Put this directly after the `\\ Part 2: Update Particles` placeholder. Then we'll want to iterate over all particles, skipping any that are "dead" (have a `Life` of 0 or less):
```csharp
for (int i = 0; i < particles.Length; i++)
{
// Skip any "dead" particles
if (particles[i].Life <= 0) continue;
// TODO: Update the individual particles
}
```
And, of course, we'll need to update the particles. Remember your physics? Acceleration is the change in velocity over time, so we'll apply the `Acceleration` vector, scaled by `deltaT` to the `Velocity` vector:
```csharp
particles[i].Velocity += deltaT * particles[i].Acceleration;
```
Similarly, velocity is the change in position over time:
```csharp
particles[i].Position += deltaT * particles[i].Velocity;
```
Finally, we'll want to reduce the particle's life:
```csharp
particles[i].Life -= deltaT;
```
These three lines should go where our `// TODO: Update the individual particles` placeholder is.
## Drawing the Particles
The last step is drawing our particles. This is done in a `Draw()` method:
```csharp
/// <summary>
/// Draw the active particles in the particle system
/// </summary>
public void Draw()
{
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive);
// TODO: Draw particles
spriteBatch.End();
}
```
Since we have our own `SpriteBatch` instance, we'll need to invoke its `Begin()` and `End()` methods ourselves. We take advantage of the `Begin()` method overloading to use the additive blend state. This will combine our particle's colors with each other and the background.
We'll iterate through our particles, skipping any dead ones, just as we did in the `Update()` method. Any that exist, we'll draw with an apporopriate `SpriteBatch.Draw()` overload (This code should replace the `//TODO: Draw particles` placeholder):
```csharp
// Iterate through the particles
for(int i = 0; i < particles.Length; i++)
{
// Skip any "dead" particles
if (particles[i].Life <= 0) continue;
// Draw the individual particles
spriteBatch.Draw(texture, particles[i].Position, null, particles[i].Color, 0f, Vector2.Zero, particles[i].Scale, SpriteEffects.None, 0);
}
```
That wraps up the `ParticleSystem` class for now. We'll need to use it in our `Game1` class next.
\ No newline at end of file
---
title: "Using the Particle System"
pre: "4. "
weight: 4
date: 2018-08-24T10:53:26-05:00
---
To use our particle system, we'll need to create one in our `Game1` class in our _Game1.cs_ file. Let's start by adding a private field to hold a reference to one:
```csharp
ParticleSystem particleSystem;
```
And a reference to the texture we'll use with it:
```csharp
Texture2D texture2D;
```
We can't create our `particleSystem` until the `GraphicsDevice` has been initialized, and we have our texture ready. A good spot to do so is in our `LoadContent()` method, just after we load our texture:
```csharp
// TODO: use this.Content to load your game content here
particleTexture = Content.Load<Texture2D>("particle");
particleSystem = new ParticleSystem(this, 1000, particleTexture);
particleSystem.Emitter = new Vector2(100, 100);
particleSystem.SpawnPerFrame = 4;
```
Here we've set our particle system to spawn particles at a rate of 4 per frame, at position (100, 100).
Now we'll need to invoke its `Update()` method in the `Game1`'s `Update()` method:
```csharp
// TODO: Add your update logic here
particleEngine.Update(gameTime);
```
And our `Draw()` method in the `Game1`'s `Draw()` method:
```csharp
// TODO: Add your drawing code here
particleEngine.Draw();
```
If you run the project now, you should see particles emerging at position (100, 100) and flaring out into a cone between 0 and -90 degrees:
![screenshot of particles](/static/images/particles-0.png)
Now that we have a basic particle system up and running, it's time to make it more flexible and powerful!
\ No newline at end of file
---
title: "Implementing Delegates"
pre: "5. "
weight: 5
date: 2018-08-24T10:53:26-05:00
---
While we have a working particle system, it's not very flexible. There are a number of approaches we can use to add some flexibility to it. Let's start by thinking about what will change between different particle systems. Let's think through a couple of examples:
_rain_ - a rain particle system will typically have particles falling from the top of the screen to the bottom. Potentially there will be some horizontal motion as well; either constant or changing over time (wind). Also, particles will spawn randomly across the entire top of the screen.
_explosion_ - and explosion particle system will typically have particles spawn at the center of the explosion with a constant velocity outward at a random angle between 0 and 360 degrees. The particles will also fade to transparency the longer they live.
_fire_ - a fire will typically have particles spawining similar to an explosion with less velocity, and over time these particles will all adopt an upward motion (acceleration in the negative Y). They also will change color, moving from the red of a fire to the grey of smoke (alternatively, you can use _two_ particle systems - one for the fire and one for the smoke).
Looking at these examples, the differences mostly come down to a) spawning and b) updating.
One classic technique would be to create a base class, and extend it for each kind of particle system we want to implement. But since there is only two functions that need to change, we might instead look at a powerful feature of C# - [delegates](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/using-delegates).
Delegates are simply methods that we can pass as arguments to a function, much like function pointers in C++. However, C# delegates are far more robust than C++ function pointers, as they actually represent a _type_, much like a class doees.
## Defining our Delegates
Let's go ahead and define our delegates. In your _ParticleSystem.cs_ class, just above the class definition, add:
```csharp
/// <summary>
/// A delegate for spawning particles
/// </summary>
/// <param name="particle">The particle to spawn</param>
public delegate void ParticleSpawner(ref Particle particle);
```
Note that we define delegates with the `delegate` keyword. Other than that detail, a delegate looks like any method definition - it has access modifiers (`public` here), a return type (`void` in this instance), a name (`ParticleSpawner`), and arguments (`ref Particle particle`).
Since our particles are value types, we want to use the [ref](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/ref) keyword so that the `Particle` argument is passed by reference (so changes we make to it in the method are actually applied to it, not to a copy of it).
Our delegate for updating a particle will be similar:
```csharp
/// <summary>
/// A delegate for updating particles
/// </summary>
/// <param name="deltaT">The seconds elapsed between frames</param>
/// <param name="particle">The particle to update</param>
public delegate void ParticleUpdater(float deltaT, ref Particle particle);
```
Here we take one additional argument - the time elapsed between frames, as a float.
## Adding Delegate Properties
Now we need to add corresponding properties to our `ParticleSystem` class with these delegate types. Add this code inside the class:
```csharp
/// <summary>
/// Holds a delegate to use when spawning a new particle
/// </summary>
public ParticleSpawner SpawnParticle { get; set; }
/// <summary>
/// Holds a delegate to use when updating a particle
/// </summary>
/// <param name="particle"></param>
public ParticleUpdater UpdateParticle { get; set; }
```
Just as with any Type, we can create a property or field to store a representation of one.
## Refactor the Update Method
And we'll need to use these delegate properties inside our `Update()` method. Replace your existing `Update()` with:
```csharp
/// <summary>
/// Updates the particle system, spawining new particles and
/// moving all live particles around the screen
/// </summary>
/// <param name="gameTime">A structure representing time in the game</param>
public void Update(GameTime gameTime) {
// Make sure our delegate properties are set
if (SpawnParticle == null || UpdateParticle == null) return;
// Part 1: Spawn new particles
for (int i = 0; i < SpawnPerFrame; i++)
{
// Create the particle
SpawnParticle(ref particles[nextIndex]);
// Advance the index
nextIndex++;
if (nextIndex > particles.Length-1) nextIndex = 0;
}
// Part 2: Update Particles
float deltaT = (float)gameTime.ElapsedGameTime.TotalSeconds;
for (int i = 0; i < particles.Length; i++)
{
// Skip any "dead" particles
if (particles[i].Life <= 0) continue;
// Update the individual particle
UpdateParticle(deltaT, ref particles[i]);
}
}
```
Notice how we are now using our delegate properties to both spawn and update our particles? Also, we need to make sure our delegate properties exist (are not null) before we try using them. If they were null, and we tried using them, we'd throw an exception.