Table of Contents:

第1讲 | 游戏开发需要了解哪些背景知识?

游戏有哪些种类?

不管何种类型、何种平台的游戏,其开发的顺序和手段几乎是一样的。而对于初学游戏开发的人来说,首先需要掌握的是开发流程,在有了一定基础之后,再学习细节的知识,就会如虎添翼。

第2讲 | 2D游戏和3D游戏有什么区别?

要做出一款好的 2D 游戏,要比 3D 游戏下更多的功夫,特别是在游戏的内涵、创意、音乐和细节上面。因为 2D 游戏先天只能呈现 2D 画面,所以玩家在入手游戏后,会很快对游戏有一个总体的评价,因此,开发者会想方设法留住用户。而一些 3D 游戏创意和游戏内容其实都不是非常优秀,只是由于画面突出,效果卓绝,也能吸引不少玩家战斗到最后一关。

3D 和 2D 游戏的区别主要体现在呈现画面和文件体积上;
借助 3D 引擎可以提升 2D 游戏的声光和特效效果;
成功的游戏不一定是优秀的游戏。

成功的游戏:销量好,玩的人多
优秀的游戏:是一种文化,或者说信仰

第3讲 | 游戏的发动机:游戏引擎

游戏引擎是什么?

游戏引擎背后的工作方式是:
* 在代码层面,游戏引擎是对绘图接口、操作系统、音频等接口进行的代码层面的封装;
* 在工具层面,游戏引擎是一整套游戏内容的制作工具,方便你制作针对这个引擎的游戏内容。

第4讲 | 底层绘图接口的妙用

几种常见的绘图接口

  1. OpenGL
    OpenGL 是老牌的图形图像接口。GL 是 Graphics Library 的缩写。所以,顾名思义,OpenGL 就是开放图形接口的意思。和接下来要讲的 DirectX 一样,OpenGL 也可以创建和渲染 2D、3D 图形。但是,和 DirectX 不同的是,它可以在多种平台下运行,比如 Windows、Linux、macOS 和部分 UNIX,而 DirectX 只能在 Windows 生态下运行。
    OpenGL 本身只提供图形渲染接口,如果你需要别的功能,比如音频、鼠标、键盘的操作,甚至是创建一个窗体,都需要别的扩展库支持。

  2. DirectX
    在 DirectX 发布之前,微软已经将 OpenGL 包含在 Windows 系统里面。随着时间的推移,OpenGL 逐渐成为了行业标准,而 DirectX 自然免不了与其展开竞争。
    这里,我主要介绍一下 DirectX 中的两个核心组件。这两个核心组件的功能与 2D 游戏编程息息相关,你一定得了解一下。
    第一个是DirectDraw。它是早期 DirectX 中掌管 2D 部分的组件。DirectDraw 类似我之后要说的 GDI,支持显存位图,而不是只能将位图存放在内存里,所以 DirectDraw 更贴近硬件。但是在 DirectX 7 版本之后,DirectDraw 被合并到 Direct Graphics 组件中。虽然目前仍有很多人在使用 DirectDraw 的老版本开发包,然而 DirectDraw 已经被微软逐渐淘汰。
    第二个是Direct2D。它是微软推出的最新 2D 组件,它的出现是为了取代 Windows 下的 GDI、GDI+ 和 DirectDraw。Direct2D 能通过硬件加速来绘制 2D 图形,也支持高质量 2D 图形渲染,比如支持 ClearType 呈现的方式、除锯齿、几何位图的绘制和填充等等。

  3. SDL
    SDL 全称Simple DirectMedia Layer,直译就是简单的直接媒体层。从严格意义上来讲,SDL 并不算是“独立的”图形渲染接口,因为它将各类操作系统的图形图像渲染接口进行了封装,包装成统一的函数,以此来方便调用。比如,在 Windows 下,它封装了 DirectX 和 GDI+;在 Linux 下,它封装了 Xlib 等等。同时,它也提供了 OpenGL 的调用函数。
    SDL 不仅仅可以对现有图形图像接口进行封装,它也提供 SDL 官方自己发布的编程接口。比如,SDL_image、图像接口、SDL_net、网络接口等等。后续我将介绍到的 Pygame(Python 封装的游戏库),其背后就是调用 SDL 编写的。

  4. GDI
    GDI,全称Graphics Device Interface,也是 Windows 下的图形设备接口。它所做的就是处理 Windows 程序的图形输出,负责在 Windows 系统和绘图程序之间进行信息的交换。使用 GDI 的人已经越来越少,从编程的方便性和硬件加速等功能来看,GDI 被 GDI+ 取代是显而易见的。
    GDI 是最早的图形API,用于基本的图形和文本渲染。它适用于简单的图形界面应用和基本的图像处理,但在复杂的图形和游戏应用中性能和功能有限

  5. GDI+
    在 Windows 下,大部分接触过图形编程的程序员都会用过 GDI+。而 GDI+ 其实就是 GDI 的进阶版本。
    GDI+ 是有硬件加速功能的,而 GDI 没有;
    GDI 是以 C 语言接口的形式提供的,而 GDI+ 则是 C++ 和托管类的形式提供;
    从接口代码的层次上说,GDI+ 对程序员更友好,使用起来也更顺手。
    GDI+ 还提供了图像处理的接口,比如提供了 Image、Bitmap 等类,可以用于读取、保存、显示,操作各种类型的图像,比如 BMP、JPG、GIF 等。
    GDI 和 GDI+ 的绘图操作也存在差别。GDI 中存在一个称为“当前坐标”(MoveTo)的位置。“当前坐标”的存在是为了提高绘画的效率。
    DirectX逐渐成为游戏开发等领域的主流选择,而GDI和GDI+在一些简单的图形界面应用和特定的图形处理需求中仍然有其用武之地。在现代Windows平台上,DirectX和其他现代图形API取代了GDI和GDI+在复杂图形应用中的使用

  6. Vulkan
    由Khronos Group开发的低级、跨平台的图形和计算API,旨在提供更好的性能和更大的控制力,特别适用于需要高性能图形渲染的应用和游戏。

  7. Metal
    苹果公司推出的图形和计算API,用于iOS和macOS平台上的图形渲染和计算任务。Metal提供了在苹果设备上进行高性能3D渲染所需的功能。

  8. WebGL
    基于OpenGL ES的JavaScript API,用于在Web浏览器中进行3D图形渲染。它使得浏览器可以直接利用GPU进行高性能的3D渲染,常用于网页上的3D图形展示。

