基于 SOC 职业分类
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/wukaikailive/weixin-game-skill --skill weixin-game命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
| name | weixin-game |
| description | 微信小游戏开发技术限制与最佳实践指南,帮助 Claude Code 在开发微信小游戏时遵循正确的 API 和技术规范。 |
| version | 1.0.0 |
| author | community |
| tags | ["weixin","minigame","wechat","game-development","canvas"] |
指导 Claude Code 在开发微信小游戏时遵循技术限制和实现规范。
TRIGGER when:
wx. API(如 wx.createCanvas、wx.onTouchStart 等)game.json 或 project.config.json(微信小游戏配置文件)DO NOT trigger when:
微信小游戏运行在 JavaScriptCore/V8 引擎中,不是浏览器环境:
| 特性 | 浏览器 | 微信小游戏 |
|---|---|---|
| 全局对象 | window | globalThis |
| DOM | document.* | 不支持 |
| Canvas 创建 | document.createElement('canvas') | wx.createCanvas() |
| 图片加载 | new Image() | wx.createImage() |
| 存储 | localStorage | wx.setStorageSync() |
| 音频 | new Audio() | wx.createInnerAudioContext() |
| 网络 | fetch() / XMLHttpRequest | wx.request() |
主包限制: 4MB
分包总限制: 20MB
单分包限制: 2MB(建议不超过 1.5MB)
应对策略:
subpackages 配置)// ❌ 不支持的 API
ctx.roundRect() // 需手动实现圆角矩形
ctx.toDataURL() // 部分限制
ctx.getImageData() // 性能敏感
// ✅ 手动实现圆角矩形
function drawRoundRect(ctx, x, y, w, h, r) {
ctx.beginPath()
ctx.moveTo(x + r, y)
ctx.lineTo(x + w - r, y)
ctx.quadraticCurveTo(x + w, y, x + w, y + r)
ctx.lineTo(x + w, y + h - r)
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h)
ctx.lineTo(x + r, y + h)
ctx.quadraticCurveTo(x, y + h, x, y + h - r)
ctx.lineTo(x, y + r)
ctx.quadraticCurveTo(x, y, x + r, y)
ctx.closePath()
}
// ❌ 部分格式不支持或不稳定
ctx.fillStyle = '#00d4ff40' // 带透明度的简写 hex
ctx.fillStyle = 'hsl(200, 100%, 50%)' // HSL 格式
// ✅ 推荐使用 rgba
ctx.fillStyle = 'rgba(0, 212, 255, 0.25)'
ctx.fillStyle = '#00d4ff' // 标准 hex
// 必须配置合法域名(小程序后台配置)
// 仅支持 HTTPS
// 请求超时默认 60 秒
// ❌ 不支持
fetch('https://api.example.com/data')
// ✅ 使用 wx.request
wx.request({
url: 'https://api.example.com/data',
method: 'GET',
success(res) {
console.log(res.data)
},
fail(err) {
console.error(err)
}
})
// WebSocket 限制
wx.connectSocket({
url: 'wss://socket.example.com' // 必须是 wss
})
// ❌ 浏览器写法
window.gameState = {}
window.myGlobal = value
// ✅ 微信小游戏写法
// 方案1: 使用 globalThis
globalThis.gameState = {}
// 方案2: 文件顶层定义
let gameState = {} // 模块级变量
// 方案3: 创建全局管理器
// GameGlobal.js
export const GameGlobal = {
state: {},
config: {}
}
// 主 Canvas(屏幕 Canvas)
const mainCanvas = wx.createCanvas()
const ctx = mainCanvas.getContext('2d')
// 离屏 Canvas(用于缓存、预渲染)
const offCanvas = wx.createOffscreenCanvas({
type: '2d',
width: 512,
height: 512
})
// 获取实际显示尺寸
const { windowWidth, windowHeight } = wx.getSystemInfoSync()
mainCanvas.width = windowWidth
mainCanvas.height = windowHeight
// ✅ 微信小游戏图片加载
function loadImage(src) {
return new Promise((resolve, reject) => {
const img = wx.createImage()
img.onload = () => resolve(img)
img.onerror = (err) => reject(err)
img.src = src // 相对路径或配置的 CDN 域名
})
}
// 加载多张图片
async function loadAssets() {
const assets = {
player: await loadImage('images/player.png'),
enemy: await loadImage('images/enemy.png'),
bg: await loadImage('images/background.png')
}
return assets
}
// 微信使用全局触摸事件,不是 Canvas 上的事件
class TouchManager {
constructor() {
this.touches = new Map()
this.callbacks = {
start: [],
move: [],
end: []
}
this.init()
}
init() {
wx.onTouchStart(e => this.handleTouch('start', e))
wx.onTouchMove(e => this.handleTouch('move', e))
wx.onTouchEnd(e => this.handleTouch('end', e))
wx.onTouchCancel(e => this.handleTouch('end', e))
}
handleTouch(type, e) {
e.touches.forEach(touch => {
this.callbacks[type].forEach( => (touch))
})
}
() {
.[type].(callback)
}
}
touchManager = ()
touchManager.(, {
.(, touch., touch.)
})
// 音频管理器
class AudioManager {
constructor() {
this.sounds = new Map()
this.music = null
this.muted = false
}
// 加载音效
loadSound(name, src) {
const audio = wx.createInnerAudioContext()
audio.src = src
this.sounds.set(name, audio)
return audio
}
// 播放音效
playSound(name, volume = 1.0) {
if (this.muted) return
const audio = this.sounds.get(name)
if (audio) {
audio.volume = volume
audio.stop()
audio.play()
}
}
// 播放背景音乐
playMusic(src, loop = true) {
if (this.music) {
this.music.stop()
..()
}
. = wx.()
.. = src
.. = loop
..()
}
() {
..( audio.())
..()
(.) {
..()
. =
}
}
}
// ✅ 微信存储 API
// 同步存储
wx.setStorageSync('playerData', { level: 5, score: 1000 })
const data = wx.getStorageSync('playerData') || {}
// 异步存储(推荐用于大数据)
wx.setStorage({
key: 'largeData',
data: largeObject,
success() { console.log('Saved') },
fail(err) { console.error(err) }
})
// 删除
wx.removeStorageSync('key')
wx.clearStorageSync() // 清空所有
微信小游戏右上角有固定的操作菜单按钮("..."按钮),点击后显示菜单选项。必须避开此区域放置关键UI元素。
┌─────────────────────────────────┐
│ [≡] [•••] │ ← 右上角菜单按钮区域(约44px高度)
│ │
│ 安全区 (safeArea) │ ← 主要内容区域
│ │
│ │
│ │
│ _______________________________│ ← 底部安全区域(iPhone X系列手势条)
└─────────────────────────────────┘
避开区域尺寸:
systemInfo.statusBarHeight// 获取安全区域信息
const systemInfo = wx.getSystemInfoSync()
const { safeArea, statusBarHeight } = systemInfo
// 安全区域边界
console.log('安全区域:', {
top: safeArea.top, // 安全区域顶部 y 坐标
bottom: safeArea.bottom, // 安全区域底部 y 坐标
left: safeArea.left, // 安全区域左侧 x 坐标
right: safeArea.right, // 安全区域右侧 x 坐标
width: safeArea.width, // 安全区域宽度
height: safeArea.height // 安全区域高度
})
// 右上角按钮区域(保守估计)
const menuButtonArea = {
top: statusBarHeight,
height: 44,
right: systemInfo.windowWidth,
width: 87 // iOS 较宽,Android 较窄
}
class SafeAreaManager {
constructor() {
const systemInfo = wx.getSystemInfoSync()
this.safeArea = systemInfo.safeArea
this.screenWidth = systemInfo.windowWidth
this.screenHeight = systemInfo.windowHeight
this.statusBarHeight = systemInfo.statusBarHeight
// 计算危险区域(需要避开)
this.dangerAreas = {
// 顶部:状态栏 + 菜单按钮
top: {
y: 0,
height: Math.max(this.safeArea.top, this.statusBarHeight + 44)
},
// 右上角:菜单按钮
topRight: {
x: this.screenWidth - 100,
y: 0,
width: 100,
height: this.statusBarHeight + 44
},
// 底部:手势条区域(iPhone X系列)
bottom: {
: ..,
: . - ..
}
}
}
() {
danger = .
(y < danger..)
(x + width > danger.. && y < danger..)
(y + height > danger..)
}
() {
{
: ...,
: ..,
: ..,
: ..
}
}
() {
bounds = .()
(x < bounds.) x = bounds.
(x + width > bounds.) x = bounds. - width
(y < bounds.) y = bounds.
(y + height > bounds.) y = bounds. - height
{ x, y }
}
}
safeArea = ()
button = { : , : , : , : }
(!safeArea.(button., button., button., button.)) {
adjusted = safeArea.(button., button., button., button.)
button. = adjusted.
button. = adjusted.
}
// ❌ 错误:按钮放在右上角菜单按钮下方
const button = { x: screenWidth - 120, y: 20 }
// ✅ 正确:按钮放在安全区域内
const safeY = safeArea.top + 10
const button = { x: screenWidth - 120, y: safeY }
// ❌ 错误:忽略底部手势条
const bottomButton = { y: screenHeight - 50 }
// ✅ 正确:考虑底部安全区域
const safeBottomY = safeArea.bottom - 50 - 10
const bottomButton = { y: safeBottomY }
// 获取设备信息
const deviceInfo = wx.getDeviceInfo()
const isIPhoneX = deviceInfo.model.includes('iPhone X') ||
deviceInfo.model.includes('iPhone 1') // iPhone 11, 12, 13, 14, 15 系列
// iPhone X 系列特殊处理
if (isIPhoneX) {
// 底部额外留白
this.bottomPadding = 34
}
class ScreenAdapter {
constructor(designWidth = 375, designHeight = 667) {
this.designWidth = designWidth
this.designHeight = designHeight
const info = wx.getSystemInfoSync()
this.screenWidth = info.windowWidth
this.screenHeight = info.windowHeight
this.pixelRatio = info.pixelRatio
this.safeArea = info.safeArea
// 计算缩放(保持宽高比)
this.scale = Math.min(
this.screenWidth / designWidth,
this.screenHeight / designHeight
)
// 计算偏移(居中)
this.offsetX = (this.screenWidth - designWidth * this.scale) / 2
this.offsetY = (this.screenHeight - designHeight * this.scale) / 2
}
() {
{
: (x - .) / .,
: (y - .) / .
}
}
() {
{
: x * . + .,
: y * . + .
}
}
() {
{
: ..,
: ..,
: ..,
: ..
}
}
}
// ❌ 每帧都清除整个画布
ctx.clearRect(0, 0, canvas.width, canvas.height)
// ✅ 只清除变化区域(对于静态背景)
ctx.clearRect(obj.x - 1, obj.y - 1, obj.width + 2, obj.height + 2)
// ✅ 使用离屏 Canvas 缓存静态元素
const cacheCanvas = wx.createOffscreenCanvas({ width: 200, height: 200 })
const cacheCtx = cacheCanvas.getContext('2d')
// 绘制一次
cacheCtx.drawImage(backgroundImg, 0, 0)
// 主循环中直接绘制缓存
ctx.drawImage(cacheCanvas, 0, 0)
class ObjectPool {
constructor(createFn, initialSize = 10) {
this.createFn = createFn
this.pool = []
this.active = []
// 预创建对象
for (let i = 0; i < initialSize; i++) {
this.pool.push(createFn())
}
}
get() {
const obj = this.pool.pop() || this.createFn()
this.active.push(obj)
return obj
}
release(obj) {
const index = this.active.indexOf(obj)
if (index > -1) {
this.active.splice(index, 1)
this.pool.push(obj)
}
}
releaseAll() {
while (this.active.) {
..(..())
}
}
}
bulletPool = ( ({ : , : , : }), )
// 资源预加载
class AssetLoader {
constructor() {
this.images = new Map()
this.audios = new Map()
}
async loadImages(list) {
const promises = list.map(item =>
loadImage(item.src).then(img => {
this.images.set(item.name, img)
return { name: item.name, img }
})
)
return Promise.all(promises)
}
getImage(name) {
return this.images.get(name)
}
}
// 使用
const loader = new AssetLoader()
await loader.loadImages([
{ name: 'player', src: 'images/player.png' },
{ name: , : }
])
微信小游戏的好友排行榜必须在开放数据域中实现,主域无法直接访问好友数据。
项目结构:
├── game.js # 主域入口
├── open-data-context/ # 开放数据域
│ └── index.js # 排行榜逻辑
└── game.json # 配置
game.json 配置:
{
"openDataContext": "open-data-context"
}
主域与开放数据域通信:
// 主域 game.js
const openDataContext = wx.getOpenDataContext()
// 发送消息到开放数据域
openDataContext.postMessage({
type: 'updateScore',
score: 1000,
level: 5
})
// 创建共享 Canvas(用于显示排行榜)
const sharedCanvas = openDataContext.canvas
sharedCanvas.width = 300
sharedCanvas.height = 400
// 在主 Canvas 上绘制共享 Canvas
const mainCtx = mainCanvas.getContext('2d')
mainCtx.drawImage(sharedCanvas, 50, 100)
开放数据域 index.js:
// 接收主域消息
wx.onMessage(data => {
if (data.type === 'updateScore') {
// 更新用户数据到云端
wx.setUserCloudStorage({
KVDataList: [{
key: 'score',
value: JSON.stringify({ score: data.score, level: data.level })
}],
success() {
console.log('分数上传成功')
// 刷新排行榜
getFriendRanking()
}
})
}
if (data.type === 'getRanking') {
getFriendRanking()
}
})
// 获取好友排行榜
function getFriendRanking() {
wx.getFriendCloudStorage({
keyList: ['score'],
success(res) {
// res.data 是好友数据数组
const sorted = res.data.sort((a, b) => {
const scoreA = JSON.parse(a.KVDataList[0].value).
scoreB = .(b.[].).
scoreB - scoreA
})
(sorted)
}
})
}
() {
canvas = wx.()
ctx = canvas.()
ctx. =
ctx.(, , canvas., canvas.)
ctx. =
ctx. =
data.( {
score = .(player.[].).
ctx.(, , + index * )
})
}
重要限制:
wx.getFriendCloudStorage() 只能在开放数据域调用wx. API(如网络请求受限)sharedCanvas 与主域共享用于将耗时计算放到后台线程,避免阻塞主线程。
// 主线程
const worker = wx.createWorker('workers/physics.js')
// 发送数据
worker.postMessage({
type: 'calculate',
bodies: physicsData
})
// 接收结果
worker.onMessage(res => {
if (res.type === 'result') {
// 更新物理状态
updatePhysics(res.data)
}
})
// workers/physics.js
worker.onMessage(data => {
if (data.type === 'calculate') {
// 执行耗时计算
const result = heavyPhysicsCalculation(data.bodies)
// 返回结果
worker.postMessage({
type: 'result',
data: result
})
}
})
微信小游戏对内存敏感,需要主动管理:
// 监听内存警告
wx.onMemoryWarning((res) => {
console.log('内存警告,级别:', res.level) // level: 0-5,越大越严重
// 清理缓存
textureCache.clear()
audioCache.clear()
// 释放未使用的资源
gc()
})
// 主动触发垃圾回收(调试用)
wx.triggerGC()
// 获取内存使用情况
const performance = wx.getPerformance()
const memory = performance.getEntriesByType('memory')
console.log('内存使用:', memory)
内存优化建议:
// 1. 及时销毁音频
const audio = wx.createInnerAudioContext()
audio.src = 'sound.mp3'
audio.play()
audio.onEnded(() => {
audio.destroy() // 播放完毕后销毁
})
// 2. 图片对象池
class ImagePool {
constructor(maxSize = 20) {
this.pool = new Map()
this.maxSize = maxSize
}
get(src) {
if (this.pool.has(src)) {
return this.pool.get(src)
}
const img = wx.createImage()
img.src = src
if (this.pool.size >= this.maxSize) {
// 移除最旧的
const firstKey = this.pool.keys().next().value
this.pool.delete(firstKey)
}
..(src, img)
img
}
}
{
() {
. = []
}
() {
..(callback)
}
() {
. = []
}
}
// 被动分享(用户点击右上角菜单)
wx.onShareAppMessage(() => {
return {
title: '来挑战我的高分!',
imageUrl: 'images/share.png',
query: 'level=5&score=1000' // 参数会传递给被分享者
}
})
// 主动分享
wx.shareAppMessage({
title: '我通关了第5关!',
imageUrl: 'images/share-level5.png',
query: 'from=share&level=5',
success() {
console.log('分享成功')
},
fail(err) {
console.error('分享失败', err)
}
})
// 分享到群
wx.shareAppMessage({
title: '一起来玩!',
imageUrl: 'images/share.png',
toGroup: true
})
// 发起支付(需在小程序后台开通)
wx.requestMidasPayment({
mode: 'game',
env: 0, // 0: 正式环境, 1: 沙箱环境
offerId: '123456', // 商品ID
currencyType: 'CNY',
buyQuantity: 10,
success(res) {
console.log('支付成功', res)
// 发放道具
},
fail(err) {
console.error('支付失败', err)
}
})
// 检查 API 是否可用
if (wx.canIUse('getDeviceInfo')) {
const deviceInfo = wx.getDeviceInfo()
}
// 比较基础库版本
function compareVersion(v1, v2) {
v1 = v1.split('.')
v2 = v2.split('.')
const len = Math.max(v1.length, v2.length)
while (v1.length < len) v1.push('0')
while (v2.length < len) v2.push('0')
for (let i = 0; i < len; i++) {
const num1 = parseInt(v1[i])
const num2 = parseInt(v2[i])
if (num1 > num2) return 1
if (num1 < num2) return -1
}
return 0
}
const systemInfo = wx.getSystemInfoSync()
const baseVersion = systemInfo.SDKVersion
if (compareVersion(baseVersion, '2.10.0') >= 0) {
// 使用新 API
} {
}
window is not defined// ❌ 使用 window
window.myVar = value
// ✅ 使用 globalThis 或模块变量
globalThis.myVar = value
document is not defined// ❌ 使用 document
const el = document.getElementById('xxx')
document.createElement('canvas')
// ✅ 使用微信 API
const canvas = wx.createCanvas()
// 检查路径是否正确
// 检查是否配置了 download 域名(网络图片)
// 检查图片格式(支持 png, jpg, gif)
wx.createImage()
img.onerror = (e) => {
console.error('Image load failed:', e)
}
// 用户交互后才能播放音频
// 首次播放需要在 touchstart/touchend 回调中触发
let audioInitialized = false
wx.onTouchStart(() => {
if (!audioInitialized) {
// 初始化音频
audioInitialized = true
}
})
my-minigame/
├── game.json # 游戏配置
├── game.js # 入口文件
├── project.config.json # 项目配置
├── js/
│ ├── Game.js # 游戏主类
│ ├── scenes/ # 场景
│ │ ├── MenuScene.js
│ │ └── GameScene.js
│ ├── entities/ # 实体类
│ │ ├── Player.js
│ │ └── Enemy.js
│ ├── managers/ # 管理器
│ │ ├── AudioManager.js
│ │ ├── InputManager.js
│ │ └── SceneManager.js
│ └── utils/ # 工具类
│ ├── ObjectPool.js
│ └── ScreenAdapter.js
├── images/ # 图片资源
├── audio/ # 音频资源
└── subpackages/ # 分包
├── level2/
└── level3/
{
"deviceOrientation": "portrait",
"showStatusBar": false,
"networkTimeout": {
"request": 10000,
"connectSocket": 10000,
"uploadFile": 10000,
"downloadFile": 10000
},
"subpackages": [
{
"name": "level2",
"root": "subpackages/level2"
},
{
"name": "level3",
"root": "subpackages/level3"
}
],
"plugins": {}
开发微信小游戏时,Claude Code 应确保:
window、document、DOM APIwx.createCanvas() 创建 Canvaswx.createImage() 加载图片wx.createInnerAudioContext() 播放音频wx.onTouchStart/Move/End 处理触摸wx.setStorageSync/getStorageSync 存储wx.request() 进行网络请求roundRect() 等不支持的 APIrgba() 或标准 #RRGGBB 格式wx.getSystemInfoSync().safeArea 获取安全区域wx.onMemoryWarning 处理内存警告wx.canIUse() 检查 API 兼容性