diff --git a/.github/workflows/chrome_ci.yml b/.github/workflows/chrome_ci.yml index 7f45502..62f0a5a 100644 --- a/.github/workflows/chrome_ci.yml +++ b/.github/workflows/chrome_ci.yml @@ -1,6 +1,12 @@ name: Google Chrome CI -on: [push, pull_request] +on: + push: + branches: + main + pull-request: + branches: + main jobs: build: diff --git a/.github/workflows/electron_ci.yml b/.github/workflows/electron_ci.yml index 70af899..4344bb3 100644 --- a/.github/workflows/electron_ci.yml +++ b/.github/workflows/electron_ci.yml @@ -1,6 +1,12 @@ name: Electron CI -on: [push, pull_request] +on: + push: + branches: + main + pull-request: + branches: + main jobs: build: diff --git a/cypress/pages/classPage.js b/cypress/pages/classPage.js deleted file mode 100644 index b0ff0f5..0000000 --- a/cypress/pages/classPage.js +++ /dev/null @@ -1,11 +0,0 @@ -import { HomePage } from "./homePage"; - -class ClassPage extends HomePage { - visit() { - super.visit(); - cy.visit("/guide/intro/classes"); - } - -} - -export { ClassPage }; \ No newline at end of file diff --git a/cypress/pages/fontPage.js b/cypress/pages/fontPage.js deleted file mode 100644 index 44704e6..0000000 --- a/cypress/pages/fontPage.js +++ /dev/null @@ -1,11 +0,0 @@ -import { HomePage } from "./homePage"; - -class FontPage extends HomePage { - visit() { - super.visit(); - cy.visit("/guide/intro/fonts"); - } - -} - -export { FontPage }; \ No newline at end of file diff --git a/cypress/pages/setupPage.js b/cypress/pages/setupPage.js deleted file mode 100644 index 714d84d..0000000 --- a/cypress/pages/setupPage.js +++ /dev/null @@ -1,11 +0,0 @@ -import { HomePage } from "./homePage"; - -class SetupPage extends HomePage { - visit() { - super.visit(); - cy.visit("/guide/intro/setup"); - } - -} - -export { SetupPage }; \ No newline at end of file diff --git a/guide/intro/chapter-1/index.html b/guide/intro/chapter-1/index.html new file mode 100644 index 0000000..0c0b7dc --- /dev/null +++ b/guide/intro/chapter-1/index.html @@ -0,0 +1,87 @@ +--- +layout: default +title: What is Pygame +next_page: [/guide/intro/chapter-2, Opening A Window] +--- + + +

In this article, we'll go over what pygame is and how to install it.

+ +

Table of contents

+

+
    + +
+ +

Introduction

+

+ It's time to get started on your pygame journey, but what is pygame? Well + pygame is "a free and open-source cross-platform library for the development + of multimedia applications like video games using Python"(pygame-ce github repository). As the + description suggests, it's mostly used to make video games - which is why in + this book we'll be learning techniques relevant to making games +

+ +

+ Pygame aims to be a user-friendly library to draw and manipulate pixels onto + the screen, much like SDL2, but with + extended features in various forms that makes it easier to use. Given this + nature, you generally have a lot of control on how you want your code to + run, so it may have a bit of a learning curve. But fear not! After reading + this book you should be able to use pygame just fine, as long as you get + your practice in 🔫 +

+ +

+ In this book we'll aim to cover how to do basic things with pygame, with + little exercises in between to make sure you're not falling off + + Good Luck! +

+ +

Pygame-CE

+

+ So pygame currently has two major distributors, the original one and a fork + of the project. What is a fork? Well it means it's a copy of the project, and + in this case they branched off to be two completely separate packages now. +

+

+ pygame-ce is a + fork of the original pygame, which got created due to all the active + developers of the original project being banned by the "owner". The fork is not owned + by any one person and follows a more democratic model to its management +

+

+ So what happened to all the active developers of the original pygame? They + formed pygame-ce! That's right, so pygame-ce is being developed by the same + people (and more) as the original pygame, and with a more democratic model + have been able to push releases way more easily and introduce many more + features. +

+

+ For all sane purposes, we will be using pygame-ce in this book + and we advise the same. It has had more performance improvements, code + features, and active development than the original pygame for a while now. +

+ +

Installation

+

+ Now that you know what pygame-ce is, let's install it. + +

+  pip uninstall pygame
+  pip install pygame-ce
+
+ In order for pygame-ce to work, pygame needs to be uninstalled first. + This is because both upstream pygame and pygame-ce share the import name, + and so upstream pygame must be removed first. +

+

