程序汪3万18天接的塔防射击游戏小程序

本项目来自程序汪背后的私活小团队,开发了一个塔防射击小程序,给粉丝分享一下解决方案,希望给想接私活的朋友一些经验参考

文末有游戏版本源码

视频版本 在 B站【我是程序汪】

目录

  • 一、项目构成

  • 二、开发人员

  • 三、项目背景

  • 四、小程序端

  • 五、后端系统

项目构成

  • 开发工具:Cocos Creator

  • 开发语言:Java + JavaScript

  • 服务端框架:Spring Boot + MongoDB+Mysql

  • 通讯协议: WebSocket+http

  • 客户端语言:Cocos Creator/JavaScript

开发人员以及费用情况

  • 开发周期18天

  • 开发人数 2人 (美工1 前后端1)

  • 整体费用是3万

  • 走的正规合同

  • 服务器1台

  • Cocos游戏引擎

  • 维护费用:4000/年

项目背景

当下微信小程序游戏是比较火的,运营的好会带来很大的流量,后期靠广告收益就非常舒服了,原创开发游戏成本非常大,于是我们购买相应的游戏模板在上面进行二次开发就节省很多成本,开发速度也快了。本游戏一期接入广告,二期接入支付(需要版本号 很贵)

小程序端+后端服务

复制代码

基于你的需求(新增"巨龙身体含武器宝箱,击毁宝箱解锁对应武器"的核心机制),以下是更详细的开发方案,重点扩展了宝箱系统和武器解锁逻辑:

一、核心机制补充设计

  1. 巨龙与宝箱关系

    • 巨龙身体包含3-5个武器宝箱(身体各个部位),每个宝箱有独立生命值

    • 宝箱随巨龙同步移动,被子弹击中时减少自身生命值

    • 宝箱击毁后掉落对应武器(如散弹枪、激光炮、导弹等),同时可能触发巨龙短暂僵直

    • 巨龙本体有总生命值,宝箱击毁影响本体血量,本体血量归零时游戏胜利

  2. 武器系统扩展

    • 头部宝箱 → 散弹枪(范围伤害、中射速)

    • 左翼宝箱 → 激光炮(持续伤害、低射速)

    • 右翼宝箱 → 导弹(高伤害、带爆炸范围)

    • 初始武器:普通单发子弹(低伤害、高射速)

    • 宝箱对应武器:

    • 解锁后可通过按钮/快捷键切换武器,每种武器有独立弹药/冷却(可选)

二、新增核心模块实现

1. 武器配置类(WeaponConfig)

统一管理武器属性,便于扩展和维护:

复制代码
// WeaponConfig.ts
exportenum WeaponType {
    NORMAL = 0,   // 普通子弹
    SHOTGUN = 1,  // 散弹枪
    LASER = 2,    // 激光炮
    MISSILE = 3   // 导弹
}

exportclass WeaponData {
    type: WeaponType;
    name: string;
    damage: number;
    fireRate: number;  // 射击间隔(秒)
    bulletSpeed: number;
    bulletPrefab: string;  // 预制体路径
    icon: string;  // UI图标路径

    constructor(type: WeaponType, name: string, damage: number, fireRate: number, bulletSpeed: number, bulletPrefab: string, icon: string) {
        this.type = type;
        this.name = name;
        this.damage = damage;
        this.fireRate = fireRate;
        this.bulletSpeed = bulletSpeed;
        this.bulletPrefab = bulletPrefab;
        this.icon = icon;
    }
}

// 武器配置表
exportconst WeaponConfigs: {[key in WeaponType]: WeaponData} = {
    [WeaponType.NORMAL]: new WeaponData(
        WeaponType.NORMAL, "普通子弹", 10, 0.5, 1500, "Prefabs/BulletNormal", "Textures/IconNormal"
    ),
    [WeaponType.SHOTGUN]: new WeaponData(
        WeaponType.SHOTGUN, "散弹枪", 8, 1.2, 1200, "Prefabs/BulletShotgun", "Textures/IconShotgun"
    ),
    [WeaponType.LASER]: new WeaponData(
        WeaponType.LASER, "激光炮", 5, 0.1, 3000, "Prefabs/BulletLaser", "Textures/IconLaser"
    ),
    [WeaponType.MISSILE]: new WeaponData(
        WeaponType.MISSILE, "导弹", 50, 3, 800, "Prefabs/BulletMissile", "Textures/IconMissile"
    )
};
2. 宝箱控制器(TreasureBox)

