From c5c01c6cf4978787ccda6b7e6b6fd6d6cc8697c2 Mon Sep 17 00:00:00 2001 From: walkpan Date: Fri, 31 Jan 2025 21:50:59 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BD=BF=E7=94=A8ecs=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/script/game/hero/Aheros.meta | 9 ++ assets/script/game/hero/Aheros/BaseComp.ts | 14 ++ .../script/game/hero/Aheros/BaseComp.ts.meta | 9 ++ assets/script/game/hero/Aheros/Module.ts | 34 ++++ assets/script/game/hero/Aheros/Module.ts.meta | 9 ++ assets/script/game/hero/Hero.ts | 70 +++----- assets/script/game/heros.meta | 9 ++ assets/script/game/heros/HeroComponent.ts | 50 ++++++ .../script/game/heros/HeroComponent.ts.meta | 9 ++ assets/script/game/heros/HeroSystem.ts | 22 +++ assets/script/game/heros/HeroTypeSystem.ts | 28 ++++ assets/script/game/heros/Heros.ts | 0 assets/script/game/heros/Heros.ts.meta | 9 ++ assets/script/game/skill/DOTSystem.ts | 27 ++++ .../script/game/skill/SkillCollisionSystem.ts | 19 +++ assets/script/game/skill/SkillComponent.ts | 20 +++ assets/script/game/skill/SkillEffectSystem.ts | 47 ++++++ settings/README.md | 150 ++++++++++++++++++ 18 files changed, 491 insertions(+), 44 deletions(-) create mode 100644 assets/script/game/hero/Aheros.meta create mode 100644 assets/script/game/hero/Aheros/BaseComp.ts create mode 100644 assets/script/game/hero/Aheros/BaseComp.ts.meta create mode 100644 assets/script/game/hero/Aheros/Module.ts create mode 100644 assets/script/game/hero/Aheros/Module.ts.meta create mode 100644 assets/script/game/heros.meta create mode 100644 assets/script/game/heros/HeroComponent.ts create mode 100644 assets/script/game/heros/HeroComponent.ts.meta create mode 100644 assets/script/game/heros/HeroSystem.ts create mode 100644 assets/script/game/heros/HeroTypeSystem.ts create mode 100644 assets/script/game/heros/Heros.ts create mode 100644 assets/script/game/heros/Heros.ts.meta create mode 100644 assets/script/game/skill/DOTSystem.ts create mode 100644 assets/script/game/skill/SkillCollisionSystem.ts create mode 100644 assets/script/game/skill/SkillComponent.ts create mode 100644 assets/script/game/skill/SkillEffectSystem.ts create mode 100644 settings/README.md diff --git a/assets/script/game/hero/Aheros.meta b/assets/script/game/hero/Aheros.meta new file mode 100644 index 00000000..cbb3eb36 --- /dev/null +++ b/assets/script/game/hero/Aheros.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "29b69aa0-d30f-47bf-adb0-1c761bf4bec8", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/game/hero/Aheros/BaseComp.ts b/assets/script/game/hero/Aheros/BaseComp.ts new file mode 100644 index 00000000..f18cc386 --- /dev/null +++ b/assets/script/game/hero/Aheros/BaseComp.ts @@ -0,0 +1,14 @@ +import { _decorator, Component, Node } from 'cc'; +const { ccclass, property } = _decorator; + +@ccclass('BaseComp') +export class BaseComp extends Component { + start() { + + } + + update(deltaTime: number) { + + } +} + diff --git a/assets/script/game/hero/Aheros/BaseComp.ts.meta b/assets/script/game/hero/Aheros/BaseComp.ts.meta new file mode 100644 index 00000000..87187afb --- /dev/null +++ b/assets/script/game/hero/Aheros/BaseComp.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "0a844c8e-978a-4c93-99ed-f4e6a7d53286", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/game/hero/Aheros/Module.ts b/assets/script/game/hero/Aheros/Module.ts new file mode 100644 index 00000000..e1a27c0b --- /dev/null +++ b/assets/script/game/hero/Aheros/Module.ts @@ -0,0 +1,34 @@ +import { ecs } from "../../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; + +/** Module 模块 */ +@ecs.register(`Module`) +export class Module extends ecs.Entity { + /** ---------- 数据层 ---------- */ + // ModuleModel!: ModuleModelComp; + + /** ---------- 业务层 ---------- */ + // ModuleBll!: ModuleBllComp; + + /** ---------- 视图层 ---------- */ + // ModuleView!: ModuleViewComp; + + /** 实始添加的数据层组件 */ + protected init() { + // this.addComponents(); + } + + /** 模块资源释放 */ + destroy() { + // 注: 自定义释放逻辑,视图层实现 ecs.IComp 接口的 ecs 组件需要手动释放 + super.destroy(); + } +} + +/** Module 模块业务逻辑系统组件,如无业务逻辑处理可删除此对象 */ +export class EcsModuleSystem extends ecs.System { + constructor() { + super(); + + // this.add(new ecs.ComblockSystem()); + } +} diff --git a/assets/script/game/hero/Aheros/Module.ts.meta b/assets/script/game/hero/Aheros/Module.ts.meta new file mode 100644 index 00000000..72b3549d --- /dev/null +++ b/assets/script/game/hero/Aheros/Module.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "013c01e2-3700-446a-824e-074584d7787b", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/game/hero/Hero.ts b/assets/script/game/hero/Hero.ts index 08785b17..4ac65328 100644 --- a/assets/script/game/hero/Hero.ts +++ b/assets/script/game/hero/Hero.ts @@ -21,19 +21,14 @@ export class Hero extends ecs.Entity { HeroModel!: HeroModelComp; // 视图层 HeroView!: HeroViewComp; - - protected init() { - } - destroy(): void { this.remove(HeroViewComp); this.remove(MoveToComp); super.destroy(); } - /** 加载角色 */ load(pos: Vec3 = Vec3.ZERO,scale:number = 1,uuid:number=1001,is_call:boolean=false,lv:number=1) { scale = 1 @@ -50,7 +45,6 @@ export class Hero extends ecs.Entity { this.hero_init(uuid,node,scale,box_group,is_call,lv) oops.message.dispatchEvent("hero_load",this) } - hero_init(uuid:number=1001,node:Node,scale:number=1,box_group=BoxSet.HERO,is_call:boolean=false,lv:number=1){ var hv = node.getComponent(HeroViewComp)!; // console.log("hero_init",buff) @@ -81,50 +75,38 @@ export class Hero extends ecs.Entity { let sklv=slv if(sklv >= 5) sklv=5; - hv.sk1 = hero.sk1[sklv]; - hv.sk2 = hero.sk2[sklv]; - hv.sk3 = hero.sk3[sklv]; - hv.akc = hero.akc[sklv]; - hv.uac = hero.uac[sklv]; - hv.crc = hero.crc[sklv]; - hv.dgc = hero.dgc[sklv]; - hv.akr = hero.akr[sklv]; - hv.uar = hero.uar[sklv]; - hv.crr = hero.crr[sklv]; - hv.dgr = hero.dgr[sklv]; - hv.rhp_max=hv.hp= hv.hp_max =(hero.hp+hero.hp_up*hv.lv)*(1+hero.shp_up/100*slv) ; + hv.sk1 = hero.sk1[sklv] + hv.sk2 = hero.sk2[sklv] + hv.sk3 = hero.sk3[sklv] + hv.akc = hero.akc[sklv] + hv.uac = hero.uac[sklv] + hv.crc = hero.crc[sklv] + hv.dgc = hero.dgc[sklv] + hv.akr = hero.akr[sklv] + hv.uar = hero.uar[sklv] + hv.crr = hero.crr[sklv] + hv.dgr = hero.dgr[sklv] + hv.rhp_max=hv.hp= hv.hp_max =(hero.hp+hero.hp_up*hv.lv)*(1+hero.shp_up/100*slv) hv.ap = (hero.ap+hero.ap_up*hv.lv) *(1+hero.sap_up/100*slv); hv.def= (hero.def+hero.def_up*hv.lv)*(1+hero.sdef_up/100*slv); hv.cd = hero.a_cd - hv.crit = hero.crit; //暴击率 + hv.crit = hero.crit //暴击率 hv.crit_add = hero.crit_add;//暴击伤害加成 - hv.dodge = hero.dodge; //闪避率 - hv.aexp=hero.aexp; - hv.uaexp=hero.uaexp; + hv.dodge = hero.dodge //闪避率 + hv.aexp=hero.aexp + hv.uaexp=hero.uaexp hv.cexp=hero.cexp hv.doexp=hero.doexp - hv.dexp=hero.dexp; - + hv.dexp=hero.dexp this.add(hv); } - set_ratio(uuid:number){ - let ratio=1; - switch (HeroInfo[uuid].level) { - case 2: - ratio=1.05 - break; - case 3: - ratio=1.1 - break; - case 4: - ratio=1.15 - break; - case 5: - ratio=1.2 - break; - default: - ratio=1 - } - return ratio; - } + +} + +/** Module 模块业务逻辑系统组件,如无业务逻辑处理可删除此对象 */ +export class EcsModuleSystem extends ecs.System { + constructor() { + super(); + // this.add(new ecs.ComblockSystem()); + } } \ No newline at end of file diff --git a/assets/script/game/heros.meta b/assets/script/game/heros.meta new file mode 100644 index 00000000..87ac5875 --- /dev/null +++ b/assets/script/game/heros.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "7419c29e-7ead-48a1-bef4-06c52b520491", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/game/heros/HeroComponent.ts b/assets/script/game/heros/HeroComponent.ts new file mode 100644 index 00000000..b0ee83a2 --- /dev/null +++ b/assets/script/game/heros/HeroComponent.ts @@ -0,0 +1,50 @@ +import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; + +import { _decorator, Node } from 'cc'; +const { ccclass } = _decorator; + +// 继承框架的ECComponent + +// 基础属性组件 +@ecs.register('HeroBase') +export class HeroBase extends ecs.Comp { + // 定义需要序列化的字段 + static serializeFields = ['hp', 'attack', 'node']; + + hp: number = 100; + attack: number = 10; + node: Node = null; + + reset() { + this.hp = 100; + this.attack = 10; + this.node = null; + } +} + +// 技能组件 +@ecs.register('HeroSkill') +export class HeroSkill extends ecs.Comp { + static serializeFields = ['skillId']; + + skillId: string = ""; + cooldown: number = 0; + + reset() { + this.skillId = ""; + this.cooldown = 0; + } +} + +// 状态组件 +@ecs.register('HeroState') +export class HeroState extends ecs.Comp { + current: 'idle' | 'attack' | 'die' = 'idle'; + previous: 'idle' | 'attack' | 'die' = 'idle'; + + reset() { + this.current = 'idle'; + this.previous = 'idle'; + } +} + diff --git a/assets/script/game/heros/HeroComponent.ts.meta b/assets/script/game/heros/HeroComponent.ts.meta new file mode 100644 index 00000000..ac622877 --- /dev/null +++ b/assets/script/game/heros/HeroComponent.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "dbeb1c15-5c41-49bd-b633-728335443a38", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/game/heros/HeroSystem.ts b/assets/script/game/heros/HeroSystem.ts new file mode 100644 index 00000000..20166527 --- /dev/null +++ b/assets/script/game/heros/HeroSystem.ts @@ -0,0 +1,22 @@ +@ecs.registerSystem() +export class HeroMoveSystem extends ecs.System { + filter(): ecs.IMatcher { + return ecs.allOf(HeroModelComp, HeroViewComp, MoveToComp); + } + + update(entities: Hero[]) { + const deltaTime = oops.timer.delta; + entities.forEach(hero => { + // 每个英雄独立计算移动 + const move = hero.get(MoveToComp); + const view = hero.get(HeroViewComp); + + // 计算移动向量(单个英雄逻辑) + const dir = move.target.subtract(view.node.position).normalize(); + const speed = view.speed * deltaTime; + + // 更新位置(独立操作) + view.node.position = view.node.position.add(dir.multiplyScalar(speed)); + }); + } +} \ No newline at end of file diff --git a/assets/script/game/heros/HeroTypeSystem.ts b/assets/script/game/heros/HeroTypeSystem.ts new file mode 100644 index 00000000..174a9c1e --- /dev/null +++ b/assets/script/game/heros/HeroTypeSystem.ts @@ -0,0 +1,28 @@ +@ecs.registerSystem() +export class HeroTypeSystem extends ecs.System { + filter() { + return ecs.allOf(HeroTypeComp); + } + + update(entities: Hero[]) { + entities.forEach(hero => { + const type = hero.get(HeroTypeComp).type; + switch(type) { + case HeroType.MELEE: + this.processMelee(hero); + break; + case HeroType.RANGED: + this.processRanged(hero); + break; + } + }); + } + + private processMelee(hero: Hero) { + // 近战英雄特有逻辑 + } + + private processRanged(hero: Hero) { + // 远程英雄特有逻辑 + } +} \ No newline at end of file diff --git a/assets/script/game/heros/Heros.ts b/assets/script/game/heros/Heros.ts new file mode 100644 index 00000000..e69de29b diff --git a/assets/script/game/heros/Heros.ts.meta b/assets/script/game/heros/Heros.ts.meta new file mode 100644 index 00000000..c5fe37de --- /dev/null +++ b/assets/script/game/heros/Heros.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "3d62e7a3-a90b-47f3-bf88-85745af0b8b4", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/game/skill/DOTSystem.ts b/assets/script/game/skill/DOTSystem.ts new file mode 100644 index 00000000..b5ee4083 --- /dev/null +++ b/assets/script/game/skill/DOTSystem.ts @@ -0,0 +1,27 @@ +@ecs.registerSystem() +export class DOTSystem extends ecs.System { + filter(): ecs.IMatcher { + return ecs.allOf(DOTComp); + } + + update(entities: ecs.Entity[]) { + const delta = oops.timer.delta; + + entities.forEach(hero => { + const dot = hero.get(DOTComp); + dot.duration -= delta; + + // 每秒伤害 + if (dot.accumulator >= 1) { + hero.get(HeroModelComp).hp -= dot.damagePerSec; + dot.accumulator -= 1; + } + dot.accumulator += delta; + + // 效果结束 + if (dot.duration <= 0) { + hero.remove(DOTComp); + } + }); + } +} \ No newline at end of file diff --git a/assets/script/game/skill/SkillCollisionSystem.ts b/assets/script/game/skill/SkillCollisionSystem.ts new file mode 100644 index 00000000..b21d1ae4 --- /dev/null +++ b/assets/script/game/skill/SkillCollisionSystem.ts @@ -0,0 +1,19 @@ +@ecs.registerSystem() +export class SkillCollisionSystem extends ecs.System { + filter(): ecs.IMatcher { + return ecs.allOf(SkillEffectComp, ColliderComp); + } + + update(skills: ecs.Entity[]) { + skills.forEach(skill => { + const collider = skill.get(ColliderComp); + const collisionResult = skill.get(CollisionResultComp) || skill.add(CollisionResultComp); + + // 检测碰撞(伪代码,实际使用物理引擎检测) + const hitHeroes = PhysicsSystem.overlap(collider.bounds, 'Hero'); + + // 转换为ECS实体 + collisionResult.targets = hitHeroes.map(h => h.entity); + }); + } +} \ No newline at end of file diff --git a/assets/script/game/skill/SkillComponent.ts b/assets/script/game/skill/SkillComponent.ts new file mode 100644 index 00000000..5cd599f9 --- /dev/null +++ b/assets/script/game/skill/SkillComponent.ts @@ -0,0 +1,20 @@ +// 技能效果组件 +@ecs.register('SkillEffect') +export class SkillEffectComp extends ecs.Comp { + damage: number = 0; // 基础伤害 + effectType: 'instant' | 'dot' = 'instant'; // 效果类型 + duration: number = 0; // 持续时间(DOT用) + + reset() { + this.damage = 0; + this.effectType = 'instant'; + this.duration = 0; + } +} + +// 碰撞结果组件 +@ecs.register('CollisionResult') +export class CollisionResultComp extends ecs.Comp { + targets: ecs.Entity[] = []; // 碰撞到的英雄实体 + reset() { this.targets = []; } +} \ No newline at end of file diff --git a/assets/script/game/skill/SkillEffectSystem.ts b/assets/script/game/skill/SkillEffectSystem.ts new file mode 100644 index 00000000..1b01b74a --- /dev/null +++ b/assets/script/game/skill/SkillEffectSystem.ts @@ -0,0 +1,47 @@ +@ecs.registerSystem() +export class SkillEffectSystem extends ecs.System { + filter(): ecs.IMatcher { + return ecs.allOf(SkillEffectComp, CollisionResultComp); + } + + update(skills: ecs.Entity[]) { + skills.forEach(skill => { + const effect = skill.get(SkillEffectComp); + const targets = skill.get(CollisionResultComp).targets; + + targets.forEach(hero => { + // 应用即时伤害 + if (effect.effectType === 'instant') { + this.applyInstantDamage(hero, effect.damage); + } + // 应用持续效果 + else if (effect.effectType === 'dot') { + this.applyDOT(hero, effect); + } + }); + + // 清除碰撞结果 + skill.remove(CollisionResultComp); + }); + } + + private applyInstantDamage(hero: ecs.Entity, damage: number) { + const model = hero.get(HeroModelComp); + model.hp -= damage; + + // 触发受击事件 + oops.message.dispatch('HeroDamaged', { + hero, + damage, + currentHp: model.hp + }); + } + + private applyDOT(hero: ecs.Entity, effect: SkillEffectComp) { + // 添加持续伤害组件 + hero.add(DOTComp, { + damagePerSec: effect.damage, + duration: effect.duration + }); + } +} \ No newline at end of file diff --git a/settings/README.md b/settings/README.md new file mode 100644 index 00000000..b3c55b85 --- /dev/null +++ b/settings/README.md @@ -0,0 +1,150 @@ +# 基于马斯洛需求理论的IAA小游戏设计指南 +## 设计理论映射 +### 需求层次对应游戏机制 +1. **生理需求** → 即时反馈机制(基础操作快感) +2. **安全需求** → 稳定预期系统(进度保存/每日奖励) +3. **社交需求** → 轻量化社交功能(排行榜/分享) +4. **尊重需求** → 成就认可体系(徽章/称号) +5. **自我实现** → 成长系统(角色培养/技能树) +![马斯洛需求层次与游戏机制对应关系示意图] +## 分层实现策略 +### 1. 生理需求层 - 即时反馈系统 +```typescript +/** 短平快爽感循环设计 */ +class GameCore { + // 每10秒设置一个小高潮点 + createQuickRewardCycle() { + setInterval(() => { + this.spawnPowerUp(); // 生成强化道具 + this.playJuicyEffect(); // 播放炸裂特效 + this.vibrateDevice(200); // 设备震动反馈 + }, 10000); + } +} +``` +### 2. 安全需求层 - 稳定预期系统 +```typescript +/** 七日循环奖励机制 */ +class DailyReward { + private streakDays: number = 0; + + checkLogin() { + if (!this.isClaimedToday()) { + this.streakDays = (this.streakDays + 1) % 7; + this.giveReward(this.streakDays); + + // 第七日大奖设计 + if (this.streakDays === 0) { + this.giveSpecialPrize(); + this.showFireworksAnimation(); + } + } + } +} +``` +### 3. 社交需求层 - 异步互动系统 +```typescript +/** 动态排行榜实现 */ +class SocialSystem { + updateLeaderboard(score: number) { + // 三轴对比系统 + const comparisonData = { + top10: this.getTopPlayers(10), + nearby: this.getNearestPlayers(score), + friends: this.getFriendScores() + }; + + this.displayLeaderboard(comparisonData); + + // 破纪录特效 + if (score > this.bestScore) { + this.playRecordBreakEffect(); + this.saveLocalRecord(score); + } + } +} +``` +### 4. 尊重需求层 - 成就系统 +```typescript +/** 阶梯式成就解锁 */ +class AchievementSystem { + unlockAchievement(id: string) { + if (!this.isUnlocked(id)) { + // 成就解锁三要素 + this.showUnlockAnimation(id); + this.triggerSocialNotification(); + this.awardExclusiveSkin(id); + } + } + + /** 进度追踪设计 */ + trackProgress(metric: string, target: number) { + const progress = this.getProgress(metric); + this.showProgressBar(metric, progress, target); + } +} +``` +### 5. 自我实现层 - 成长系统 +```typescript +/** 非线性成长曲线 */ +class ProgressionSystem { + private xp: number = 0; + + /** 经验值增益算法 */ + addExperience(value: number) { + const multiplier = 1 + this.getPrestigeLevel() * 0.2; + this.xp += value * multiplier; + + // 动态难度调整 + const needXP = 100 * Math.pow(1.5, this.level) * this.difficultyFactor; + if (this.xp >= needXP) { + this.levelUp(); + this.unlockBranchingAbility(); + } + } +} +``` +## 增强粘性设计 +### 可变奖励机制 +```typescript +/** 多级奖励池设计 */ +class LootSystem { + generateReward() { + const rewardTiers = [ + { pool: ["coin", "gem"], weight: 80 }, + { pool: ["energy", "ticket"], weight: 15 }, + { pool: ["legendarySkin"], weight: 5 } + ]; + + return this.selectFromWeightedPool(rewardTiers); + } +} +``` +### 峰终体验设计 +```typescript +/** 单局体验优化 */ +class RoundManager { + endGameSession() { + // 终局三要素 + this.playHighlightReplay(); + this.showProgressComparison(); + this.presentContinueOptions(); + + // 广告接入点 + if (this.checkAdOpportunity()) { + this.showRewardedAdButton(); + } + } +} +``` +## 关键平衡要素 +1. **时长控制**:单局时长 ≤3分钟(通过动态难度调整实现) +2. **广告融合**:在成就时刻(新纪录/升级时)提供奖励视频选项 +3. **内容更新**:每次版本更新保留10%新内容供探索 +4. **难度曲线**:波浪形难度设计(紧张→放松→紧张的循环) +## 系统协同效应 +- 🎮 **即时反馈** → 满足本能快感(生理层) +- 📅 **每日目标** → 建立稳定预期(安全层) +- 🏆 **社交对比** → 激发竞争欲望(社交/尊重层) +- 🌱 **成长体系** → 实现自我价值(自我实现层) +最终形成「高频短时+长线目标」的双层吸引力模型,通过神经可塑性原理培养玩家习惯,提升长期留存。