如何直接使用绘图接口进行游戏开发?

由于 SDL 的开发便利性和通用性,所以我拿 SDL 编程接口作为例子,来阐述一下究竟怎样通过图形接口直接进行游戏的开发。

#include <SDL.h>
int main(int argc,char *args[])
{
    SDL_Window* window;
    SDL_Renderer* render;
    SDL_Event e;
    bool q = 0;
    int sdl=SDL_Init(SDL_INIT_EVERYTHING);
    if(0 <= sdl ){
        // 当 SDL 初始化完成后创建一个标题为 "SDL Window" 的窗口, 窗口对齐方式为居中对齐,分辨率为 640x480 的窗口
        g_pWindow=SDL_CreateWindow("SDL Window",
            SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,
            640,480,SDL_WINDOW_SHOWN);
        if(0 != window)
            render=SDL_CreateRenderer(window,-1,0);  
    }
    SDL_SetRenderDrawColor(render,0,255,255,255);
    SDL_RenderClear(render);
    SDL_RenderPresent(render);
while( 0 == q )
    {
       while( 0 != SDL_PollEvent( &e ) )
                {
                    // 检测到用户需要退出
                    if( e.type == SDL_QUIT )
                        q = true;
                }
    }
    SDL_Quit();
    return 0;
}

这个简单的例子说明了如何直接利用 SDL 接口编写游戏。直接利用其他图形接口编写游戏,也基本是这样的步骤。

第5讲 | 构建游戏场景的武器:地图编辑器

地图编辑器在游戏开发的前期扮演着非常重要的角色,它的主要功能就是用来制作游戏地图。
地图编辑器本身一般会由专业的地图编辑器程序员来开发完成。
地图编辑器可以使用原生引擎所支持的语言来编写,比如 C/C++;也可以使用引擎加嵌入脚本语言的方式来进行编写,比如 Lua 等。但是现在,一些成熟的游戏引擎都会自带地图编辑器。所以,大多数时候地图编辑器不需要你自己开发。

为什么要了解地图编辑器的编写过程?

  1. 编写地图编辑器的过程,能让你更好地了解游戏引擎。 从编写地图编辑器开始,就是对游戏引擎的一次全面应用。为什么这么说呢,因为在编写地图编辑器的过程中,除了音乐模块或许暂时不会被用到外,利用引擎编写的地图编辑器、地图资源的载入代码、保存代码等等,都会被用在正式的游戏中。
  2. 编写地图编辑器的过程,可以优化游戏开发流程。 程序员之间会讨论关于资源载入、游戏内容加载和读取这些基础问题,并将最基础的流程完整地梳理一遍。在游戏正式开始开发之前,把这些流程梳理一遍,能极大提升开发过程中的流畅性。
  3. 编写地图编辑器的过程,涉及 UI、按钮和键盘事件的使用。地图编辑器开发完毕后,主要是给美术人员使用,所以 UI 的点击、鼠标键盘事件是必不可少的。在地图编辑器中运用 UI、按钮、键盘事件后,在正式游戏开发中,这些代码可以直接移植到游戏代码中使用。
  4. 编写地图编辑器的过程,能起到团队磨合的作用。 在编写的过程中,你会接触到策划、美术等很多人的意见,程序员之间也会对引擎的应用有很多的讨论。

地图编辑器与关卡编辑器

说到地图编辑器,不得不提到关卡编辑器。关卡编辑器是一种主要给策划使用的编辑器,它涉及地图中的关卡内容。 一般情况下,关卡编辑器不涉及具体的地图细节,但是它涉及总体的游戏地图和游戏流程。
简单来说,地图编辑器的功能就相当于房屋的结构朝向图。我们可以从这张图里,看到每个房间的俯视图。里面包括门的位置、阳台的位置,甚至厕所的位置。而装修公司设计家居,就相当于关卡编辑器的功能。设计师会在你已有房间的结构内,摆放各种桌子、椅子、床、柜子、灯泡等具体的家居用品。
我们拿 FPS 枪战游戏来举例。我们可以先在编辑器内画完所有俯视角的地图通路,然后在这个地方放上某种武器,在那个地方放上某种升级包。这就是一个关卡的总体架构。
随后,你需要在编辑器里编写一些简单的脚本程序。这些脚本程序会涉及玩家在进入游戏后总体的游戏进程和逻辑。比如,你可能会写“分数达到多少之后,等级 +1”“击杀敌人达到多少之后,分数 +10”“失血达到多少出现什么情况”等等。
总之,关卡编辑器和地图编辑器的功能,就好比家装设计图和房屋结构朝向图,一个是在地图内设置物件的摆放位置和具体的表现形式;一个是设计地图本身。

为什么要做游戏 Demo?