管理单个宝箱的生命值、碰撞和武器掉落逻辑:

复制代码
// TreasureBox.ts
import { _decorator, Component, Node, Collider, ITriggerEvent, ProgressBar, find, Vec3 } from'cc';
import { WeaponType } from'./WeaponConfig';
import { PlayerController } from'./PlayerController';
import { GameManager } from'./GameManager';
const { ccclass, property } = _decorator;

@ccclass('TreasureBox')
exportclass TreasureBox extends Component {
    @property
    maxHP: number = 200;  // 宝箱生命值
    @property({ type: cc.Enum(WeaponType) })
    dropWeapon: WeaponType = WeaponType.SHOTGUN;  // 掉落的武器类型
    @property(Node) hpBarNode: Node = null;  // 宝箱血条节点

    private currentHP: number = 200;
    private isDestroyed: boolean = false;
    private hpBar: ProgressBar = null;

    onLoad() {
        this.currentHP = this.maxHP;
        this.hpBar = this.hpBarNode.getComponent(ProgressBar);
        this.hpBar.progress = 1;

        // 绑定碰撞事件
        const collider = this.getComponent(Collider);
        collider.on('onTriggerEnter', this.onTriggerEnter, this);
    }

    // 碰撞检测(被子弹击中)
    onTriggerEnter(event: ITriggerEvent) {
        if (this.isDestroyed) return;

        const bullet = event.otherCollider.node.getComponent(BulletController);
        if (bullet) {
            this.takeDamage(bullet.damage);
        }
    }

    // 受到伤害
    takeDamage(damage: number) {
        this.currentHP -= damage;
        this.hpBar.progress = this.currentHP / this.maxHP;

        if (this.currentHP <= 0) {
            this.destroyBox();
        }
    }

    // 宝箱击毁逻辑
    destroyBox() {
        this.isDestroyed = true;
        this.node.active = false;  // 隐藏宝箱
        this.hpBarNode.active = false;  // 隐藏血条

        // 掉落武器:通知主角解锁对应武器
        const player = find('Player').getComponent(PlayerController);
        player.unlockWeapon(this.dropWeapon);

        // 触发巨龙僵直(可选)
        this.node.parent.getComponent(DragonController).stun(1);  // 僵直1秒

        // 播放宝箱爆炸特效(略)
        GameManager.instance.playEffect("Explosion");
    }
}
3. 巨龙控制器扩展(DragonController)

新增宝箱管理和僵直逻辑:

复制代码
// DragonController.ts(扩展部分)
import { _decorator, Component, Node, Vec3, tween, Animation, ProgressBar, find } from'cc';
import { GameManager } from'./GameManager';
const { ccclass, property } = _decorator;

