.. Copyright (c) 2018-2024 William Emerison Six Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Moving the Paddles - Keyboard Input - Demo 04 ============================================= Purpose ^^^^^^^ Add movement to the paddles using keyboard input. .. figure:: static/screenshots/demo04.png :align: center :alt: Demo 01 :figclass: align-center Demo 04 How to Execute ^^^^^^^^^^^^^^ On Linux or on MacOS, in a shell, type "python src/demo04/demo.py". On Windows, in a command prompt, type "python src\\demo04\\demo.py". Move the Paddles using the Keyboard ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ============== ====================== Keyboard Input Action ============== ====================== *w* Move Left Paddle Up *s* Move Left Paddle Down *k* Move Right Paddle Down *i* Move Right Paddle Up ============== ====================== Paddles which don't move are quite boring. Let's make them move up or down by getting keyboard input. And while we are at it, let's go ahead and create data structures for a Vertex, and for the collection of verticies that make up a Paddle. Code ^^^^ Data Structures ~~~~~~~~~~~~~~~ Here we use dataclasses_, which automatically creates on the class a constructor, accessor methods, and pretty-printer. This saves a lot of boiler plate code. .. _dataclasses: https://www.youtube.com/watch?v=vRVVyl9uaZc .. literalinclude:: ../src/demo04/demo.py :language: python :start-after: doc-region-begin ca6358d2d0e38e03d5c0642954e9a34ed62ab406 :end-before: doc-region-end ca6358d2d0e38e03d5c0642954e9a34ed62ab406 .. literalinclude:: ../src/demo04/demo.py :language: python :start-after: doc-region-begin ecf8e1a61285c18b321fef38792c6e6a5c1ca79c :end-before: doc-region-end ecf8e1a61285c18b321fef38792c6e6a5c1ca79c Although Python is a dynamically-typed language, we can add type information as helpful hints to the reader, and for use with static type-checking tools for Python, such as `mypy`_. .. _mypy: http://mypy-lang.org/ .. literalinclude:: ../src/demo04/demo.py :language: python :start-after: doc-region-begin 6ab0efd624f5d076b983e875700a2b2307788cc2 :end-before: doc-region-end 6ab0efd624f5d076b983e875700a2b2307788cc2 * Create two instances of a Paddle. I make heavy use of `keyword arguments`_ in Python. .. _keyword arguments: https://www.pythontutorial.net/python-basics/python-keyword-arguments/ Notice that I am nesting the constructors. I could have instead have written the construction of paddle1 like this: .. code-block:: python x = -1.0 y = -0.3 vertex_one = Vertex(x, y) x = -0.8 y = -0.3 vertex_two = Vertex(x, y) x = -0.8 y = 0.3 vertex_three = Vertex(x, y) x = -1.0 y = 0.3 vertex_four = Vertex(x, y) vertex_list = list(vertex_one, vertex_two, vertex_three, vertex_four) r = 0.57 g = 0.0 b = 1.0 paddle1 = Paddle(vertex_list, r, g, b) But then I would have many local variables, some of whose values change frequently over time, and most of which are single use variables. By nesting the constructors as the author has done above, the author minimizes those issues at the expense of requiring a degree on non-linear reading of the code, which gets easy with practice. Query User Input and Use It To Animate ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. literalinclude:: ../src/demo04/demo.py :language: python :start-after: doc-region-begin 4b68726b67eef939645c430941518f4fb374f0c8 :end-before: doc-region-end 4b68726b67eef939645c430941518f4fb374f0c8 - If the user presses 's' this frame, subtract 0.1 from the y component of each of the vertices in the paddle. If the key continues to be held down over time, this value will continue to decrease. - If the user presses 'w' this frame, add 0.1 more to the y component of each of the vertices in the paddle - If the user presses 'k' this frame, subtract 0.1. - If the user presses 'i' this frame, add 0.1 more. * when writing to global variables within a nested scope, you need to declare their scope as global at the top of the nested scope. (techincally it's not a global variable, it's local to the current python module, but the point remains) The Event Loop ~~~~~~~~~~~~~~ Monitors can have variable framerates, and in order to ensure that movement is consistent across different monitors, we choose to only flush the screen at 60 hertz (frames per second). .. literalinclude:: ../src/demo04/demo.py :language: python :start-after: doc-region-begin 2ef80e67f318610c9d846513e604bdff5d037285 :end-before: doc-region-end 2ef80e67f318610c9d846513e604bdff5d037285 .. literalinclude:: ../src/demo04/demo.py :language: python :start-after: doc-region-begin 89e003b98e8ebecccb7ad30f6cd29e35a1a6e0f2 :end-before: doc-region-end 89e003b98e8ebecccb7ad30f6cd29e35a1a6e0f2 .. literalinclude:: ../src/demo04/demo.py :language: python :start-after: doc-region-begin c414af3df41f977118e25fb4e96de3194469a04a :end-before: doc-region-end c414af3df41f977118e25fb4e96de3194469a04a .. literalinclude:: ../src/demo04/demo.py :language: python :start-after: doc-region-begin daaa146fafc0d1c453ca4dcc38b7b9df1f92b0fd :end-before: doc-region-end daaa146fafc0d1c453ca4dcc38b7b9df1f92b0fd .. literalinclude:: ../src/demo04/demo.py :language: python :start-after: doc-region-begin 96ff95610f3df28ee581dcad279dce732a45920c :end-before: doc-region-end 96ff95610f3df28ee581dcad279dce732a45920c * We're still near the beginning of the event loop, and we haven't drawn the paddles yet. So we call the function to query the user input, which will also modify the vertices' values if there was input. .. literalinclude:: ../src/demo04/demo.py :language: python :start-after: doc-region-begin 43814a73075f8265e8b55941fded5bd024914743 :end-before: doc-region-end 43814a73075f8265e8b55941fded5bd024914743 * While rendering, we now loop over the verticies of the paddle. The paddles may be displaced from their original position that was hardcoded, as the callback may have updated the values based off of the user input. * When glVertex is now called, we are not directly passing numbers into it, but instead we are getting the numbers from the data structures of Paddle and its associated verticies. .. figure:: static/plot3.png :align: center :alt: Adding input offset :figclass: align-center Adding input offset to Paddle 1 .. literalinclude:: ../src/demo04/demo.py :language: python :start-after: doc-region-begin 2126570070cea12469df9ade20858acf7ac414c7 :end-before: doc-region-end 2126570070cea12469df9ade20858acf7ac414c7 .. figure:: static/plot4.png :align: center :alt: Adding input offset to Paddle 1 :figclass: align-center Adding input offset to Paddle 2 .. literalinclude:: ../src/demo04/demo.py :language: python :start-after: doc-region-begin cda9e45b9bd0c4a866156e72290667c32015ea59 :end-before: doc-region-end cda9e45b9bd0c4a866156e72290667c32015ea59