按照传统的标准游戏开发流程,在编写完地图编辑器之后,就要开发游戏了。事实上,开发游戏的初期,我们必须要制作这款游戏的 Demo。很多商业游戏制作之前都是要先做游戏 Demo 的。有以下几方面的原因:
1. 游戏 Demo 可以完成游戏核心框架。 绝大部分情况下,最终完成的游戏成品,都会和游戏 Demo 有较大的差异。这些差异体现在美术画面的优化细节、操作的优化细节、网络优化上等等。游戏 Demo 所肩负的任务,就是完成该游戏总体的核心内容框架。 在这个框架内,我们可以一窥游戏的总体逻辑、想呈现给玩家的是什么内容。比如,《三国志》题材的游戏,游戏 Demo 就会呈现在大地图上进行城市内政的管理、军队的管理,以及战斗画面这些游戏的核心内容;比如 FPS 射击游戏,游戏 Demo 呈现出的就是,主角在一个有限的空间内进行射击和击杀敌人的操作,因为这些就是游戏的核心。至于游戏是不是好玩,画面是不是炫酷,音乐是不是好听,并不在游戏 Demo 中呈现。
2. 游戏 Demo 可以测试 bug。 在游戏开发中,地图编辑器做完之后,会载入经地图编辑器制作的地图,这些地图在游戏中是否有 bug,是否需要调整,游戏体验好不好。这些内容在游戏 Demo 中将被测试出来,并且在正式开发中进行调整。
3. 游戏 Demo 可以磨合开发团队。 前面我们提到,有一些引擎自带地图编辑器,所以大多数情况下,地图编辑器不需要你自己编写。这个时候,你敲下的第一行代码就是游戏 Demo 的代码,所以游戏 Demo 就肩负另一个使命,就是团队人员的磨合。和地图编辑器一样,第一次编写游戏 Demo,会有来自策划、美术、程序等各方面的意见和讨论,而制作游戏 Demo 的过程对磨合团队是非常有利的。
4. 游戏 Demo 最终会被舍弃。 在开发过程中,Demo 版本是不稳定的,是有 bug 的。只要不是特别严重的,一般不会去进行特别的修正。但是一般来说,bug 会在正式版本中被修正。所以,游戏 Demo 肩负的另一个任务,就是“试水”。程序代码在游戏中跑起来是不是有瓶颈,bug 出现在哪里,哪些部分可以从 Demo 中提取出来,在正式开发中使用,哪些部分可以在正式版本中优化,这些都是游戏 Demo 所背负的“使命”。

在游戏开发的正规流程中,我们要经历地图编辑器、关卡编辑器、游戏 Demo、正式开发等几个流程,每一个流程都肩负不一样的任务。但简单如贪吃蛇类的游戏,我们可以直接上手编写代码。

第6讲 | 从0开始整理开发流程

1. 选择开发环境

为什么选择 Windows 环境进行开发
* 首先,在 Windows 环境下,拥有比较方便的调试工具。不管是 Python 脚本语言还是 C/C++ 语言,都可以使用图形界面进行调试;
* 其次,Windows 下的IDE 开发环境也比其他平台更多,你拥有更多的工具可供选择。另外,在开发游戏的时候,你可以选择 OpenGL、DirectX 或者 SDL 等图形库进行编程。作为游戏开发,DirectX 几乎是不可或缺的标准,而我在第四节讲述底层绘图接口的时候说过,它是由微软提供的游戏编程接口,在 Windows 下提供了更为方便的底层调用。
* 除了 Windows 外,Linux 平台的图形显卡驱动几乎是不完善的,无法发挥显卡的最大优势。苹果平台又一家独大,开发人员只能为其定制专一的代码,开发难度比之 Windows 就大了不少。

专为 Python 开发的游戏库 Pygame

Pygame 包装了 SDL 的实现。在编写 2D 游戏方面,它的表现可以用三个词来形容:成熟,稳定,简单。它把一些细枝末节隐藏在了 Python 语法背后

第7讲 | 如何建立一个Windows窗体?

如果你有一些编程功底,应该知道建立 Windows 的窗体所需的一些基础知识。如果你经验稍丰富一些,还应该知道 Delphi、C++Builder、C# 等等。这些工具都可以帮助你非常方便地做出一个空白窗体,但是这些窗体并没有游戏的绘图系统,所以它们只是“建立了一个标准窗体”而已。因此,虽然建立窗体是我们这一节的内容,但我们要探讨的是,在窗体背后,Windows 系统做了什么。

使用 C/C++ 编写 Windows 窗体

mfc 全称 microsoft foundation classes,微软基础库,建立窗体可以使用 mfc,也可以使用ATL等等,但基础代码还是windows api

  1. MFC(Microsoft Foundation Class):MFC是一个封装了许多Windows API功能的C++类库,旨在简化Windows桌面应用程序的开发。它提供了一系列类和函数,用于创建用户界面、处理消息、管理窗口和控件等。MFC包含了大量的类,用于处理常见的GUI元素和操作,例如对话框、按钮、菜单、文本框等。它为开发者提供了更高级别的抽象,使得Windows应用程序的开发变得更加快捷和方便。

  2. ATL(Active Template Library):ATL是一个轻量级的模板库,专注于COM(Component Object Model)和active X技术。它提供了一系列的模板类、宏和函数,用于简化COM组件的开发。ATL的设计目标是提供高性能和低开销的COM组件开发解决方案。它通常用于开发需要与其他系统或语言进行交互的组件,如插件、控件、服务等。ATL通常比MFC更轻量级,并且更专注于COM技术相关的开发。

使用 Python 编写 Windows 窗体

Python 的 Windows 窗体编程一般会使用默认的 Tinker 库。不过用别的窗体库也可一建立一个窗体,比如 Python 版本的 QT 库或者 wxPython。
现在来看一下,使用默认的 Tinker 来建立一个窗体。

import Tkinter
 