@ccclass('DragonController')
exportclass DragonController extends Component {
    @property(Node[]) treasureBoxes: Node[] = [];  // 所有宝箱子节点
    @property
    maxHP: number = 3000;  // 巨龙本体生命值
    @property
    moveSpeed: number = 80;  // 移动速度
    @property
    distanceToPlayer: number = 150;  // 失败判定距离

    private currentHP: number = 3000;
    private playerPos: Vec3 = Vec3.ZERO;
    private isMoving: boolean = false;
    private isStunned: boolean = false;  // 是否僵直

    onLoad() {
        this.currentHP = this.maxHP;
        this.playerPos = find('Player').worldPosition;
    }

    // 开始移动
    startMoving() {
        this.isMoving = true;
        this.moveLoop();
    }

    // 移动循环(新增僵直判断)
    private moveLoop() {
        if (!this.isMoving || this.isStunned) {
            // 若僵直,延迟后重新移动
            if (this.isStunned) {
                setTimeout(() => {
                    this.isStunned = false;
                    this.moveLoop();
                }, 1000);
            }
            return;
        }

        // 计算移动方向(向玩家)
        const direction = new Vec3(
            this.playerPos.x - this.node.worldPosition.x,
            this.playerPos.y - this.node.worldPosition.y,
            0
        ).normalize();

        // 移动一步(每0.1秒更新一次位置)
        const nextPos = new Vec3(
            this.node.worldPosition.x + direction.x * this.moveSpeed * 0.1,
            this.node.worldPosition.y + direction.y * this.moveSpeed * 0.1,
            0
        );

        tween(this.node)
            .to(0.1, { worldPosition: nextPos })
            .call(() => {
                // 检测是否到达玩家位置
                const distance = Vec3.distance(this.node.worldPosition, this.playerPos);
                if (distance <= this.distanceToPlayer) {
                    GameManager.instance.gameOver();
                    return;
                }
                this.moveLoop();
            })
            .start();
    }

    // 僵直效果(被宝箱击毁时触发)
    stun(duration: number) {
        this.isStunned = true;
        // 播放僵直动画(如停顿、抖动)
        this.getComponent(Animation).play('dragon_stun');
    }

    // 巨龙本体受伤害(被子弹直接击中时)
    takeDamage(damage: number) {
        this.currentHP -= damage;
        this.updateHPBar();

        if (this.currentHP <= 0) {
            this.die();
        }
    }

    // 其他方法(死亡、更新血条等)同上一版,略
}
4. 主角控制器扩展(PlayerController)

新增武器解锁和切换逻辑:

复制代码
// PlayerController.ts(扩展部分)
import { _decorator, Component, Node, Prefab, instantiate, Vec3, Input, input, KeyCode, find, Label } from'cc';
import { GameManager } from'./GameManager';
import { WeaponType, WeaponConfigs, WeaponData } from'./WeaponConfig';
const { ccclass, property } = _decorator;