+ I simply included it as the first step in order to install pygame-ce in a foolproof manner with no issues, + but I must remind you that a pre-requisite of using this site is knowing how to manage your + environment, so please do learn that if you havent already +

+ + diff --git a/guide/intro/audio/index.html b/guide/intro/chapter-10/index.html similarity index 100% rename from guide/intro/audio/index.html rename to guide/intro/chapter-10/index.html diff --git a/guide/intro/colors/index.html b/guide/intro/chapter-11/index.html similarity index 100% rename from guide/intro/colors/index.html rename to guide/intro/chapter-11/index.html diff --git a/guide/intro/images/index.html b/guide/intro/chapter-12/index.html similarity index 100% rename from guide/intro/images/index.html rename to guide/intro/chapter-12/index.html diff --git a/guide/intro/moving-things/index.html b/guide/intro/chapter-13/index.html similarity index 100% rename from guide/intro/moving-things/index.html rename to guide/intro/chapter-13/index.html diff --git a/guide/intro/platformer/index.html b/guide/intro/chapter-2/index.html similarity index 100% rename from guide/intro/platformer/index.html rename to guide/intro/chapter-2/index.html diff --git a/guide/intro/shapes/index.html b/guide/intro/chapter-3/index.html similarity index 100% rename from guide/intro/shapes/index.html rename to guide/intro/chapter-3/index.html diff --git a/guide/obscure/blend-modes/index.html b/guide/intro/chapter-4/index.html similarity index 100% rename from guide/obscure/blend-modes/index.html rename to guide/intro/chapter-4/index.html diff --git a/guide/obscure/pixelarray/index.html b/guide/intro/chapter-5/index.html similarity index 100% rename from guide/obscure/pixelarray/index.html rename to guide/intro/chapter-5/index.html diff --git a/guide/obscure/set-mode-flags/index.html b/guide/intro/chapter-6/index.html similarity index 100% rename from guide/obscure/set-mode-flags/index.html rename to guide/intro/chapter-6/index.html diff --git a/guide/intro/chapter-7/index.html b/guide/intro/chapter-7/index.html new file mode 100644 index 0000000..e69de29 diff --git a/guide/intro/chapter-8/index.html b/guide/intro/chapter-8/index.html new file mode 100644 index 0000000..e69de29 diff --git a/guide/intro/chapter-9/index.html b/guide/intro/chapter-9/index.html new file mode 100644 index 0000000..e69de29 diff --git a/guide/intro/classes/index.html b/guide/intro/classes/index.html deleted file mode 100644 index e516e03..0000000 --- a/guide/intro/classes/index.html +++ /dev/null @@ -1,377 +0,0 @@ ---- -layout: default -title: Classes in Python and Pygame -pg: -links: - - url: http://pythonic.zoomquiet.top/data/20070308094655/index.html - name: Python Types and Objects - - url: https://www.youtube.com/watch?v=X1PQ7zzltz4 - name: mCoding Video on super() -next_page: [/guide/intro/audio, "Audio in Pygame"] -prev_page: [/guide/intro/images, "Images in Pygame"] ---- - -

Table of Contents

-

-

    - -
-

- -
-

What Are Classes and Why Do We Care?

-

- Python is an object oriented programming language, which means that everything in Python is an object. But that doesn't tell us what objects are. - The word "object" here just means a collection of data and/or functionality. More specifically, any piece of data attached to a class is - called an "attribute". Attributes can be either variables or functions, but attributes that are functions have another common name: methods. Variable attributes - can be called "instance variables" to distinguish them from methods, but when talking about classes with other Python programmers, it is generally - accepted that "attribute" usually means a variable and "method" means a function. Now, why do they matter and how do they relate to classes? We'll get to that. -

- -

- You should be familiar with the built-in datatypes such as: -

- Each of these built-in datatypes is actually a class! When talking about objects and classes, there are 4 key terms - that should be kept in mind. Those terms are "class", "type", "instance", and "object". So let's discuss what these terms mean. -

-

- A "class" is ultimately just a container for some data and functionality. The key part to recognize about a "class" - is that the "class" term usually specifically refers to the abstract container itself. So int is a class - but 1 is not. 1 is what's called an instance of the int class. Note, however, - int behaves a bit differently than most classes you'll define for - your own use. But, you can explicitly instantiate int in a similar fashion to the way you'll be instantiating - your own classes. Just append () to the end of the class name to create an instance (so like - int() would return an int instance. You can also put any number into the parentheses and it - will create an instance based on that number, otherwise it defaults to 0). -

-