def my_window(w, h):
  ws = root.winfo_screenwidth()
  hs = root.winfo_screenheight()
  x = (ws/2) - (w/2)
  y = (hs/2) - (h/2)
  root.geometry("%dx%d+%d+%d" % (w, h, x, y))
 
root = Tkinter.Tk(className='python windows app')
my_window(100, 100)
root.mainloop()

游戏所有的内容都是在一个循环内完成的,即我们所有的绘图、线程、操作、刷新,都在一个大循环内完成,类似我们在前面看到的代码。
你可以想象一个循环完成一个大的绘制过程,第二个循环刷新前一次绘制过程,最终类似电影一样,完成整个动画的绘制以及不间断的操作。

在建立 Windows 窗体的时候,程序会从入口函数 WinMain 开始运行,定义和初始化窗体类,然后将窗体类实例化,随后进行消息循环获取消息,然后将消息发送给消息处理函数,最后做出相应的操作。

小结
* 窗体的结构是在建立窗体之前就定义下来的;
* 所有长时间运行的程序,包括游戏,包括 Windows 本身都是一个大循环。我们在这个循环里做我们想做的事情,直到循环结束;

第8讲 | 如何区分图形和图像?

搞清楚了游戏开发中绘制、载入、保存的究竟是图形还是图像,你会对接口函数的使用有一个更深入的认识。
比如,如果是图形接口,可能它的接口函数是:

Surface* DrawSomething(int start_x, int start_y, int finish_x, int finish_y);

比如,如果是图形接口,可能它的接口函数是:

Surface* LoadFromFile(const string& filename);

图像,是由数据组成的任意像素点的描述对象。比如我们所看到的照片。
在电脑中,图形的显示过程是有一定顺序(比如从左往右)的,而图像则是按照像素点进行显示的。
电脑对于图形的编辑、修改更为简单方便,因为单一的图形具有特殊的属性(比如圆圈的直径、颜色等等,因为这些在这个图形建立的时候就固定了下来)。
图形其实是图像的一种抽象表现形式。一般来讲,图形的轮廓并不复杂,比如一个圆圈、一个方块、一个三角形、一条线、某些几何图形、工程上面使用的图纸和 CAD 等,这些都属于图形。图形的色彩也并不是很丰富。
而图像一般都有复杂的轮廓、非常多的细节和颜色(当然也有纯单一的颜色,比如黑白照片)。

跟我一起绘制图形和图形

import pygame
pygame.init()
caption=pygame.display.set_caption('Python App')
screen=pygame.display.set_mode([320,200]) # 窗口大小为 640*480
while True:
    for event in pygame.event.get():
        if event.type==pygame.QUIT:
            pygame.quit()
    pygame.draw.rect(screen,[255,0,0],[150,10,20,40],0)
    pygame.draw.circle(screen,[0,0,0],[20,50],20,1)
    pygame.display.update()
    screen.fill([255,255,255])# 用白色填充窗口
sys.exit()

我们可以看到,使用 Pygame 游戏库来建立一个 Windows 窗体比前面我们提到的任何一种方式都快。那是因为Pygame 封装了建立窗体的代码和图形显示模块

加载图像的代码

import pygame
pygame.init()
caption=pygame.display.set_caption('Python App')
screen=pygame.display.set_mode([320,200]) # 窗口大小为 640*480
 
obj = pygame.image.load("test.png").convert_alpha()  
 
while True:  
 
    for event in pygame.event.get():
        if event.type==pygame.QUIT:
            pygame.quit()
            sys.exit()
    screen.blit(obj, (20,10))
    pygame.display.update()
    screen.fill([255,255,255])# 用白色填充窗口

blit()这个函数会以各种函数形式出现在图形引擎的函数里面,比如 FastBlit 等等。这个函数具体负责将图像从某一个平面复制到另一个平面,或者将图像从内存复制到屏幕。简而言之,这个函数的功能就是将图像“绘制”在游戏窗体的屏幕上。

第9讲 | 如何绘制游戏背景?

基本上就是调用pygame.image.load(background)绘制图像

第11讲 | 如何载入“飞机”和“敌人”?

第12讲 | 如何设置图像的前后遮挡?

在一般的游戏引擎,或者像 Pygame 这样的游戏库中,基本都是“先绘制的图案先出来”,“后绘制的图案后出来”,而后绘制的图案一定遮挡前面绘制的图案。因为 2D 就是一个平面,从逻辑上讲,按照先后顺序绘制,没有任何问题。
但是如果我们现在做的游戏是斜 45 度角的游戏,类似《梦幻西游》视角的,那么人物和建筑物之间就存在遮挡的问题,如果处理不谨慎,就会出现人物浮在建筑物上,或者建筑物把人挡住了。
在一些 2D 引擎中,会有一个 Z 值的概念,Z 值的概念就是在(X,Y)的基本 2D 位置上,加一个高度的概念。这个高度是一个伪概念,它模仿 3D 的 Z 值,只是作遮挡用。

for w in wall:
        if init_y == 270:
            blit_squences([plr, w], init_x, init_y) //就是在这里判断了一下位置,然后先绘制那个在绘制那个
        else:
            blit_squences(w, init_x, init_y)
        init_x += step_x
        init_y += step_y

我们其实就讲了一个内容。在做遮挡的时候,要考虑绘制顺序,先绘制的一定会被后绘制的遮挡。

如果做得比较成熟的话,利用 Python,我们需要在外面包裹一层字典。每个物件载入的时候,都告知其 Z 值,然后在绘制的时候,判断 Z 值,安排绘制顺序。

第14讲 | 如何设置精灵的变形、放大和缩小?