@ccclass('PlayerController')
exportclass PlayerController extends Component {
    @property(Node) firePoint: Node = null;  // 发射点
    @property(Node) weaponUIParent: Node = null;  // 武器UI父节点(显示已解锁武器)

    private unlockedWeapons: Set<WeaponType> = new Set();  // 已解锁武器
    private currentWeapon: WeaponType = WeaponType.NORMAL;  // 当前使用武器
    private weaponPrefabs: Map<WeaponType, Prefab> = new Map();  // 武器预制体缓存
    private fireTimer: number = 0;  // 射击冷却计时器

    onLoad() {
        // 初始解锁普通武器
        this.unlockedWeapons.add(WeaponType.NORMAL);
        // 加载武器预制体(实际项目中建议用资源加载管理器)
        this.loadWeaponPrefabs();
        // 初始化武器UI
        this.updateWeaponUI();
        // 监听武器切换输入
        input.on(Input.EventType.KEY_DOWN, this.onKeyDown, this);
    }

    // 加载武器预制体
    async loadWeaponPrefabs() {
        for (consttypein WeaponConfigs) {
            const config = WeaponConfigs[typeas unknown as WeaponType];
            const prefab = await loader.loadRes(config.bulletPrefab, Prefab);  // 简化写法,实际需用Cocos资源加载API
            this.weaponPrefabs.set(config.type, prefab);
        }
    }

    // 解锁新武器
    unlockWeapon(type: WeaponType) {
        if (!this.unlockedWeapons.has(type)) {
            this.unlockedWeapons.add(type);
            this.updateWeaponUI();  // 更新UI显示新武器
            // 播放解锁提示音效
            GameManager.instance.playEffect("WeaponUnlock");
        }
    }

    // 更新武器UI(显示已解锁武器,可点击切换)
    updateWeaponUI() {
        // 清空现有UI
        this.weaponUIParent.removeAllChildren();
        // 为每个已解锁武器创建图标
        this.unlockedWeapons.forEach(type => {
            const config = WeaponConfigs[type];
            const iconNode = new Node(`WeaponIcon_${type}`);
            // 添加Sprite组件并设置图标(略)
            // 绑定点击事件:切换到该武器
            iconNode.on('click', () => {
                this.currentWeapon = type;
            }, this);
            this.weaponUIParent.addChild(iconNode);
        });
    }

    // 键盘切换武器(1-4对应不同武器)
    onKeyDown(event: EventKeyboard) {
        const keyMap = {
            [KeyCode.KEY_1]: WeaponType.NORMAL,
            [KeyCode.KEY_2]: WeaponType.SHOTGUN,
            [KeyCode.KEY_3]: WeaponType.LASER,
            [KeyCode.KEY_4]: WeaponType.MISSILE
        };
        const targetType = keyMap[event.keyCode];
        if (targetType !== undefined && this.unlockedWeapons.has(targetType)) {
            this.currentWeapon = targetType;
        }
    }

    // 射击逻辑(根据当前武器属性发射子弹)
    update(deltaTime: number) {
        if (!GameManager.instance.isGameStarted) return;

        const config = WeaponConfigs[this.currentWeapon];
        this.fireTimer += deltaTime;

        if (this.fireTimer >= config.fireRate) {
            this.shoot(config);
            this.fireTimer = 0;
        }
    }

    // 发射子弹(适配不同武器特性)
    shoot(config: WeaponData) {
        const prefab = this.weaponPrefabs.get(config.type);
        if (!prefab) return;

        // 普通子弹/导弹:单发
        if (config.type === WeaponType.NORMAL || config.type === WeaponType.MISSILE) {
            const bullet = instantiate(prefab);
            bullet.parent = find('Bullets');
            bullet.worldPosition = this.firePoint.worldPosition;
            bullet.getComponent(BulletController).init(
                this.getDragonDirection(),  // 朝向巨龙的方向
                config.damage,
                config.bulletSpeed,
                config.type  // 传递武器类型(用于特效区分)
            );
        }

        // 散弹枪:同时发射3颗子弹(扇形分布)
        elseif (config.type === WeaponType.SHOTGUN) {
            const baseDir = this.getDragonDirection();
            [-0.1, 0, 0.1].forEach(angle => {  // 角度偏移
                const dir = this.rotateDirection(baseDir, angle);
                const bullet = instantiate(prefab);
                // ... 同上设置子弹属性
            });
        }

        // 激光炮:持续光束(用粒子/线条渲染,略)
    }

    // 计算朝向巨龙的方向
    private getDragonDirection(): Vec3 {
        const dragonPos = find('Dragon').worldPosition;
        returnnew Vec3(
            dragonPos.x - this.firePoint.worldPosition.x,
            dragonPos.y - this.firePoint.worldPosition.y,
            0
        ).normalize();
    }

    // 旋转方向向量(用于散弹枪角度偏移)
    private rotateDirection(dir: Vec3, angle: number): Vec3 {
        const cos = Math.cos(angle);
        const sin = Math.sin(angle);
        returnnew Vec3(
            dir.x * cos - dir.y * sin,
            dir.x * sin + dir.y * cos,
            0
        ).normalize();
    }
}
5. 子弹控制器扩展(BulletController)

区分武器类型,处理不同碰撞逻辑(如导弹爆炸范围):

复制代码
// BulletController.ts(扩展部分)
import { _decorator, Component, Node, Vec3, tween, Collider, ITriggerEvent, find } from'cc';
import { WeaponType } from'./WeaponConfig';
import { DragonController } from'./DragonController';
import { TreasureBox } from'./TreasureBox';
const { ccclass, property } = _decorator;