- A "type" is pretty much another word for "class" in Python. The type function returns the class that the - argument is an instance of (if you print type(1) in Python, you'll get int). But wait, why did - I say "pretty much another word for 'class'" and not "another word for class?" Well, that part is complicated and well - beyond the scope of this guide. There is a link at the bottom of the page that goes into depth about the relationship - between the two concepts in Python. I only mention that there is a difference for the pedants that might take issue with - my simplification. So, you can ignore all but the first couple of sentences in this paragraph in 99.99999% of cases. -

-

- An "object" is an instance of a class. 1 is an object. Here's how to think about class vs instance: A class is - a template for an instance. The class tells you how to construct whatever data structure you're trying to construct, and an - instance is what you get when you follow the template. Again for the pedants: technically everything in python - is an "object" too, but I'm still simplifying here. -

-
- -
-

Creating a Simple Class

-

- Now's the time that we can start actually making classes. So, let's start off by creating a very simple one. Here's a class definition that we will - parse: - -{% highlight python linenos %} -class Foo: - def __init__(self): - print('bar') -{% endhighlight %} -

- -

- Let's talk about the class Foo: part first. This is where we create a class named "Foo". That's it. - No more black magic here. We are literally just telling Python that we want to create a class called "Foo". -

- -

- Now for the next line, def __init__(self):. This is called the class constructor. Almost every class you'll ever write - has this part. I'm leaving out a few details here since they are way out of scope of this discussion. This particular function is called the constructor - because it, in essence, "constructs" the class. Generally, you don't want to put anything computationally expensive in the constructor nor do you want to - call a computationally expensive function from the constructor. Its job is to just set up the instance in such a way that you can use it. The - self parameter is always needed and will get passed in automatically when you instantiate the class. To create an instance of the - above class, you essentially "call" it like you do a function (i.e. add the parentheses): foo_instance = Foo(). Most classes don't provide - much functionality just by existing. You need an instance of the class. There are exceptions and we'll touch on - those, so let's continue. -

- -

- Now let's start building a class that we will continue to build on throughout this tutorial: - -{% highlight python linenos %} -class Actor: - def __init__(self): - pass -{% endhighlight %} - - But what if we want to have our player have some actual data? How do we do that? The answer is attributes. We can define class attributes that persist - even when the constructor has finished and these attributes can be accessed from other places in your code. So, let's define a few attributes: - -{% highlight python linenos %} -class Actor: - def __init__(self): - self.health = 100 - self.attack = 5 - self.defense = 0 - self.inventory = ['sword', 'shield'] -{% endhighlight %} - - Alright, let's parse what we see here. When we create a Actor instance, the constructor is going to assign the values health, attack, defense, - and some inventory to something called self. The self parameter represents the class instance (or in other words, it's - referring to itself), so you're really just assigning those bits of data to the instance of the class. This serves a quite useful purpose. - You see, if I didn't prefix all of those with self, then when the constructor finished running the values would get garbage collected - away and would not exist anymore. That's not what we want at all. So, we assign them to be instance attributes as above and those values stay in - scope for as long as the instance itself exists. Another benefit is that we can change them for individual instances and the changes - only apply to that single instance and no others. - Here's an example: - - - - We can see that all of the attributes we prefixed with self printed just fine, but as soon as we tried to print the value that wasn't prefixed, - we got an AttributeError which means that the attribute we tried to access does not exist. -

- -

- A general rule of thumb for whether something should be an attribute or not is this: -

- Make it an attribute if you need it later and ONLY if you need it later -
-

- -

- Python variable and method names also have a few naming conventions (note that these are common conventions and are - not enforced at all in Python). To read the full convention, see PEP 8: -

-

- -

No leading underscores

-

- These are the simplest to understand. They're quite simply things like above: self.health = 100. There are no underscores at the beginning of - the attribute name. The purpose of these attributes is that they are intended to be directly accessed and modified by the user. -

- -

Single leading underscores

-

- These are a little more complicated to understand. A single leading underscore attribute would look like self._health = 100. - The purpose of these attributes is that they are intended to be "protected". The user can still access these, but the leading underscore is the best - indication to them that tinkering with this variable could break something because it's not intended to be modified directly. These attributes will - be easily accessible under the same name in any subclasses (we will get to those, so be patient). -

- -

Double leading underscores

-

- These are the trickiest to understand. One of these attributes would look like
- self.__health = 100. These attributes are intended to be private; users are not supposed to access these nor are any subclasses. - This doesn't make them inaccessible. They're actually harder to access. This is due to name mangling. When you define one of these - attributes on a class, the name of the attribute is changed outside of the class itself. If my class name is Actor with the - attribute I listed above, I'd have to access the attribute like instance._Actor__health, but within the Actor class, - I could just call self.__health. We'll talk about the subclass implications after we talk about subclasses in the Inheritance section. -

