关于 gamemaker-expert
此技能为 GameMaker Studio 2 和 GML 项目提供开发指导,涵盖对象与资源创建、语言用法、架构和故障排查,适用于开发与维护 GameMaker 游戏的人员。
主要功能
- 01指导对象、事件和项目资源创建
- 02讲解 GML 语法与数据结构
- 03提供架构模式和常见问题参考
- 04涵盖内存管理与文件处理
- 05建议查阅官方手册
适用场景
- 01创建或修改 GameMaker 对象和脚本
- 02排查编译错误或运行时问题
- 03实现碰撞、绘制或网络功能
- 04优化项目结构与性能
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ leihaht/ gamemaker-skills --skill gamemaker-expert安装后在 Claude Code 中输入/gamemaker-expert确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/ADVANCED_TOPICS.mdARCHITECTURE_PATTERNS.mdBUILT_IN_VARIABLES.mdCOMMON_PITFALLS.mdGML_REFERENCE.mdMANAGER_CONSTRUCTOR_GUIDE.mdQUICK_REFERENCE.md详细介绍
GameMaker 专家
概述
此技能为所有 GameMaker 项目提供全面的 GameMaker Studio 2 和 GML(GameMaker Language)专业知识。涵盖从创建基本对象到着色器编程、网络和平台专属开发等高级主题的所有内容。
适用场景:
- 处理
.gml文件(GML 脚本和事件) - 创建或修改
.yy文件(对象定义、精灵定义等) - 编辑
.yyp项目文件 - 创建 GameMaker 对象、脚本或资产
- 调试 GameMaker 编译错误或运行时问题
- 实现 GameMaker 功能(绘图、碰撞、网络等)
- 优化 GameMaker 性能
- 任何 GameMaker Studio 2 开发任务
GameMaker 官方手册
主要参考资料:请始终查阅 GameMaker 官方手册,以获取全面且最新的文档: https://manual.gamemaker.io/monthly/en/
手册主要章节
- GML 概述:https://manual.gamemaker.io/monthly/en/GameMaker_Language/GML_Overview.htm
- 变量、数据类型、运算符、关键字、控制流
- GML 参考:https://manual.gamemaker.io/monthly/en/GameMaker_Language/GML_Reference.htm
- 按类别整理的所有内置函数(绘图、数学、字符串、实例等)
- 对象与事件:https://manual.gamemaker.io/monthly/en/The_Asset_Editors/Objects.htm
- 对象属性、事件系统、父子关系
- 精灵:https://manual.gamemaker.io/monthly/en/The_Asset_Editors/Sprites.htm
- 精灵编辑器、碰撞遮罩、动画
- 数据结构:https://manual.gamemaker.io/monthly/en/GameMaker_Language/GML_Reference/Data_Structures.htm
- ds_list、ds_map、ds_grid、ds_priority、ds_queue、ds_stack
- 文件处理:https://manual.gamemaker.io/monthly/en/GameMaker_Language/GML_Reference/File_Handling.htm
- 文件输入/输出、JSON、缓冲区、目录
适用场景:
- 查询函数签名和参数
- 了解特定平台的行为
- 查看 IDE 版本的最新语法变更
- 深入了解 GameMaker 功能
补充参考资料
高级架构指南:
- 架构模式:references/ARCHITECTURE_PATTERNS.md
- GameMaker 专属模式(服务定位器、命令模式、事件驱动流程)
- 针对 GML 调整的 SOLID 原则
- 使用管理器构造函数实现的封装模式
- 性能模式与反模式
- 管理器构造函数指南:references/MANAGER_CONSTRUCTOR_GUIDE.md
- 创建管理器构造函数的分步指南
- 公共 API 设计模式
- 用于向后兼容的引用公开模式
- 清理职责与测试策略
- 内置变量:references/BUILT_IN_VARIABLES.md
- GameMaker 保留变量的完整列表
- 位置、精灵、物理、路径、时间线类别
- 已弃用的全局变量(
health、score、lives) - 安全替代方案和验证清单
- GML 参考:references/GML_REFERENCE.md
- 语言语法、运算符、数据类型
- 常见陷阱:references/COMMON_PITFALLS.md
- 常见错误及解决方案
- 快速参考:references/QUICK_REFERENCE.md
- 常见模式速查表
适用场景:
- 为游戏全局状态设计管理器构造函数
- 避免与 GameMaker 内置变量冲突
- 在 GML 中实现封装模式
- 理解伪私有成员的
__前缀约定 - 学习 GameMaker 环境中的 SOLID 原则
- 查找有助于维护代码的架构模式
GameMaker 核心工作流
对象创建工作流(重要)
创建 GameMaker 对象需要完成全部四个步骤。缺少任何一步都会导致对象无法显示在 IDE 中或编译失败。
步骤 1:创建对象目录
objects/objYourObjectName/
命名约定:
- 以
obj前缀开头 - 使用 PascalCase(例如,
objPlayer、objEnemyMelee、objInventoryUI) - 使用能表明用途的描述性名称
步骤 2:创建事件文件
为对象需要的每个事件创建 .gml 文件:
常见事件:
| 事件文件 | 事件类型 | 使用时机 |
|---|---|---|
Create_0.gml | 创建事件 | 初始化变量(务必包含) |
Step_0.gml | 步骤事件 | 每帧更新逻辑 |
Draw_0.gml | 绘制事件 | 绘制游戏世界中的图形 |
Draw_64.gml | 绘制 GUI 事件 | 绘制 UI 覆盖层 |
Destroy_0.gml | 销毁事件 | 销毁时进行清理 |
Collision_objX.gml | 碰撞事件 | 处理碰撞 |
事件命名规则:
- 创建:
Create_0.gml - 步骤:
Step_0.gml - 绘制:
Draw_0.gml - 绘制 GUI:
Draw_64.gml(不是Draw_GUI_0.gml!) - 碰撞:
Collision_objTargetName.gml
Create_0.gml 示例:
/// objPlayer Create Event
// Initialize player variables
// Movement
move_speed = 4;
direction = 0;
// Stats
health = 100;
max_health = 100;
// State
is_moving = false;
show_debug_message("objPlayer created");
步骤 3:创建 .yy 文件(必需)
文件:objects/objYourObjectName/objYourObjectName.yy
模板:
{
"$GMObject":"",
"%Name":"objYourObjectName",
"eventList":[
{"$GMEvent":"v1","%Name":"","collisionObjectId":null,"eventNum":0,"eventType":0,"isDnD":false,"name":"","resourceType":"GMEvent","resourceVersion":"2.0",},
{"$GMEvent":"v1","%Name":"","collisionObjectId":null,"eventNum":64,"eventType":8,"isDnD":false,"name":"","resourceType":"GMEvent","resourceVersion":"2.0",},
{"$GMEvent":"v1","%Name":"","collisionObjectId":null,"eventNum":0,"eventType":3,"isDnD":false,"name":"","resourceType":"GMEvent","resourceVersion":"2.0",},
],
"managed":true,
"name":"objYourObjectName",
"overriddenProperties":[],
"parent":{
"name":"Objects",
"path":"folders/Objects.yy",
},
"parentObjectId":null,
"persistent":false,
"physicsAngularDamping":0.1,
"physicsDensity":0.5,
"physicsFriction":0.2,
"physicsGroup":1,
"physicsKinematic":false,
"physicsLinearDamping":0.1,
"physicsObject":false,
"physicsRestitution":0.1,
"physicsSensor":false,
"physicsShape":1,
"physicsShapePoints":[],
"physicsStartAwake":true,
"properties":[],
"resourceType":"GMObject",
"resourceVersion":"2.0",
"solid":false,
"spriteId":null,
"spriteMaskId":null,
"visible":true,
}
事件类型编号:
| 事件 | eventType | eventNum |
|---|---|---|
| 创建 | 0 | 0 |
| 步骤 | 3 | 0 |
| 绘制 | 8 | 0 |
| 绘制 GUI | 8 | 64 |
| 销毁 | 1 | 0 |
重要字段:
%Name和name:必须与对象名称完全一致eventList:为创建的每个 .gml 文件添加条目parentObjectId:使用继承时,设置为父对象的资源路径(例如,{"name":"objCreature","path":"objects/objCreature/objCreature.yy"})persistent:对象应跨房间持续存在时设为truevisible:没有精灵的控制器对象设为false
步骤 4:在项目文件中注册(必需)
文件:YourProject.yyp
位置:找到 "resources":[ 数组
添加条目(按字母顺序排序):
{"id":{"name":"objYourObjectName","path":"objects/objYourObjectName/objYourObjectName.yy",},},
示例:
"resources":[
{"id":{"name":"objInventoryUI","path":"objects/objInventoryUI/objInventoryUI.yy",},},
{"id":{"name":"objPlayer","path":"objects/objPlayer/objPlayer.yy",},},
{"id":{"name":"objYourObjectName","path":"objects/objYourObjectName/objYourObjectName.yy",},},
]
重要:缺少此步骤会导致“object not found”等编译错误。
GML 语言基础
变量作用域
// Local variable (function/script scope only)
var _local_var = 100;
// Instance variable (attached to object instance)
instance_var = 50;
// Global variable (accessible everywhere)
global.game_state = "playing";
最佳实践:为清晰起见,局部变量名以下划线 _ 开头。
数据类型
- 实数:数字
var _x = 100;var _pi = 3.14159; - 字符串:文本
var _name = "Player"; - 布尔值:真/假
var _is_alive = true; - 数组:
var _items = [1, 2, 3];var _nested = [[1,2], [3,4]]; - 结构体:
var _player = { x: 0, y: 0, health: 100 }; - 未定义:
undefined(使用is_undefined()检查) - 指针/ID:对实例、数据结构和资源的引用
运算符
算术:+ - * / div mod
比较:== != > < >= <=
逻辑:&& || ! ^^ (and, or, not, xor)
位运算:& | ^ << >> ~
三元运算符:condition ? true_value : false_value
重要 — 三元运算符链式调用:
链式三元运算符必须使用双括号括起嵌套条件:
// ✅ CORRECT
var _result = condition_a ? "foo" : ((condition_b ? "bar" : "baz"));
// ❌ WRONG (will cause compile error in some versions)
var _result = condition_a ? "foo" : condition_b ? "bar" : "baz";
参考:https://manual.gamemaker.io/beta/en/GameMaker_Language/GML_Overview/Expressions_And_Operators.htm
控制流
// If statement
if (condition) {
// code
} else if (other_condition) {
// code
} else {
// code
}
// Switch statement
switch (value) {
case 1:
// code
break;
case 2:
case 3:
// code for 2 or 3
break;
default:
// code
break;
}
// For loop
for (var _i = 0; _i < 10; _i++) {
show_debug_message(_i);
}
// While loop
while (condition) {
// code
}
// Repeat loop
repeat (10) {
// code
}
// With loop (iterate instances)
with (objEnemy) {
health -= 10;
}
数据结构与内存管理
Struct(轻量级 — 推荐)
// Create struct (no manual cleanup needed!)
var _player = {
name: "Hero",
health: 100,
max_health: 100,
level: 1
};
// Access properties
show_debug_message(_player.name); // "Hero"
_player.health -= 10;
// Check if property exists
if (variable_struct_exists(_player, "mana")) {
// property exists
}
// Dynamic property access
var _prop_name = "health";
var _value = _player[$ _prop_name]; // Same as _player.health
适用场景:轻量级数据容器、配置数据、简单的键值对。 优点:无需手动清理,由垃圾回收自动处理。
ds_list(动态数组)
// Create
var _list = ds_list_create();
// Add items
ds_list_add(_list, "apple", "banana", "cherry");
ds_list_insert(_list, 1, "orange"); // Insert at index
// Access
var _item = _list[| 0]; // "apple"
var _size = ds_list_size(_list);
// Iterate
for (var _i = 0; _i < ds_list_size(_list); _i++) {
show_debug_message(_list[| _i]);
}
// CRITICAL: Always destroy when done!
ds_list_destroy(_list);
适用场景:大型集合、需要特定列表操作、性能关键型代码。
重要:必须使用 ds_list_destroy() 手动销毁,否则会发生内存泄漏!
ds_map(键值对)
// Create
var _map = ds_map_create();
// Add items
_map[? "name"] = "Player";
_map[? "health"] = 100;
// Access
var _hp = _map[? "health"]; // 100
// Check if key exists
if (ds_map_exists(_map, "mana")) {
// key exists
}
// Iterate
var _key = ds_map_find_first(_map);
while (!is_undefined(_key)) {
show_debug_message(_key + ": " + string(_map[? _key]));
_key = ds_map_find_next(_map, _key);
}
// CRITICAL: Always destroy when done!
ds_map_destroy(_map);
适用场景:旧版代码、需要特定映射操作、与现有 ds_map 代码集成。
重要:必须手动调用 ds_map_destroy() 销毁,否则会发生内存泄漏!
建议:尽可能使用结构体代替 ds_map——结构体更简单,无需手动清理。
防止内存泄漏
必须销毁的数据结构:
ds_list_destroy(_list)ds_map_destroy(_map)ds_grid_destroy(_grid)ds_priority_destroy(_priority)ds_queue_destroy(_queue)ds_stack_destroy(_stack)surface_free(_surface)buffer_delete(_buffer)
在 Destroy 事件中进行清理的模式:
/// objPlayer Destroy Event
// Clean up data structures
if (ds_exists(inventory_list, ds_type_list)) {
ds_list_destroy(inventory_list);
}
if (surface_exists(shadow_surface)) {
surface_free(shadow_surface);
}
文件系统与持久化
datafiles/ 文件夹(包含的文件)
重要限制:某些 GameMaker 版本仅支持 datafiles/ 中的根目录文件——不支持子目录!
✅ CORRECT:
datafiles/config.json
datafiles/items.json
datafiles/level_data.json
❌ WRONG (may not work in all versions):
datafiles/configs/game_config.json
datafiles/data/items/weapons.json
必须注册:datafiles/ 中的每个文件都必须在 .yyp 项目文件中注册:
{"$GMIncludedFile":"","%Name":"config.json","CopyToMask":-1,"filePath":"datafiles","name":"config.json","resourceType":"GMIncludedFile","resourceVersion":"2.0",},
JSON 文件
// Load JSON from datafiles/
function load_json_file(_filename) {
if (!file_exists(_filename)) {
show_debug_message("ERROR: File not found: " + _filename);
return noone;
}
var _file = file_text_open_read(_filename);
var _json_string = "";
while (!file_text_eof(_file)) {
_json_string += file_text_read_string(_file);
file_text_readln(_file);
}
file_text_close(_file);
return json_parse(_json_string);
}
// Save JSON to working directory
function save_json_file(_filename, _data) {
var _json_string = json_stringify(_data);
var _file = file_text_open_write(_filename);
file_text_write_string(_file, _json_string);
file_text_close(_file);
}
存档文件位置
// Working directory (platform-dependent save location)
var _save_path = working_directory + "savegame.json";
// Game save ID (unique folder for your game)
// On Windows: %LOCALAPPDATA%/[game_save_id]/
var _save_id = game_save_id;
事件系统与执行顺序
事件执行顺序(每帧)
- Begin Step - 主逻辑执行前
- Alarm Events - 倒计时计时器
- Keyboard/Mouse Events - 输入检测
- Step - 主要更新逻辑
- Collision Events - 移动后
- End Step - 所有逻辑执行后
- Draw - 渲染游戏世界中的图形
- Draw GUI - 渲染 UI 覆盖层
- Draw End - 后期处理
持久对象
在 .yy 文件中设置 persistent: true,使对象在房间切换后继续存在。
重要:对持久对象使用事件处理程序时,重新进入房间时要清除处理程序,以避免重复注册!
// rmGameplay Room Creation Code
// Clear handlers to prevent duplicates
global.input_controller.clear_all_handlers();
// Register handlers fresh
global.input_controller.register_handler("ACTION", function(_cmd) {
// handle action
});
绘图与图形
基本绘图
// Draw sprite
draw_sprite(spr_player, image_index, x, y);
draw_sprite_ext(spr_player, image_index, x, y, scale_x, scale_y, rotation, color, alpha);
// Draw text
draw_text(x, y, "Hello World");
draw_set_font(fnt_arial);
draw_set_color(c_white);
draw_set_halign(fa_center);
draw_set_valign(fa_middle);
// Draw shapes
draw_rectangle(x1, y1, x2, y2, outline);
draw_circle(x, y, radius, outline);
draw_line(x1, y1, x2, y2);
Surface(渲染目标)
// Create surface
if (!surface_exists(shadow_surface)) {
shadow_surface = surface_create(room_width, room_height);
}
// Draw to surface
surface_set_target(shadow_surface);
draw_clear_alpha(c_black, 0);
// ... draw operations ...
surface_reset_target();
// Draw surface to screen
draw_surface(shadow_surface, 0, 0);
// CRITICAL: Free surface when done
if (surface_exists(shadow_surface)) {
surface_free(shadow_surface);
}
性能优化
绘制调用优化
- 将精灵批量放入纹理页,以尽量减少绘制调用
- 对静态内容(背景、图块)使用图层
- 缓存开销较大的计算(不要在 Draw 事件中重复计算)
- 避免在 Draw 事件中创建或销毁实例
碰撞优化
- 精确碰撞检测前,先进行宽阶段距离检查
- 设置适当的碰撞遮罩(精确或边界框)
- 使用碰撞图层和组
- 需要获取实例时,可考虑使用
instance_position()代替place_meeting()
内存管理
- 使用完毕后始终销毁数据结构
- 不再需要时释放 Surface
- 使用后删除缓冲区
- 在 Destroy 事件中进行清理
常见问题与解决方案
请参阅 COMMON_PITFALLS.md 获取全面的故障排查指南。
快速参考
对象创建问题:
- 缺少 .yy 文件 → 对象不会出现在 IDE 中 → 使用上面的模板创建 .yy 文件
- 缺少 .yyp 注册 → 编译错误“object not found” → 将条目添加到 .yyp 资源数组中
- 事件名称错误 → 事件不会触发 → 使用准确名称(Draw_64.gml,而不是 Draw_GUI_0.gml)
内存问题:
- 数据结构泄漏 → 内存使用量不断增长 → 始终调用 ds_destroy 函数
- Surface 泄漏 → 内存使用量不断增长 → 始终调用 surface_free()
- 忘记添加 Destroy 事件 → 资源未清理 → 添加包含清理代码的 Destroy 事件
文件系统问题:
- datafiles/ 子目录 → 文件未找到错误 → 将文件移到 datafiles/ 根目录
- 缺少 .yyp 注册 → 找不到包含的文件 → 在 .yyp 中注册文件
GML 语法问题:
- 三元运算符链式嵌套 → 编译错误 → 使用双括号包裹嵌套条件
- 变量作用域 → 未定义变量 → 检查局部、实例和全局作用域
- 数组边界 → 索引超出范围 → 访问前检查数组大小
高级主题
如需深入了解高级功能,请参阅:
- GML_REFERENCE.md - 按类别整理的完整 GML 函数参考
- QUICK_REFERENCE.md - 表格、常量和速查内容
- ADVANCED_TOPICS.md - 着色器、网络和平台专属开发
着色器编程(GLSL ES)
何时使用着色器:
- 高级视觉效果(光照、模糊、描边、色彩分级)
- 逐像素操作(自定义渲染效果)
- 性能优化(基于 GPU 的计算)
基本着色器工作流:
- 在 IDE 中创建着色器资源
- 编写顶点着色器(.vsh)和片段着色器(.fsh)
- 设置着色器:
shader_set(shd_outline); - 传递 uniforms:
shader_set_uniform_f(u_time, current_time); - 绘制精灵/表面
- 重置着色器:
shader_reset();
GLSL ES 语法、示例和常见着色器模式请参阅 ADVANCED_TOPICS.md。
网络与多人游戏
何时使用网络功能:
- 多人游戏(客户端-服务端、点对点)
- 在线排行榜
- HTTP API 集成
- 云存档系统
异步 HTTP 示例:
// Send GET request
var _request_id = http_get("https://api.example.com/data");
// In Async HTTP event
var _id = async_load[? "id"];
if (_id == _request_id) {
var _status = async_load[? "status"];
if (_status == 0) {
var _result = async_load[? "result"];
var _data = json_parse(_result);
// Process data
}
}
网络套接字、缓冲区、多人游戏架构和同步模式请参阅 ADVANCED_TOPICS.md。
平台专属开发
移动端(iOS/Android):
- 触摸输入:
device_mouse_x_to_gui()、device_mouse_y_to_gui() - 虚拟按键:
virtual_key_add() - 传感器:
device_get_tilt_x()、device_get_tilt_y()
主机(PlayStation、Xbox、Nintendo Switch):
- 手柄支持:
gamepad_button_check()、gamepad_axis_value() - 平台检测:
os_type、os_get_config()
HTML5:
- 浏览器集成:
clickable_add()、clickable_set_style() - JavaScript 执行:
js_runner_eval()
桌面端(Windows、macOS、Linux):
- 文件对话框:
get_open_filename()、get_save_filename() - 窗口控制:
window_set_fullscreen()、window_set_size()
平台专属开发指南请参阅 ADVANCED_TOPICS.md。
资源
此技能包含全面的参考文档:
references/
GML_REFERENCE.md(约 1500 行)
- 按类别整理的完整 GML 函数参考
- 绘图、数学、字符串、实例、房间、数据结构、文件、网络、着色器、表面、粒子、音频
- 常见任务的代码模式(库存、状态机、对话、保存/加载、摄像机系统)
- Struct 与 Object 的选择指南
- 构造函数模式和方法变量
ARCHITECTURE_PATTERNS.md(约 500 行)
- GameMaker 专属架构模式(服务定位器、命令模式、事件驱动流程、过程式逻辑、数据驱动设计)
- 为什么游戏引擎不适合 MVC/MVP(事件驱动架构与请求/响应架构)
- GML 场景下的 SOLID 原则(SRP、OCP、LSP、ISP、DIP,附代码示例)
- 设计原则(DRY、KISS、YAGNI)
- 性能模式(热路径优化、struct 与 ds_map、查找映射)
- 应避免的反模式(滥用全局变量、深层继承、函数重定义)
COMMON_PITFALLS.md(约 800 行)
- 对象创建问题(缺少 .yy、未在 .yyp 中注册、事件名称错误)
- 文件系统问题(datafiles/ 子目录、.yyp 注册、路径)
- GML 语法陷阱(三元运算符、变量作用域、数组边界、访问未定义内容)
- 内存管理(ds_* 泄漏、表面泄漏、缓冲区泄漏、清理模式)
- 性能问题(绘制优化、碰撞优化、实例管理)
- 网络问题(异步事件、缓冲区类型、超时、平台差异)
- 着色器问题(GLSL ES 兼容性、uniform 命名、纹理采样)
QUICK_REFERENCE.md(约 600 行)
- 事件类型/编号完整表及执行顺序
- 键盘常量(vk_* 常量、ord() 用法、按键按下与按键检测)
- 鼠标常量(mb_* 常量、鼠标事件类型、device_mouse_* 函数)
- 常见 GML 模式(距离检测、方向计算、碰撞检测、随机范围)
- 颜色函数(常量、make_color_、color_get_、merge_color)
- IDE 版本参考(常见版本、功能、兼容性说明)
ADVANCED_TOPICS.md(约 1000 行)
- 着色器编程(GLSL ES 语法、顶点/片段着色器示例、uniform、多通道技术、调试)
- 网络模式(客户端-服务端架构、大厅系统、数据包结构、延迟补偿、缓冲区)
- 平台专属开发(移动端触摸/传感器、主机游戏手柄/成就、HTML5 浏览器 API、桌面操作系统交互)
- 高级图形(表面、光照系统、法线贴图、粒子、图块地图、摄像机)
- 性能分析(调试覆盖层、瓶颈定位、内存分析、减少绘制调用、代码分析)
请记住:此技能提供指导和参考。请始终查阅官方 GameMaker 手册,以获取最新、最全面的文档!