@ccclass('BulletController')
exportclass BulletController extends Component {
    private direction: Vec3 = Vec3.ZERO;
    private damage: number = 0;
    private speed: number = 0;
    private weaponType: WeaponType = WeaponType.NORMAL;

    // 初始化子弹(新增武器类型参数)
    init(direction: Vec3, damage: number, speed: number, weaponType: WeaponType) {
        this.direction = direction;
        this.damage = damage;
        this.speed = speed;
        this.weaponType = weaponType;
        this.move();
    }

    // 移动逻辑(导弹有爆炸范围,不需要精准命中)
    private move() {
        if (this.weaponType === WeaponType.MISSILE) {
            // 导弹飞行一段时间后爆炸(无论是否命中)
            setTimeout(() => {
                this.explode();  // 触发爆炸范围伤害
                this.node.destroy();
            }, 2000);  // 2秒后自动爆炸
        } else {
            // 普通子弹/散弹:直线飞行,出界销毁
            const targetPos = new Vec3(
                this.node.position.x + this.direction.x * 10000,
                this.node.position.y + this.direction.y * 10000,
                0
            );
            tween(this.node)
                .to(10000 / this.speed, { position: targetPos })
                .call(() =>this.node.destroy())
                .start();
        }
    }

    // 导弹爆炸范围伤害
    private explode() {
        const dragon = find('Dragon');
        const distanceToDragon = Vec3.distance(this.node.worldPosition, dragon.worldPosition);
        if (distanceToDragon < 200) {  // 爆炸范围200
            dragon.getComponent(DragonController).takeDamage(this.damage);
        }

        // 同时检测是否命中宝箱
        dragon.children.forEach(child => {
            const box = child.getComponent(TreasureBox);
            if (box && !box.isDestroyed) {
                const distance = Vec3.distance(this.node.worldPosition, child.worldPosition);
                if (distance < 200) {
                    box.takeDamage(this.damage);
                }
            }
        });
    }

    // 碰撞检测(区分击中巨龙本体还是宝箱)
    onTriggerEnter(event: ITriggerEvent) {
        const otherNode = event.otherCollider.node;

        // 击中宝箱
        if (otherNode.getComponent(TreasureBox)) {
            const box = otherNode.getComponent(TreasureBox);
            if (!box.isDestroyed) {
                box.takeDamage(this.damage);
                // 普通子弹/散弹击中后销毁,激光持续存在(略)
                if (this.weaponType !== WeaponType.LASER) {
                    this.node.destroy();
                }
            }
        }

        // 击中巨龙本体(非宝箱部分)
        elseif (otherNode.name === 'Dragon' && !otherNode.getComponent(TreasureBox)) {
            otherNode.getComponent(DragonController).takeDamage(this.damage);
            if (this.weaponType !== WeaponType.LASER) {
                this.node.destroy();
            }
        }
    }
}

三、场景与资源配置

  1. 巨龙节点结构

    复制代码
    Dragon(父节点,挂DragonController)
    ├─ Body(巨龙身体,挂碰撞体,用于本体伤害检测)
    ├─ TreasureBox_Head(头部宝箱,挂TreasureBox组件,dropWeapon设为SHOTGUN)
    │  └─ HPBar(宝箱血条,初始隐藏,受击时显示)
    ├─ TreasureBox_LeftWing(左翼宝箱,dropWeapon设为LASER)
    └─ TreasureBox_RightWing(右翼宝箱,dropWeapon设为MISSILE)
  2. UI节点结构

    复制代码
    UI
    ├─ StartPanel(开始面板,含开始按钮)
    ├─ GameOverPanel(结束面板)
    ├─ VictoryPanel(胜利面板)
    ├─ DragonHPBar(巨龙本体血条)
    └─ WeaponPanel(武器面板)
       └─ WeaponIcons(武器图标父节点,动态生成已解锁武器图标)

四、微信小游戏适配要点

  1. 资源加载优化

    • 武器预制体和图标使用微信小游戏的 wx.loadSubpackage 分包加载,减少初始包体大小

    • 巨龙和宝箱的动画用序列帧而非骨骼动画,降低性能消耗

  2. 输入适配

    • 除键盘快捷键外,添加屏幕虚拟按钮(如武器切换按钮),适配移动端触摸操作
  3. 性能监控

    • 用微信开发者工具的"性能面板"监控DrawCall,确保同时存在的子弹/特效不超过30个

    • 非活跃的宝箱血条设置 active = false,减少渲染压力

五、扩展建议

  1. 增加武器升级系统:击毁多个同类型宝箱可升级对应武器(如提高伤害、降低冷却)

  2. 巨龙添加阶段性技能:当某个宝箱被击毁后,巨龙释放对应技能(如头部宝箱击毁后喷火)

  3. 限时道具:宝箱有概率掉落临时增益(如10秒无敌、双倍伤害)