- -

Trailing underscore

-

- A common thing that beginners do is to name their attributes something like type or id. While this usually won't get - you into trouble and it will usually work fine, this is still frowned upon because type and id have specific meanings in Python. - The commonly accepted way to use names like these is to append an underscore to the end of the variable name, so instead of self.id, - you should do self.id_. There is really no other purpose to a single trailing underscore in an attribute name, it's just a common way to - prevent yourself from accidentally shadowing something else. -

-
- -
-

Inheritance

-

- Sometimes you want to create multiple classes that have the same basic functionality, but they each behave a bit differently. - An example relevant to pygame might be that you have NPCs, Enemies, and the Player. Each one is gonna have pretty much the same basic logic: -

    -
  1. making the image
  2. -
  3. scaling it to the appropriate size (actually, you should ideally make your images the correct size in the first place, but scale if needed)
  4. -
  5. making sure that you have a way to position the character once it's time to draw it on screen
  6. -
- But maybe each one has different movement logic and possibly the same or different attack logic. It's always a pain to write the - same piece of code over and over again. Also, if you change your mind, you'll have to remember every place you need to change - your code. This is a great example of how something called subclassing can be useful. -

- -

- Let's say I want to create NPC and Enemy classes. I might start off with defining an Actor class that doesn't have much functionality, but will serve - as the base of my NPC and Enemy classes. That Actor class might be defined in this way: - -{% highlight python linenos %} -class Actor: - def __init__(self, position, image, size): - self.image = image - self.size = size - self.pos = position -{% endhighlight %} - - Note that I've put a few more arguments into the constructor: position, image, and - size. These are bits of information that I would expect pretty much any character in the game to need. - Now, I want to create an NPC class. I could rewrite all of that code in the constructor of - my NPC class, but that's a lot of overhead as mentioned before. Instead, let's subclass Actor: - -{% highlight python linenos %} -class NPC(Actor): - def __init__(self, position, image, size, dialogue): - self.dialogue = dialogue -{% endhighlight %} - - And that's all there is to it right? Wrong. -

- -

- Python has no idea what our intentions are. It could assume that we want the constructor of Actor to be - called immediately, but that's a lot of overhead and might not actually be what we want. So, we have to explicitly - call the constructor of Actor (side note: Actor here is also known as the superclass of NPC, and that'll - be important in a second). There are a couple of ways to do that: explicitly calling the constructor of Actor, - or by calling the constructor of the superclass. -

- -

- Calling Actor's constructor: - -{% highlight python linenos %} -class NPC(Actor): - def __init__(self, position, image, size, dialogue): - Actor.__init__(self, position, image, size) - self.dialogue = dialogue -{% endhighlight %} - - Calling the superclass constructor: - -{% highlight python linenos %} -class NPC(Actor): - def __init__(self, position, image, size, dialogue): - super().__init__(position, image, size) - self.dialogue = dialogue -{% endhighlight %} -

- -

- These look practically identical (and in fact, they practically do the exact same thing in simple cases), but there are a - few reasons to choose one over the other. Generally, super().__init__ is preferred because it's easy to swap - out or rename the superclass if you need to, as well as not needing to remember the self parameter. And for - longer class names, it's also shorter. But, the situation where you would possibly want to use Actor.__init__ - would be in the case of more complicated inheritance structures (long chains of inheritance or multiple inheritance, - both of which are well outside the scope of this article and game design in general, but a link is at the bottom of the - page for an excellent video on the topic from the - mCoding YouTube channel). -

- -

- The power of class inheritance is that attributes are inherited from the superclass to the subclass. So, an instance of - NPC as given above has the same attributes as Actor (note that this would not be the case if we had - not called the constructor of the superclass). Here's an example to play around with: - - - But remember from the beginning what I said about attributes and methods? I said that methods are attributes, so - anything that applies to variable attributes also applies to methods! This means that methods are inherited too. Try it - in the embed above. Define a method of the Actor class and try to call it from the instance of NPC. - One example of that might look like: - -{% highlight python linenos %} -class Actor: - def __init__(self, position, image, size): - self.image = image - self.size = size - self.pos = position - - def talk(self, text): - print(text) - - -class NPC(Actor): - def __init__(self, position, image, size, dialogue): - super().__init__(position, image, size) - self.dialogue = dialogue - - -npc = NPC("Tom", "assets/tom.png") -print(npc.name) -print(npc.image) -npc.talk("Hello World!") -{% endhighlight %} -

- -

