头图

How easy is it to make games in Python?

Many developers have dreamed of developing a game of their own, which can be easily achieved with Python. The picture below is a simple game I developed with PyGame.
image.png

In this article, I will introduce to you, how to use PyGame to implement a pig that moves on the map.

Texture source: itch.io
Reference tutorial: PyGame: A Primer on Game Programming in Python

image.png

basic framework

First of all, no matter what game you are doing, don't worry about 3721, copy and paste the following code into your editor first. All games require these lines of code:

 import pygame
​
​
def main():
    pygame.init()
    pygame.display.set_caption('未闻Code:青南做的游戏')  # 游戏标题
    win = pygame.display.set_mode((800, 600))  # 窗口尺寸,宽800高600
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:  # 点击左上角或者右上角的x关闭窗口时,停止程序
                running = False
​
​
main()

The operation effect is shown in the following figure:

Load material

Now, let's randomly find two pictures, one for the background and one for the protagonist. Don't worry too much about the size, it's almost fine, because we can dynamically adjust it with code. The following two pictures are the materials I found casually. Please pay attention to the place enclosed by the red frame in the picture, which is the size of these two pictures.

We use the following code to load the image:

 img_surf = pygame.image.load('图片地址').convert_alpha()

The .convert_alpha() is to preserve the transparent background of the png image. If the image you loaded is not a png image, you can change convert_alpha() to convert().
If you want to modify the image size, use the following code:

 img_surf = pygame.transform.scale(img_surf, (宽, 高))

To display the image in the window, use the following two lines of code:

 win.blit(素材对象, (素材左上角的横坐标, 素材左上角的纵坐标))
pygame.display.flip()

The complete code is as follows:

 import pygame
​
​
def main():
    pygame.init()
    pygame.display.set_caption('未闻Code:青南做的游戏')  # 游戏标题
    win = pygame.display.set_mode((800, 600))  # 窗口尺寸
    bg_small = pygame.image.load('bg.png').convert_alpha()
    bg_big = pygame.transform.scale(bg_small, (800, 600))
    pig = pygame.image.load('pig_in_car.png').convert_alpha()
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:  # 点击左上角或者右上角的x关闭窗口时,停止程序
                running = False
​
        win.blit(bg_big, (0, 0))  # 背景图最先加载,坐标是(left, top)
        win.blit(pig, (200, 300))
        pygame.display.flip()
​
​
main()

The operation effect is shown in the following figure:

It should be noted that both win.blit and pygame.display.flip() are placed inside the while loop. where the first parameter of win.blit is the material object we just loaded. The second parameter is a tuple, marking the coordinates of the upper left corner of the image on the canvas. The upper left corner of the entire canvas corresponds to the coordinates (0, 0). Since the size of the background image is also (800, 600), the upper left corner of the background image is placed at (0, 0), which can just cover the entire canvas.

Where can I find materials?

What we are doing is a pixel style game, you can find materials on itch.io:

This site improves a lot of game material, and most of the material is free for personal non-commercial use. After you find the material you like, you can download it directly, and you don't even need to log in during the whole process (more conscience than domestic junk material websites).

Why does my material look like this?

After you download the material, you may find a very strange thing, how come all the materials are drawn on one picture?

In fact, this is the industry practice. The person who makes the material will arrange each type of material on a picture. When you want to use it, you need to cut it yourself. For example, all plants on one map, all statues on one map, and foundation textures on one map.

The background image we used for the demonstration above looks like a green image at first glance, but it actually contains multiple foundation elements. Please note the part I framed in red:

In the official game, we have to take out every basic element and put it together again. When reorganizing, some elements need to be copied and reused, and some elements need to be rotated and scaled. Finally combined into the following map that looks good:

Generally speaking, pixel-style materials are mostly 16x16, 32x32, 64x64, 128x128 in size. Material authors normally provide cropping instructions. If not provided, you can also look at it with the naked eye and take a guess.

For example, I want to cut out the red framed goddess from the statue material:

So, I can write code like this:

 img_surf = pygame.image.load('雕像素材.png').convert_alpha()
