diff --git a/README.md b/README.md index 5c821ab6133c1cd845a01aaed848e9b8fca5a50b..041b4464cc04afab1daffd63d638daaa9422acf8 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,196 @@ # skill_tree_game -游戏开发入门技能树 \ No newline at end of file +`游戏开发入门技能树`是[技能森林](https://gitcode.net/csdn/skill_tree)的一部分。 + +## 编辑环境初始化 + +``` +pip install -r requirements.txt +``` + + +## 目录结构说明 + +技能树编辑仓库的 data 目录是主要的编辑目录,目录的结构是固定的 + +* 技能树`骨架文件`: + * 位置:`data/tree.json` + * 说明:该文件是执行 `python main.py` 生成的,请勿人工编辑 +* 技能树`根节点`配置文件: + * 位置:`data/config.json` + * 说明:可编辑配置关键词等字段,其中 `node_id` 字段是生成的,请勿编辑 +* 技能树`难度节点`: + * 位置:`data/xxx`,例如: `data/1.游戏开发入门初阶` + * 说明: + * 每个技能树有 3 个等级,目录前的序号是必要的,用来保持文件夹目录的顺序 + * 每个目录下有一个 `config.json` 可配置关键词信息,其中 `node_id` 字段是生成的,请勿编辑 +* 技能树`章节点`: + * 位置:`data/xxx/xxx`,例如:`data/1.游戏开发入门初阶/1.简易游戏` + * 说明: + * 每个技能树的每个难度等级有 n 个章节,目录前的序号是必要的,用来保持文件夹目录的顺序 + * 每个目录下有一个 `config.json` 可配置关键词信息,其中 `node_id` 字段是生成的,请勿编辑 +* 技能树`知识节点`: + * 位置:`data/xxx/xxx`,例如:`data/1.游戏开发入门初阶/1.简易游戏` + * 说明: + * 每个技能树的每章有 n 个知识节点,目录前的序号是必要的,用来保持文件夹目录的顺序 + * 每个目录下有一个 `config.json` + * 其中 `node_id` 字段是生成的,请勿编辑 + * 其中 `keywords` 可配置关键字字段 + * 其中 `children` 可配置该`知识节点`下的子树结构信息,参考后面描述 + * 其中 `export` 可配置该`知识节点`下的导出习题信息,参考后面描述 + +## `知识节点` 子树信息结构 + +例如 `data/1.游戏开发入门初阶/1.简易游戏/1.HelloGame/config.json` 里配置对该知识节点子树信息结构,这个配置是可选的: +```json +{ + // ... + + "children": [ + { + "游戏开发入门": { + "keywords": [ + "游戏开发", + "入门" + ], + "children": [], + "keywords_must": [ + "起源" + ], + "keywords_forbid": [] + } + } + ], +} +``` + +## `知识节点` 的导出习题编辑 + +例如 `data/1.游戏开发入门初阶/1.简易游戏/1.HelloGame/config.json` 里配置对该知识节点导出的习题 + +```json +{ + // ... + "export": [ + "helloworld.json" + ] +} +``` + +helloworld.json 的格式如下: +```bash +{ + "type": "code_options", + "author": "huanhuilong", + "source": "helloworld.md", + "notebook_enable": false, + "exercise_id": "c821a21bb363442eab64f0aea124f3bd" +} +``` + +其中 +* "type": "code_options" 是固定的 +* "author" 可以放作者的 CSDN id, +* "source" 指向了习题 MarkDown文件 +* "notebook_enable" 目前都是false +* "exercise_id" 是工具生成的,不填 + + +示例 helloworld.md 如下: + +````mardown +# 一个简单的 Python 小游戏 + +我们可以使用 python 的 pygame 快速建立一个弹球游戏,环境依赖 +* 安装 python +* 使用 python 的包管理程序,安装依赖的 pygame 包:`pip install pygame` + +编写你的第一游戏代码`game.py`,代码如下: + +```python +import sys +import pygame + +# 初始化 +pygame.init() + +# 游戏对话框大小 +size = width, height = 320, 240 + +# 小球移动速度 +speed = [2, 2] + +# 黑色背景 +black = 0, 0, 0 + +# 创建游戏屏幕 +screen = pygame.display.set_mode(size) + +# 加载小球 gif +ball = pygame.image.load("ball.gif") + +# 获取游戏图片的矩形框 +ballrect = ball.get_rect() + +# 游戏循环 +while 1: + # 判断是否退出 + for event in pygame.event.get(): + if event.type == pygame.QUIT: + sys.exit() + + # 按 speed 速度,计算移动小球后的矩形框 + ballrect = ballrect.move(speed) + + # 更新速度(大小和方向) + if ballrect.left < 0 or ballrect.right > width: + speed[0] = -speed[0] + if ballrect.top < 0 or ballrect.bottom > height: + speed[1] = -speed[1] + + # 绘制黑色背景 + screen.fill(black) + + # 绘制小球 + screen.blit(ball, ballrect) + + # 使用双缓存机制刷新屏幕 + pygame.display.flip() +``` + +其中小球 gif 资源如下: + +![](./test/ball.gif) + +使用命令`python game.py`启动游戏,游戏启动后的画面如下 + +![](./test/ball_game.gif) + +以下关于上述游戏代码说法错误的是? + +## 答案 + +编写游戏只能使用 pygame + +## 选项 + +### A + +一个游戏程序,一般有一个死循环,返回绘制游戏画面 + +### B + +许多游戏的渲染,都会采用双缓存机制,在背后的帧里面做渲染,再一次性刷新到前台帧 + +### C + +在游戏循环里,可以检测各种交互事件,并响应事件,从而完成游戏玩家和游戏的互动,例如:退出游戏 +```` + +## 技能树合成 + +在根目录下执行 `python main.py` 会合成技能树文件,合成的技能树文件: `data/tree.json` +* 合成过程中,会自动检查每个目录下 `config.json` 里的 `node_id` 是否存在,不存在则生成 +* 合成过程中,会自动检查每个知识点目录下 `config.json` 里的 `export` 里导出的习题配置,检查是否存在`exercise_id` 字段,如果不存在则生成 +* 在 节 目录下根据需要,可以添加一些子目录用来测试代码,例如在 data/1.游戏开发入门进阶/1.简易游戏/1.HelloGame/ 节目录下,为了测试方便,增加了一个 test/ 子目录,放测试游戏代码 +* 开始游戏入门技能树构建之旅,GoodLuck! diff --git "a/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/1.HelloGame/config.json" "b/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/1.HelloGame/config.json" new file mode 100644 index 0000000000000000000000000000000000000000..4256cb5eedf7e2a06a873f15872e3b32a3b49e6f --- /dev/null +++ "b/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/1.HelloGame/config.json" @@ -0,0 +1,25 @@ +{ + "node_id": "game-3763515eddfe4932887fd39c826f317e", + "keywords": [], + "children": [ + { + "游戏开发入门": { + "keywords": [ + "游戏开发", + "入门" + ], + "children": [], + "keywords_must": [ + "起源" + ], + "keywords_forbid": [], + "node_id": "game-8b79c45606e74fd48485bcdca73de39e" + } + } + ], + "export": [ + "helloworld.json" + ], + "keywords_must": [], + "keywords_forbid": [] +} \ No newline at end of file diff --git "a/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/1.HelloGame/helloworld.json" "b/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/1.HelloGame/helloworld.json" new file mode 100644 index 0000000000000000000000000000000000000000..81ac0df4c4e1e53a8cee9d1d55c6fcfcf0a876e8 --- /dev/null +++ "b/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/1.HelloGame/helloworld.json" @@ -0,0 +1,7 @@ +{ + "type": "code_options", + "author": "huanhuilong", + "source": "helloworld.md", + "notebook_enable": false, + "exercise_id": "c821a21bb363442eab64f0aea124f3bd" +} \ No newline at end of file diff --git "a/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/1.HelloGame/helloworld.md" "b/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/1.HelloGame/helloworld.md" new file mode 100644 index 0000000000000000000000000000000000000000..4b8b80b8043a4977cb23c4b071e909e6e7022d0f --- /dev/null +++ "b/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/1.HelloGame/helloworld.md" @@ -0,0 +1,86 @@ +# 一个简单的 Python 小游戏 + +我们可以使用 python 的 pygame 快速建立一个弹球游戏,环境依赖 +* 安装 python +* 使用 python 的包管理程序,安装依赖的 pygame 包:`pip install pygame` + +编写你的第一游戏代码`game.py`,代码如下: + +```python +import sys +import pygame + +# 初始化 +pygame.init() + +# 游戏对话框大小 +size = width, height = 320, 240 + +# 小球移动速度 +speed = [2, 2] + +# 黑色背景 +black = 0, 0, 0 + +# 创建游戏屏幕 +screen = pygame.display.set_mode(size) + +# 加载小球 gif +ball = pygame.image.load("ball.gif") + +# 获取游戏图片的矩形框 +ballrect = ball.get_rect() + +# 游戏循环 +while 1: + # 判断是否退出 + for event in pygame.event.get(): + if event.type == pygame.QUIT: + sys.exit() + + # 按 speed 速度,计算移动小球后的矩形框 + ballrect = ballrect.move(speed) + + # 更新速度(大小和方向) + if ballrect.left < 0 or ballrect.right > width: + speed[0] = -speed[0] + if ballrect.top < 0 or ballrect.bottom > height: + speed[1] = -speed[1] + + # 绘制黑色背景 + screen.fill(black) + + # 绘制小球 + screen.blit(ball, ballrect) + + # 使用双缓存机制刷新屏幕 + pygame.display.flip() +``` + +其中小球 gif 资源如下: + +![](./test/ball.gif) + +使用命令`python game.py`启动游戏,游戏启动后的画面如下 + +![](./test/ball_game.gif) + +以下关于上述游戏代码说法错误的是? + +## 答案 + +编写游戏只能使用 pygame + +## 选项 + +### A + +一个游戏程序,一般有一个死循环,返回绘制游戏画面 + +### B + +许多游戏的渲染,都会采用双缓存机制,在背后的帧里面做渲染,再一次性刷新到前台帧 + +### C + +在游戏循环里,可以检测各种交互事件,并响应事件,从而完成游戏玩家和游戏的互动,例如:退出游戏 \ No newline at end of file diff --git "a/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/1.HelloGame/test/ball.gif" "b/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/1.HelloGame/test/ball.gif" new file mode 100644 index 0000000000000000000000000000000000000000..bbc4a95fe883abaa4ad009669fc2d0f8c367aaf9 Binary files /dev/null and "b/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/1.HelloGame/test/ball.gif" differ diff --git "a/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/1.HelloGame/test/ball_game.gif" "b/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/1.HelloGame/test/ball_game.gif" new file mode 100644 index 0000000000000000000000000000000000000000..9319a4099c932cc4949a7f20b9ef1ceb60e2061f Binary files /dev/null and "b/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/1.HelloGame/test/ball_game.gif" differ diff --git "a/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/1.HelloGame/test/game.py" "b/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/1.HelloGame/test/game.py" new file mode 100644 index 0000000000000000000000000000000000000000..de8171f0f935f0c845fb569942540a862de32183 --- /dev/null +++ "b/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/1.HelloGame/test/game.py" @@ -0,0 +1,27 @@ +import sys +import pygame +pygame.init() + +size = width, height = 320, 240 +speed = [2, 2] +black = 0, 0, 0 + +screen = pygame.display.set_mode(size) + +ball = pygame.image.load("ball.gif") +ballrect = ball.get_rect() + +while 1: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + sys.exit() + + ballrect = ballrect.move(speed) + if ballrect.left < 0 or ballrect.right > width: + speed[0] = -speed[0] + if ballrect.top < 0 or ballrect.bottom > height: + speed[1] = -speed[1] + + screen.fill(black) + screen.blit(ball, ballrect) + pygame.display.flip() diff --git "a/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/config.json" "b/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/config.json" new file mode 100644 index 0000000000000000000000000000000000000000..99595ac940c3c0df36ba6135cf3fb8be3bf601fb --- /dev/null +++ "b/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/1.\347\256\200\346\230\223\346\270\270\346\210\217/config.json" @@ -0,0 +1,6 @@ +{ + "node_id": "game-8693485194904570b938d427a3f7e562", + "keywords": [], + "keywords_must": [], + "keywords_forbid": [] +} \ No newline at end of file diff --git "a/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/config.json" "b/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/config.json" new file mode 100644 index 0000000000000000000000000000000000000000..9854f255f2a9e14025bfbf4ac7d864eaf224e1a6 --- /dev/null +++ "b/data/1.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\345\210\235\351\230\266/config.json" @@ -0,0 +1,6 @@ +{ + "node_id": "game-5e55c6182eef4b1b9c18fa7b3a9045e6", + "keywords": [], + "keywords_must": [], + "keywords_forbid": [] +} \ No newline at end of file diff --git "a/data/2.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\344\270\255\351\230\266/config.json" "b/data/2.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\344\270\255\351\230\266/config.json" new file mode 100644 index 0000000000000000000000000000000000000000..8976e1ae0f578cc9e5555c1b053f3fdb4cc68a2a --- /dev/null +++ "b/data/2.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\344\270\255\351\230\266/config.json" @@ -0,0 +1,6 @@ +{ + "node_id": "game-82c9d09fd93e4217ac88adf2286f91fa", + "keywords": [], + "keywords_must": [], + "keywords_forbid": [] +} \ No newline at end of file diff --git "a/data/3.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\351\253\230\351\230\266/config.json" "b/data/3.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\351\253\230\351\230\266/config.json" new file mode 100644 index 0000000000000000000000000000000000000000..81aa822ae8ce7f8dc1e78f8d8d6260ea83022212 --- /dev/null +++ "b/data/3.\346\270\270\346\210\217\345\274\200\345\217\221\345\205\245\351\227\250\351\253\230\351\230\266/config.json" @@ -0,0 +1,6 @@ +{ + "node_id": "game-e80ae7bc0c3845a98257576b54d63056", + "keywords": [], + "keywords_must": [], + "keywords_forbid": [] +} \ No newline at end of file diff --git a/data/config.json b/data/config.json new file mode 100644 index 0000000000000000000000000000000000000000..12b4338bc9eba3e66e6cbbc9014382965805d655 --- /dev/null +++ b/data/config.json @@ -0,0 +1,7 @@ +{ + "tree_name": "game", + "keywords": [], + "node_id": "game-49582f1fc060413a81adea001042f6ee", + "keywords_must": [], + "keywords_forbid": [] +} \ No newline at end of file diff --git a/data/tree.json b/data/tree.json new file mode 100644 index 0000000000000000000000000000000000000000..a11002dc3d3a5a3b177bb244d845d3cf31bc9ff0 --- /dev/null +++ b/data/tree.json @@ -0,0 +1,72 @@ +{ + "game": { + "node_id": "game-49582f1fc060413a81adea001042f6ee", + "keywords": [], + "children": [ + { + "游戏开发入门初阶": { + "node_id": "game-5e55c6182eef4b1b9c18fa7b3a9045e6", + "keywords": [], + "children": [ + { + "简易游戏": { + "node_id": "game-8693485194904570b938d427a3f7e562", + "keywords": [], + "children": [ + { + "HelloGame": { + "node_id": "game-3763515eddfe4932887fd39c826f317e", + "keywords": [], + "children": [ + { + "游戏开发入门": { + "keywords": [ + "游戏开发", + "入门" + ], + "children": [], + "keywords_must": [ + "起源" + ], + "keywords_forbid": [], + "node_id": "game-12a897ff3a0b4568a25f810c9c100c20" + } + } + ], + "keywords_must": [], + "keywords_forbid": [] + } + } + ], + "keywords_must": [], + "keywords_forbid": [] + } + } + ], + "keywords_must": [], + "keywords_forbid": [] + } + }, + { + "游戏开发入门中阶": { + "node_id": "game-82c9d09fd93e4217ac88adf2286f91fa", + "keywords": [], + "children": [], + "keywords_must": [], + "keywords_forbid": [] + } + }, + { + "游戏开发入门高阶": { + "node_id": "game-e80ae7bc0c3845a98257576b54d63056", + "keywords": [], + "children": [], + "keywords_must": [], + "keywords_forbid": [] + } + } + ], + "keywords_must": [], + "keywords_forbid": [] + } +} \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000000000000000000000000000000000000..052d4967f24ce3669cce27c74dcedb732030783d --- /dev/null +++ b/main.py @@ -0,0 +1,5 @@ +from skill_tree.tree import TreeWalker + +if __name__ == '__main__': + walker = TreeWalker("data", "game", "游戏开发入门", ignore_keywords=True) + walker.walk() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..64ed38e57345d5936e74877a533ebc8e8fe2d831 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pre_commit~=2.16.0 +skill-tree-parser~=0.0.8 \ No newline at end of file