From 8c2d705f94fd60b109d6dd1129e2f755d1558d1d Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Thu, 19 Jun 2025 07:53:36 -0700 Subject: [PATCH 01/11] Update README and lesson files for clarity and corrections - Fixed typos and formatting in README.md for better readability. - Updated assignment instructions in 00_Motion_and_Physics.ipynb for consistency. - Added detailed assignment descriptions in 01_Tom_the_Turtle.py and 02_Gravity_Bounce.ipynb. - Corrected assignment numbering in 02_Gravity_Bounce.ipynb and 03_gravity_bounce_obj.py. --- README.md | 21 ++++++++----- .../00_Motion_and_Physics.ipynb | 31 ++++++++++++------- .../01_Tom_the_Turtle.py | 22 +++++++++++++ .../02_Gravity_Bounce.ipynb | 2 +- .../03_gravity_bounce_obj.py | 13 ++++++++ 5 files changed, 69 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 4f7b9e4..dd8bed0 100644 --- a/README.md +++ b/README.md @@ -5,20 +5,27 @@ https://python-apprentice.jointheleague.org/) class, you are ready to continue your Python journey by learning to program games. To get started, either clone this repository to your local machine, or fork and -create a Codespace, then open the file [lessons/00_Getting_Started/README.md](lessons/00_Getting_Started/README.md). +create a Codespace, then open the file +[lessons/00_Getting_Started/README.md](lessons/00_Getting_Started/README.md). This will give you an overview of the module and how to get started. -The easiest way to get started is, from the [Python-Games Github Repo](https://github.com/league-curriculum/Python-Games), -click on the green ![code](https://images.jointheleague.org/github/code_button_sm.png) button, then select the 'Codespaces' tab, then ![ccom](https://images.jointheleague.org/github/create_codespace_sm.png). -This will create a codespace for you to work in, which has a web based IDE and all the necessary tools to get started. +The easiest way to get started is, from the [Python-Games Github Repo](https://github.com/league-curriculum Python-Games), click on the green +![code](https://images.jointheleague.org/github/code_button_sm.png) button, then +select the 'Codespaces' tab, then +![ccom](https://images.jointheleague.org/github/create_codespace_sm.png). This +will create a codespace for you to work in, which has a web based IDE and all +the necessary tools to get started. -It's easiest to read the lesson documentation on Github; just click on the link in the `Next Steps` section below, but you -will also see all of the documentation in your coding editor. +It's easiest to read the lesson documentation on Github; just click on the link +in the `Next Steps` section below, but you will also see all of the +documentation in your coding editor. ## Next Steps -Open the file [lessons/00_Getting_Started/README.md](lessons/00_Getting_Started/README.md) to begin the lessons. +Open the file +[lessons/00_Getting_Started/README.md](lessons/00_Getting_Started/README.md) to +begin the lessons. ------------------- diff --git a/lessons/01_Motion_and_Physics/00_Motion_and_Physics.ipynb b/lessons/01_Motion_and_Physics/00_Motion_and_Physics.ipynb index 0d7c16e..144f3e7 100644 --- a/lessons/01_Motion_and_Physics/00_Motion_and_Physics.ipynb +++ b/lessons/01_Motion_and_Physics/00_Motion_and_Physics.ipynb @@ -20,7 +20,7 @@ "\n", "## Assignment 1\n", "\n", - "1. Open the program `01-move.py` \n", + "1. Open the program `01_move.py` \n", "2. Run the program and see what it does. Use the arrow keys to move the square\n", " around the screen.\n", "3. Read the code and try to understand how it works.\n", @@ -83,7 +83,7 @@ "\n", "## Assignment 3\n", "\n", - "The most common game mechanic with acelleration is jumping. Let's look at\n", + "The most common game mechanic with acceleration is jumping. Let's look at\n", "what it takes to make a player jump.\n", "\n", "1. Open `04_gravity.py` \n", @@ -112,9 +112,10 @@ "collision is to just not let the player move into the wall, but a more realistic\n", "way is to bounce the player off the wall.\n", "\n", - "Collisions with the edges of the screen are simple to detect: the player's position in x or y either\n", - "exceeds the screen size or goes below zero. Making the player bounce is also easy: just reverse the\n", - "velocity in the direction of the collision. \n", + "Collisions with the edges of the screen are simple to detect: the player's\n", + "position in x or y either exceeds the screen size or goes below zero. Making the\n", + "player bounce is also easy: just reverse the velocity in the direction of the\n", + "collision. \n", "\n", "```python\n", "if player.x < 0 or player.x > screen_width:\n", @@ -126,7 +127,7 @@ "\n", "## Assignment 2\n", "\n", - "1. Copy `05_gravity_bounce.py` into this directory.\n", + "1. Open and read through `05_gravity_bounce.py`.\n", "2. Change the program so that the player doesn't always jump. Instead, the player\n", " jumps only when the `space` key is pressed.\n", "3. Change the program so that the player can jump to the left or right by pressing\n", @@ -136,14 +137,18 @@ " less than 0.1, set it to zero. This will make the player slow down and stop.\n", " If the Players velocity is zero, the player can jump again. The player\n", " shoud bounce off the ground.\n", + "5. Change the program to account for elasticity. The ball should lose a bit of\n", + " energy each time it bounces. \n", "\n", "Hints: \n", "\n", - "* Jumping up will involve setting the y velocity, and jumping left or right\n", - "will also involve setting the x velocity.\n", + "* Jumping up will involve setting the y velocity, and jumping left or right will\n", + " also involve setting the x velocity.\n", "* You can implement drag either by subtracting off a little bit of the velocity,\n", " but you will need to make sure the velocity doesn't go negative. Another way\n", - " to do this is to multiply the velocity by a number less than 1, like 0.99.\n", + " to do this is to multiply the velocity by a number less than 1, like 0.99.\n", + "* Elasticity works a lot like drag, except it only causes a reduction in\n", + " velocity when the ball bounces, not every time step. \n", "\n", "\n", "## Next Steps\n", @@ -151,10 +156,12 @@ "First, check in your code! You should check in ( commit ) and push your code\n", "regularly. \n", "\n", - "Second, don't forget about the [Pygame Documentation](https://www.pygame.org/docs/index.html). Your programs will\n", - "get better the more you know about Pygame.\n", + "Second, don't forget about the \n", + "[Pygame Documentation](https://www.pygame.org/docs/index.html). \n", + "Your programs will get better the more you know about Pygame.\n", "\n", - "Open [lessons/02_Classes_and_Objects/README.md](../02_Classes_and_Objects/README.md) to start the next lesson. " + "Open [lessons/02_Classes_and_Objects/README.md](../02_Classes_and_Objects/README.md) \n", + "to start the next lesson. " ] } ], diff --git a/lessons/02_Classes_and_Objects/01_Tom_the_Turtle.py b/lessons/02_Classes_and_Objects/01_Tom_the_Turtle.py index 91be936..f7b447a 100644 --- a/lessons/02_Classes_and_Objects/01_Tom_the_Turtle.py +++ b/lessons/02_Classes_and_Objects/01_Tom_the_Turtle.py @@ -4,6 +4,28 @@ way to introduce programming, so let's make something similar in PyGame, using objects. +This program is for assignment 3 and 4 + +Assignment 3: + +1. Read and run the program `01_Tom_the_Turtle.py` in this lession directory. +2. Create a derived class from the `Turtle` class that add some new behavior. + Add a function `right` that turns the turtle to the right. ( Bonus, use the + `left` function to implement the `right` function ) +3. In your derived class, add a new variable `color` and a way to set it. Use + that color to set the color of the turtle's line +4. Add a `pen_up` and `pen_down` function that will raise and lower the pen. + +Assignment 4: + +1. Create a function ( not a method, the function should not be part of a class) + in your `01_Tom_the_Turtle` program that takes a Turtle object and prints + out the x and y position of the turtle. +2. Show that your function works by creating a turtle, moving it around, and + then calling your function, for both the base `Turtle` class and your derived + class. + + """ import math diff --git a/lessons/02_Classes_and_Objects/02_Gravity_Bounce.ipynb b/lessons/02_Classes_and_Objects/02_Gravity_Bounce.ipynb index fba0f7d..7d44054 100644 --- a/lessons/02_Classes_and_Objects/02_Gravity_Bounce.ipynb +++ b/lessons/02_Classes_and_Objects/02_Gravity_Bounce.ipynb @@ -9,7 +9,7 @@ "Now that we have a basic understanding of how objects work, we can work on an\n", "updated version of the Gravity Bounce program. \n", "\n", - "## Assignment\n", + "## Assignment 5\n", "\n", "1. Open `03_gravity_bounce_obj.py` \n", "2. Review the program and try to understand how it works.\n", diff --git a/lessons/02_Classes_and_Objects/03_gravity_bounce_obj.py b/lessons/02_Classes_and_Objects/03_gravity_bounce_obj.py index d6e6375..f67edf2 100644 --- a/lessons/02_Classes_and_Objects/03_gravity_bounce_obj.py +++ b/lessons/02_Classes_and_Objects/03_gravity_bounce_obj.py @@ -6,6 +6,19 @@ a separate class. This makes the code easier to read and understand, and allows for more complex games with multiple objects. +## Assignment 5 + +1. Open `03_gravity_bounce_obj.py` +2. Review the program and try to understand how it works. +3. Change the program so that the player's initial velocity and position are set + in the initializer to the `Player` class. +4. Add a color for the player, configurable in the initializer. +5. Add a second player to the game. The second player should be a different + color and have different initial position and velocity. + +When you are done, your program should have two player objects ( but only one +Player class!), of different colors, bouncing around in different trajectories. + """ import pygame From 2ae57124fc90d98f63444a44c6fedbbcb5d389c1 Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Thu, 19 Jun 2025 09:33:35 -0700 Subject: [PATCH 02/11] Refactor vector lesson content for clarity and conciseness --- lessons/03_Vectors/00_Vectors.ipynb | 255 ++++++++++++++++++---- lessons/03_Vectors/02_Using_Vectors.ipynb | 2 - 2 files changed, 208 insertions(+), 49 deletions(-) diff --git a/lessons/03_Vectors/00_Vectors.ipynb b/lessons/03_Vectors/00_Vectors.ipynb index b804133..9ab73f5 100644 --- a/lessons/03_Vectors/00_Vectors.ipynb +++ b/lessons/03_Vectors/00_Vectors.ipynb @@ -16,7 +16,17 @@ "* The other is as a direction and a magnitude, which is a line with a length and\n", "a direction.\n", "\n", - "Here is what the vector (x=8, y=8) looks like:\n", + "These two was are actually the same. Any point in space, (x,y) is relative to\n", + "the origin, which is the point at (0,0), so for the first way of thinking about\n", + "a vector, a point in space, (x, y) we also imiagine that there is a line from\n", + "the origin (0,0) to the point (x,y), and that line will have a length and a\n", + "direction, which is our second way of thinking about vectors. \n", + "\n", + "There are a lot of videos and web pages to help you understand vectors in more\n", + "depth, [here is a good one](https://youtu.be/VqrYlDcZQ54?feature=shared).\n", + "\n", + "Here is what the vector (x=8, y=8) looks like when we show it on the X/Y coordinate plane, \n", + "starting from the origin. \n", "\n", "![Vector](images/v88.png)\n", "\n", @@ -25,13 +35,23 @@ "angle of 45 degrees. So, it can both be a point and a line, as long as we can\n", "assume a starting point for the line. \n", "\n", + "( But wait ... why are there only two numbers in a vector? Can there be more?\n", + "Yes! You can have any number of numbers in a vector. We are using 2 numbers\n", + "because our games are 2 dimensional. But if we were creating 3D games, our\n", + "vectors would have 3 numbers, and mathematicians and physicists often use\n", + "vectors that have more numbers. )\n", + "\n", + "Since we have a new kind of mathematical object, vectors, that have two numbers\n", + "in them, like (8,8), we are going to need a new work to refer to regular\n", + "numbers, like just plain old '8': these regular numbers are called \"scalars\".\n", + "\n", "Here is a really simple Vector class that we can use to represent vectors:\n", "\n" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -80,22 +100,140 @@ "\n", "\n", "As you can see, your create the vector by passing in the x and y values, and\n", - "then you can calculate the length and angle using trigonometry.\n", + "then you can calculate the length and angle using trigonometry.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are two very important things that you can do with a vector: we can\n", + "multiply a vector by a scalar, and we can add two vectors together. \n", + "\n", + "* vector * scalar: Multiply the elements of the vector by the scalar\n", + "* vector + vector: Add the elements in each position to create a new vector\n", + "\n", + "Let's suppose that we have the vectors (2,3) and (4,5) and the scalar 5.\n", + "\n", + "* (2,3) * 5 = ( 2*5, 3*5) = (10,15)\n", + "* (2,3) + (4,5) = ( 2+4, 3+5 ) \n", + "\n", + "Multiplying a vector by a scalar is called \"scaling\" the vector; it makes the\n", + "vector longer or shorter, but does not change its direction. Adding two vectors\n", + "combines the vectors and produces a new vectors that can have a different\n", + "magnitude and direction. \n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Assignment\n", + "\n", + "In the code cell below, update our Vector class with implmentations of the special methods for multiplication ( `__mul__`) and \n", + "addition ( `__add__`)\n", + "\n", + "You can check the type of an argument with code like this: \n", + "\n", + "```python\n", + "if isinstance(scalar, (int, float)):\n", + " print('Yes, it is a scalar')\n", + "else:\n", + " print('No not a scalar')\n", + "```\n", + "\n", + "\n", + "If the user has provided the wrong types for the arguments, you can signal an error with a line of code like this: \n", + "\n", + "```python\n", + " raise TypeError(\"Can only multiply Vector by a scalar (int or float)\")\n", + " ```\n", + "\n", + "Both of your implementations must return a new Vector, so they should have code like this:\n", + "\n", + "```python \n", + "return Vector( x_value, y_value)\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Vector: Vector(4, 4)\n", + "Magnitude: 5.656854249492381\n", + "Direction: 45.0°\n" + ] + } + ], + "source": [ + "import math\n", + "\n", + "class Vector:\n", + " def __init__(self, x, y):\n", + " self.x = x\n", + " self.y = y\n", + "\n", + " @property\n", + " def magnitude(self): # magnitude is the length of the vector\n", + " # Magnitude of the vector: sqrt(x^2 + y^2)\n", + " return math.sqrt(self.x**2 + self.y**2)\n", + "\n", + " @property\n", + " def direction(self):\n", + " # Direction (angle) in radians: atan2(y, x)\n", + " return math.atan2(self.y, self.x) * 180 / math.pi\n", + "\n", + " def __repr__(self):\n", + " return f\"Vector({self.x}, {self.y})\"\n", + " \n", + "\n", + " def __mul__(self, scalar):\n", + " \"\"\"Multiply the vector by a scalar value.\"\"\"\n", + "\n", + " pass\n", + "\n", + " def __add__(self, other):\n", + " pass\n", "\n", - "If we have a vector that is a position, we can add two vectors together to get a\n", - "new vector that is the sum of the two. This is called vector addition. If we\n", - "have a vector that is a direction and magnitude, we can multiply the vector by a\n", - "scalar ( a single number ) to get a new vector that is the original vector\n", - "multiplied by the scalar. This is called scalar multiplication.\n", + "# Example usage:\n", + "v = Vector(4, 4)\n", + "print(f\"Vector: {v}\")\n", + "print(f\"Magnitude: {v.magnitude}\")\n", + "print(f\"Direction: {v.direction}°\")\n", + "\n", + "# Add more example usage to test the __mul__ and __add__ methods\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## PyGame Vector2\n", + "\n", + "Now that you've done all that work creating your own vector class, we aren't going to use it anymore. Sorry, sometimes you write code you don't use, but we wanted you to really understand vectors. \n", + "\n", + "Instead, we are going to use the Vector class that is built into Pygame: \n", + "\n", + "```python\n", + "from pygame.math import Vector2\n", "\n", - "So, suppose that we have a vector called `p1`, which is the position of the player, at\n", + "```\n", + "\n", + "Suppose that we have a vector called `p1`, which is the position of the player, at\n", "x=100 and y=100. We can write this as:\n", "\n" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -104,7 +242,7 @@ "" ] }, - "execution_count": 7, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -115,7 +253,37 @@ "\n", "p1 = Vector2(100, 100)\n", "\n", - "p1" + "p1 " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When you add vectors, you really just add the x and y values together. " + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[30, 30]\n" + ] + } + ], + "source": [ + "from pygame import Vector2\n", + "\n", + "v1 = Vector2(10, 10 )\n", + "v2 = Vector2(20, 20 )\n", + "\n", + "\n", + "print(v1 + v2) # Same as Vector(10+20, 10+20) = Vector(30, 30)" ] }, { @@ -131,7 +299,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -140,16 +308,17 @@ "" ] }, - "execution_count": 8, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\n", + "p1 = Vector2(100, 100)\n", "move = Vector2(10, 0) # Move 10 pixels to the right, none up/down\n", "\n", - "p1 += move\n", + "p1 += move # same as: p1 = p1 + move\n", "p1" ] }, @@ -169,7 +338,7 @@ "source": [ "\n", "For a more detaied example. run the program `01a_vector_example.py.` This program has a custom version\n", - "of the vector class that displays on a larger grid. Here is what the programs output looks like:\n", + "of the vector class that displays on a larger grid. Here is what the program's output looks like:\n", "\n", "![Vector Example](images/vector_grid.png)\n", "\n", @@ -191,12 +360,18 @@ "\n", "\n", "## Rotations\n", - "Or, suppose that we want to move the player 100 pixels in the direction of 45 degrees. We can write this as:\n" + "\n", + "Since vectors have a direction, it would make sense that we'd want to change the\n", + "direction. This operation is called a 'rotation'.\n", + "\n", + "Suppose that we want to move the player 100 pixels in the direction of 45\n", + "degrees. The easiest way to write this is to create the vector a 100 units long\n", + "in the x direction, then rotate it to 45 degrees. \n" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -205,14 +380,14 @@ "" ] }, - "execution_count": 9, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "p1 = Vector2(100, 100)\n", - "move = Vector2(100, 0)\n", + "p1 = Vector2(100, 100) # Player's current position\n", + "move = Vector2(100, 0) # 100 unit long vector\n", "move.rotate_ip(45) # rotate the vector \"in-place\"\n", "\n", "p1 += move\n", @@ -224,30 +399,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "When you add vectors, you really just add the x and y values together. \n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[30, 30]\n" - ] - } - ], - "source": [ - "from pygame import Vector2\n", - "\n", - "v1 = Vector2(10, 10 )\n", - "v2 = Vector2(20, 20 )\n", + "Notice the `_ip` suffix after `rotate`; this stands for \"in place\" and means we are going to change the vector. Without the `_ip` suffix, the `.rotate()` will return a new vector, so we'd have to write it like this: \n", "\n", - "\n", - "print(v1 + v2)" + "```python\n", + "move = move.rotate()\n", + "```" ] }, { @@ -260,7 +416,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -291,7 +447,12 @@ "\n", "Draw a square with Vectors. \n", "\n", + "For this assignment we will be using our own vector class, `Vector20` which is\n", + "based on `Vector2`, but has extensions for plotting a grid with numbers and\n", + "labels. Use it just like you would a `Vector2`\n", + "\n", "1. Open and read `01a_vector_example.py` and run the program.\n", + "2. Copy the code to a new file, `01b_vector_assignment.py`\n", "2. Create vector v1 with x = 0 and y = 1 \n", "3. Scale the vector by 10 with multiplication, then rotate it by 90 degrees so\n", " it points to the right.\n", @@ -326,7 +487,7 @@ ], "metadata": { "kernelspec": { - "display_name": ".venv", + "display_name": "Python-Games", "language": "python", "name": "python3" }, @@ -340,7 +501,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.0" + "version": "3.13.3" } }, "nbformat": 4, diff --git a/lessons/03_Vectors/02_Using_Vectors.ipynb b/lessons/03_Vectors/02_Using_Vectors.ipynb index 4c4d827..7a29b24 100644 --- a/lessons/03_Vectors/02_Using_Vectors.ipynb +++ b/lessons/03_Vectors/02_Using_Vectors.ipynb @@ -6,8 +6,6 @@ "source": [ "# Vector Example Program\n", "\n", - "( Use \"Open Preview\" to see the images in this document. )\n", - "\n", "The program `03_vectors.py` is a simple program that demonstrates how\n", "to use vectors to move an object around the screen.\n", "\n", From fe9c34d216b009b98639b3639c18d733dc81aa3c Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Thu, 19 Jun 2025 11:26:04 -0700 Subject: [PATCH 03/11] renaming --- .devcontainer/devcontainer.json | 3 +-- .../00_Physics_for_Games.ipynb} | 3 +-- .../{01_Motion_and_Physics => 01_Physics_for_Games}/01_move.py | 0 .../02_no_acceleration.py | 0 .../03_acceleration.py | 0 .../04_gravity.py | 0 .../05_gravity_bounce.py | 0 .../{01_Motion_and_Physics => 01_Physics_for_Games}/README.md | 0 8 files changed, 2 insertions(+), 4 deletions(-) rename lessons/{01_Motion_and_Physics/00_Motion_and_Physics.ipynb => 01_Physics_for_Games/00_Physics_for_Games.ipynb} (99%) rename lessons/{01_Motion_and_Physics => 01_Physics_for_Games}/01_move.py (100%) rename lessons/{01_Motion_and_Physics => 01_Physics_for_Games}/02_no_acceleration.py (100%) rename lessons/{01_Motion_and_Physics => 01_Physics_for_Games}/03_acceleration.py (100%) rename lessons/{01_Motion_and_Physics => 01_Physics_for_Games}/04_gravity.py (100%) rename lessons/{01_Motion_and_Physics => 01_Physics_for_Games}/05_gravity_bounce.py (100%) rename lessons/{01_Motion_and_Physics => 01_Physics_for_Games}/README.md (100%) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 1c48802..42b679f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -48,8 +48,7 @@ "customizations": { "codespaces": { "openFiles": [ - //"lessons/00_Turtles/01_Get_Started.ipynb" - "README.md" + "lessons/01_Physics_for_Games/00_Physics_for_Games.ipynb" ] }, "vscode": { diff --git a/lessons/01_Motion_and_Physics/00_Motion_and_Physics.ipynb b/lessons/01_Physics_for_Games/00_Physics_for_Games.ipynb similarity index 99% rename from lessons/01_Motion_and_Physics/00_Motion_and_Physics.ipynb rename to lessons/01_Physics_for_Games/00_Physics_for_Games.ipynb index 144f3e7..e4b715e 100644 --- a/lessons/01_Motion_and_Physics/00_Motion_and_Physics.ipynb +++ b/lessons/01_Physics_for_Games/00_Physics_for_Games.ipynb @@ -4,8 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Motion and Physics\n", - "\n", + "# Physics for Games\n", "\n", "Most of the games we will be writing have some physics, which is the motion of\n", "objects in the game that should look realistic, like jumping or falling. Pygame\n", diff --git a/lessons/01_Motion_and_Physics/01_move.py b/lessons/01_Physics_for_Games/01_move.py similarity index 100% rename from lessons/01_Motion_and_Physics/01_move.py rename to lessons/01_Physics_for_Games/01_move.py diff --git a/lessons/01_Motion_and_Physics/02_no_acceleration.py b/lessons/01_Physics_for_Games/02_no_acceleration.py similarity index 100% rename from lessons/01_Motion_and_Physics/02_no_acceleration.py rename to lessons/01_Physics_for_Games/02_no_acceleration.py diff --git a/lessons/01_Motion_and_Physics/03_acceleration.py b/lessons/01_Physics_for_Games/03_acceleration.py similarity index 100% rename from lessons/01_Motion_and_Physics/03_acceleration.py rename to lessons/01_Physics_for_Games/03_acceleration.py diff --git a/lessons/01_Motion_and_Physics/04_gravity.py b/lessons/01_Physics_for_Games/04_gravity.py similarity index 100% rename from lessons/01_Motion_and_Physics/04_gravity.py rename to lessons/01_Physics_for_Games/04_gravity.py diff --git a/lessons/01_Motion_and_Physics/05_gravity_bounce.py b/lessons/01_Physics_for_Games/05_gravity_bounce.py similarity index 100% rename from lessons/01_Motion_and_Physics/05_gravity_bounce.py rename to lessons/01_Physics_for_Games/05_gravity_bounce.py diff --git a/lessons/01_Motion_and_Physics/README.md b/lessons/01_Physics_for_Games/README.md similarity index 100% rename from lessons/01_Motion_and_Physics/README.md rename to lessons/01_Physics_for_Games/README.md From f705e46dc8d63ea168a4a0ffcce5a9ad9b4ac1c5 Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Mon, 23 Jun 2025 22:01:50 -0700 Subject: [PATCH 04/11] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dd8bed0..ba5ea2b 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ create a Codespace, then open the file [lessons/00_Getting_Started/README.md](lessons/00_Getting_Started/README.md). This will give you an overview of the module and how to get started. -The easiest way to get started is, from the [Python-Games Github Repo](https://github.com/league-curriculum Python-Games), click on the green +The easiest way to get started is, from the [Python-Games Github Repo](https://github.com/league-curriculum/Python-Games), click on the green ![code](https://images.jointheleague.org/github/code_button_sm.png) button, then select the 'Codespaces' tab, then ![ccom](https://images.jointheleague.org/github/create_codespace_sm.png). This From d19285496cf7fce88c08c88cfedd768ae1a1a989 Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Tue, 24 Jun 2025 10:40:37 -0700 Subject: [PATCH 05/11] Update README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index ba5ea2b..93f2c97 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,3 @@ Open the file [lessons/00_Getting_Started/README.md](lessons/00_Getting_Started/README.md) to begin the lessons. -------------------- - -Development of The LEAGUE's curriculum is generously funded by the Itzkowitz Family Foundation. From 17aa2fa7cea97aa1a4a6351884e2fa67f25ef7a1 Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Fri, 27 Jun 2025 21:16:10 +0000 Subject: [PATCH 06/11] Refactor physics calculations in movement lessons for consistency and clarity --- lessons/01_Physics_for_Games/01_move.py | 36 +++-- .../02_no_acceleration.py | 23 ++- .../01_Physics_for_Games/03_acceleration.py | 20 ++- lessons/01_Physics_for_Games/04_gravity.py | 33 ++-- lessons/01_Physics_for_Games/scratch.ipynb | 143 ++++++++++++++++++ 5 files changed, 217 insertions(+), 38 deletions(-) create mode 100644 lessons/01_Physics_for_Games/scratch.ipynb diff --git a/lessons/01_Physics_for_Games/01_move.py b/lessons/01_Physics_for_Games/01_move.py index ab944bf..fefe326 100644 --- a/lessons/01_Physics_for_Games/01_move.py +++ b/lessons/01_Physics_for_Games/01_move.py @@ -16,9 +16,12 @@ SQUARE_SIZE = 50 SQUARE_COLOR = (0, 128, 255) # Red-Green-Blue color in the range 0-255 BACKGROUND_COLOR = (255, 255, 255) # White -SQUARE_SPEED = 5 +SQUARE_SPEED = 300 FPS = 60 +v = SQUARE_SPEED # Speed of the square in pixels per second +d_t = 1 / FPS # Time step for physics calculations + # Initialize the screen screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption("Move the Square") @@ -29,8 +32,8 @@ # Main function def main(): # Initial position of the square - square_x = SCREEN_WIDTH // 2 - SQUARE_SIZE // 2 - square_y = SCREEN_HEIGHT // 2 - SQUARE_SIZE // 2 + x = SCREEN_WIDTH // 2 - SQUARE_SIZE // 2 + y = SCREEN_HEIGHT // 2 - SQUARE_SIZE // 2 running = True @@ -48,19 +51,32 @@ def main(): # with a boolean value of whether they are pressed or not keys = pygame.key.get_pressed() + + # Calculate the change tin the position + d_x = 0 + d_y = 0 + # Move the square based on arrow keys if keys[pygame.K_LEFT]: - square_x -= SQUARE_SPEED + d_x = -v * d_t + if keys[pygame.K_RIGHT]: - square_x += SQUARE_SPEED + d_x = v * d_t + if keys[pygame.K_UP]: - square_y -= SQUARE_SPEED + d_y = -v * d_t + if keys[pygame.K_DOWN]: - square_y += SQUARE_SPEED + d_y = v * d_t + + # Update the position of the square + x = x + d_x + y = y + d_y + # Prevent the square from going off the screen - square_x = max(0, min(SCREEN_WIDTH - SQUARE_SIZE, square_x)) - square_y = max(0, min(SCREEN_HEIGHT - SQUARE_SIZE, square_y)) + x = max(0, min(SCREEN_WIDTH - SQUARE_SIZE, x)) + y = max(0, min(SCREEN_HEIGHT - SQUARE_SIZE, y)) # This will clear the screen by filling it # with the background color. If we didn't do this, @@ -68,7 +84,7 @@ def main(): screen.fill(BACKGROUND_COLOR) # Draw the square - pygame.draw.rect(screen, SQUARE_COLOR, (square_x, square_y, SQUARE_SIZE, SQUARE_SIZE)) + pygame.draw.rect(screen, SQUARE_COLOR, (x, y, SQUARE_SIZE, SQUARE_SIZE)) # Update the display. Imagine that the screen is two different whiteboards. One # whiteboard is currently visible to the player, and the other whiteboard is being diff --git a/lessons/01_Physics_for_Games/02_no_acceleration.py b/lessons/01_Physics_for_Games/02_no_acceleration.py index e3edfb3..010908f 100644 --- a/lessons/01_Physics_for_Games/02_no_acceleration.py +++ b/lessons/01_Physics_for_Games/02_no_acceleration.py @@ -10,15 +10,19 @@ SCREEN_WIDTH, SCREEN_HEIGHT = 600, 600 SQUARE_SIZE = 50 SQUARE_COLOR = (255, 0, 0) # Red -SQUARE_SPEED = 5 +SQUARE_SPEED = 300 + +FPS = 60 # Frames per second + +d_t = 1 / FPS # Time step for physics calculations # Set up the display screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption("Moving Red Square") # Square starting position -x_pos = 0 -y_pos = (SCREEN_HEIGHT - SQUARE_SIZE) // 2 +x = 0 +y = (SCREEN_HEIGHT - SQUARE_SIZE) // 2 # Movement direction: 1 for right, -1 for left direction = 1 @@ -31,25 +35,28 @@ running = False # Move the square, a bit each frame - x_pos += SQUARE_SPEED * direction + + d_x = SQUARE_SPEED * direction * d_t + + x += d_x # Check for screen bounds and reverse direction if necessary - if x_pos + SQUARE_SIZE > SCREEN_WIDTH: + if x + SQUARE_SIZE > SCREEN_WIDTH: direction = -1 # Move left - elif x_pos < 0: + elif x < 0: direction = 1 # Move right # Fill the screen with black (clears previous frame) screen.fill((0, 0, 0)) # Draw the red square - pygame.draw.rect(screen, SQUARE_COLOR, (x_pos, y_pos, SQUARE_SIZE, SQUARE_SIZE)) + pygame.draw.rect(screen, SQUARE_COLOR, (x, y, SQUARE_SIZE, SQUARE_SIZE)) # Update the display pygame.display.flip() # Frame rate control - pygame.time.Clock().tick(60) + pygame.time.Clock().tick(FPS) # Quit Pygame pygame.quit() diff --git a/lessons/01_Physics_for_Games/03_acceleration.py b/lessons/01_Physics_for_Games/03_acceleration.py index 73f1591..95b36b5 100644 --- a/lessons/01_Physics_for_Games/03_acceleration.py +++ b/lessons/01_Physics_for_Games/03_acceleration.py @@ -7,7 +7,8 @@ SCREEN_WIDTH, SCREEN_HEIGHT = 600, 600 SQUARE_SIZE = 50 SQUARE_COLOR = (255, 0, 0) # Red -K = .0004 +FPS = 60 +K = 3 # Spring constant, controls how strong the spring force is # Set up the display screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) @@ -16,6 +17,10 @@ # Square starting position x_pos = 20 y_pos = (SCREEN_HEIGHT - SQUARE_SIZE) // 2 + +d_t = 1 / FPS # Time step for physics calculations + +mass = 2.0 # Mass of the square, used to calculate acceleration velocity = 0 # Movement direction: 1 for right, -1 for left @@ -27,22 +32,25 @@ while running: for event in pygame.event.get(): if event.type == pygame.QUIT: - running = False # Calculate the spring force, which is the force that pulls the square back # to the center, as if it was attached to a spring - a = -K * (x_pos - (SCREEN_WIDTH-SQUARE_SIZE) // 2) + + + # Calculate the spring force, accounting for mass (F = -k*x) + # Force divided by mass gives acceleration (F = ma → a = F/m) + a = (-K * (x_pos - (SCREEN_WIDTH-SQUARE_SIZE) // 2)) / mass # Update the velocity with the acceleration. Notice that we change # the velocity by adding the acceleration, not setting it to the acceleration, # and we change it a bit each frame. - velocity += a + velocity += a * d_t # Update the position with the velocity. Like with the velocity, we change # the position by adding the velocity, not setting it to the velocity, and # we change it a bit each frame. - x_pos += velocity + x_pos += velocity * d_t # Fill the screen with black (clears previous frame) screen.fill((0, 0, 0)) @@ -54,7 +62,7 @@ pygame.display.flip() # Frame rate control - pygame.time.Clock().tick(60) + pygame.time.Clock().tick(FPS) # Quit Pygame pygame.quit() diff --git a/lessons/01_Physics_for_Games/04_gravity.py b/lessons/01_Physics_for_Games/04_gravity.py index 26ff474..0a96361 100644 --- a/lessons/01_Physics_for_Games/04_gravity.py +++ b/lessons/01_Physics_for_Games/04_gravity.py @@ -26,17 +26,19 @@ class GameSettings: screen_height: int = 500 player_size: int = 10 player_x: int = 100 # Initial x position of the player - gravity: float = 0.3 # acelleration, the change in velocity per frame - jump_velocity: int = 15 + + jump_velocity: int = 200 white: tuple = (255, 255, 255) black: tuple = (0, 0, 0) - tick_rate: int = 30 # Frames per second + + gravity: float = 60.0 # acceleration, the change in velocity per frame + d_t: float = 1.0/30 + m: float = 2.0 # mass of the player, used to calculate acceleration # Initialize game settings settings = GameSettings() - # Initialize screen screen = pygame.display.set_mode((settings.screen_width, settings.screen_height)) @@ -45,7 +47,6 @@ class GameSettings: settings.screen_height - settings.player_size, settings.player_size, settings.player_size) -player_y_velocity = 0 is_jumping = False # Main game loop @@ -64,15 +65,19 @@ class GameSettings: # Jumping means that the player is going up. The top of the # screen is y=0, and the bottom is y=SCREEN_HEIGHT. So, to go up, # we need to have a negative y velocity - player_y_velocity = -settings.jump_velocity + d_v_y = -settings.jump_velocity is_jumping = True - # Update player position. Gravity is always pulling the player down, - # which is the positive y direction, so we add GRAVITY to the y velocity - # to make the player go up more slowly. Eventually, the player will have - # a positive y velocity, and gravity will pull the player down. - player_y_velocity += settings.gravity - player.y += player_y_velocity + # acelleration in sht y direction + a_y = settings.gravity + + # Change in the velocity due to accelleration + d_v_y += a_y * settings.d_t + + # Change in the position due to the velocity + d_y = d_v_y * settings.d_t + + player.y += d_y # If the player hits the ground, stop the player from falling. # The player's position is measured from the top left corner, so the @@ -82,7 +87,7 @@ class GameSettings: # and stop the player from falling if player.bottom >= settings.screen_height: player.bottom = settings.screen_height - player_y_velocity = 0 + d_v_y = 0 is_jumping = False # Draw everything @@ -90,6 +95,6 @@ class GameSettings: pygame.draw.rect(screen, settings.black, player) pygame.display.flip() - clock.tick(settings.tick_rate) + clock.tick( int(1/settings.d_t)) pygame.quit() diff --git a/lessons/01_Physics_for_Games/scratch.ipynb b/lessons/01_Physics_for_Games/scratch.ipynb new file mode 100644 index 0000000..cf2235a --- /dev/null +++ b/lessons/01_Physics_for_Games/scratch.ipynb @@ -0,0 +1,143 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "id": "9c85ba9f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 + 1" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "d0e8dc19", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(17.1, 3.0999999999999996)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = 10.1\n", + "b = 7\n", + "a+b, a-b" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "751a7eea", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(3.1, 3.0999999999999996)" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "((a*10) - (b*10))/10, a-b" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "349897fb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "10000000000.0" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = 10**10\n", + "b = 10**-10\n", + "\n", + "(a+b)-b " + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "102b23fe", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "8" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "17//2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "de90e7d2", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From c125469b2a8580ccef03a43ae8685b94c15e4092 Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Fri, 27 Jun 2025 22:11:45 +0000 Subject: [PATCH 07/11] Refactor game settings and player mechanics for improved clarity and consistency --- .../01_Physics_for_Games/05_gravity_bounce.py | 112 ++++++++++-------- 1 file changed, 61 insertions(+), 51 deletions(-) diff --git a/lessons/01_Physics_for_Games/05_gravity_bounce.py b/lessons/01_Physics_for_Games/05_gravity_bounce.py index d176005..15d2166 100644 --- a/lessons/01_Physics_for_Games/05_gravity_bounce.py +++ b/lessons/01_Physics_for_Games/05_gravity_bounce.py @@ -10,37 +10,41 @@ from dataclasses import dataclass @dataclass -class Settings: +class GameSettings: """Class for keeping track of game settings and constants.""" screen_width: int = 500 screen_height: int = 500 - white: tuple = (255, 255, 255) - black: tuple = (0, 0, 0) - red: tuple = (255, 0, 0) - player_size: int = 20 - gravity: int = 1 - jump_y_velocity: int = 30 - jump_x_velocity: int = 10 + square_size: int = 20 + square_color: tuple = (0, 0, 0) # Black + background_color: tuple = (255, 255, 255) # White + fps: int = 30 + gravity: float = 60.0 # Acceleration due to gravity + jump_velocity_y: float = 200.0 # Initial jump velocity in y direction + jump_velocity_x: float = 100.0 # Initial jump velocity in x direction + d_t: float = 1.0/30 # Time step for physics calculations # Initialize Pygame pygame.init() -# Create an instance of Settings -settings = Settings() +# Initialize game settings +settings = GameSettings() # Initialize screen screen = pygame.display.set_mode((settings.screen_width, settings.screen_height)) +pygame.display.set_caption("Gravity Bounce") -# Define player -player = pygame.Rect(100, settings.screen_height - settings.player_size, settings.player_size, settings.player_size) +# Square starting position +x_pos = 100 +y_pos = settings.screen_height - settings.square_size -player_y_velocity = 0 -player_x_velocity = 0 -x_direction = 1 # Elther 1 or negative 1, so we can keep track for direction after hitting the ground +# Initial velocities +velocity_x = 0 +velocity_y = 0 +x_direction = 1 # Either 1 or -1, to keep track of direction after hitting the ground is_jumping = False -# Main game loop +# Main loop running = True clock = pygame.time.Clock() @@ -51,56 +55,62 @@ class Settings: if event.type == pygame.QUIT: running = False - # Continuously jump. If the player is not jumping, make it jump + # Continuously jump. If the square is not jumping, make it jump if is_jumping is False: - # Jumping means that the player is going up. The top of the - # screen is y=0, and the bottom is y=settings.screen_height. So, to go up, + # Jumping means that the square is going up. The top of the + # screen is y=0, and the bottom is y=screen_height. So, to go up, # we need to have a negative y velocity - player_y_velocity = -settings.jump_y_velocity - player_x_velocity = settings.jump_x_velocity * x_direction + velocity_y = -settings.jump_velocity_y + velocity_x = settings.jump_velocity_x * x_direction is_jumping = True - else: # the player is jumping - # Update player position. Gravity is always pulling the player down, + else: # the square is jumping + # Update square position. Gravity is always pulling the square down, # which is the positive y direction, so we add settings.gravity to the y velocity - # to make the player go up more slowly. Eventually, the player will have - # a positive y velocity, and gravity will pull the player down. + # to make the square go up more slowly. Eventually, the square will have + # a positive y velocity, and gravity will pull the square down. - player_y_velocity += settings.gravity - player.y += player_y_velocity - player.x += player_x_velocity + velocity_y += settings.gravity * settings.d_t - # If the player hits one side of the screen or the other, bounce the player - if player.left <= 0 or player.right >= settings.screen_width: - player_x_velocity = -player_x_velocity + # Update the position with the velocity. Like with the velocity, we change + # the position by adding the velocity, not setting it to the velocity, and + # we change it a bit each frame. + y_pos += velocity_y * settings.d_t + x_pos += velocity_x * settings.d_t - # One way to change direction. - x_direction = -x_direction - # But this way is more reliable, since it will always be 1 or -1 and dir is tied to velocity - x_direction = player_x_velocity // abs(player_x_velocity) - - # If the player hits the top of the screen, bounce the player - if player.top <= 0: - player_y_velocity = -player_y_velocity - - # If the player hits the ground, stop the player from falling. - if player.bottom > settings.screen_height: - player.bottom = settings.screen_height - player_y_velocity = 0 - - player_x_velocity = 0 + # If the square hits one side of the screen or the other, bounce the square + if x_pos <= 0 or x_pos + settings.square_size >= settings.screen_width: + velocity_x = -velocity_x + # Update direction tracking + x_direction = -x_direction + # This way is more reliable, since it will always be 1 or -1 and direction is tied to velocity + if velocity_x != 0: + x_direction = int(velocity_x / abs(velocity_x)) + + # If the square hits the top of the screen, bounce the square + if y_pos <= 0: + velocity_y = -velocity_y + + # If the square hits the ground, stop the square from falling. + if y_pos + settings.square_size > settings.screen_height: + y_pos = settings.screen_height - settings.square_size + velocity_y = 0 + velocity_x = 0 is_jumping = False + # Fill the screen with background color (clears previous frame) + screen.fill(settings.background_color) + # Draw the square + pygame.draw.rect(screen, settings.square_color, (x_pos, y_pos, settings.square_size, settings.square_size)) - # Draw everything - screen.fill(settings.white) - pygame.draw.rect(screen, settings.black, player) - + # Update the display pygame.display.flip() - clock.tick(30) + + # Frame rate control + clock.tick(settings.fps) pygame.quit() From 024c780eb7bb934071238dd2bfc48d5b431f8810 Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Fri, 27 Jun 2025 22:27:18 +0000 Subject: [PATCH 08/11] Clean up whitespace in 01_move.py for improved readability --- lessons/01_Physics_for_Games/01_move.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lessons/01_Physics_for_Games/01_move.py b/lessons/01_Physics_for_Games/01_move.py index fefe326..9ebc459 100644 --- a/lessons/01_Physics_for_Games/01_move.py +++ b/lessons/01_Physics_for_Games/01_move.py @@ -8,6 +8,7 @@ """ import pygame + # Initialize Pygame pygame.init() @@ -39,7 +40,6 @@ def main(): while running: - # Event handling for event in pygame.event.get(): @@ -51,7 +51,6 @@ def main(): # with a boolean value of whether they are pressed or not keys = pygame.key.get_pressed() - # Calculate the change tin the position d_x = 0 d_y = 0 From ed40407fcbdb132b719c25b7afff45c9ee4ba646 Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Sat, 19 Jul 2025 00:09:17 +0000 Subject: [PATCH 09/11] Remove Dockerfile and update devcontainer configuration for improved clarity and consistency --- .devcontainer/Dockerfile | 3 --- .devcontainer/devcontainer.json | 18 ++++-------------- 2 files changed, 4 insertions(+), 17 deletions(-) delete mode 100644 .devcontainer/Dockerfile diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index a8d9ab6..0000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM ghcr.io/league-infrastructure/jtlpython:20240719 -ENV VNC_RESOLUTION=600x600x16 - diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 42b679f..75750cd 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -3,32 +3,22 @@ { "name": "Python 3", - // This is the default - // "image": "mcr.microsoft.com/devcontainers/universal:2", - - // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - //"image": "mcr.microsoft.com/devcontainers/python:1-${templateOption:imageVariant}", - //"image": "mcr.microsoft.com/devcontainers/python:1-3.12-bookworm", - - "build": { - "dockerfile": "Dockerfile", - "context": ".." - }, + "image": "mcr.microsoft.com/devcontainers/python:1-3.12-bookworm", "containerEnv": { + "VNC_RESOLUTION": "600x600x16", "SDL_VIDEO_WINDOW_POS": "0,0", // GUI Windows all the way in the upper left. "SDL_AUDIODRIVER": "dummy" // Disable audio }, // Features to add to the dev container. More info: https://containers.dev/features. "features": { - "ghcr.io/devcontainers/features/desktop-lite:1": { + "ghcr.io/devcontainers/features/desktop-lite:1.2.7": { "password" : "code4life" } }, - // Use 'forwardPorts' to make a list of ports inside the container available locally. "forwardPorts": [6080, 5901], @@ -48,7 +38,7 @@ "customizations": { "codespaces": { "openFiles": [ - "lessons/01_Physics_for_Games/00_Physics_for_Games.ipynb" + "lessons/00_Getting_Started/README.md" ] }, "vscode": { From bcea3ca4a5f99c2c0bec368bc7c4e07ca04c945b Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Fri, 20 Jun 2025 15:06:56 -0700 Subject: [PATCH 10/11] . --- lessons/03_Vectors/00_Vectors.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lessons/03_Vectors/00_Vectors.ipynb b/lessons/03_Vectors/00_Vectors.ipynb index 9ab73f5..8f2ec2c 100644 --- a/lessons/03_Vectors/00_Vectors.ipynb +++ b/lessons/03_Vectors/00_Vectors.ipynb @@ -487,7 +487,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python-Games", + "display_name": ".venv", "language": "python", "name": "python3" }, From 2daaf8e7512c3deb4548fe364bd91d669761840d Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Tue, 22 Jul 2025 11:02:12 -0700 Subject: [PATCH 11/11] Fix typos and improve clarity in lesson notebooks; update player settings in gravity bounce game --- .devcontainer/package.json | 0 .devcontainer/vscode-setup.ts | 0 lessons/01_Physics_for_Games/scratch.ipynb | 143 ------------------ .../00_Classes_and_Objects.ipynb | 24 +-- .../01_Tom_the_Turtle.py | 2 +- .../03_gravity_bounce_obj.py | 34 +++-- 6 files changed, 35 insertions(+), 168 deletions(-) create mode 100644 .devcontainer/package.json create mode 100644 .devcontainer/vscode-setup.ts delete mode 100644 lessons/01_Physics_for_Games/scratch.ipynb diff --git a/.devcontainer/package.json b/.devcontainer/package.json new file mode 100644 index 0000000..e69de29 diff --git a/.devcontainer/vscode-setup.ts b/.devcontainer/vscode-setup.ts new file mode 100644 index 0000000..e69de29 diff --git a/lessons/01_Physics_for_Games/scratch.ipynb b/lessons/01_Physics_for_Games/scratch.ipynb deleted file mode 100644 index cf2235a..0000000 --- a/lessons/01_Physics_for_Games/scratch.ipynb +++ /dev/null @@ -1,143 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 2, - "id": "9c85ba9f", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "2" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "1 + 1" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "d0e8dc19", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(17.1, 3.0999999999999996)" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a = 10.1\n", - "b = 7\n", - "a+b, a-b" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "751a7eea", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(3.1, 3.0999999999999996)" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "((a*10) - (b*10))/10, a-b" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "349897fb", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "10000000000.0" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a = 10**10\n", - "b = 10**-10\n", - "\n", - "(a+b)-b " - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "102b23fe", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "8" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "17//2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "de90e7d2", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.4" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/lessons/02_Classes_and_Objects/00_Classes_and_Objects.ipynb b/lessons/02_Classes_and_Objects/00_Classes_and_Objects.ipynb index 4845473..9a9a69b 100644 --- a/lessons/02_Classes_and_Objects/00_Classes_and_Objects.ipynb +++ b/lessons/02_Classes_and_Objects/00_Classes_and_Objects.ipynb @@ -82,7 +82,7 @@ "Notice that we use the dot \".\" to reference things that are part of the object,\n", "so `alice.name` gets the `name` variable in the `alice` object \n", "\n", - "Now, we have both a class, `Person` and an obect of that class, `alice`.\n", + "Now, we have both a class, `Person` and an object of that class, `alice`.\n", "Remember our definition of an object from the start: it is a structure that has\n", "identity, state and behavior. \n", "\n", @@ -96,7 +96,8 @@ "metadata": {}, "source": [ "Now, of course, you actually already know a lot about objects. Here is part of\n", - "the first program in our first Python lesson :\n", + "the first program in our first Python lesson, back when you were writting turtle\n", + "programs:\n", "\n", "```python \n", "\n", @@ -151,7 +152,7 @@ "source": [ "## Assignment 1\n", "\n", - "Write a class for that describes an alien. The Alien will have these important variables:\n", + "Write a class that describes an alien. The Alien will have these important variables:\n", "\n", "* number of eyes\n", "* number of legs\n", @@ -169,11 +170,11 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "# Test Yourself\n", + "# Test Yourself by writing a class for an alien\n", "\n" ] }, @@ -184,7 +185,10 @@ "## Inheritance, Polymorphism and Overloading\n", "\n", "Inheritance is a way to create a new class that is based on an existing class. Inheritance allows you \n", - "to mosltly copy the existing class, but then add new variables and methods. For instance, here is our People class:\n", + "to mosltly copy the existing class, but then add new variables and methods. Just like a child might inherit a parents\n", + "eye color, classes can inhert properties from its parent. \n", + "\n", + "For instance, here is our People class:\n", "\n", "```python\n", "\n", @@ -247,7 +251,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, "outputs": [ { @@ -378,7 +382,7 @@ "## Assignment 3\n", "\n", "1. Read and run the program `01_Tom_the_Turtle.py` in this lession directory.\n", - "2. Create a derived class from the `Turtle` class that add some new behavior.\n", + "2. Create a derived class from the `Turtle` class that adds some new behavior.\n", " Add a function `right` that turns the turtle to the right. ( Bonus, use the\n", " `left` function to implement the `right` function )\n", "3. In your derived class, add a new variable `color` and a way to set it. Use\n", @@ -471,7 +475,7 @@ ], "metadata": { "kernelspec": { - "display_name": ".venv", + "display_name": "Python-Games", "language": "python", "name": "python3" }, @@ -485,7 +489,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.0" + "version": "3.13.3" } }, "nbformat": 4, diff --git a/lessons/02_Classes_and_Objects/01_Tom_the_Turtle.py b/lessons/02_Classes_and_Objects/01_Tom_the_Turtle.py index f7b447a..ec38e80 100644 --- a/lessons/02_Classes_and_Objects/01_Tom_the_Turtle.py +++ b/lessons/02_Classes_and_Objects/01_Tom_the_Turtle.py @@ -9,7 +9,7 @@ Assignment 3: 1. Read and run the program `01_Tom_the_Turtle.py` in this lession directory. -2. Create a derived class from the `Turtle` class that add some new behavior. +2. Create a derived class from the `Turtle` class that adds some new behavior. Add a function `right` that turns the turtle to the right. ( Bonus, use the `left` function to implement the `right` function ) 3. In your derived class, add a new variable `color` and a way to set it. Use diff --git a/lessons/02_Classes_and_Objects/03_gravity_bounce_obj.py b/lessons/02_Classes_and_Objects/03_gravity_bounce_obj.py index f67edf2..1b7e2f7 100644 --- a/lessons/02_Classes_and_Objects/03_gravity_bounce_obj.py +++ b/lessons/02_Classes_and_Objects/03_gravity_bounce_obj.py @@ -11,7 +11,7 @@ 1. Open `03_gravity_bounce_obj.py` 2. Review the program and try to understand how it works. 3. Change the program so that the player's initial velocity and position are set - in the initializer to the `Player` class. + in the initializer to the `Player` class. 4. Add a color for the player, configurable in the initializer. 5. Add a second player to the game. The second player should be a different color and have different initial position and velocity. @@ -34,14 +34,20 @@ class GameSettings: """Settings for the game""" width: int = 500 height: int = 500 - gravity: float = 0.3 - player_start_x: int = 100 - player_start_y: int = None - player_v_y: float = 0 # Initial y velocity - player_v_x: float = 7 # Initial x velocity player_width: int = 20 player_height: int = 20 - player_jump_velocity: float = 15 + + player_start_x: int = 100 + player_start_y: int = None + + gravity: float = 200 + v_0_y: float = 0 # Initial y velocity + v_0_x: float = 75 # Initial x velocity + + jump_v_y: float = 400 + + FPS = 30 + d_t = 1 / FPS # Time step class Game: @@ -79,7 +85,7 @@ def run(self): player.draw(self.screen) pygame.display.flip() - self.clock.tick(60) + self.clock.tick(self.settings.FPS) pygame.quit() @@ -95,13 +101,13 @@ def __init__(self, game: Game): self.height = settings.player_height self.is_jumping = False - self.v_jump = settings.player_jump_velocity + self.v_jump = settings.jump_v_y self.y = settings.player_start_y if settings.player_start_y is not None else settings.height - self.height self.x = settings.player_start_x - self.v_x = settings.player_v_x # X Velocity - self.v_y = settings.player_v_y # Y Velocity + self.v_x = settings.v_0_x # X Velocity + self.v_y = settings.v_0_y # Y Velocity def update(self): """Update player position, continuously jumping""" @@ -111,8 +117,8 @@ def update(self): def update_y(self): """Update the player's y position based on gravity and velocity""" - self.v_y += self.game.settings.gravity # Add gravity to the y velocity - self.y += self.v_y # Update the player's y position, based on the current velocity + self.v_y += self.game.settings.gravity * self.game.settings.d_t # Add gravity to the y velocity + self.y += self.v_y * self.game.settings.d_t # Update the player's y position, based on the current velocity if self.y >= self.game.settings.height - self.height: self.y = self.game.settings.height - self.height @@ -121,7 +127,7 @@ def update_y(self): def update_x(self): """Update the player's x position based on horizontal velocity and bounce on edges""" - self.x += self.v_x # Update the player's x position based on the current velocity + self.x += self.v_x * self.game.settings.d_t # Update the player's x position based on the current velocity if self.x <= 0: self.x = 0