goddess= img_surf.subsurface(( 女神像左上角的横坐标 , 女神像左上角的纵坐标, 女神像的宽, 女神像的高))

The operation effect is shown in the following figure:

Some students may ask: Why are the coordinates of the goddess like this? I can only say that this coordinate is what I have tried many times and tried out.

Use sprites to manage objects

Except for the background image, every element we add is an object, such as the pig and the goddess above. In principle, the above code is enough for you to make the game beautiful. If you want to add something, just keep loading the picture material and put it in the right place.

But we can use object-oriented design methods to make the code easier to maintain and simpler. In PyGame, there is a class called Sprite. We can implement a class for each object, inherit Sprite, and then set the material of the object to the .surf property and the position of the object to the .rect property. For example, the above code, let's modify it:

 import pygame
​
​
class Bg(pygame.sprite.Sprite):
    def __init__(self):
        super(Bg, self).__init__()
        bg_small = pygame.image.load('bg.png').convert_alpha()
        grass_land = bg_small.subsurface((0, 0, 128, 128))
        self.surf = pygame.transform.scale(grass_land, (800, 600))
        self.rect = self.surf.get_rect(left=0, top=0)  # 左上角定位
​
​
class Pig(pygame.sprite.Sprite):
    def __init__(self):
        super(Pig, self).__init__()
        self.surf = pygame.image.load('pig_in_car.png').convert_alpha()
        self.rect = self.surf.get_rect(center=(400, 300))  # 中心定位
​
​
class Goddess(pygame.sprite.Sprite):
    def __init__(self):
        super(Goddess, self).__init__()
        building = pygame.image.load('building.png').convert_alpha()
        self.surf = building.subsurface(((7 * 64 - 10, 0, 50, 100)))
        self.rect = self.surf.get_rect(center=(500, 430))  # 女神像的中心放到画布(500, 430)的位置
​
​
def main():
    pygame.init()
    pygame.display.set_caption('未闻Code:青南做的游戏')  # 游戏标题
    win = pygame.display.set_mode((800, 600))  # 窗口尺寸
​
    bg = Bg()
    goddess = Goddess()
    pig = Pig()
    all_sprites = [bg, goddess, pig]  # 注意添加顺序,后添加的对象图层在先添加的对象的图层上面
​
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:  # 点击左上角或者右上角的x关闭窗口时,停止程序
                running = False
​
        for sprite in all_sprites:
            win.blit(sprite.surf, sprite.rect)
        pygame.display.flip()
​
​
if __name__ == '__main__':
    main()

The operation effect is shown in the following figure:

Note all_sprites = [bg, goddess, pig] in the code, here I am using a list. Later there will be a more advanced data structure SpriteGroup to store them. Using a list is enough today.

The material object .get_rect() will return a coordinate positioning object, this object has multiple properties, such as .left, .top, .center, .width, .height. In the case of no parameters, the default .left=0, .top=0, PyGame will automatically calculate .width, .height and .center according to the size of this object. We can actively set it in the form of incoming parameters. When you set the upper left corner, it can automatically calculate the coordinates of the center point; when you pass in the center coordinates, it can automatically calculate the coordinates of the upper left corner.

In theory, within each class, the material object can have any name, not necessarily .surf. The coordinate positioning object does not have to use .rect, as long as you correspond to it in win.blit. But if you use .surf and .rect uniformly it will give you a lot of benefits. This point we will talk about the place where the object collides. So I suggest you just use both names.

make the pig move

Since it is a game, you must press the keyboard to make the protagonist move. Otherwise, what's the difference with a painting? Everyone pay attention to the while running loop in the main() function. If you add a line of code to the loop: print(111), you will find that when you run the game, 111 will be continuously printed out.

In essence, PyGame is to draw pictures continuously through win.blit. Since this while loop runs many times per second, if every time it runs, we let the second parameter of win.blit, that is, the coordinates of the material object have Subtle differences, then to the human eye, the material object is moving.

