《游戏编程模式》学习笔记(五)原型模式 Prototype Pattern
原型的定义
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
举个例子
假设我现在要做一款游戏,这个游戏里有许多不同种类的怪物,鬼魂,恶魔和巫师。这些怪物通过“生产者”进入这片区域,每种敌人有不同的生产者。
假设每种怪物都有不同的类,同时他们都继承怪兽这个基类,那么我们的代码就会是这样
class Monster
{// 代码……
};class Ghost : public Monster {};
class Demon : public Monster {};
class Sorcerer : public Monster {};
为了能够产生这些怪物,我们需要不同的生产者类,这些类都继承spawner这个基类,那么我们就得写如下的代码:
class Spawner
{
public:virtual ~Spawner() {}virtual Monster* spawnMonster() = 0;
};class GhostSpawner : public Spawner
{
public:virtual Monster* spawnMonster(){return new Ghost();}
};class DemonSpawner : public Spawner
{
public:virtual Monster* spawnMonster(){return new Demon();}
};// 你知道思路了……
那么这么一来,我们的架构就类似于这样:

每个怪物类都有生产者类,得到平行的类结构
已经闻到臭味了……你的代码里有一堆特定的spawner,将来要是有一个新怪物,你就得多些一堆代码。 众多类,众多引用,众多冗余,众多副本,众多重复自我……
那么这个时候,原型模式就可以派上用场了! 我们来看看实现
原型模式提供了一个解决方案。 关键思路是一个对象可以产出与它自己相近的对象。 如果你有一个恶灵,你可以制造更多恶灵。 如果你有一个恶魔,你可以制造其他恶魔。 任何怪物都可以被视为原型怪物,产出其他版本的自己。
我们给基类Monster增加一个Clone()的方法
class Monster
{
public:virtual ~Monster() {}virtual Monster* clone() = 0;// 其他代码……
};
每个怪兽子类提供一个特定实现,返回与它自己的类和状态都完全一样的新对象。就像这样
class Ghost : public Monster {
public:Ghost(int health, int speed): health_(health),speed_(speed){}virtual Monster* clone(){return new Ghost(health_, speed_);}private:int health_;int speed_;
};
然后我们就不需要那么多特定的spawner了,我们只需要一个spawner()如下:
class Spawner
{
public:Spawner(Monster* prototype): prototype_(prototype){}Monster* spawnMonster(){return prototype_->clone();}private:Monster* prototype_;
};
可以看到这个Spawner内部有一个Monster*类型的prototype,这就是原型,一个隐藏的怪物, 它唯一的任务就是被生产者当做模板,去产生更多一样的怪物, 有点像一个从来不离开巢穴的蜂后。