- But there's more that we can do! super().__init__() in the constructor of a subclass hints that we can access - the methods of the superclass in the subclass! In fact, this is one of the most powerful aspects of inheritance! Let's say - my Actor superclass has a method func and I want to run that method in my NPC subclass - but I also want it to do some additional stuff. One option would be to copy/paste the code from Actor.func into - NPC.func. But there's an alternative that is easier to maintain and reduces the amount of duplicate code: call - super().func() in NPC.func, like so: - -{% highlight python linenos %} -class Actor: - def __init__(self, position, image, size): - self.image = image - self.size = size - self.pos = position - - def talk(self, text): - print(text) - - -class NPC(Actor): - def __init__(self, position, image, size, dialogue): - super().__init__(position, image, size) - self.dialogue = dialogue - - def talk(self, player_text, npc_text): - print(f"You said {player_text}") - super().talk(npc_text) - - -npc = NPC((100, 200), "assets/npc.png", (20, 20), "Hello World!") -print(npc.pos) -print(npc.dialogue) -npc.talk("Hello", "Hello there") -{% endhighlight %} -

-
- -
-

Instance Methods

-

Enter some text and code here

-
- -
-

Class Methods

-

Enter some text and code here

-
- -
-

Static Methods

-

Enter some text and code here

-
- -
-

Magic Methods

-

Enter some text and code here

-
- -
-

Properties

-

Enter some text and code here

-
- -
-

Integrating with Pygame

-

Enter some text and code here

-
- - - - \ No newline at end of file diff --git a/guide/intro/fonts/index.html b/guide/intro/fonts/index.html deleted file mode 100644 index 79f01f5..0000000 --- a/guide/intro/fonts/index.html +++ /dev/null @@ -1,111 +0,0 @@ ---- -layout: default -title: Fonts in Pygame -pg: https://pyga.me/docs/ref/font.html ---- - -

- In this article, we'll learn how to use custom and system fonts in Pygame. -

- -

Table of contents

-
    - -
- -

Initializing Pygame

-

- Before using fonts in pygame, keep in mind that you have to initialize pygame before using the pygame.font module. To do that, you can simply put pygame.init() or pygame.font.init() at the top of your python script. -
-

- -

Loading fonts in Pygame

-

-

System fonts

- To load a system font in pygame, we create an instance of the pygame.font.SysFont class:
- - {% highlight python %} -pygame.font.SysFont(name, size, bold=False, italic=False) -> Font{% endhighlight %} - - The class requires 2 arguments: first being the name of the font (as a string, for example: "comicsans"), and the second being the size of the font (as an integer).
- You can also use the bold and italic parameters (they are booleans), which are False by default. - -

Example:

- - {% highlight python %} -font = pygame.font.SysFont("comicsans", 32, bold=True){% endhighlight %} - - -

Custom fonts (from a file)

- To load a custom font in pygame, we create an instance of the pygame.font.Font class:
- - {% highlight python %} -Font(filename, size) -> Font -Font(pathlib.Path, size) -> Font -Font(object, size) -> Font{% endhighlight %} - - The class requires 2 arguments: first being the file object or the name of the font file (as a string, for example: "my_font.ttf"), and the second being the size of the font (as an integer). - -

Example:

- - {% highlight python %} -font = pygame.font.Font("arima.ttf", 32){% endhighlight %} - - -

Default font

- You can also use the default pygame font (which is freesansbold) as a pygame.font.Font. To do this, you can use None as the font argument, for example:
- - {% highlight python %} -font = pygame.font.Font(None, 32){% endhighlight %} -
-

- -

Rendering fonts in Pygame

-

- To render a pygame.font.Font object, we can use the pygame.font.Font.render method:
- {% highlight python %} -render(text, antialias, color, background=None) -> Surface{% endhighlight %} - As we can see, the method requires 3 arguments (and 1 optional argument): -

- Note: for all supported color formats, see Colors in pygame. -

- - The method returns a pygame.Surface. Here's some docs from the pygame documentation: "This creates a new Surface with the specified text rendered on it. pygame provides no way to directly draw text on an existing Surface: instead you must use Font.render() to create an image (Surface) of the text, then blit this image onto another Surface. The text can only be a single line: newline characters are not rendered." - -

Example:

- {% highlight python %} -import pygame -pygame.init() - -screen = pygame.display.set_mode((600, 450)) -clock = pygame.time.Clock() - -# None for the default font -font = pygame.font.Font(None, 64) -# here we are rendering text that says "Test" -font_surface = font.render("Test", True, "white") - -running = True -while running: - for event in pygame.event.get(): - if event.type == pygame.QUIT: - running = False - - screen.fill("black") - # draw the text - screen.blit(font_surface, (0, 0)) - - clock.tick(30) - pygame.display.flip() - -pygame.quit(){% endhighlight %} -
-

- diff --git a/guide/intro/setup/index.html b/guide/intro/setup/index.html deleted file mode 100644 index 8064101..0000000 --- a/guide/intro/setup/index.html +++ /dev/null @@ -1,398 +0,0 @@ ---- -layout: default -title: Basic Window Setup in Pygame -pg: -links: - - url: https://automatetheboringstuff.com/ - name: Automate the Boring Stuff ---- - -

In this article, we will go over the structure to open and keep the window running in pygame.

- -

Table of contents

-

-

    - -
-

- -

What is Pygame?

-

- Before we get into the nitty gritty parts of it, let us first understand what pygame is. - Pygame is a free and open source library for the Python programming language built on top - of SDL2. Pygame allows us to build multimedia - applications such as video games. - -

-

- Pygame provides the very basic utility to draw and manipulate pixels - on to the screen, much like SDL2, but with extended utility in various forms - that makes it easier to use. Pygame gives you direct control over how you - want the code to run, this means you could also easily do things that could - drastically cause the drop of performance of your application - if you aren't careful. This article guides you - through how to avoid such things, while also giving you deeper - understanding of pygame and how to use it. -

- -

Prerequisites

-

- As pygame is a library for python, it is necessary that you - are familiar with Python itself. This goes beyond the scope of this book, but - there are some other great resources for learning Python, such - as automate the boring stuff. - - Now, luckily you don't have to be an Anthony Shaw or Tim Peters to be - able to use pygame, just some basic Python will do. - This includes: -

- - Since in Python everything is an object, that is also how you would - interact with the utilities provided through pygame, so knowing how they work - is crucial. -

- -

Installing Pygame

-

- Pygame can be installed through pypi by - using the pip package manager. - - This is as simple as, -

- -

-{% highlight python %} -pip install pygame-ce -{% endhighlight %} - - BUT, how you access your pip depends on your Operating System and what interpreter - you plan on using to run the code you will be writing throughout this article. - - For the sake of this article, we recommend installing pygame globally, and using your global interpreter - to run your code. - - For windows, the way you access pip can be - - -{% highlight python %} -pip install pygame-ce -# Or - -# Accessing pip as a module through your python launcher - -# For windows -py -m pip install pygame-ce - -# For unix (Mac/Linux) -python3 -m pip install pygame-ce - -# Relevant to both -python -m pip install pygame-ce -py3 -m pip install pygame-ce - -{% endhighlight %} - - If you aren't too confident with where pygame is being installed, use your python launcher - as shown in the last few methods to install it, and use the same launcher to run the code accordingly! - -

- -

Setting Up the Project Directory

-

- Throughout this whole miniseries, you will be required to work with a couple different - files, it would be better and recommended if everything required to run your game is within - one directory. We will create the required folders as we go, rather than take the generic - approach and dump everything into our project folder that we wouldn't need(yet). But, - we do still need some basic setup. -

- -

- First, create a folder wherever you would like, and name it whatever you want. I will be - naming it pygame_platformer and I will store it in my D:\dev\projects\ directory. - Once you have your project directory created, make a main.py file. This is where - the code for our game will be! For now that is enough. If you check the contents of your project - folder, it should look like this: -{% highlight python %} -PS D:\dev\projects\pygame_platformer> ls - - - Directory: D:\dev\projects\pygame_platformer - - -Mode LastWriteTime Length Name ----- ------------- ------ ---- --a---- 11-08-2022 22:09 0 main.py - - -{% endhighlight %} -

- -

Opening a Window With Pygame

-

- Finally, we get into the actual code! Time to write the most legendary line - at the top of our main.py file, - -{% highlight python %} -import pygame -{% endhighlight %} -

- -

- Alright, now recall the part where I mention you should run all code through - the same launcher to which pygame was installed. For this, the safest bet is opening - up the same terminal application, and using your respective python launcher to run - main.py. For me, this is py as I am on windows. -

- -

- Your output should look like this: -{% highlight python %} -PS D:\dev\projects\pygame_platformer> py main.py -pygame-ce 2.1.3 (SDL 2.0.22, Python 3.11.1) -{% endhighlight %} - - If your code runs fine and you get the desired output, congrats 🎉 ! You just installed - pygame successfully on your system! - - The Python version is to be noted, we will be using 3.10.4 in this article, - the pygame version will be 2.1.2, but pygame takes backward compatibility - seriously, and most older version code would work on newer verions too, and - we wouldn't be introducing any of the newer features in this article. As of now - 2.1.2 is the latest stable release of pygame. -

- -