Our goal is to hold down the up, down, left, and right arrow keys on the keyboard, and the piglet moves in 4 different directions. In PyGame, get the key that the keyboard presses and hold, and use the following code to achieve:
keys = pygame.key.get_pressed()
It returns an object that looks like a list (but not a list). When we want to judge whether a key is pressed, we only need to judge if keys [the key to judge]. If it returns True, it means that the key is pressed. Press and hold. Based on this principle, let's write two pieces of code. First modify the Pig class and add a .update method:

 class Pig(pygame.sprite.Sprite):
    def __init__(self):
        super(Pig, self).__init__()
        self.surf = pygame.image.load('pig_in_car.png').convert_alpha()
        self.rect = self.surf.get_rect(center=(400, 300))  # 中心定位
​
    def update(self, keys):
        if keys[pygame.K_LEFT]:
            self.rect.move_ip((-5, 0))  # 横坐标向左
        elif keys[pygame.K_RIGHT]:
            self.rect.move_ip((5, 0))  # 横坐标向右
        elif keys[pygame.K_UP]:
            self.rect.move_ip((0, -5))  #纵坐标向上
        elif keys[pygame.K_DOWN]:
            self.rect.move_ip((0, 5))  # 纵坐标向下
​
        # 防止小猪跑到屏幕外面
        if self.rect.left < 0:
            self.rect.left = 0
        if self.rect.right > 800:
            self.rect.right = 800
        if self.rect.top < 0:
            self.rect.top = 0
        if self.rect.bottom > 600:
            self.rect.bottom = 600

The .update method receives a parameter keys, which is the list-like object returned by the key. Then determine which direction key was pressed. Depending on the key being pressed, the .rect coordinate positioning object modifies the value of the corresponding direction. rect.move_ip The ip here is the abbreviation of inplace, which is to modify the .rect attribute itself. Its argument is a tuple corresponding to the abscissa and ordinate. If the horizontal and vertical coordinates are less than 0, it means to the left or up, and if it is greater than 0, it means to the right or down.

The original main() function only needs to add two lines of code before win.blit:

keys = pygame.key.get_pressed()
pig.update(keys)
The complete code is as follows:

 import pygame
​
​
class Bg(pygame.sprite.Sprite):
    def __init__(self):
        super(Bg, self).__init__()
        bg_small = pygame.image.load('bg.png').convert_alpha()
        grass_land = bg_small.subsurface((0, 0, 128, 128))
        self.surf = pygame.transform.scale(grass_land, (800, 600))
        self.rect = self.surf.get_rect(left=0, top=0)  # 左上角定位
​
​
class Pig(pygame.sprite.Sprite):
    def __init__(self):
        super(Pig, self).__init__()
        self.surf = pygame.image.load('pig_in_car.png').convert_alpha()
        self.rect = self.surf.get_rect(center=(400, 300))  # 中心定位
​
    def update(self, keys):
        if keys[pygame.K_LEFT]:
            self.rect.move_ip((-5, 0))
        elif keys[pygame.K_RIGHT]:
            self.rect.move_ip((5, 0))
        elif keys[pygame.K_UP]:
            self.rect.move_ip((0, -5))
        elif keys[pygame.K_DOWN]:
            self.rect.move_ip((0, 5))
​
        # 防止小猪跑到屏幕外面
        if self.rect.left < 0:
            self.rect.left = 0
        if self.rect.right > 800:
            self.rect.right = 800
        if self.rect.top < 0:
            self.rect.top = 0
        if self.rect.bottom > 600:
            self.rect.bottom = 600
​
​
class Goddess(pygame.sprite.Sprite):
    def __init__(self):
        super(Goddess, self).__init__()
        building = pygame.image.load('building.png').convert_alpha()
        self.surf = building.subsurface(((7 * 64 - 10, 0, 50, 100)))
        self.rect = self.surf.get_rect(center=(500, 430))  # 女神像的中心放到画布(500, 430)的位置
​
​
def main():
    pygame.init()
    pygame.display.set_caption('未闻Code:青南做的游戏')  # 游戏标题
    win = pygame.display.set_mode((800, 600))  # 窗口尺寸