精灵是一个游戏开发中的名词,英文叫 Sprite
从宏观的概念讲,精灵就是一幅图片。比如我们之前中讲过的那些飞机图、背景图,这些都可以认为是精灵或者是从精灵中派生出来的。它就是一系列可以变化的图片。这些图片可以变形、放大、缩小,或者是一系列的动画帧等等。
从编程的角度讲,精灵是一种管理器。在一个精灵的管理器中,可能会有一系列的方法去操作精灵,比如添有加、删除操作,比如有图像的变形、放大、缩小操作,还有系列帧的显示操作等。

设置变形、放大和缩小需要用到哪些函数?

翻转函数 flip,缩放函数 scale,旋转函数 rotate,剪切函数 chop

pln_t = pygame.transform.flip(pln, 1, 1)
pln_t = pygame.transform.scale(pln, (220,220))
pln_t = pygame.transform.rotate(pln, 20)
pln_t = pygame.transform.chop(pln, [20,150,25,155])

Pygame 中的 Sprite

我们再来看一下 Pygame 本身,Pygame 本身就提供有 Sprite 模块,Sprite 模块提供了 Sprite 类,事实上,Pygame 的精灵类最方便的功能就是将某些序列帧的图片,做成动画,并且保存在 Sprite 的组(group)里面。在 Pygame 里面,Sprite 是一个轻量级的模块,我们需要做的是要将这个模块继承下来,并且重载某些方法。
在精灵类中,我们除了动画的呈现,还有碰撞效果的制作。这属于更为复杂的层面,后续的内容,我将会用简单的方式来呈现碰撞的实现。

当然,Sprite 类还有更为高阶的用法,除了碰撞,还有 Layer(层)的概念。group 的添加精灵,事实上是没有次序概念的,所以哪个精灵在前,哪个在后是不清楚的,到了这个时候,你可以使用 OrderUpdates、LayerUpdates 这些类,其中 LayerUpdates 拥有众多方法可以调用,这样就会有分层的概念。

第15讲 | 如何设置淡入淡出和碰撞检测?

在 Pygame 中并不存在“画面的淡入淡出”这样的函数,需要我们自己去实现这样的功能。就是用程序去控制透明度的变化。

事实上,在游戏中,碰撞属于物理引擎的一部分。特别是在 3D 游戏当中,物理引擎是独立于图形引擎的一个模块。程序员需要将图形引擎的对象填入到物理引擎中,计算出碰撞的结果,然后再返回给图形引擎,给出画面效果。做得精致的 2D 游戏也有独立的物理引擎,专门检测碰撞、计算重力等等。
普通图像碰撞的检测,则是通过判断几何图形相交来判断。

第17讲 | 如何制作游戏资源包和保存机制?

资源包

所谓的资源包,就是将游戏的所有资源和素材,进行打包分类,并且进行资源整合,亦或将资源和素材进行压缩,减少游戏体积。
现在很多游戏公司都不会编写特殊的资源包格式。因为设计一种资源包格式,需要经过一系列复杂的动作,包括包头、包身和包尾。
关于这个格式的设计,一会儿我会给你仔细分析。因为,和我们自定义网络协议包一样,一个好的资源包,能够很方便进行解包、打包、删除文件、插入文件的操作,以及游戏的在线更新、补丁更新、资源包的解包、打包、删除、插入、更新文件等操作。

而一个好的资源包格式,不会占用主程序大量的时间。因为在游戏中,需要直接读取包文件里面的内容。

如果不编写特殊的资源包格式,那应该怎么制作资源包呢?
答案是,使用现成的压缩软件库,进行打包压缩,直接在程序内使用。比如我们最常用的 zip 文件、rar 文件,都是可以拿来做资源包文件的。在 Python 中有内置 zip 模块,可以直接读取 zip 文件。我们可以直接在 Pygame 中结合 zip 模块进行编程。

资源包的格式
* 资源包头,是一种标记,存放在包裹里最开始的几个字节,一般是 2~4 个字节。资源包头可以用来辨别这个资源包是哪个公司出品的。
* 资源包版本,这个不是必须的。如果考虑到一款游戏各个版本之间变化很大,未来可能会修改资源包的格式,那么这个时候就需要版本号。
* 资源包是否进行压缩
* 资源包的目录结构以及素材名文件名偏移量,资源包内的目录结构都是虚拟的,所以你可以定义在资源包内类似于 /game/res 这样的目录结构。但是事实上,这只是方便程序调用,事实上目录是不存在的,这是一种只存在在包裹内的虚拟目录。
* 资源包的素材本体。每个本体都可能是一个二进制文件、文本文件或其他任何文件。这些文件的文件名在资源包的素材文件名中都被定义好了。在资源包的素材本体中,我们可能会碰到各种各样的二进制字符,那么我们怎么知道这些素材是从哪里开始哪里结束的呢?
* 资源包的素材长度,规定素材的长度有两种方法,一种方法是在定义资源包的目录结构以及素材偏移量的时候,再加上一个素材长度,也是四个字节的整型数字。这种方法的好处是,不需要添加某个分隔符告诉程序,这个素材的本体到这里结束。第二种方法是在本体结束的位置添加分隔符,比如一个逗号或者分隔符号|。这种方法的好处是,不需要知道文件长度是多少。但是坏处是,分割符号可能会和素材本体重叠。
* 资源包结束符,这个也不是必须的。我们要结束资源包,必须在资源包的结尾添加结束符,这个结束符是告诉程序,资源包已经结束了。

资源包的存在,有两个目的:
一是让游戏目录干净整洁,不然看上去都是乱七八糟的图片和各种配置
二是让游戏程序能更快地从内存中读取游戏资源制作的包裹文件,加速游戏的运行效率。这个包裹文件中含有虚拟目录、资源、资源位置、资源名字等等信息。我们不需要从文件目录中去读取单一文件,只需要从内存中载入的资源包中取出某个文件即可。

