本项目来自程序汪背后的私活小团队,开发了一个塔防射击小程序,给粉丝分享一下解决方案,希望给想接私活的朋友一些经验参考
文末有游戏版本源码
视频版本 在 B站【我是程序汪】


目录
-
一、项目构成
-
二、开发人员
-
三、项目背景
-
四、小程序端
-
五、后端系统
项目构成
-
开发工具:Cocos Creator
-
开发语言:Java + JavaScript
-
服务端框架:Spring Boot + MongoDB+Mysql
-
通讯协议: WebSocket+http
-
客户端语言:Cocos Creator/JavaScript
开发人员以及费用情况
-
开发周期18天
-
开发人数 2人 (美工1 前后端1)
-
整体费用是3万
-
走的正规合同
-
服务器1台
-
Cocos游戏引擎
-
维护费用:4000/年
项目背景
当下微信小程序游戏是比较火的,运营的好会带来很大的流量,后期靠广告收益就非常舒服了,原创开发游戏成本非常大,于是我们购买相应的游戏模板在上面进行二次开发就节省很多成本,开发速度也快了。本游戏一期接入广告,二期接入支付(需要版本号 很贵)
小程序端+后端服务






基于你的需求(新增"巨龙身体含武器宝箱,击毁宝箱解锁对应武器"的核心机制),以下是更详细的开发方案,重点扩展了宝箱系统和武器解锁逻辑:
一、核心机制补充设计
-
巨龙与宝箱关系
-
巨龙身体包含3-5个武器宝箱(身体各个部位),每个宝箱有独立生命值
-
宝箱随巨龙同步移动,被子弹击中时减少自身生命值
-
宝箱击毁后掉落对应武器(如散弹枪、激光炮、导弹等),同时可能触发巨龙短暂僵直
-
巨龙本体有总生命值,宝箱击毁影响本体血量,本体血量归零时游戏胜利
-
-
武器系统扩展
-
头部宝箱 → 散弹枪(范围伤害、中射速)
-
左翼宝箱 → 激光炮(持续伤害、低射速)
-
右翼宝箱 → 导弹(高伤害、带爆炸范围)
-
初始武器:普通单发子弹(低伤害、高射速)
-
宝箱对应武器:
-
解锁后可通过按钮/快捷键切换武器,每种武器有独立弹药/冷却(可选)
-
二、新增核心模块实现
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();
}
}
}
}
三、场景与资源配置
-
巨龙节点结构
Dragon(父节点,挂DragonController) ├─ Body(巨龙身体,挂碰撞体,用于本体伤害检测) ├─ TreasureBox_Head(头部宝箱,挂TreasureBox组件,dropWeapon设为SHOTGUN) │ └─ HPBar(宝箱血条,初始隐藏,受击时显示) ├─ TreasureBox_LeftWing(左翼宝箱,dropWeapon设为LASER) └─ TreasureBox_RightWing(右翼宝箱,dropWeapon设为MISSILE) -
UI节点结构
UI ├─ StartPanel(开始面板,含开始按钮) ├─ GameOverPanel(结束面板) ├─ VictoryPanel(胜利面板) ├─ DragonHPBar(巨龙本体血条) └─ WeaponPanel(武器面板) └─ WeaponIcons(武器图标父节点,动态生成已解锁武器图标)
四、微信小游戏适配要点
-
资源加载优化
-
武器预制体和图标使用微信小游戏的
wx.loadSubpackage分包加载,减少初始包体大小 -
巨龙和宝箱的动画用序列帧而非骨骼动画,降低性能消耗
-
-
输入适配
- 除键盘快捷键外,添加屏幕虚拟按钮(如武器切换按钮),适配移动端触摸操作
-
性能监控
-
用微信开发者工具的"性能面板"监控DrawCall,确保同时存在的子弹/特效不超过30个
-
非活跃的宝箱血条设置
active = false,减少渲染压力
-
五、扩展建议
-
增加武器升级系统:击毁多个同类型宝箱可升级对应武器(如提高伤害、降低冷却)
-
巨龙添加阶段性技能:当某个宝箱被击毁后,巨龙释放对应技能(如头部宝箱击毁后喷火)
-
限时道具:宝箱有概率掉落临时增益(如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
核心的后端功能如下菜单
权限管理、插件管理、会员管理、关卡信息、游玩记录、任务列表




参考文档

参考文档:
微信小游戏官方
https://developers.weixin.qq.com/minigame/dev/guide/game-engine/cocos-laya-egret.html
cocos游戏引擎官方文档
https://docs.cocos.com/creator/2.4/manual/zh/physics/collision/edit-collider-component.html
Phaser 官方教程:https://phaser.io/learn
HTML5 游戏开发书籍:如《HTML5游戏开发实战》。
小程序塔防 游戏源码模板如下

资源分享
通过网盘分享的文件:dq.zip
链接: https://pan.baidu.com/s/1QDJY-OX-fTJoo9nWYztW2A
提取码: s64f