​
    bg = Bg()
    goddess = Goddess()
    pig = Pig()
    all_sprites = [bg, goddess, pig]  # 注意添加顺序,后添加的对象图层在先添加的对象的图层上面
​
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:  # 点击左上角或者右上角的x关闭窗口时,停止程序
                running = False
​
        keys = pygame.key.get_pressed()
        pig.update(keys)
        for sprite in all_sprites:
            win.blit(sprite.surf, sprite.rect)
        pygame.display.flip()
​
​
if __name__ == '__main__':
    main()

The final running effect is shown in the following video:

Summarize

PyGame is really very simple to make games. As long as you can load the materials, you can make a game that can still be seen. Today we learned how to add material and how to capture keyboard events.

PyGame can read Gif images, but you will find that after loading, the Gif will not move. Soon, we will talk about how to make a character you control move, for example, control a small doll, and when it moves, its feet also move. And object collision detection.

References

  • [1] itch.io:

https://itch.io/game-assets

  • [2] PyGame: A Primer on Game Programming in Python:

https://realpython.com/pygame-a-primer


Long press to identify the QR code and follow Microsoft China MSDN

微软技术生态官方平台。予力众生,成就不凡!微软致力于用技术改变世界,助力企业实现数字化转型。

397 声望
961 粉丝
0 条评论
推荐阅读
如何扛住游戏流量高峰?Evil Dead 主创这样说
“鬼玩人 (Evil Dead)”系列是恐怖幽默动作电影的经典之作,40多年的IP经典化历程中,《鬼玩人》(Evil Dead)正在推出该系列的第5部电影作品。同时也从经典三部曲中衍生出了“Evil Dead: The Game”——主打生存挑战的...

微软技术栈

封面图
python里打印list的四种方法
原文链接标题:Print lists in Python (4 Different Ways)用for循环来打印 {代码...} 结果1 2 3 4 5用 * 星号来打印 {代码...} 结果 {代码...} 把list转换为str来打印 {代码...} 结果 {代码...} 用map把数组里非...

chiiinnn阅读 10.3k

封面图
Ubuntu20.04 从源代码编译安装 python3.10
Ubuntu 22.04 Release DateUbuntu 22.04 Jammy Jellyfish is scheduled for release on April 21, 2022If you’re ready to use Ubuntu 22.04 Jammy Jellyfish, you can either upgrade your current Ubuntu syste...

ponponon1阅读 4k评论 1

日常Python 代码片段整理
1、简单的 HTTP Web 服务器 {代码...} 2、单行循环List {代码...} 3、更新字典 {代码...} 4、拆分多行字符串 {代码...} 5、跟踪列表中元素的频率 {代码...} 6、不使用 Pandas 读取 CSV 文件 {代码...} 7、将列表...

墨城2阅读 308

Unicode 正则表达式(qbit)
前言本文根据《精通正则表达式》和 Unicode Regular Expressions 整理。本文的示例默认以 Python3 为实现语言,用到 Python3 的 re 模块或 regex 库。基本的 Unicode 属性分类 {代码...} 基本的 Unicode 子属性Le...

qbit阅读 4.4k

Python + Sqlalchemy 对数据库的批量插入或更新(Upsert)
由于不同数据库对这种 upsert 的实现机制不同,Sqlalchemy 也就不再试图做一致性的封装了,而是提供了各自的方言 API,具体到 Mysql,就是给 insert statement ,增加了 on_duplicate_key_update 方法。

songofhawk1阅读 2k评论 4

封面图
打脸了兄弟们,Go1.20 arena 来了!
大家好,我是煎鱼。大概半年前,我写过一篇文章《Go 要违背初心吗?新提案:手动管理内存》。有兴趣了深入解的同学,可以再回顾一下。当时我们还想着 Go 团队应该不会接纳,至少不会那么快:懒得翻也可以看我再次...

煎鱼阅读 3.2k

微软技术生态官方平台。予力众生,成就不凡!微软致力于用技术改变世界,助力企业实现数字化转型。

397 声望
961 粉丝
宣传栏