如何制作游戏的保存机制?

每一个游戏几乎都有保存和载入的机制。首先你需要知道,只有保存了数据,我们才能载入数据。
一种做法是,将游戏保存的文件直接导出成一个脚本文件,以后每次读取数据就只需要使用程序读取脚本就可以了。

第18讲 | 如何载入背景音乐和音效?

Pygame 支持 mp3、ogg、wav 音频和音效的播放。音乐的模块都在 pygame.mixer 中,这里面包括音乐和音效。
需要在 pygame.init() 的初始化之后初始化一下,pygame.mixer.init()
在游戏编程中,我们需要嵌入音频和音效,特别是在没有 Pygame 的时候,如果有一些游戏引擎没有提供音频库的话,我们就需要自己使用第三方的音频库。虽然可以使用耳熟能详的 ffmpeg,但是感觉有点大材小用了,所以我们需要一个专门的音频库。
在这里,我推荐BASS 音频库。你可以去 http://www.un4seen.com 下载开发库。这个音频库是不开源的,如果你只是自己开发游戏玩玩,非商业目的,就可以使用。如果是商业使用,那就需要购买证书。
小结
* 在 Pygame 中,播放音乐是不需要进行多线程控制的。它本身就会在后台进行播放。
* 所有的音乐和音效都在 pygame.mixer 模块中,如果载入的是音乐,就使用 music 模块;如果载入的是音效,就使用 Sound 模块。
* 随后我们介绍了 BASS 音频库。这几乎是最专业的音频库了。由于是 C 接口,所以通用多种语言,你可以使用.NET 或者 VB 等语言来应用。当然如果要进行后台播放、多个频道播放等功能,你需要编写多线程的代码,并没有 Pygame 那么轻松,这里面很多事情需要自己去做。

第20讲 | 如何在游戏中载入UI和菜单?

UI 的两种呈现形式
* 第一种就是美术画好 UI 图片,然后直接贴图,用鼠标控制一系列的点击和按下操作;
* 另外一种是自己画 UI 界面,比如画一个框,并且在框里面写上文字。

鼠标操作
在 Pygame 中,鼠标操作用到的模块是 pygame.mouse。
绘制矩形
绘制字体和字符

第21讲 | 如何用鼠标和键盘来操作游戏?

  1. 首先是键盘事件判断,这里会出现按一下键盘做一下操作的情况,问题出在 KEYDOWN 事件判断上。但是如果你需要一直按键的判断,可以使用 get_pressed 函数。
  2. 组合键可以写在同一个判断下,使用 and 连起来做判断。
  3. get_pressed 会返回一个 tuple,里面存放了所有的 key 值,只要判断 key 值是不是为 True 就是判断了有没有按键。
  4. 鼠标操作也可以使用 get_pressed 函数,也是返回 tuple,其中下标 0、1、2 分别代表了左、中、右三个按键。

第23讲 | 如何判断客户端的网络连接?

讲了一下网络的一些基础知识,不过基本上没有什么干货

第24讲 | 如何嵌入脚本语言?

为什么使用 Lua 脚本嵌入 C/C++ 硬代码?

Lua 脚本足够轻量级,几乎没有冗余的代码。Lua 虚拟机的执行效率几乎可以媲美 C/C++ 的执行效率。如果选择 Python、Ruby 等常用脚本语言来嵌入,并不是不行,而是要付出执行效率作为代价。因为 Python、Ruby 的执行效率远逊于 Lua。
Lua 的虚拟机很简单,指令设计得也精简,Lua 本身是基于寄存器的虚拟机实现,而 Python 等其他脚本语言是基于堆栈的虚拟机,而基于寄存器的虚拟机字节码更简单、高效。因为字节码,一般会同时包含指令、操作数、操作目标等内容。

Lua 脚本在 C/C++ 语言里面嵌入,需要先声明一个虚拟机并且赋值给指针。
Lua 脚本需要先 loadfile 再 pcall 调用脚本文件,loadfile 会检查最基本的脚本文件内容,比如文件是否存在,比如脚本代码是否出错,而 pcall 会在运行时出错的时候将错误压至栈顶。

第26讲 | 脚本语言在游戏开发中有哪些应用?

讲了怎样在c中编写代码,然后在lua中调用

第27讲 | 如何使用脚本语言编写周边工具?

讲的是用 Lua 脚本来编写一个游戏周边工具 Makefile,然而并没有什么用处,很鸡肋。

第29讲 | 服务器端开发,如何选择合适的开发语言?

第30讲 | 如何定制合适的开发协议?

什么是开发协议?说得简单一点,就是一种客户端和服务器端的网络沟通协议(Protocol)。广义上说,协议是计算机各种设备之间沟通的桥梁。
从狭义上说,协议指的就是网络协议。比如在网络游戏中,客户端和服务器端之间的内容交互,就需要网络协议;在 Web 网站中,前端和后端的交互,也需要协议;再比如,邮件服务的网络交互也需要协议的交互等等。可以说,任何与网络相关的服务都少不了协议的支撑。

三种最流行的开发协议

如何自己定义协议包?

我们所说的协议包,是在 TCP 和 UDP 传输之上的协议包,也就是通过字符串的形式发送的协议包。这些协议包在客户端和服务器之间做了约定,也就是说,客户端和服务器都能通过拿到协议包来进行解包操作,并且进行一系列的逻辑运算并返回结果,当然结果也是协议包的形式发送出去。
一个好的协议不仅能节约网络带宽,也能让接收端快速拿到和解析需要的内容。设计协议包,必须保证安全性完整性
为了保证完整性,接收方需要知道协议的长度,或者知道协议的尾部在哪里。
例如:
[协议长度 2 字节][协议体]