通过以上实现,游戏将具备"击毁巨龙身体宝箱解锁武器→用新武器更高效攻击巨龙→最终消灭巨龙"的完整闭环,玩法更有层次感和策略性。

Cocos Creator

利用Cocos Creator开发小游戏,必须熟悉下它的数据通讯

Cocos Creator客户端与服务器数据通讯全流程

MongoDB

本游戏项目的游戏记录数据库采用的MongoDB,下面是部分DB数据的案例游戏项目对反应速度要求非常高,采用MongoDB就非常适合,用户信息排名信息这些用的Mysql

下面是游戏记录的部分json案例

复制代码
{
 "_id": "64a8e2f9c5b3a10012345678",
"playerId": "player_123456789",
"gameSessionId": "session_987654321",
"gameMode": "campaign",
"levelId": "forest_castle_01",
"difficulty": "hard",
"startTime": "2023-07-07T10:30:00Z",
"endTime": "2023-07-07T11:15:30Z",
"durationSeconds": 2730,
"result": "victory",
"score": 15420,
"resources": {
"startingGold": 1000,
"finalGold": 250,
"totalEarned": 2250,
"totalSpent": 2000
 },
"towers": [{
"type": "archer_tower",
"position": {
   "x": 120,
   "y": 80
  },
"upgrades": ["range_boost", "damage_plus"],
"sellTime": null
 }, {
"type": "cannon_tower",
"position": {
   "x": 200,
   "y": 150
  },
"upgrades": ["slow_effect"],
"sellTime": "2023-07-07T10:45:15Z"
 }],
"enemies": {
"totalSpawned": 120,
"totalKilled": 115,
"escaped": 5
 },
"waves": {
"totalWaves": 15,
"completedWaves": 15
 },
"statistics": {
"towersBuilt": 8,
"towersSold": 1,
"upgradesPurchased": 3,
"specialAbilitiesUsed": 2,
"restarts": 0
 },
"achievementsUnlocked": ["first_victory_hard", "no_escape_wave_5"],
"createdAt": "2023-07-07T11:15:30Z",
"updatedAt": "2023-07-07T11:15:30Z"
}

后端系统

后端采用springboot

核心的后端功能如下菜单

权限管理、插件管理、会员管理、关卡信息、游玩记录、任务列表

参考文档

参考文档:

  1. 微信小游戏官方

    https://developers.weixin.qq.com/minigame/dev/guide/game-engine/cocos-laya-egret.html

  2. cocos游戏引擎官方文档

    https://docs.cocos.com/creator/2.4/manual/zh/physics/collision/edit-collider-component.html

  3. Phaser 官方教程https://phaser.io/learn

  4. HTML5 游戏开发书籍:如《HTML5游戏开发实战》。

小程序塔防 游戏源码模板如下

资源分享

通过网盘分享的文件:dq.zip

链接: https://pan.baidu.com/s/1QDJY-OX-fTJoo9nWYztW2A

提取码: s64f

相关推荐
qq_124987075313 小时前
基于微信小程序的线下点餐系统的设计与实现(源码+论文+部署+安装)
spring boot·微信小程序·小程序·毕业设计
春卷同学13 小时前
足球游戏 - Electron for 鸿蒙PC项目实战案例
游戏·electron·harmonyos
码界奇点14 小时前
基于Python与Pygame的多功能游戏系统设计与实现
python·游戏·毕业设计·pygame·源代码管理
春卷同学15 小时前
篮球游戏 - Electron for 鸿蒙PC项目实战案例
游戏·electron·harmonyos
i橡皮擦16 小时前
使用gamedig 查询恐龙岛TheIsle游戏服务器
运维·服务器·游戏·steam·恐龙岛·the isle
春卷同学17 小时前
滑雪游戏 - Electron for 鸿蒙PC项目实战案例
游戏·electron·harmonyos
刻刻帝的海角18 小时前
创建一个简单的记忆翻牌游戏
游戏
兔儿资源19 小时前
游戏包站GM手游
游戏
少云清19 小时前
【功能测试】5_小程序项目 _抓包修改轮播图(重要)
小程序