- Next up, after importing pygame, the first thing you do is to always initialize - the module. If you don't do this step it can lead to problems later on, this initializes - many submodules such as pygame.font, pygame.mixer, etc. which we will cover in detail later. -{% highlight python %} -import pygame - -pygame.init() -{% endhighlight %} -

- -

- Now let's actually open up a window, for this, we will look at the - pygame.display.set_mode function. - This allows us to initialize the window, and it returns a pygame.Surface when called. A - pygame.Surface is a data type which allows us to manipulate and draw pixels on it. This special - surface is directly linked to what shows up on the window, so everything we want to appear - on the window should be drawn on this special surface first. For now, let us look at what we need in - order to call pygame.display.set_mode - -{% highlight python %} -import pygame - -pygame.init() - - -WIN_SIZE = (420, 250) -screen = pygame.display.set_mode(WIN_SIZE) -{% endhighlight %} - - Notice that we passed in a tuple containing two integers, the first integer is the width - of the window we want to create, and the second is the height of the window. This fills in the size - parameter which is the only required argument to call this function. -

- -

- When you run this code, the window opens up and then closes immediately. Why is that? - This is because there is nothing telling pygame that the window is alive, nor is the - python program continuing to run. After creating that special surface we were talking about, - it just closes. To keep the window alive and running, we need to keep the Python program running. - To do this, let us create a while loop. From this point onward, the code presented is assumed to be added at the end - of the file unless mentioned otherwise. - -{% highlight python %} -while True: - pass -{% endhighlight %} - - We notice two problems here: -

    -
  1. The window is really laggy, it hangs immediately. To close the application run ctrl + c in your terminal.
  2. -
  3. The window is small, a bit too small.
  4. -
- - We will address both the issues present here. -

- -

The window is really laggy.

-

- While the python program is still running, pygame has no way of understanding - that the window is still running, it needs some kind of system that checks for these events. - For this, we can grab all the window events that are going on - -{% highlight python %} -while True: - events = pygame.event.get() -{% endhighlight %} - - This should make it responsive, and not hang. -

- -

The window is small.

-

- It's all well and good if you want to window to be small, but generally - you cant really tell what size of display your user is going to have, a fix-all solution - for this is to use the pygame.SCALED flag. This way your application directly adjusts its - size to fit the user's display. Not its aspect ratio, its size. The aspect ratio of the window stays the same. - Try adding in the flag when calling pygame.display.set_mode - -{% highlight python %} -screen = pygame.display.set_mode(WIN_SIZE, flags=pygame.SCALED) -{% endhighlight %} - - There we go! Our window is much more fit for our desktop now. Notice the aspect ratio of the window stays the same. -

- -

Exiting the game

-

- We have an interesting problem, if you tried closing the application with the X button, - it won't work. This is because you haven't defined what you want pygame to do when the X button is - pressed. The way you exit it as of now is to use ctrl + c in the terminal you are running it from. -

- -

- Obviously, this isn't ideal, at all. Remember how we grabbed all the events - that were going on in the window? That actually includes closing it as well. - All we have to do is check if the window is being closed(i.e, the X button is being pressed), - and if it is we just need to exit our while loop. - - For this, we first iterate over the events - -{% highlight python %} -for event in events: - pass -{% endhighlight %} - - Make sure this is within the while loop. The iterator, i.e, the event object - presented to us here is pretty useful. We can access it's type attribute - which will be an integer, and check if that is going to be the type of event that - corresponds to closing the window. - Here's how it is done, - -{% highlight python %} -for event in events: - if event.type == pygame.QUIT: - pass -{% endhighlight %} - - pygame.QUIT is the integer constant that represents trying to quit the application. - Now, you can guess what to do next, we need to exit the loop. We ran this using a - -{% highlight python %} -while True: -{% endhighlight %} - - So the only way to exit this is to throw an exception, the way one of the - lead contributors of the pygame project itself like to do it, and I have adopted as well, - is to raise SystemExit. Alternatively you can import sys, and run - sys.exit(). - -{% highlight python %} -while True: - events = pygame.event.get() - - for event in events: - if event.type == pygame.QUIT: - raise SystemExit -{% endhighlight %} - - And there we have it! Our game should close when we press the X button accordingly. -

- -

- There are many different styles and ways to close a pygame application, - you can raise an exception like shown here or use a variable to determine if the game is still running, -{% highlight python %} -running = True -while running: - events = pygame.event.get() - - for event in events: - if event.type == pygame.QUIT: - running = False -{% endhighlight %} - - There are some benefits to this, the application only closes after it completes the last - iteration, rather than stopping midway! - - Another thing you can add to this is quitting pygame after the while loop. - This can solve problems like IDLE keeping the window open even after you ended the application. - -{% highlight python %} -while running: - events = pygame.event.get() - - for event in events: - if event.type == pygame.QUIT: - running = False - -pygame.quit() -{% endhighlight %} - -