当你要使用的时候,你只需要这么做:
Monster* ghostPrototype = new Ghost(15, 3);
Spawner* ghostSpawner = new Spawner(ghostPrototype);
这个模式的灵巧之处在于它不但拷贝原型的类,也拷贝它的状态。 这就意味着我们可以创建一个生产者,生产快速鬼魂,虚弱鬼魂,慢速鬼魂,而只需创建一个合适的原型鬼魂。
如果你还是觉得麻烦,懒得写Clone()方法的话,这里还有一种思路,使用生产函数来代替生产者类,我们这么写一个生产函数:
Monster* spawnGhost()
{return new Ghost();
}
这比构建怪兽生产者类更简洁。生产者类只需简单地存储一个函数指针:
typedef Monster* (*SpawnCallback)();class Spawner
{
public:Spawner(SpawnCallback spawn): spawn_(spawn){}Monster* spawnMonster(){return spawn_();}private:SpawnCallback spawn_;
};
而你调用的时候,就这样写就行:
Spawner* ghostSpawner = new Spawner(spawnGhost);
原型还可以做什么?
原型模式不仅仅可以应用在代码之中,我们还可以将其用在一些别的地方,比如数据存储中。
再举个例子,我们在游戏中经常使用Json来存储一些数据,比如怪物的各种属性。
所以游戏中的哥布林也许被定义为像这样的东西:
{"name": "goblin grunt","minHealth": 20,"maxHealth": 30,"resists": ["cold", "poison"],"weaknesses": ["fire", "light"]
}
接下来,如果策划和你说,我还要别的哥布林,例如哥布林巫师,哥布林弓箭手,即使他们的很多属性都是一样的,我们还是不得不这么写:
{"name": "goblin wizard","minHealth": 20,"maxHealth": 30,"resists": ["cold", "poison"],"weaknesses": ["fire", "light"],"spells": ["fire ball", "lightning bolt"]
}{"name": "goblin archer","minHealth": 20,"maxHealth": 30,"resists": ["cold", "poison"],"weaknesses": ["fire", "light"],"attacks": ["short bow"]
}
太重复了,我们讨厌重复,太多重复的数据意味着我们要话更多的时间去维护和管理,这是我们都不想看到的。
如果这是代码,我们会为“哥布林”构建抽象,并在三个哥布林类型中重用。 但是无能的JSON没法这么做。所以让我们把它做得更加巧妙些。
我们可以为对象添加"prototype"字段,记录委托对象的名字。 如果在此对象内没找到一个字段,那就去委托对象中查找。
这样,我们可以简化我们的哥布林JSON内容:
{"name": "goblin grunt","minHealth": 20,"maxHealth": 30,"resists": ["cold", "poison"],"weaknesses": ["fire", "light"]
}{"name": "goblin wizard","prototype": "goblin grunt","spells": ["fire ball", "lightning bolt"]
}{"name": "goblin archer","prototype": "goblin grunt","attacks": ["short bow"]
}
这样不就好多了?只需在游戏引擎上多花点时间,你就能让设计者更加方便地添加不同的武器和怪物,而增加的这些丰富度能够取悦玩家。
这一节的内容有好多关于原型模式的思想方面的介绍,作者还介绍了原型模式在编程语言方面的应用,我认为这些都是暂时对游戏编程帮助不大,所以没有记录下来,有兴趣的同学请翻阅原文。
原文链接:https://gpp.tkchu.me/prototype.html
相关文章:
《游戏编程模式》学习笔记(五)原型模式 Prototype Pattern
原型的定义 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 举个例子 假设我现在要做一款游戏,这个游戏里有许多不同种类的怪物,鬼魂,恶魔和巫师。这些怪物通过“生产者”进入这片区域,每种敌人…...
ansible案列之LNMP分布式剧本
LNMP分布式剧本 一:环境设置二:编写Nginx剧本准备nginx下载源准备配置文件并开放PHP的访问路径准备php测试页面编写nginx剧本 三:编写Mysql剧本编写密码获取脚本准备Mysql的yum源编写mysql剧本 四:准备PHP剧本准备两个配置文件编写…...
React2023电商项目实战 - 1.项目搭建
古人学问无遗力,少壮工夫老始成。 纸上得来终觉浅,绝知此事要躬行。 —— 陆游《《冬夜读书示子聿》》 系列文章目录 项目搭建App登录及网关App文章自媒体平台(博主后台)内容审核(自动) 文章目录 系列文章目录一、项目介绍1.页面…...
数据库连接池(c3p0和德鲁伊)
目录 连接池介绍 c3p0连接池 传统方法引入jar包 配置文件 德鲁伊连接池 德鲁伊工具类 传统jdbc数据库使用DriverManger来获取,每次向数据库建立连接需要将Connection加载到内存中,频繁的操作会造成占用很多系统资源,造成服务器崩溃&…...
ARM--day6(实现字符、字符串收发的代码和现象,分析RCC、GPIO、UART章节)
uart4.h #ifndef __UART4_H__ #define __UART4_H__#include "stm32mp1xx_rcc.h" #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_uart.h"//RCC/GPIO/UART4章节初始化 void hal_uart4_init();//发送一个字符函数 void hal_put_char(const c…...
2023牛客暑期多校训练营9 B.Semi-Puzzle: Brain Storm
文章目录 题目大意题解求解回溯 参考代码 题目大意 给定两个数 a , m a,m a,m ,求满足 a u ≡ u ( m o d m ) a^u \equiv u (mod\ \ m) au≡u(mod m) 的一个解。 ( 1 ≤ a , m ≤ 1 0 9 , 0 ≤ u ≤ 1 0 18 ) (1\leq a,m \leq10^9 ,0\leq u\leq 10^{18}) (1≤a…...
mysql中的窗口函数
MySQL中的窗口函数(Window Functions)是一种用于在查询结果集内执行计算的功能。窗口函数可以在查询中进行分析和聚合操作,而无需将查询结果分组。它们可以用于计算排名、行号、累积值等各种分析操作。窗口函数通常与OVER子句一起使用&#x…...
【双指针】经典数组双指针题LeetCode
文章目录 27. 移除元素 简单283. 移动零 简单🔥167. 两数之和 II - 输入有序数组 中等11. 盛最多水的容器 中等🔥15. 三数之和 中等(N数之和)中等🔥42. 接雨水 困难 🔥26. 删除有序数组中的重复项 简单5. 最…...
极智嘉x吉利汽车 x京东物流,引领汽车行业智慧物流新变革!
近日,中国领先的汽车制造商吉利汽车携手中国领先的技术驱动的供应链解决方案及物流服务商京东物流、全球仓储机器人引领者极智嘉(Geek),在西安吉利汽车制造基地RDC仓库率先落地SkyPick上存下拣解决方案,实现了全物流链精益化、智能化、一体化…...
RK3588平台开发系列讲解(AI 篇)RKNN C API 详细说明
文章目录 一、API 硬件平台支持说明二、API 函数介绍2.1、rknn_init2.2、rknn_destroy2.3、rknn_query2.4、rknn_inputs_set2.5、rknn_run2.6、rknn_outputs_get2.7、rknn_outputs_release沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章主要讲解 RKNN C API 详细…...
【基础】Android Handler
一、博客参考 Handler机制详解【重点】:https://www.jianshu.com/p/b4d745c7ff7a Handler Thread工作线程操作UI范例【重点】:https://www.cnblogs.com/net168/p/4075126.html 二、内存泄漏的解决:静态内部类弱引用 关于 Handler…...
c语言实现MD5算法
MD5加密 文章目录 MD5加密MD5介绍应用场景代码分析 (基于qt5.14.2)测试记录 MD5介绍 1。 一种单向加密算法,即对明文加密,而不能通过密文得到明文。对原数据的任何改动,哪怕是1字节,得到的MD5值都有很大的区…...
Apache Doris 2.0.0 特性分析
1、存算分离 所谓存算分离是指查询外表时,使用一种专门做计算的BE节点,但对于存储在BE上的内部表,目前还不能做到存储分离。 doris可以查询外部表,包括: Hive、Iceberg、Hudi、Elasticsearch、JDBC、Paimon 早期版本中…...
如何做H5性能测试?
提起H5性能测试,可能许多同学有所耳闻,但是不知道该如何对H5做性能测试,或者不知道H5应该关注哪些性能指标。今天我们就来看下,希望阅读本文后,能够有所了解。 常用指标 1、H5性能相关参数介绍 白屏时间:…...
【Docker】Docker Desktop配置资源:cpu、内存等(windows环境下)
Docker Desktop配置资源:cpu、内存等(windows环境下) 一、WSL2 以及 hyper-v区别,二者安装docker desktop1.WSL2和hyper-v区别2.安装Docker Desktop 二、docker desktop限额配置,资源配置方法 Docker 是指容器化技术&a…...
8.2.tensorRT高级(3)封装系列-内存管理的封装,内存的复用
目录 前言1. 内存管理封装2. 补充知识总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程,之前有看过一遍,但是没有做笔记,很多东西也忘了。这次重新撸一遍,顺便记记笔记。 本次课程学习 tensorRT 高级-内存管理的封装&…...
Keepalived入门指南:实现故障转移和负载均衡
文章目录 一、简介1. Keepalived概述2. 高可用性和负载均衡的重要性 二、故障转移1. 什么是故障转移2. Keepalived的故障转移原理a) VRRP协议b) 虚拟路由器ID和优先级 3. 配置Keepalived实现故障转移a) 主备服务器的设置b) 监控网络接口c) 虚拟IP的配置d) 备份服务器接管流程 三…...
cuOSD(CUDA On-Screen Display Library)库的学习
目录 前言1. cuOSD1.1 Description1.2 Getting started1.3 For Python Interface1.4 Demo1.5 Performance Table 2. cuOSD案例2.1 环境配置2.2 simple案例2.3 segment案例2.4 segment2案例2.5 polyline案例2.6 comp案例2.7 perf案例 3. cuOSD浅析3.1 simple_draw函数 4. 补充知…...
c++函数指针基本用法
将函数像变量一样传递,实际上拿到的是函数的地址,由于函数类型的多样,可以使用auto关键字,可以使用 void(*function2)() ,不过它太繁琐,因此使用typedef 起个名字 typedef void(*HelloWorldFunction)(); 叫…...
Java创建对象的几种方式
在Java中,对象是程序中的一种基本元素,它通过类定义和创建。本篇教程旨在介绍Java中创建对象的几种方式,包括使用new关键字、反射、clone、反序列化等方式。 使用new关键字创建对象 在Java中,最常用的创建对象方式是使用new关键…...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
