소스 정보
- 저장소
- wukaikailive/weixin-game-skill
- 최근 소스 활동
- 2026년 3월 17일 13:18
- 감지된 SKILL.md 언어
- 중국어
- 스타
- 7
- 포크
- 1
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/wukaikailive/weixin-game-skill --skill weixin-game명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SOC 직업 분류 기준
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 兼容性