引言

小游戏市场在2024年继续蓬勃发展,微信小游戏月活跃用户突破4亿,小游戏市场规模达到500亿元。随着开发门槛降低和变现渠道多样化,越来越多开发者涌入这个领域。本文将从技术实现、商业模式、用户运营等多个维度,全面解析小游戏开发与变现的策略。

小游戏技术架构

技术栈选择

LayaAir引擎

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// LayaAir 2.x 游戏初始化
class GameMain {
    constructor() {
        // 初始化引擎
        Laya.init(1136, 640, WebGL).then(() => {
            this.init()
        })
    }

    init() {
        // 设置适配模式
        Laya.stage.scaleMode = Laya.Stage.SCALE_FIXED_AUTO;
        Laya.stage.screenMode = Laya.Stage.SCREEN_HORIZONTAL;

        // 加载资源
        this.loadResources();
    }

    loadResources() {
        Laya.loader.load([
            "res/atlas/ui.atlas",
            "res/scene/scene.json",
            "res/character/player.json"
        ], Laya.Handler.create(this, this.onLoaded));
    }

    onLoaded() {
        // 创建游戏场景
        this.gameScene = new GameScene();
        Laya.stage.addChild(this.gameScene);

        // 启动游戏循环
        this.startGameLoop();
    }

    startGameLoop() {
        Laya.timer.frameLoop(1, this, this.update);
    }

    update() {
        // 游戏逻辑更新
        this.gameScene.update();
    }
}

// 启动游戏
new GameMain();

Cocos Creator

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// Cocos Creator 游戏脚本
import { _decorator, Component, Node, input, Input, EventTouch, Vec3 } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('PlayerController')
export class PlayerController extends Component {
    @property(Node)
    playerNode: Node = null!;

    @property
    moveSpeed: number = 300;

    private _moveDir: Vec3 = new Vec3();