第32讲 | 不可忽视的多线程及并发问题

为什么 Python 用多个 CPU 的时候会出现问题?

C 语言编写的 Python 有 GIL 锁的问题,会让其多线程计算密集型的任务效率更低,解决方案有,利用多进程解决问题 或者 更换 Python 语言的实现版本,比如 PyPy 或者 JPython 等等。

第33讲 | 如何判断心跳包是否离线?

心跳包允许你每隔多少毫秒发送数据给服务器端,告诉服务器我还活着,否则服务器就当它已经死了,确认超时,并且退出。
判断非阻塞模型的网络是否断开,可以使用心跳包和计算超时的方式进行断开操作,比如 30 秒没收到心跳包,则可以强制关闭 Socket 句柄断开。

第35讲 | 如何用网关服务器进行负载均衡?

两种常用的负载均衡技术
常用的负载均衡技术有软件模式硬件模式
其中,硬件模式用得比较多的是 F5。F5 是 F5 Networks 公司提供的一个负载均衡器专用设备, F5 BIG-IP LTM 的官方名称叫本地流量管理器,可以做 4~7 层负载均衡,具有负载均衡、应用交换、会话交换、状态监控等等全备的强大网络功能。
F5 硬件负载均衡的优点是,负载均衡能直接通过智能交换机实现, 处理能力更强,与操作系统无关,负载性能强,适用于一大堆设备、大访问量,其缺点也是很明显的,那就是购买成本高,设备的配置冗余,有些用不上的都给默认配置了。另外,由于设备只有单个,所以单个负载均衡配置无法有效掌握所有服务器及应用状态。
硬件的负载均衡,是从网络层来判断负载规则,所以有时候软件的系统处理能力出现问题,网络硬件还可以作出负载的动作。

软件模式的话,比较常用的软件的有 LVS、Nginx、HAProxy。
LVS 是四层负载均衡,根据目标地址和端口选择内部服务器。Nginx 这种负载均衡工具是七层负载均衡。而 HAProxy 同时支持四层、七层负载均衡,还可以根据报文内容选择内部服务器。

因此,LVS 分发路径优于 Nginx 和 HAProxy,性能上也要高些。但 Nginx 和 HAProxy 则更具配置性,比如说可以用来做动静分离。所谓动静分离,就是根据请求协议的特征,去选择静态资源服务器还是应用服务器。

第36讲 | 如何制作游戏内容保存和缓存处理?

Redis 不仅仅可以用作普通的缓存机制使用,也可以当作正常的数据库使用,Redis 也支持主从同步,要按照应用场景不同来配置不同的 Redis 使用场景。
缓存机制不仅仅针对读取游戏保存文件这么一种方案,也可以用作各种数据文件的读取和写入操作。

第10讲 | 热点剖析(一):HTML5技术是如何取代Flash的?

Flash 是什么?

Flash 是由 Adobe 公司开发的一种富媒体技术,起初是一种放置在浏览器中的插件,填补了当时 HTML 页面平淡的空白,增强了网页交互的能力。你可以在 Flash 中做出任何东西,也可以访问本地电脑中的东西。后来,Adobe 公司推出了播放器,在电脑上不打开浏览器,也可以观看或者游戏 Flash 程序员编写出来的产品。乃至今日,依然有大量应用于 Flash 的富媒体应用,比如视频的播放,比如独立的小游戏,比如网页游戏,甚至桌面应用,都是使用 Flash 开发的。
由于 Flash 技术掌握在 Adobe 公司下,一家独大,所以从商业角度讲,很多大公司并不会坐视不管,就开始从 Flash 中抽取内容并制定公有的 Flash 规范,到了后面就添加到了 H5 规范下,成为大家都能掌握的规范。
谷歌苹果等大公司都不支持 Flash 应用,所以在大公司的压力下以及现在 H5 的流行,Adobe 公司决定在 2020 年终止 Flash 技术的提供。

H5 技术究竟是什么?

H5 技术拥有很多最新的特性。
在语法方面,HTML5 的语法给了网页编写程序员拥有更好的语法结构以及更加丰富的标签。比如,video 标签就可以直接在 H5 页面中嵌入播放器。
H5 也支持本地存储的功能,我们可以使用 H5 技术来开发网页 app,这些都来自本地存储的缓存技术,所以 H5 网页的载入速度会比传统网页更加迅速和便捷,连接也变得更快。H5 支持多媒体这是必然的,包括音频、视频、摄像头等功能,事实上使用 H5 技术的多媒体技术已经完全可以取代 Flash 技术了。

用 H5 编写游戏有什么优点?

使用 H5 技术编写的应用和游戏,可以很方便地嵌入到苹果或者安桌的 App 中。这种方式可以免去原生开发两套 App 的麻烦,只需要编写一套类似 App 的 H5 页面,然后使用类似 WebView 的方式来嵌入到原生应用中。
H5 游戏拥有比 Flash 更好用更开放的规范,开发和发布也更方便,修改代码后放到网上,只需要刷新一下页面就可以看到修改的内容;
另外,Flash 技术需要安装一个插件,而 H5 技术直接在浏览器就可以呈现。所以 H5 技术完全替代 Flash 只是时间问题。

第13讲 | 热点剖析(二):如何选择一款HTML5引擎?

第16讲 | 热点剖析(三):试试看,你也可以编写一款HTML5小游戏!

今天,我们使用 lufylegend 引擎剖析了 HTML5 游戏的编写方式。我来总结一下。
* HTML5 游戏的启动页是一个 HTML 文件。这个文件包含引擎的 js 文件。在其他别的引擎中,也是使用类似的方式来加载引擎。包含了引擎,就可以针对引擎所封装的接口进行游戏的开发。
* HTML5 游戏的循环方式和传统游戏的循环方式不同。由于 HTML5 游戏引擎绝大部分是使用 JavaScript 脚本编写的,而 JS 本身就是以事件驱动的方式来工作的,所以使用事件驱动是 HTML5 游戏引擎的一个很大特点。我们在事件侦听函数中注册一个事件函数,在这个函数内编写“游戏循环”内的代码,就能起到传统游戏循环的作用。
* 在 HTML5 游戏中,我们几乎不需要关心图片的刷新或者清空操作。这些操作比之传统客户端游戏更为简洁,我们只需要关心游戏的图片呈现以及操作即可。

第19讲 | 热点剖析(四):安卓端和苹果端游戏开发有什么区别?

两大平台下开发,除了开发语言的区别之外,底层的内容并没有什么区别,比如 Cocos2d-x、libGDX 等等,都是跨平台生成不同目标代码的引擎。

第22讲 | 热点剖析(五):如何选择移动端的游戏引擎?

第25讲 | 热点剖析(六):AR和人工智能在游戏领域有哪些应用?

VR 是什么?

VR本身是Virual Reality的缩写,意思就是虚拟现实。VR 相对于 AR、MR 等技术来说,在技术上是最为简单的。简单地说,它只需要将 3D 画面做成可以通过 3D 眼镜呈现的内容即可,那么 VR 眼镜是怎么播放 VR 画面的呢?

如何区分 AR 和 MR?

随着 VR 的兴起,AR 也逐渐进入了人们的视野。AR(Augmented Reality)就是增强现实。简单地描述,所谓增强现实,就是利用摄像头或者头盔,将现实世界外部影像结合内部 3D 计算的影像,将内容混合。

比如我们走在一个空旷的屋子里面,忽然在 AR 视觉效果里面,出现一个怪兽,向你扑过来,你需要拿起手柄,将手柄变成武器,将怪兽杀死,这里面就涉及 AR,它将现实中不存在的东西模拟出来,变成增强了效果的现实场景。

AR 和 VR 是对于人们在现实世界的一种增强技术,将现实和虚拟结合起来,做出更多优秀和好玩的游戏,而人工智能则能让人们在游戏中的体验能更好,比如智能的 NPC 行为,漂亮的随机地图的生成等等,这些技术的应用能使我们的游戏体验能变得更加丰富。

第28讲 | 热点剖析(七):谈谈微信小游戏的成功点

其实微信小游戏、小程序,甚至微信本身都是基于 HTML5 技术的,而微信小游戏用的其实就是 HTML5 技术,只是在小游戏中运用的是 Canvas 2D 的 API 和 WebGL 1.0 的 API。
毕竟微信小游戏也属于小程序的范畴,所以我们先来看一下微信小程序。
微信小程序用的是基于 WebView 的技术。所谓的 WebView,你可以理解为在手机中内置一款高性能 WebKit 内核的浏览器,然后将这个浏览器核心封装成 SDK,供接口方调用。这个浏览器核心没有地址栏和导航栏,只是单纯的在软件需要的地方展示一个网页界面。

微信小程序本身分为页面的视图层和逻辑层。
页面的视图层运用的是 WXML 和 WXSS,它们是两种腾讯修改过的 HTML 和 CSS 技术;
而逻辑层则分为 Manager 和 API。
视图层和逻辑层都呼叫了 JSBridge 技术,更下层则是一些网络服务、离线存储,再下层则是系统层。

微信小游戏脱胎于微信小程序。小游戏兼顾传统 HTML5 技术和小程序技术,但是小游戏却没有用到 WebView 技术,而是修改了 HTML5 规范的一些接口内容,成为腾讯自己的内容。也就是说,同一款游戏,如果要在微信小游戏和普通网站都能运行,需要编写两套代码。

我们从以下几个方面来看为什么微信要自己定义一套体系。
1. 可以自定义 Web 标准。为什么要自定义 Web 标准呢?我们从结果来看,自然是为了提升用户体验。而从技术层面讲,这和 Web 兼容性有关。Web 标准本身是个庞大的体系。所以如果既要全部兼容 Web 体系又要按照自己的意愿去实现功能,这是很难做到的事情。比如,如果微信本身的小程序浏览器会重塑一套渲染规则,比如播放视频的时候自动屏蔽广告、按钮默认变成椭圆形等等,因此,自定义 Web 标准,可以去做更多的事情。
2. 可以自定义开发标准。微信扔掉了 Web 兼容性以及标准 HTML5 的内容之后,就开始自定义开发标准了,所以微信强制要求开发者按照某种编码规则来编写代码,从而解决了在普通 Web 编程中“如果不用某种规则来编码,就会出现兼容性问题”的难题,这样,就从源头上解决了这个问题。事实上,这也是“强制约束开发者写出素质较高的代码”。
3. 可以有比 HTML5 更强的功能。完全兼容标准 HTML5 的话,并非不可能,如果你熟悉前端开发的话,就会知道这个坑会有多大。因为首先 HTML5 不具备很多功能,比如获取手机设备信息、获取手机罗盘、地图定位等等。但如果用自定义的体系,加上从微信作为 App 本身具有的底层获取功能,就完全可以做到了。
4. 方便后续优化。由于微信自己那一套体系是高层次抽象层,所以微信小程序团队可以在用户完全没有感知的情况下进行底层优化,而上层不用修改任何代码就可以了。

第31讲 | 热点剖析(八):谈谈移动游戏的未来发展

第34讲 | 热点剖析(九):谈谈独立开发者的未来发展