- - -

- As a rule of thumb, you should also fill your screen with a solid color every frame, as to avoid - seeing the last frame in the current one, and also updating your display. -

- - -{% highlight python %} -screen.fill("black") - -pygame.display.flip() -{% endhighlight %} - - We can pass strings for colors in pygame, - as covered in the colors article. - - pygame.display.flip just updates what is shown on the window, it does the same as - pygame.display.update but - internally when you call pygame.display.update without any arguments it simply calls pygame.display.flip -

- -

Final Result

-

- Code: - -{% highlight python %} -import pygame - -pygame.init() - - -WIN_SIZE = (420, 250) -screen = pygame.display.set_mode(WIN_SIZE, flags=pygame.SCALED) - -running = True -while running: - events = pygame.event.get() - - for event in events: - if event.type == pygame.QUIT: - running = False - - screen.fill("black") - pygame.display.flip() - -pygame.quit() - -{% endhighlight %} -

- - - - diff --git a/index.html b/index.html index 000e7ac..93b7e26 100644 --- a/index.html +++ b/index.html @@ -1,44 +1,144 @@ --- layout: default -next_page: [/guide/intro/setup, Base Setup] +next_page: [/guide/intro/chapter-1, What Is Pygame] --- -

Introduction

-

- "Where can I learn how to use pygame?" - one of the - most commonly asked questions that I get. But we always struggle to - answer. We sometimes point them to the docs and examples, but that - isn't a comprehensive guide on building good pygame-based applications. - There are many other tutorials and guides we have found, but we always - find something lacking. There are many comments we give and many nitpicks we have - - so much so that some of us gathered and decided to put all of them into a book. - This book. -

- -

Table of contents

-

Working towards a simple platformer

-
    -
  1. Base setup and opening a window
  2. -
  3. Drawing shapes
  4. -
  5. Moving things
  6. -
  7. Loading images and drawing them
  8. -
  9. Using classes to better contain our code
  10. -
  11. Loading and playing audio files
  12. -
  13. Making a small game to demonstrate how these go together
  14. -
- -

Important topics

-
    -
  1. Fonts in pygame
  2. -
  3. Colors in pygame
  4. -
- -

Obscure but useful things

-
    -
  1. PixelArray
  2. -
  3. Blend modes
  4. -
  5. pygame.display.set_mode flags
  6. -
- +

Preface

+

+ "Where can I learn how to use pygame?" - one of the most commonly asked + questions that we get. But we always struggle to answer. We sometimes point + them to the docs and examples, but that isn't a comprehensive guide on + building good pygame-based applications. There are many other tutorials and + guides we have found, but we always find something lacking. There are many + comments we give and many nitpicks we have - so much so that some of us + gathered and decided to put all of them into a book. This book. +

+ +

Prerequisites

+

+ This book is meant for people who already know a bit of Python. Here's + what you need to know: +

+ + +

+ If you aren't familiar with the above, there are already some excellent + pre-existing Python resources out there that we suggest depending on your + situation +

+

New to programming

+

+ If you're new to programming, here are some resources that are meant to + teach you from the ground up +

+

Text resources:

+ +

Video resources:

+ + +

Familiar with programming

+

+ If you're already familiar with programming, you probably don't want + variables, loops, conditions and the like explained to you again, the + Official Python Tutorial acts as a schema to the language + while still being more expressive than documentation and covers the features + of the language nicely +

+ +

Table of contents

+

Learning Pygame

+
    +
  1. + Chapter 01: What Is Pygame +
  2. +
  3. + Chapter 02: Opening A Window +
  4. +
  5. + Chapter 03: Surfaces And Positional Datatypes +
  6. +
  7. + Chapter 04: Implementing An Entity +
  8. +
  9. + Chapter 05: Implementing A Collection For Entities +
  10. +
  11. + Chapter 06: Loading Assets +
  12. + +
  13. + Chapter 07: Animation +
  14. +
  15. + Chapter 08: The pygame.draw API +
  16. +
  17. + Chapter 09: Rects And Collisions +
  18. +
  19. + Chapter 10: The Pygame Input API +
  20. + +
  21. + Chapter 11: Game States +
  22. +
  23. + Chapter 12: Basic UI +
  24. +
  25. + Chapter 13: Basic File Structure +
  26. +
+ +

Technical Concepts

+ + +

Useful Game Mechanics

+ + +

Higher Theory

+ +