技能创建器
skill-creator
创建、改进和评估自定义技能,把专业流程转化为可重复使用的 AI 能力。
gamemaker-gml【数据与研究】Best practices for GameMaker Language (GML) development, covering scripts, object events, rooms, data structures, and performance-minded game code. Use when writing GML scripts or object event code in GameMaker Studio, structuring create/step/draw events, managing rooms and instances, building state machines for gameplay, or optimizing GML for perfor…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
This skill covers writing maintainable, performant game code in GameMaker Language (GML), including code organization, style conventions, gameplay architecture, and performance patterns specific to GameMaker Studio.
ds_map, ds_list, structs), and cache references (layer IDs, other instance IDs) in the Create event.ds_list_destroy, etc.) and free surfaces to avoid memory leaks.get_timer()) to find actual hot paths before restructuring for performance.scr_), objects (obj_), sprites (spr_), rooms (rm_), and global variables/macros, matching whatever convention the project has already established.player_speed, enemy_target) and avoid single-letter names outside small, obviously-scoped loops (for (var i = 0; i < count; i++)).var (function/event-scoped) over unnecessary instance variables — instance variables should hold state that genuinely needs to persist or be accessed elsewhere.enum PlayerState { IDLE, RUN, JUMP }), and macros (#macro) for repeated identifiers, layer names, state IDs, and collision groups — never scatter raw numeric or string literals for meaningful values.instance_exists(other_id) before accessing their variables, since instance IDs can become invalid after the referenced instance is destroyed.global.*) minimal and documented; excessive globals make an object's actual dependencies invisible.// scr_player_states.gml
function player_state_enter(_inst, _state) {
_inst.state = _state;
switch (_state) {
case PlayerState.IDLE:
_inst.sprite_index = spr_player_idle;
break;
case PlayerState.RUN:
_inst.sprite_index = spr_player_run;
break;
case PlayerState.JUMP:
_inst.sprite_index = spr_player_jump;
_inst.vspeed = -_inst.jump_force;
break;
}
}
function player_state_step(_inst) {
switch (_inst.state) {
case PlayerState.IDLE:
if (_inst.input_x != 0) {
player_state_enter(_inst, PlayerState.RUN);
} else if (!_inst.on_ground) {
player_state_enter(_inst, PlayerState.JUMP);
}
break;
case PlayerState.RUN:
_inst.hspeed = _inst.input_x * _inst.move_speed;
if (_inst.input_x == 0) {
player_state_enter(_inst, PlayerState.IDLE);
} else if (!_inst.on_ground) {
player_state_enter(_inst, PlayerState.JUMP);
}
break;
case PlayerState.JUMP:
_inst.hspeed = _inst.input_x * _inst.move_speed;
if (_inst.on_ground && _inst.vspeed >= 0) {
player_state_enter(_inst, PlayerState.IDLE);
}
break;
}
}
// obj_player - Create event
enum PlayerState { IDLE, RUN, JUMP }
move_speed = 4;
jump_force = 8;
input_x = 0;
on_ground = false;
state = PlayerState.IDLE;
player_state_enter(id, PlayerState.IDLE);
// obj_player - Step event
input_x = (keyboard_check(vk_right) - keyboard_check(vk_left));
on_ground = place_meeting(x, y + 1, obj_ground);
player_state_step(id);
move_and_collide(hspeed, vspeed, obj_ground);
place_meeting, instance_place, and collision events consistently rather than mixing multiple ad hoc distance checks for the same interaction.keyboard_check/gamepad_* into intent variables like input_x) from action execution (applying that intent to movement/attacks), so input remapping or AI-driven control doesn't require touching gameplay logic.{ level: 3, hp: 80, inventory: [...] }) and version the save format (a save_version field) so future format changes can migrate old saves instead of breaking them.instance_find/instance_number loops or repeated full-room collision scans inside every Step event; scope searches with object type filters and spatial partitioning (grids, layers) where possible.layer_get_id("Instances")) every Step.ds_list_destroy, ds_map_destroy, ds_grid_destroy) when no longer needed — GML data structures are not garbage collected and leak until manually freed or the game restarts.instance_deactivate_object/reactivate, or a custom pool) for frequently spawned projectiles, particles, or short-lived effects once instance_create_layer/instance_destroy churn becomes a measurable cost.get_timer() deltas around suspect code) and keep hot-path code simple — premature micro-optimization usually costs more in the code review than it saves in frame time.if (!instance_exists(target)) return;) for states that should be impossible, so bugs fail loudly during development instead of silently misbehaving.ds_list_create, ds_map_create, surfaces) without ever destroying them, leaking memory over a play session.state = 2) instead of named enum values (state = PlayerState.JUMP), which makes the code unreadable and error-prone to change.