    onLoad() {
        // 初始化输入系统
        input.on(Input.EventType.TOUCH_START, this.onTouchStart, this);
        input.on(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
        input.on(Input.EventType.TOUCH_END, this.onTouchEnd, this);
    }

    onTouchStart(event: EventTouch) {
        // 获取触摸位置
        const touchPos = event.getUILocation();
        this.updateMoveDirection(touchPos);
    }

    onTouchMove(event: EventTouch) {
        const touchPos = event.getUILocation();
        this.updateMoveDirection(touchPos);
    }

    onTouchEnd() {
        this._moveDir.set(0, 0, 0);
    }

    private updateMoveDirection(touchPos: Vec3) {
        const playerPos = this.playerNode.position;
        const dir = new Vec3(
            touchPos.x - playerPos.x,
            touchPos.y - playerPos.y,
            0
        );
        Vec3.normalize(dir, dir);
        this._moveDir = dir;
    }

    update(deltaTime: number) {
        if (this._moveDir.lengthSquared() > 0) {
            const movement = new Vec3();
            Vec3.multiplyScalar(movement, this._moveDir, this.moveSpeed * deltaTime);

            const newPos = this.playerNode.position.clone();
            Vec3.add(newPos, newPos, movement);
            this.playerNode.position = newPos;
        }
    }

    onDestroy() {
        input.off(Input.EventType.TOUCH_START, this.onTouchStart, this);
        input.off(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
        input.off(Input.EventType.TOUCH_END, this.onTouchEnd);
    }
}

游戏性能优化

对象池管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// 对象池实现
class ObjectPool {
    constructor(createFn, resetFn, maxSize = 50) {
        this.createFn = createFn;
        this.resetFn = resetFn;
        this.maxSize = maxSize;
        this.pool = [];
    }

    get() {
        if (this.pool.length > 0) {
            return this.pool.pop();
        } else {
            return this.createFn();
        }
    }

    put(obj) {
        if (this.pool.length < this.maxSize) {
            this.resetFn(obj);
            this.pool.push(obj);
        }
    }

    clear() {
        this.pool = [];
    }
}

// 子弹对象池使用示例
class BulletManager {
    constructor() {
        this.bulletPool = new ObjectPool(
            // 创建函数
            () => {
                const bullet = new Laya.Sprite();
                bullet.graphics.drawCircle(0, 0, 5, "#ff0000");
                return bullet;
            },
            // 重置函数
            (bullet) => {
                bullet.removeSelf();
                bullet.active = false;
            }
        );

        this.activeBullets = [];
    }

    createBullet(x, y, direction, speed) {
        const bullet = this.bulletPool.get();
        bullet.pos(x, y);
        bullet.active = true;
        bullet.direction = direction;
        bullet.speed = speed;

        this.activeBullets.push(bullet);
        Laya.stage.addChild(bullet);
    }

    update() {
        for (let i = this.activeBullets.length - 1; i >= 0; i--) {
            const bullet = this.activeBullets[i];

            // 更新位置
            bullet.x += bullet.direction.x * bullet.speed;
            bullet.y += bullet.direction.y * bullet.speed;

            // 检查是否超出屏幕
            if (bullet.x < -50 || bullet.x > Laya.stage.width + 50 ||
                bullet.y < -50 || bullet.y > Laya.stage.height + 50) {
                this.removeBullet(bullet);
            }
        }
    }

    removeBullet(bullet) {
        const index = this.activeBullets.indexOf(bullet);
        if (index > -1) {
            this.activeBullets.splice(index, 1);
            this.bulletPool.put(bullet);
        }
    }
}

资源加载优化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// 资源加载管理器
class ResourceManager {
    constructor() {
        this.loadedResources = new Map();
        this.loadingPromises = new Map();
    }

    // 预加载资源
    async preloadResources(resourceList) {
        const groups = this.groupResources(resourceList);

        for (const group of groups) {
            await this.loadResourceGroup(group);
        }
    }

    // 分组资源(按优先级)
    groupResources(resources) {
        const critical = [];
        const important = [];
        const optional = [];

        resources.forEach(resource => {
            switch (resource.priority) {
                case 'critical':
                    critical.push(resource);
                    break;
                case 'important':
                    important.push(resource);
                    break;
                case 'optional':
                    optional.push(resource);
                    break;
            }
        });

        return [critical, important, optional];
    }

    // 加载资源组
    async loadResourceGroup(group) {
        const promises = group.map(resource => this.loadResource(resource));
        return Promise.all(promises);
    }

    // 加载单个资源
    async loadResource(resource) {
        if (this.loadedResources.has(resource.url)) {
            return this.loadedResources.get(resource.url);
        }

        if (this.loadingPromises.has(resource.url)) {
            return this.loadingPromises.get(resource.url);
        }

        const promise = this.doLoadResource(resource);
        this.loadingPromises.set(resource.url, promise);

        try {
            const result = await promise;
            this.loadedResources.set(resource.url, result);
            return result;
        } finally {
            this.loadingPromises.delete(resource.url);
        }
    }

    // 实际加载逻辑
    async doLoadResource(resource) {
        return new Promise((resolve, reject) => {
            Laya.loader.load(resource.url, Laya.Handler.create(this, resolve), null, resource.type);
        });
    }

    // 释放未使用资源
    releaseUnusedResources() {
        // 实现资源释放逻辑
        Laya.loader.clearRes();
    }
}

广告变现策略

微信小游戏广告系统集成

Banner广告

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// Banner广告管理器
class BannerAdManager {
    constructor() {
        this.bannerAd = null;
        this.bannerAdUnitId = 'adunit-xxxxx'; // 替换为实际广告位ID
        this.isShowing = false;
        this.retryCount = 0;
        this.maxRetryCount = 3;
    }

    init() {
        // 创建Banner广告
        this.bannerAd = wx.createBannerAd({
            adUnitId: this.bannerAdUnitId,
            style: {
                left: 0,
                top: wx.getSystemInfoSync().windowHeight - 100,
                width: wx.getSystemInfoSync().windowWidth
            }
        });

        // 监听广告加载
        this.bannerAd.onLoad(() => {
            console.log('Banner广告加载成功');
            this.retryCount = 0;
        });

        // 监听广告错误
        this.bannerAd.onError((err) => {
            console.error('Banner广告加载失败:', err);
            this.retryLoad();
        });

        // 监听广告关闭
        this.bannerAd.onClose(() => {
            console.log('Banner广告关闭');
            this.isShowing = false;
        });
    }

    show() {
        if (this.bannerAd && !this.isShowing) {
            this.bannerAd.show().then(() => {
                this.isShowing = true;
            }).catch(err => {
                console.error('Banner广告显示失败:', err);
            });
        }
    }

    hide() {
        if (this.bannerAd && this.isShowing) {
            this.bannerAd.hide();
            this.isShowing = false;
        }
    }

    destroy() {
        if (this.bannerAd) {
            this.bannerAd.destroy();
            this.bannerAd = null;
        }
    }

    retryLoad() {
        if (this.retryCount < this.maxRetryCount) {
            this.retryCount++;
            setTimeout(() => {
                this.init();
            }, 2000 * this.retryCount);
        }
    }
}

激励视频广告

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// 激励视频广告管理器
class RewardedVideoAdManager {
    constructor() {
        this.rewardedVideoAd = null;
        this.adUnitId = 'adunit-yyyyy'; // 替换为实际广告位ID
        this.isLoaded = false;
        this.loadPromise = null;
        this.watchCallback = null;
    }

    init() {
        this.rewardedVideoAd = wx.createRewardedVideoAd({
            adUnitId: this.adUnitId
        });

        this.setupEventListeners();
        this.loadAd();
    }

    setupEventListeners() {
        // 广告加载成功
        this.rewardedVideoAd.onLoad(() => {
            console.log('激励视频广告加载成功');
            this.isLoaded = true;
            this.loadPromise = null;
        });

        // 广告加载失败
        this.rewardedVideoAd.onError((err) => {
            console.error('激励视频广告加载失败:', err);
            this.isLoaded = false;
            this.loadPromise = null;

            // 通知回调失败
            if (this.watchCallback) {
                this.watchCallback({ success: false, error: err });
                this.watchCallback = null;
            }
        });

        // 广告关闭
        this.rewardedVideoAd.onClose((res) => {
            console.log('激励视频广告关闭', res);

            // 预加载下一个广告
            this.loadAd();

            // 通知回调结果
            if (this.watchCallback) {
                this.watchCallback({
                    success: res.isEnded || res.ended,
                    rewarded: res.isEnded || res.ended
                });
                this.watchCallback = null;
            }
        });
    }

    async loadAd() {
        if (this.loadPromise) {
            return this.loadPromise;
        }

        this.loadPromise = new Promise((resolve) => {
            this.rewardedVideoAd.load().then(() => {
                resolve();
            }).catch(() => {
                resolve();
            });
        });

        return this.loadPromise;
    }

    // 观看广告
    async watchAd() {
        return new Promise((resolve) => {
            if (this.watchCallback) {
                resolve({ success: false, error: '广告正在播放中' });
                return;
            }

            if (!this.isLoaded) {
                resolve({ success: false, error: '广告未加载完成' });
                return;
            }

            this.watchCallback = resolve;
            this.rewardedVideoAd.show()
                .catch(err => {
                    this.watchCallback = null;
                    resolve({ success: false, error: err });
                });
        });
    }
}

插屏广告

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// 插屏广告管理器
class InterstitialAdManager {
    constructor() {
        this.interstitialAd = null;
        this.adUnitId = 'adunit-zzzzz'; // 替换为实际广告位ID
        this.isLoaded = false;
        this.lastShowTime = 0;
        this.minInterval = 30000; // 最小间隔30秒
    }

    init() {
        this.interstitialAd = wx.createInterstitialAd({
            adUnitId: this.adUnitId
        });

        this.interstitialAd.onLoad(() => {
            console.log('插屏广告加载成功');
            this.isLoaded = true;
        });

        this.interstitialAd.onError((err) => {
            console.error('插屏广告加载失败:', err);
            this.isLoaded = false;
        });

        this.interstitialAd.onClose(() => {
            console.log('插屏广告关闭');
            this.isLoaded = false;
            // 延迟重新加载
            setTimeout(() => {
                this.loadAd();
            }, 5000);
        });

        this.loadAd();
    }

    loadAd() {
        if (!this.isLoaded) {
            this.interstitialAd.load();
        }
    }

    // 显示插屏广告
    show() {
        const now = Date.now();

        // 检查时间间隔
        if (now - this.lastShowTime < this.minInterval) {
            console.log('插屏广告间隔时间未到');
            return false;
        }

        if (this.isLoaded) {
            this.interstitialAd.show()
                .then(() => {
                    this.lastShowTime = now;
                    console.log('插屏广告显示成功');
                    return true;
                })
                .catch(err => {
                    console.error('插屏广告显示失败:', err);
                    return false;
                });
        } else {
            console.log('插屏广告未加载完成');
            return false;
        }
    }
}

内购系统设计

虚拟商品管理

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// 商店系统
class GameStore {
    constructor() {
        this.products = [
            {
                id: 'coin_100',
                name: '100金币',
                price: 6, // 6元
                description: '获得100金币,可用于购买道具',
                icon: 'res/icons/coin_100.png',
                type: 'consumable'
            },
            {
                id: 'remove_ads',
                name: '去广告',
                price: 12,
                description: '永久移除所有广告',
                icon: 'res/icons/no_ads.png',
                type: 'permanent'
            },
            {
                id: 'vip_month',
                name: '月度VIP',
                price: 18,
                description: '30天VIP特权,享双倍奖励',
                icon: 'res/icons/vip.png',
                type: 'subscription',
                duration: 30 * 24 * 60 * 60 * 1000 // 30天
            }
        ];

        this.purchasedItems = new Map();
        this.initPayment();
    }

    initPayment() {
        // 初始化支付系统
        if (wx.requestPayment) {
            this.paymentProvider = 'wechat';
        } else {
            console.error('支付系统不可用');
        }
    }

    // 获取商品列表
    getProducts() {
        return this.products;
    }

    // 购买商品
    async purchaseProduct(productId) {
        const product = this.products.find(p => p.id === productId);
        if (!product) {
            throw new Error('商品不存在');
        }

        // 检查是否已购买永久商品
        if (product.type === 'permanent' && this.isProductPurchased(productId)) {
            throw new Error('该商品已购买');
        }

        try {
            // 发起支付
            await this.requestPayment(product);

            // 支付成功,处理购买逻辑
            await this.processPurchase(product);

            return { success: true, product };
        } catch (error) {
            console.error('购买失败:', error);
            return { success: false, error: error.message };
        }
    }

    // 发起支付请求
    async requestPayment(product) {
        // 获取支付参数
        const paymentParams = await this.getPaymentParams(product);

        return new Promise((resolve, reject) => {
            wx.requestPayment({
                ...paymentParams,
                success: resolve,
                fail: reject
            });
        });
    }

    // 获取支付参数
    async getPaymentParams(product) {
        // 调用后端API获取支付参数
        const response = await wx.request({
            url: 'https://api.example.com/payment/create',
            method: 'POST',
            data: {
                productId: product.id,
                price: product.price,
                userId: wx.getStorageSync('userId')
            }
        });

        return response.data;
    }

    // 处理购买结果
    async processPurchase(product) {
        const purchaseInfo = {
            productId: product.id,
            purchaseTime: Date.now(),
            type: product.type
        };

        switch (product.type) {
            case 'consumable':
                // 消耗品,立即给予奖励
                this.grantRewards(product);
                break;

            case 'permanent':
                // 永久商品,记录购买状态
                this.purchasedItems.set(product.id, purchaseInfo);
                this.savePurchasedItems();
                break;

            case 'subscription':
                // 订阅商品,设置过期时间
                purchaseInfo.expireTime = Date.now() + product.duration;
                this.purchasedItems.set(product.id, purchaseInfo);
                this.savePurchasedItems();
                break;
        }

        // 触发购买事件
        this.triggerPurchaseEvent(product);
    }

    // 给予奖励
    grantRewards(product) {
        switch (product.id) {
            case 'coin_100':
                // 增加100金币
                gameManager.addCoins(100);
                break;
            case 'remove_ads':
                // 移除广告
                adManager.removeAllAds();
                break;
        }
    }

    // 检查商品是否已购买
    isProductPurchased(productId) {
        const purchase = this.purchasedItems.get(productId);
        if (!purchase) return false;

        if (purchase.type === 'subscription') {
            // 检查订阅是否过期
            return Date.now() < purchase.expireTime;
        }

        return true;
    }

    // 获取购买状态
    getPurchaseStatus() {
        const status = {};
        this.purchasedItems.forEach((purchase, productId) => {
            status[productId] = {
                purchased: this.isProductPurchased(productId),
                purchaseTime: purchase.purchaseTime,
                expireTime: purchase.expireTime
            };
        });
        return status;
    }

    // 保存购买记录
    savePurchasedItems() {
        const data = Array.from(this.purchasedItems.entries());
        wx.setStorageSync('purchasedItems', data);
    }

    // 加载购买记录
    loadPurchasedItems() {
        const data = wx.getStorageSync('purchasedItems') || [];
        this.purchasedItems = new Map(data);
    }

    // 触发购买事件
    triggerPurchaseEvent(product) {
        // 数据统计
        wx.reportAnalytics('purchase', {
            product_id: product.id,
            price: product.price,
            timestamp: Date.now()
        });

        // 游戏内事件
        gameManager.eventSystem.emit('product_purchased', product);
    }
}

用户留存与增长

游戏数据分析

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
// 游戏数据分析系统
class GameAnalytics {
    constructor() {
        this.sessionStartTime = Date.now();
        this.sessionId = this.generateSessionId();
        this.customEvents = [];
        this.levelProgress = new Map();
    }

    // 生成会话ID
    generateSessionId() {
        return Date.now().toString(36) + Math.random().toString(36).substr(2);
    }

    // 记录游戏开始
    trackGameStart() {
        const event = {
            eventName: 'game_start',
            timestamp: Date.now(),
            sessionId: this.sessionId,
            properties: {
                deviceInfo: wx.getSystemInfoSync(),
                isNewPlayer: this.isNewPlayer(),
                lastPlayTime: wx.getStorageSync('lastPlayTime') || null
            }
        };

        this.reportEvent(event);
    }

    // 记录游戏结束
    trackGameEnd(properties = {}) {
        const sessionDuration = Date.now() - this.sessionStartTime;

        const event = {
            eventName: 'game_end',
            timestamp: Date.now(),
            sessionId: this.sessionId,
            properties: {
                sessionDuration,
                level: properties.level || 1,
                score: properties.score || 0,
                reason: properties.reason || 'normal'
            }
        };

        this.reportEvent(event);

        // 保存游戏时长
        this.savePlaytime(sessionDuration);
    }

    // 记录关卡进度
    trackLevelStart(levelId) {
        const event = {
            eventName: 'level_start',
            timestamp: Date.now(),
            sessionId: this.sessionId,
            properties: {
                levelId,
                attempts: this.getLevelAttempts(levelId)
            }
        };

        this.reportEvent(event);
    }

    trackLevelComplete(levelId, properties = {}) {
        const attempts = this.getLevelAttempts(levelId);

        const event = {
            eventName: 'level_complete',
            timestamp: Date.now(),
            sessionId: this.sessionId,
            properties: {
                levelId,
                attempts,
                duration: properties.duration,
                stars: properties.stars || 0,
                score: properties.score || 0
            }
        };

        this.reportEvent(event);

        // 更新关卡进度
        this.updateLevelProgress(levelId, 'complete');
    }

    trackLevelFail(levelId, properties = {}) {
        const attempts = this.getLevelAttempts(levelId);

        const event = {
            eventName: 'level_fail',
            timestamp: Date.now(),
            sessionId: this.sessionId,
            properties: {
                levelId,
                attempts,
                reason: properties.reason || 'timeout',
                progress: properties.progress || 0
            }
        };

        this.reportEvent(event);

        // 更新关卡进度
        this.updateLevelProgress(levelId, 'fail');
    }

    // 记录广告观看
    trackAdWatch(adType, result) {
        const event = {
            eventName: 'ad_watch',
            timestamp: Date.now(),
            sessionId: this.sessionId,
            properties: {
                adType, // 'banner', 'interstitial', 'rewarded'
                result, // 'completed', 'skipped', 'failed'
                placement: this.getCurrentPlacement()
            }
        };

        this.reportEvent(event);
    }

    // 记录内购行为
    trackPurchase(productId, price, result) {
        const event = {
            eventName: 'purchase',
            timestamp: Date.now(),
            sessionId: this.sessionId,
            properties: {
                productId,
                price,
                result, // 'success', 'failed', 'cancelled'
                level: this.getCurrentLevel()
            }
        };

        this.reportEvent(event);
    }

    // 记录自定义事件
    trackCustomEvent(eventName, properties = {}) {
        const event = {
            eventName,
            timestamp: Date.now(),
            sessionId: this.sessionId,
            properties
        };

        this.customEvents.push(event);
        this.reportEvent(event);
    }

    // 上报事件到服务器
    async reportEvent(event) {
        try {
            await wx.request({
                url: 'https://api.example.com/analytics/events',
                method: 'POST',
                data: {
                    events: [event],
                    userId: wx.getStorageSync('userId'),
                    appId: wx.getAccountInfoSync().miniProgram.appId
                }
            });
        } catch (error) {
            console.error('事件上报失败:', error);
            // 本地缓存,稍后重试
            this.cacheEvent(event);
        }
    }

    // 缓存事件
    cacheEvent(event) {
        const cachedEvents = wx.getStorageSync('cachedEvents') || [];
        cachedEvents.push(event);

        // 限制缓存大小
        if (cachedEvents.length > 100) {
            cachedEvents.shift();
        }

        wx.setStorageSync('cachedEvents', cachedEvents);
    }

    // 重试上报缓存的事件
    async retryCachedEvents() {
        const cachedEvents = wx.getStorageSync('cachedEvents') || [];
        if (cachedEvents.length === 0) return;

        try {
            await wx.request({
                url: 'https://api.example.com/analytics/events',
                method: 'POST',
                data: {
                    events: cachedEvents,
                    userId: wx.getStorageSync('userId'),
                    appId: wx.getAccountInfoSync().miniProgram.appId
                }
            });

            // 清空缓存
            wx.removeStorageSync('cachedEvents');
        } catch (error) {
            console.error('缓存事件上报失败:', error);
        }
    }

    // 获取关卡尝试次数
    getLevelAttempts(levelId) {
        const key = `level_attempts_${levelId}`;
        return wx.getStorageSync(key) || 0;
    }

    // 增加关卡尝试次数
    incrementLevelAttempts(levelId) {
        const key = `level_attempts_${levelId}`;
        const attempts = this.getLevelAttempts(levelId);
        wx.setStorageSync(key, attempts + 1);
    }

    // 更新关卡进度
    updateLevelProgress(levelId, status) {
        if (!this.levelProgress.has(levelId)) {
            this.levelProgress.set(levelId, {
                attempts: 0,
                bestScore: 0,
                completed: false,
                completedAt: null
            });
        }

        const progress = this.levelProgress.get(levelId);
        progress.attempts++;

        if (status === 'complete' && !progress.completed) {
            progress.completed = true;
            progress.completedAt = Date.now();
        }

        wx.setStorageSync('levelProgress', Array.from(this.levelProgress.entries()));
    }

    // 检查是否为新玩家
    isNewPlayer() {
        return !wx.getStorageSync('hasPlayedBefore');
    }

    // 保存游戏时长
    savePlaytime(duration) {
        const today = new Date().toDateString();
        const playtimeData = wx.getStorageSync('playtimeData') || {};

        if (!playtimeData[today]) {
            playtimeData[today] = 0;
        }

        playtimeData[today] += duration;
        wx.setStorageSync('playtimeData', playtimeData);
        wx.setStorageSync('lastPlayTime', Date.now());
        wx.setStorageSync('hasPlayedBefore', true);
    }

    // 获取当前关卡
    getCurrentLevel() {
        return wx.getStorageSync('currentLevel') || 1;
    }

    // 获取当前广告位
    getCurrentPlacement() {
        return wx.getStorageSync('currentPlacement') || 'unknown';
    }
}

A/B测试系统

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// A/B测试管理器
class ABTestManager {
    constructor() {
        this.experiments = new Map();
        this.userVariant = new Map();
        this.loadExperiments();
    }

    // 加载实验配置
    async loadExperiments() {
        try {
            const response = await wx.request({
                url: 'https://api.example.com/abtest/experiments',
                method: 'GET'
            });

            response.data.forEach(experiment => {
                this.experiments.set(experiment.id, experiment);
            });

            // 为用户分配实验组
            this.assignUserToExperiments();
        } catch (error) {
            console.error('加载A/B测试配置失败:', error);
        }
    }

    // 为用户分配实验组
    assignUserToExperiments() {
        const userId = wx.getStorageSync('userId') || this.generateUserId();
        wx.setStorageSync('userId', userId);

        this.experiments.forEach((experiment, expId) => {
            if (!this.userVariant.has(expId)) {
                const variant = this.assignVariant(experiment, userId);
                this.userVariant.set(expId, variant);

                // 记录分组事件
                this.trackExperimentAssignment(expId, variant);
            }
        });

        // 保存分组结果
        this.saveUserVariants();
    }

    // 分配变体
    assignVariant(experiment, userId) {
        const hash = this.hashString(userId + experiment.id);
        const ratio = hash / 0xFFFFFFFF;

        let cumulativeRatio = 0;
        for (const variant of experiment.variants) {
            cumulativeRatio += variant.ratio;
            if (ratio <= cumulativeRatio) {
                return variant.id;
            }
        }

        // 默认返回第一个变体
        return experiment.variants[0].id;
    }

    // 字符串哈希
    hashString(str) {
        let hash = 0;
        for (let i = 0; i < str.length; i++) {
            const char = str.charCodeAt(i);
            hash = ((hash << 5) - hash) + char;
            hash = hash & hash;
        }
        return Math.abs(hash);
    }

    // 生成用户ID
    generateUserId() {
        return Date.now().toString(36) + Math.random().toString(36).substr(2);
    }

    // 获取实验变体
    getVariant(experimentId) {
        return this.userVariant.get(experimentId);
    }

    // 检查是否在实验中
    isInExperiment(experimentId, variantId) {
        const userVariant = this.getVariant(experimentId);
        return userVariant === variantId;
    }

    // 应用实验配置
    applyExperimentConfig(experimentId, defaultValue) {
        const variant = this.getVariant(experimentId);
        const experiment = this.experiments.get(experimentId);

        if (!experiment || !variant) {
            return defaultValue;
        }

        const variantConfig = experiment.variants.find(v => v.id === variant);
        return variantConfig ? variantConfig.config : defaultValue;
    }

    // 记录实验分组
    trackExperimentAssignment(experimentId, variant) {
        wx.reportAnalytics('experiment_assignment', {
            experiment_id: experimentId,
            variant: variant,
            timestamp: Date.now()
        });
    }

    // 记录实验结果
    trackExperimentResult(experimentId, metric, value) {
        const variant = this.getVariant(experimentId);

        wx.reportAnalytics('experiment_result', {
            experiment_id: experimentId,
            variant: variant,
            metric: metric,
            value: value,
            timestamp: Date.now()
        });
    }

    // 保存用户分组
    saveUserVariants() {
        const data = Array.from(this.userVariant.entries());
        wx.setStorageSync('abtest_variants', data);
    }

    // 加载用户分组
    loadUserVariants() {
        const data = wx.getStorageSync('abtest_variants') || [];
        this.userVariant = new Map(data);
    }
}

总结

小游戏开发与变现需要综合考量多个方面:

技术实现:

  1. 选择合适的游戏引擎和开发框架
  2. 实现高效的性能优化策略
  3. 建立完善的资源管理系统

变现模式:

  1. 多元化广告系统集成
  2. 合理的内购机制设计
  3. 持续的用户价值挖掘

用户运营:

  1. 深度的数据分析和监控
  2. A/B测试驱动的产品优化
  3. 用户留存和增长策略

商业化成功要素:

  1. 优质的游戏内容和体验
  2. 合理的变现节奏和策略
  3. 精细化的运营和优化

通过系统化的开发和运营策略,小游戏可以实现商业成功,为用户创造价值的同时获得持续收益。


相关资源: