以游戏玩家的视角开启设计模式(一)

以游戏玩家的视角开启设计模式(一)

首页模拟经营网红明星经纪人模拟更新时间:2024-10-06
前言

最近学习设计模式和TypeScript,发现网上的资料略显硬核,不太容易理解记忆,经常看完就忘。作为一名游戏玩家,发现游戏中的很多场景都能和相应的设计模式相关联,不仅便于理解,更利于合理地运用设计模式。由于个人水平有限,只整理个人觉得比较有趣的设计模式,每个模式采用**哲学三问**进行讲解。如果对你有帮助的话,欢迎点赞和收藏,图片源自网络,侵删。

目录

设计模式

单例模式

What

Game:在DNF一起组团刷本的日子中,副本BOSS看作为一个单例,玩家可以通过各种技能或者平A去消耗BOSS

并且该本中的所有玩家都是对同一个BOSS造成伤害

How

TS版本

class Boss{ private static instance: Boss = null; private hp: number = 1000; getInjured(harm: number){ this.hp -= harm; } getHp(): number{ return this.hp; } static getInstance(): Boss{ // 如果已经实例化了那么直接返回 if(!this.instance){ this.instance = new Boss(); } return this.instance; } } class Player{ constructor(private name: string){} attack(harm: number,boss: Boss): void{ boss.getInjured(harm); console.log(`我是一名${this.name},打出了${harm}伤害,BOSS还剩${boss.getHp()}血`) } } let p1: Player = new Player('鬼泣'); let p2: Player = new Player('街霸'); let p3: Player = new Player('阿修罗'); // 对同一个boss疯狂输出 p1.attack(100,Boss.getInstance()); p2.attack(80,Boss.getInstance()); p3.attack(120,Boss.getInstance()); // 我是一名鬼泣,打出了100伤害,BOSS还剩900血 // 我是一名街霸,打出了80伤害,BOSS还剩820血 // 我是一名阿修罗,打出了120伤害,BOSS还剩700血

JS版本

var Boss = /** @class */ (function () { function Boss() { this.hp = 1000; } Boss.prototype.getInjured = function (harm) { this.hp -= harm; }; Boss.prototype.getHp = function () { return this.hp; }; Boss.getInstance = function () { // 如果已经实例化了那么直接返回 if (!this.instance) { this.instance = new Boss(); } return this.instance; }; Boss.instance = null; return Boss; }()); var Player = /** @class */ (function () { function Player(name) { this.name = name; } Player.prototype.attack = function (harm, boss) { boss.getInjured(harm); console.log("我是一名" this.name ",打出了" harm "伤害,BOSS还剩" boss.getHp() "血"); }; return Player; }()); var p1 = new Player('鬼泣'); var p2 = new Player('街霸'); var p3 = new Player('阿修罗'); // 对同一个boss疯狂输出 p1.attack(100, Boss.getInstance()); p2.attack(80, Boss.getInstance()); p3.attack(120, Boss.getInstance()); // 我是一名鬼泣,打出了100伤害,BOSS还剩900血 // 我是一名街霸,打出了80伤害,BOSS还剩820血 // 我是一名阿修罗,打出了120伤害,BOSS还剩700血

Why

优点

缺点

应用场景:针对一些静态的共享资源,可以采用单例模式对其进行访问。从此处可以看出举的游戏例子本身不太适合用单例模式(因为它是一个频繁变动的对象)

策略模式

What

Game:在炉石传说游戏中,盗贼的两套经典卡牌奇迹贼爆牌贼,虽然都是盗贼玩家取胜的法宝,但这两套牌的取胜之道却截然不同。奇迹贼依靠一回合的极限操作往往能转危为安,让对面突然去世;而爆牌贼则是以疲劳和爆对面key牌的运营方式取胜。

How

TS版本

interface Deck{ name: string; kill(): void; } class MiracleDeck implements Deck{ name: string = '奇迹贼'; kill(){ console.log(`${this.name}:十七张牌就是能秒你`); } } class ExplosiveDeck implements Deck{ name: string = '爆牌贼'; kill(){ console.log(`${this.name}:我要爆光你的牌库!`) } } class Robber{ private deck: Deck; setDeck(deck: Deck){ this.deck = deck; } winTheGame(): void{ this.deck.kill(); }; } let rb = new Robber(); rb.setDeck(new MiracleDeck()); rb.winTheGame(); rb.setDeck(new ExplosiveDeck()); rb.winTheGame(); // 奇迹贼:十七张牌就是能秒你 // 爆牌贼:我要爆光你的牌库!

JS版本

var MiracleDeck = /** @class */ (function () { function MiracleDeck() { this.name = '奇迹贼'; } MiracleDeck.prototype.kill = function () { console.log(this.name "十七张牌就是能秒你"); }; return MiracleDeck; }()); var ExplosiveDeck = /** @class */ (function () { function ExplosiveDeck() { this.name = '爆牌贼'; } ExplosiveDeck.prototype.kill = function () { console.log(this.name "我要爆光你的牌库!"); }; return ExplosiveDeck; }()); var Robber = /** @class */ (function () { function Robber() { } Robber.prototype.setDeck = function (deck) { this.deck = deck; }; Robber.prototype.winTheGame = function () { this.deck.kill(); }; ; return Robber; }()); var rb = new Robber(); rb.setDeck(new MiracleDeck()); rb.winTheGame(); rb.setDeck(new ExplosiveDeck()); rb.winTheGame(); // 奇迹贼:十七张牌就是能秒你 // 爆牌贼:我要爆光你的牌库!

Why

优点

算法可以自由切换,可扩展性好

缺点

策略类会增多并且每个策略类都需要向外暴露

应用场景:多态场景,如分享功能,分享到不同平台的内部实现是不相同的

代理模式

What

定义:为一个对象提供一个代理者,以便控制对它的访问

解释:比如明星和经纪人的关系,经纪人会帮助明星处理商演赞助等细节问题,明星负责签字就好

Game:作为一个`FM`懒人玩家,只想把时间花在看模拟比赛上,其他不想做的事就很开心地甩给助理教练啦

How

TS版本

interface Match{ play(): void; } class Manager implements Match{ play(): void{ console.log('比赛开始了,是由皇家马德里对阵拜仁慕尼黑。。。') } } class Cotch implements Match{ private manager: Manager = new Manager(); beforePlay(): void{ console.log('布置战术'); console.log('球队训话'); } afterPlay(): void{ console.log('赛后采访'); } play(): void{ this.beforePlay(); this.manager.play(); this.afterPlay(); } } let proxy: Cotch = new Cotch(); proxy.play(); // 布置战术 // 球队训话 // 比赛开始了,是由皇家马德里对阵拜仁慕尼黑。。。 // 赛后采访

JS版本

var Manager = /** @class */ (function () { function Manager() { } Manager.prototype.play = function () { console.log('比赛开始了,是由皇家马德里对阵拜仁慕尼黑。。。'); }; return Manager; }()); var Cotch = /** @class */ (function () { function Cotch() { this.manager = new Manager(); } Cotch.prototype.beforePlay = function () { console.log('布置战术'); console.log('球队训话'); }; Cotch.prototype.afterPlay = function () { console.log('赛后采访'); }; Cotch.prototype.play = function () { this.beforePlay(); this.manager.play(); this.afterPlay(); }; return Cotch; }()); var proxy = new Cotch(); proxy.play(); // 布置战术 // 球队训话 // 比赛开始了,是由皇家马德里对阵拜仁慕尼黑。。。 // 赛后采访

Why

优点

缺点

发布-订阅模式

What

定义:对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知

解释:比如JS中的addEventListener

Game:进游戏后你会订阅队友和敌人的生存状态,当生存状态发生变化时,系统会及时通知你

How

TS版本

class EventEmitter{ private events: Object = {}; // 存储事件 private key: number = 0; // 事件的唯一标识key on(name: string,event: any): number{ event.key = this.key; this.events[name] ? this.events[name].push(event) : (this.events[name] = []) && this.events[name].push(event); return this.key; } off(name: string,key: number){ if(this.events[name]){ this.events[name] = this.events[name].filter(x => x.key !== key); }else{ this.events[name] = []; } } emit(name: string,key?: number){ if(this.events[name].length === 0 ) throw Error(`抱歉,你没有定义 ${name}监听器`) if(key){ this.events[name].forEach(x => x.key === key && x()); }else { this.events[name].forEach(x => x()); } } } let player: EventEmitter = new EventEmitter(); player.on('friendDied',function(): void{ console.log('你可爱的队友已被击*'); }) player.on('enemyDied',function(): void{ console.log('打的好呀,小帅哥') }) // 模拟战况 let rand: number; let k: number = 1; while(k < 10){ rand = Math.floor(Math.random() * 10); if(rand % 2 === 0){ player.emit('friendDied'); }else{ player.emit('enemyDied'); } k ; } // 你可爱的队友已被击* // 打的好呀,小帅哥 // 你可爱的队友已被击* // 你可爱的队友已被击* // 打的好呀,小帅哥 // 你可爱的队友已被击* // 你可爱的队友已被击* // 打的好呀,小帅哥 // 打的好呀,小帅哥

JS版本

var EventEmitter = /** @class */ (function () { function EventEmitter() { this.events = {}; // 存储事件 this.key = 0; // 事件的唯一标识key } EventEmitter.prototype.on = function (name, event) { event.key = this.key; this.events[name] ? this.events[name].push(event) : (this.events[name] = []) && this.events[name].push(event); return this.key; }; EventEmitter.prototype.off = function (name, key) { if (this.events[name]) { this.events[name] = this.events[name].filter(function (x) { return x.key !== key; }); } else { this.events[name] = []; } }; EventEmitter.prototype.emit = function (name, key) { if (this.events[name].length === 0) throw Error("\u62B1\u6B49\uFF0C\u4F60\u6CA1\u6709\u5B9A\u4E49 " name "\u76D1\u542C\u5668"); if (key) { this.events[name].forEach(function (x) { return x.key === key && x(); }); } else { this.events[name].forEach(function (x) { return x(); }); } }; return EventEmitter; }()); var player = new EventEmitter(); player.on('friendDied', function () { console.log('你可爱的队友已被击*'); }); player.on('enemyDied', function () { console.log('打的好呀,小帅哥'); }); // 模拟战况 var rand; var k = 1; while (k < 10) { rand = Math.floor(Math.random() * 10); if (rand % 2 === 0) { player.emit('friendDied'); } else { player.emit('enemyDied'); } k ; } // 你可爱的队友已被击* // 打的好呀,小帅哥 // 你可爱的队友已被击* // 你可爱的队友已被击* // 打的好呀,小帅哥 // 你可爱的队友已被击* // 你可爱的队友已被击* // 打的好呀,小帅哥 // 打的好呀,小帅哥

Why

优点

很好的实现事件推送,建立一套完备的触发机制

缺点

应用场景:JS中的addEventListenerRedux中的数据流模型

后续文章

看到这里的朋友请给自己竖一个大指姆,你真棒!其余的设计模式将在后续文章中补全~

觉得有帮助的话别忘了点赞 收藏 关注哦,你的支持就是我前进的动力❥(^_-)~

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved